From 67e44f7f8e16534fd6fd7b01b2d89e8812cc2145 Mon Sep 17 00:00:00 2001 From: mardock2009 Date: Mon, 16 Feb 2026 09:02:48 +0000 Subject: [PATCH] =?UTF-8?q?Initial=20commit=20=E2=80=93=20m=C3=B3j=20build?= =?UTF-8?q?=20oscama=20z=20Advanced=20fake=20DCW=20detection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 24 + .gitmodules | 6 + CMakeLists.txt | 1045 ++ CODING.RULES.txt | 59 + COPYING | 674 + Distribution/doc/example/oscam.ac | 10 + Distribution/doc/example/oscam.cacheex | 12 + Distribution/doc/example/oscam.cert | 9 + Distribution/doc/example/oscam.conf | 71 + Distribution/doc/example/oscam.dvbapi | 41 + Distribution/doc/example/oscam.guess | 9 + Distribution/doc/example/oscam.ird | 10 + Distribution/doc/example/oscam.provid | 14 + Distribution/doc/example/oscam.server | 169 + Distribution/doc/example/oscam.services | 19 + Distribution/doc/example/oscam.srvid | 6 + Distribution/doc/example/oscam.tiers | 7 + Distribution/doc/example/oscam.user | 71 + Distribution/doc/example/oscam.whitelist | 26 + Distribution/doc/html/list_smargo.1.html | 36 + Distribution/doc/html/oscam.1.html | 243 + Distribution/doc/html/oscam.ac.5.html | 54 + Distribution/doc/html/oscam.cacheex.5.html | 53 + Distribution/doc/html/oscam.cert.5.html | 46 + Distribution/doc/html/oscam.conf.5.html | 1985 +++ Distribution/doc/html/oscam.dvbapi.5.html | 142 + Distribution/doc/html/oscam.guess.5.html | 45 + Distribution/doc/html/oscam.ird.5.html | 46 + Distribution/doc/html/oscam.provid.5.html | 46 + Distribution/doc/html/oscam.ratelimit.5.html | 63 + Distribution/doc/html/oscam.server.5.html | 1109 ++ Distribution/doc/html/oscam.services.5.html | 72 + Distribution/doc/html/oscam.srvid.5.html | 58 + Distribution/doc/html/oscam.srvid2.5.html | 51 + Distribution/doc/html/oscam.tiers.5.html | 54 + Distribution/doc/html/oscam.user.5.html | 542 + Distribution/doc/html/oscam.whitelist.5.html | 87 + Distribution/doc/man/list_smargo.1 | 10 + Distribution/doc/man/oscam.1 | 195 + Distribution/doc/man/oscam.ac.5 | 21 + Distribution/doc/man/oscam.cacheex.5 | 19 + Distribution/doc/man/oscam.cert.5 | 16 + Distribution/doc/man/oscam.conf.5 | 1513 +++ Distribution/doc/man/oscam.dvbapi.5 | 90 + Distribution/doc/man/oscam.guess.5 | 15 + Distribution/doc/man/oscam.ird.5 | 15 + Distribution/doc/man/oscam.provid.5 | 15 + Distribution/doc/man/oscam.ratelimit.5 | 30 + Distribution/doc/man/oscam.server.5 | 887 ++ Distribution/doc/man/oscam.services.5 | 32 + Distribution/doc/man/oscam.srvid.5 | 24 + Distribution/doc/man/oscam.srvid2.5 | 20 + Distribution/doc/man/oscam.tiers.5 | 20 + Distribution/doc/man/oscam.user.5 | 405 + Distribution/doc/man/oscam.whitelist.5 | 47 + Distribution/doc/txt/list_smargo.txt | 21 + Distribution/doc/txt/oscam.ac.txt | 29 + Distribution/doc/txt/oscam.cacheex.txt | 31 + Distribution/doc/txt/oscam.cert.txt | 25 + Distribution/doc/txt/oscam.conf.txt | 1086 ++ Distribution/doc/txt/oscam.dvbapi.txt | 82 + Distribution/doc/txt/oscam.guess.txt | 26 + Distribution/doc/txt/oscam.ird.txt | 25 + Distribution/doc/txt/oscam.provid.txt | 25 + Distribution/doc/txt/oscam.ratelimit.txt | 33 + Distribution/doc/txt/oscam.server.txt | 709 ++ Distribution/doc/txt/oscam.services.txt | 37 + Distribution/doc/txt/oscam.srvid.txt | 34 + Distribution/doc/txt/oscam.srvid2.txt | 30 + Distribution/doc/txt/oscam.tiers.txt | 31 + Distribution/doc/txt/oscam.txt | 150 + Distribution/doc/txt/oscam.user.txt | 309 + Distribution/doc/txt/oscam.whitelist.txt | 53 + Distribution/monitor/mpcsmon-src-0.6.tar.bz2 | Bin 0 -> 40331 bytes Distribution/monitor/mpcsmon.sh | 140 + LICENSE | 232 + Makefile | 962 ++ Makefile.extra | 301 + README.build | 302 + README.config | 87 + README.dvbapi_protocol | 418 + README.md | 188 + SoftCam.Key | 0 config.h | 110 + config.sh | 1015 ++ cscrypt/CMakeLists.txt | 16 + cscrypt/Makefile | 2 + cscrypt/aes.c | 1309 ++ cscrypt/aes.h | 45 + cscrypt/bn.h | 516 + cscrypt/bn_add.c | 327 + cscrypt/bn_asm.c | 854 ++ cscrypt/bn_ctx.c | 146 + cscrypt/bn_div.c | 391 + cscrypt/bn_exp.c | 529 + cscrypt/bn_lcl.h | 419 + cscrypt/bn_lib.c | 775 ++ cscrypt/bn_mul.c | 804 ++ cscrypt/bn_print.c | 299 + cscrypt/bn_shift.c | 211 + cscrypt/bn_sqr.c | 296 + cscrypt/bn_word.c | 204 + cscrypt/buffer.h | 98 + cscrypt/des.c | 920 ++ cscrypt/des.h | 40 + cscrypt/fast_aes.c | 1187 ++ cscrypt/fast_aes.h | 32 + cscrypt/i_cbc.c | 177 + cscrypt/i_ecb.c | 89 + cscrypt/i_skey.c | 167 + cscrypt/idea.h | 103 + cscrypt/idea_lcl.h | 215 + cscrypt/md5.c | 402 + cscrypt/md5.h | 22 + cscrypt/mdc2.c | 669 + cscrypt/mdc2.h | 104 + cscrypt/mem.c | 276 + cscrypt/openssl_mods.h | 11 + cscrypt/rc6.c | 224 + cscrypt/rc6.h | 38 + cscrypt/sha1.c | 320 + cscrypt/sha1.h | 30 + cscrypt/sha256.c | 317 + cscrypt/sha256.h | 125 + csctapi/CMakeLists.txt | 62 + csctapi/Makefile | 2 + csctapi/atr.c | 453 + csctapi/atr.h | 142 + csctapi/cardreaders.h | 20 + csctapi/icc_async.c | 1377 ++ csctapi/icc_async.h | 57 + csctapi/ifd_amsmc.c | 92 + csctapi/ifd_azbox.c | 172 + csctapi/ifd_cool.c | 333 + csctapi/ifd_db2com.c | 152 + csctapi/ifd_db2com.h | 12 + csctapi/ifd_drecas.c | 130 + csctapi/ifd_drecas.h | 14 + csctapi/ifd_mp35.c | 265 + csctapi/ifd_pcsc.c | 390 + csctapi/ifd_phoenix.c | 247 + csctapi/ifd_phoenix.h | 13 + csctapi/ifd_sc8in1.c | 1281 ++ csctapi/ifd_sci.c | 576 + csctapi/ifd_sci_global.h | 103 + csctapi/ifd_sci_ioctl.h | 64 + csctapi/ifd_smargo.c | 274 + csctapi/ifd_smartreader.c | 1831 +++ csctapi/ifd_smartreader_types.h | 107 + csctapi/ifd_stapi.c | 104 + csctapi/ifd_stinger.c | 577 + csctapi/io_serial.c | 887 ++ csctapi/io_serial.h | 88 + csctapi/protocol_t0.c | 555 + csctapi/protocol_t0.h | 33 + csctapi/protocol_t1.c | 330 + devtools/README | 16 + devtools/check_cmdline_opts.sh | 63 + devtools/check_config_tables.sh | 51 + devtools/extract_config.sh | 12 + extapi/README | 5 + extapi/coolapi.h | 240 + extapi/cygwin/SCardErr.h | 655 + extapi/cygwin/WinSCard.h | 1137 ++ extapi/cygwin/WinSmCrd.h | 333 + extapi/linux/README | 2 + extapi/linux/serial.h | 126 + extapi/linux/tty_flags.h | 78 + extapi/openxcas/libOpenXCASAPI.a | Bin 0 -> 87544 bytes extapi/openxcas/openxcas.conf | 5 + extapi/openxcas/openxcas_api.h | 228 + extapi/openxcas/openxcas_message.h | 138 + extapi/openxcas/openxcas_smartcard.h | 17 + gitupdate.sh | 25 + globals.h | 2813 +++++ globals.h.orig | 2811 +++++ images/image1.jpg | Bin 0 -> 73977 bytes minilzo/CMakeLists.txt | 8 + minilzo/Makefile | 2 + minilzo/README.LZO | 123 + minilzo/lzoconf.h | 453 + minilzo/lzodefs.h | 3268 +++++ minilzo/minilzo.c | 6365 ++++++++++ minilzo/minilzo.h | 106 + module-anticasc.c | 490 + module-anticasc.h | 28 + module-cacheex.c | 1559 +++ module-cacheex.h | 78 + module-camd33.c | 262 + module-camd35-cacheex.c | 1771 +++ module-camd35-cacheex.c.orig | 1754 +++ module-camd35-cacheex.h | 25 + module-camd35.c | 1296 ++ module-camd35.h | 11 + module-cccam-cacheex.c | 1574 +++ module-cccam-cacheex.h | 22 + module-cccam-data.h | 255 + module-cccam.c | 5140 ++++++++ module-cccam.h | 50 + module-cccshare.c | 1728 +++ module-cccshare.h | 44 + module-constcw.c | 170 + module-csp.c | 278 + module-cw-cycle-check.c | 879 ++ module-cw-cycle-check.h | 12 + module-dvbapi-azbox.c | 354 + module-dvbapi-azbox.h | 15 + module-dvbapi-chancache.c | 212 + module-dvbapi-chancache.h | 24 + module-dvbapi-coolapi-legacy.c | 701 ++ module-dvbapi-coolapi.c | 952 ++ module-dvbapi-coolapi.h | 11 + module-dvbapi-mca.c | 624 + module-dvbapi-mca.h | 15 + module-dvbapi-stapi.c | 769 ++ module-dvbapi-stapi.h | 26 + module-dvbapi-stapi5.c | 749 ++ module-dvbapi.c | 9412 ++++++++++++++ module-dvbapi.h | 552 + module-emulator-biss.c | 883 ++ module-emulator-biss.h | 22 + module-emulator-cryptoworks.c | 688 + module-emulator-cryptoworks.h | 10 + module-emulator-director.c | 644 + module-emulator-director.h | 11 + module-emulator-irdeto.c | 602 + module-emulator-irdeto.h | 15 + module-emulator-nagravision.c | 376 + module-emulator-nagravision.h | 10 + module-emulator-omnicrypt.c | 72 + module-emulator-omnicrypt.h | 10 + module-emulator-osemu.c | 986 ++ module-emulator-osemu.h | 96 + module-emulator-powervu.c | 2795 +++++ module-emulator-powervu.h | 63 + module-emulator-viaccess.c | 1183 ++ module-emulator-viaccess.h | 11 + module-emulator.c | 894 ++ module-gbox-cards.c | 666 + module-gbox-cards.h | 32 + module-gbox-helper.c | 283 + module-gbox-helper.h | 15 + module-gbox-remm.c | 469 + module-gbox-remm.h | 23 + module-gbox-sms.c | 478 + module-gbox-sms.h | 22 + module-gbox.c | 2767 +++++ module-gbox.h | 196 + module-ghttp.c | 891 ++ module-lcd.c | 268 + module-lcd.h | 12 + module-led.c | 389 + module-led.h | 83 + module-monitor.c | 999 ++ module-monitor.h | 13 + module-newcamd-des.c | 500 + module-newcamd-des.h | 17 + module-newcamd.c | 1922 +++ module-newcamd.h | 6 + module-pandora.c | 321 + module-radegast.c | 407 + module-scam.c | 1197 ++ module-serial.c | 1521 +++ module-stat.c | 2119 ++++ module-stat.h | 46 + module-streamrelay.c | 2230 ++++ module-streamrelay.h | 129 + module-webif-lib.c | 1146 ++ module-webif-lib.h | 119 + module-webif-tpl.c | 892 ++ module-webif-tpl.h | 62 + module-webif.c | 10369 ++++++++++++++++ module-webif.c.orig | 10342 +++++++++++++++ module-webif.h | 18 + modules.h | 21 + oscam-aes.c | 236 + oscam-aes.h | 18 + oscam-array.c | 74 + oscam-array.h | 32 + oscam-cache.c | 987 ++ oscam-cache.h | 24 + oscam-chk.c | 1353 ++ oscam-chk.c.orig | 1316 ++ oscam-chk.c.rej | 11 + oscam-chk.h | 51 + oscam-client.c | 919 ++ oscam-client.h | 24 + oscam-conf-chk.c | 668 + oscam-conf-chk.h | 25 + oscam-conf-mk.c | 1135 ++ oscam-conf-mk.h | 36 + oscam-conf.c | 581 + oscam-conf.h | 232 + oscam-config-account.c | 707 ++ oscam-config-global.c | 1628 +++ oscam-config-reader.c | 1694 +++ oscam-config.c | 1494 +++ oscam-config.c.orig | 1494 +++ oscam-config.h | 74 + oscam-ecm.c | 4205 +++++++ oscam-ecm.c.orig | 3788 ++++++ oscam-ecm.h | 63 + oscam-emm-cache.c | 534 + oscam-emm-cache.h | 21 + oscam-emm.c | 899 ++ oscam-emm.h | 10 + oscam-failban.c | 141 + oscam-failban.h | 9 + oscam-files.c | 220 + oscam-files.h | 14 + oscam-garbage.c | 218 + oscam-garbage.h | 13 + oscam-hashtable.c | 76 + oscam-hashtable.h | 20 + oscam-llist.c | 706 ++ oscam-llist.h | 92 + oscam-lock.c | 271 + oscam-lock.h | 28 + oscam-log-reader.c | 151 + oscam-log-reader.h | 13 + oscam-log.c | 854 ++ oscam-log.h | 49 + oscam-net.c | 926 ++ oscam-net.h | 53 + oscam-reader.c | 1558 +++ oscam-reader.h | 33 + oscam-signing.c | 565 + oscam-signing.h | 56 + oscam-simples.c | 484 + oscam-string-isotables.h | 18 + oscam-string.c | 1470 +++ oscam-string.h | 58 + oscam-time.c | 310 + oscam-time.h | 32 + oscam-work.c | 632 + oscam-work.h | 43 + oscam.c | 2048 +++ reader-bulcrypt.c | 780 ++ reader-common.c | 619 + reader-common.h | 83 + reader-conax.c | 678 + reader-cryptoworks.c | 915 ++ reader-dgcrypt.c | 275 + reader-dre-cas.c | 885 ++ reader-dre-common.c | 845 ++ reader-dre-common.h | 11 + reader-dre-st20.c | 467 + reader-dre-st20.h | 6 + reader-dre.c | 1269 ++ reader-griffin.c | 485 + reader-irdeto.c | 1404 +++ reader-irdeto.h | 10 + reader-nagra-common.c | 526 + reader-nagra-common.h | 10 + reader-nagra.c | 1558 +++ reader-nagracak7.c | 1831 +++ reader-seca.c | 773 ++ reader-tongfang.c | 644 + reader-viaccess.c | 2554 ++++ reader-videoguard-common.c | 1149 ++ reader-videoguard-common.h | 66 + reader-videoguard1.c | 384 + reader-videoguard12.c | 428 + reader-videoguard2.c | 1620 +++ readers.h | 22 + tests.c | 317 + tommyDS_hashlin/tommychain.h | 219 + tommyDS_hashlin/tommyhash.c | 241 + tommyDS_hashlin/tommyhash.h | 135 + tommyDS_hashlin/tommyhashlin.c | 332 + tommyDS_hashlin/tommyhashlin.h | 344 + tommyDS_hashlin/tommylist.c | 60 + tommyDS_hashlin/tommylist.h | 399 + tommyDS_hashlin/tommytypes.h | 518 + toolchains/toolchain-arm-coolstream.cmake | 4 + .../toolchain-arm-dockstar-openwrt.cmake | 5 + toolchains/toolchain-arm-friendlyarm.cmake | 4 + toolchains/toolchain-arm-mca.cmake | 4 + toolchains/toolchain-arm-none.cmake | 4 + toolchains/toolchain-arm-nslu2-openwrt.cmake | 4 + toolchains/toolchain-arm-nslu2-unslung.cmake | 4 + toolchains/toolchain-arm-su980.cmake | 4 + .../toolchain-arm-wrt350nv2-openwrt.cmake | 5 + toolchains/toolchain-mips-agv2_w.cmake | 4 + toolchains/toolchain-mips-azbox.cmake | 4 + toolchains/toolchain-mips-dir825.cmake | 4 + toolchains/toolchain-mips-fonera2.cmake | 4 + toolchains/toolchain-mips-tuxbox.cmake | 4 + toolchains/toolchain-mips-wrt54g.cmake | 4 + ...olchain-mipsel-tuxbox-broken-pthread.cmake | 5 + .../toolchain-mipsel-tuxbox-linux-gnu.cmake | 10 + toolchains/toolchain-mipsel-tuxbox.cmake | 4 + toolchains/toolchain-powerpc-tuxbox.cmake | 4 + toolchains/toolchain-sh4-amino.cmake | 4 + toolchains/toolchain-sh4-qboxhd.cmake | 4 + toolchains/toolchain-sh4-tuxbox-stapi.cmake | 5 + toolchains/toolchain-sh4-tuxbox.cmake | 4 + toolchains/toolchain-sparc-padre.cmake | 4 + toolchains/toolchain-tripledragon.cmake | 4 + utils/CMakeLists.txt | 35 + utils/list_smargo.c | 155 + webif/.gitignore | 6 + webif/Makefile | 56 + webif/README | 19 + webif/api.json/cacheex.json | 5 + webif/api.json/cacheexaiobit.json | 18 + webif/api.json/cacheexbit.json | 16 + webif/api.json/entitlementbit.json | 1 + webif/api.json/entitlements.json | 1 + webif/api.json/footer.json | 2 + webif/api.json/header.json | 107 + webif/api.json/reader.json | 1 + webif/api.json/readerbit.json | 1 + webif/api.json/status.json | 21 + webif/api.json/status_statusbits.json | 42 + webif/api.json/user.json | 3 + webif/api.json/userbit.json | 46 + webif/api.xml/cccamcardlist.xml | 7 + webif/api.xml/cccamcardlist_cardlist.xml | 10 + .../cccamcardlist_cardlist_nodelist.xml | 1 + .../cccamcardlist_cardlist_providerlist.xml | 1 + webif/api.xml/confirmation.xml | 3 + webif/api.xml/error.xml | 3 + webif/api.xml/failban.xml | 5 + webif/api.xml/failban_failbanrow.xml | 1 + webif/api.xml/file.xml | 5 + webif/api.xml/footer.xml | 1 + webif/api.xml/header.xml | 2 + webif/api.xml/readers.xml | 5 + webif/api.xml/readers_readerlist.xml | 1 + webif/api.xml/readerstats.xml | 11 + webif/api.xml/readerstats_ecmstats.xml | 1 + webif/api.xml/readerstats_emmstats.xml | 1 + webif/api.xml/status.xml | 7 + webif/api.xml/status_statusbits.xml | 5 + webif/api.xml/userconfiglist.xml | 46 + webif/api.xml/userconfiglist_userconfigs.xml | 16 + webif/api.xml/useredit.xml | 32 + webif/cacheex/cacheex.html | 56 + webif/cacheex/cacheex_tablerow.html | 1 + webif/cacheexaio/cacheex.html | 59 + webif/cacheexaio/cacheex_tablerow.html | 1 + webif/cacheexaio/cacheex_tablerow_stats.html | 1 + webif/config/anticasc.html | 36 + webif/config/cache.html | 7 + webif/config/cache_cacheexaiocsp.html | 32 + webif/config/cache_cacheexcsp.html | 14 + webif/config/cache_cwcycle.html | 10 + webif/config/cacheaio.html | 7 + webif/config/camd33.html | 8 + webif/config/camd35.html | 7 + webif/config/camd35tcp.html | 7 + webif/config/cccam.html | 68 + webif/config/cccam_control.html | 19 + webif/config/cccreshare.html | 6 + webif/config/config.html | 18 + webif/config/dvbapi.html | 61 + webif/config/dvbapi_demuxerfix.html | 2 + webif/config/dvbapi_extended_cw_api.html | 9 + webif/config/gbox.html | 73 + webif/config/global.html | 170 + webif/config/global_cacheex_aio_logging.html | 2 + webif/config/global_enableledbit.html | 2 + webif/config/global_getblockemmauprovid.html | 1 + webif/config/global_localcards.html | 6 + webif/config/global_suppresscmd08.html | 2 + webif/config/global_unlockparental.html | 1 + webif/config/lcd.html | 7 + webif/config/loadbalancer.html | 35 + webif/config/loadbalancer_control.html | 16 + webif/config/menu.html | 23 + webif/config/menu_anticasc.html | 1 + webif/config/menu_camd33.html | 1 + webif/config/menu_camd35.html | 1 + webif/config/menu_camd35tcp.html | 1 + webif/config/menu_cccam.html | 1 + webif/config/menu_cmcaptioncwc.html | 1 + webif/config/menu_dvbapi.html | 1 + webif/config/menu_gbox.html | 1 + webif/config/menu_lcd.html | 1 + webif/config/menu_loadbalancer.html | 1 + webif/config/menu_monitor.html | 1 + webif/config/menu_newcamd.html | 1 + webif/config/menu_radegast.html | 1 + webif/config/menu_scam.html | 1 + webif/config/menu_serial.html | 1 + webif/config/menu_streamrelay.html | 1 + webif/config/monitor.html | 19 + webif/config/newcamd.html | 11 + webif/config/radegast.html | 7 + webif/config/scam.html | 6 + webif/config/serial.html | 4 + webif/config/serial_devices.html | 1 + webif/config/streamrelay.html | 44 + webif/config/streamrelay_emusettings.html | 9 + webif/config/webif.html | 104 + webif/config/webif_httpssl.html | 4 + webif/config/webif_show_jquery.html | 1 + webif/config/webif_showcacheexinfo.html | 1 + webif/emm/emm.html | 80 + webif/emm_running/emm_running.html | 24 + webif/entitlements/entitlements.html | 10 + webif/entitlements/entitlements_bit.html | 28 + webif/entitlements/entitlements_bit_nds.html | 2 + webif/entitlements/entitlements_cccambit.html | 6 + .../entitlements_cccambit_statsentry.html | 1 + .../entitlements/entitlements_genericbit.html | 4 + webif/entitlements/entitlements_itembit.html | 1 + webif/failban/failban.html | 14 + webif/failban/failban_failbanrow.html | 1 + webif/files/file.html | 15 + webif/files/file_edit_css.html | 1 + webif/files/file_filterform.html | 7 + webif/files/file_writeprotection.html | 1 + webif/files/menu.html | 29 + webif/files/menu_anticasc.html | 1 + webif/files/menu_constantcw.html | 1 + webif/files/menu_dvbapi.html | 1 + webif/files/menu_fakecws.html | 1 + webif/files/menu_gbox.html | 14 + webif/files/menu_softcamkey.html | 1 + webif/files/menu_twin.html | 1 + webif/ghttp/autoconf.html | 5 + webif/ghttp/pre_autoconf.html | 24 + webif/graph/graph.svg | 249 + webif/images/ICARRL.svg | 1 + webif/images/ICARRR.svg | 1 + webif/images/ICDEL.svg | 1 + webif/images/ICDIS.svg | 1 + webif/images/ICEDI.svg | 1 + webif/images/ICEMM.svg | 1 + webif/images/ICENA.svg | 1 + webif/images/ICENT.svg | 1 + webif/images/ICHID.svg | 1 + webif/images/ICKIL.svg | 1 + webif/images/ICMLOGO.svg | 1 + webif/images/ICREF.svg | 1 + webif/images/ICRES.svg | 1 + webif/images/ICSHW.svg | 1 + webif/images/ICSPAC.gif | Bin 0 -> 42 bytes webif/images/ICSTA.svg | 1 + webif/images/ICSTART.svg | 1 + webif/images/ICSTOP.svg | 1 + webif/images/favicon.ico | Bin 0 -> 3259 bytes webif/include/body.html | 6 + webif/include/cccamentitlements.html | 1 + webif/include/cccamentitletooltip.html | 1 + webif/include/css.css | 1289 ++ webif/include/footer.html | 14 + webif/include/foundentitlements.html | 1 + webif/include/header.html | 3 + webif/include/header_short.html | 13 + webif/include/jquery.js | 2 + webif/include/jscript.js | 3031 +++++ webif/include/logobit.html | 1 + webif/include/logobit_img.html | 1 + webif/include/menu.html | 16 + webif/include/menu_cacheexmenuitem.html | 1 + webif/include/message.html | 1 + webif/include/message_bit.html | 1 + webif/include/noentitlements.html | 1 + webif/include/poll.html | 1 + webif/include/pollingset.html | 1 + webif/include/protocamd3aiopic.html | 1 + webif/include/protocccampic.html | 1 + webif/include/protonewcamdpic.html | 1 + webif/include/protootherpic.html | 1 + webif/include/refresh.html | 1 + webif/logmenu/log_clearlog.html | 1 + webif/logmenu/log_clearuserlog.html | 1 + webif/logmenu/log_disablelogmenu.html | 1 + webif/logmenu/log_filterform.html | 1 + webif/logmenu/log_logmenuonoff.html | 1 + webif/logpage/logpage.html | 37 + webif/logpage/logpage_debugmenu.html | 19 + webif/logpage/logpage_menu.html | 1 + webif/logpage/logpage_sizemenu.html | 8 + webif/pages_gen.c | 557 + webif/pages_index.txt | 345 + webif/pages_index_check | 21 + webif/pages_mkdep | 10 + webif/pages_wiki.c | 655 + webif/pages_wiki.h | 27 + webif/readerconfig/readerconfig.html | 101 + .../readerconfig_cacheexaiobit.html | 82 + .../readerconfig/readerconfig_cacheexbit.html | 32 + .../readerconfig/readerconfig_camd35bit.html | 2 + webif/readerconfig/readerconfig_cccambit.html | 26 + .../readerconfig/readerconfig_cs378xbit.html | 5 + webif/readerconfig/readerconfig_emubit.html | 2 + webif/readerconfig/readerconfig_gboxbit.html | 24 + .../readerconfig_gboxcccresharebit.html | 3 + webif/readerconfig/readerconfig_ghttpbit.html | 3 + webif/readerconfig/readerconfig_hopbit.html | 3 + webif/readerconfig/readerconfig_hwreader.html | 32 + .../readerconfig_hwreader_boxkey.html | 1 + .../readerconfig_hwreader_conax.html | 2 + .../readerconfig_hwreader_cryptoworks.html | 2 + .../readerconfig_hwreader_deskey.html | 1 + .../readerconfig_hwreader_dre.html | 3 + .../readerconfig_hwreader_irdeto.html | 2 + .../readerconfig_hwreader_nagra.html | 13 + .../readerconfig_hwreader_nagracak7.html | 63 + .../readerconfig_hwreader_nano.html | 2 + .../readerconfig_hwreader_pincode.html | 1 + .../readerconfig_hwreader_rsakey.html | 1 + .../readerconfig_hwreader_sc8in1.html | 2 + .../readerconfig_hwreader_smargo.html | 2 + .../readerconfig_hwreader_tongfang.html | 5 + .../readerconfig_hwreader_viaccess.html | 4 + .../readerconfig_hwreader_videoguard.html | 67 + webif/readerconfig/readerconfig_ipv6bit.html | 1 + webif/readerconfig/readerconfig_lbweight.html | 2 + .../readerconfig/readerconfig_ncd524bit.html | 6 + .../readerconfig/readerconfig_ncd525bit.html | 6 + .../readerconfig_radegastbit.html | 4 + webif/readerconfig/readerconfig_scambit.html | 4 + webif/readerconfig/readerconfig_sid.html | 12 + .../readerconfig/readerconfig_sidlbokbit.html | 2 + webif/readerconfig/readerconfig_sidnobit.html | 1 + webif/readerconfig/readerconfig_sidokbit.html | 3 + webif/readerconfig/readerconfigaio.html | 101 + webif/readerconfig/readerinfo_gbox_remm.html | 17 + webif/readers/readerctypbit.html | 1 + webif/readers/readerctypnoicon.html | 1 + webif/readers/readerlabel.html | 1 + webif/readers/readernamebit.html | 1 + webif/readers/readernoicon.html | 1 + webif/readers/readers.html | 77 + webif/readers/readers_lblweightbit.html | 1 + webif/readers/readers_lblweightd.html | 1 + webif/readers/readers_lblweightu.html | 1 + webif/readers/readers_readerlist.html | 22 + .../readers_readerlist_entitlement.html | 1 + webif/readers/readers_readerlist_lbstat.html | 1 + webif/readers/readers_readerlist_refresh.html | 1 + .../readers/readers_readerlist_writeemm.html | 1 + webif/readers/readersaio.html | 78 + webif/readers/readersaio_readerlist.html | 23 + webif/readerstats/readerstats.html | 31 + webif/readerstats/readerstats_nostats.html | 3 + webif/readerstats/readerstats_statsbit.html | 1 + webif/readerstats/readerstatsinvalid.html | 4 + webif/readerstats/readerstatsnotfound.html | 4 + webif/readerstats/readerstatsrowbit.html | 1 + webif/readerstats/readerstatstimeoutbit.html | 4 + webif/savetemplates/savetemplates.html | 6 + webif/scanusb/scanusb.html | 17 + webif/scanusb/scanusb_pcscbit.html | 1 + webif/scanusb/scanusb_udevbit.html | 1 + webif/scanusb/scanusb_usbbit.html | 1 + webif/script/script.html | 37 + webif/services/services.html | 25 + webif/services/services_servicetabs.html | 9 + .../services_servicetabs_sidlist.html | 1 + webif/services_edit/services_edit.html | 21 + webif/services_edit/services_editaio.html | 24 + webif/shutdown/pre_shutdown.html | 13 + webif/shutdown/shutdown.html | 5 + webif/status/status.html | 60 + webif/status/status_cacheexaioinfo.html | 11 + webif/status/status_cacheexinfo.html | 10 + webif/status/status_cheadline.html | 1 + webif/status/status_cheadlineadd.html | 1 + webif/status/status_clientheadlinebit.html | 3 + webif/status/status_clientstatusbit.html | 14 + webif/status/status_currentchannel.html | 1 + webif/status/status_currentchannelbit.html | 1 + webif/status/status_currentchannelpic.html | 1 + webif/status/status_headline.html | 5 + webif/status/status_hidebutton.html | 1 + webif/status/status_killbutton.html | 1 + webif/status/status_lblvaluereaderbit.html | 1 + webif/status/status_lbvaluereaderproxy.html | 1 + webif/status/status_loghistory.html | 8 + webif/status/status_mheadline.html | 1 + webif/status/status_pheadline.html | 1 + webif/status/status_pheadlineadd.html | 1 + webif/status/status_reader.html | 1 + webif/status/status_readericon.html | 1 + webif/status/status_readerinfo.html | 58 + webif/status/status_restartbutton.html | 1 + webif/status/status_rheadline.html | 1 + webif/status/status_rheadlineadd.html | 1 + webif/status/status_sdebug.html | 18 + webif/status/status_sdebugaio.html | 19 + webif/status/status_sheadline.html | 1 + webif/status/status_systeminfo.html | 40 + webif/status/status_user.html | 1 + webif/status/status_usericon.html | 1 + webif/status/status_userinfo.html | 59 + webif/user_edit/user_edit.html | 80 + webif/user_edit/user_edit_anticasc.html | 51 + webif/user_edit/user_edit_cacheexaiobit.html | 101 + webif/user_edit/user_edit_cacheexbit.html | 51 + webif/user_edit/user_edit_cccam.html | 20 + webif/user_edit/user_edit_cwcycle.html | 2 + webif/user_edit/user_edit_monlevel.html | 11 + webif/user_edit/user_edit_sid.html | 11 + webif/user_edit/user_edit_sidnobit.html | 2 + webif/user_edit/user_edit_sidokbit.html | 3 + webif/user_edit/user_editaio.html | 80 + webif/userconfig/userconfig.html | 110 + webif/userconfig/userconfig_anticascbit.html | 1 + .../userconfig/userconfig_cwanticascthv.html | 2 + webif/userconfig/userconfig_cwcyclebit.html | 1 + webif/userconfig/userconfig_cwcyclethv.html | 1 + webif/userconfig/userconfig_entry.html | 25 + .../userconfig_entry_cwanticasctbv.html | 2 + .../userconfig_entry_cwcycletbv.html | 1 + .../userconfig_lastchannelicon.html | 1 + webif/userconfig/userconfig_newuserform.html | 7 + webif/userconfig/userconfig_notify.html | 1 + webif/userconfig/userconfig_usericon.html | 1 + webif/userconfig/userconfig_userlabel.html | 1 + webif/userconfig/userconfig_usernoicon.html | 1 + webif/wiki/wiki.json | 1 + webif/wiki/wikierror.json | 1 + webif/wiki/wikinotfound.json | 1 + webif/wiki/wikistatus.json | 1 + webif/wiki_gen | Bin 0 -> 35256 bytes webif/wiki_gen.c | 976 ++ wiki | 1 + 723 files changed, 211326 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 CODING.RULES.txt create mode 100644 COPYING create mode 100644 Distribution/doc/example/oscam.ac create mode 100644 Distribution/doc/example/oscam.cacheex create mode 100644 Distribution/doc/example/oscam.cert create mode 100644 Distribution/doc/example/oscam.conf create mode 100644 Distribution/doc/example/oscam.dvbapi create mode 100644 Distribution/doc/example/oscam.guess create mode 100644 Distribution/doc/example/oscam.ird create mode 100644 Distribution/doc/example/oscam.provid create mode 100644 Distribution/doc/example/oscam.server create mode 100644 Distribution/doc/example/oscam.services create mode 100644 Distribution/doc/example/oscam.srvid create mode 100644 Distribution/doc/example/oscam.tiers create mode 100644 Distribution/doc/example/oscam.user create mode 100644 Distribution/doc/example/oscam.whitelist create mode 100644 Distribution/doc/html/list_smargo.1.html create mode 100644 Distribution/doc/html/oscam.1.html create mode 100644 Distribution/doc/html/oscam.ac.5.html create mode 100644 Distribution/doc/html/oscam.cacheex.5.html create mode 100644 Distribution/doc/html/oscam.cert.5.html create mode 100644 Distribution/doc/html/oscam.conf.5.html create mode 100644 Distribution/doc/html/oscam.dvbapi.5.html create mode 100644 Distribution/doc/html/oscam.guess.5.html create mode 100644 Distribution/doc/html/oscam.ird.5.html create mode 100644 Distribution/doc/html/oscam.provid.5.html create mode 100644 Distribution/doc/html/oscam.ratelimit.5.html create mode 100644 Distribution/doc/html/oscam.server.5.html create mode 100644 Distribution/doc/html/oscam.services.5.html create mode 100644 Distribution/doc/html/oscam.srvid.5.html create mode 100644 Distribution/doc/html/oscam.srvid2.5.html create mode 100644 Distribution/doc/html/oscam.tiers.5.html create mode 100644 Distribution/doc/html/oscam.user.5.html create mode 100644 Distribution/doc/html/oscam.whitelist.5.html create mode 100644 Distribution/doc/man/list_smargo.1 create mode 100644 Distribution/doc/man/oscam.1 create mode 100644 Distribution/doc/man/oscam.ac.5 create mode 100644 Distribution/doc/man/oscam.cacheex.5 create mode 100644 Distribution/doc/man/oscam.cert.5 create mode 100644 Distribution/doc/man/oscam.conf.5 create mode 100644 Distribution/doc/man/oscam.dvbapi.5 create mode 100644 Distribution/doc/man/oscam.guess.5 create mode 100644 Distribution/doc/man/oscam.ird.5 create mode 100644 Distribution/doc/man/oscam.provid.5 create mode 100644 Distribution/doc/man/oscam.ratelimit.5 create mode 100644 Distribution/doc/man/oscam.server.5 create mode 100644 Distribution/doc/man/oscam.services.5 create mode 100644 Distribution/doc/man/oscam.srvid.5 create mode 100644 Distribution/doc/man/oscam.srvid2.5 create mode 100644 Distribution/doc/man/oscam.tiers.5 create mode 100644 Distribution/doc/man/oscam.user.5 create mode 100644 Distribution/doc/man/oscam.whitelist.5 create mode 100644 Distribution/doc/txt/list_smargo.txt create mode 100644 Distribution/doc/txt/oscam.ac.txt create mode 100644 Distribution/doc/txt/oscam.cacheex.txt create mode 100644 Distribution/doc/txt/oscam.cert.txt create mode 100644 Distribution/doc/txt/oscam.conf.txt create mode 100644 Distribution/doc/txt/oscam.dvbapi.txt create mode 100644 Distribution/doc/txt/oscam.guess.txt create mode 100644 Distribution/doc/txt/oscam.ird.txt create mode 100644 Distribution/doc/txt/oscam.provid.txt create mode 100644 Distribution/doc/txt/oscam.ratelimit.txt create mode 100644 Distribution/doc/txt/oscam.server.txt create mode 100644 Distribution/doc/txt/oscam.services.txt create mode 100644 Distribution/doc/txt/oscam.srvid.txt create mode 100644 Distribution/doc/txt/oscam.srvid2.txt create mode 100644 Distribution/doc/txt/oscam.tiers.txt create mode 100644 Distribution/doc/txt/oscam.txt create mode 100644 Distribution/doc/txt/oscam.user.txt create mode 100644 Distribution/doc/txt/oscam.whitelist.txt create mode 100644 Distribution/monitor/mpcsmon-src-0.6.tar.bz2 create mode 100644 Distribution/monitor/mpcsmon.sh create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 Makefile.extra create mode 100644 README.build create mode 100644 README.config create mode 100644 README.dvbapi_protocol create mode 100644 README.md create mode 100644 SoftCam.Key create mode 100644 config.h create mode 100644 config.sh create mode 100644 cscrypt/CMakeLists.txt create mode 100644 cscrypt/Makefile create mode 100644 cscrypt/aes.c create mode 100644 cscrypt/aes.h create mode 100644 cscrypt/bn.h create mode 100644 cscrypt/bn_add.c create mode 100644 cscrypt/bn_asm.c create mode 100644 cscrypt/bn_ctx.c create mode 100644 cscrypt/bn_div.c create mode 100644 cscrypt/bn_exp.c create mode 100644 cscrypt/bn_lcl.h create mode 100644 cscrypt/bn_lib.c create mode 100644 cscrypt/bn_mul.c create mode 100644 cscrypt/bn_print.c create mode 100644 cscrypt/bn_shift.c create mode 100644 cscrypt/bn_sqr.c create mode 100644 cscrypt/bn_word.c create mode 100644 cscrypt/buffer.h create mode 100644 cscrypt/des.c create mode 100644 cscrypt/des.h create mode 100644 cscrypt/fast_aes.c create mode 100644 cscrypt/fast_aes.h create mode 100644 cscrypt/i_cbc.c create mode 100644 cscrypt/i_ecb.c create mode 100644 cscrypt/i_skey.c create mode 100644 cscrypt/idea.h create mode 100644 cscrypt/idea_lcl.h create mode 100644 cscrypt/md5.c create mode 100644 cscrypt/md5.h create mode 100644 cscrypt/mdc2.c create mode 100644 cscrypt/mdc2.h create mode 100644 cscrypt/mem.c create mode 100644 cscrypt/openssl_mods.h create mode 100644 cscrypt/rc6.c create mode 100644 cscrypt/rc6.h create mode 100644 cscrypt/sha1.c create mode 100644 cscrypt/sha1.h create mode 100644 cscrypt/sha256.c create mode 100644 cscrypt/sha256.h create mode 100644 csctapi/CMakeLists.txt create mode 100644 csctapi/Makefile create mode 100644 csctapi/atr.c create mode 100644 csctapi/atr.h create mode 100644 csctapi/cardreaders.h create mode 100644 csctapi/icc_async.c create mode 100644 csctapi/icc_async.h create mode 100644 csctapi/ifd_amsmc.c create mode 100644 csctapi/ifd_azbox.c create mode 100644 csctapi/ifd_cool.c create mode 100644 csctapi/ifd_db2com.c create mode 100644 csctapi/ifd_db2com.h create mode 100644 csctapi/ifd_drecas.c create mode 100644 csctapi/ifd_drecas.h create mode 100644 csctapi/ifd_mp35.c create mode 100644 csctapi/ifd_pcsc.c create mode 100644 csctapi/ifd_phoenix.c create mode 100644 csctapi/ifd_phoenix.h create mode 100644 csctapi/ifd_sc8in1.c create mode 100644 csctapi/ifd_sci.c create mode 100644 csctapi/ifd_sci_global.h create mode 100644 csctapi/ifd_sci_ioctl.h create mode 100644 csctapi/ifd_smargo.c create mode 100644 csctapi/ifd_smartreader.c create mode 100644 csctapi/ifd_smartreader_types.h create mode 100644 csctapi/ifd_stapi.c create mode 100644 csctapi/ifd_stinger.c create mode 100644 csctapi/io_serial.c create mode 100644 csctapi/io_serial.h create mode 100644 csctapi/protocol_t0.c create mode 100644 csctapi/protocol_t0.h create mode 100644 csctapi/protocol_t1.c create mode 100644 devtools/README create mode 100644 devtools/check_cmdline_opts.sh create mode 100644 devtools/check_config_tables.sh create mode 100644 devtools/extract_config.sh create mode 100644 extapi/README create mode 100644 extapi/coolapi.h create mode 100644 extapi/cygwin/SCardErr.h create mode 100644 extapi/cygwin/WinSCard.h create mode 100644 extapi/cygwin/WinSmCrd.h create mode 100644 extapi/linux/README create mode 100644 extapi/linux/serial.h create mode 100644 extapi/linux/tty_flags.h create mode 100644 extapi/openxcas/libOpenXCASAPI.a create mode 100644 extapi/openxcas/openxcas.conf create mode 100644 extapi/openxcas/openxcas_api.h create mode 100644 extapi/openxcas/openxcas_message.h create mode 100644 extapi/openxcas/openxcas_smartcard.h create mode 100644 gitupdate.sh create mode 100644 globals.h create mode 100644 globals.h.orig create mode 100644 images/image1.jpg create mode 100644 minilzo/CMakeLists.txt create mode 100644 minilzo/Makefile create mode 100644 minilzo/README.LZO create mode 100644 minilzo/lzoconf.h create mode 100644 minilzo/lzodefs.h create mode 100644 minilzo/minilzo.c create mode 100644 minilzo/minilzo.h create mode 100644 module-anticasc.c create mode 100644 module-anticasc.h create mode 100644 module-cacheex.c create mode 100644 module-cacheex.h create mode 100644 module-camd33.c create mode 100644 module-camd35-cacheex.c create mode 100644 module-camd35-cacheex.c.orig create mode 100644 module-camd35-cacheex.h create mode 100644 module-camd35.c create mode 100644 module-camd35.h create mode 100644 module-cccam-cacheex.c create mode 100644 module-cccam-cacheex.h create mode 100644 module-cccam-data.h create mode 100644 module-cccam.c create mode 100644 module-cccam.h create mode 100644 module-cccshare.c create mode 100644 module-cccshare.h create mode 100644 module-constcw.c create mode 100644 module-csp.c create mode 100644 module-cw-cycle-check.c create mode 100644 module-cw-cycle-check.h create mode 100644 module-dvbapi-azbox.c create mode 100644 module-dvbapi-azbox.h create mode 100644 module-dvbapi-chancache.c create mode 100644 module-dvbapi-chancache.h create mode 100644 module-dvbapi-coolapi-legacy.c create mode 100644 module-dvbapi-coolapi.c create mode 100644 module-dvbapi-coolapi.h create mode 100644 module-dvbapi-mca.c create mode 100644 module-dvbapi-mca.h create mode 100644 module-dvbapi-stapi.c create mode 100644 module-dvbapi-stapi.h create mode 100644 module-dvbapi-stapi5.c create mode 100644 module-dvbapi.c create mode 100644 module-dvbapi.h create mode 100644 module-emulator-biss.c create mode 100644 module-emulator-biss.h create mode 100644 module-emulator-cryptoworks.c create mode 100644 module-emulator-cryptoworks.h create mode 100644 module-emulator-director.c create mode 100644 module-emulator-director.h create mode 100644 module-emulator-irdeto.c create mode 100644 module-emulator-irdeto.h create mode 100644 module-emulator-nagravision.c create mode 100644 module-emulator-nagravision.h create mode 100644 module-emulator-omnicrypt.c create mode 100644 module-emulator-omnicrypt.h create mode 100644 module-emulator-osemu.c create mode 100644 module-emulator-osemu.h create mode 100644 module-emulator-powervu.c create mode 100644 module-emulator-powervu.h create mode 100644 module-emulator-viaccess.c create mode 100644 module-emulator-viaccess.h create mode 100644 module-emulator.c create mode 100644 module-gbox-cards.c create mode 100644 module-gbox-cards.h create mode 100644 module-gbox-helper.c create mode 100644 module-gbox-helper.h create mode 100644 module-gbox-remm.c create mode 100644 module-gbox-remm.h create mode 100644 module-gbox-sms.c create mode 100644 module-gbox-sms.h create mode 100644 module-gbox.c create mode 100644 module-gbox.h create mode 100644 module-ghttp.c create mode 100644 module-lcd.c create mode 100644 module-lcd.h create mode 100644 module-led.c create mode 100644 module-led.h create mode 100644 module-monitor.c create mode 100644 module-monitor.h create mode 100644 module-newcamd-des.c create mode 100644 module-newcamd-des.h create mode 100644 module-newcamd.c create mode 100644 module-newcamd.h create mode 100644 module-pandora.c create mode 100644 module-radegast.c create mode 100644 module-scam.c create mode 100644 module-serial.c create mode 100644 module-stat.c create mode 100644 module-stat.h create mode 100644 module-streamrelay.c create mode 100644 module-streamrelay.h create mode 100644 module-webif-lib.c create mode 100644 module-webif-lib.h create mode 100644 module-webif-tpl.c create mode 100644 module-webif-tpl.h create mode 100644 module-webif.c create mode 100644 module-webif.c.orig create mode 100644 module-webif.h create mode 100644 modules.h create mode 100644 oscam-aes.c create mode 100644 oscam-aes.h create mode 100644 oscam-array.c create mode 100644 oscam-array.h create mode 100644 oscam-cache.c create mode 100644 oscam-cache.h create mode 100644 oscam-chk.c create mode 100644 oscam-chk.c.orig create mode 100644 oscam-chk.c.rej create mode 100644 oscam-chk.h create mode 100644 oscam-client.c create mode 100644 oscam-client.h create mode 100644 oscam-conf-chk.c create mode 100644 oscam-conf-chk.h create mode 100644 oscam-conf-mk.c create mode 100644 oscam-conf-mk.h create mode 100644 oscam-conf.c create mode 100644 oscam-conf.h create mode 100644 oscam-config-account.c create mode 100644 oscam-config-global.c create mode 100644 oscam-config-reader.c create mode 100644 oscam-config.c create mode 100644 oscam-config.c.orig create mode 100644 oscam-config.h create mode 100644 oscam-ecm.c create mode 100644 oscam-ecm.c.orig create mode 100644 oscam-ecm.h create mode 100644 oscam-emm-cache.c create mode 100644 oscam-emm-cache.h create mode 100644 oscam-emm.c create mode 100644 oscam-emm.h create mode 100644 oscam-failban.c create mode 100644 oscam-failban.h create mode 100644 oscam-files.c create mode 100644 oscam-files.h create mode 100644 oscam-garbage.c create mode 100644 oscam-garbage.h create mode 100644 oscam-hashtable.c create mode 100644 oscam-hashtable.h create mode 100644 oscam-llist.c create mode 100644 oscam-llist.h create mode 100644 oscam-lock.c create mode 100644 oscam-lock.h create mode 100644 oscam-log-reader.c create mode 100644 oscam-log-reader.h create mode 100644 oscam-log.c create mode 100644 oscam-log.h create mode 100644 oscam-net.c create mode 100644 oscam-net.h create mode 100644 oscam-reader.c create mode 100644 oscam-reader.h create mode 100644 oscam-signing.c create mode 100644 oscam-signing.h create mode 100644 oscam-simples.c create mode 100644 oscam-string-isotables.h create mode 100644 oscam-string.c create mode 100644 oscam-string.h create mode 100644 oscam-time.c create mode 100644 oscam-time.h create mode 100644 oscam-work.c create mode 100644 oscam-work.h create mode 100644 oscam.c create mode 100644 reader-bulcrypt.c create mode 100644 reader-common.c create mode 100644 reader-common.h create mode 100644 reader-conax.c create mode 100644 reader-cryptoworks.c create mode 100644 reader-dgcrypt.c create mode 100644 reader-dre-cas.c create mode 100644 reader-dre-common.c create mode 100644 reader-dre-common.h create mode 100644 reader-dre-st20.c create mode 100644 reader-dre-st20.h create mode 100644 reader-dre.c create mode 100644 reader-griffin.c create mode 100644 reader-irdeto.c create mode 100644 reader-irdeto.h create mode 100644 reader-nagra-common.c create mode 100644 reader-nagra-common.h create mode 100644 reader-nagra.c create mode 100644 reader-nagracak7.c create mode 100644 reader-seca.c create mode 100644 reader-tongfang.c create mode 100644 reader-viaccess.c create mode 100644 reader-videoguard-common.c create mode 100644 reader-videoguard-common.h create mode 100644 reader-videoguard1.c create mode 100644 reader-videoguard12.c create mode 100644 reader-videoguard2.c create mode 100644 readers.h create mode 100644 tests.c create mode 100644 tommyDS_hashlin/tommychain.h create mode 100644 tommyDS_hashlin/tommyhash.c create mode 100644 tommyDS_hashlin/tommyhash.h create mode 100644 tommyDS_hashlin/tommyhashlin.c create mode 100644 tommyDS_hashlin/tommyhashlin.h create mode 100644 tommyDS_hashlin/tommylist.c create mode 100644 tommyDS_hashlin/tommylist.h create mode 100644 tommyDS_hashlin/tommytypes.h create mode 100644 toolchains/toolchain-arm-coolstream.cmake create mode 100644 toolchains/toolchain-arm-dockstar-openwrt.cmake create mode 100644 toolchains/toolchain-arm-friendlyarm.cmake create mode 100644 toolchains/toolchain-arm-mca.cmake create mode 100644 toolchains/toolchain-arm-none.cmake create mode 100644 toolchains/toolchain-arm-nslu2-openwrt.cmake create mode 100644 toolchains/toolchain-arm-nslu2-unslung.cmake create mode 100644 toolchains/toolchain-arm-su980.cmake create mode 100644 toolchains/toolchain-arm-wrt350nv2-openwrt.cmake create mode 100644 toolchains/toolchain-mips-agv2_w.cmake create mode 100644 toolchains/toolchain-mips-azbox.cmake create mode 100644 toolchains/toolchain-mips-dir825.cmake create mode 100644 toolchains/toolchain-mips-fonera2.cmake create mode 100644 toolchains/toolchain-mips-tuxbox.cmake create mode 100644 toolchains/toolchain-mips-wrt54g.cmake create mode 100644 toolchains/toolchain-mipsel-tuxbox-broken-pthread.cmake create mode 100644 toolchains/toolchain-mipsel-tuxbox-linux-gnu.cmake create mode 100644 toolchains/toolchain-mipsel-tuxbox.cmake create mode 100644 toolchains/toolchain-powerpc-tuxbox.cmake create mode 100644 toolchains/toolchain-sh4-amino.cmake create mode 100644 toolchains/toolchain-sh4-qboxhd.cmake create mode 100644 toolchains/toolchain-sh4-tuxbox-stapi.cmake create mode 100644 toolchains/toolchain-sh4-tuxbox.cmake create mode 100644 toolchains/toolchain-sparc-padre.cmake create mode 100644 toolchains/toolchain-tripledragon.cmake create mode 100644 utils/CMakeLists.txt create mode 100644 utils/list_smargo.c create mode 100644 webif/.gitignore create mode 100644 webif/Makefile create mode 100644 webif/README create mode 100644 webif/api.json/cacheex.json create mode 100644 webif/api.json/cacheexaiobit.json create mode 100644 webif/api.json/cacheexbit.json create mode 100644 webif/api.json/entitlementbit.json create mode 100644 webif/api.json/entitlements.json create mode 100644 webif/api.json/footer.json create mode 100644 webif/api.json/header.json create mode 100644 webif/api.json/reader.json create mode 100644 webif/api.json/readerbit.json create mode 100644 webif/api.json/status.json create mode 100644 webif/api.json/status_statusbits.json create mode 100644 webif/api.json/user.json create mode 100644 webif/api.json/userbit.json create mode 100644 webif/api.xml/cccamcardlist.xml create mode 100644 webif/api.xml/cccamcardlist_cardlist.xml create mode 100644 webif/api.xml/cccamcardlist_cardlist_nodelist.xml create mode 100644 webif/api.xml/cccamcardlist_cardlist_providerlist.xml create mode 100644 webif/api.xml/confirmation.xml create mode 100644 webif/api.xml/error.xml create mode 100644 webif/api.xml/failban.xml create mode 100644 webif/api.xml/failban_failbanrow.xml create mode 100644 webif/api.xml/file.xml create mode 100644 webif/api.xml/footer.xml create mode 100644 webif/api.xml/header.xml create mode 100644 webif/api.xml/readers.xml create mode 100644 webif/api.xml/readers_readerlist.xml create mode 100644 webif/api.xml/readerstats.xml create mode 100644 webif/api.xml/readerstats_ecmstats.xml create mode 100644 webif/api.xml/readerstats_emmstats.xml create mode 100644 webif/api.xml/status.xml create mode 100644 webif/api.xml/status_statusbits.xml create mode 100644 webif/api.xml/userconfiglist.xml create mode 100644 webif/api.xml/userconfiglist_userconfigs.xml create mode 100644 webif/api.xml/useredit.xml create mode 100644 webif/cacheex/cacheex.html create mode 100644 webif/cacheex/cacheex_tablerow.html create mode 100644 webif/cacheexaio/cacheex.html create mode 100644 webif/cacheexaio/cacheex_tablerow.html create mode 100644 webif/cacheexaio/cacheex_tablerow_stats.html create mode 100644 webif/config/anticasc.html create mode 100644 webif/config/cache.html create mode 100644 webif/config/cache_cacheexaiocsp.html create mode 100644 webif/config/cache_cacheexcsp.html create mode 100644 webif/config/cache_cwcycle.html create mode 100644 webif/config/cacheaio.html create mode 100644 webif/config/camd33.html create mode 100644 webif/config/camd35.html create mode 100644 webif/config/camd35tcp.html create mode 100644 webif/config/cccam.html create mode 100644 webif/config/cccam_control.html create mode 100644 webif/config/cccreshare.html create mode 100644 webif/config/config.html create mode 100644 webif/config/dvbapi.html create mode 100644 webif/config/dvbapi_demuxerfix.html create mode 100644 webif/config/dvbapi_extended_cw_api.html create mode 100644 webif/config/gbox.html create mode 100644 webif/config/global.html create mode 100644 webif/config/global_cacheex_aio_logging.html create mode 100644 webif/config/global_enableledbit.html create mode 100644 webif/config/global_getblockemmauprovid.html create mode 100644 webif/config/global_localcards.html create mode 100644 webif/config/global_suppresscmd08.html create mode 100644 webif/config/global_unlockparental.html create mode 100644 webif/config/lcd.html create mode 100644 webif/config/loadbalancer.html create mode 100644 webif/config/loadbalancer_control.html create mode 100644 webif/config/menu.html create mode 100644 webif/config/menu_anticasc.html create mode 100644 webif/config/menu_camd33.html create mode 100644 webif/config/menu_camd35.html create mode 100644 webif/config/menu_camd35tcp.html create mode 100644 webif/config/menu_cccam.html create mode 100644 webif/config/menu_cmcaptioncwc.html create mode 100644 webif/config/menu_dvbapi.html create mode 100644 webif/config/menu_gbox.html create mode 100644 webif/config/menu_lcd.html create mode 100644 webif/config/menu_loadbalancer.html create mode 100644 webif/config/menu_monitor.html create mode 100644 webif/config/menu_newcamd.html create mode 100644 webif/config/menu_radegast.html create mode 100644 webif/config/menu_scam.html create mode 100644 webif/config/menu_serial.html create mode 100644 webif/config/menu_streamrelay.html create mode 100644 webif/config/monitor.html create mode 100644 webif/config/newcamd.html create mode 100644 webif/config/radegast.html create mode 100644 webif/config/scam.html create mode 100644 webif/config/serial.html create mode 100644 webif/config/serial_devices.html create mode 100644 webif/config/streamrelay.html create mode 100644 webif/config/streamrelay_emusettings.html create mode 100644 webif/config/webif.html create mode 100644 webif/config/webif_httpssl.html create mode 100644 webif/config/webif_show_jquery.html create mode 100644 webif/config/webif_showcacheexinfo.html create mode 100644 webif/emm/emm.html create mode 100644 webif/emm_running/emm_running.html create mode 100644 webif/entitlements/entitlements.html create mode 100644 webif/entitlements/entitlements_bit.html create mode 100644 webif/entitlements/entitlements_bit_nds.html create mode 100644 webif/entitlements/entitlements_cccambit.html create mode 100644 webif/entitlements/entitlements_cccambit_statsentry.html create mode 100644 webif/entitlements/entitlements_genericbit.html create mode 100644 webif/entitlements/entitlements_itembit.html create mode 100644 webif/failban/failban.html create mode 100644 webif/failban/failban_failbanrow.html create mode 100644 webif/files/file.html create mode 100644 webif/files/file_edit_css.html create mode 100644 webif/files/file_filterform.html create mode 100644 webif/files/file_writeprotection.html create mode 100644 webif/files/menu.html create mode 100644 webif/files/menu_anticasc.html create mode 100644 webif/files/menu_constantcw.html create mode 100644 webif/files/menu_dvbapi.html create mode 100644 webif/files/menu_fakecws.html create mode 100644 webif/files/menu_gbox.html create mode 100644 webif/files/menu_softcamkey.html create mode 100644 webif/files/menu_twin.html create mode 100644 webif/ghttp/autoconf.html create mode 100644 webif/ghttp/pre_autoconf.html create mode 100644 webif/graph/graph.svg create mode 100644 webif/images/ICARRL.svg create mode 100644 webif/images/ICARRR.svg create mode 100644 webif/images/ICDEL.svg create mode 100644 webif/images/ICDIS.svg create mode 100644 webif/images/ICEDI.svg create mode 100644 webif/images/ICEMM.svg create mode 100644 webif/images/ICENA.svg create mode 100644 webif/images/ICENT.svg create mode 100644 webif/images/ICHID.svg create mode 100644 webif/images/ICKIL.svg create mode 100644 webif/images/ICMLOGO.svg create mode 100644 webif/images/ICREF.svg create mode 100644 webif/images/ICRES.svg create mode 100644 webif/images/ICSHW.svg create mode 100644 webif/images/ICSPAC.gif create mode 100644 webif/images/ICSTA.svg create mode 100644 webif/images/ICSTART.svg create mode 100644 webif/images/ICSTOP.svg create mode 100644 webif/images/favicon.ico create mode 100644 webif/include/body.html create mode 100644 webif/include/cccamentitlements.html create mode 100644 webif/include/cccamentitletooltip.html create mode 100644 webif/include/css.css create mode 100644 webif/include/footer.html create mode 100644 webif/include/foundentitlements.html create mode 100644 webif/include/header.html create mode 100644 webif/include/header_short.html create mode 100644 webif/include/jquery.js create mode 100644 webif/include/jscript.js create mode 100644 webif/include/logobit.html create mode 100644 webif/include/logobit_img.html create mode 100644 webif/include/menu.html create mode 100644 webif/include/menu_cacheexmenuitem.html create mode 100644 webif/include/message.html create mode 100644 webif/include/message_bit.html create mode 100644 webif/include/noentitlements.html create mode 100644 webif/include/poll.html create mode 100644 webif/include/pollingset.html create mode 100644 webif/include/protocamd3aiopic.html create mode 100644 webif/include/protocccampic.html create mode 100644 webif/include/protonewcamdpic.html create mode 100644 webif/include/protootherpic.html create mode 100644 webif/include/refresh.html create mode 100644 webif/logmenu/log_clearlog.html create mode 100644 webif/logmenu/log_clearuserlog.html create mode 100644 webif/logmenu/log_disablelogmenu.html create mode 100644 webif/logmenu/log_filterform.html create mode 100644 webif/logmenu/log_logmenuonoff.html create mode 100644 webif/logpage/logpage.html create mode 100644 webif/logpage/logpage_debugmenu.html create mode 100644 webif/logpage/logpage_menu.html create mode 100644 webif/logpage/logpage_sizemenu.html create mode 100644 webif/pages_gen.c create mode 100644 webif/pages_index.txt create mode 100644 webif/pages_index_check create mode 100644 webif/pages_mkdep create mode 100644 webif/pages_wiki.c create mode 100644 webif/pages_wiki.h create mode 100644 webif/readerconfig/readerconfig.html create mode 100644 webif/readerconfig/readerconfig_cacheexaiobit.html create mode 100644 webif/readerconfig/readerconfig_cacheexbit.html create mode 100644 webif/readerconfig/readerconfig_camd35bit.html create mode 100644 webif/readerconfig/readerconfig_cccambit.html create mode 100644 webif/readerconfig/readerconfig_cs378xbit.html create mode 100644 webif/readerconfig/readerconfig_emubit.html create mode 100644 webif/readerconfig/readerconfig_gboxbit.html create mode 100644 webif/readerconfig/readerconfig_gboxcccresharebit.html create mode 100644 webif/readerconfig/readerconfig_ghttpbit.html create mode 100644 webif/readerconfig/readerconfig_hopbit.html create mode 100644 webif/readerconfig/readerconfig_hwreader.html create mode 100644 webif/readerconfig/readerconfig_hwreader_boxkey.html create mode 100644 webif/readerconfig/readerconfig_hwreader_conax.html create mode 100644 webif/readerconfig/readerconfig_hwreader_cryptoworks.html create mode 100644 webif/readerconfig/readerconfig_hwreader_deskey.html create mode 100644 webif/readerconfig/readerconfig_hwreader_dre.html create mode 100644 webif/readerconfig/readerconfig_hwreader_irdeto.html create mode 100644 webif/readerconfig/readerconfig_hwreader_nagra.html create mode 100644 webif/readerconfig/readerconfig_hwreader_nagracak7.html create mode 100644 webif/readerconfig/readerconfig_hwreader_nano.html create mode 100644 webif/readerconfig/readerconfig_hwreader_pincode.html create mode 100644 webif/readerconfig/readerconfig_hwreader_rsakey.html create mode 100644 webif/readerconfig/readerconfig_hwreader_sc8in1.html create mode 100644 webif/readerconfig/readerconfig_hwreader_smargo.html create mode 100644 webif/readerconfig/readerconfig_hwreader_tongfang.html create mode 100644 webif/readerconfig/readerconfig_hwreader_viaccess.html create mode 100644 webif/readerconfig/readerconfig_hwreader_videoguard.html create mode 100644 webif/readerconfig/readerconfig_ipv6bit.html create mode 100644 webif/readerconfig/readerconfig_lbweight.html create mode 100644 webif/readerconfig/readerconfig_ncd524bit.html create mode 100644 webif/readerconfig/readerconfig_ncd525bit.html create mode 100644 webif/readerconfig/readerconfig_radegastbit.html create mode 100644 webif/readerconfig/readerconfig_scambit.html create mode 100644 webif/readerconfig/readerconfig_sid.html create mode 100644 webif/readerconfig/readerconfig_sidlbokbit.html create mode 100644 webif/readerconfig/readerconfig_sidnobit.html create mode 100644 webif/readerconfig/readerconfig_sidokbit.html create mode 100644 webif/readerconfig/readerconfigaio.html create mode 100644 webif/readerconfig/readerinfo_gbox_remm.html create mode 100644 webif/readers/readerctypbit.html create mode 100644 webif/readers/readerctypnoicon.html create mode 100644 webif/readers/readerlabel.html create mode 100644 webif/readers/readernamebit.html create mode 100644 webif/readers/readernoicon.html create mode 100644 webif/readers/readers.html create mode 100644 webif/readers/readers_lblweightbit.html create mode 100644 webif/readers/readers_lblweightd.html create mode 100644 webif/readers/readers_lblweightu.html create mode 100644 webif/readers/readers_readerlist.html create mode 100644 webif/readers/readers_readerlist_entitlement.html create mode 100644 webif/readers/readers_readerlist_lbstat.html create mode 100644 webif/readers/readers_readerlist_refresh.html create mode 100644 webif/readers/readers_readerlist_writeemm.html create mode 100644 webif/readers/readersaio.html create mode 100644 webif/readers/readersaio_readerlist.html create mode 100644 webif/readerstats/readerstats.html create mode 100644 webif/readerstats/readerstats_nostats.html create mode 100644 webif/readerstats/readerstats_statsbit.html create mode 100644 webif/readerstats/readerstatsinvalid.html create mode 100644 webif/readerstats/readerstatsnotfound.html create mode 100644 webif/readerstats/readerstatsrowbit.html create mode 100644 webif/readerstats/readerstatstimeoutbit.html create mode 100644 webif/savetemplates/savetemplates.html create mode 100644 webif/scanusb/scanusb.html create mode 100644 webif/scanusb/scanusb_pcscbit.html create mode 100644 webif/scanusb/scanusb_udevbit.html create mode 100644 webif/scanusb/scanusb_usbbit.html create mode 100644 webif/script/script.html create mode 100644 webif/services/services.html create mode 100644 webif/services/services_servicetabs.html create mode 100644 webif/services/services_servicetabs_sidlist.html create mode 100644 webif/services_edit/services_edit.html create mode 100644 webif/services_edit/services_editaio.html create mode 100644 webif/shutdown/pre_shutdown.html create mode 100644 webif/shutdown/shutdown.html create mode 100644 webif/status/status.html create mode 100644 webif/status/status_cacheexaioinfo.html create mode 100644 webif/status/status_cacheexinfo.html create mode 100644 webif/status/status_cheadline.html create mode 100644 webif/status/status_cheadlineadd.html create mode 100644 webif/status/status_clientheadlinebit.html create mode 100644 webif/status/status_clientstatusbit.html create mode 100644 webif/status/status_currentchannel.html create mode 100644 webif/status/status_currentchannelbit.html create mode 100644 webif/status/status_currentchannelpic.html create mode 100644 webif/status/status_headline.html create mode 100644 webif/status/status_hidebutton.html create mode 100644 webif/status/status_killbutton.html create mode 100644 webif/status/status_lblvaluereaderbit.html create mode 100644 webif/status/status_lbvaluereaderproxy.html create mode 100644 webif/status/status_loghistory.html create mode 100644 webif/status/status_mheadline.html create mode 100644 webif/status/status_pheadline.html create mode 100644 webif/status/status_pheadlineadd.html create mode 100644 webif/status/status_reader.html create mode 100644 webif/status/status_readericon.html create mode 100644 webif/status/status_readerinfo.html create mode 100644 webif/status/status_restartbutton.html create mode 100644 webif/status/status_rheadline.html create mode 100644 webif/status/status_rheadlineadd.html create mode 100644 webif/status/status_sdebug.html create mode 100644 webif/status/status_sdebugaio.html create mode 100644 webif/status/status_sheadline.html create mode 100644 webif/status/status_systeminfo.html create mode 100644 webif/status/status_user.html create mode 100644 webif/status/status_usericon.html create mode 100644 webif/status/status_userinfo.html create mode 100644 webif/user_edit/user_edit.html create mode 100644 webif/user_edit/user_edit_anticasc.html create mode 100644 webif/user_edit/user_edit_cacheexaiobit.html create mode 100644 webif/user_edit/user_edit_cacheexbit.html create mode 100644 webif/user_edit/user_edit_cccam.html create mode 100644 webif/user_edit/user_edit_cwcycle.html create mode 100644 webif/user_edit/user_edit_monlevel.html create mode 100644 webif/user_edit/user_edit_sid.html create mode 100644 webif/user_edit/user_edit_sidnobit.html create mode 100644 webif/user_edit/user_edit_sidokbit.html create mode 100644 webif/user_edit/user_editaio.html create mode 100644 webif/userconfig/userconfig.html create mode 100644 webif/userconfig/userconfig_anticascbit.html create mode 100644 webif/userconfig/userconfig_cwanticascthv.html create mode 100644 webif/userconfig/userconfig_cwcyclebit.html create mode 100644 webif/userconfig/userconfig_cwcyclethv.html create mode 100644 webif/userconfig/userconfig_entry.html create mode 100644 webif/userconfig/userconfig_entry_cwanticasctbv.html create mode 100644 webif/userconfig/userconfig_entry_cwcycletbv.html create mode 100644 webif/userconfig/userconfig_lastchannelicon.html create mode 100644 webif/userconfig/userconfig_newuserform.html create mode 100644 webif/userconfig/userconfig_notify.html create mode 100644 webif/userconfig/userconfig_usericon.html create mode 100644 webif/userconfig/userconfig_userlabel.html create mode 100644 webif/userconfig/userconfig_usernoicon.html create mode 100644 webif/wiki/wiki.json create mode 100644 webif/wiki/wikierror.json create mode 100644 webif/wiki/wikinotfound.json create mode 100644 webif/wiki/wikistatus.json create mode 100644 webif/wiki_gen create mode 100644 webif/wiki_gen.c create mode 160000 wiki diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b0ae39 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# generated binaries +Distribution/oscam-* +Distribution/list_smargo-* + +# backup/swap file for vi +*~ +*.swp + +# IDE resources +.vscode + +# build directories +/build/ + +# Currently enabled config items +webif/is_defined.txt + +# Testing program +tests.bin +tests.bin.debug + +# Patch files +*.patch +*.diff diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9321b65 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "wiki"] + path = wiki + url = https://git.streamboard.tv/common/oscam.wiki.git + branch = main + shallow = true + depends = WEBIF_WIKI diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..86a08f6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,1045 @@ +#----------------------- minimum version of cmake to use ------------ + +cmake_minimum_required(VERSION 2.8.12...3.10) + +project (OSCam C) + +cmake_policy(SET CMP0003 NEW) +if(POLICY CMP0042) + cmake_policy(SET CMP0042 NEW) +endif() +if(POLICY CMP0054) + cmake_policy(SET CMP0054 NEW) +endif() + +#----------------------- detect system ------------------------------ + +if (CMAKE_CROSSCOMPILING) + if (OSCAM_SYSTEM_NAME MATCHES "Tuxbox") + set (OSCamOperatingSystem "Tuxbox") + elseif (OSCAM_SYSTEM_NAME MATCHES "TripleDragon") + set (OSCamOperatingSystem "TripleDragon") + elseif (OSCAM_SYSTEM_NAME MATCHES "Coolstream") + set (OSCamOperatingSystem "NeutrinoHD") + elseif (OSCAM_SYSTEM_NAME MATCHES "CST2") + set (OSCamOperatingSystem "NHD2") + elseif (OSCAM_SYSTEM_NAME MATCHES "NSLU2") + set (OSCamOperatingSystem "SlugOS") + elseif (OSCAM_SYSTEM_NAME MATCHES "WRT350NV2") + set (OSCamOperatingSystem "OpenWRT") + set (CS_OS_HW "wrt350nv2") + elseif (OSCAM_SYSTEM_NAME MATCHES "Dockstar") + set (OSCamOperatingSystem "OpenWRT") + set (CS_OS_HW "dockstar") + elseif (OSCAM_SYSTEM_NAME MATCHES "Fonera2") + set (OSCamOperatingSystem "Fonera2") + elseif (OSCAM_SYSTEM_NAME MATCHES "DIR-825") + set (OSCamOperatingSystem "DIR-825") + elseif (OSCAM_SYSTEM_NAME MATCHES "AZBox") + set (OSCamOperatingSystem "AZBox") + elseif (OSCAM_SYSTEM_NAME MATCHES "SU980") + set (OSCamOperatingSystem "SU980") + elseif (OSCAM_SYSTEM_NAME MATCHES "MCA") + set (OSCamOperatingSystem "MCA") + elseif (OSCAM_SYSTEM_NAME MATCHES "agv2+w") + set (OSCamOperatingSystem "agv2+w") + elseif (OSCAM_SYSTEM_NAME MATCHES "WRT54G") + set (OSCamOperatingSystem "WRT54G") + elseif (OSCAM_SYSTEM_NAME MATCHES "Amino") + set (OSCamOperatingSystem "Amino") + elseif (OSCAM_SYSTEM_NAME MATCHES "QboxHD") + set (OSCamOperatingSystem "QboxHD") + elseif (OSCAM_SYSTEM_NAME MATCHES "Padre") + set (OSCamOperatingSystem "Padre") + elseif (OSCAM_SYSTEM_NAME MATCHES "FriendlyARM") + set (OSCamOperatingSystem "FriendlyARM") + elseif (OSCAM_SYSTEM_NAME MATCHES "Amsmc") + set (OSCamOperatingSystem "Amsmc") + else (OSCAM_SYSTEM_NAME MATCHES "Tuxbox") + message (STATUS "Unknown cross system name: <${CMAKE_SYSTEM_NAME}>") + set (OSCamOperatingSystem "Unknown") + endif (OSCAM_SYSTEM_NAME MATCHES "Tuxbox") +else (CMAKE_CROSSCOMPILING) + if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set (OSCamOperatingSystem "Linux") + elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + set (OSCamOperatingSystem "FreeBSD") + elseif (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD") + set (OSCamOperatingSystem "OpenBSD") + elseif (OSCamOperatingSystem MATCHES "Padre") + add_definitions ("-DPADRE -DSPARC") + set (DEFAULT_CS_CONFDIR "/etc/oscam") + elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set (OSCamOperatingSystem "Mac OS X") + execute_process(COMMAND sw_vers -productVersion COMMAND cut -c 1-4 COMMAND tr -d '\n' OUTPUT_VARIABLE SWVER) + execute_process(COMMAND xcodebuild -version COMMAND grep Xcode COMMAND sed "s/Xcode\ //g" COMMAND tr -d '\n' OUTPUT_VARIABLE XCODEVER) + message (STATUS "mac sdk version is ${SWVER}") + message (STATUS "mac Xcode version is ${XCODEVER}") + add_definitions ("-w") + set (CMAKE_OSX_DEPLOYMENT_TARGET "${SWVER}") + if (SWVER GREATER 10.5) + set (CMAKE_OSX_ARCHITECTURES "x86_64") + endif (SWVER GREATER 10.5) + if (SWVER EQUAL 10.7 AND XCODEVER LESS 4.3) + message (STATUS "Compiling with xcode less then 4.3 on 10.7 If You have time update You're Xcode") + endif (SWVER EQUAL 10.7 AND XCODEVER LESS 4.3) + set (STATIC_LIBUSB True) + elseif (${CYGWIN}) + set (OSCamOperatingSystem "Windows/Cygwin") + set (CMAKE_SYSTEM_PROCESSOR "i386") + else (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + message (STATUS "Unknown system name: <${CMAKE_SYSTEM_NAME}>") + set (OSCamOperatingSystem "Unknown") + endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") +endif (CMAKE_CROSSCOMPILING) + +#----------------------- some paths ------------------------------ + +set (OPTIONAL_LINK_DIR "" CACHE STRING "Some optional link directories") +set (OPTIONAL_INCLUDE_DIR "" CACHE STRING "Some optional include directories") + +if (OSCamOperatingSystem MATCHES "Windows/Cygwin") + include_directories ( + ${CMAKE_CURRENT_SOURCE_DIR}/csctapi + ${CMAKE_CURRENT_SOURCE_DIR}/cscrypt + ${CMAKE_CURRENT_SOURCE_DIR}/minilzo + ${CMAKE_CURRENT_SOURCE_DIR}/extapi/cygwin + /usr/include/w32api + ${OPTIONAL_INCLUDE_DIR} + ) +else (OSCamOperatingSystem MATCHES "Windows/Cygwin") + include_directories ( + ${CMAKE_CURRENT_SOURCE_DIR}/csctapi + ${CMAKE_CURRENT_SOURCE_DIR}/cscrypt + ${CMAKE_CURRENT_SOURCE_DIR}/minilzo + ${OPTIONAL_INCLUDE_DIR} + ) +endif (OSCamOperatingSystem MATCHES "Windows/Cygwin") + +if(OSCamOperatingSystem MATCHES "Windows/Cygwin") + link_directories ( + /usr/lib + /usr/lib/w32api + /cygdrive/c/WINDOWS/system32/ + ${OPTIONAL_LINK_DIR} + ) +else (OSCamOperatingSystem MATCHES "Windows/Cygwin") + link_directories ( + ${OPTIONAL_LINK_DIR} + ) +endif (OSCamOperatingSystem MATCHES "Windows/Cygwin") + +#----------------------- global options ------------------------------ + +if (OSCamOperatingSystem MATCHES "Linux") + set (DEFAULT_CS_CONFDIR "/usr/local/etc") +elseif (OSCamOperatingSystem MATCHES "Mac OS X") + if (NOT HAVE_PCSC EQUAL 0) + add_definitions ("-DWITH_PCSC=1") + endif (NOT HAVE_PCSC EQUAL 0) + set (DEFAULT_CS_CONFDIR "/usr/local/etc") +elseif (OSCamOperatingSystem MATCHES "Tuxbox") + set (DEFAULT_CS_CONFDIR "/var/tuxbox/config") +elseif (OSCamOperatingSystem MATCHES "TripleDragon") + add_definitions ("-DSTB04SCI=1") + set (DEFAULT_CS_CONFDIR "/var/tuxbox/config") +elseif (OSCamOperatingSystem MATCHES "NeutrinoHD") + add_definitions ("-DWITH_COOLAPI=1") + set (USE_COOLAPI "USE_COOLAPI") + set (DEFAULT_CS_CONFDIR "/var/tuxbox/config") +elseif (OSCamOperatingSystem MATCHES "NHD2") + add_definitions ("-DWITH_COOLAPI2=1") + set (USE_COOLAPI2 "USE_COOLAPI2") + set (DEFAULT_CS_CONFDIR "/var/tuxbox/config") +elseif (OSCamOperatingSystem MATCHES "SlugOS") + set (DEFAULT_CS_CONFDIR "/var/etc") +elseif (OSCamOperatingSystem MATCHES "OpenWRT") + if (CS_OS_HW MATCHES "wrt350nv2") + set (DEFAULT_CS_CONFDIR "/etc/oscam") + elseif (CS_OS_HW MATCHES "dockstar") + set (DEFAULT_CS_CONFDIR "/usr/oscam") + endif (CS_OS_HW MATCHES "wrt350nv2") +elseif (OSCamOperatingSystem MATCHES "Fonera2") + set (DEFAULT_CS_CONFDIR "/var/etc") +elseif (OSCamOperatingSystem MATCHES "DIR-825") + set (DEFAULT_CS_CONFDIR "/var/etc") +elseif (OSCamOperatingSystem MATCHES "AZBox") + add_definitions ("-DWITH_AZBOX=1") + set (USE_AZBOX "USE_AZBOX") + set (DEFAULT_CS_CONFDIR "/PLUGINS/OpenXCAS/oscamCAS") +elseif (OSCamOperatingSystem MATCHES "SU980") + set (USE_SU980 "USE_SU980") + add_definitions ("-DWITH_SU980=1") + set (DEFAULT_CS_CONFDIR "/var/tuxbox/config") +elseif (OSCamOperatingSystem MATCHES "MCA") + add_definitions ("-DWITH_MCA=1") + set (USE_MCA "USE_MCA") + set (DEFAULT_CS_CONFDIR "/var/mca/d1") +elseif (OSCamOperatingSystem MATCHES "agv2+w") + set (DEFAULT_CS_CONFDIR "/etc") +elseif (OSCamOperatingSystem MATCHES "WRT54G") + set (DEFAULT_CS_CONFDIR "/jffs/etc/config/oscam") +elseif (OSCamOperatingSystem MATCHES "Amino") + set (DEFAULT_CS_CONFDIR "/var/tuxbox/config") +elseif (OSCamOperatingSystem MATCHES "QboxHD") + add_definitions ("-DQBOXHD=1") + set (DEFAULT_CS_CONFDIR "/var/tuxbox/config") +elseif (OSCamOperatingSystem MATCHES "Windows/Cygwin") + add_definitions ("-static -DWITH_PCSC=1") + set (DEFAULT_CS_CONFDIR ".") +elseif (OSCamOperatingSystem MATCHES "FreeBSD") + add_definitions ("-I/usr/local/include -L/usr/local/lib") + set (DEFAULT_CS_CONFDIR ".") +elseif (OSCamOperatingSystem MATCHES "OpenBSD") + add_definitions ("-I/usr/local/include -L/usr/local/lib") + set (DEFAULT_CS_CONFDIR "/etc/oscam") +elseif (OSCamOperatingSystem MATCHES "FriendlyARM") + set (DEFAULT_CS_CONFDIR ".") +elseif (OSCamOperatingSystem MATCHES "Amsmc") + set (USE_AMSMC "USE_AMSMC") + add_definitions ("-DWITH_AMSMC=1") +endif (OSCamOperatingSystem MATCHES "Linux") + +set (CS_CONFDIR ${DEFAULT_CS_CONFDIR} CACHE STRING "Default path for the config files") +add_definitions ("-DCS_CONFDIR=\"${CS_CONFDIR}\"") + +#----------------------- git submodule support ------------------------------ + +execute_process( + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --submodule + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" +) + +# upx compression +execute_process (COMMAND upx --version RESULT_VARIABLE UPX_RET ERROR_QUIET COMMAND head -n 1 OUTPUT_VARIABLE UPX_VER OUTPUT_STRIP_TRAILING_WHITESPACE) +set(COMP_LEVEL "--best" CACHE STRING "${COMP_LEVEL}") + +if (NOT UPX_RET EQUAL 0) + set (UPX_VER "n.a.") +endif (NOT UPX_RET EQUAL 0) + +if (NOT USE_COMPRESS OR USE_COMPRESS EQUAL 0) + message (STATUS "COMPRESSION is disabled") +elseif (USE_COMPRESS EQUAL 1 AND NOT UPX_RET EQUAL 0) + set (USE_COMPRESS "0") + message (STATUS "COMPRESSION requested but upx binary NOT FOUND COMPILING WHITOUT UPX COMPRESSION !!!") +else (NOT USE_COMPRESS OR USE_COMPRESS EQUAL 0) + set (USE_COMPRESS_FLAG "USE_COMPRESS") + set (UPX_SPLIT_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/signing/upx.") + add_definitions ("-D'USE_COMPRESS=\"${USE_COMPRESS}\"' -D'COMP_LEVEL=\"${COMP_LEVEL}\"' -D'COMP_VERSION=\"${UPX_VER}\"'") + message (STATUS "COMPRESSION is added by You, upx compress binary after compiling") +endif (NOT USE_COMPRESS OR USE_COMPRESS EQUAL 0) + +# binary signing +find_program(STAT NAMES gnustat) +find_program(STAT NAMES stat) +find_program(SPLIT NAMES gsplit) +find_program(SPLIT NAMES split) +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_SIGNING OUTPUT_VARIABLE CONFIG_WITH_SIGNING OUTPUT_STRIP_TRAILING_WHITESPACE) +if (CONFIG_WITH_SIGNING MATCHES "N" AND WITH_SIGNING EQUAL 1) + message (STATUS "SIGNING is added by You compiling with signing included") +elseif (CONFIG_WITH_SIGNING MATCHES "Y" AND WITH_SIGNING EQUAL 1) + message (STATUS "SIGNING is added by You but it was already enabled by config file") +elseif (CONFIG_WITH_SIGNING MATCHES "Y" AND NOT DEFINED WITH_SIGNING) + message(STATUS "SIGNING is added by config file ${CONFIG_WITH_SIGNING} compiling with signing included") +elseif (CONFIG_WITH_SIGNING MATCHES "N" OR WITH_SIGNING EQUAL 0) + set (WITH_SIGNING "0") + message (STATUS "SIGNING is disabled") +endif (CONFIG_WITH_SIGNING MATCHES "N" AND WITH_SIGNING EQUAL 1) + +if ((CONFIG_WITH_SIGNING MATCHES "Y" AND NOT WITH_SIGNING EQUAL 0) OR WITH_SIGNING EQUAL 1) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --create-cert ecdsa prime256v1 ca ERROR_QUIET) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --cert-file cert RESULT_VARIABLE CONFIG_RET ERROR_QUIET OUTPUT_VARIABLE SIGN_CERT OUTPUT_STRIP_TRAILING_WHITESPACE) + if (NOT CONFIG_RET EQUAL 0) + set (WITH_SIGNING "0") + message (STATUS "SIGNING requested but certificates NOT FOUND COMPILING WHITOUT SIGNING !!!") + else (NOT CONFIG_RET EQUAL 0) + add_definitions ("-DWITH_SIGNING -DWITH_SSL") + set (WITH_SIGNING "1") + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --cert-file privkey ERROR_QUIET OUTPUT_VARIABLE SIGN_PRIVKEY OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --sign-marker OUTPUT_VARIABLE SIGN_MARKER OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --upx-marker OUTPUT_VARIABLE SIGN_UPXMARKER OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --cert-info COMMAND head -n 1 OUTPUT_VARIABLE SIGN_SUBJECT OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --cert-info COMMAND tail -n 1 OUTPUT_VARIABLE SIGN_SIGALGO OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --cert-info COMMAND head -n 4 COMMAND tail -n 1 OUTPUT_VARIABLE SIGN_VALID OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --cert-info COMMAND head -n 5 COMMAND tail -n 1 OUTPUT_VARIABLE SIGN_PUBALGO OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --cert-info COMMAND head -n 6 COMMAND tail -n 1 OUTPUT_VARIABLE SIGN_PUBBIT OUTPUT_STRIP_TRAILING_WHITESPACE) + set (SIGN_PUBKEY "${CMAKE_CURRENT_BINARY_DIR}/signing/pkey") + set (SIGN_HASH "${CMAKE_CURRENT_BINARY_DIR}/signing/sha256") + set (SIGN_DIGEST "${CMAKE_CURRENT_BINARY_DIR}/signing/digest") + string(TOUPPER ${SIGN_PUBALGO} SIGN_PUBALGO_LIST) + string(REPLACE "-" " " SIGN_PUBALGO_LIST ${SIGN_PUBALGO_LIST}) + string(REPLACE " " "" SIGN_PUBALGO_LIST ${SIGN_PUBALGO_LIST}) + string(REPLACE ":" ";" SIGN_PUBALGO_LIST ${SIGN_PUBALGO_LIST}) + list(GET SIGN_PUBALGO_LIST 1 SIGN_PUBALGO_DEF) + add_definitions ("-D'CERT_ALGO_${SIGN_PUBALGO_DEF}'") + endif (NOT CONFIG_RET EQUAL 0) +elseif (CONFIG_WITH_SIGNING MATCHES "Y" AND WITH_SIGNING EQUAL 0) + message (STATUS " The config file has signing enabled, but You disabled it by you cmake command COMPILING WHITOUT SIGNING") +endif ((CONFIG_WITH_SIGNING MATCHES "Y" AND NOT WITH_SIGNING EQUAL 0) OR WITH_SIGNING EQUAL 1) + +function(SIGN_COMMAND_OSCAM) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/signing) + add_custom_command(TARGET ${exe_name} POST_BUILD COMMAND sha256sum ${exe_name} | awk '{ print $$1 }' | tr -d '\\n' > ${SIGN_HASH} + COMMAND openssl x509 -pubkey -noout -in ${SIGN_CERT} -out ${SIGN_PUBKEY} + COMMAND openssl dgst -sha256 -sign ${SIGN_PRIVKEY} -out ${SIGN_DIGEST} ${SIGN_HASH} + COMMAND printf '[SIGN] SHA256\('\; ${STAT} -c %s ${SIGN_HASH} | tr -d '\\n'\; printf '\): '\; cat ${SIGN_HASH}\; printf ' -> ' + COMMAND openssl dgst -sha256 -verify ${SIGN_PUBKEY} -signature ${SIGN_DIGEST} ${SIGN_HASH} | tr -d '\\n' + COMMAND test -f ${UPX_SPLIT_PREFIX}aa && cat ${UPX_SPLIT_PREFIX}aa > ${exe_name} || true + COMMAND printf '${SIGN_MARKER}' | cat - ${SIGN_DIGEST} >> ${exe_name} + COMMAND test -f ${UPX_SPLIT_PREFIX}ab && cat ${UPX_SPLIT_PREFIX}ab >> ${exe_name} || true + COMMAND printf ' <- DIGEST\('\; ${STAT} -c %s ${SIGN_DIGEST} | tr -d '\\n'\; printf '\)\\n' + ) +endfunction() +#----------------------- subdirectories ------------------------------ + +include (CheckIncludeFile) + +check_include_file ("pthread.h" HAVE_PTHREAD) +if (HAVE_PTHREAD) + message(STATUS " pthread found. Adding pthread support ") + add_definitions ("-DHAVE_PTHREAD_H") +elseif (HAVE_PTHREAD) + message(STATUS " no pthread found. No smartreader ") +endif (HAVE_PTHREAD) + +check_include_file ("openssl/aes.h" HAVE_LIBCRYPTO) +if (HAVE_LIBCRYPTO) + add_definitions ("-DWITH_LIBCRYPTO=1") + set (USE_LIBCRYPTO "USE_LIBCRYPTO") +else (HAVE_LIBCRYPTO) + message (STATUS " LIBCRYTO NOT FOUND OR DISABLED NO SSL SUPPORT POSSIBLE") +endif (HAVE_LIBCRYPTO) + +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_SSL OUTPUT_VARIABLE CONFIG_WITH_SSL OUTPUT_STRIP_TRAILING_WHITESPACE) +if (CONFIG_WITH_SSL MATCHES "N" AND WITH_SSL EQUAL 1) + message ( STATUS " ssl is added by You compiling with ssl included") +elseif (CONFIG_WITH_SSL MATCHES "Y" AND WITH_SSL EQUAL 1) + message (STATUS " ssl is added by You but it was already enabled by config file") +elseif (CONFIG_WITH_SSL MATCHES "N" AND NOT WITH_SSL EQUAL 1) + message (STATUS " SSL IS NOT INCLUDED IN THIS COMPILATION") +endif (CONFIG_WITH_SSL MATCHES "N" AND WITH_SSL EQUAL 1) + +if (CONFIG_WITH_SSL MATCHES "Y" AND NOT WITH_SSL EQUAL 0) + message(STATUS " ssl is added by config file ${CONFIG_WITH_SSL} compiling with ssl included") + add_definitions ("-DWITH_SSL") + set (WITH_SSL "1") +elseif (CONFIG_WITH_SSL MATCHES "Y" AND WITH_SSL EQUAL 0) + message (STATUS " The config file has ssl enabled, but You disabled it by you cmake command COMPILING WHITOUT SSL") +endif (CONFIG_WITH_SSL MATCHES "Y" AND NOT WITH_SSL EQUAL 0) + +find_package(OpenSSL) +if (OPENSSL_FOUND AND HAVE_LIBCRYPTO) + include_directories(${OPENSSL_INCLUDE_DIR}) + message(STATUS " OpenSSL found. Using header files located at ${OPENSSL_INCLUDE_DIR}.") + if (WITH_SSL EQUAL 1) + set(USE_SSL "USE_SSL") + endif (WITH_SSL EQUAL 1) +else (OPENSSL_FOUND AND HAVE_LIBCRYPTO) + message(STATUS "SSL REQUESTED BUT OPENSSL or LYBCRYPTO NOT FOUND !!") + if (WITH_SSL) + message(STATUS " ssl support requested but openssl or libcrypto NOT FOUND COMPILING WHITOUT SSL !!! ") + set (WITH_SSL "0") + endif (WITH_SSL) +endif (OPENSSL_FOUND AND HAVE_LIBCRYPTO) + +if (NOT OSCamOperatingSystem MATCHES "Mac OS X") + if (LIBRTDIR) + check_include_file ("${LIBRTDIR}/include/time.h" HAVE_LIBRT_STATIC) + if (HAVE_LIBRT_STATIC) + if (EXISTS ${LIBRTDIR}/lib/librt.a) + message(STATUS " static librt found : ${LIBRTDIR}/lib/librt.a (needed by libusb).") + add_definitions ("-I${LIBRTDIR}/include/") + add_library(imp_librt STATIC IMPORTED) + set_property(TARGET imp_librt PROPERTY IMPORTED_LOCATION ${LIBRTDIR}/lib/librt.a) + else (EXISTS ${LIBRTDIR}/lib/librt.a) + message(STATUS " ${LIBRTDIR}/lib/librt.a not found (needed by libusb). No smartreader support)") + set (HAVE_LIBRT_STATIC False) + endif (EXISTS ${LIBRTDIR}/lib/librt.a) + else (HAVE_LIBRT_STATIC) + message(STATUS " no librt found (needed by libusb). No smartreader support") + endif (HAVE_LIBRT_STATIC) + else (LIBRTDIR) + check_include_file ("time.h" HAVE_LIBRT) + if (HAVE_LIBRT) + message(STATUS " librt found (needed by libusb).") + else (HAVE_LIBRT) + message(STATUS " no librt found (needed by libusb. No smartreader support") + endif (HAVE_LIBRT) + endif (LIBRTDIR) + if (WITH_STAPI) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/stapi/liboscam_stapi.a) + message(STATUS " liboscam_stapi.a found") + add_definitions ("-DWITH_STAPI=1") + add_library(stapilib STATIC IMPORTED) + set_property(TARGET stapilib PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/stapi/liboscam_stapi.a) + set (HAVE_LIBSTAPI True) + set (USE_STAPI "USE_STAPI") + else (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/stapi/liboscam_stapi.a) + message(FATAL " liboscam_stapi.a support requested but not found! ") + set (HAVE_LIBSTAPI False) + endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/stapi/liboscam_stapi.a) + endif (WITH_STAPI) + if (WITH_STAPI5) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/stapi/liboscam_stapi5.a) + message(STATUS " liboscam_stapi5.a found") + add_definitions ("-DWITH_STAPI5=1") + add_library(stapi5lib STATIC IMPORTED) + set_property(TARGET stapi5lib PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/stapi/liboscam_stapi5.a) + set (HAVE_LIBSTAPI5 True) + set (USE_STAPI5 "USE_STAPI5") + else (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/stapi/liboscam_stapi5.a) + message(FATAL " liboscam_stapi5.a support requested but not found! ") + set (HAVE_LIBSTAPI5 False) + endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/stapi/liboscam_stapi5.a) + endif (WITH_STAPI5) +else (NOT OSCamOperatingSystem MATCHES "Mac OS X") + set (HAVE_LIBRT True) +endif (NOT OSCamOperatingSystem MATCHES "Mac OS X") + + +if( HAVE_LIBRT OR HAVE_LIBRT_STATIC) + if (LIBUSBDIR) + check_include_file ("${LIBUSBDIR}/include/libusb-1.0/libusb.h" HAVE_LIBUSB) + if (HAVE_LIBUSB AND HAVE_PTHREAD) + if (EXISTS ${LIBUSBDIR}/lib/libusb-1.0.a) + if (STATIC_LIBUSB EQUAL 1) + message(STATUS " libusb 1.0 found (${LIBUSBDIR}/lib/libusb-1.0.a). Adding smartreader support ") + add_definitions ("-DWITH_LIBUSB=1") + add_definitions ("-I${LIBUSBDIR}/include/") + set (LIBUSB "USE_LIBUSB") + add_library(imp_libusb STATIC IMPORTED) + set_property(TARGET imp_libusb PROPERTY IMPORTED_LOCATION ${LIBUSBDIR}/lib/libusb-1.0.a) + elseif (NOT STATIC_LIBUSB OR STATIC_LIBUSB EQUAL 0) + set (CMAKE_FIND_ROOT_PATH ${LIBUSBDIR}) + find_library (LIBUSB_LIBRARY NAMES usb-1.0) + message(STATUS "libusb 1.0 found (${LIBUSB_LIBRARY}). Adding smartreader support ") + add_definitions ("-DWITH_LIBUSB=1") + add_definitions ("-I${LIBUSBDIR}/include/") + set (LIBUSB "USE_LIBUSB") + add_library(imp_libusb SHARED IMPORTED) + set_property(TARGET imp_libusb PROPERTY IMPORTED_LOCATION ${LIBUSB_LIBRARY} ) + endif (STATIC_LIBUSB EQUAL 1) + else (EXISTS ${LIBUSBDIR}/lib/libusb-1.0.a) + message(STATUS " libusb 1.0 not found (${LIBUSBDIR}/lib/libusb). No smartreader support ") + set (HAVE_LIBUSB False) + set (HAVE_LIBRT False) + set (HAVE_LIBRT_STATIC False) + endif (EXISTS ${LIBUSBDIR}/lib/libusb-1.0.a) + else (HAVE_LIBUSB AND HAVE_PTHREAD) + message(STATUS " no libusb 1.0 found. No smartreader support") + set (HAVE_LIBUSB False) + set (HAVE_LIBRT False) + set (HAVE_LIBRT_STATIC False) + endif (HAVE_LIBUSB AND HAVE_PTHREAD) + else (LIBUSBDIR) + if (NOT HAVE_LIBUSB EQUAL 0) + check_include_file ("libusb-1.0/libusb.h" HAVE_LIBUSB) + check_include_file ("libusb-1.0/libusb.h" PRESENT) + endif(NOT HAVE_LIBUSB EQUAL 0) + if (OSCamOperatingSystem MATCHES "Mac OS X") + if (NOT HAVE_LIBUSB EQUAL 0 AND NOT PRESENT) + find_file (USBINCL_LOC "libusb-1.0/libusb.h") + if (EXISTS ${USBINCL_LOC}) + add_definitions ("-I/usr/local/include -L/usr/local/lib -I/usr/include -L/usr/lib") + set (HAVE_LIBUSB True) + set (PRESENT True) + set (USE_LIBUSB "USE_LIBUSB") + if (NOT STATIC_LIBUSB OR STATIC_LIBUSB EQUAL 0) + set (STATICLIBUSB False) + elseif (STATIC_LIBUSB EQUAL 1) + set (STATICLIBUSB True) + endif (NOT STATIC_LIBUSB OR STATIC_LIBUSB EQUAL 0) + endif (EXISTS ${USBINCL_LOC}) + endif (NOT HAVE_LIBUSB EQUAL 0 AND NOT PRESENT) + endif (OSCamOperatingSystem MATCHES "Mac OS X") + if (NOT STATIC_LIBUSB OR STATIC_LIBUSB EQUAL 0) + if(HAVE_LIBUSB AND HAVE_PTHREAD AND PRESENT) + message(STATUS " libusb 1.0 found (libusb-1.0.so) Adding smartreader support ") + add_definitions ("-DWITH_LIBUSB=1") + set (USE_LIBUSB "USE_LIBUSB") + set (STATICLIBUSB False) + endif(HAVE_LIBUSB AND HAVE_PTHREAD AND PRESENT) + elseif(STATIC_LIBUSB EQUAL 1) + if (HAVE_LIBUSB AND HAVE_PTHREAD AND PRESENT) + message(STATUS " static libusb 1.0 found (libusb-1.0.a). Adding smartreader support ") + add_definitions ("-DWITH_LIBUSB=1") + set (USE_LIBUSB "USE_LIBUSB") + set (STATICLIBUSB True) + else (HAVE_LIBUSB AND HAVE_PTHREAD AND PRESENT) + message(STATUS " no libusb 1.0 found. No smartreader support") + set (HAVE_LIBUSB False) + endif (HAVE_LIBUSB AND HAVE_PTHREAD AND PRESENT) + endif(NOT STATIC_LIBUSB OR STATIC_LIBUSB EQUAL 0) + endif (LIBUSBDIR) +endif( HAVE_LIBRT OR HAVE_LIBRT_STATIC) + +check_include_file ("PCSC/wintypes.h" HAVE_PCSC) +if (HAVE_PCSC) + FIND_PATH (PCSC_PATH PCSC/wintypes.h) + if(OPTIONAL_INCLUDE_DIR AND EXISTS ${OPTIONAL_INCLUDE_DIR}/PCSC/wintypes.h) + message(STATUS " PCSC headers found (${OPTIONAL_INCLUDE_DIR}/PCSC). Adding PCSC support ") + add_definitions ("-DWITH_PCSC=1") + add_definitions ("-I${OPTIONAL_INCLUDE_DIR}/PCSC") + set (USE_PCSC "USE_PCSC") + elseif (EXISTS ${PCSC_PATH}/PCSC/wintypes.h AND NOT OPTIONAL_INCLUDE_DIR) + message(STATUS " PCSC headers found (${PCSC_PATH}/PCSC). Adding PCSC support ") + add_definitions ("-DWITH_PCSC=1") + add_definitions ("-I${PCSC_PATH}/PCSC") + set (USE_PCSC "USE_PCSC") + else (OPTIONAL_INCLUDE_DIR AND EXISTS ${OPTIONAL_INCLUDE_DIR}/PCSC/wintypes.h) + if (NOT OSCamOperatingSystem MATCHES "Mac OS X") + set (HAVE_PCSC False) + if(OPTIONAL_INCLUDE_DIR) + message(STATUS " PCSC headers not found (not in /usr/include/PCSC or /usr/local/include/PCSC or ${OPTIONAL_INCLUDE_DIR}/PCSC). No PCSC support ") + else(OPTIONAL_INCLUDE_DIR) + message(STATUS " PCSC headers not found (not in /usr/include/PCSC or /usr/local/include/PCSC). No PCSC support ") + endif(OPTIONAL_INCLUDE_DIR) + endif (NOT OSCamOperatingSystem MATCHES "Mac OS X") + endif(OPTIONAL_INCLUDE_DIR AND EXISTS ${OPTIONAL_INCLUDE_DIR}/PCSC/wintypes.h) + if (OSCamOperatingSystem MATCHES "Mac OS X" AND NOT HAVE_PCSC EQUAL 0) + set (USE_PCSC "USE_PCSC") + set (HAVE_PCSC "1") + endif (OSCamOperatingSystem MATCHES "Mac OS X" AND NOT HAVE_PCSC EQUAL 0) +else (HAVE_PCSC) + if (OSCamOperatingSystem MATCHES "Windows/Cygwin") + add_definitions ("-Icygwin") + set (HAVE_PCSC "1") + set (USE_PCSC "USE_PCSC") + endif (OSCamOperatingSystem MATCHES "Windows/Cygwin") +endif (HAVE_PCSC) + +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled MODULE_STREAMRELAY OUTPUT_VARIABLE CONFIG_STREAMRELAY OUTPUT_STRIP_TRAILING_WHITESPACE) +if (CONFIG_STREAMRELAY MATCHES "Y" AND NOT MODULE_STREAMRELAY EQUAL 1) + if (LIBDVBCSADIR) + check_include_file ("${LIBDVBCSADIR}/include/dvbcsa/dvbcsa.h" FOUND_LIBDVBCSA) + set (LIBADVBCSA_LIBRARY "${LIBDVBCSADIR}/libdvbcsa.a") + set (LIBDVBCSA_LIBRARY "${LIBDVBCSADIR}/libdvbcsa.so") + else (LIBDVBCSADIR) + check_include_file ("dvbcsa/dvbcsa.h" FOUND_LIBDVBCSA) + set(HAVE_LIBDVBCSA ${FOUND_LIBDVBCSA}) + find_library (LIBADVBCSA_LIBRARY NAMES libdvbcsa.a) + find_library (LIBDVBCSA_LIBRARY NAMES dvbcsa) + endif (LIBDVBCSADIR) + + if (HAVE_LIBDVBCSA) + if (STATIC_LIBDVBCSA AND FOUND_LIBDVBCSA AND EXISTS ${LIBADVBCSA_LIBRARY}) + message(STATUS " static libdvbcsa found (libdvbcsa.a).") + add_library(imp_libdvbcsa STATIC IMPORTED) + set_property(TARGET imp_libdvbcsa PROPERTY IMPORTED_LOCATION ${LIBADVBCSA_LIBRARY}) + set (dvbcsa_link "imp_libdvbcsa") + set (STATICLIBDVBCSA 1) + elseif ((NOT STATIC_LIBDVBCSA OR STATIC_LIBDVBCSA EQUAL 0) AND FOUND_LIBDVBCSA AND EXISTS ${LIBDVBCSA_LIBRARY}) + message(STATUS " libdvbcsa found (libdvbcsa.so).") + add_library(imp_libdvbcsa SHARED IMPORTED) + set_property(TARGET imp_libdvbcsa PROPERTY IMPORTED_LOCATION ${LIBDVBCSA_LIBRARY} ) + set(dvbcsa_link "imp_libdvbcsa") + set (STATICLIBDVBCSA 0) + else (STATIC_LIBDVBCSA AND FOUND_LIBDVBCSA AND EXISTS ${LIBADVBCSA_LIBRARY}) + message(FATAL_ERROR " no libdvbcsa found!") + endif (STATIC_LIBDVBCSA AND FOUND_LIBDVBCSA AND EXISTS ${LIBADVBCSA_LIBRARY}) + add_definitions ("-DSTATIC_LIBDVBCSA=${STATICLIBDVBCSA}") + else (HAVE_LIBDVBCSA) + message(FATAL_ERROR " HAVE_LIBDVBCSA disabled!") + endif (HAVE_LIBDVBCSA) +endif (CONFIG_STREAMRELAY MATCHES "Y" AND NOT MODULE_STREAMRELAY EQUAL 1) + +# Manage config.h based on command line parameters +# Manipulate config file based on given parameters and read unset parameters + +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_EMU OUTPUT_VARIABLE CONFIG_WITH_EMU OUTPUT_STRIP_TRAILING_WHITESPACE) +if (CONFIG_WITH_EMU MATCHES "Y" AND NOT WITH_EMU EQUAL 1) + add_definitions ("-DWITH_EMU") + set (WITH_EMU "1") + message (STATUS " EMU is added by config compiling with EMU") +endif (CONFIG_WITH_EMU MATCHES "Y" AND NOT WITH_EMU EQUAL 1) + +if (WITH_EMU) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_SOFTCAM OUTPUT_VARIABLE CONFIG_WITH_SOFTCAM OUTPUT_STRIP_TRAILING_WHITESPACE) + if (CONFIG_WITH_SOFTCAM MATCHES "Y" AND NOT WITH_SOFTCAM EQUAL 1) + add_definitions ("-DWITH_SOFTCAM") + set (WITH_SOFTCAM "1") + message (STATUS " SOFTCAM is added by config linking SoftCam.Key") + endif (CONFIG_WITH_SOFTCAM MATCHES "Y" AND NOT WITH_SOFTCAM EQUAL 1) +endif (WITH_EMU) + +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --show-valid OUTPUT_VARIABLE config_vars_string OUTPUT_STRIP_TRAILING_WHITESPACE) +string(REGEX MATCHALL "[A-Z0-9_]+" config_vars ${config_vars_string}) + +MACRO(GENERATE_OSCAM_CONFIG fullpath) + foreach(option ${config_vars}) + if(DEFINED ${option}) + if(${option}) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enable ${option}) + else(${option}) + execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --disable ${option}) + endif(${option}) + endif(DEFINED ${option}) + endforeach(option) +ENDMACRO(GENERATE_OSCAM_CONFIG fullpath) + +GENERATE_OSCAM_CONFIG("${CMAKE_CURRENT_SOURCE_DIR}/config.h") + +# Build config.mak and related files +# FIXME: Create USE_FLAGS and pass them to config.sh using --use-flags +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --objdir ${CMAKE_CURRENT_BINARY_DIR} --use-flags "${USE_STAPI} ${USE_STAPI5} / +${USE_COOLAPI} ${USE_SU980} ${USE_AZBOX} ${USE_AMSMC} ${USE_MCA} ${USE_SSL} ${USE_LIBCRYPTO} ${USE_LIBUSB} ${USE_PCSC} ${USE_COMPRESS_FLAG}" --make-config.mak) +# Generate webif/pages.c +execute_process (COMMAND make --no-print-directory --quiet -C ${CMAKE_CURRENT_SOURCE_DIR}/webif) + +#----------------------- subdirectories ------------------------------ + +add_subdirectory (csctapi) +add_subdirectory (minilzo) +add_subdirectory (cscrypt) + +#----------------------- file groups ------------------------------ +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled MODULE_CAMD33 OUTPUT_VARIABLE CAMD33 OUTPUT_STRIP_TRAILING_WHITESPACE) +if (CAMD33 MATCHES "N") +set (CAMD_33 "module-camd33*") +endif (CAMD33 MATCHES "N") +if (OSCamOperatingSystem MATCHES "Mac OS X") +file (GLOB csmodules_mac "module-dvbapi*" "module-lcd*" "${CAMD_33}") +file (GLOB csmodules_srcs "module-*.c" "webif/pages.c") +list (REMOVE_ITEM csmodules_srcs ${csmodules_mac}) +file (GLOB csmodules_hdrs "module-*.h" "webif/pages.h") +list (REMOVE_ITEM csmodules_hdrs ${csmodules_mac}) +file (GLOB csreaders_srcs "reader-*.c") +file (GLOB csreaders_hdrs "reader-*.h") +file (GLOB csoscam_srcs "oscam-*.c") +file (GLOB csoscam_hdrs "oscam-*.h") +file (GLOB exe_srcs "oscam.c") +file (GLOB exe_hdrs "globals.h") +file (GLOB all_srcs ${csmodules_srcs} ${csreaders_srcs} ${csoscam_srcs} ${exe_srcs}) +else (OSCamOperatingSystem MATCHES "Mac OS X") +file (GLOB csmodules_srcs "module-*.c" "webif/pages.c") +file (GLOB csmodules_hdrs "module-*.h" "webif/pages.h") +file (GLOB csreaders_srcs "reader-*.c") +file (GLOB csreaders_hdrs "reader-*.h") +file (GLOB csoscam_srcs "oscam-*.c" "${CMAKE_CURRENT_BINARY_DIR}/config.c") +file (GLOB csoscam_hdrs "oscam-*.h") +file (GLOB exe_srcs "oscam.c") +file (GLOB exe_hdrs "globals.h") +file (GLOB all_srcs ${csmodules_srcs} ${csreaders_srcs} ${csoscam_srcs} ${exe_srcs}) +endif (OSCamOperatingSystem MATCHES "Mac OS X") + +# Add wiki pages if WEBIF_WIKI is enabled (applies to both platforms) +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WEBIF_WIKI OUTPUT_VARIABLE CONFIG_WEBIF_WIKI OUTPUT_STRIP_TRAILING_WHITESPACE) +if (CONFIG_WEBIF_WIKI MATCHES "Y" AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/webif/pages_wiki.c") + list (APPEND csmodules_srcs "${CMAKE_CURRENT_SOURCE_DIR}/webif/pages_wiki.c") + message(STATUS " WEBIF_WIKI enabled - including wiki documentation") +endif() + +#----------------------- modules ------------------------------ + +set (csmodules "csmodules") +add_library (${csmodules} STATIC ${csmodules_srcs} ${csmodules_hdrs}) + +#----------------------- readers ------------------------------ + +set (csreaders "csreaders") +add_library (${csreaders} STATIC ${csreaders_srcs} ${csreaders_hdrs}) + +#----------------------- other oscam files ------------------------------ + +set (csoscam "csoscam") +if (NOT WITH_SIGNING) + list(REMOVE_ITEM csoscam_srcs "${CMAKE_SOURCE_DIR}/oscam-signing.c") + list(REMOVE_ITEM csoscam_hdrs "${CMAKE_SOURCE_DIR}/oscam-signing.h") +endif (NOT WITH_SIGNING) +add_library (${csoscam} STATIC ${csoscam_srcs} ${csoscam_hdrs}) + +#----------------------- the executable ------------------------------ + +if (NOT USE_COMPRESS EQUAL 1) + set (exe_name "oscam") +else (NOT USE_COMPRESS EQUAL 1) + set (exe_name "oscam-upx") +endif (NOT USE_COMPRESS EQUAL 1) +add_executable (${exe_name} ${exe_srcs} ${exe_hdrs}) +target_link_libraries (${exe_name} ${csoscam} ${csmodules} ${csreaders} csctapi cscrypt minilzo) +if (WITH_SIGNING EQUAL 1) + SIGN_COMMAND_OSCAM() +endif (WITH_SIGNING EQUAL 1) +if (USE_COMPRESS EQUAL 1) + add_custom_command(TARGET ${exe_name} POST_BUILD COMMAND upx -q ${COMP_LEVEL} ${exe_name} | grep '^[[:space:]]*[[:digit:]]* ->' | xargs | cat | xargs -0 printf '[ UPX] %s') + if (WITH_SIGNING EQUAL 1) + set_property(TARGET ${exe_name} APPEND PROPERTY ADDITIONAL_CLEAN_FILES ${UPX_SPLIT_PREFIX}aa ${UPX_SPLIT_PREFIX}ab) + add_custom_command(TARGET ${exe_name} POST_BUILD COMMAND ${SPLIT} --bytes=`grep -oba '${SIGN_UPXMARKER}' ${exe_name} | tail -1 | awk -F':' '{ print $$1 }'` ${exe_name} ${UPX_SPLIT_PREFIX}) + SIGN_COMMAND_OSCAM() + endif (WITH_SIGNING EQUAL 1) +endif (USE_COMPRESS EQUAL 1) + +if(HAVE_LIBRT AND HAVE_LIBUSB) + if (LIBUSBDIR) + set (libusb_link "imp_libusb") + else(LIBUSBDIR) + if (STATICLIBUSB) + set (libusb_link "libusb-1.0.a") + else (STATICLIBUSB) + set (libusb_link "usb-1.0") + endif(STATICLIBUSB) + endif(LIBUSBDIR) + if (NOT OSCamOperatingSystem MATCHES "Mac OS X") + set (rt_link "rt") + endif (NOT OSCamOperatingSystem MATCHES "Mac OS X") + if (OSCamOperatingSystem MATCHES "Windows/Cygwin") + set (setupapi_link "setupapi") + set (ole32_link "ole32") + set (shell32_link "shell32") + endif (OSCamOperatingSystem MATCHES "Windows/Cygwin") +endif(HAVE_LIBRT AND HAVE_LIBUSB) + +if (HAVE_LIBRT_STATIC AND HAVE_LIBUSB) + if (LIBUSBDIR) + set (libusb_link "imp_libusb") + else(LIBUSBDIR) + if (STATICLIBUSB) + set (libusb_link "libusb-1.0.a") + else (STATICLIBUSB) + set (libusb_link "usb-1.0") + endif(STATICLIBUSB) + endif(LIBUSBDIR) + if (NOT OSCamOperatingSystem MATCHES "Mac OS X") + set (rt_link "imp_librt") + endif (NOT OSCamOperatingSystem MATCHES "Mac OS X") + if (OSCamOperatingSystem MATCHES "Windows/Cygwin") + set (setupapi_link "setupapi") + set (ole32_link "ole32") + set (shell32_link "shell32") + endif (OSCamOperatingSystem MATCHES "Windows/Cygwin") +endif (HAVE_LIBRT_STATIC AND HAVE_LIBUSB) + +if (HAVE_PTHREAD) + set (pthread_link "pthread") +endif (HAVE_PTHREAD) + +if (WITH_SSL) + target_link_libraries (${exe_name} "${OPENSSL_SSL_LIBRARIES}") +endif (WITH_SSL) + +if (HAVE_LIBCRYPTO) + target_link_libraries (${exe_name} "${OPENSSL_CRYPTO_LIBRARIES}") +endif (HAVE_LIBCRYPTO) + +if (HAVE_PCSC) +if (NOT OSCamOperatingSystem MATCHES "Mac OS X") +if (NOT OSCamOperatingSystem MATCHES "Windows/Cygwin") + target_link_libraries (${exe_name} pcsclite) +endif (NOT OSCamOperatingSystem MATCHES "Windows/Cygwin") +endif (NOT OSCamOperatingSystem MATCHES "Mac OS X") +endif (HAVE_PCSC) + +if (HAVE_LIBSTAPI) + target_link_libraries(${exe_name} stapilib) +endif (HAVE_LIBSTAPI) + +if (HAVE_LIBSTAPI5) + target_link_libraries(${exe_name} stapi5lib) +endif (HAVE_LIBSTAPI5) + +add_dependencies (${exe_name} ${csoscam} ${csreaders} ${csmodules}) + +#----------------------- specific options ------------------------------ + +if (OSCamOperatingSystem MATCHES "Linux") + set (dl_link "dl") +elseif (OSCamOperatingSystem MATCHES "Mac OS X") + if (NOT HAVE_PCSC EQUAL 0) + FIND_LIBRARY (PCSC_LIBRARY PCSC) + message (STATUS "PCSC Mac OS X found : ${PCSC_LIBRARY}") + add_definitions ("-DWITH_PCSC=1") + target_link_libraries ( ${exe_name} ${PCSC_LIBRARY}) + set (HAVE_PCSC True) + FIND_LIBRARY (IOKit_LIBRARY IOKit) + target_link_libraries ( ${exe_name} ${IOKit_LIBRARY}) + FIND_LIBRARY (CoreFoundation_LIBRARY CoreFoundation) + target_link_libraries ( ${exe_name} ${CoreFoundation_LIBRARY}) + else (NOT HAVE_PCSC EQUAL 0) + message (STATUS "PCSC Mac OS X not found no pcsc support") + endif (NOT HAVE_PCSC EQUAL 0) +elseif (OSCamOperatingSystem MATCHES "Tuxbox") + set (dl_link "dl") +elseif (OSCamOperatingSystem MATCHES "TripleDragon") + set (dl_link "dl") +elseif (OSCamOperatingSystem MATCHES "SlugOS") +elseif (OSCamOperatingSystem MATCHES "OpenWRT") +elseif (OSCamOperatingSystem MATCHES "Fonera2") +elseif (OSCamOperatingSystem MATCHES "DIR-825") +elseif (OSCamOperatingSystem MATCHES "agv2+w") +elseif (OSCamOperatingSystem MATCHES "WRT54G") +elseif (OSCamOperatingSystem MATCHES "Amino") +elseif (OSCamOperatingSystem MATCHES "QboxHD") +elseif (OSCamOperatingSystem MATCHES "Windows/Cygwin") + message (STATUS "Windows system PCSC : winscard.dll") + target_link_libraries (${exe_name} winscard) + target_link_libraries (${exe_name} setupapi ) + target_link_libraries (${exe_name} ole32) + target_link_libraries (${exe_name} shell32) +elseif (OSCamOperatingSystem MATCHES "NeutrinoHD") + target_link_libraries (${exe_name} rt nxp) +elseif (OSCamOperatingSystem MATCHES "NHD2") + target_link_libraries (${exe_name} lnxcssUsr lnxscsUsr lnxnotifyqUsr lnxUKAL lnxplatUsr rt) +elseif (OSCamOperatingSystem MATCHES "AZBox") + add_library(xcas STATIC IMPORTED) + set_property(TARGET xcas PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/extapi/openxcas/libOpenXCASAPI.a) + set (xcas_link "xcas") +elseif (OSCamOperatingSystem MATCHES "SU980") + target_link_libraries (${exe_name} rt entropic) +endif (OSCamOperatingSystem MATCHES "Linux") + +target_link_libraries (${exe_name} ${libusb_link} ${rt_link} ${setupapi_link} ${ole32_link} ${shell32_link} ${pthread_link} ${dl_link} ${xcas_link} ${dvbcsa_link}) + +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --oscam-version COMMAND tr -d '\n' OUTPUT_VARIABLE CS_VERSION) +add_definitions ("-D'CS_VERSION=\"${CS_VERSION}\"'") +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --oscam-commit COMMAND tr -d '\n' OUTPUT_VARIABLE CS_GIT_COMMIT) +add_definitions ("-D'CS_GIT_COMMIT=\"${CS_GIT_COMMIT}\"'") +string(TIMESTAMP CS_BUILD_DATE "%d.%m.%Y %H:%M:%S") +add_definitions ("-D'CS_BUILD_DATE=\"${CS_BUILD_DATE}\"'") + +execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpmachine COMMAND tr -d '\n' OUTPUT_VARIABLE CS_TARGET) +add_definitions ("-D'CS_TARGET=\"${CS_TARGET}\"'") + +execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_ARM_NEON OUTPUT_VARIABLE CONFIG_WITH_ARM_NEON OUTPUT_STRIP_TRAILING_WHITESPACE) +if (CONFIG_WITH_ARM_NEON MATCHES "Y" AND NOT WITH_ARM_NEON EQUAL 0) + add_definitions ("-DWITH_ARM_NEON") + set (WITH_ARM_NEON "1") +elseif (CONFIG_WITH_ARM_NEON MATCHES "Y" AND NOT WITH_ARM_NEON EQUAL 0) + message (STATUS " The config file has WITH_ARM_NEON enabled, but You disabled it by you cmake command COMPILING WHITOUT arm neon optimization") +endif (CONFIG_WITH_ARM_NEON MATCHES "Y" AND NOT WITH_ARM_NEON EQUAL 0) +#----------------------- global compile and link options ------------------------------ +#enable sse2 on x86, neon on arm +if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse -msse2 -msse3") +elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "(arm)|(ARM)") + if (WITH_ARM_NEON EQUAL 1) + message(STATUS " ARM NEON is enabled, compiling with neon optimization") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon") + set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -mfpu=neon") + else (WITH_ARM_NEON EQUAL 1) + message(STATUS " ARM NEON is disabled, compiling without neon optimization") + endif (WITH_ARM_NEON EQUAL 1) +endif (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") + +# disable warning about unused but set variables in gcc 4.6+ +if (CMAKE_COMPILER_IS_GNUCC) + execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + string(REGEX MATCHALL "[0-9]+" GCC_VERSION_COMPONENTS ${GCC_VERSION}) + list(GET GCC_VERSION_COMPONENTS 0 GCC_MAJOR) + list(GET GCC_VERSION_COMPONENTS 0 GCC_MINOR) + add_definitions ("-W -Wall ") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O2 -pipe -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-schedule-insns") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O2") + set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -ggdb") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ggdb") +endif (CMAKE_COMPILER_IS_GNUCC) +# some optimisations +if (OSCamOperatingSystem MATCHES "Mac OS X") + if (SWVER GREATER 10.6) + if (SWVER EQUAL 10.7 AND XCODEVER LESS 4.3) + add_definitions("-isysroot /Developer/SDKs/MacOSX${SWVER}.sdk") + else (SWVER EQUAL 10.7 AND XCODEVER LESS 4.3) + add_definitions("-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${SWVER}.sdk") + endif (SWVER EQUAL 10.7 AND XCODEVER LESS 4.3) + elseif(SWVER LESS 10.7) + add_definitions("-isysroot /Developer/SDKs/MacOSX${SWVER}.sdk") + endif (SWVER GREATER 10.6) +endif (OSCamOperatingSystem MATCHES "Mac OS X") + +# we don't want the '-rdynamic' in the link command +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") + +# we want to strip if not Debug build +if (OSCamOperatingSystem MATCHES "Mac OS X") + if (SWVER MATCHES "10.8") + if (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + set (CMAKE_EXE_LINKER_FLAGS "-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${SWVER}.sdk") + else (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + message (STATUS " building with debug") + set (CMAKE_EXE_LINKER_FLAGS "-ggdb -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${SWVER}.sdk") + endif (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + elseif (SWVER MATCHES "10.7") + if (SWVER EQUAL 10.7 AND XCODEVER LESS 4.3) + if (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + set (CMAKE_EXE_LINKER_FLAGS "-isysroot /Developer/SDKs/MacOSX${SWVER}.sdk") + else (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + message (STATUS " building with debug") + set (CMAKE_EXE_LINKER_FLAGS "-ggdb -isysroot /Developer/SDKs/MacOSX${SWVER}.sdk") + endif (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + else(SWVER EQUAL 10.7 AND XCODEVER LESS 4.3) + if (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + set (CMAKE_EXE_LINKER_FLAGS "-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${SWVER}.sdk") + else (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + message (STATUS " building with debug") + set (CMAKE_EXE_LINKER_FLAGS "-ggdb -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${SWVER}.sdk") + endif (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + endif(SWVER EQUAL 10.7 AND XCODEVER LESS 4.3) + elseif (SWVER MATCHES "10.6") + if (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + set (CMAKE_EXE_LINKER_FLAGS "-isysroot /Developer/SDKs/MacOSX${SWVER}.sdk") + else (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + message (STATUS " building with debug") + set (CMAKE_EXE_LINKER_FLAGS "-ggdb -isysroot /Developer/SDKs/MacOSX${SWVER}.sdk") + endif (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + endif (SWVER MATCHES "10.8") +else (OSCamOperatingSystem MATCHES "Mac OS X") + if (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + set (CMAKE_EXE_LINKER_FLAGS "-s") + else (NOT CMAKE_BUILD_TYPE STREQUAL Debug) + message (STATUS " building with debug") + set (CMAKE_EXE_LINKER_FLAGS "-ggdb") + endif (NOT CMAKE_BUILD_TYPE STREQUAL Debug) +endif (OSCamOperatingSystem MATCHES "Mac OS X") + +if (OSCamOperatingSystem MATCHES "Windows/Cygwin") + set (CMAKE_EXE_LINKER_FLAGS "--enable-stdcall-fixup") +endif (OSCamOperatingSystem MATCHES "Windows/Cygwin") + +#----------------------- subdirectory list smargo ------------------------------ + +if( HAVE_LIBRT OR HAVE_LIBRT_STATIC) + if (HAVE_LIBUSB) + add_subdirectory (utils) + endif (HAVE_LIBUSB) +endif( HAVE_LIBRT OR HAVE_LIBRT_STATIC) + +#-------------------------------------------------------------------------------- + +if (NOT OSCamOperatingSystem MATCHES "Mac OS X") +if (NOT DEFINED ENV{ANDROID_NDK}) +if (NOT DEFINED ENV{ANDROID_STANDALONE_TOOLCHAIN}) + if (WITH_SOFTCAM) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key) + execute_process (COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key ${CMAKE_CURRENT_BINARY_DIR}/SoftCam.Key) + else (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key) + execute_process (COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/SoftCam.Key) + endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--format=binary -Wl,SoftCam.Key -Wl,--format=default") + if (NOT OSCamOperatingSystem MATCHES "Windows/Cygwin") + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,noexecstack") + endif (NOT OSCamOperatingSystem MATCHES "Windows/Cygwin") + endif (WITH_SOFTCAM) +endif (NOT DEFINED ENV{ANDROID_STANDALONE_TOOLCHAIN}) +endif (NOT DEFINED ENV{ANDROID_NDK}) +endif (NOT OSCamOperatingSystem MATCHES "Mac OS X") + +#----------------------- installation ----------------------------- + +file (GLOB config_files "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/oscam.*") +file (GLOB doc_files "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/doc/txt/*.txt") +file (GLOB man1_files "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/doc/man/*.1") +file (GLOB man5_files "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/doc/man/*.5") +file (GLOB example_files "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/doc/example/oscam.ac" + "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/doc/example/oscam.cert" + "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/doc/example/oscam.conf" + "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/doc/example/oscam.guess" + "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/doc/example/oscam.ird" + "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/doc/example/oscam.server" + "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/doc/example/oscam.services" + "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/doc/example/oscam.srvid" + "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/doc/example/oscam.user") +file (GLOB monitor_files "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/monitor/mpcs*.sh" + "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/monitor/mpcs*.tar") + +install (PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${exe_name}${CMAKE_EXECUTABLE_SUFFIX} DESTINATION bin COMPONENT bin) +install (FILES ${config_files} DESTINATION etc COMPONENT config) +install (FILES ${doc_files} DESTINATION share/doc/oscam COMPONENT doc) +install (FILES ${man1_files} DESTINATION share/man/man1 COMPONENT doc) +install (FILES ${man5_files} DESTINATION share/man/man5 COMPONENT doc) +install (FILES ${example_files} DESTINATION share/doc/oscam/example COMPONENT doc) +install (FILES ${monitor_files} DESTINATION share/doc/oscam/monitor COMPONENT doc) + +#----------------------- we can use CPack to build pacakges -------------------------- + +# get version from source file +string (REGEX REPLACE ".*\"\(.*\)\".*" "\\1" CS_VERSION ${CS_VERSION}) +string (REGEX REPLACE "\(.*\)\\..*\\..*" "\\1" CPACK_PACKAGE_VERSION_MAJOR ${CS_VERSION}) +string (REGEX REPLACE ".*\\.\(.*\)\\..*" "\\1" CPACK_PACKAGE_VERSION_MINOR ${CS_VERSION}) +string (REGEX REPLACE ".*\\..*\\.\(.*\)" "\\1" CPACK_PACKAGE_VERSION_PATCH ${CS_VERSION}) +set (CPACK_PACKAGE_VERSION + "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") + +# compute my filename +set (CPACK_PACKAGE_FILE_NAME + "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}-${CS_TARGET}") + +# wich archive we want to generate +set (CPACK_GENERATOR "TGZ") + +# what to put in the archive +set (CPACK_STRIP_FILES TRUE) +set (CPACK_COMPONENTS_ALL bin)# config doc) + +# ok, we can now use CPack +include (CPack) + +#----------------------- printout resume ----------------------------- + +message (STATUS "") +message (STATUS " operating system: ${OSCamOperatingSystem}") +message (STATUS " target system: ${CS_TARGET}") +message (STATUS " revision: ${CS_VERSION}") +message (STATUS " commit: ${CS_GIT_COMMIT}") +message (STATUS " build date: ${CS_BUILD_DATE}") +if (USE_COMPRESS EQUAL 1) + message (STATUS " packer: ${UPX_VER} (compression level ${COMP_LEVEL})") +endif (USE_COMPRESS EQUAL 1) +if (WITH_SIGNING EQUAL 1) + message (STATUS " signing: ${SIGN_PUBALGO}, ${SIGN_PUBBIT}, ${SIGN_SIGALGO},\n\t Valid ${SIGN_VALID}, ${SIGN_SUBJECT}") +endif (WITH_SIGNING EQUAL 1) +if (HAVE_LIBCRYPTO) + message (STATUS " use system libcrypto functions") +else (HAVE_LIBCRYPTO) + message (STATUS " use built-in crypt functions") +endif (HAVE_LIBCRYPTO) +if (HAVE_PCSC) + message (STATUS " use system pcsc functions") +endif (HAVE_PCSC) +if (HAVE_PTHREAD) + message (STATUS " use system pthread functions") +endif (HAVE_PTHREAD) +if (HAVE_LIBUSB) + if(STATIC_LIBUSB EQUAL 1) + message (STATUS " You selected to enable static libusb") + endif(STATIC_LIBUSB EQUAL 1) + if(STATICLIBUSB AND NOT LIBUSBDIR) + message (STATUS " use static libusb functions") + else(STATICLIBUSB AND NOT LIBUSBDIR) + if (LIBUSBDIR AND STATIC_LIBUSB EQUAL 0) + message(STATUS " use system libusb from selected LIBUSBDIR functions") + elseif (LIBUSBDIR AND STATIC_LIBUSB EQUAL 1) + message(STATUS " use static libusb from selected LIBUSBDIR functions") + elseif(LIBUSBDIR) + message(STATUS " use system libusb from selected LIBUSBDIR functions") + elseif(NOT LIBUSBDIR AND NOT STATIC_LIBUSB) + message (STATUS " use system libusb functions") + endif(LIBUSBDIR AND STATIC_LIBUSB EQUAL 0) + endif(STATICLIBUSB AND NOT LIBUSBDIR) +endif (HAVE_LIBUSB) +if (HAVE_LIBDVBCSA) + if(STATIC_LIBDVBCSA EQUAL 1) + message (STATUS " You selected to enable static libdvbcsa") + endif(STATIC_LIBDVBCSA EQUAL 1) + if(STATICLIBDVBCSA AND NOT LIBDVBCSADIR) + message (STATUS " use static libdvbcsa functions") + else(STATICLIBDVBCSA AND NOT LIBDVBCSADIR) + if (LIBDVBCSADIR AND STATIC_LIBDVBCSA EQUAL 0) + message(STATUS " use system libdvbcsa from selected LIBDVBCSADIR functions") + elseif (LIBDVBCSADIR AND STATIC_LIBDVBCSA EQUAL 1) + message(STATUS " use static libdvbcsa from selected LIBDVBCSADIR functions") + elseif(LIBDVBCSADIR) + message(STATUS " use system libdvbcsa from selected LIBDVBCSADIR functions") + elseif(NOT LIBDVBCSADIR AND NOT STATIC_LIBDVBCSA) + message (STATUS " use system libdvbcsa functions") + endif(LIBDVBCSADIR AND STATIC_LIBDVBCSA EQUAL 0) + endif(STATICLIBDVBCSA AND NOT LIBDVBCSADIR) +endif (HAVE_LIBDVBCSA) + +if (WITH_EMU) + message (STATUS " Compile with EMU support") + if (WITH_SOFTCAM) + message (STATUS " SoftCam.Key will be linked as well") + endif (WITH_SOFTCAM) +endif (WITH_EMU) + +message (STATUS "") diff --git a/CODING.RULES.txt b/CODING.RULES.txt new file mode 100644 index 0000000..68765e3 --- /dev/null +++ b/CODING.RULES.txt @@ -0,0 +1,59 @@ +/* CODING RULES */ + +//single line comment + +/* ##### + * multi + * line + * comment + */ + +#include + +/* use tab == (4 spaces) + * or 2tab == (8 spaces) + * for increment + * and each level + */ + +//remove spaces at the end of lines + +//one empty line between functions +const char *idea_options(void){ +<------>if (sizeof(short) != sizeof(IDEA_INT)) // please do not use shortenings +<------>{ +<------><-->return ("idea(int)"); +<------>} else { +<------><-->return ("idea(short)"); +<------>} +} + +//alternative usual notation +<------>if (sizeof(short) != sizeof(IDEA_INT)) { // please do not use shortenings +<------><-->return ("idea(int)"); +<------>} else { +<------><-->return ("idea(short)"); +<------>} +} + +/* ########################## + * each function or procedure + * begins & ends with a brace + */ + +int main(void) +{ +<------>printf("hello, world\n"); +<------>for (count=1; count < 11; count = count + 1) +<------>{ +<------><-->printf(" %d\n", count); +<------>} + +<------>//one empty line between codeblocks +<------>while(i < x) +<------>{ +<------><-->printf(" %d\n", i); +<------><-->i++; +<------>} +<------>exit(0); +} diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Distribution/doc/example/oscam.ac b/Distribution/doc/example/oscam.ac new file mode 100644 index 0000000..d8cc74d --- /dev/null +++ b/Distribution/doc/example/oscam.ac @@ -0,0 +1,10 @@ +# +# anti-cascading table +# +# format: +# +# := +# + +0500:000000=10 +*=7 diff --git a/Distribution/doc/example/oscam.cacheex b/Distribution/doc/example/oscam.cacheex new file mode 100644 index 0000000..e2b3271 --- /dev/null +++ b/Distribution/doc/example/oscam.cacheex @@ -0,0 +1,12 @@ +# +# ECM length matching table +# +# format: +# +# matching: +# m:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1][,ECM length 2]...= +# [CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1][,ECM length 2]... +# + +m:1234:::::93=5678:::::93 # matching CAID 1234 and CAID 5678 with + # ECM length 93 \ No newline at end of file diff --git a/Distribution/doc/example/oscam.cert b/Distribution/doc/example/oscam.cert new file mode 100644 index 0000000..652dbdb --- /dev/null +++ b/Distribution/doc/example/oscam.cert @@ -0,0 +1,9 @@ +# +# Issuer Public Keys (IPK) +# +# format: +# +# CAID:reserved:KEY +# + +0100:00000000:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F diff --git a/Distribution/doc/example/oscam.conf b/Distribution/doc/example/oscam.conf new file mode 100644 index 0000000..2dcf29a --- /dev/null +++ b/Distribution/doc/example/oscam.conf @@ -0,0 +1,71 @@ +# +# main configuration +# + +[global] +nice = -1 +WaitForCards = 1 + +# logging + +logfile = /var/log/oscam/oscam.log +usrfile = /var/log/oscam/oscamuser.log +cwlogdir = /var/log/oscam/cw + +# monitor + +[monitor] +port = 988 +aulow = 120 +monlevel = 1 + +# web interface + +[webif] +httpport = 8888 +httpuser = myusername +httppwd = mypassword +httpallowed = 127.0.0.1,192.168.0.0-192.168.255.255,::1 + +# anti-cascading + +[anticasc] +enabled = 1 +numusers = 1 +sampletime = 2 +samples = 5 +penalty = 1 +aclogfile = /var/log/oscam/aclog.log +denysamples = 9 + +# protocols + +[newcamd] +key = 000102030405060708090A0B0C0D +port = 10000@0100:FFFFFF;10001@0200:FFF000,FFFF00;10002@0300:FFFFFF + +[radegast] +port = 20000 +user = user1 +allowed = 192.168.0.0-192.168.255.255 + +[cs378x] +port = 30000@0100:FFFFFF;30001@0200:FFF000,FFFF00;30002@0300:FFFFFF + +[cccam] +port = 40000 +version = 1.2.3 +build = 1234 +reshare = 2 + +[gbox] +hostname = host.example.com +port = 50000 +my_password = AB1122C1 + +[serial] +device = user2@/dev/ttyS0?delay=1&timeout=300;user3@192.160.0.10,2006?delay=1&timeout=5000 + +[dvbapi] +enabled = 1 +user = user1 diff --git a/Distribution/doc/example/oscam.dvbapi b/Distribution/doc/example/oscam.dvbapi new file mode 100644 index 0000000..129ac9c --- /dev/null +++ b/Distribution/doc/example/oscam.dvbapi @@ -0,0 +1,41 @@ +# +# dvbapi configuration +# +# format: +# +# priority: +# P: CAID[:][provider ID][:][service ID][:][ECM PID] [continue] +# +# ignore: +# I: CAID[:][provider ID][:][service ID][:][ECM PID] +# +# wait: +# D: CAID[:][provider ID][:][service ID][:][ECM PID] delay +# +# map: +# M: CAID[:][provider ID][:][service ID][:][ECM PID] target CAID[:][target provider ID] +# +# length: +# L: CAID[:][provider ID][:][service ID][:][ECM PID] length +# + +P: 0100:123456 # prioritise CAID 0100 with provider 123456 + +P: :1234 # prioritise ECM with provider ID 1234 on + # on any CAID and service + +P: 0200 # prioritise CAID 0200 + +P: 0300::9ABC # prioritise CAID 0300 on service 9ABC only + +M: 0400 0500:123456 # map CAID 0400 to provider ID 123456 with + # CAID 0500 + +D: 0600 200 # wait 200 ms before writing CW for CAID 0600 + +I: :654321 # ignore provider ID 654321 for every CAID and + # service + +I: 0 # ignore every CAID that was not handled before + +L: 0700 8e # ECM length for CAID 0700 to 8e (hexadecimal) \ No newline at end of file diff --git a/Distribution/doc/example/oscam.guess b/Distribution/doc/example/oscam.guess new file mode 100644 index 0000000..8c2a179 --- /dev/null +++ b/Distribution/doc/example/oscam.guess @@ -0,0 +1,9 @@ +# +# CAID guessing table +# +# format: +# +# : +# + +12:3456 diff --git a/Distribution/doc/example/oscam.ird b/Distribution/doc/example/oscam.ird new file mode 100644 index 0000000..d04dbad --- /dev/null +++ b/Distribution/doc/example/oscam.ird @@ -0,0 +1,10 @@ +# +# Irdeto guessing table +# +# format: +# +# ::: +# + +12:0000000A:12AB:CD01 + diff --git a/Distribution/doc/example/oscam.provid b/Distribution/doc/example/oscam.provid new file mode 100644 index 0000000..2b7be9a --- /dev/null +++ b/Distribution/doc/example/oscam.provid @@ -0,0 +1,14 @@ +# +# provider table +# +# format: +# +# :||| +# + +0100:012345|MyPay-TV1|Astra 19.2E|German +0200:543210|MyPay-TV2|Hot Bird 13.0E|English +0300:112233|MyPay-TV3|Hot Bird 13.0E|Italian +0400:223344|MyPay-TV4|Atra 28.2E|English + + diff --git a/Distribution/doc/example/oscam.server b/Distribution/doc/example/oscam.server new file mode 100644 index 0000000..7efb0e3 --- /dev/null +++ b/Distribution/doc/example/oscam.server @@ -0,0 +1,169 @@ +# +# reader configuration +# + +# serial reader + +[reader] +label = reader1 +protocol = mouse +detect = CD +device = /dev/ttyS0 +group = 1 +emmcache = 1,3,2 +services = services1 +caid = 0100 + +# USB serial reader + +[reader] +label = reader2 +protocol = mouse +detect = CD +device = /dev/ttyUSB0 +aeskey = 000102030405060708090A0B0C0D0E0F +group = 2 +emmcache = 1,3,2 +services = services2 +caid = 0200 + +# remote newcamd reader with fallback for group 1 + +[reader] +label = remote1 +protocol = newcamd +key = 0102030405060708091011121314 +device = 192.168.0.2,66666 +user = user1 +password = password1 +group = 1 +fallback = 1 + +# remote camd 3.78x reader with fallback for group 2 + +[reader] +label = remote2 +protocol = cs378x +device = 192.168.0.3,23456 +user = user2 +password = password2 +group = 2 +fallback = 1 + +# remote gbox reader + +[reader] +label = remote3 +protocol = gbox +device = 192.168.0.5,45678 +password = AF1BC100 +user = gbox_client +group = 3 +services = services1 + +# remote CCcam reader + +[reader] +label = remote4 +protocol = cccam +device = 192.168.0.5,45678 +user = user4 +password = password4 +group = 4 +caid = 0400 +cccversion = 1.2.34 +cccbuild = 5678 + +# remote radegast reader + +[reader] +label = remote5 +protocol = radegast +device = 192.168.0.6,56789 +group = 5 +caid = 0500 + +# PCSC reader + +[reader] +label = mypcscreader +protocol = pcsc +device = 0 +aeskey = 0102030405060708090a0b0c0d0e0f10 +group = 6 +caid = 0600 + +# Smargo Smartreader+ (Tripple Reader as well) using kernel drivers (recommended) + +[reader] +label = mysmargo +protocol = smargo +device = /dev/ttyUSB1 +aeskey = 0102030405060708090a0b0c0d0e0f10 +group = 7 +caid = 0700 + +# Smargo Smartreader+ using libusb + +[reader] +label = mysmartreader +protocol = smartreader +device = 001:002 +group = 8 +caid = 0800 + +# internal reader + +[reader] +label = myinternalreader +protocol = internal +device = /dev/sci0 +group = 9 +caid = 0900 + +# AD-Teknik AB Multiprogrammer 3.5 serial + +[reader] +label = reader9 +protocol = mp35 +detect = CD +device = /dev/ttyS1 +group = 10 +emmcache = 1,3,2 +services = services9 +caid = 1000 + +# AD-Teknik AB Multiprogrammer 3.6 USB + +[reader] +label = reader10 +protocol = mp35 +detect = CD +device = /dev/ttyUSB1 +group = 11 +emmcache = 1,3,2 +services = services10 +caid = 1100 + +# AD-Teknik AB USB Phoenix at 6.00 mhz + +[reader] +label = reader12 +protocol = mp35 +detect = CD +device = /dev/ttyUSB2 +mhz = 600 +group = 12 +emmcache = 1,3,2 +services = services11 +caid = 1200 + +# sc8in1 reader + +[reader] +label = reader12 +protocol = sc8in1 +device = /dev/ttyUSB3:1 +group = 13 +emmcache = 1,3,2 +caid = 1300 diff --git a/Distribution/doc/example/oscam.services b/Distribution/doc/example/oscam.services new file mode 100644 index 0000000..6eb23d3 --- /dev/null +++ b/Distribution/doc/example/oscam.services @@ -0,0 +1,19 @@ +# +# definition of services +# +# format: +# +# [name] +# caid=CAID[,CAID]... +# provid = provider ID[,provider ID]... +# srvid = service ID[,service ID]... +# + +[services1] +caid=0100 +provid=000001,ABCDEF +srvid=0001,0002,000A,000B + +[services2] +caid=0200 +srvid=0003,0004,000C,000D diff --git a/Distribution/doc/example/oscam.srvid b/Distribution/doc/example/oscam.srvid new file mode 100644 index 0000000..970b337 --- /dev/null +++ b/Distribution/doc/example/oscam.srvid @@ -0,0 +1,6 @@ +# +# service ID configuration file for web interface and monitor +# + +1111,2222:000A|provider A|Name C|TV|package E +3333,4444,5555:000B|provider B|Name D|Radio|package F diff --git a/Distribution/doc/example/oscam.tiers b/Distribution/doc/example/oscam.tiers new file mode 100644 index 0000000..667654e --- /dev/null +++ b/Distribution/doc/example/oscam.tiers @@ -0,0 +1,7 @@ +# +# TIERS +# + +0001,0002,0003:000a|my TIER 1 +0004:000b|my TIER 2 +0005:000b|my TIER 3 diff --git a/Distribution/doc/example/oscam.user b/Distribution/doc/example/oscam.user new file mode 100644 index 0000000..5704ee4 --- /dev/null +++ b/Distribution/doc/example/oscam.user @@ -0,0 +1,71 @@ +# +# user configuration +# + +# user for group 1 with Betacrypt tunnel, no monitoring, only one connection possible, AU enabled + +[account] +user = user1 +pwd = password1 +monlevel = 0 +uniq = 1 +group = 1 +au = reader1 +services = services1 +betatunnel = 0300.FFFF:0100,0400.FFFF:0100 +ident = 0100:000000 +caid = 0100 + +# user for group 2 with monitor access, AU enabled + +[account] +user = user2 +pwd = password2 +monlevel = 1 +uniq = 0 +group = 2 +au = reader2 +services = services2 +ident = 0200:000000 +caid = 0200 + +# user for group 2 without monitor access, AU disabled, account disabled + +[account] +user = user3 +pwd = password3 +disabled = 1 +group = 2 +services = services2 +ident = 0200:000000 +caid = 0200 + +# user for group 3 without monitor access, only one connection possible, AU disabled, multiple idents + +[account] +user = user4 +pwd = password4 +monlevel = 0 +uniq = 1 +group = 3 +ident = 0300:000000,FFFFFF;0400:FFFFFF +caid = 0300 + +# user for group 5 with anti-cascading, only 1 logging allowed, send fake CWs as penalty + +[account] +user = user5 +pwd = password5 +group = 5 +services = services5 +ident = 0500:000000 +caid = 0500 +numusers = 1 +penalty = 1 + +# user for gbox reader + +[account] +user = gbox_client +pwd = +group = 3 diff --git a/Distribution/doc/example/oscam.whitelist b/Distribution/doc/example/oscam.whitelist new file mode 100644 index 0000000..70a0eb7 --- /dev/null +++ b/Distribution/doc/example/oscam.whitelist @@ -0,0 +1,26 @@ +# +# whitelist table +# +# format: +# +# whitelist: +# w:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1][,ECM length 2]... +# +# whitelist, not proceed with any other ECM length whitelisting when matching: +# l:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1][,ECM length 2]... +# +# ignore: +# i:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1][,ECM length 2]... +# + +w:0100 # whitelisting for CAID 0100 + +i:0200:1234 # ignore CAID 0200 with service ID 1234 + +i:::::2345 # ignore CHID 2345 + +w: # allow all others (blacklist) + +l:0300 # whitelisting for CAID 0300 not proceeding + # if matching + diff --git a/Distribution/doc/html/list_smargo.1.html b/Distribution/doc/html/list_smargo.1.html new file mode 100644 index 0000000..1ffcdd5 --- /dev/null +++ b/Distribution/doc/html/list_smargo.1.html @@ -0,0 +1,36 @@ + +Man page of list_smargo + +

list_smargo

+Section: User Commands (1)
Index +Return to Main Contents
+ +  +

NAME

+ +list_smargo - list all connected Smartreader+ +  +

SYNOPSIS

+ +list_smargo +  +

DESCRIPTIONS

+ +The list_smargo software lists all connected Smartreader+ with bus number and device address. + +  +

SEE ALSO

+ +oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.1.html b/Distribution/doc/html/oscam.1.html new file mode 100644 index 0000000..4c7acb7 --- /dev/null +++ b/Distribution/doc/html/oscam.1.html @@ -0,0 +1,243 @@ + +Man page of oscam + +

oscam

+Section: User Commands (1)
Index +Return to Main Contents
+ +  +

NAME

+ +OSCam - SC server +  +

DESCRIPTIONS

+ +The OSCam software is an open source multi-protocol/multi-platform SC server. +

+Please check the compile options for included features in the binary. +

+OSCam supports the following protocols: +

+
+newcamd with cascading/remote server ECM support +
+camd 3.3x TCP +
+camd camd 3.5x / 3.57x UDP with cascading/remote server ECM support, ECM and EMM support with size > 256 bytes +
+camd 3.78x TCP with cascading/remote server ECM support, ECM and EMM support with size > 256 bytes +
+CCcam with cascading/remote server ECM support, ECM and EMM support with size > 256 bytes +
+DVB API with multi tuner and PIP support +
+gbox with cascading/remote server ECM support +
+serial (HSIC, SSSP, BOMBA, DSR 9500) +
+radegast +
OSCam works on the following platforms:
+
+Linux (Tuxbox, ARM, MIPS, MIPSel, SH-4, PowerPC, ...) +
+Windows (based on cygwin1.dll) +
+Mac OS X +
+  +

OPTIONS

+ +

+ +-a|--crash-dump +

+write oscam.crash on segfault (needs installed GDB and OSCam compiled with debug infos -ggdb) +
+ +

+ +-b|--daemon +

+starts in background, writing oscam.version with starttime and version info in temporary directory +
+ +

+ +-B|--pidfile <filename> +

+set PID file, overrides pidfile of oscam.conf, default:none +
+ +

+ +-c|--config-dir <directory> +

+read configuration from <directory>, default:see CS_CONFDIR in globals.h, +while starting OSCam prints warnings on invalid keywords, comment lines start with # character. +

+Autodiscover of the following directories will be done: +

+

+
+/etc/tuxbox/config +
+/etc/tuxbox/config/oscam +
+/config/oscam +
+/usr/keys +
+/var/etc +
+/var/etc/oscam +
+/var/keys +
+/var/oscam +
+/var/tuxbox/config +

+

+
+ +

+ +-d|--debug <level> +

+debug level mask: +

+
     0 = no debugging (default) +
     2 = ATR parsing info, ECM dumps, CW dumps +
     4 = traffic from/to the reader +
     8 = traffic from/to the clients +
    16 = traffic to the reader-device on IFD layer +
    32 = traffic to the reader-device on I/O layer +
    64 = EMM logging +
   128 = DVBAPI logging +
   256 = load balancing logging +
   512 = cache exchange logging +
  1024 = client ECM logging +
  2048 = CSP logging +
  4096 = CWC logging +
  8192 = CW Cache logging +
 65535 = debug all +

+ +

+ +-g|--gcollect <mode> +

+garbage collector debug mode, default:none: +

+
   1 = immediate free +
   2 = check for double frees +

+ +

+ +-h|--help +

+usage +
+ +

+ +-I|--syslog-ident <ident> +

+set syslog ident, default:oscam +
+ +

+ +-p|--pending-ecm <number> +

+maximum number of pending ECM packets, default:32, maximum:255 +
+ +

+ +-r|--restart <level> +

+restart level: +

+
   0 = disabled, restart request sets exit status to 99 +
   1 = restart activated, web interface can restart oscam (default) +
   2 = like 1, but also restart on segmentation faults +

+ +

+ +-S|--show-sensitive +

+do not filter sensitive info (card serial numbers) in the logs +
+ +

+ +-s|--capture-segfaults +

+capture segmentation faults +
+ +

+ +-t|--temp-dir <directory> +

+use <directory> for temporary data, default:temporary directory of OS +
+ +

+ +-V|--build-info +

+show OSCam version info +
+ +

+ +-w|--wait <seconds> +

+time waiting for system time to be set correctly +
+ +  +

SIGNALS

+ +

+ +SIGHUP +

+reinit user db, readers, TIERs, services, clients and anti-cascading, for newcamd connections: after reloading the ident, please restart newcamd client +
+ +

+ +SIGUSR1 +

+shift debug level to next level (see debug level mask above) +
+ +

+ +SIGUSR2 +

+get reader SC info +
+ +  +

SEE ALSO

+ +list_smargo(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
DESCRIPTIONS
+
OPTIONS
+
SIGNALS
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.ac.5.html b/Distribution/doc/html/oscam.ac.5.html new file mode 100644 index 0000000..7314a79 --- /dev/null +++ b/Distribution/doc/html/oscam.ac.5.html @@ -0,0 +1,54 @@ + +Man page of oscam.ac + +

oscam.ac

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.ac - anti-cascading table for OSCam +  +

SYNOPSIS

+ +anti-cascading table +  +

DESCRIPTIONS

+ +

+ +<CAID>:<provider ID>=<seconds> +

+define time cycles between CWs changes relating to CAID and provider ID +
+ +

+ +*=<seconds> +

+default time cycles between CWs changes required +
+ +  +

EXAMPLES

+ +
 0100:000000=10 +
 *=7 +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.cacheex.5.html b/Distribution/doc/html/oscam.cacheex.5.html new file mode 100644 index 0000000..7a66683 --- /dev/null +++ b/Distribution/doc/html/oscam.cacheex.5.html @@ -0,0 +1,53 @@ + +Man page of oscam.cacheex + +

oscam.cacheex

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam..cacheex - global ECM length matching configuration file for OSCam +  +

SYNOPSIS

+ +ECM length matching +  +

DESCRIPTIONS

+ +

+ +m:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]]=[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]] +

+
 ECM length matching from remote cache exchange partner to local  +
 cache, for cache exchange pull mode (cacheex = 1) only +
+ +  +

ANNONTATIONS

+ +Please use Unix text file format only. +  +

EXAMPLES

+ +
 m:1234:::::93=5678:::::93  # matching CAID 1234 and CAID 5678 with  +
                            # ECM length 93 +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid (5), oscam.srvid2(5),oscam.user(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
ANNONTATIONS
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.cert.5.html b/Distribution/doc/html/oscam.cert.5.html new file mode 100644 index 0000000..5e7944f --- /dev/null +++ b/Distribution/doc/html/oscam.cert.5.html @@ -0,0 +1,46 @@ + +Man page of oscam.cert + +

oscam.cert

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.cert - Issuer Public Keys (IPK) for OSCam +  +

SYNOPSIS

+ +Issuer Public Keys (IPK) +  +

DESCRIPTIONS

+ +

+ +CAID:reserved:IPK +

+mapping between CAID and IPK/sessions keys in hex, currently for Cryptoworks only +
+  +

EXAMPLES

+ +
 0100:00000000:0102030405060708090A0B0C0D0E0F + + +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + +
+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.conf.5.html b/Distribution/doc/html/oscam.conf.5.html new file mode 100644 index 0000000..4fe6251 --- /dev/null +++ b/Distribution/doc/html/oscam.conf.5.html @@ -0,0 +1,1985 @@ + +Man page of oscam.conf + +

oscam.conf

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.conf - main configuration file for OSCam +  +

SYNOPSIS

+ +The main configuration file for OSCam contains global parameters +such as debugging, logging, monitor, protocols and anti-cascading. +sections in oscam.conf are nonrecurring. The [global] +section is required. All other sections are optional. +  +

DESCRIPTIONS

+ +  +

The [global] section

+ +

+

+ +nice = -20..+20 +

+system priority, default:99 +
+ +

+ +pidfile = filename +

+set PID file, default:none +
+ +

+ +logfile = [filename][;syslog][;stdout] +

+logging targets, default:/var/log/oscam.log. You can define a maximum of one filename and +additionally to log to stdout or syslog (you can also only log to stdout or syslog and omit +the filename). +
+ +PP +initial_debuglevel = level +
+set initial debug level for OSCam start, default:0 +
+ +PP +sysloghost = hostname +
+set remote syslog host, default:none +
+ +

+ +syslogport = port +

+set TCP/IP port for remote syslog host, default:none +
+ +

+ +ecmfmt = format +

+define ECM log format, default:c&p/i/s/l:h +

+possible variables: +

+
  c = CAID +
  d = PID +
  e = CSP hash +
  g = ID of origin gbox peer +
  h = checksum +
  i = channel ID +
  j = distance of gbox hops +
  l = length +
  o = ONID +
  p = provider ID +
  s = service ID +
  w = CW +
  y = payload +

+use a value as prefix to hide variable with this value, control characters will be escaped by "\" +

+
 example: ecmfmt = c&0p/i/d/s/l:h.e_w +
          (hide provider ID if 0) +

+ +

+ +loghistorylines = lines +

+size of log message history in web interface or monitor, 0 = disabled, default:256 +
+ +

+ +maxlogsize = kbytes +

+maximum log file size, 0 = unlimited, default:10 +
+ +

+ +logduplicatelines = 0|1 +

+1 = enable logging of duplicate lines in the log, default:0 +
+ +

+ +disablelog = 0|1 +

+1 = disable log file, default:0 +
+ +

+ +cwlogdir = path +

+directory for CW logging, default:config dir +
+ +

+ +emmlogdir = path +

+directory for EMM logging, default:config dir +
+ +

+ +usrfile = filename +

+log file for user logging, default:none +

+log file format: +

+
 date +
 time +
 CWs per second +
 username +
 IP address of client +
 TCP/IP port +
 CWs found +
 CWs from cache +
 CWs not found +
 CWs ignored +
 CWs timed out +
 CWs tunneled +
 login time in unix/POSIX format +
 logout time in unix/POSIX format +
 protocol +

+ +

+ +disableuserfile = 0|1 +

+1 = avoid logging although userfile is set, default:1 (also set automatically if userfile is empty) +
+ +

+ +usrfileflag = 0|1 +

+usrfile logging mode: +

+
  0 = only client logon/logoff will be logged in usrfile (default) +
  1 = each zapping of a client will be logged in usrfile +

+ +

+ +disablemail = 0|1 +

+1 = disable saving NDS Videoguard mail messages from provider, default:1 +
+ +

+ +mailfile = file +

+define file saving NDS Videoguard mail messages from provider, default:none +
+ +

+ +enableled = 0|1|2 +

+
 0 = LED support disabled (default) +
 1 = LED support enabled for routers +
 2 = LED support enabled for Qbox HD +
+ +

+ +waitforcards = 0|1 +

+1 = wait for local SCs on startup before opening network ports, default:1 +
+ +

+ +waitforcards_extra_delay = delay +

+additional delay in milli-seconds after waiting for local SCs on startup before opening network ports, default:500 +
+ +

+ +preferlocalcards = 0|1 +

+SC decoding behavior: +

+
 0 = local SCs used like a remote reader +
 1 = prefer cache exchange based SCs (default) +
 2 = prefer local SCs +

+ +

+ +readerrestartseconds = seconds +

+seconds beetween restarts, 0 = disable reader restart, default:5 +
+ +

+ +block_same_ip = 0|1 +

+1 = reject looping ECMs from clients to readers with the same IP address, default:1 +
+ +

+ +block_same_name = 0|1 +

+1 = reject looping ECMs from clients to readers with the same name, default:1 +
+ +

+ +clienttimeout = milli-seconds|seconds +

+value (clienttimeout in seconds < 100, else milli-seconds) for client process to wait for key, default:5 +
+ +

+ +clientmaxidle = seconds +

+value for client process being idle before disconnect, 0 = idle disconnect disabled, default:120 +
+ +

+ +suppresscmd08 = 0|1 +

+0 = tell camd 3.5x, 3.57x and 3.78x clients not to request again for rejected +CAID, service ID and provider ID combination, 1 = disable, can be overwritten +per user in oscam.user, default:0 +
+ +

+ +fallbacktimeout = milli-seconds +

+time falling back to fallback reader, default:2500 +
+ +

+ +fallbacktimeout_percaid = milli-seconds +

+time falling back to CAID restricted fallback reader, default:2500 +
+ +

+ +sleep = minutes +

+time waiting for inactive users, default:none, can be overwritten per user in oscam.user +
+ +

+ +serverip = IP address +

+bind service to specified IP address, default:none +
+ +

+ +bindwait = seconds +

+value to wait for bind request to complete, default:120 +
+ +

+ +netprio = 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20 +

+value for network priority: +
IPP value will be applied to SO_PRIORITY (system internal prioritization) +
DSCP value will be applied to IP_TOS/IPV6_TCLASS (the TOS field in the IP packet header) +

+
   0 = IPP=0; DSCP=CS0 (default) +
   1 = IPP=1; DSCP=CS1 +
   2 = IPP=1; DSCP=AF11 +
   3 = IPP=1; DSCP=AF12 +
   4 = IPP=1; DSCP=AF13 +
   5 = IPP=2; DSCP=CS2 +
   6 = IPP=2; DSCP=AF21 +
   7 = IPP=2; DSCP=AF22 +
   8 = IPP=2; DSCP=AF23 +
   9 = IPP=3; DSCP=CS3 +
 10 = IPP=3; DSCP=AF31 +
 11 = IPP=3; DSCP=AF32 +
 12 = IPP=3; DSCP=AF33 +
 13 = IPP=4; DSCP=CS4 +
 14 = IPP=4; DSCP=AF41 +
 15 = IPP=4; DSCP=AF42 +
 16 = IPP=4; DSCP=AF43 +
 17 = IPP=5; DSCP=CS5 +
 18 = IPP=5; DSCP=EF +
 19 = IPP=6; DSCP=CS6 +
 20 = IPP=7; DSCP=CS7 +

+ +

+ +resolvegethostbyname = 0|1 +

+set mode for DNS resolving: +

+
  0 = getadressinfo (default) +
  1 = gethostbyname +

+ +

+ +failbancount = count +

+number of incorrect logins after an ip address will be blocked, default:0 +
+ +

+ +failbantime = minutes +

+time for IP based blocking for clients with an invalid login attempt, 0 = failban is disabled, default:0 +
+ +

+ +dropdups = 0|1 +

+mode for duplicate client connections (requirement: uniq > 0): +

+
  0 = mark client as duplicate, but don't disconnect them (default) +
  1 = drop duplicate connections instead of marking as duplicate +

+ +

+ +unlockparental = 0|1 +

+1 = unlock parental mode option to disable Seca and Viaccess pin code request for adult movie, default:0 +
+ +

+ +double_check = 0|1 +

+1 = ECM will be send to two or more readers with the same SC and the CWs will be verified against each other, lb_nbest_readers must be set to 2 or higher, default:0 +
+ +

+ +double_check_caid = [CAID1|first two digits of CAID1],[CAID2|first two digits of CAID2]... +

+ECM will be send to two or more readers with the same SC and the CWs will be verified against each other for defined CAID or first two bytes of CAID, lb_nbest_readers must be set to 2 or higher, default:none +
+ +getblockemmauprovid = 0|1 +
+1 = server overrides EMM blocking defined on client site, default:0 +
+ +

+ +lb_mode = mode +

+load balancing mode: +

+
 0 = load balance disabled, ECMs go to all readers (default) +
 1 = fastest reader first, after 5 ECMs the reader with the fastest  +
     response time will be selected +
 2 = oldest reader first, reader with the longest no answer +
 3 = lowest usage level, the usage level will be calculated by the  +
     sum of 5 ECMS response times, the higher a reader is busy, the  +
     higher is usage level +

+ +

+ +lb_save = 0|counts +

+save auto load balance statistics: +

+
      0 = saving of auto load balance statistics disabled (default) +
 counts = save auto load balance statistics every counts ECMs +
          (minimum 100) +

+To save CPU power a minimum counts of 100 is recommended. +

+ +

+ +lb_nbest_readers = counts +

+set count of best readers for load balancing, default:1 +
+ +

+ +lb_nfb_readers = counts +

+set count of fallback readers for load balancing, default:1 +
+ +

+ +lb_nbest_percaid = CAID1:count1[,CAID2:count2]... +

+set count of best readers per CAIDs for load balancing, wildcard CAIDs with two-digit CAIDs possible, default:none +

+
 example: lb_nbest_percaid = 0100:4,0200:3,03:2,04:1 +
          (wildcard CAIDs 03xx and 04xx) +

+ +

+ +lb_min_ecmcount = counts +

+minimal ECM count to evaluate load balancing values, default:5 +
+ +

+ +lb_max_ecmcount = counts +

+maximum ECM count before resetting load balancing values, default:500 +
+ +

+ +lb_reopen_seconds = seconds +

+time between retrying failed load balanced readers/CAIDs/providers/services, default:900 +
+ +

+ +lb_reopen_invalid = 0|fB1 +

+0 = E_INVALID will be blocked until statistics has been cleaned, default:1 +
+ +

+ +lb_force_reopen_always = 0|1 +

+1 = force reopening immediately all failing readers if no matching reader was found, default:0 +
+ +

+ +lb_retrylimit = milli-seconds +

+retry next load balanced reader only if response time is higher then lb_retrylimit, default:0 +
+ +

+ +lb_savepath = filename +

+filenanme for saving load balancing statistics, default:/tmp/.oscam/stat +
+ +

+ +lb_stat_cleanup = hour +

+hours after the load balancing statistics will be deleted, default:336 +
+ +

+ +lb_retrylimits = CAID1:time1[,CAID2:time2]... +

+load balancing retry limit time per CAID, wildcard CAIDs with two-digit CAIDs possible, default:none +

+
 example: lb_retrylimits = 12:0100,34:0200,5678:0300  +
          (wildcard CAIDs 12xx and 34xx) +

+ +

+ +lb_noproviderforcaid = CAID1[,CAID2]... +
+ignore provider information for CAIDs to reduce load balancing statistic data, +wildcard CAIDs with two-digit CAIDs possible, default:none +

+
 example: lb_noproviderforcaid = 0100,02,0300,04  +
          (wildcard CAIDs 02xx and 04xx) +

+ +

+ +lb_max_readers = limit +
+restrict the reader count to limit during load balancing learning: +

+
     0 = unlimited (default) +
 limit = restrict load balancer readers to limit +

+ +

+ +lb_auto_timeout = 0|1 +

+1 = enable automatic timeout based on load balancing statistics, default:0 +
+ +

+ +lb_auto_timeout_p = percent +

+percent added to average time as timeout time, default:30 +
+ +

+ +lb_auto_timeout_t = milli seconds +

+minimal time added to average time as timeout time, default:300 +
+ +

+ +lb_auto_betatunnel = 0|1 +

+1 = enable automatic Betacrypt tunneling detection for CAIDs 1801, 1833, 1834, and 1835 for load balancing, Betacrypt defintion in oscam.user with betatunnel will be prefered, default:1 +
+ +

+ +lb_auto_betatunnel_mode = 0|1|2 +

+set mode for automatic Betacrypt tunneling: +

+
 0 = CAID 18XX tunneling to CAID 17X2 only (default) +
 1 = CAID 18XX tunneling to CAID 17X2 and CAID 17X2 tunneling to CAID 18XX (CAID 1833/1801) +
 2 = CAID 18XX tunneling to CAID 17X2 and CAID 17X2 tunneling to CAID 18XX (CAID 1833/1834) +
 3 = CAID 18XX tunneling to CAID 17X2 and CAID 17X2 tunneling to CAID 18XX (CAID 1833/1835) +
 4 = CAID 17X2 tunneling to CAID 18XX (CAID 1833/1801 only) +
 5 = CAID 17X2 tunneling to CAID 18XX (CAID 1833/1834 only) +
 6 = CAID 17X2 tunneling to CAID 18XX (CAID 1833/1835 only) +

+ +

+ +lb_auto_betatunnel_prefer_beta = direction +

+set direction for automatic Betacrypt/Nagravision selection: +

+
   0 = disabled (default) +
   1 = always Betacrypt +
 105 = represents the middle +
 200 = always Nagravision +

+ +  +

The [monitor] section

+ +

+ +port = 0|port +

+UDP port for monitor, 0 = monitor disabled, default:0 +
+ +

+ +serverip = IP address +

+bind service to specified IP address, default:all +
+ +

+ +nocrypt = IP address|IP address range[,IP address|IP address range]... +

+unsecured monitor connection, default:none +

+
 example: nocrypt = 127.0.0.1,192.168.0.0-192.168.255.255 +

+ +

+ +aulow = minutes +

+time no EMM occurs so that client is set to low, switch from status "active" to "on", default:30 +
+ +

+ +monlevel = 0|1|2|3|4 +

+monitor level: +

+
 0 = no access to monitor +
 1 = only server and own procs +
 2 = all procs, but viewing only (default) +
 3 = all procs, reload of oscam.user possible +
 4 = complete access +

+monlevel can be overwritten per user in oscam.user. +

+ +

+ +hideclient_to = seconds +

+time to hide clients in the monitor if not sending requests, 0 = disabled, default:25 +
+ +  +

The [webif] section

+ +httpport = [+]port +
+port for web interface, 0 = disabled, praefix + = enable SSL, default:none, required +
+ +

+ +httpcert = file +

+file for http SSL certificate, default:oscam.pem +
+ +

+ +httpforcesslv3 = 0|1 +

+1 = force using SSLv3, default:0 +
+ +

+ +httpuser = username +

+username for password protection, default:none +
+ +

+ +httppwd = password +

+password for password protection, default:none +
+ +

+ +httpcss = path +

+path for external CSS file, default:none +
+ +

+ +http_prepend_embedded_css = 0|1 +

+1 = embedded CSS will be added before external CSS , default:0 +
+ +

+ +httptpl = path +

+path for external templates and picons, multiple simultaneously templates and +picons are possible by creating sub folders (maximum length of 32 alphanumeric +characters), sub folders naming is corresponding to sub folder in URL, default:none +

+
 example: httptpl = /this/is/my/path +

+
          folder with multiple templates: +
            /this/is/my/path/template1 +
            /this/is/my/path/template2 +

+
          valid URLs: +
           http://host:port/template1 +
           http://host:port/template2 +

+ +

+ +httpjscript = path +

+path for oscam.js javascript, default:none +
+ +

+ +httprefresh = seconds +

+status refresh in seconds, default:none +
+ +

+ +httphideidleclients = 0|1 +

+1 = enables hiding clients after idle time set in parameter hideclient_to, default:0 +
+ +

+ +httphidetype = type[type]... +

+characters defining columns to hide in web interface status page (see type +column), default:none +

+
 types: +

+
 'c': client +
 'h': http +
 'm': monitor +
 'p': proxy +
 'r': reader +
 's': server +
 'x': cache exchange +

+ +

+ +httpscript = path +

+path to an executable script which you wish to start from web interface, default:none +
+ +

+ +httpallowed = IP address|IP address range[,IP address|IP address range]... +

+http web interface connections allowed, default:127.0.0.1,192.168.0.0-192.168.255.255,10.0.0.0-10.255.255.255,::1 +
  +
 example: httpallowed = 127.0.0.1,192.168.0.0-192.168.255.255 +
+ +

+ +httpdyndns = hostname[,hostname][,hostname] +

+http web interface connections allowed, default:none +
  +
 example: httpdyndns = host.example.com +
          httpdyndns = host1.example.com,host2.example.com +
+ +

+ +httpsavefullcfg = 0|1 +

+write config: +

+
 0 = all not empty parameters, all not default parameters, all  +
     parameters not containing the same value as the same  +
     parameter in global configuration (default) +
 1 = all parameters +

+ +

+ +httpoverwritebakfile = 0|1 +

+1 = overwrite backup configuration files, default:0 +
+ +

+ +httpreadonly = 0|1 +

+1 = read only modus for web interface, default:0 +
+ +

+ +httpshowpicons = 0|1 +

+1 = show picons in user list, default:0 +
+ +

+ +httppiconpath = path +

+path to picons, default:none +
+ +

+ +httphelplang = en|de|fr|<available wiki languages> +

+set right language for wiki entry point, default:en +
+ +

+ +httplocale = environment +

+set the locale environment, default:none +
+ +  +

The [lcd] section

+ + +

+ +httposcamlabel = text +

+set individual label in web interface header, default:OSCam +
+ +

+ +enablelcd = 0|1 +

+1 =enable LCD output, default:0 +

+

+ +lcd_outputpath = path +
+path for LCD output, default:/tmp +
+ +

+ +lcd_hideidle = 0|1 +

+1 = hide reader in LCD output if reader idle > 20 seconds, default:0 +
+ +

+ +lcd_writeintervall = seconds +

+LCD refresh interval (minimum 5), default:10 +
+ + +  +

The [cache] section

+ +delay = milli-seconds +
+value to delay cached requests, default:0 +
+ +

+ +max_time = seconds +

+maximum time CWs resist in cache, the time must be 2 seconds highter than the parameter clienttimeout, default:15 +
+ +

+ +max_hit_time = seconds +

+maximum time for cache exchange hits resist in cache for evaluating wait_time, default:15 +
+ +

+ +wait_time = [caid][&mask][@provid][$servid][:awtime][:]dwtime[,[caid][&mask][@provid][$servid][:awtime][:]dwtime]... +

+wait time in milli-seconds for cache exchange and Cardservproxy before sending ECMs to reader or proxy, default:none +

+
 example: wait_time = 0:50:250,0200@00009X:50:150,15:950,0500@000001:150,1602&ffdf:1200 +

+ +

+ +cacheexenablestats = 0|1 +

+1 = enable statistics for cache exchange mode, default:0 +

+Please consider memory consumption. +

+ +

+ +cacheex_cw_check = [caid][&mask][@provid][$servid]:mode:counter[,[caid][&mask][@provid][$servid]:mode:counter]... +

+

+
 mode = specify behaviour for counter: +
               +
        0 = when wait_time expires, serve highest counter's CW  +
            got anyway, even if no counter reached (default) +
        1 = never serve CW from cache exchange stored in cache,  +
            if it's counter not reaches counter. When wait_time  +
            expires, requests will go to normal readers +
  +
 counter = set minimum CW counter to allow CW is used, default:1 +

+ +

+ +cacheex_mode1_delay = CAID1:time,[BCAID2:time]... +

+delay in milli-seconds for asking cache exchange mode 1 readers, default:none +
+ +

+ +csp_port = port +

+UDP port of Cardservproxy for cache exchange, default:none +
+ +

+ +csp_serverip = IP +

+bind Cardservproxy for cache exchange to specified IP address, default:none +
+ +

+ +csp_ecm_filter = [caid][&mask][@provid][$servid][,[caid][&mask][@provid][$servid]]... +

+Cardservproxy incoming ECM filter setting, default:none +
+ +

+ +csp_allow_request = 0|1 +

+allow incoming ECM request from Cardservproxy, default:1 +
+ +

+ +csp_allow_reforward = 0|1 +

+1 = reforward other cacheex updates to Cardservproxy peers, option could cause loops, default:0 +
+ +

+ +cwcycle_check_enable = 0|1 +

+1 = enable CW cycle check, default:0 +
+ +

+ +cwcycle_check_caid = CAID[,CAID]... +

+CAID enabled for CW cycle check, default:none +
+ +

+ +cwcycle_maxlist = count +

+maximum CW cycle list entries, default:500, maximum:4000 +
+ +

+ +cwcycle_keeptime = minutes +

+minimum time a learned cycle time resists in memory, default:15, maximum:15 +
+ +

+ +cwcycle_onbad = 0|1 +

+0 = log bad CW cycle only, 1 = drop bad CW cycle, default:1 +
+ +

+ +cwcycle_dropold = 0|1 +

+1= drop old CW cycle, default:1 +
+ +

+ +cwcycle_sensitive = 0|2|3|4 +

+drop CW mode: +

+
 0 = disabled +
 2 = 2 (or more) same bytes and drop new CW +
 3 = 3 (or more) same bytes and drop new CW +
 4 = 4 (or more) same bytes and drop new CW (default) +

+ +

+ +cwcycle_allowbadfromffb = 0|1 +

+1 = allow bad cycles from a fixed fallback reader, default:0 +
+ +

+ +cwcycle_usecwcfromce = 0|1 +

+1 = use CW info from cache exchange, default:0 +
+ +

+ +wait_until_ctimeout = 0|1 +

+answer when cache exchange timeout expires, if no normal readers are available for sending ECMs: +

+
 0 = immediately send 'not found' to client (default) +
 1 = wait for cache exchange answer until client timeout expires +

+ +  +

The [camd33] section

+ +port = 0|port +
+TCP port for camd 3.3x clients, 0 = disabled, default:0 +
+ +

+ +serverip = IP address +

+bind service to specified IP address, default:all +
+ +

+ +nocrypt = IP address|IP address range[,IP address|IP address range]... +

+unsecured camd 3.3x client connection, default:none +

+
 example: nocrypt = 127.0.0.1,192.168.0.0-192.168.255.255 +

+ +

+ +passive = 0|1 +

+1 = force passive camd 3.3x client, default:0 +
+ +

+ +key = 128 bit key +

+key for camd 3.3x client encryption, default:none +

+
 example: key = 01020304050607080910111213141516 +

+ +  +

The [cs357x] section

+ +

+ +port = 0|port +

+UDP port for camd 3.57x clients, 0 = disabled, default:0 +
+ +

+ +serverip = IP address +

+bind service to specified IP address, default:all +
+ +

+ +suppresscmd08 = 0|1 +

+0 = tell camd 3.5x / 3.57x clients not to request again for rejected CAID, service ID and provider ID combination, 1 = disable, can be overwritten per user in oscam.user, default:0 +
+ +  +

The [cs378x] section

+ +

+ +port = 0|port[@CAID][:provid][,provid]...[;port@CAID[:provid][,provid]...]... +

+TCP port/CAID/provid definitions for camd 3.78x clients, 0 = disabled, default:0 +

+
 examples: port = 10000@0100:100000;20000@0200:200000,300000,400000 +
           port = 30000 +

+ +

+ +serverip = IP address +

+bind service to specified IP address, default:all +
+ +

+ +keepalive = 0|1 +

+0 = disable camd 3.78x keepalive modus, default:0 +
+ +

+ +suppresscmd08 = 0|1 +

+0 = tell camd 3.78x clients not to request again for rejected CAID, service ID and provider ID combination, 1 = disable, can be overwritten per user in oscam.user, default:0 +
+ +  +

The [newcamd] section

+ +

+ +key = DES key +

+default key for newcamd client encryption, default:none +

+
 example: key = 0102030405060708091011121314 +

+ +

+ +port = port[{DES key}]@CAID[:provid][,provid]...[;port[{DES key}]@CAID[:provid][,provid]...]... +

+TCP port/DES key/CAID/provid definitions, default:none +

+
 example: port = 10000@0100:100000;20000{0102030405060708091011121314}@0200:200000,300000 +

+Each CAID requires a separate port. If you don't specify a DES key for a port, the default DES key will be used. +

+ +

+ +serverip = IP address +

+bind newcamd service to specified IP address, default:all +
+ +

+ +allowed = IP address|IP address range[,IP address|IP address range]... +

+newcamd client connections allowed from, default:none +

+
 example: allowed = 127.0.0.1,192.168.0.0-192.168.255.255,::1 +

+ +

+ +keepalive = 0|1 +

+0 = disable newcamd keepalive modus, default:0 +
+ +

+ +mgclient = 0|1 +

+1 = provide share information of all available CAIDs and provider IDs to mgcamd clients, default:0 +
+ +  +

The [radegast] section

+ +

+ +port = 0|port +

+TCP/IP port for radegast clients, 0 = disabled, default:0 +
+ +

+ +serverip = IP address +

+bind service to specified IP address, default:all +
+ +

+ +allowed = IP address|IP address range[,IP address|IP address range]... +

+client connections allowed from, default:none +

+
 example: allowed = 127.0.0.1,192.168.0.0-192.168.255.255 +

+ +

+ +user = username +

+user name for radegast client +
+ +  +

The [serial] section

+ +

+ +device = <user>@<device>[:baud][?option1=value1[&option2=value2]...] +
         [;<user>@<device>[:baud][?option1=value1[&option2=value2]...]]... +

+

+parameters: +
 user   = account +
 device = serial device name|hostname|IP,port +
 baud   = serial port speed (for serial devices only) +
 option = timeout = milli-seconds, timeout for connection,  +
                    default:50 +
          delay   = milli-seconds, additional delay between two +
                    characters, default:0 +

+supported serial devices (autodection): +
 HSIC    (humax sharing interface client) +
 SSSP    (simple serial sharing protocol) +
 bomba   (BOMBA firmware) +
 dsr9500 (DSR 9500) +
  +
 example: user1@/dev/ttyS1:115200?delay=1&timeout=5000 +
          user2@192.160.0.1,12345?delay=1&timeout=5000 +

+ +  +

The [cccam] section

+ +

+ +port = 0|port[,0|port]... +

+TCP/IP ports for CCcam clients, 0 = disabled, default:0 +
+ +

+ +version = <main version>.<version>.<sub version> +

+define CCcam version, minimum CCcam version 2.0.11, used with original CCcam only, default:none +

+
 example: version = 1.2.34 +

+ +

+ +reshare = level +

+reshare level for CCcam clients (default:10): +

+-1 = no resharing +
 0 = resharing for direct peer only +
 1 = resharing for direct peer and next level +
 x = resharing for direct peer and next x level +

+ +

+ +reshare_mode = mode +

+CCcam reshare mode: +

+
 0 = reader reshares only received SCs for CCcam readers,  +
     defined filters/CAIDs/provids on other readers +
 1 = reader reshares received SCs (like=0) and defined services +
 2 = reader reshares only defined reader services as virtual SCs +
 3 = reader reshares only defined user services as virtual SCs +
 4 = reader reshares only received SCs (default) +

+Every server is shared as hop = 0 and with defined reshare values. +

+Service reshare only works if positive services defined: no service - no reshare! +

+ +

+ +ignorereshare = 0|1 +

+CCcam reshare setting: +

+
 0 = use reshare setting of server (default) +
 1 = use reshare setting of reader or user +

+ +

+ +stealth = 0|1 +

+1 = behaviour like the original CCcam: no activate partner detection and +extended OSCam-CCcam protocol, prevent other OSCam to detect the server +as OSCam server, default:0 +
+ +

+ +minimizecards = mode +

+mode how to provide CCcam servers to CCcam clients: +

+
 0 = no aggregation, remove duplicates only (default) +
 1 = based on minimum hop: two SCs with different hops are  +
     summarized, new SCs get a smaller hop +
 2 = aggregation based on CAIDs: all SCs with the same CAIDs  +
     will be merged, provider (maximum 32) will be merged, too +

+ +

+ +updateinterval = seconds +

+interval to provide share list update to CCcam clients, values <= 10 are invalid and will be set to 30, default:240 +
+ +

+ +keepconnected = 0|1 +

+set CCcam keepalive modus: +

+
  0 = disconnect client when maximum idle time is reached +
  1 = keep client connected (default) +

+ +

+ +recv_timeout = milli-seconds +

+set network timeout for receiving data, default:2000 +
+ +

+ +forward_origin_card = 0|1 +

+1 = forward ECM request to reader holding this card, +load balancer, fallback and caching will be disabled, default:0 +
+ +

+ +nodeid = ID +

+set CCcam node ID in hex, default:none +

+
 example: nodeid = 0a0b0c0d0e0f1011 +

+ +  +

The [gbox] section

+ +

+ +hostname = hostname| IP address +

+set hostname or IP address for gbox protocol, default:none +
+ +

+ +port = port[,port]... +

+UDP port for gbox server, default:0 +
+ +

+ +my_password = password +

+password for connection to local gbox peer, default:none +
+ +

+ +proxy_card = <CAID><provid>[,<CAID><provid>]... +

+proxy reader SCs to be reshared into gbox network, default:none +
+ +

+ +ccc_reshare = 0|1 +

+1 = enable CCCam reshare, default:0 +
+ +

+ +my_vers = version +

+set gbox version in hexadecimal low byte, default:2A +
+ +

+ +my_cpu_api = byte +

+set gbox CPU and API byte in hexadecimal, default:40 +
+ +

+ +gbox_reconnect = time +

+send message to peers in seconds, default:180, min:60, max:300 +
+ +

+ +log_hello = 0|1 +

+1 = log hello messages, default:1 +
+ +

+ +dis_attack_txt = 0|1 +

+1 = disable attack.txt, default:0 +
+ +

+ +gsms_disable = 0|1 +

+1 = disable gbox short message service (GSMS), default:1 +

+sending a messeage: /tmp/gsms.txt: <box ID> <1=mormal message|2=OSD/TV message> +<message 6 to 127 characters>, status will be stored in '/tmp/gsms.ack' +respective 'gsms.nack', receiving a message: The message will be stored in +/tmp/gsms.log +

+ +

+ +tmp_dir = path +

+temporary directory for gbox, default:/tmp/.oscam +
+ +

+ +accept_remm_peer = peer-id1[,peer-id2]... +

+accept REMM requests from gbox peer(s), default:none +
+ +  +

The [scam] section

+ +

+ +port = port +

+UDP port for scam server, default:0 +
+ +  +

The [dvbapi] section

+ +

+ +enabled = 0|1 +

+1 = DVB API enabled, default:0 +

+Create file /tmp/.pauseoscam to pause DVB API, e.g. if STB goes into standby and OSCam remains as SC server only. +

+ +

+ +listen_port = 0|port +

+TCP/IP port for SAT IP clients, filtering has to be done on client site, 0 = disabled, default:0 +
+ +

+ +serverip = IP address +

+bind service to specified IP address, default:none +
+ +

+ +user = username +

+user name for DVB API client, default:anonymous +
+ +

+ +ignore = <CAID>[,<CAID>]... (detached by oscam.dvbapi, obsolete) +

+CAIDs to be ignored, default:none +
+ +

+ +services = <service ID>[,<service ID>]... (detached by oscam.dvbapi, obsolete) +

+services to be prioritized, default:none +
+ +

+ +priority = <CAID>:<provider ID>[,CAID:<provider ID>]... (detached by oscam.dvbapi, obsolete) +

+CAIDs and provider IDs to be prioritized, default:CAIDs and provider IDs of local SCs will be prioritized +
+ +

+ +au = 0|1 +

+AU mode: +

+
 0 = disable AU (default) +
 1 = enable AU +

+ +

+ +pmt_mode = 0|1|2|3|4|5 +

+PMT mode: +

+
 0 = use camd.socket and PMT file, default +
 1 = disable reading PMT file +
 2 = disable camd.socket +
 3 = read PMT file on startup only +
 4 = do not use signal handler for monitoring /tmp +
 5 = do not use signal handler for monitoring /tmp,  +
     disable camd.socket +

+ +

+ +ecminfo_file = 0|1 +

+ecm.info file: +

+
 0 = Disable ecm.info file +
 1 = Enable ecm.info file (default) +

+ +

+ +ecminfo_type = 0|1|2|3|4|5 +

+ecm.info types: +

+
 0 = OSCam syntax (default) +
 1 = OSCam syntax with ECM time in ms instead of seconds +
 2 = WiCardd +
 3 = mgcamd +
 4 = CCcam +
 5 = camd3 +

+ +

+ +request_mode = 0|1 +

+CAID request mode: +

+
 0 = try all possible CAIDs one by one (default) +
 1 = try all CAIDs simultaneously +

+ +

+ +boxtype = dbox2|dreambox|dm7000|duckbox|ufs910|ipbox|ipbox-pmt|qboxhd|coolstream|neumo|samygo|pc +

+set boxtype, auto detection of DVB API will be aspired, default:dreambox +

+ipbox with camd.socket support, currently only with PGI image version 0.6 or above, +verified on HD models only +

+ipbox-pmt can be used on any DGS based images (with or without camd.socket support), +verified on HD models only +

+pc is for generic pc support (currently supported on VDR with vdr-plugin-dvbapi) +

+ +

+ +read_sdt = 0|1|2 +

+mode of provider, channel name and service type auto detection via SDT: +

+
 0 = disabled (default) +
 1 = enabled for non FTA channels only +
 2 = enabled for all channels +

+ +

+ +write_sdt_prov = 0|1 +

+mode writing provider name into oscam.srvid2 file: +

+
 0 = disabled (default) +
 1 = enabled +

+ +

+ +demuxer_fix = 0|1 +

+try fixing audio/video sync errors: +

+
 0 = disabled (default) +
 1 = enabled +

+ +

+ +cw_delay = milli-seconds +

+delay of CW writing, default:none +
+ +

+ +delayer = milli-seconds +

+minimum time to write CW, default:0 +
+ +

+ +reopenonzap = 0|1 +

+1 = reopen demux devices on each channel switching, default:0 +
+ +  +

The [anticasc] section

+ +

+ +enabled = 0|1 +

+1 = enable anti-cascading, default:0 +
+ +

+ +numusers = quantity +

+anti-cascading: user per account, 0 = anti-cascading disabled, default:0 +
+ +

+ +sampletime = minutes +

+duration of sample, default:2 +
+ +

+ +samples = quantity +

+quantity of samples over limit, default:10 +
+ +

+ +penalty = 0|1|2|3 +

+level of penalty: +

+
 0 = only logging (default) +
 1 = send fake CWs +
 2 = temporary user ban +
 3 = send delayed CWs +

+penalty can be overwritten per user in oscam.user. +

+ +

+ +aclogfile = filename +

+file for anti-cascading logging, default:none +
+ +

+ +fakedelay = milli-seconds +

+fake delay time, default:1000, minimum value is 100, maximum value is 3000 +
+ +

+ +denysamples = quantity +

+how many samples should be penalized, default:8 +
+ +

+ +acosc_enabled = 0|1 +

+1 = enable anti-cascading over SID count, default:0 +
+ +

+ +acosc_max_active_sids = count +

+maximum active SIDs with anti-cascading over SID, 0 = unlimited, default:0 +

+Can be overwritten per user in oscam.user. +

+ +

+ +acosc_max_ecms_per_minute = count +

+maximum ecms per minute, 0 = unlimited, default:0 +

+Can be overwritten per user in oscam.user. +

+ +

+ +acosc_zap_limit = count +

+zap limit for anti-cascading over SID, 0 = unlimited, default:0 +

+Can be overwritten per user in oscam.user. +

+ +

+ +acosc_penalty = 0|1|2|3|4 +

+level of penalty with anti-cascading over SID count: +

+
 0 = only logging (default) +
 1 = send fake CWs +
 2 = temporary user ban +
 3 = send delayed CWs +
 4 = temporary hidecards to the client +

+Can be overwritten per user in oscam.user. +

+ +

+ +acosc_penalty_duration = seconds +

+penalty duration for anti-cascading over SID count, default:0 +

+Can be overwritten per user in oscam.user. +

+ +

+ +acosc_delay = milli-seconds +

+delay for anti-cascading over SID count, default:0 +

+Can be overwritten per user in oscam.user. +

+ +  +

LOGGING

+ +
+
+reader stages +

+
 1 = cacheex (=1) reader (C) +
 2 = local SCs (L) +
 3 = other reader / proxies (P) +
 4 = fallback reader (F) +

+logging format +

+
 stage/used/chosen/possible +

+  +

MONITOR

+ +

+monitor commands: +

+
+login <user> <password> +

+login (for unencrypted connections only) +

+

+getuser <user> <parameter>=<value> +

+get parameter for user +
  +

+setuser <user> <parameter>=<value> +

+set parameter for user +
  +

+setserver <parameter>=<value> +

+set parameter for server +
  +

+exit +

+exit monitor +
  +

+log <on|onwohist|off> +

+enable|enable without hitory|disable logging for 2 minutes +

+

+status +

+list of current processes and clients +

+

+shutdown +

+shutdown OSCam +
  +

+restart +

+restart OSCam +
  +

+keepalive +

+send keepalive +
  +

+reload +

+reinit user db, clients and anti-cascading, for newcamd connections: after reloading the provid, please restart newcamd client +

+

+details <PID> +

+details about selected PID +

+

+reread +

+read again +

+

+debug <level> +

+set debug level (monlevel > 3 required) +

+debug level mask: +
     0 = no debugging (default) +
     1 = detailed error messages +
     2 = ATR parsing info, ECM dumps, CW dumps +
     4 = traffic from/to the reader +
     8 = traffic from/to the clients +
    16 = traffic to the reader-device on IFD layer +
    32 = traffic to the reader-device on I/O layer +
    64 = EMM logging +
   128 = DVB API logging +
   256 = load balacing logging +
   512 = cache exchange logging +
  1024 = client ECM logging +
 65535 = debug all +

+version +

+show OSCam version +

+

+commands +

+show all valid monitor commands +

+  +

WEB INTERFACE

+ +
+
+template system +

+The web interface allows you to create your own template. For developing your +own template request the orignal template with the non-linked page +savetemplates.html. Store your own template in the directory specified +by httptpl. +

+  +

CACHING

+ +types of ECM caching: +
+
+cache1 +

+ECM and CW in cache already. +

+cache2 +

+ECM and checksum in cache already. +

+  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
+
The [global] section
+
The [monitor] section
+
The [webif] section
+
The [lcd] section
+
The [cache] section
+
The [camd33] section
+
The [cs357x] section
+
The [cs378x] section
+
The [newcamd] section
+
The [radegast] section
+
The [serial] section
+
The [cccam] section
+
The [gbox] section
+
The [scam] section
+
The [dvbapi] section
+
The [anticasc] section
+
+
LOGGING
+
MONITOR
+
WEB INTERFACE
+
CACHING
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.dvbapi.5.html b/Distribution/doc/html/oscam.dvbapi.5.html new file mode 100644 index 0000000..82e07b2 --- /dev/null +++ b/Distribution/doc/html/oscam.dvbapi.5.html @@ -0,0 +1,142 @@ + +Man page of oscam.dvbapi + +

oscam.dvbapi

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.dvbapi - DVB API configuration file for OSCam +  +

SYNOPSIS

+ +DVBAPI settings, first match - first used +  +

DESCRIPTIONS

+ +

+ +P: [CAID]:[provider ID]:[service ID]:[ECM PID]:[CHID] [force]:[PIDx] priority +

+
 set priority, continue = 1: proceed with priority, recommended for  +
 pay-per-view services / EMMs (use carefully),  although local SCs  +
 will be prioritised higher
, default:none +
+ +

+ +I: [CAID]:[provider ID]:[service ID]:[ECM PID]:[CHID] [PIDx] ignore +

+
 set ignore +
+ +

+ +J: [CAID]:[provider ID]:[service ID]:[ECM PID] joined CAID:joined provider ID:joined ECM PID +

+
 join to another ECM PID +
+ +

+ +A: ::service ID:[PMT PID] :[provider ID][:][ECM PID] +

+
 set a dummy ECM request with CAID FFFF for services with a constant CW shown as unencrypted service  +(for STBs with PMT PID support only) +
+ +

+ +A: ::service ID:[video PID] :[provider ID][:][ECM PID] +

+
 set a dummy ECM request with CAID FFFF for services with a constant CW shown as unencrypted service  +(for STBs without PMT PID support only) +
+ +

+ +X: [CAID]:[provider ID]:[service ID]:[ECM PID] +

+
 add decoding on an extra demux index on the same CA device (Multi ECM) (not support on all STBs) +
+ +

+ +D: [CAID]:[provider ID]:[service ID]:[ECM PID] delay +

+
 set delay in milli-seconds writing CWs +
+ +

+ +M: [CAID]:[provider ID]:[service ID]:[ECM PID] target CAID[:][target provider ID] +

+
 mapping +
+ +

+ +S: [device] [PMT file name] +

+
 set DVB API device name and PMT file name (valid for STAPI only) +
+ +

+ +L: [CAID]:[provider ID]:[service ID]:[ECM PID] length +

+
 set ECM length in hexadecimal +
+ + +  +

ANNONTATIONS

+ +Please use Unix text file format only. +  +

EXAMPLES

+ +
 P: 0100:123456       # prioritise CAID 0100 with provider 123456 +

+
 P: :1234             # prioritise ECM with provider ID 1234 on +
                      # on any CAID and service +

+
 P: 0200              # prioritise CAID 0200 +

+
 P: 0300::9ABC        # prioritise CAID 0300 on service 9ABC only +

+
 P: 0400 1            # prioritise CAID 0400 for pay-per-view services +
  +
 P: : 1               # prioritise for EMMs +

+
 M: 0500 0600:123456  # map CAID 0500 to provider ID 123456 with +
                      # CAID 0600 +

+
 D: 0700 200          # wait 200 ms before writing CW for CAID 0700 +

+
 I: :654321           # ignore provider ID 654321 for every CAID and +
                      # service +

+
 I: 0                 # ignore every CAID that was not handled before +

+
 L: 0800 8e           # ECM length for CAID 0800 to 8e (hexadecimal) +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.user(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
ANNONTATIONS
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.guess.5.html b/Distribution/doc/html/oscam.guess.5.html new file mode 100644 index 0000000..06f8cc4 --- /dev/null +++ b/Distribution/doc/html/oscam.guess.5.html @@ -0,0 +1,45 @@ + +Man page of oscam.guess + +

oscam.guess

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.guess - CAID guessing table for OSCam +  +

SYNOPSIS

+ +CAID guessing table +  +

DESCRIPTIONS

+ +

+ +<length of ECM>:<CAID> +

+CAID guessing table by len in hex, only needed for protocols (at the moment BOMBA protocol only) that does not pass CAIDs +
+ +  +

EXAMPLES

+ +
 12:3456 +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + +
+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.ird.5.html b/Distribution/doc/html/oscam.ird.5.html new file mode 100644 index 0000000..b8d86a0 --- /dev/null +++ b/Distribution/doc/html/oscam.ird.5.html @@ -0,0 +1,46 @@ + +Man page of oscam.ird + +

oscam.ird

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.ird - Irdeto guessing table for OSCam +  +

SYNOPSIS

+ +Irdeto guessing table +  +

DESCRIPTIONS

+ +

+ +<byte3>:<byte4-7>:<CAID>:<SID> +

+Irdeto guessing table by signature +
+ +  +

EXAMPLES

+ +
 12:0000000a:12ab:cd01 +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.provid.5.html b/Distribution/doc/html/oscam.provid.5.html new file mode 100644 index 0000000..dd8a2df --- /dev/null +++ b/Distribution/doc/html/oscam.provid.5.html @@ -0,0 +1,46 @@ + +Man page of oscam.provid + +

oscam.provid

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.provid - provider table for OSCam +  +

SYNOPSIS

+ +provider table +  +

DESCRIPTIONS

+ +

+ +<caid>:<provid>|<provider>|<satellite>|<language> +

+provider table +
+ +  +

EXAMPLES

+ +
 0100:012345|MyPay-TV|Astra 19E|German +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.ratelimit.5.html b/Distribution/doc/html/oscam.ratelimit.5.html new file mode 100644 index 0000000..67f85a0 --- /dev/null +++ b/Distribution/doc/html/oscam.ratelimit.5.html @@ -0,0 +1,63 @@ + +Man page of oscam.ratelimit + +

oscam.ratelimit

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.ratelimit - ECMs ratelimit for OSCam +  +

SYNOPSIS

+ +limit rate of ECMs allowed for an interval +  +

DESCRIPTIONS

+ +

+ +CAID:provider ID:service ID:ChID:ratelimitecm:ratelimitseconds:srvidholdseconds +

+

+ratelimitecm +

+number of different SIDs in ECMs allowed for an interval +
+ +

+ratelimitseconds +

+interval in seconds for ratelimit +
+ +

+srvidholdseconds +

+extra time in seconds this service ID is kept in a slot before another service ID can take its place +
+ +
+  +

EXAMPLES

+ +
 0100:00002A:3A3A:4A00:0002:0010:0004 + + +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + +
+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.server.5.html b/Distribution/doc/html/oscam.server.5.html new file mode 100644 index 0000000..48fc01d --- /dev/null +++ b/Distribution/doc/html/oscam.server.5.html @@ -0,0 +1,1109 @@ + +Man page of oscam.server + +

oscam.server

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.server - reader configuration file for OSCam +  +

SYNOPSIS

+ +The server configuration file for OSCam contains reader parameters. +sections in oscam.server are recurring (more than one reader possible). +At least one [reader] section is required. +  +

DESCRIPTIONS

+ +  +

The [reader] section

+ +

+ +label = name +

+name for reader, required +
+ +

+ +enable = 0|1 +

+0 = deactivate reader, default:1 +
+ +

+ +description = text +

+description of reader, default:none +
+ +

+ +protocol = reader protocol +

+reader protocol, required: +

+
 camd35|cs357x +
 cccam +
 cs378x +
 constcw +
 gbox +
 ghttp +
 internal +
 mouse +
 mp35 +
 newcamd|newcamd525 +
 newcamd524 +
 pcsc +
 radegast +
 scam +
 sc8in1 +
 serial +
 smargo +
 smartreader +

+ +

+ +device = [<readertype>;]serial:serialnum|bus:device| +
         <device|device:slot>| +
         <ip|hostname>,<port>[,<lport>]| +
         <ip|hostname>,<gboxpport>| +
         <ip|hostname>,<scamport>| +
         pcsc| +
         <0|1>>| +
         constantcw +

+define local or remote reader +

+
 readertype:  set reader type +

+
                SR:       Smartreader+ (default) +
                Infinity: Infinity USB +
                TripleP1: Smargo Triple Reader port 1 +
                TripleP2: Smargo Triple Reader port 2 +
                TripleP3: Smargo Triple Reader port 3 +

+
 bus:device:  bus name and device name of the Smartreader+ or  +
              Infinity USB (get the names with lsusb 'Bus'  +
              and 'Device') +

+
 serialnum:   serial number of reader of the Smartreader+ or  +
              Infinity USB +

+
 device:      device name +

+
 device:slot: device name and slot number sc8in1 [1-8] +
              (only one SC8in1 reader supported) +

+
 ip|hostname: IP address or host name +

+
 port:        TCP/IP port +

+
 lport:       remapping to local TCP/IP port +

+
 gboxpport:   UDP port for remote gbox peer +

+
 PCSC:        number of PCSC reader, starting with 0 +

+
 0|1:         for Coolstream HD-1 STB only: select reader 0 or  +
              reader 1 +

+
 constantcw:  constant CW file name +

+constant CW file format: +

+
• standard format
+

+CAID:Provider ID:Service ID:PMT ID:ECM PID::key (16 Bytes seperated by spaces) +

+example: 1234:123456:1234:2345:3456::00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F +

• extended OScam format
+

+CAID:Provider ID:Service ID:PMT ID:ECM PID:Video PID:key (16 Bytes seperated by spaces) +

+example: 1234:123456:1234:2345:3456:7890:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F +

+
+ +

+ +detect = [!]CD|[!]DSR|[!]CTS|[!]RING|[!]NONE|[!]gpio[1-7] +

+status detect of card, NONE = no detection, ! = inverse, default:CD +
+ +

+ +cardmhz = mhz +

+set standard SC frequency in units of 10 kHz, for Irdeto SC set to 600 mhz, +for Dreambox DM800 / DM8000 set to 2700 mhz, for Dreambox DM7025 set to +8300 mhz, for older PowerPC Dreambox STBs set to 3150 mhz, refer to +OVERCLOCKING, default:357 +
+ +

+ +mhz = frequency +

+set reader frequency in units of 10 kHz, if mhz > cardmhz you +are in overclocking mode. For Smargo readers and Dreambox internal readers +frequency will be set by ATR if autospeed is set to 1, default:357 +
+ +

+ +autospeed = 0|1 +

+1 = sets mhz according to ATR. Currently only used for smartreader, Smargo and +Dreambox internal protocol, other readers will be adapted to use this parameter +as well. If You wan't to overclock you're card set it to 0, default:1 +
+ +

+ +deprecated = 0|1 +

+First the SC will be initialized in normal mode. If it fails, the SC will be automatically +reverted to deprecated mode, so that the SC speed will not be changed and the communication +will remain on normal ATR speed of 9600 baud. +

+1 = use deprecated SC mode only, default:0 +

+ +

+ +mode = mode +

+set card init mode for AzBox internal reader, default:none +
+ +

+ +smargopatch = 0|1 +

+1 = enable workaround for Smartreader+ reader until native mode works, default:0 +
+ +

+ +sc8in1_dtrrts_patch = 0|1 +

+1 = enable fix for SC8in1/MCR DTR/RTS kernel bug, default:0 +
+ +

+ +use_gpio = 0|1 +

+1 = use GPIO to init the reader. This needs to be set on WRT54G router, default:0 +
+ +

+ + ins2e06 = payload +

+add check control for pin payload (4 hex bytes) for NDS Videoguard 2 SCs, valid for physical readers only, default:none +
+ +

+ +ins7e = payload +

+add 26 hex-bytes payload for NDS Videoguard 2 SCs, valid for physical readers only, default:none +
+ +

+ +ins7e11 = TA1 byte +

+set TA1 byte for NDS Videoguard 2 SCs, valid for physical readers only, default:none +
+ +

+ +fix07 = 0|1 +

+1=enable 0x07 fix for NDS Videoguard 2 SCs, valid for physical readers only, default:1 +
+ +

+ +force_irdeto = 0|1 +

+1 = force Irdeto SC mode even if RSA key is set for Irdeto tunnled Nagravion SC, default:0 +
+ +

+ +nagra_read = 0|1|2 +

+read Nagravison records (on NCMED SCs only): +

+
 0 = disabled (default) +
 1 = read all records with expired rights +
 2 = read records with valid rights only +

+ +

+ +rsakey = RSA key +

+RSA key for Nagravision/Tiger SCs, CAM key data for Irdeto SCs, Conax SCs, default:none +
+ +

+ +deskey = DES key +

+DES key for Viaccess SCs post-processing, default:none +
+ +

+ +boxkey = box key +

+box key for Nagravision SCs / CAM key for Irdeto SCs +
+ +

+ +pincode = pincode +

+pincode for Conax, Cryptoworks and Viaccess SCs, default:none +
+ +

+ +fix9993 = 0|1 +

+1 = enable fix for 9993 error with CAID 0919 Videoguard SCs, default:0 +
+ +

+ +readtiers = 0|1|2 +

+method to get tiers of NDS Videoguard SCs: +

+
 0 = disabled (default) +
 1 = ins70 method +
 2 = ins76 method +

+ +

+ +boxid = NDS box ID +

+NDS receiver box id +
+ +

+ +ndsversion = 0|1|12|2 +

+set NDS Videoguard version +

+
  0 = autodetection (default) +
  1 = NDS Videoguard 1 +
 12 = NDS Videoguard 1+ +
  2 = NDS Videoguard 2 +

+ +

+ +aeskeys = CAID #0@provid:AES key #0 CAID #0[,AES key #1 CAID #0],...[;CAID #1@provid:AES key #0 CAID #1[,AES key #1 CAID #1],...]... +

+multiple 16 bytes AES keys for Viaccess SCs (the used postprocessing AES key is specified through the D2 nano of the ECM) +

+special AES keys: +

+
 00 = do not return any CW, no AES key specified +
 FF = return CW received from the S, no AES key specified +

+example: +

+
 aeskeys = 0500@012345:000102030405060708090a0b0c0d0e0f;0500@543210:000102030405060708090a0b0c0d0e0f,0,0f0e0d0c0b0a090807060504030201 +

+ +

+ +key = DES key +

+key for newcamd remote reader encryption +
+ +

+ +user = name +

+user for remote reader +
+ +

+ +password = password +

+password for remote reader +
+ +

+ +services = [!]services[,[!]<services>]... +

+reader [de]assignment to service group, default=none +
+ +

+ +caid = <CAID>[&<mask>][:<target CAID>][,<CAID>[&<mask>][:target <CAID>]]... +

+define and mapping of CAIDs for reader, default:all CAIDs with mask FFFF +

+example: caid = 0100 +
         caid = 0200&ffee:0300 +
         caid = 0400&ff00:0500,0600 +
         caid = 0702,0722 +
         caid = 0702&ffdf (shortcut for the example above) +

+ +

+ +ident = <CAID>:<provid>[,provid]...[;<CAID>:<provid>[,provid]...]... +

+set CAID and SC specific ident for reader +

+example: ident = 0100:123456,234567;0200:345678,456789 +

+ +

+ +class = [!]class[,[!]class]... +

+set SC specific class in hex for reader +

+example: class = 01,02,!1b,!2b +

+ +

+ +chid = CAID:ChID +

+set SC specific ChIDs for reader, default:none +

+example: chid = 0100:12 +

+ +

+ +group = 1..64[,1..64]... +

+reader assingment to groups, default:none, required +
+ +

+ +audisabled = 0|1 +

+1 = exclude reader from auto AU, default:0 +
+ +

+ +auprovid = provider ID +

+set provider ID to use the right reader for auto AU +

+example: auprovid = 123456 +

+ +

+ +disableserverfilter = 0|1 +

+1 = ignore caid and provid settings of reader due faulty clients, default:0 +
+ +

+ +inactivitytimeout = seconds +

+inactivity timeout for all TCP based remote readers, -1 = reconnect on network failure for newcamd, even in idle, default:0 +
+ +

+ +reconnecttimeout = seconds +

+reconnect if missing answers from a remote reader, default:30 +
+ +

+ +reconnectdelay = milli-seconds +

+set maximum TCP connection block delay, default:60000 +
+ +

+ +connectoninit = 0|1 +

+1 = allow newcamd connections to be established on startup although there isn't a request yet, default:0 +
+ +

+ +keepalive = 0|1 +

+1 = allow cs378x TCP socket to be always connected, default:0. Always on if cacheex reader type. +
+ +

+ +fallback = 0|1 +

+1 = define reader as fallback, standard and fallback reader must have the same group, default:0 +
+ +

+ +fallback_percaid = <CAID>[:<ident>[,ident]]...[;<CAID>[:<ident>[,ident]]...].... +

+use reader as fallback for defined CAIDs only, two-digit wildcard CAIDs are possible, fallback_percaid overrules fallback, default:none +

+
 example: fallback_percaid = 1234:234567;89;10:345678 +

+ +

+ +emmcache = usecache,rewrite,logging +

+set EMM cache of local reader: +

+
 usecache = 0|1||2 +

+
            0 = EMM caching disabeld (default) +
            1 = enable EMM caching and save EMMs to file after  +
                stopping OSCam +
            2 = enable EMM caching, don't save EMMs to file  +
                after stopping OSCam +

+
 rewrite  = determines how often one and the same EMM is +
            written, default:0 +

+
 logging  = EMM logging mask: +

+
             0 = EMM logging disabled (default) +
             1 = logging EMM errors +
             2 = logging written EMMs +
             4 = logging skipped EMMs +
             8 = logging blocked EMMs +
            16 = logging disabled AU +

+
 example: emmcache = 1,3,2 +

+ +

+ +cacheex = 0|1|2|3 +

+set cache exchange mode +

+
 0: disable cache exchange mode (default) +
 1: enable cache exchange pull mode +
 2: enable cache exchange push mode for camd 3.5x / 3.57x and CCcam  +
    protocol +
 3: enable reverse cache exchange push mode for camd 3.5x / 3.57x  +
    and CCcam protocol +

+Identical cache exchange modes must be set on local OSCam server and remote OSCam user asccount. +

+Please consider memory consumption. +

+ +

+ +cacheex_maxhop = hops +

+define maximum hops for cache exchange, default=10 +
+ +

+ +csp_ecm_filter = [caid][&mask][@provid][$servid],n +

+cache exchange incoming ECM filter setting (mode 2 only) for Cardservproxy, default:none +
+ +

+ +cacheex_drop_csp = 0|1 +

+1 = drop incoming Cardservproxy cache (mode 2 only), detection is zero ecmd5, default:0 +
+ +

+ +cacheex_allow_request = 0|1 +

+1 = allow incoming ECM request (mode 2), default:1 +
+ +

+ +cacheex_allow_filter = 0|1 +

+1 = allow cache exchange filter (for cache exchange mode 2 only), default:1 +
+ +

+ +cacheex_block_fakecws = 0|1 +

+1 = enable fake DCWs blocking (for cache exchange mode 2 only), get fake DCWs form oscam.fakecws, default:0 +
+ +

+ +ecmwhitelist = [CAID[@provid]:]length[,length]...[;[CAID[@provid]:]length[,length]...]... +

+set valid ECM length per CAID and provid in hex, default:none,provid=000000 +

+example: ecmwhitelist = 10,20,0a,0b +
         ecmwhitelist = 0100:10,20;0200@123456:0a,4b +

+In normal operation mode this parameter is not required. +

+ +

+ +ecmheaderwhitelist = [CAID[@provid]:]header[,header]...[;[CAID[@provid]:]header[,header]...]... +

+set vaild ECM header per CAID and provid in hex, default:none,provid=000000 +
+ +

+ +ratelimitecm = count +

+number of different SIDs in ECMs allowed for an interval, default:0 +
+ +

+ +ecmnotfoundlimit = count +

+number of ECMs with "not found" answer until the reader will be restarted, 0 = no limit, default:0 +
+ +

+ +resetcycle = count +

+number of ECMs until SC reset is performed, 0 = disabled, valid for physical readers only, default:0 +
+ +

+ +ratelimitseconds = seconds +

+interval for rate limit, default:0 +
+ +

+ +ecmunique = 0|1 +

+1 = enable check for matching ECM hash in ratelimit slot , default:0 +
+ +

+ +srvidholdseconds = seconds +

+time to keep service ID in ratelimit slot, during this time checkeding for ecmunique is disbaled, default:0 +
+ +

+ +cooldown = delay,duration +

+
 define cooldown: +

+
 delay:    delay in seconds for which the reader is allowed to do  +
           more ECM requests than defined by ecmratelimit,  +
           default: none  +

+
 duration: duration in seconds the reader needs to cooldown,  +
           default:none +

+ratelimitecm and ratelimitseconds are required +

+ +

+ +blocknano = nano[,nano]...|all +

+list of EMM-nanos to block (in hex w/o 0x) or all EMM-nanos, valid for physical readers only, default:none +

+
 example: blocknano = 45,93,7a,ff +
          blocknano = all +

+ +

+ +blockemm-u = 0|1 +

+1 = block unique EMMs, default:0 +
+ +

+ +blockemm-s = 0|1 +

+1 = block shared EMMs, default:0 +
+ +

+ +blockemm-g = 0|1 +

+1 = block global EMMs, default:0 +
+ +

+ +blockemm-unknown = 0|1 +

+1 = block unknown types of EMMs, default:0 +
+ +

+ +blockemm-bylen = [length range,length range]... +

+block all types of EMMs by length, default:none +

+
 example: blockemm-bylen = 1-10,11- +

+ +

+ +read_old_classes = 0|1 (Viaccess SCs only) +

+0 = read only active entitlements +1 = read all entitlements (default) +
+ +

+ +saveemm-u = 0|1 +

+1 = save unique EMMs to log file, default:0 +
+ +

+ +saveemm-s = 0|1 +

+1 = save shared EMMs to log file, default:0 +
+ +

+ +saveemm-g = 0|1 +

+1= save global EMMs to log file, default:0 +
+ +

+ +saveemm-unknown = 0|1 +

+1 = save unknown types of EMMs to log file, default:0 +
+ +

+ +savenano = nano[,nano]....|all (obsolete) +

+list of EMM-nanos to save (in hex w/o 0x) or all EMM-nanos, only valid for physical readers, default:none +

+
 example: savenano = 45,93,7a,ff +
          savenano = all +

+ +

+ +readnano = [path]filename +

+write file (usually a copy of a file saved by savenano) to your smartcard, if no path is specified, the specified file is searched for in the configuration directory, only valid for physical readers, default:none +

+
 example: readnano = write.emm +
          readnano = /var/oscam/write.emm +

+ +

+ +dropbadcws = 0|1 +

+1 = reject bad CWs, send "not found" instead of bad CWs, default:0 +
+ +

+ +disablecrccws = 0|1 +

+1 = disable CRC for CW, default: 0 +

+In normal operation mode this parameter is not required. Parameter is incompatible with DVB standard. +

+ +

+ +ident = <CAID1>[:<ident1>[,<ident2>]...][;<CAID2>[:<ident3>[,<ident4>]...]]... +

+use this reader as local in loadbalancer's reader selection, default:none +
+ +

+ +lb_whitelist_services = <services>,<services>... +

+reader assignement to service group for channels which may never be blocked by the loadbalancer to the reader , default=none +
+ +

+ +lb_weight = weight +

+the higher the value the higher the probability for reader selection in load balacing mode, default:100 +

+
 It's an divider for the average responstime. +

+ +

+ +lb_force_fallback = 0|1 +

+1 = set the reader always as fallaback for load balacing without considering the reader's statistics, default:0 +
+ +

+ +cccversion = <main version>.<version>.<sub version> +

+set CCcam version, default:none +

+example: cccversion = 1.2.34 +

+ +

+ +cccmaxhops = hops +

+set CCcam maximum SC distance hops, default:10 +

+
 -1 = disabled +
  0 = remote local SCs only +
  1 = remote local SCs and + 1 hop +
  2 = remote local SCs and + 2 hops +
 and so on +

+After reading this SC hop will be incremented by one. +

+ +

+ +ccchop = hop +

+set hop for non CCCam readers, default:0 +
+ +

+ +cccreshare = hop +

+set reader's CCcam reshare hop, default:0 +

+
 -1 = reshare value off cccam in global config +
  0 = resharing for direct peer only +
  x = resharing for direct peer and share level x +

+ +

+ +cccwantemu = 0|1 +

+1 = request to provide emu from CCCam server, too, default:0 +
+ +

+ +ccckeepalive = 0|1 +

+1 = send keepalive messages to keep connection to remote CCCam server up, default:0 +
+ +

+ +cccreconnect = timeout +

+reconnect again after ECM request timeout in milli-seconds, default:4000 +
+ +

+ +cccmindown = number +

+filters all readers with hops smaller than number, default:0 +
+ +

+ +gbox_reshare = level +

+gbox reshare level of local cards, default:0 +
+ +

+ +gbox_max_distance = distance +

+maximum distance to receive gbox peer cards, default:2 +
+ +

+ +gbox_max_ecm_send = number +

+maximum of gbox peers ECMs will be send to, default:3 +
+ +

+ +use_ssl = 0|1 +

+1 = use SSL for ghttp protocol, default:0 +
+ +  +

OVERCLOCKING

+ +
+
+Dreambox and other internal readers +

+For Dreambox and other internal readers the highest possible clockrate will be +auto detected. The mhz parameter lets you override the values chosen by +OSCam, if it differs from 357 and 358, but usually you will not set any value +for mhz. +

+For certain Dreamboxes (especially PPC clones) the default mhz parameter leads +to slow ECM times and/or "not found" ECMs. By setting mhz to values like +200, 300, 400, ... 1600 you can find a value that works for your receiver and +your card. The higher the mhz value, the slower the ECM time (strange enough). +

+If you choose the value too low, your card is not recognized (no ATR or "card +not supported"). If you choose the value too high, you get slow ECM times. Our +experience is that either no mhz line, or a line mhz = 1000 works +best. +

+Phoenix / Smartmouse reader +

+Overclocking does not work with Windows and Mac OS X. +Set mhz equivalent to the frequency of the reader. +OSCam can not set the frequency of the reader. +

+Smargo Smartreader+ +

+Use protocol = smargo for the FDDI kernel drivers (no libusb needed) or (not +recommended) use protocol = smartreader for OSCam's driver implementation +based on libusb. +

+Set the reader frequency with the native Smargo Smartreader+ tool (srp_tools). +If not setting mhz and cardmhz, OSCam tries to set the baudrate +automatically, according to the maximum speed indicated by ATR. Overclocking +is possible. +

+

+ +OSCam tries to set the baudrate automatically. +A standard serial port has limited baudrate settings, so SC overclocking might not work. +When using a serial reader the best way for overclocking is connecting it to a FTDI based USB to serial port adapter. +

+If overclocking does not work, verify the effective baudrate in the logfile. +If it deviates too much from the requested baudrate, the SC will not be recognized (no ATR) +and the value for mhz should be adjusted again. +The higher the baudrate, the more accurate the effective baudrate can be. +  +

CACHE EXCHANGE

+ +
+
+pull mode (on request: cache exchange from remote to local OSCam) +

+ECM requests will be forwarded to the remote cache exchange partner. If the CW +could not be found in the cache of the remote exchange partner, a not found +will be answered. If the CW could not be found in the cache of the remote +exchange partner but a pending ECM request is open, the request will be +re-initiated after the wait time defined in cacheexwaittime. +

+push mode (continuous: cache exchange from remote to local OSCam) +

+CWs from the remote cache exchange partner will be forwarded to the local +cache. Forwarding only works while the camd camd 3.5x / 3.57x or CCcam +protocol connection between the local and remote OSCam has been established. +

+reverse push mode (continuous: cache exchange from local to remote OSCam) +

+CWs from the local cache will be forwarded to the remote cache exchange +partner. Forwarding only works while the camd camd 3.5x / 3.57x or CCcam +protocol connection between the remote and local OSCam has been established. +

+  +

EXAMPLES

+ +
+
+serial mouse compatible reader +
  +
 [reader] +
 label    = myserialmousereader +
 detect   = cd +
 protocol = mouse +
 device   = /dev/ttyS1 +
 group    = 1 +
 caid     = 0100 +
 services = myservice,!thisservice +
+USB mouse compatible reader +
  +
 [reader] +
 label    = myusbmousereader +
 detect   = cd +
 protocol = mouse +
 device   = /dev/ttyUSB0 +
 aeskey   = 0102030405060708090a0b0c0d0e0f10 +
 group    = 2 +
 caid     = 0200 +
+camd 3.78x reader +
  +
 [reader] +
 label    = mycamd378xreader +
 protocol = cs378x +
 device   = 192.168.0.1,1234 +
 user     = user1 +
 password = password1 +
 group    = 3 +
+newcamd reader +
  +
 [reader] +
 label    = mynewcamdreader +
 protocol = newcamd +
 key      = 0102030405060708091011121314 +
 device   = 192.168.0.2,2345 +
 user     = user2 +
 password = password2 +
 group    = 4 +
+CCcam reader +
  +
 [reader] +
 label      = mycccamreader +
 protocol   = cccam +
 device     = 192.168.0.3,3456 +
 user       = user3 +
 password   = password3 +
 group      = 5 +
 caid       = 0300,0400,0500 +
 cccversion = 1.2.3 +
+PCSC reader +

+
 [reader] +
 label    = mypcscreader +
 protocol = pcsc +
 device   = 0 +
 aeskey   = 0102030405060708090a0b0c0d0e0f10 +
 group    = 6 +
 caid     = 0600 +

+Smargo Smartreader+ +

+
 [reader] +
 label    = mysmartreader +
 protocol = smartreader +
 device   = 001:002 +
 aeskey   = 0102030405060708090a0b0c0d0e0f10 +
 group    = 7 +
 caid     = 0700 +

+internal reader +

+
 [reader] +
 label    = myinternalreader +
 protocol = internal +
 device   = /dev/sci0 +
 group    = 8 +
 caid     = 0800 +

+sc8in1 reader +

+
 [reader] +
 label    = mysc8in1reader +
 protocol = sc8in1 +
 device   = /dev/ttyUSB0:1 +
 group    = 9 +
 caid     = 0900 +

+constant CW +

+
 [reader] +
 label    = myconstantcw +
 protocol = constcw +
 device   = /var/keys/constant.cw +
 group    = 10 +

+gbox reader +

+
 [reader] +
 label    = mygboxreader +
 protocol = gbox +
 device   = 192.168.0.4,45678,56789 +
 user     = user4 +
 password = password4 +
 group    = 11 +
 caid     = 1100 +

+  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
+
The [reader] section
+
+
OVERCLOCKING
+
CACHE EXCHANGE
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.services.5.html b/Distribution/doc/html/oscam.services.5.html new file mode 100644 index 0000000..854cf3c --- /dev/null +++ b/Distribution/doc/html/oscam.services.5.html @@ -0,0 +1,72 @@ + +Man page of oscam.services + +

oscam.services

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.services - definition of services for OSCam +  +

SYNOPSIS

+ +service definitions +  +

DESCRIPTIONS

+ +  +

The [<service name>] section

+ +service name section, service name sections are recurring, required, maximum 64 services are allowed +

+ +caid = CAID[,CAID]... +

+listing of CAIDs in hex +
+ + +

+ +provid = provider ID[,provider ID]... +

+listing of provider IDs in hex +
+ + +

+ +srvid = service ID[,service ID]... +

+listing of service IDs in hex +
+ +  +

EXAMPLES

+ +
 [myservice] +
 CAID=0100,0200,000A +
 provid=000001,ABCDEF +
 srvid=0001,0002,000A,000B +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.srvid(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
+
The [<service name>] section
+
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.srvid.5.html b/Distribution/doc/html/oscam.srvid.5.html new file mode 100644 index 0000000..9d6a7fb --- /dev/null +++ b/Distribution/doc/html/oscam.srvid.5.html @@ -0,0 +1,58 @@ + +Man page of oscam.srvid + +

oscam.srvid

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.srvid - service ID configuration file for OSCam +  +

SYNOPSIS

+ +service ID mappings +  +

DESCRIPTIONS

+ +

+ +CAID[,CAID]...:service ID|[provider]|[name]|[type]|[description] +

+

+mapping between CAID, service ID, provider, name, type and description of service +
+ +  +

ANNONTATIONS

+ +Please use Unix text file format only. +

+You only need the oscam.srvid when using the monitor or the web interface. +For saving memory consumption only insert the service IDs you really need. Some +external programs use their own oscam.srvid and do not need the oscam.srvid of OSCam. +  +

EXAMPLES

+ +
 0001,0002,0003:000a|my provider 1|tv name 1|tv|my tv package +
 0004,0005,0006:000a|my provider 2|radio name 2|radio|my radio package +
 0006:000b|my provider 3|tv name 3|  +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
ANNONTATIONS
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.srvid2.5.html b/Distribution/doc/html/oscam.srvid2.5.html new file mode 100644 index 0000000..6681600 --- /dev/null +++ b/Distribution/doc/html/oscam.srvid2.5.html @@ -0,0 +1,51 @@ + +Man page of oscam.srvid2 + +

oscam.srvid2

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.srvid2 - service ID configuration file for OSCam +  +

SYNOPSIS

+ +service ID mappings +  +

DESCRIPTIONS

+ +

+ +service ID:CAID[:@provider ID[@provider ID]...][,:CAID[:@provider ID[@provider ID]...]][name]|[type]|[description]|[provider] +

+

+mapping between service ID, CAID, provider ID, name, type, description and proivder +
+ +  +

ANNONTATIONS

+ +Please use Unix text file format only. +

+You only need the oscam.srvid2 when using the monitor or the web interface. +For saving memory consumption only insert the service IDs you really need. Some +external programs use their own oscam.srvid2 and do not need the oscam.srvid2 of OSCam. +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
ANNONTATIONS
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.tiers.5.html b/Distribution/doc/html/oscam.tiers.5.html new file mode 100644 index 0000000..24f5ae3 --- /dev/null +++ b/Distribution/doc/html/oscam.tiers.5.html @@ -0,0 +1,54 @@ + +Man page of oscam.tiers + +

oscam.tiers

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.tiers - TIER configuration file for OSCam +  +

SYNOPSIS

+ +TIER mappings +  +

DESCRIPTIONS

+ +

+ +CAID[,CAID]...:TIER ID|description +

+

+mapping between CAID, TIER ID and description of TIER +
+ +  +

ANNONTATIONS

+ +Please use Unix text file format only. +  +

EXAMPLES

+ +
 0001,0002,0003:000a|my TIER 1 +
 0004:000b|my TIER 2 +
 0005:000b|my TIER 3 +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.user(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
ANNONTATIONS
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.user.5.html b/Distribution/doc/html/oscam.user.5.html new file mode 100644 index 0000000..8788277 --- /dev/null +++ b/Distribution/doc/html/oscam.user.5.html @@ -0,0 +1,542 @@ + +Man page of oscam.user + +

oscam.user

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.user - user configuration file for OSCam +  +

SYNOPSIS

+ +The user configuration file for OSCam contains user definitions. [account] +sections in oscam.user are recurring (more than one account). +  +

DESCRIPTIONS

+ +  +

The [account] section

+ +

+ +user = name +

+account name, required +
+ +

+ +pwd = password +

+password for account, required +
+ +

+ +description = text +

+description of user account +
+ +

+ +disabled = 0|1 +

+1 = account disabled, default:0 +
+ +

+ +hostname = hostname +

+host from which user connection is allowed +
+ +

+ +expdate = <year>-<month>-<day>|<year>/<month>/<day> +

+expiration date for account, default:none +

+
 example: expdate = 2001-11-21 +
          expdate = 2002/12/22 +

+ +

+ +allowedprotocols = [camd33][,][camd35][,][cs357x][,][cs378x][,][newcamd][,][cccam][,][gbox][,][radegast] +

+list of all allowed connection protocols, default:all connection protocols +
+ +

+ +allowedtimeframe = DAY@hh:mm-hh:mm[,hh:mm-hh:mm][,hh:mm-hh:mm][;DAY@hh:mm-hh:mm[,hh:mm-hh:mm][,hh:mm-hh:mm]]
+                         +where DAY is SUN,MON,TUE,WED,THU,FRI,SAT or ALL (for all possible days) +

+account enabled from hh:mm to hh:mm for the specified days, default:none
+
+comma (,) to separate times and semicolon(; ) to separate the different days.
+You can use ALL@ if you want the same time frames for everyday.
+
+Example:
+allowedtimeframe = ALL@10:00-22:00;MON@00:00-02:00,02:45-04:37;FRI@00:00-10:00,22:00-24:00;SAT@00:00-24:00
+
+If you use: DAY@22:00-05:00 this will be turned into DAY@00:00-05:00,22:00-24:00
+
+ALL@ is always checked and used, so you can watch TV the whole day on FRIday in this exemple. There is no problem to overlap ALL@ in a day definition, like for SAT@ definition.
+
+ +

+ +max_connections = count +

+maximum allowed connections per user when unique level will be adducted, default:1 +
+ +

+ +uniq = 0|1|2|4 +

+unique level: +

+
 0 = disabled (default) +
 1 = only one connection per user is allowed +
 2 = set user to fake if source ip is different  +
     (e.g. for newcamd clients with different CAIDs and ports) +
 3 = only one connection per user, but only the last login  +
     will survive (old MpCS behavior) +
 4 = set user only to fake if source ip is different,  +
     but only the last login will survive  +

+ +

+ +numusers = quantity +

+anti-cascading: user per account, 0 = anti-cascading disabled, -1 = global value from oscam.conf, default:-1 +
+ +

+ +penalty = 0|1|2 +

+level of penalty: +

+
 -1 = level of oscam.conf (default) +
  0 = only logging +
  1 = send fake CWs +
  2 = temporary user ban +
  3 = send delayed CWs +

+ +

+ +fakedelay = 0|1|milli-seconds +

+set fake delay time individually for user: +

+
  0 = disable fake delay +
 -1 = fake delay of oscam.conf (default) +

+ +

+ +acosc_max_ecms_per_minute = count +

+maximum ecms per minute, 0 = unlimited, default:0 +

+Can be overwritten per user in oscam.user. +

+ +

+ +acosc_max_active_sids = count +

+maximum active SIDs with anti-cascading over SID, 0 = unlimited, -1 = use global setting, default:0 +
+ +

+ +acosc_zap_limit = count +

+zap limit for anti-cascading over SID, 0 = unlimited, -1 = use global setting, default:0 +
+ +

+ +acosc_penalty = 0|1|2|3|4|-1 +

+level of penalty with anti-cascading over SID count: +

+
  0 = only logging (default) +
  1 = send fake CWs +
  2 = temporary user ban +
  3 = send delayed CWs +
  4 = temporary hidecards to the client +
 -1 = use global setting +

+ +

+ +acosc_penalty_duration = seconds +

+penalty duration for anti-cascading over SID count, -1 = use global setting, default:0 +
+ +

+ +acosc_delay = milli-seconds +

+delay for anti-cascading over SID count, -1 = use global setting, default:0 +
+ +

+ +failban = 0|2|4|8 +

+mask for IP address based blocking: +

+
 0 = ignore (default) +
 2 = block IP address of a disabled account on connecting +
 4 = block IP address of a sleeping account while sleeping comes up +
 8 = block duplicate IP address +

+ +

+ +lb_nbest_readers = counts +

+set count of best readers for load balancing, -1 = use global lb_nbest_readers, default:-1 +
+ +

+ +lb_nfb_readers = counts +

+set count of fallback readers for load balancing, -1 = use global lb_nfb_readers, default:1 +
+ +

+ +lb_nbest_percaid = CAID1:count1[,CAID2:count2]... +

+set count of best readers per CAIDs for load balancing, wildcard CAIDs with two-digit CAIDs possible, default:none +

+
 example: lb_nbest_percaid = 0100:4,0200:3,03:2,04:1 +
          (wildcard CAIDs 03xx and 04xx) +

+ +

+ +preferlocalcards = 0|1 +

+SC decoding behavior:. +

+
 -1 = global value from oscam.conf (default) +
  0 = local SCs used like a remote reader +
  1 = prefer cache exchange based SCs +
  2 = prefer local SCs +

+ +

+ +cwc_disable = 0|1 +

+1 = disbale CW cycle check, default:0 +
+ +

+ +cacheex = 0|1|2|3 +

+set cache exchange mode +

+
 0: disable cache exchange mode (default) +
 1: enable cache exchange pull mode +
 2: enable cache exchange push mode for camd 3.5x / 3.57x and  +
    CCcam protocol +
 3: enable reverse cache exchange push mode for camd 3.5x / 3.57x  +
    and CCcam protocol +

+Identical cache exchange modes must be set on local OSCam user account and remote OSCam server. +

+Please consider memory consumption. +

+ +

+ +cacheex_maxhop = hops +

+define maximum hops for cache exchange, default=10 +
+ +

+ +no_wait_time = 0|1 +

+set wait time behaviour: +
  +
 0: use wait_time set in oscam.conf (default) +
 1: do not use wait_time set in oscam.conf +
+ +

+ +csp_ecm_filter = [caid][&mask][@provid][$servid],n +

+cache exchange incoming ECM filter setting (mode 3 only) for Cardservproxy, default:none +
+ +

+ +cacheex_drop_csp = 0|1 +

+1 = drop incoming Cardservproxy cache (mode 3 only), detection is zero ecmd5, default:0 +
+ +

+ +cacheex_allow_request = 0|1 +

+1 = allow incoming ECM request (mode 3 only), default:1 +
+ +

+ +cacheex_allow_filter = 0|1 +

+1= allow cache exchange filter (for cache exchange mode 3 only), default:1 +
+ +

+ +cacheex_block_fakecws = 0|1 +

+1 = enable fake DCWs blocking (for cache exchange mode 3 only), get fake DCWs form oscam.fakecws, default:0 +
+ +

+ +sleep = minutes +

+time waiting for inactive user, default:none +
+ +

+ +sleepsend = 0|255 +

+255 = OSCam client only: stopping requests until next zap, 255 = camd 3.x only: stopping requests until restart of camd 3.x client, default:0 +
+ +

+ +suppresscmd08 = 0|1 +

+0 = tell camd 3.5x, 3.57x and 3.78x clients not to request again for rejected +CAID, service ID and provider ID combination, 1 = disable, default:0 +
+ +

+ +keepalive = 0|1 +

+0 = disable keepalive between server and client for newcamd or CCcam protocol, default:1 +
+ +

+ +umaxidle = seconds +

+value for user being idle before disconnect, 0 = idle disconnect disabled, -1 use clientmaxidle in global section, default:-1 +
+ +

+ +caid = <CAID>[&<mask>][:<target CAID>][,<CAID>[&<mask>][:<target CAID>]]... +

+limit and mapping of CAIDs, default:all CAIDs with mask FFFF +

+example: caid = 0100 +
         caid = 0200&ffee:0300 +
         caid = 0400&ff00:0500,0600 +
         caid = 0702,0722 +
         caid = 0702&ffdf (shortcut for the example above) +

+ +

+ +au = label of reader[,label of reader]...|1 +

+AU setting, default:none: +

+
  label of reader = sending EMMs to specified reader  +
                    (security issue: clients can see SC data!) +
  1               = auto AU is sending EMMs to all readers +
                    (security issue: clients can see SC data!) +

+ +

+ +group = 1..64[,1..64]... +

+user assingment to reader groups, default:none, required +
+ +

+ +betatunnel = <CAID>.<ServiceID>:<target CAID>[,<CAID>.<ServiceID>: +<target CAID>]... +

+Define Betacrypt tunneling. +The ServiceID can also be used for wildcarded CAIDs. +

+
 example: betatunnel = 0100.0001:0200,0300.0004:0500 +
          betatunnel = 0600.FFFF:0700 +

+Be carefull using abbreviations. +

+ +

+

+ +emmreassembly = 0|1||2 +

+EMM reassembly, should be set for Viaccess and Cryptoworks readers if the +client that you are using to send EMMs is reassembling them instead of +just sending them to OSCam for processing. +

+
  0: disabled +
  1: enabled for DVB API +
  2: enabled (default) +

+ +

+ +services = [!]services[,[!]<services>]... +

+user [de]assingment to service group, default:none +
+ +

+ +ident = <CAID>:<provid>[,<provid>,...][;<CAID>:<provid>[,<provid>,...]]... +

+user assingment to SC specific idents, default:none +
+ +

+ +class = [!]class[,[!]class]... +

+user [de]assingment to SC specific classes, default=none +

+
 example: class = 01,02,!03,!04 +

+ +

+ +chid = <CAID>:<ChID>[,<CAID>:<ChID>]... +

+user assingment to SC specific ChIDs, default:none +
+ +

+ +monlevel = 0|1|2|3|4 +

+monitor level: +

+
 0 = no access to monitor (default) +
 1 = only server and own procs +
 2 = all procs, but viewing only +
 3 = all procs, reload of oscam.user possible +
 4 = complete access +

+ +

+ +cccmaxhops = hops +

+maximum hops limit for CCcam clients, default:10 +

+
 -1 = CCcam disabled for this user +
  0 = local SCs only +
  1 = local SCs + 1 hop +
  2 = local SCs + 2 hops +
 and so on +

+ +

+ +cccreshare = level +

+reshare level for CCcam clients +

+
 -1 = use reshare level of oscam.conf (default) +
  0 = resharing for direct peer only +
  x = resharing for direct peer and share level x +

+ +

+ +cccignorereshare = -1|0|1 +

+CCcam ignore reshare setting: +

+
 -1 = use ignore reshare level of oscam.conf (default) +
  0 = use ignore reshare setting of server +
  1 = use ignore reshare setting of reader or user +

+ +

+ +cccstealth = -1|1 +

+CCcam stealth: +

+
 -1 = use CCcam stealth of oscam.conf (default) +
  0 = use extended OSCam-CCcam protocol +
  1 = behaviour like the original CCcam: no activate partner  +
      detection and extended OSCam-CCcam protocol, prevent  +
      other OSCam to detect the server as OSCam server +

+ +  +

EXAMPLES

+ +
 [account] +
 user       = username +
 pwd        = password +
 group      = 1 +
 au         = myserialmousereader +
 services   = myservice +
 betatunnel = 0100.0001:0101,0100.0002:0101 +
 caid       = 0100  +
 ident      = 0100:000000 +
 uniq       = 1 +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.tiers(5), oscam.srvid(5), oscam.srvid2(5), oscam.whitelist(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
+
The [account] section
+
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/html/oscam.whitelist.5.html b/Distribution/doc/html/oscam.whitelist.5.html new file mode 100644 index 0000000..727b6cb --- /dev/null +++ b/Distribution/doc/html/oscam.whitelist.5.html @@ -0,0 +1,87 @@ + +Man page of oscam.whitelist + +

oscam.whitelist

+Section: File Formats (5)
Index +Return to Main Contents
+ +  +

NAME

+ +oscam.whitelist - global ECM length whitelisting configuration file for OSCam +  +

SYNOPSIS

+ +ECM length whitelisting +  +

DESCRIPTIONS

+ +

+ +w:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]] +

+
 ECM length whitelisting +
+ +

+ +l:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]] +

+
 ECM length whitelisting, does not proceed with any other ECM length  +
 whitelisting when matching, abbreviation for normal ECM length  +
 whitelisting using w parameter +
+ +

+ +i:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]] +

+
 ignore ECM length +
+ +

+ +m:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]] [new CAID][:][new provider ID] +

+
 CAID und provider ID mapping, first matching rulesmapping is  +
 preferred over all other whitelistings
+
+ +  +

ANNONTATIONS

+ +Please use Unix text file format only. +  +

EXAMPLES

+ +
 w:0100                          # whitelisting for CAID 0100 +

+
 i:0200::1234                    # ignore CAID 0200 with  +
                                 # service ID 1234 +

+
 i:::::2345                      # ignore CHID 2345 +

+
 m:3456:123456::::: 4567:234567  # mapping +

+
 w:                              # allow all others (blacklist) +

+
 l:0300                          # whitelisting for CAID 0300 not  +
                                 # proceeding if matching +  +

SEE ALSO

+ +list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5),coscam.user(5) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTIONS
+
ANNONTATIONS
+
EXAMPLES
+
SEE ALSO
+
+ + diff --git a/Distribution/doc/man/list_smargo.1 b/Distribution/doc/man/list_smargo.1 new file mode 100644 index 0000000..bf2af69 --- /dev/null +++ b/Distribution/doc/man/list_smargo.1 @@ -0,0 +1,10 @@ +.TH list_smargo 1 +.SH NAME +\fBlist_smargo\fR - list all connected Smartreader+ +.SH SYNOPSIS +list_smargo +.SH DESCRIPTIONS +The list_smargo software lists all connected Smartreader+ with bus number and device address. +.RE +.SH "SEE ALSO" +\fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.1 b/Distribution/doc/man/oscam.1 new file mode 100644 index 0000000..2f2c260 --- /dev/null +++ b/Distribution/doc/man/oscam.1 @@ -0,0 +1,195 @@ +.TH oscam 1 +.SH NAME +\fBOSCam\fR - SC server +.SH DESCRIPTIONS +The OSCam software is an \fIopen source\fR multi-protocol/multi-platform SC server. + +\fIPlease check the compile options for included features in the binary.\fR + +OSCam supports the following protocols: +.TP 3n +\(bu +newcamd with cascading/remote server ECM support +.TP 3n +\(bu +camd 3.3x TCP +.TP 3n +\(bu +camd camd 3.5x / 3.57x UDP with cascading/remote server ECM support, ECM and EMM support with size > 256 bytes +.TP 3n +\(bu +camd 3.78x TCP with cascading/remote server ECM support, ECM and EMM support with size > 256 bytes +.TP 3n +\(bu +CCcam with cascading/remote server ECM support, ECM and EMM support with size > 256 bytes +.TP 3n +\(bu +DVB API with multi tuner and PIP support +.TP 3n +\(bu +gbox with cascading/remote server ECM support +.TP 3n +\(bu +serial (HSIC, SSSP, BOMBA, DSR 9500) +.TP 3n +\(bu +radegast +.TP 3n +OSCam works on the following platforms: +.TP 3n +\(bu +Linux (Tuxbox, ARM, MIPS, MIPSel, SH-4, PowerPC, ...) +.TP 3n +\(bu +Windows (based on cygwin1.dll) +.TP 3n +\(bu +Mac OS X +.SH OPTIONS +.PP +\fB-a\fP|\fB--crash-dump\fP +.RS 3n +write oscam.crash on segfault (needs installed GDB and OSCam compiled with debug infos -ggdb) +.RE +.PP +\fB-b\fP|\fB--daemon\fP +.RS 3n +starts in background, writing oscam.version with starttime and version info in temporary directory +.RE +.PP +\fB-B\fP|\fB--pidfile\fP +.RS 3n +set PID file, overrides pidfile of \fBoscam.conf\fR, default:none +.RE +.PP +\fB-c\fP|\fB--config-dir\fP +.RS 3n +read configuration from , default:see CS_CONFDIR in \fBglobals.h\fR, +while starting OSCam prints warnings on invalid keywords, comment lines start with \fB#\fP character. + +Autodiscover of the following directories will be done: + +.TP 3n +\(bu +/etc/tuxbox/config +.TP 3n +\(bu +/etc/tuxbox/config/oscam +.TP 3n +\(bu +/config/oscam +.TP 3n +\(bu +/usr/keys +.TP 3n +\(bu +/var/etc +.TP 3n +\(bu +/var/etc/oscam +.TP 3n +\(bu +/var/keys +.TP 3n +\(bu +/var/oscam +.TP 3n +\(bu +/var/tuxbox/config + +.RE +.PP +\fB-d\fP|\fB--debug\fP +.RS 3n +debug level mask: + + \fB0\fP = no debugging (default) + \fB2\fP = ATR parsing info, ECM dumps, CW dumps + \fB4\fP = traffic from/to the reader + \fB8\fP = traffic from/to the clients + \fB16\fP = traffic to the reader-device on IFD layer + \fB32\fP = traffic to the reader-device on I/O layer + \fB64\fP = EMM logging + \fB128\fP = DVBAPI logging + \fB256\fP = load balancing logging + \fB512\fP = cache exchange logging + \fB1024\fP = client ECM logging + \fB2048\fP = CSP logging + \fB4096\fP = CWC logging + \fB65535\fP = debug all +.RE +.PP +\fB-g\fP|\fB--gcollect\fP +.RS 3n +garbage collector debug mode, default:none: + + \fB1\fP = immediate free + \fB2\fP = check for double frees +.RE +.PP +\fB-h\fP|\fB--help\fP +.RS 3n +usage +.RE +.PP +\fB-I\fP|\fB--syslog-ident\fP +.RS 3n +set syslog ident, default:oscam +.RE +.PP +\fB-p\fP|\fB--pending-ecm\fP +.RS 3n +maximum number of pending ECM packets, default:32, maximum:255 +.RE +.PP +\fB-r\fP|\fB--restart\fP +.RS 3n +restart level: + + \fB0\fP = disabled, restart request sets exit status to 99 + \fB1\fP = restart activated, web interface can restart oscam (default) + \fB2\fP = like 1, but also restart on segmentation faults +.RE +.PP +\fB-S\fP|\fB--show-sensitive\fP +.RS 3n +do not filter sensitive info (card serial numbers) in the logs +.RE +.PP +\fB-s\fP|\fB--capture-segfaults\fP +.RS 3n +capture segmentation faults +.RE +.PP +\fB-t\fP|\fB--temp-dir\fP +.RS 3n +use for temporary data, default:temporary directory of OS +.RE +.PP +\fB-V\fP|\fB--build-info\fP +.RS 3n +show OSCam version info +.RE +.PP +\fB-w\fP|\fB--wait\fP +.RS 3n +time waiting for system time to be set correctly +.RE +.SH SIGNALS +.PP +\fBSIGHUP\fP +.RS 3n +reinit user db, readers, TIERs, services, clients and anti-cascading, for newcamd connections: after reloading the ident, please restart newcamd client +.RE +.PP +\fBSIGUSR1\fP +.RS 3n +shift debug level to next level (see debug level mask above) +.RE +.PP +\fBSIGUSR2\fP +.RS 3n +get reader SC info +.RE +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.ac.5 b/Distribution/doc/man/oscam.ac.5 new file mode 100644 index 0000000..ea80fa8 --- /dev/null +++ b/Distribution/doc/man/oscam.ac.5 @@ -0,0 +1,21 @@ +.TH oscam.ac 5 +.SH NAME +\fBoscam.ac\fR - anti-cascading table for OSCam +.SH SYNOPSIS +anti-cascading table +.SH DESCRIPTIONS +.PP +\fB\fP:\fB\fP=\fB\fP +.RS 3n +define time cycles between CWs changes relating to CAID and provider ID +.RE +.PP +\fB*\fP=\fB\fP +.RS 3n +default time cycles between CWs changes \fIrequired\fR +.RE +.SH EXAMPLES + 0100:000000=10 + *=7 +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.cacheex.5 b/Distribution/doc/man/oscam.cacheex.5 new file mode 100644 index 0000000..12b2a48 --- /dev/null +++ b/Distribution/doc/man/oscam.cacheex.5 @@ -0,0 +1,19 @@ +.TH oscam.cacheex 5 +.SH NAME +\fBoscam..cacheex\fR - global ECM length matching configuration file for OSCam +.SH SYNOPSIS +ECM length matching +.SH DESCRIPTIONS +.PP +\fBm\fP:\fB[CAID]\fP[:]\fB[provider ID]\fP[:]\fB[service ID]\fP[:]\fB[ECM PID]\fP[:]\fB[CHID]\fP[:]\fB[ECM length 1[,ECM length 2]...]]\fP=\fB[CAID]\fP[:]\fB[provider ID]\fP[:]\fB[service ID]\fP[:]\fB[ECM PID]\fP[:]\fB[CHID]\fP[:]\fB[ECM length 1[,ECM length 2]...]]\fP +.RS 3n + ECM length matching from remote cache exchange partner to local + cache, \fIfor cache exchange pull mode (cacheex = 1) only\fR +.RE +.SH ANNONTATIONS +\fIPlease use Unix text file format only.\fR +.SH EXAMPLES + m:1234:::::93=5678:::::93 # matching CAID 1234 and CAID 5678 with + # ECM length 93 +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid \fR(5), \fBoscam.srvid2\fR(5),\fBoscam.user\fR(5) diff --git a/Distribution/doc/man/oscam.cert.5 b/Distribution/doc/man/oscam.cert.5 new file mode 100644 index 0000000..4e37fee --- /dev/null +++ b/Distribution/doc/man/oscam.cert.5 @@ -0,0 +1,16 @@ +.TH oscam.cert 5 +.SH NAME +\fBoscam.cert\fR - Issuer Public Keys (IPK) for OSCam +.SH SYNOPSIS +Issuer Public Keys (IPK) +.SH DESCRIPTIONS +.PP +\fBCAID\fP:\fBreserved\fP:\fBIPK\fP +.RS 3n +mapping between CAID and IPK/sessions keys in hex, currently for Cryptoworks only +.SH EXAMPLES + 0100:00000000:0102030405060708090A0B0C0D0E0F +.RE +.RE +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) \ No newline at end of file diff --git a/Distribution/doc/man/oscam.conf.5 b/Distribution/doc/man/oscam.conf.5 new file mode 100644 index 0000000..1918619 --- /dev/null +++ b/Distribution/doc/man/oscam.conf.5 @@ -0,0 +1,1513 @@ +.TH oscam.conf 5 +.SH NAME +\fBoscam.conf\fR - main configuration file for OSCam +.SH SYNOPSIS +The main configuration file for OSCam contains global parameters +such as debugging, logging, monitor, protocols and anti-cascading. +sections in \fBoscam.conf\fR are \fInonrecurring\fR. The [global] +section is \fIrequired\fR. All other sections are optional. +.SH DESCRIPTIONS +.SS "The [global] section" + +.PP +\fBnice\fP = \fB-20\fP..\fB+20\fP +.RS 3n +system priority, default:99 +.RE +.PP +\fBpidfile\fP = \fBfilename\fP +.RS 3n +set PID file, default:none +.RE +.PP +\fBlogfile\fP = [\fBfilename\fP][\fB;syslog\fP][\fB;stdout\fP] +.RS 3n +logging targets, default:/var/log/oscam.log. You can define a maximum of one filename and +additionally to log to stdout or syslog (you can also only log to stdout or syslog and omit +the filename). +.RE +PP +\fBinitial_debuglevel\fP = \fBlevel\fP +.RS 3n +set initial debug level for OSCam start, default:0 +.RE +PP +\fBsysloghost\fP = \fBhostname\fP +.RS 3n +set remote syslog host, default:none +.RE +.PP +\fBsyslogport\fP = \fBport\fP +.RS 3n +set TCP/IP port for remote syslog host, default:none +.RE +.PP +\fBecmfmt\fP = \fBformat\fP +.RS 3n +define ECM log format, default:c&p/i/s/l:h + +possible variables: + + \fBc\fP = CAID + \fBd\fP = PID + \fBe\fP = CSP hash + \fBg\fP = ID of origin gbox peer + \fBh\fP = checksum + \fBi\fP = channel ID + \fBj\fP = distance of gbox hops + \fBl\fP = length + \fBo\fP = ONID + \fBp\fP = provider ID + \fBs\fP = service ID + \fBw\fP = CW + \fBy\fP = payload + +use a value as prefix to hide variable with this value, control characters will be escaped by "\\" + + example: ecmfmt = c&0p/i/d/s/l:h.e_w + (hide provider ID if 0) +.RE +.PP +\fBloghistorysize\fP = \fBbytes\fP +.RS 3n +size of log message history in web interface or monitor, 0 = disabled, default:4096 +.RE +.PP +\fBmaxlogsize\fP = \fBkbytes\fP +.RS 3n +maximum log file size, 0 = unlimited, default:10 +.RE +.PP +\fBlogduplicatelines\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable logging of duplicate lines in the log, default:0 +.RE +.PP +\fBdisablelog\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = disable log file, default:0 +.RE +.PP +\fBcwlogdir\fP = \fBpath\fP +.RS 3n +directory for CW logging, default:config dir +.RE +.PP +\fBemmlogdir\fP = \fBpath\fP +.RS 3n +directory for EMM logging, default:config dir +.RE +.PP +\fBusrfile\fP = \fBfilename\fP +.RS 3n +log file for user logging, default:none + +log file format: + + date + time + CWs per second + username + IP address of client + TCP/IP port + CWs found + CWs from cache + CWs not found + CWs ignored + CWs timed out + CWs tunneled + login time in unix/POSIX format + logout time in unix/POSIX format + protocol +.RE +.PP +\fBdisableuserfile\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = avoid logging although \fBuserfile\fP is set, default:1 (also set automatically if userfile is empty) +.RE +.PP +\fBusrfileflag\fP = \fB0\fP|\fB1\fP +.RS 3n +usrfile logging mode: + + 0 = only client logon/logoff will be logged in usrfile (default) + 1 = each zapping of a client will be logged in usrfile +.RE +.PP +\fBdisablemail\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = disable saving NDS Videoguard mail messages from provider, default:1 +.RE +.PP +\fBmailfile\fP = \fBfile\fP +.RS 3n +define file saving NDS Videoguard mail messages from provider, default:none +.RE +.PP +\fBenableled\fP = \fB0\fP|\fB1\fP|\fB2\fP +.RS 3n + \fB0\fP = LED support disabled (default) + \fB1\fP = LED support enabled for routers + \fB2\fP = LED support enabled for Qbox HD +.RE +.PP +\fBwaitforcards\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = wait for local SCs on startup before opening network ports, default:1 +.RE +.PP +\fBwaitforcards_extra_delay\fP = \fBdelay\fP +.RS 3n +additional delay in milli-seconds after waiting for local SCs on startup before opening network ports, default:500 +.RE +.PP +\fBpreferlocalcards\fP = \fB0\fP|\fB1\fP +.RS 3n +SC decoding behavior: + + \fB0\fP = local SCs used like a remote reader + \fB1\fP = prefer cache exchange based SCs (default) + \fB2\fP = prefer local SCs +.RE +.PP +\fBreaderrestartseconds\fP = \fBseconds\fP +.RS 3n +seconds beetween restarts, 0 = disable reader restart, default:5 +.RE +.PP +\fBblock_same_ip\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = reject looping ECMs from clients to readers with the same IP address, default:1 +.RE +.PP +\fBblock_same_name\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = reject looping ECMs from clients to readers with the same name, default:1 +.RE +.PP +\fBclienttimeout\fP = \fBmilli-seconds\fP|\fBseconds\fP +.RS 3n +value (clienttimeout in seconds < 100, else milli-seconds) for client process to wait for key, default:5 +.RE +.PP +\fBclientmaxidle\fP = \fBseconds\fP +.RS 3n +value for client process being idle before disconnect, 0 = idle disconnect disabled, default:120 +.RE +.PP +\fBsuppresscmd08\fP = \fB0\fP|\fB1\fP +.RS 3n +0 = tell camd 3.5x, 3.57x and 3.78x clients not to request again for rejected +CAID, service ID and provider ID combination, 1 = disable, can be overwritten +per user in \fBoscam.user\fP, default:0 +.RE +.PP +\fBfallbacktimeout\fP = \fBmilli-seconds\fP +.RS 3n +time falling back to fallback reader, default:2500 +.RE +.PP +\fBfallbacktimeout_percaid\fP = \fBmilli-seconds\fP +.RS 3n +time falling back to CAID restricted fallback reader, default:2500 +.RE +.PP +\fBsleep\fP = \fBminutes\fP +.RS 3n +time waiting for inactive users, default:none, can be overwritten per user in \fBoscam.user\fR +.RE +.PP +\fBserverip\fP = \fBIP address\fP +.RS 3n +bind service to specified IP address, default:none +.RE +.PP +\fBbindwait\fP = \fBseconds\fP +.RS 3n +value to wait for bind request to complete, default:120 +.RE +.PP +\fBnetprio\fP = \fB0\fP|\fB1\fP|\fB2\fP|\fB3\fP|\fB4\fP|\fB5\fP|\fB6\fP|\fB7\fP|\fB8\fP|\fB9\fP|\fB10\fP|\fB11\fP|\fB12\fP|\fB13\fP|\fB14\fP|\fB15\fP|\fB16\fP|\fB17\fP|\fB18\fP|\fB19\fP|\fB20\fP +.RS 3n +value for network priority: +IPP value will be applied to SO_PRIORITY (system internal prioritization) +DSCP value will be applied to IP_TOS/IPV6_TCLASS (the TOS field in the IP packet header) + + \fB0\fP = IPP=0; DSCP=CS0 (default) + \fB1\fP = IPP=1; DSCP=CS1 + \fB2\fP = IPP=1; DSCP=AF11 + \fB3\fP = IPP=1; DSCP=AF12 + \fB4\fP = IPP=1; DSCP=AF13 + \fB5\fP = IPP=2; DSCP=CS2 + \fB6\fP = IPP=2; DSCP=AF21 + \fB7\fP = IPP=2; DSCP=AF22 + \fB8\fP = IPP=2; DSCP=AF23 + \fB9\fP = IPP=3; DSCP=CS3 + \fB10\fP = IPP=3; DSCP=AF31 + \fB11\fP = IPP=3; DSCP=AF32 + \fB12\fP = IPP=3; DSCP=AF33 + \fB13\fP = IPP=4; DSCP=CS4 + \fB14\fP = IPP=4; DSCP=AF41 + \fB15\fP = IPP=4; DSCP=AF42 + \fB16\fP = IPP=4; DSCP=AF43 + \fB17\fP = IPP=5; DSCP=CS5 + \fB18\fP = IPP=5; DSCP=EF + \fB19\fP = IPP=6; DSCP=CS6 + \fB20\fP = IPP=7; DSCP=CS7 +.RE +.PP +\fBresolvegethostbyname\fP = \fB0\fP|\fB1\fP +.RS 3n +set mode for DNS resolving: + + \fB0\fP = getadressinfo (default) + \fB1\fP = gethostbyname +.RE +.PP +\fBfailbancount\fP = \fBcount\fP +.RS 3n +number of incorrect logins after an ip address will be blocked, default:0 +.RE +.PP +\fBfailbantime\fP = \fBminutes\fP +.RS 3n +time for IP based blocking for clients with an invalid login attempt, 0 = failban is disabled, default:0 +.RE +.PP +\fBdropdups\fP = \fB0\fP|\fB1\fP +.RS 3n +mode for duplicate client connections (requirement: uniq > 0): + + \fB0\fP = mark client as duplicate, but don't disconnect them (default) + \fB1\fP = drop duplicate connections instead of marking as duplicate +.RE +.PP +\fBunlockparental\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = unlock parental mode option to disable Seca and Viaccess pin code request for adult movie, default:0 +.RE +.PP +\fBdouble_check\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = ECM will be send to two or more readers with the same SC and the CWs will be verified against each other, \fBlb_nbest_readers\fP must be set to 2 or higher, default:0 +.RE +.PP +\fBdouble_check_caid\fP = [CAID1|first two digits of CAID1],[CAID2|first two digits of CAID2]... +.RS 3n +ECM will be send to two or more readers with the same SC and the CWs will be verified against each other for defined CAID or first two bytes of CAID, \fBlb_nbest_readers\fP must be set to 2 or higher, default:none +.RE +\fBgetblockemmauprovid\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = server overrides EMM blocking defined on client site, default:0 +.RE +.PP +\fBlb_mode\fP = \fBmode\fP +.RS 3n +load balancing mode: + + \fB0\fP = load balance disabled, ECMs go to all readers (default) + \fB1\fP = fastest reader first, after 5 ECMs the reader with the fastest + response time will be selected + \fB2\fP = oldest reader first, reader with the longest no answer + \fB3\fP = lowest usage level, the usage level will be calculated by the + sum of 5 ECMS response times, the higher a reader is busy, the + higher is usage level +.RE +.PP +\fBlb_save\fP = \fB0\fP|\fBcounts\fP +.RS 3n +save auto load balance statistics: + + \fB0\fP = saving of auto load balance statistics disabled (default) + \fBcounts\fP = save auto load balance statistics every \fBcounts\fP ECMs + (minimum 100) + +To save CPU power a minimum counts of 100 is recommended. +.RE +.PP +\fBlb_nbest_readers\fP = \fBcounts\fP +.RS 3n +set count of best readers for load balancing, default:1 +.RE +.PP +\fBlb_nfb_readers\fP = \fBcounts\fP +.RS 3n +set count of fallback readers for load balancing, default:1 +.RE +.PP +\fBlb_nbest_percaid\fP = \fBCAID1:count1[,CAID2:count2]...\fP +.RS 3n +set count of best readers per CAIDs for load balancing, wildcard CAIDs with two-digit CAIDs possible, default:none + + example: lb_nbest_percaid = 0100:4,0200:3,03:2,04:1 + (wildcard CAIDs 03xx and 04xx) +.RE +.PP +\fBlb_min_ecmcount\fP = \fBcounts\fP +.RS 3n +minimal ECM count to evaluate load balancing values, default:5 +.RE +.PP +\fBlb_max_ecmcount\fP = \fBcounts\fP +.RS 3n +maximum ECM count before resetting load balancing values, default:500 +.RE +.PP +\fBlb_reopen_seconds\fP = \fBseconds\fP +.RS 3n +time between retrying failed load balanced readers/CAIDs/providers/services, default:900 +.RE +.PP +\fBlb_reopen_invalid\fP = \fB0\fP|\fBfB1\fP +.RS 3n +0 = E_INVALID will be blocked until statistics has been cleaned, default:1 +.RE +.PP +\fBlb_force_reopen_always\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = force reopening immediately all failing readers if no matching reader was found, default:0 +.RE +.PP +\fBlb_retrylimit\fP = \fBmilli-seconds\fP +.RS 3n +retry next load balanced reader only if response time is higher then lb_retrylimit, default:0 +.RE +.PP +\fBlb_savepath\fP = \fBfilename\fP +.RS 3n +filenanme for saving load balancing statistics, default:/tmp/.oscam/stat +.RE +.PP +\fBlb_stat_cleanup\fP = \fBhour\fP +.RS 3n +hours after the load balancing statistics will be deleted, default:336 +.RE +.PP +\fBlb_retrylimits\fP = \fBCAID1:time1[,CAID2:time2]...\fP +.RS 3n +load balancing retry limit time per CAID, wildcard CAIDs with two-digit CAIDs possible, default:none + + example: lb_retrylimits = 12:0100,34:0200,5678:0300 + (wildcard CAIDs 12xx and 34xx) +.PP +.RE +\fBlb_noproviderforcaid\fP = \fBCAID1[,CAID2]...\fP +.RS 3n +ignore provider information for CAIDs to reduce load balancing statistic data, +wildcard CAIDs with two-digit CAIDs possible, default:none + + example: lb_noproviderforcaid = 0100,02,0300,04 + (wildcard CAIDs 02xx and 04xx) +.PP +.RE +\fBlb_max_readers\fP = \fBlimit\fP +.RS 3n +restrict the reader count to limit during load balancing learning: + + \fB0\fP = unlimited (default) + \fBlimit\fP = restrict load balancer readers to limit +.RE +.PP +\fBlb_auto_timeout\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable automatic timeout based on load balancing statistics, default:0 +.RE +.PP +\fBlb_auto_timeout_p\fP = \fBpercent\fP +.RS 3n +percent added to average time as timeout time, default:30 +.RE +.PP +\fBlb_auto_timeout_t\fP = \fBmilli seconds\fP +.RS 3n +minimal time added to average time as timeout time, default:300 +.RE +.PP +\fBlb_auto_betatunnel\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable automatic Betacrypt tunneling detection for CAIDs 1801, 1833, 1834, and 1835 for load balancing, Betacrypt defintion in \fBoscam.user\fR with \fBbetatunnel\fR will be prefered, default:1 +.RE +.PP +\fBlb_auto_betatunnel_mode\fP = \fB0\fP|\fB1\fP|\fB2\fP +.RS 3n +set mode for automatic Betacrypt tunneling: + + \fB0\fP = CAID 18XX tunneling to CAID 17X2 only (default) + \fB1\fP = CAID 18XX tunneling to CAID 17X2 and CAID 17X2 tunneling to CAID 18XX (CAID 1833/1801) + \fB2\fP = CAID 18XX tunneling to CAID 17X2 and CAID 17X2 tunneling to CAID 18XX (CAID 1833/1834) + \fB3\fP = CAID 18XX tunneling to CAID 17X2 and CAID 17X2 tunneling to CAID 18XX (CAID 1833/1835) + \fB4\fP = CAID 17X2 tunneling to CAID 18XX (CAID 1833/1801 only) + \fB5\fP = CAID 17X2 tunneling to CAID 18XX (CAID 1833/1834 only) + \fB6\fP = CAID 17X2 tunneling to CAID 18XX (CAID 1833/1835 only) +.RE +.PP +\fBlb_auto_betatunnel_prefer_beta\fP = \fBdirection\fP +.RS 3n +set direction for automatic Betacrypt/Nagravision selection: + + \fB 0\fP = disabled (default) + \fB 1\fP = always Betacrypt + \fB105\fP = represents the middle + \fB200\fP = always Nagravision +.RE +.SS "The [monitor] section" +.PP +\fBport\fP = \fB0\fP|\fBport\fP +.RS 3n +UDP port for monitor, 0 = monitor disabled, default:0 +.RE +.PP +\fBserverip\fP = \fBIP address\fP +.RS 3n +bind service to specified IP address, default:all +.RE +.PP +\fBnocrypt\fP = \fBIP address\fP|\fBIP address range\fP[,\fBIP address\fP|\fBIP address range]\fP... +.RS 3n +unsecured monitor connection, default:none + + example: nocrypt = 127.0.0.1,192.168.0.0-192.168.255.255 +.RE +.PP +\fBaulow\fP = \fBminutes\fP +.RS 3n +time no EMM occurs so that client is set to low, switch from status "active" to "on", default:30 +.RE +.PP +\fBmonlevel\fP = \fB0\fP|\fB1\fP|\fB2\fP|\fB3\fP|\fB4\fP +.RS 3n +monitor level: + + \fB0\fP = no access to monitor + \fB1\fP = only server and own procs + \fB2\fP = all procs, but viewing only (default) + \fB3\fP = all procs, reload of \fBoscam.user\fR possible + \fB4\fP = complete access + +monlevel can be overwritten per user in \fBoscam.user\fR. +.RE +.PP +\fBhideclient_to\fP = \fBseconds\fP +.RS 3n +time to hide clients in the monitor if not sending requests, 0 = disabled, default:25 +.RE +.SS "The [webif] section" +\fBhttpport\fP = [\fB+\fP]\fBport\fP +.RS 3n +port for web interface, 0 = disabled, praefix + = enable SSL, default:none, \fIrequired\fR +.RE +.PP +\fBhttpcert\fP = \fBfile\fP +.RS 3n +file for http SSL certificate, default:\fBoscam.pem\fP +.RE +.PP +\fBhttpforcesslv3 \fP = \fB0\fP|\fB1\fP +.RS 3n +1 = force using SSLv3, default:0 +.RE +.PP +\fBhttpuser\fP = \fBusername\fP +.RS 3n +username for password protection, default:none +.RE +.PP +\fBhttppwd\fP = \fBpassword\fP +.RS 3n +password for password protection, default:none +.RE +.PP +\fBhttpcss\fP = \fBpath\fP +.RS 3n +path for external CSS file, default:none +.RE +.PP +\fBhttp_prepend_embedded_css\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = embedded CSS will be added before external CSS , default:0 +.RE +.PP +\fBhttptpl\fP = \fBpath\fP +.RS 3n +path for external templates and picons, multiple simultaneously templates and +picons are possible by creating sub folders (maximum length of 32 alphanumeric +characters), sub folders naming is corresponding to sub folder in URL, default:none + + example: httptpl = /this/is/my/path + + folder with multiple templates: + /this/is/my/path/template1 + /this/is/my/path/template2 + + valid URLs: + http://host:port/template1 + http://host:port/template2 +.RE +.PP +\fBhttpjscript\fP = \fBpath\fP +.RS 3n +path for oscam.js javascript, default:none +.RE +.PP +\fBhttprefresh\fP = \fBseconds\fP +.RS 3n +status refresh in seconds, default:none +.RE +.PP +\fBhttphideidleclients\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enables hiding clients after idle time set in parameter \fBhideclient_to\fP, default:0 +.RE +.PP +\fBhttphidetype\fP = \fBtype\fP[\fBtype\fP]... +.RS 3n +characters defining columns to hide in web interface status page (see type +column), default:none + + types: + + 'c': client + 'h': http + 'm': monitor + 'p': proxy + 'r': reader + 's': server + 'x': cache exchange +.RE +.PP +\fBhttpscript\fP = \fBpath\fP +.RS 3n +path to an executable script which you wish to start from web interface, default:none +.RE +.PP +\fBhttpallowed\fP = \fBIP address\fP|\fBIP address range\fP[,\fBIP address\fP|\fBIP address range]\fP... +.RS 3n +http web interface connections allowed, default:127.0.0.1,192.168.0.0-192.168.255.255,10.0.0.0-10.255.255.255,::1 + + example: httpallowed = 127.0.0.1,192.168.0.0-192.168.255.255 +.RE +.PP +\fBhttpdyndns\fP = \fBhostname[,hostname][,hostname]\fP +.RS 3n +http web interface connections allowed, default:none + + example: httpdyndns = host.example.com + httpdyndns = host1.example.com,host2.example.com +.RE +.PP +\fBhttpsavefullcfg\fP = \fB0\fP|\fB1\fP +.RS 3n +write config: + + \fB0\fP = all not empty parameters, all not default parameters, all + parameters not containing the same value as the same + parameter in global configuration (default) + \fB1\fP = all parameters +.RE +.PP +\fBhttpoverwritebakfile\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = overwrite backup configuration files, default:0 +.RE +.PP +\fBhttpreadonly\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = read only modus for web interface, default:0 +.RE +.PP +\fBhttpshowpicons\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = show picons in user list, default:0 +.RE +.PP +\fBhttppiconpath\fP = \fBpath\fP +.RS 3n +path to picons, default:none +.RE +.PP +\fBhttphelplang\fP = \fBen\fP|\fBde\fP|\fBfr\fP|\fB\fP +.RS 3n +set right language for wiki entry point, default:en +.RE +.PP +\fBhttplocale\fP = \fBenvironment\fP +.RS 3n +set the locale environment, default:none +.RE +.SS "The [lcd] section" +.RE +.PP +\fBhttposcamlabel\fP = \fBtext\fP +.RS 3n +set individual label in web interface header, default:OSCam +.RE +.PP +\fBenablelcd\fP = \fB0\fP|\fB1\fP +.RS 3n +1 =enable LCD output, default:0 + +.RE +\fBlcd_outputpath\fP = \fBpath\fP +.RS 3n +path for LCD output, default:/tmp +.RE +.PP +\fBlcd_hideidle\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = hide reader in LCD output if reader idle > 20 seconds, default:0 +.RE +.PP +\fBlcd_writeintervall\fP = \fBseconds\fP +.RS 3n +LCD refresh interval (minimum 5), default:10 +.RE +.RE +.SS "The [cache] section" +\fBdelay\fP = \fBmilli-seconds\fP +.RS 3n +value to delay cached requests, default:0 +.RE +.PP +\fBmax_time\fP = \fBseconds\fP +.RS 3n +maximum time CWs resist in cache, the time must be 2 seconds highter than the parameter \fBclienttimeout\fP, default:15 +.RE +.PP +\fBmax_hit_time\fP = \fBseconds\fP +.RS 3n +maximum time for cache exchange hits resist in cache for evaluating \fBwait_time\fP, default:15 +.RE +.PP +\fBwait_time\fP = \fB[caid][&mask][@provid][$servid][:awtime][:]dwtime[,[caid][&mask][@provid][$servid][:awtime][:]dwtime]...\fP +.RS 3n +wait time in milli-seconds for cache exchange and Cardservproxy before sending ECMs to reader or proxy, default:none + + example: wait_time = 0:50:250,0200@00009X:50:150,15:950,0500@000001:150,1602&ffdf:1200 +.RE +.PP +\fBcacheexenablestats\fP = \fB0|1\fP +.RS 3n +1 = enable statistics for cache exchange mode, default:0 + +\fIPlease consider memory consumption.\fR +.RE +.PP +\fBcacheex_cw_check\fP = \fB[caid][&mask][@provid][$servid]:mode:counter[,[caid][&mask][@provid][$servid]:mode:counter]...\fP +.RS 3n + + \fBmode\fP = specify behaviour for counter: + + \fB0\fP = when \fBwait_time\fP expires, serve highest counter's CW + got anyway, even if no counter reached (default) + \fB1\fP = never serve CW from cache exchange stored in cache, + if it's counter not reaches counter. When \fBwait_time\fP + expires, requests will go to normal readers + + \fBcounter\fP = set minimum CW counter to allow CW is used, default:1 +.RE +.PP +\fBcacheex_mode1_delay\fP = \fBCAID1:time,[BCAID2:time]...\fP +.RS 3n +delay in milli-seconds for asking cache exchange mode 1 readers, default:none +.RE +.PP +\fBcsp_port\fP = \fBport\fP +.RS 3n +UDP port of Cardservproxy for cache exchange, default:none +.RE +.PP +\fBcsp_serverip\fP = \fBIP\fP +.RS 3n +bind Cardservproxy for cache exchange to specified IP address, default:none +.RE +.PP +\fBcsp_ecm_filter\fP = \fB[caid][&mask][@provid][$servid][,[caid][&mask][@provid][$servid]]...\fP +.RS 3n +Cardservproxy incoming ECM filter setting, default:none +.RE +.PP +\fBcsp_allow_request\fP = \fB0\fP|\fB1\fP +.RS 3n +allow incoming ECM request from Cardservproxy, default:1 +.RE +.PP +\fBcsp_allow_reforward\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = reforward other cacheex updates to Cardservproxy peers, \fIoption could cause loops\fR, default:0 +.RE +.PP +\fBcwcycle_check_enable\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable CW cycle check, default:0 +.RE +.PP +\fBcwcycle_check_caid\fP = \fBCAID\fP[,\fBCAID\fP]... +.RS 3n +CAID enabled for CW cycle check, default:none +.RE +.PP +\fBcwcycle_maxlist\fP = \fBcount\fP +.RS 3n +maximum CW cycle list entries, default:500, maximum:4000 +.RE +.PP +\fBcwcycle_keeptime\fP = \fBminutes\fP +.RS 3n +minimum time a learned cycle time resists in memory, default:15, maximum:15 +.RE +.PP +\fBcwcycle_onbad\fP = \fB0\fP|\fB1\fP +.RS 3n +0 = log bad CW cycle only, 1 = drop bad CW cycle, default:1 +.RE +.PP +\fBcwcycle_dropold\fP = \fB0\fP|\fB1\fP +.RS 3n +1= drop old CW cycle, default:1 +.RE +.PP +\fBcwcycle_sensitive\fP = \fB0\fP|\fB2\fP|\fB3\fP|\fB4\fP +.RS 3n +drop CW mode: + + \fB0\fP = disabled + \fB2\fP = 2 (or more) same bytes and drop new CW + \fB3\fP = 3 (or more) same bytes and drop new CW + \fB4\fP = 4 (or more) same bytes and drop new CW (default) +.RE +.PP +\fBcwcycle_allowbadfromffb\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = allow bad cycles from a fixed fallback reader, default:0 +.RE +.PP +\fBcwcycle_usecwcfromce\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = use CW info from cache exchange, default:0 +.RE +.PP +\fBwait_until_ctimeout\fP = \fB0\fP|\fB1\fP +.RS 3n +answer when cache exchange timeout expires, if no normal readers are available for sending ECMs: + + \fB0\fP = immediately send 'not found' to client (default) + \fB1\fP = wait for cache exchange answer until client timeout expires +.RE +.SS "The [camd33] section" +\fBport\fP = \fB0\fP|\fBport\fP +.RS 3n +TCP port for camd 3.3x clients, 0 = disabled, default:0 +.RE +.PP +\fBserverip\fP = \fBIP address\fP +.RS 3n +bind service to specified IP address, default:all +.RE +.PP +\fBnocrypt\fP = \fBIP address\fP|\fBIP address range\fP[,\fBIP address\fP|\fBIP address range]\fP... +.RS 3n +unsecured camd 3.3x client connection, default:none + + example: nocrypt = 127.0.0.1,192.168.0.0-192.168.255.255 +.RE +.PP +\fBpassive\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = force passive camd 3.3x client, default:0 +.RE +.PP +\fBkey\fP = \fB128 bit key\fP +.RS 3n +key for camd 3.3x client encryption, default:none + + example: key = 01020304050607080910111213141516 +.RE +.SS "The [cs357x] section" +.PP +\fBport\fP = \fB0\fP|\fBport\fP +.RS 3n +UDP port for camd 3.57x clients, 0 = disabled, default:0 +.RE +.PP +\fBserverip\fP = \fBIP address\fP +.RS 3n +bind service to specified IP address, default:all +.RE +.PP +\fBsuppresscmd08\fP = \fB0\fP|\fB1\fP +.RS 3n +0 = tell camd 3.5x / 3.57x clients not to request again for rejected CAID, service ID and provider ID combination, 1 = disable, can be overwritten per user in \fBoscam.user\fP, default:0 +.RE +.SS "The [cs378x] section" +.PP +\fBport\fP = \fB0\fP|\fBport[@CAID][:provid][,provid]...[;port@CAID[:provid][,provid]...]...\fP +.RS 3n +TCP port/CAID/provid definitions for camd 3.78x clients, 0 = disabled, default:0 + + examples: port = 10000@0100:100000;20000@0200:200000,300000,400000 + port = 30000 +.RE +.PP +\fBserverip\fP = \fBIP address\fP +.RS 3n +bind service to specified IP address, default:all +.RE +.PP +\fBkeepalive\fP = \fB0\fP|\fB1\fP +.RS 3n +0 = disable camd 3.78x keepalive modus, default:0 +.RE +.PP +\fBsuppresscmd08\fP = \fB0\fP|\fB1\fP +.RS 3n +0 = tell camd 3.78x clients not to request again for rejected CAID, service ID and provider ID combination, 1 = disable, can be overwritten per user in \fBoscam.user\fP, default:0 +.RE +.SS "The [newcamd] section" +.PP +\fBkey\fP = \fBDES key\fP +.RS 3n +default key for newcamd client encryption, default:none + + example: key = 0102030405060708091011121314 +.RE +.PP +\fBport\fP = \fBport[{DES key}]@CAID[:provid][,provid]...[;port[{DES key}]@CAID[:provid][,provid]...]...\fP +.RS 3n +TCP port/DES key/CAID/provid definitions, default:none + + example: port = 10000@0100:100000;20000{0102030405060708091011121314}@0200:200000,300000 + +Each CAID requires a separate port. If you don't specify a DES key for a port, the default DES key will be used. +.RE +.PP +\fBserverip\fP = \fBIP address\fP +.RS 3n +bind newcamd service to specified IP address, default:all +.RE +.PP +\fBallowed\fP = \fBIP address\fP|\fBIP address range\fP[,\fBIP address\fP|\fBIP address range]\fP... +.RS 3n +newcamd client connections allowed from, default:none + + example: allowed = 127.0.0.1,192.168.0.0-192.168.255.255,::1 +.RE +.PP +\fBkeepalive\fP = \fB0\fP|\fB1\fP +.RS 3n +0 = disable newcamd keepalive modus, default:0 +.RE +.PP +\fBmgclient\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = provide share information of all available CAIDs and provider IDs to mgcamd clients, default:0 +.RE +.SS "The [radegast] section" +.PP +\fBport\fP = \fB0\fP|\fBport\fP +.RS 3n +TCP/IP port for radegast clients, 0 = disabled, default:0 +.RE +.PP +\fBserverip\fP = \fBIP address\fP +.RS 3n +bind service to specified IP address, default:all +.RE +.PP +\fBallowed\fP = \fBIP address\fP|\fBIP address range\fP[,\fBIP address\fP|\fBIP address range]\fP... +.RS 3n +client connections allowed from, default:none + + example: allowed = 127.0.0.1,192.168.0.0-192.168.255.255 +.RE +.PP +\fBuser\fP = \fBusername\fP +.RS 3n +user name for radegast client +.RE +.SS "The [serial] section" +.PP +\fBdevice\fP = \fB\fP@\fB\fP[:\fBbaud\fP][?\fBoption1\fP=\fBvalue1\fP[&\fBoption2\fP=\fBvalue2\fP]...] + [;\fB\fP@\fB\fP[:\fBbaud\fP][?\fBoption1\fP=\fBvalue1\fP[&\fBoption2\fP=\fBvalue2\fP]...]]... +.RS 3n + +parameters: + \fBuser\fP = \fBaccount\fP + \fBdevice\fP = \fBserial device name\fP|\fBhostname|IP\fP,\fBport\fP + \fBbaud\fP = \fBserial port speed\fP (for serial devices only) + \fBoption\fP = \fBtimeout\fP = milli-seconds, timeout for connection, + default:50 + \fBdelay\fP = milli-seconds, additional delay between two + characters, default:0 + +supported serial devices (autodection): + HSIC (humax sharing interface client) + SSSP (simple serial sharing protocol) + bomba (BOMBA firmware) + dsr9500 (DSR 9500) + + example: user1@/dev/ttyS1:115200?delay=1&timeout=5000 + user2@192.160.0.1,12345?delay=1&timeout=5000 +.RE +.SS "The [cccam] section" +.PP +\fBport\fP = \fB0\fP|\fBport\fP[,\fB0\fP|\fBport\fP]... +.RS 3n +TCP/IP ports for CCcam clients, 0 = disabled, default:0 +.RE +.PP +\fBversion\fP = \fB
..\fP +.RS 3n +define CCcam version, \fIminimum CCcam version 2.0.11\fR, used with original CCcam only, default:none + + example: version = 1.2.34 +.RE +.PP +\fBreshare\fP = \fBlevel\fP +.RS 3n +reshare level for CCcam clients (default:10): + +\fB-1\fP = no resharing + \fB0\fP = resharing for direct peer only + \fB1\fP = resharing for direct peer and next level + \fBx\fP = resharing for direct peer and next x level +.RE +.PP +\fBreshare_mode\fP = \fBmode\fP +.RS 3n +CCcam reshare mode: + + \fB0\fP = reader reshares only received SCs for CCcam readers, + defined filters/CAIDs/provids on other readers + \fB1\fP = reader reshares received SCs (like=0) and defined services + \fB2\fP = reader reshares only defined reader services as virtual SCs + \fB3\fP = reader reshares only defined user services as virtual SCs + \fB4\fP = reader reshares only received SCs (default) + +Every server is shared as hop = 0 and with defined reshare values. + +Service reshare only works if positive services defined: no service - no reshare! +.RE +.PP +\fBignorereshare\fP = \fB0\fP|\fB1\fP +.RS 3n +CCcam reshare setting: + + \fB0\fP = use reshare setting of server (default) + \fB1\fP = use reshare setting of reader or user +.RE +.PP +\fBstealth\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = behaviour like the original CCcam: no activate partner detection and +extended OSCam-CCcam protocol, prevent other OSCam to detect the server +as OSCam server, default:0 +.RE +.PP +\fBminimizecards\fP = \fBmode\fP +.RS 3n +mode how to provide CCcam servers to CCcam clients: + + \fB0\fP = no aggregation, remove duplicates only (default) + \fB1\fP = based on minimum hop: two SCs with different hops are + summarized, new SCs get a smaller hop + \fB2\fP = aggregation based on CAIDs: all SCs with the same CAIDs + will be merged, provider (maximum 32) will be merged, too +.RE +.PP +\fBupdateinterval\fP = \fBseconds\fP +.RS 3n +interval to provide share list update to CCcam clients, values <= 10 are invalid and will be set to 30, default:240 +.RE +.PP +\fBkeepconnected\fP = \fB0\fP|\fB1\fP +.RS 3n +set CCcam keepalive modus: + + \fB0\fP = disconnect client when maximum idle time is reached + \fB1\fP = keep client connected (default) +.RE +.PP +\fBrecv_timeout\fP = \fBmilli-seconds\fP +.RS 3n +set network timeout for receiving data, default:2000 +.RE +.PP +\fBforward_origin_card\fP = \fB0\fP|\fB1\fP +.RS 3 +1 = forward ECM request to reader holding this card, +\fIload balancer, fallback and caching will be disabled\fR, default:0 +.RE +.PP +\fBnodeid\fP = \fBID\fP +.RS 3n +set CCcam node ID in hex, default:none + + example: nodeid = 0a0b0c0d0e0f1011 +.RE +.SS "The [gbox] section" +.PP +\fBhostname\fP = \fBhostname\fP| \fBIP address\fP +.RS 3n +set hostname or IP address for gbox protocol, default:none +.RE +.PP +\fBport\fP = \fBport\fP[,\fBport\fP]... +.RS 3n +UDP port for gbox server, default:0 +.RE +.PP +\fBmy_password\fP = \fBpassword\fP +.RS 3n +password for connection to local gbox peer, default:none +.RE +.PP +\fBproxy_card\fP = \fB[,]...\fP +.RS 3n +proxy reader SCs to be reshared into gbox network, default:none +.RE +.PP +\fBccc_reshare\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable CCCam reshare, default:0 +.RE +.PP +\fBmy_vers\fP = \fBversion\fP +.RS 3n +set gbox version in hexadecimal low byte, default:2A +.RE +.PP +\fBmy_cpu_api\fP = \fBbyte\fP +.RS 3n +set gbox CPU and API byte in hexadecimal, default:40 +.RE +.PP +\fBgbox_reconnect\fP = \fBtime\fP +.RS 3n +send hello message to peers in seconds, default:180, min:60, max:300 +.RE +.PP +\fBlog_hello\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = log hello messages, default:1 +.RE +.PP +\fBdis_attack_txt\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = disable creation of attack.txt, default:0 +.RE +.PP +\fBgsms_disable\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = disable gbox short message service (GSMS),default:1 + +sending a messeage: /tmp/gsms.txt: <1=mormal message|2=OSD/TV message> +, status will be stored in '/tmp/gsms.ack' +respective 'gsms.nack', receiving a message: The message will be stored in +/tmp/gsms.log +.RE +.PP +\fBtmp_dir\fP = \fBpath\fP +.RS 3n +temporary directory for gbox, default:/tmp/.oscam +.RE +.PP +\fBaccept_remm_peer\fP = \fBpeer-id1\fP[,\fBpeer-id2\fP]... +.RS 3n +accept REMM requests from gbox peer(s), default:none +.RE +.SS "The [scam] section" +.PP +\fBport\fP = \fBport\fP +.RS 3n +UDP port for scam server, default:0 +.RE +.SS "The [dvbapi] section" +.PP +\fBenabled\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = DVB API enabled, default:0 + +Create file /tmp/.pauseoscam to pause DVB API, e.g. if STB goes into standby and OSCam remains as SC server only. +.RE +.PP +\fBlisten_port\fP = \fB0\fP|\fBport\fP +.RS 3n +TCP/IP port for SAT IP clients, filtering has to be done on client site, 0 = disabled, default:0 +.RE +.PP +\fBserverip\fP = \fBIP address\fP +.RS 3n +bind service to specified IP address, default:none +.RE +.PP +\fBuser\fP = \fBusername\fP +.RS 3n +user name for DVB API client, default:anonymous +.RE +.PP +\fBignore\fP = \fB[,]...\fP \fI(detached by oscam.dvbapi, obsolete)\fR +.RS 3n +CAIDs to be ignored, default:none +.RE +.PP +\fBservices\fP = \fB[,]...\fP \fI(detached by oscam.dvbapi, obsolete)\fR +.RS 3n +services to be prioritized, default:none +.RE +.PP +\fBpriority\fP = \fB:[,CAID:]...\fP \fI(detached by oscam.dvbapi, obsolete)\fR +.RS 3n +CAIDs and provider IDs to be prioritized, default:CAIDs and provider IDs of local SCs will be prioritized +.RE +.PP +\fBau\fP = \fB0\fP|\fB1\fP|\fB2\fP +.RS 3n +AU mode: + + \fB0\fP = disable AU (default) + \fB1\fP = enable AU +.RE +.PP +\fBpmt_mode\fP = \fB0\fP|\fB1\fP|\fB2\fP|\fB3\fP|\fB4\fP|\fB5\fP +.RS 3n +PMT mode: + + \fB0\fP = use camd.socket and PMT file, default + \fB1\fP = disable reading PMT file + \fB2\fP = disable camd.socket + \fB3\fP = read PMT file on startup only + \fB4\fP = do not use signal handler for monitoring /tmp + \fB5\fP = do not use signal handler for monitoring /tmp, + disable camd.socket +.RE +.PP +\fBecminfo_file\fP = \fB0\fP|\fB1\fP +.RS 3n +ecm.info file: + + \fB0\fP = Disable ecm.info file + \fB1\fP = Enable ecm.info file (default) +.RE +.PP +\fBecminfo_type\fP = \fB0\fP|\fB1\fP|\fB2\fP|\fB3\fP|\fB4\fP|\fB5\fP +.RS 3n +ecm.info types: + + \fB0\fP = OSCam syntax (default) + \fB1\fP = OSCam syntax with ECM time in ms instead of seconds + \fB2\fP = WiCardd + \fB3\fP = mgcamd + \fB4\fP = CCcam + \fB5\fP = camd3 +.RE +.PP +\fBrequest_mode\fP = \fB0\fP|\fB1\fP +.RS 3n +CAID request mode: + + \fB0\fP = try all possible CAIDs one by one (default) + \fB1\fP = try all CAIDs simultaneously +.RE +.PP +\fBboxtype\fP = \fBdbox2\fP|\fBdreambox\fP|\fBdm7000\fP|\fBduckbox\fP|\fBufs910\fP|\fBipbox\fP|\fBipbox-pmt\fP|\fBqboxhd\fP|\fBcoolstream\fP|\fBneumo\fP|\fBsamygo\fP|\fBpc\fP +.RS 3n +set boxtype, auto detection of DVB API will be aspired, default:dreambox + +ipbox with camd.socket support, currently only with PGI image version 0.6 or above, +verified on HD models only + +ipbox-pmt can be used on any DGS based images (with or without camd.socket support), +verified on HD models only + +pc is for generic pc support (currently supported on VDR with vdr-plugin-dvbapi) +.RE +.PP +\fBread_sdt\fP = \fB0\fP|\fB1\fP|\fB2\fP +.RS 3n +mode of provider, channel name and service type auto detection via SDT: + + \fB0\fP = disabled (default) + \fB1\fP = enabled for non FTA channels only + \fB2\fP = enabled for all channels +.RE +.PP +\fBwrite_sdt_prov\fP = \fB0\fP|\fB1\fP +.RS 3n +mode writing provider name into \fBoscam.srvid2\fR file: + + \fB0\fP = disabled (default) + \fB1\fP = enabled +.RE +.PP +\fBdemuxer_fix\fP = \fB0\fP|\fB1\fP +.RS 3n +try fixing audio/video sync errors: + + \fB0\fP = disabled (default) + \fB1\fP = enabled +.RE +.PP +\fBcw_delay\fP = \fBmilli-seconds\fP +.RS 3n +delay of CW writing, default:none +.RE +.PP +\fBdelayer\fP = \fBmilli-seconds\fP +.RS 3n +minimum time to write CW, default:0 +.RE +.PP +\fBreopenonzap\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = reopen demux devices on each channel switching, default:0 +.RE +.SS "The [anticasc] section" +.PP +\fBenabled\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable anti-cascading, default:0 +.RE +.PP +\fBnumusers\fP = \fBquantity\fP +.RS 3n +anti-cascading: user per account, 0 = anti-cascading disabled, default:0 +.RE +.PP +\fBsampletime\fP = \fBminutes\fP +.RS 3n +duration of sample, default:2 +.RE +.PP +\fBsamples\fP = \fBquantity\fP +.RS 3n +quantity of samples over limit, default:10 +.RE +.PP +\fBpenalty\fP = \fB0\fP|\fB1\fP|\fB2\fP|\fB3\fP +.RS 3n +level of penalty: + + \fB0\fP = only logging (default) + \fB1\fP = send fake CWs + \fB2\fP = temporary user ban + \fB3\fP = send delayed CWs + +penalty can be overwritten per user in \fBoscam.user\fR. +.RE +.PP +\fBaclogfile\fP = \fBfilename\fP +.RS 3n +file for anti-cascading logging, default:none +.RE +.PP +\fBfakedelay\fP = \fBmilli-seconds\fP +.RS 3n +fake delay time, default:1000, minimum value is 100, maximum value is 3000 +.RE +.PP +\fBdenysamples\fP = \fBquantity\fP +.RS 3n +how many samples should be penalized, default:8 +.RE +.PP +\fBacosc_enabled\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable anti-cascading over SID count, default:0 +.RE +.PP +\fBacosc_max_ecms_per_minute\fP = \fBcount\fP +.RS 3n +maximum ecms per minute, 0 = unlimited, default:0 + +Can be overwritten per user in \fBoscam.user\fR. +.RE +.PP +\fBacosc_max_active_sids\fP = \fBcount\fP +.RS 3n +maximum active SIDs with anti-cascading over SID, 0 = unlimited, default:0 + +Can be overwritten per user in \fBoscam.user\fR. +.RE +.PP +\fBacosc_zap_limit\fP = \fBcount\fP +.RS 3n +zap limit for anti-cascading over SID, 0 = unlimited, default:0 + +Can be overwritten per user in \fBoscam.user\fR. +.RE +.PP +\fBacosc_penalty\fP = \fB0\fP|\fB1\fP|\fB2\fP|\fB3\fP|\fB4\fP +.RS 3n +level of penalty with anti-cascading over SID count: + + \fB0\fP = only logging (default) + \fB1\fP = send fake CWs + \fB2\fP = temporary user ban + \fB3\fP = send delayed CWs + \fB4\fP = temporary hidecards to the client + +Can be overwritten per user in \fBoscam.user\fR. +.RE +.PP +\fBacosc_penalty_duration\fP = \fBseconds\fP +.RS 3n +penalty duration for anti-cascading over SID count, default:0 + +Can be overwritten per user in \fBoscam.user\fR. +.RE +.PP +\fBacosc_delay\fP = \fBmilli-seconds\fP +.RS 3n +delay for anti-cascading over SID count, default:0 + +Can be overwritten per user in \fBoscam.user\fR. +.RE +.SH LOGGING +.TP 3n +\(bu +reader stages + + \fB1\fP = cacheex (=1) reader (\fBC\fP) + \fB2\fP = local SCs (\fBL\fP) + \fB3\fP = other reader / proxies (\fBP\fP) + \fB4\fP = fallback reader (\fBF\fP) +.TP 3n +\(bu +logging format + + stage/used/chosen/possible +.SH MONITOR + +monitor commands: +.TP 3n +\(bu +\fBlogin \fP + +login (for unencrypted connections only) + +.TP 3n +\(bu +\fBgetuser =\fP + +get parameter for user + +.TP 3n +\(bu +\fBsetuser =\fP + +set parameter for user + +.TP 3n +\(bu +\fBsetserver =\fP + +set parameter for server + +.TP 3n +\(bu +\fBexit\fP + +exit monitor + +.TP 3n +\(bu +\fBlog \fP + +enable|enable without hitory|disable logging for 2 minutes + +.TP 3n +\(bu +\fBstatus\fP + +list of current processes and clients + +.TP 3n +\(bu +\fBshutdown\fP + +shutdown OSCam + +.TP 3n +\(bu +\fBrestart\fP + +restart OSCam + +.TP 3n +\(bu +\fBkeepalive\fP + +send keepalive + +.TP 3n +\(bu +\fBreload\fP + +reinit user db, clients and anti-cascading, for newcamd connections: after reloading the provid, please restart newcamd client + +.TP 3n +\(bu +\fBdetails \fP + +details about selected PID + +.TP 3n +\(bu +\fBreread\fP + +read again + +.TP 3n +\(bu +\fBdebug \fP + +set debug level (\fBmonlevel\fP > 3 required) + +debug level mask: + \fB0\fP = no debugging (default) + \fB1\fP = detailed error messages + \fB2\fP = ATR parsing info, ECM dumps, CW dumps + \fB4\fP = traffic from/to the reader + \fB8\fP = traffic from/to the clients + \fB16\fP = traffic to the reader-device on IFD layer + \fB32\fP = traffic to the reader-device on I/O layer + \fB64\fP = EMM logging + \fB128\fP = DVB API logging + \fB256\fP = load balacing logging + \fB512\fP = cache exchange logging + \fB1024\fP = client ECM logging + \fB65535\fP = debug all +.TP 3n +\(bu +\fBversion\fP + +show OSCam version + +.TP 3n +\(bu +\fBcommands\fP + +show all valid monitor commands +.SH WEB INTERFACE +.TP 3n +\(bu +template system + +The web interface allows you to create your own template. For developing your +own template request the orignal template with the non-linked page +\fBsavetemplates.html\fP. Store your own template in the directory specified +by \fBhttptpl\fP. +.SH CACHING +types of ECM caching: +.TP 3n. +\(bu +\fBcache1\fP + +ECM and CW in cache already. +.TP 3n. +\(bu +\fBcache2\fP + +ECM and checksum in cache already. +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.dvbapi.5 b/Distribution/doc/man/oscam.dvbapi.5 new file mode 100644 index 0000000..370c6a2 --- /dev/null +++ b/Distribution/doc/man/oscam.dvbapi.5 @@ -0,0 +1,90 @@ +.TH oscam.dvbapi 5 +.SH NAME +\fBoscam.dvbapi\fR - DVB API configuration file for OSCam +.SH SYNOPSIS +DVBAPI settings, first match - first used +.SH DESCRIPTIONS +.PP +\fBP\fP: \fB[CAID]\fP:\fB[provider ID]\fP:\fB[service ID]\fP:\fB[ECM PID]\fP:\fB[CHID]\fP \fB[force]\fP:\fB[PIDx]\fP \fBpriority\fP +.RS 3n + set priority, \fBcontinue\fP = \fB1\fP: proceed with priority, recommended for + pay-per-view services / EMMs \fI(use carefully)\fR, \fIalthough local SCs + will be prioritised higher\fR, default:none +.RE +.PP +\fBI\fP: \fB[CAID]\fP:\fB[provider ID]\fP:\fB[service ID]\fP:\fB[ECM PID]\fP:\fB[CHID]\fP \fB[PIDx]\fP \fBignore\fP +.RS 3n + set ignore +.RE +.PP +\fBJ\fP: \fB[CAID]\fP:\fB[provider ID]\fP:\fB[service ID]\fP:\fB[ECM PID]\fP \fBjoined CAID\fP:\fBjoined provider ID\fP:\fBjoined ECM PID\fP +.RS 3n + join to another ECM PID +.RE +.PP +\fBA\fP: ::\fBservice ID\fP:\fB[PMT PID] \fP:\fB[provider ID]\fP[:]\fB[ECM PID]\fP +.RS 3n + set a dummy ECM request with CAID FFFF for services with a constant CW shown as unencrypted service +\fI(for STBs with PMT PID support only)\fR +.RE +.PP +\fBA\fP: ::\fBservice ID\fP:\fB[video PID] \fP:\fB[provider ID]\fP[:]\fB[ECM PID]\fP +.RS 3n + set a dummy ECM request with CAID FFFF for services with a constant CW shown as unencrypted service +\fI(for STBs without PMT PID support only)\fR +.RE +.PP +\fBX\fP: \fB[CAID]\fP:\fB[provider ID]\fP:\fB[service ID]\fP:\fB[ECM PID]\fP +.RS 3n + add decoding on an extra demux index on the same CA device (Multi ECM) \fI(not support on all STBs)\fR +.RE +.PP +\fBD\fP: \fB[CAID]\fP:\fB[provider ID]\fP:\fB[service ID]\fP:\fB[ECM PID]\fP \fBdelay\fP +.RS 3n + set delay in milli-seconds writing CWs +.RE +.PP +\fBM\fP: \fB[CAID]\fP:\fB[provider ID]\fP:\fB[service ID]\fP:\fB[ECM PID]\fP \fBtarget CAID\fP[:]\fB[target provider ID]\fP +.RS 3n + mapping +.RE +.PP +\fBS\fP: \fB[device]\fP \fB[PMT file name]\fP +.RS 3n + set DVB API device name and PMT file name \fI(valid for STAPI only)\fR +.RE +.PP +\fBL\fP: \fB[CAID]\fP:\fB[provider ID]\fP:\fB[service ID]\fP:\fB[ECM PID]\fP \fBlength\fP +.RS 3n + set ECM length in hexadecimal +.RE +.RE +.SH ANNONTATIONS +\fIPlease use Unix text file format only.\fR +.SH EXAMPLES + P: 0100:123456 # prioritise CAID 0100 with provider 123456 + + P: :1234 # prioritise ECM with provider ID 1234 on + # on any CAID and service + + P: 0200 # prioritise CAID 0200 + + P: 0300::9ABC # prioritise CAID 0300 on service 9ABC only + + P: 0400 1 # prioritise CAID 0400 for pay-per-view services + + P: : 1 # prioritise for EMMs + + M: 0500 0600:123456 # map CAID 0500 to provider ID 123456 with + # CAID 0600 + + D: 0700 200 # wait 200 ms before writing CW for CAID 0700 + + I: :654321 # ignore provider ID 654321 for every CAID and + # service + + I: 0 # ignore every CAID that was not handled before + + L: 0800 8e # ECM length for CAID 0800 to 8e (hexadecimal) +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.guess.5 b/Distribution/doc/man/oscam.guess.5 new file mode 100644 index 0000000..332bc18 --- /dev/null +++ b/Distribution/doc/man/oscam.guess.5 @@ -0,0 +1,15 @@ +.TH oscam.guess 5 +.SH NAME +\fBoscam.guess\fR - CAID guessing table for OSCam +.SH SYNOPSIS +CAID guessing table +.SH DESCRIPTIONS +.PP +\fB\fP:\fB\fP +.RS 3n +CAID guessing table by len in hex, only needed for protocols (at the moment BOMBA protocol only) that does not pass CAIDs +.RE +.SH EXAMPLES + 12:3456 +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) \ No newline at end of file diff --git a/Distribution/doc/man/oscam.ird.5 b/Distribution/doc/man/oscam.ird.5 new file mode 100644 index 0000000..dd9e1b1 --- /dev/null +++ b/Distribution/doc/man/oscam.ird.5 @@ -0,0 +1,15 @@ +.TH oscam.ird 5 +.SH NAME +\fBoscam.ird\fR - Irdeto guessing table for OSCam +.SH SYNOPSIS +Irdeto guessing table +.SH DESCRIPTIONS +.PP +\fB\fP:\fB\fP:\fB\fP:\fB\fP +.RS 3n +Irdeto guessing table by signature +.RE +.SH EXAMPLES + 12:0000000a:12ab:cd01 +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.provid.5 b/Distribution/doc/man/oscam.provid.5 new file mode 100644 index 0000000..d8bb2e9 --- /dev/null +++ b/Distribution/doc/man/oscam.provid.5 @@ -0,0 +1,15 @@ +.TH oscam.provid 5 +.SH NAME +\fBoscam.provid\fR - provider table for OSCam +.SH SYNOPSIS +provider table +.SH DESCRIPTIONS +.PP +\fB\fP:\fB\fP|\fB\fP|\fB\fP|\fB\fP +.RS 3n +provider table +.RE +.SH EXAMPLES + 0100:012345|MyPay-TV|Astra 19E|German +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.ratelimit.5 b/Distribution/doc/man/oscam.ratelimit.5 new file mode 100644 index 0000000..e77f12b --- /dev/null +++ b/Distribution/doc/man/oscam.ratelimit.5 @@ -0,0 +1,30 @@ +.TH oscam.ratelimit 5 +.SH NAME +\fBoscam.ratelimit\fR - ECMs ratelimit for OSCam +.SH SYNOPSIS +limit rate of ECMs allowed for an interval +.SH DESCRIPTIONS +.PP +\fBCAID\fP:\fBprovider ID\fP:\fBservice ID\fP:\fBChID\fP:\fBratelimitecm\fP:\fBratelimitseconds\fP:\fBsrvidholdseconds\fP +.RS 3n + +\fBratelimitecm\fP +.RS 3n +number of different SIDs in ECMs allowed for an interval +.RE + +\fBratelimitseconds\fP +.RS 3n +interval in seconds for ratelimit +.RE + +\fBsrvidholdseconds\fP +.RS 3n +extra time in seconds this service ID is kept in a slot before another service ID can take its place +.RE +.SH EXAMPLES + 0100:00002A:3A3A:4A00:0002:0010:0004 +.RE +.RE +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) \ No newline at end of file diff --git a/Distribution/doc/man/oscam.server.5 b/Distribution/doc/man/oscam.server.5 new file mode 100644 index 0000000..3a94758 --- /dev/null +++ b/Distribution/doc/man/oscam.server.5 @@ -0,0 +1,887 @@ +.TH oscam.server 5 +.SH NAME +\fBoscam.server\fR - reader configuration file for OSCam +.SH SYNOPSIS +The server configuration file for OSCam contains reader parameters. +sections in \fBoscam.server\fR are \fIrecurring\fR (more than one reader possible). +At least one [reader] section is \fIrequired\fR. +.SH DESCRIPTIONS +.SS "The [reader] section" +.PP +\fBlabel\fP = \fBname\fP +.RS 3n +name for reader, \fIrequired\fR +.RE +.PP +\fBenable\fP = \fB0\fP|\fB1\fP +.RS 3n +0 = deactivate reader, default:1 +.RE +.PP +\fBdescription\fP = \fBtext\fP +.RS 3n +description of reader, default:none +.RE +.PP +\fBprotocol\fP = \fBreader protocol\fP +.RS 3n +reader protocol, \fIrequired\fR: + + \fBcamd35\fP|\fBcs357x\fP + \fBcccam\fP + \fBcs378x\fP + \fBconstcw\fP + \fBgbox\fP + \fBghttp\fP + \fBinternal\fP + \fBmouse\fP + \fBmp35\fP + \fBnewcamd\fP|\fBnewcamd525\fP + \fBnewcamd524\fP + \fBpcsc\fP + \fBradegast\fP + \fBscam\fP + \fBsc8in1\fP + \fBserial\fP + \fBsmargo\fP + \fBsmartreader\fP +.RE +.PP +\fBdevice\fP = \fB[;]serial:serialnum|bus:device\fP| + \fB\fP| + \fB,[,]\fP| + \fB,\fP| + \fB,\fP| + \fBpcsc\fP| + \fB<0|1>\fP>| + \fBconstantcw\fP +.RS 3n +define local or remote reader + + \fBreadertype\fP: set reader type + + \fBSR\fP: Smartreader+ (default) + \fBInfinity\fP: Infinity USB + \fBTripleP1\fP: Smargo Triple Reader port 1 + \fBTripleP2\fP: Smargo Triple Reader port 2 + \fBTripleP3\fP: Smargo Triple Reader port 3 + + \fBbus:device\fP: bus name and device name of the Smartreader+ or + Infinity USB (get the names with lsusb 'Bus' + and 'Device') + + \fBserialnum\fP: serial number of reader of the Smartreader+ or + Infinity USB + + \fBdevice\fP: device name + + \fBdevice:slot\fP: device name and slot number sc8in1 [1-8] + \fI(only one SC8in1 reader supported)\fR + + \fBip\fP|\fBhostname\fP: IP address or host name + + \fBport\fP: TCP/IP port + + \fBlport\fP: remapping to local TCP/IP port + + \fBgboxpport\fP: UDP port for remote gbox peer + + \fBPCSC\fP: number of PCSC reader, starting with 0 + + \fB0|1\fP: for Coolstream HD-1 STB only: select reader 0 or + reader 1 + + \fBconstantcw\fP: constant CW file name + +constant CW file format: +.TP 3n +\(bu standard format + +\fPCAID\fB:\fPProvider ID\fB:\fPService ID\fB:\fPPMT ID\fB:\fPECM PID\fI::\fRkey (16 Bytes seperated by spaces) + +example: 1234:123456:1234:2345:3456\fI::\fR00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F +.TP 3n +\(bu extended OScam format + +\fPCAID\fB:\fPProvider ID\fB:\fPService ID\fB:\fPPMT ID\fB:\fPECM PID\fI:Video PID:\fRkey (16 Bytes seperated by spaces) + +example: 1234:123456:1234:2345:3456\fI:7890:\fR00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F +.RE +.PP +\fBdetect\fP = [\fB!\fP]\fBCD\fP|[\fB!\fP]\fBDSR\fP|[\fB!\fP]\fBCTS\fP|[\fB!\fP]\fBRING\fP|[\fB!\fP]\fBNONE\fP|[\fB!\fP]\fBgpio[1-7]\fP +.RS 3n +status detect of card, NONE = no detection, ! = inverse, default:CD +.RE +.PP +\fBcardmhz\fP = \fBmhz\fP +.RS 3n +set standard SC frequency in units of 10 kHz, for Irdeto SC set to 600 mhz, +for Dreambox DM800 / DM8000 set to 2700 mhz, for Dreambox DM7025 set to +8300 mhz, for older PowerPC Dreambox STBs set to 3150 mhz, refer to +OVERCLOCKING, default:357 +.RE +.PP +\fBmhz\fP = \fBfrequency\fP +.RS 3n +set reader frequency in units of 10 kHz, if \fBmhz\fP > \fBcardmhz\fP you +are in overclocking mode. For Smargo readers and Dreambox internal readers +frequency will be set by ATR if \fBautospeed\fP is set to 1, default:357 +.RE +.PP +\fBautospeed\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = sets mhz according to ATR. Currently only used for smartreader, Smargo and +Dreambox internal protocol, other readers will be adapted to use this parameter +as well. If You wan't to overclock you're card set it to 0, default:1 +.RE +.PP +\fBdeprecated\fP = \fB0\fP|\fB1\fP +.RS 3n +First the SC will be initialized in normal mode. If it fails, the SC will be automatically +reverted to deprecated mode, so that the SC speed will not be changed and the communication +will remain on normal ATR speed of 9600 baud. + +1 = use deprecated SC mode only, default:0 +.RE +.PP +\fBmode\fP = \fBmode\fP +.RS 3n +set card init mode for AzBox internal reader, default:none +.RE +.PP +\fBsmargopatch\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable workaround for Smartreader+ reader until native mode works, default:0 +.RE +.PP +\fBsc8in1_dtrrts_patch\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable fix for SC8in1/MCR DTR/RTS kernel bug, default:0 +.RE +.PP +\fBuse_gpio\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = use GPIO to init the reader. This needs to be set on WRT54G router, default:0 +.RE +.PP +\fB ins2e06\fP = \fBpayload\fP +.RS 3n +add check control for pin payload (4 hex bytes) for NDS Videoguard 2 SCs, valid for physical readers only, default:none +.RE +.PP +\fBins7e\fP = \fBpayload\fP +.RS 3n +add 26 hex-bytes payload for NDS Videoguard 2 SCs, valid for physical readers only, default:none +.RE +.PP +\fBins7e11\fP = \fBTA1 byte\fP +.RS 3n +set TA1 byte for NDS Videoguard 2 SCs, valid for physical readers only, default:none +.RE +.PP +\fBfix07\fP = \fB0\fP|\fB1\fP +.RS 3n +1=enable 0x07 fix for NDS Videoguard 2 SCs, valid for physical readers only, default:1 +.RE +.PP +\fBforce_irdeto\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = force Irdeto SC mode even if RSA key is set for Irdeto tunnled Nagravion SC, default:0 +.RE +.PP +\fBnagra_read\fP = \fB0\fP|\fB1\fP|\fB2\fP +.RS 3n +read Nagravison records (on NCMED SCs only): + + \fB0\fP = disabled (default) + \fB1\fP = read all records with expired rights + \fB2\fP = read records with valid rights only +.RE +.PP +\fBrsakey\fP = \fBRSA key\fP +.RS 3n +RSA key for Nagravision/Tiger SCs, CAM key data for Irdeto SCs, Conax SCs, default:none +.RE +.PP +\fBdeskey\fP = \fBDES key\fP +.RS 3n +DES key for Viaccess SCs post-processing, default:none +.RE +.PP +\fBboxkey\fP = \fBbox key\fP +.RS 3n +box key for Nagravision SCs / CAM key for Irdeto SCs +.RE +.PP +\fBpincode\fP = \fBpincode\fP +.RS 3n +pincode for Conax, Cryptoworks and Viaccess SCs, default:none +.RE +.PP +\fBfix9993\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable fix for 9993 error with CAID 0919 Videoguard SCs, default:0 +.RE +.PP +\fBreadtiers\fP = \fB0\fP|\fB1\fP|\fB2\fP +.RS 3n +method to get tiers of NDS Videoguard SCs: + + \fB0\fP = disabled (default) + \fB1\fP = ins70 method + \fB2\fP = ins76 method +.RE +.PP +\fBboxid\fP = \fBNDS box ID\fP +.RS 3n +NDS receiver box id +.RE +.PP +\fBndsversion\fP = \fB0\fP|\fB1\fP|\fB12\fP|\fB2\fP +.RS 3n +set NDS Videoguard version + + \fB0\fP = autodetection (default) + \fB1\fP = NDS Videoguard 1 + \fB12\fP = NDS Videoguard 1+ + \fB2\fP = NDS Videoguard 2 +.RE +.PP +\fBaeskeys\fP = \fBCAID #0\fP@\fBprovid\fP:\fBAES key #0 CAID #0\fP[,\fBAES key #1 CAID #0\fP],...[;\fBCAID #1\fP@\fBprovid\fP:\fBAES key #0 CAID #1\fP[,\fBAES key #1 CAID #1\fP],...]... +.RS 3n +multiple 16 bytes AES keys for Viaccess SCs (the used postprocessing AES key is specified through the D2 nano of the ECM) + +special AES keys: + + \fB00\fP = do not return any CW, no AES key specified + \fBFF\fP = return CW received from the S, no AES key specified + +example: + + aeskeys = 0500@012345:000102030405060708090a0b0c0d0e0f;0500@543210:000102030405060708090a0b0c0d0e0f,0,0f0e0d0c0b0a090807060504030201 +.RE +.PP +\fBkey\fP = \fBDES key\fP +.RS 3n +key for newcamd remote reader encryption +.RE +.PP +\fBuser\fP = \fBname\fP +.RS 3n +user for remote reader +.RE +.PP +\fBpassword\fP = \fBpassword\fP +.RS 3n +password for remote reader +.RE +.PP +\fBservices\fP = \fB[!]services[,[!]]...\fP +.RS 3n +reader [de]assignment to service group, default=none +.RE +.PP +\fBcaid\fP = \fB[&][:][,[&][:target ]]...\fP +.RS 3n +define and mapping of CAIDs for reader, default:all CAIDs with mask FFFF + +example: caid = 0100 + caid = 0200&ffee:0300 + caid = 0400&ff00:0500,0600 + caid = 0702,0722 + caid = 0702&ffdf (shortcut for the example above) +.RE +.PP +\fBident\fP = \fB:[,provid]...[;:[,provid]...]...\fP +.RS 3n +set CAID and SC specific ident for reader + +example: ident = 0100:123456,234567;0200:345678,456789 +.RE +.PP +\fBclass\fP = \fB[!]class[,[!]class]...\fP +.RS 3n +set SC specific class in hex for reader + +example: class = 01,02,!1b,!2b +.RE +.PP +\fBchid\fP = \fBCAID:ChID\fP +.RS 3n +set SC specific ChIDs for reader, default:none + +example: chid = 0100:12 +.RE +.PP +\fBgroup\fP = \fB1..64[,1..64]...\fP +.RS 3n +reader assingment to groups, default:none, \fIrequired\fR +.RE +.PP +\fBaudisabled\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = exclude reader from auto AU, default:0 +.RE +.PP +\fBauprovid\fP = \fBprovider ID\fP +.RS 3n +set provider ID to use the right reader for auto AU + +example: auprovid = 123456 +.RE +.PP +\fBdisableserverfilter\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = ignore \fBcaid\fP and \fBprovid\fP settings of reader due faulty clients, default:0 +.RE +.PP +\fBinactivitytimeout\fP = \fBseconds\fP +.RS 3n +inactivity timeout for all TCP based remote readers, -1 = reconnect on network failure for newcamd, even in idle, default:0 +.RE +.PP +\fBreconnecttimeout\fP = \fBseconds\fP +.RS 3n +reconnect if missing answers from a remote reader, default:30 +.RE +.PP +\fBreconnectdelay\fP = \fBmilli-seconds\fP +.RS 3n +set maximum TCP connection block delay, default:60000 +.RE +.PP +\fBconnectoninit\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = allow newcamd connections to be established on startup although there isn't a request yet, default:0 +.RE +.PP +\fBkeepalive\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = allow cs378x TCP socket to be always connected, default:0. Always on if cacheex reader type. +.RE +.PP +\fBfallback\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = define reader as fallback, standard and fallback reader must have the same group, default:0 +.RE +.PP +\fBfallback_percaid\fP = \fB[:[,ident]]...[;[:[,ident]]...]....\fP +.RS 3n +use reader as fallback for defined CAIDs only, two-digit wildcard CAIDs are possible, \fBfallback_percaid\fP overrules \fBfallback\fP, default:none + + example: fallback_percaid = 1234:234567;89;10:345678 +.RE +.PP +\fBemmcache\fP = \fBusecache,rewrite,logging\fP +.RS 3n +set EMM cache of local reader: + + \fBusecache\fP = \fB0\fP|\fB1\fP||\fB2\fP + + \fB0\fP = EMM caching disabeld (default) + \fB1\fP = enable EMM caching and save EMMs to file after + stopping OSCam + \fB2\fP = enable EMM caching, don't save EMMs to file + after stopping OSCam + + \fBrewrite\fP = determines how often one and the same EMM is + written, default:0 + + \fBlogging\fP = EMM logging mask: + + \fB0\fP = EMM logging disabled (default) + \fB1\fP = logging EMM errors + \fB2\fP = logging written EMMs + \fB4\fP = logging skipped EMMs + \fB8\fP = logging blocked EMMs + \fB16\fP = logging disabled AU + + example: emmcache = 1,3,2 +.RE +.PP +\fBcacheex\fP = \fB0\fP|\fB1\fP|\fB2\fP|\fB3\fP +.RS 3n +set cache exchange mode + + \fB0\fP: disable cache exchange mode (default) + \fB1\fP: enable cache exchange pull mode + \fB2\fP: enable cache exchange push mode for camd 3.5x / 3.57x and CCcam + protocol + \fB3\fP: enable reverse cache exchange push mode for camd 3.5x / 3.57x + and CCcam protocol + +\fIIdentical cache exchange modes must be set on local OSCam server and remote OSCam user asccount.\fR + +\fIPlease consider memory consumption.\fR +.RE +.PP +\fBcacheex_maxhop\fP = \fBhops\fP +.RS 3n +define maximum hops for cache exchange, default=10 +.RE +.PP +\fBcsp_ecm_filter\fP = \fB[caid][&mask][@provid][$servid],n\fP +.RS 3n +cache exchange incoming ECM filter setting (mode 2 only) for Cardservproxy, default:none +.RE +.PP +\fBcacheex_drop_csp\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = drop incoming Cardservproxy cache (mode 2 only), detection is zero ecmd5, default:0 +.RE +.PP +\fBcacheex_allow_request\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = allow incoming ECM request (mode 2), default:1 +.RE +.PP +\fBcacheex_allow_filter\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = allow cache exchange filter (for cache exchange mode 2 only), default:1 +.RE +.PP +\fBcacheex_block_fakecws\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable fake DCWs blocking (for cache exchange mode 2 only), get fake DCWs form \fBoscam.fakecws\fP, default:0 +.RE +.PP +\fBecmwhitelist\fP = [\fBCAID\fP[@\fBprovid\fP]:]\fBlength\fP[,\fBlength\fP]...[;[\fBCAID\fP[@\fBprovid\fP]:]\fBlength\fP[,\fBlength\fP]...]... +.RS 3n +set valid ECM length per CAID and provid in hex, default:none,provid=000000 + +example: ecmwhitelist = 10,20,0a,0b + ecmwhitelist = 0100:10,20;0200@123456:0a,4b + +\fIIn normal operation mode this parameter is not required.\fR +.RE +.PP +\fBecmheaderwhitelist\fP = [\fBCAID\fP[@\fBprovid\fP]:]\fBheader\fP[,\fBheader\fP]...[;[\fBCAID\fP[@\fBprovid\fP]:]\fBheader\fP[,\fBheader\fP]...]... +.RS 3n +set vaild ECM header per CAID and provid in hex, default:none,provid=000000 +.RE +.PP +\fBratelimitecm\fP = \fBcount\fP +.RS 3n +number of different SIDs in ECMs allowed for an interval, default:0 +.RE +.PP +\fBecmnotfoundlimit\fP = \fBcount\fP +.RS 3n +number of ECMs with "not found" answer until the reader will be restarted, 0 = no limit, default:0 +.RE +.PP +\fBresetcycle\fP = \fBcount\fP +.RS 3n +number of ECMs until SC reset is performed, 0 = disabled, valid for physical readers only, default:0 +.RE +.PP +\fBratelimitseconds\fP = \fBseconds\fP +.RS 3n +interval for rate limit, default:0 +.RE +.PP +\fBecmunique\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable check for matching ECM hash in ratelimit slot , default:0 +.RE +.PP +\fBsrvidholdseconds \fP = \fBseconds\fP +.RS 3n +time to keep service ID in ratelimit slot, during this time checkeding for ecmunique is disbaled, default:0 +.RE +.PP +\fBcooldown\fP = \fBdelay\fP,\fBduration\fP +.RS 3n + define cooldown: + + \fBdelay\fP: delay in seconds for which the reader is allowed to do + more ECM requests than defined by ecmratelimit, + default: none + + \fBduration\fP: duration in seconds the reader needs to cooldown, + default:none + +\fIratelimitecm and ratelimitseconds are required\fR +.RE +.PP +\fBblocknano\fP = \fBnano[,nano]...\fP|\fPall\fP +.RS 3n +list of EMM-nanos to block (in hex w/o 0x) or all EMM-nanos, valid for physical readers only, default:none + + example: blocknano = 45,93,7a,ff + blocknano = all +.RE +.PP +\fBblockemm-u\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = block unique EMMs, default:0 +.RE +.PP +\fBblockemm-s\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = block shared EMMs, default:0 +.RE +.PP +\fBblockemm-g\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = block global EMMs, default:0 +.RE +.PP +\fBblockemm-unknown\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = block unknown types of EMMs, default:0 +.RE +.PP +\fBblockemm-bylen\fP = \fB[length range,length range]...\fP +.RS 3n +block all types of EMMs by length, default:none + + example: blockemm-bylen = 1-10,11- +.RE +.PP +\fBread_old_classes\fP = \fB0\fP|\fB1\fP \fI(Viaccess SCs only)\fR +.RS 3n +0 = read only active entitlements +1 = read all entitlements (default) +.RE +.PP +\fBsaveemm-u\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = save unique EMMs to log file, default:0 +.RE +.PP +\fBsaveemm-s\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = save shared EMMs to log file, default:0 +.RE +.PP +\fBsaveemm-g\fP = \fB0\fP|\fB1\fP +.RS 3n +1= save global EMMs to log file, default:0 +.RE +.PP +\fBsaveemm-unknown\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = save unknown types of EMMs to log file, default:0 +.RE +.PP +\fBsavenano\fP = \fBnano[,nano]....\fP|\fPall\fP \fI(obsolete)\fR +.RS 3n +list of EMM-nanos to save (in hex w/o 0x) or all EMM-nanos, only valid for physical readers, default:none + + example: savenano = 45,93,7a,ff + savenano = all +.RE +.PP +\fBreadnano\fP = \fB[path]filename\fP +.RS 3n +write file (usually a copy of a file saved by savenano) to your smartcard, if no path is specified, the specified file is searched for in the configuration directory, only valid for physical readers, default:none + + example: readnano = write.emm + readnano = /var/oscam/write.emm +.RE +.PP +\fBdropbadcws\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = reject bad CWs, send "not found" instead of bad CWs, default:0 +.RE +.PP +\fBdisablecrccws\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = disable CRC for CW, default: 0 + +\fIIn normal operation mode this parameter is not required. Parameter is incompatible with DVB standard.\fR +.RE +.PP +\fBident\fP = \fB[:[,]...][;[:[,]...]]...\fP +.RS 3n +use this reader as local in loadbalancer's reader selection, default:none +.RE +.PP +\fBlb_whitelist_services\fP = \fB,...\fP +.RS 3n +reader assignement to service group for channels which may never be blocked by the loadbalancer to the reader , default=none +.RE +.PP +\fBlb_weight\fP = \fBweight\fP +.RS 3n +the higher the value the higher the probability for reader selection in load balacing mode, default:100 + + It's an divider for the average responstime. +.RE +.PP +\fBlb_force_fallback\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = set the reader always as fallaback for load balacing without considering the reader's statistics, default:0 +.RE +.PP +\fBcccversion\fP = \fB
..\fP +.RS 3n +set CCcam version, default:none + +example: cccversion = 1.2.34 +.RE +.PP +\fBcccmaxhops\fP = \fBhops\fP +.RS 3n +set CCcam maximum SC distance hops, default:10 + + \fB-1\fP = disabled + \fB0\fP = remote local SCs only + \fB1\fP = remote local SCs and + 1 hop + \fB2\fP = remote local SCs and + 2 hops + and so on + +After reading this SC hop will be incremented by one. +.RE +.PP +\fBccchop\fP = \fBhop\fP +.RS 3n +set hop for non CCCam readers, default:0 +.RE +.PP +\fBcccreshare\fP = \fBhop\fP +.RS 3n +set reader's CCcam reshare hop, default:0 + + \fB-1\fP = reshare value off cccam in global config + \fb0\fP = resharing for direct peer only + \fBx\fP = resharing for direct peer and share level x +.RE +.PP +\fBcccwantemu\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = request to provide emu from CCCam server, too, default:0 +.RE +.PP +\fBccckeepalive\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = send keepalive messages to keep connection to remote CCCam server up, default:0 +.RE +.PP +\fBcccreconnect\fP = \fBtimeout\fP +.RS 3n +reconnect again after ECM request timeout in milli-seconds, default:4000 +.RE +.PP +\fBcccmindown\fP = \fBnumber\fP +.RS 3n +filters all readers with hops smaller than number, default:0 +.RE +.PP +\fBgbox_reshare\fP = \fBlevel\fP +.RS 3n +gbox reshare level of local cards, default:0 +.RE +.PP +\fBgbox_max_distance\fP = \fBdistance\fP +.RS 3n +maximum distance to receive gbox peer cards, default:2 +.RE +.PP +\fBgbox_max_ecm_send\fP = \fBnumber\fP +.RS 3n +maximum of gbox peers ECMs will be send to, default:3 +.RE +.PP +\fBuse_ssl\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = use SSL for ghttp protocol, default:0 +.RE +.SH OVERCLOCKING +.TP 3n +\(bu +Dreambox and other internal readers + +For Dreambox and other internal readers the highest possible clockrate will be +auto detected. The \fBmhz\fR parameter lets you override the values chosen by +OSCam, if it differs from 357 and 358, but usually you will not set any value +for mhz. + +For certain Dreamboxes (especially PPC clones) the default mhz parameter leads +to slow ECM times and/or "not found" ECMs. By setting \fBmhz\fR to values like +200, 300, 400, ... 1600 you can find a value that works for your receiver and +your card. The higher the \fBmhz\fR value, the slower the ECM time (strange enough). + +If you choose the value too low, your card is not recognized (no ATR or "card +not supported"). If you choose the value too high, you get slow ECM times. Our +experience is that either no \fBmhz\fR line, or a line \fBmhz\fR = 1000 works +best. +.TP 3n +\(bu +Phoenix / Smartmouse reader + +Overclocking does not work with Windows and Mac OS X. +Set \fBmhz\fR equivalent to the frequency of the reader. +OSCam can not set the frequency of the reader. +.TP 3n +\(bu +Smargo Smartreader+ + +Use protocol = smargo for the FDDI kernel drivers (no libusb needed) or (not +recommended) use protocol = smartreader for OSCam's driver implementation +based on libusb. + +Set the reader frequency with the native Smargo Smartreader+ tool (srp_tools). +If not setting \fBmhz\fR and \fBcardmhz\fR, OSCam tries to set the baudrate +automatically, according to the maximum speed indicated by ATR. Overclocking +is possible. +.PP +OSCam tries to set the baudrate automatically. +A standard serial port has limited baudrate settings, so SC overclocking might not work. +When using a serial reader the best way for overclocking is connecting it to a FTDI based USB to serial port adapter. + +If overclocking does not work, verify the effective baudrate in the logfile. +If it deviates too much from the requested baudrate, the SC will not be recognized (no ATR) +and the value for \fBmhz\fR should be adjusted again. +The higher the baudrate, the more accurate the effective baudrate can be. +.SH CACHE EXCHANGE +.TP 3n +\(bu +pull mode (on request: cache exchange from remote to local OSCam) + +ECM requests will be forwarded to the remote cache exchange partner. If the CW +could not be found in the cache of the remote exchange partner, a not found +will be answered. If the CW could not be found in the cache of the remote +exchange partner but a pending ECM request is open, the request will be +re-initiated after the wait time defined in \fBcacheexwaittime\fR. +.TP 3n +\(bu +push mode (continuous: cache exchange from remote to local OSCam) + +CWs from the remote cache exchange partner will be forwarded to the local +cache. Forwarding only works while the camd camd 3.5x / 3.57x or CCcam +protocol connection between the local and remote OSCam has been established. +.TP 3n +\(bu +reverse push mode (continuous: cache exchange from local to remote OSCam) + +CWs from the local cache will be forwarded to the remote cache exchange +partner. Forwarding only works while the camd camd 3.5x / 3.57x or CCcam +protocol connection between the remote and local OSCam has been established. +.SH EXAMPLES +.TP 3n +\(bu +serial mouse compatible reader + + [reader] + label = myserialmousereader + detect = cd + protocol = mouse + device = /dev/ttyS1 + group = 1 + caid = 0100 + services = myservice,!thisservice +.TP 3n +\(bu +USB mouse compatible reader + + [reader] + label = myusbmousereader + detect = cd + protocol = mouse + device = /dev/ttyUSB0 + aeskey = 0102030405060708090a0b0c0d0e0f10 + group = 2 + caid = 0200 +.TP 3n +\(bu +camd 3.78x reader + + [reader] + label = mycamd378xreader + protocol = cs378x + device = 192.168.0.1,1234 + user = user1 + password = password1 + group = 3 +.TP 3n +\(bu +newcamd reader + + [reader] + label = mynewcamdreader + protocol = newcamd + key = 0102030405060708091011121314 + device = 192.168.0.2,2345 + user = user2 + password = password2 + group = 4 +.TP 3n +\(bu +CCcam reader + + [reader] + label = mycccamreader + protocol = cccam + device = 192.168.0.3,3456 + user = user3 + password = password3 + group = 5 + caid = 0300,0400,0500 + cccversion = 1.2.3 +.TP 3n +\(bu +PCSC reader + + [reader] + label = mypcscreader + protocol = pcsc + device = 0 + aeskey = 0102030405060708090a0b0c0d0e0f10 + group = 6 + caid = 0600 +.TP 3n +\(bu +Smargo Smartreader+ + + [reader] + label = mysmartreader + protocol = smartreader + device = 001:002 + aeskey = 0102030405060708090a0b0c0d0e0f10 + group = 7 + caid = 0700 +.TP 3n +\(bu +internal reader + + [reader] + label = myinternalreader + protocol = internal + device = /dev/sci0 + group = 8 + caid = 0800 +.TP 3n. +\(bu +sc8in1 reader + + [reader] + label = mysc8in1reader + protocol = sc8in1 + device = /dev/ttyUSB0:1 + group = 9 + caid = 0900 +.TP 3n +\(bu +constant CW + + [reader] + label = myconstantcw + protocol = constcw + device = /var/keys/constant.cw + group = 10 +.TP 3n +\(bu +gbox reader + + [reader] + label = mygboxreader + protocol = gbox + device = 192.168.0.4,45678,56789 + user = user4 + password = password4 + group = 11 + caid = 1100 +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.services.5 b/Distribution/doc/man/oscam.services.5 new file mode 100644 index 0000000..e86f3ed --- /dev/null +++ b/Distribution/doc/man/oscam.services.5 @@ -0,0 +1,32 @@ +.TH oscam.services 5 +.SH NAME +\fBoscam.services\fR - definition of services for OSCam +.SH SYNOPSIS +service definitions +.SH DESCRIPTIONS +.SS "The [] section" +service name section, service name sections are recurring, \fIrequired\fR, \fImaximum 64 services are allowed\fR +.PP +\fBcaid\fP = \fBCAID[,CAID]...\fP +.RS 3n +listing of CAIDs in hex +.RE +.RE +.PP +\fBprovid\fP = \fBprovider ID[,provider ID]...\fP +.RS 3n +listing of provider IDs in hex +.RE +.RE +.PP +\fBsrvid\fP = \fBservice ID[,service ID]...\fP +.RS 3n +listing of service IDs in hex +.RE +.SH EXAMPLES + [myservice] + CAID=0100,0200,000A + provid=000001,ABCDEF + srvid=0001,0002,000A,000B +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.srvid.5 b/Distribution/doc/man/oscam.srvid.5 new file mode 100644 index 0000000..3d9d4a5 --- /dev/null +++ b/Distribution/doc/man/oscam.srvid.5 @@ -0,0 +1,24 @@ +.TH oscam.srvid 5 +.SH NAME +\fBoscam.srvid\fR - service ID configuration file for OSCam +.SH SYNOPSIS +service ID mappings +.SH DESCRIPTIONS +.PP +\fBCAID[,CAID]...\fP:\fBservice ID\fP|\fB[provider]\fP|\fB[name]\fP|\fB[type]\fP|\fB[description]\fP + +.RS 3n +mapping between CAID, service ID, provider, name, type and description of service +.RE +.SH ANNONTATIONS +\fIPlease use Unix text file format only.\fR + +You only need the \fBoscam.srvid\fR when using the monitor or the web interface. +\fIFor saving memory consumption only insert the service IDs you really need.\fR Some +external programs use their own \fBoscam.srvid\fR and do not need the \fBoscam.srvid\fR of OSCam. +.SH EXAMPLES + 0001,0002,0003:000a|my provider 1|tv name 1|tv|my tv package + 0004,0005,0006:000a|my provider 2|radio name 2|radio|my radio package + 0006:000b|my provider 3|tv name 3| +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.srvid2.5 b/Distribution/doc/man/oscam.srvid2.5 new file mode 100644 index 0000000..88a5de6 --- /dev/null +++ b/Distribution/doc/man/oscam.srvid2.5 @@ -0,0 +1,20 @@ +.TH oscam.srvid2 5 +.SH NAME +\fBoscam.srvid2\fR - service ID configuration file for OSCam +.SH SYNOPSIS +service ID mappings +.SH DESCRIPTIONS +.PP +\fBservice ID\fP:\fBCAID\fP[:\fB@provider ID\fP[\fB@provider ID\fP]...][,:\fBCAID\fP[:\fB@provider ID\fP[\fB@provider ID\fP]...]][\fBname\fP]|[\fBtype\fP]|[\fBdescription\fP]|[\fBprovider\fP] + +.RS 3n +mapping between service ID, CAID, provider ID, name, type, description and proivder +.RE +.SH ANNONTATIONS +\fIPlease use Unix text file format only.\fR + +You only need the \fBoscam.srvid2\fR when using the monitor or the web interface. +\fIFor saving memory consumption only insert the service IDs you really need.\fR Some +external programs use their own \fBoscam.srvid2\fR and do not need the \fBoscam.srvid2\fR of OSCam. +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.tiers\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.tiers.5 b/Distribution/doc/man/oscam.tiers.5 new file mode 100644 index 0000000..7e36e0a --- /dev/null +++ b/Distribution/doc/man/oscam.tiers.5 @@ -0,0 +1,20 @@ +.TH oscam.tiers 5 +.SH NAME +\fBoscam.tiers\fR - TIER configuration file for OSCam +.SH SYNOPSIS +TIER mappings +.SH DESCRIPTIONS +.PP +\fBCAID[,CAID]...\fP:\fBTIER ID\fP|\fBdescription\fP + +.RS 3n +mapping between CAID, TIER ID and description of TIER +.RE +.SH ANNONTATIONS +\fIPlease use Unix text file format only.\fR +.SH EXAMPLES + 0001,0002,0003:000a|my TIER 1 + 0004:000b|my TIER 2 + 0005:000b|my TIER 3 +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.user\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.user.5 b/Distribution/doc/man/oscam.user.5 new file mode 100644 index 0000000..15ffa63 --- /dev/null +++ b/Distribution/doc/man/oscam.user.5 @@ -0,0 +1,405 @@ +.TH oscam.user 5 +.SH NAME +\fBoscam.user\fR - user configuration file for OSCam +.SH SYNOPSIS +The user configuration file for OSCam contains user definitions. [account] +sections in \fBoscam.user\fR are \fIrecurring\fR (more than one account). +.SH DESCRIPTIONS +.SS "The [account] section" +.PP +\fBuser\fP = \fBname\fP +.RS 3n +account name, \fIrequired\fR +.RE +.PP +\fBpwd\fP = \fBpassword\fP +.RS 3n +password for account, \fIrequired\fR +.RE +.PP +\fBdescription\fP = \fBtext\fP +.RS 3n +description of user account +.RE +.PP +\fBdisabled\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = account disabled, default:0 +.RE +.PP +\fBhostname\fP = \fBhostname\fP +.RS 3n +host from which user connection is allowed +.RE +.PP +\fBexpdate\fP = \fB--\fP|\fB//\fP +.RS 3n +expiration date for account, default:none + + example: expdate = 2001-11-21 + expdate = 2002/12/22 +.RE +.PP +\fBallowedprotocols\fP = \fB[camd33][,][camd35][,][cs357x][,][cs378x][,][newcamd][,][cccam][,][gbox][,][radegast]\fP +.RS 3n +list of all allowed connection protocols, default:all connection protocols +.RE +.PP +\fBallowedtimeframe\fP = \fBDAY@HH:MM-HH:MM[,HH:MM-HH:MM][,HH:MM-HH:MM] [;DAY@HH:MM-HH:MM[,HH:MM-HH:MM][,HH:MM-HH:MM]]\fP +.RS 10n +where DAY=\fBSUN,MON,TUE,WED,THU,FRI,SAT\fP or \fBALL\fP (for all possible days) +.RE +.RS 3n + +account enabled from hh:mm to hh:mm for the specified day(s), default:none + +comma (,) to separate times and semicolon(; ) to separate the different days. +You can use ALL@ if you want the same time frames for everyday. + +Example: + +allowedtimeframe=ALL@10:00-22:00;MON@00:00-02:00,02:45-04:37; FRI@00:00-10:00,22:00-24:00;SAT@00:00-24:00 + +If you use: DAY@22:00-05:00 this will be turned into DAY@00:00-05:00,22:00-24:00 + +ALL@ is always checked and used, so you can watch TV the whole day on FRIday in this exemple. There is no problem to overlap ALL@ in a day definition, like for SAT@ definition. +.RE +.PP +\fBmax_connections\fP = \fBcount\fP +.RS 3n +maximum allowed connections per user when unique level will be adducted, default:1 +.RE +.PP +\fBuniq\fP = \fB0\fP|\fB1\fP|\fB2\fP|\fB4\fP +.RS 3n +unique level: + + \fB0\fP = disabled (default) + \fB1\fP = only one connection per user is allowed + \fB2\fP = set user to fake if source ip is different + (e.g. for newcamd clients with different CAIDs and ports) + \fB3\fP = only one connection per user, but only the last login + will survive (old MpCS behavior) + \fB4\fP = set user only to fake if source ip is different, + but only the last login will survive +.RE +.PP +\fBnumusers\fP = \fBquantity\fP +.RS 3n +anti-cascading: user per account, 0 = anti-cascading disabled, -1 = global value from oscam.conf, default:-1 +.RE +.PP +\fBpenalty\fP = \fB0\fP|\fB1\fP|\fB2\fP +.RS 3n +level of penalty: + + \fB-1\fP = level of oscam.conf (default) + \fB 0\fP = only logging + \fB 1\fP = send fake CWs + \fB 2\fP = temporary user ban + \fB 3\fP = send delayed CWs +.RE +.PP +\fBfakedelay\fP = \fB0\fP|\fB1\fP|\fBmilli-seconds\fP +.RS 3n +set fake delay time individually for user: + + \fB 0\fP = disable fake delay + \fB-1\fP = fake delay of oscam.conf (default) +.RE +.PP +\fBacosc_max_ecms_per_minute\fP = \fBcount\fP +.RS 3n +maximum ecms per minute, 0 = unlimited, -1 = use global setting, default:0 +.RE +.PP +\fBacosc_max_active_sids\fP = \fBcount\fP +.RS 3n +maximum active SIDs with anti-cascading over SID, 0 = unlimited, -1 = use global setting, default:0 +.RE +.PP +\fBacosc_zap_limit\fP = \fBcount\fP +.RS 3n +zap limit for anti-cascading over SID, 0 = unlimited, -1 = use global setting, default:0 +.RE +.PP +\fBacosc_penalty\fP = \fB0\fP|\fB1\fP|\fB2\fP|\fB3\fP|\fB4\fP|\fB-1\fP +.RS 3n +level of penalty with anti-cascading over SID count: + + \fB0\fP = only logging (default) + \fB1\fP = send fake CWs + \fB2\fP = temporary user ban + \fB3\fP = send delayed CWs + \fB4\fP = temporary hidecards to the client + \fB-1\fP = use global setting +.RE +.PP +\fBacosc_penalty_duration\fP = \fBseconds\fP +.RS 3n +penalty duration for anti-cascading over SID count, -1 = use global setting, default:0 +.RE +.PP +\fBacosc_delay\fP = \fBmilli-seconds\fP +.RS 3n +delay for anti-cascading over SID count, -1 = use global setting, default:0 +.RE +.PP +\fBfailban\fP = \fB0\fP|\fB2\fP|\fB4\fP|\fB8\fP +.RS 3n +mask for IP address based blocking: + + \fB0\fP = ignore (default) + \fB2\fP = block IP address of a disabled account on connecting + \fB4\fP = block IP address of a sleeping account while sleeping comes up + \fB8\fP = block duplicate IP address +.RE +.PP +\fBlb_nbest_readers\fP = \fBcounts\fP +.RS 3n +set count of best readers for load balancing, -1 = use global lb_nbest_readers, default:-1 +.RE +.PP +\fBlb_nfb_readers\fP = \fBcounts\fP +.RS 3n +set count of fallback readers for load balancing, -1 = use global lb_nfb_readers, default:1 +.RE +.PP +\fBlb_nbest_percaid\fP = \fBCAID1:count1[,CAID2:count2]...\fP +.RS 3n +set count of best readers per CAIDs for load balancing, wildcard CAIDs with two-digit CAIDs possible, default:none + + example: lb_nbest_percaid = 0100:4,0200:3,03:2,04:1 + (wildcard CAIDs 03xx and 04xx) +.RE +.PP +\fBpreferlocalcards\fP = \fB0\fP|\fB1\fP +.RS 3n +SC decoding behavior:. + + \fB-1\fP = global value from oscam.conf (default) + \fB0\fP = local SCs used like a remote reader + \fB1\fP = prefer cache exchange based SCs + \fB2\fP = prefer local SCs +.RE +.PP +\fBcwc_disable\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = disbale CW cycle check, default:0 +.RE +.PP +\fBcacheex\fP = \fB0\fP|\fB1\fP|\fB2\fP|\fB3\fP +.RS 3n +set cache exchange mode + + \fB0\fP: disable cache exchange mode (default) + \fB1\fP: enable cache exchange pull mode + \fB2\fP: enable cache exchange push mode for camd 3.5x / 3.57x and + CCcam protocol + \fB3\fP: enable reverse cache exchange push mode for camd 3.5x / 3.57x + and CCcam protocol + +\fIIdentical cache exchange modes must be set on local OSCam user account and remote OSCam server.\fR + +\fIPlease consider memory consumption.\fR +.RE +.PP +\fBcacheex_maxhop\fP = \fBhops\fP +.RS 3n +define maximum hops for cache exchange, default=10 +.RE +.PP +\fBno_wait_time\fP = \fB0\fP|\fB1\fP +.RS 3n +set wait time behaviour: + + \fB0\fP: use \fBwait_time\fP set in \fBoscam.conf\fP (default) + \fB1\fP: do not use \fBwait_time\fP set in \fBoscam.conf\fP +.RE +.PP +\fBcsp_ecm_filter\fP = \fB[caid][&mask][@provid][$servid],n\fP +.RS 3n +cache exchange incoming ECM filter setting (mode 3 only) for Cardservproxy, default:none +.RE +.PP +\fBcacheex_drop_csp\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = drop incoming Cardservproxy cache (mode 3 only), detection is zero ecmd5, default:0 +.RE +.PP +\fBcacheex_allow_request\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = allow incoming ECM request (mode 3 only), default:1 +.RE +.PP +\fBcacheex_allow_filter\fP = \fB0\fP|\fB1\fP +.RS 3n +1= allow cache exchange filter (for cache exchange mode 3 only), default:1 +.RE +.PP +\fBcacheex_block_fakecws\fP = \fB0\fP|\fB1\fP +.RS 3n +1 = enable fake DCWs blocking (for cache exchange mode 3 only), get fake DCWs form \fBoscam.fakecws\fP, default:0 +.RE +.PP +\fBsleep\fP = \fBminutes\fP +.RS 3n +time waiting for inactive user, default:none +.RE +.PP +\fBsleepsend\fP = \fB0\fP|\fB255\fP +.RS 3n +255 = \fIOSCam client only\fR: stopping requests until next zap, 255 = \fIcamd 3.x only\fR: stopping requests until restart of camd 3.x client, default:0 +.RE +.PP +\fBsuppresscmd08\fP = \fB0\fP|\fB1\fP +.RS 3n +0 = tell camd 3.5x, 3.57x and 3.78x clients not to request again for rejected +CAID, service ID and provider ID combination, 1 = disable, default:0 +.RE +.PP +\fBkeepalive\fP = \fB0\fP|\fB1\fP +.RS 3n +0 = disable keepalive between server and client for newcamd or CCcam protocol, default:1 +.RE +.PP +\fBumaxidle\fP = \fBseconds\fP +.RS 3n +value for user being idle before disconnect, 0 = idle disconnect disabled, -1 use clientmaxidle in global section, default:-1 +.RE +.PP +\fBcaid\fP = \fB[&][:][,[&][:]]...\fP +.RS 3n +limit and mapping of CAIDs, default:all CAIDs with mask FFFF + +example: caid = 0100 + caid = 0200&ffee:0300 + caid = 0400&ff00:0500,0600 + caid = 0702,0722 + caid = 0702&ffdf (shortcut for the example above) +.RE +.PP +\fBau\fP = \fBlabel of reader[,label of reader]...\fP|\fB1\fP +.RS 3n +AU setting, default:none: + + \fBlabel of reader\fP = sending EMMs to specified reader + \fI(security issue: clients can see SC data!)\fR + \fB1\fP = auto AU is sending EMMs to \fIall\fR readers + \fI(security issue: clients can see SC data!)\fR +.RE +.PP +\fBgroup\fP = \fB1..64[,1..64]...\fP +.RS 3n +user assingment to reader groups, default:none, \fIrequired\fR +.RE +.PP +\fBbetatunnel\fP = \fB.:[,.: +]...\fP +.RS 3n +Define Betacrypt tunneling. +The ServiceID can also be used for wildcarded CAIDs. + + example: betatunnel = 0100.0001:0200,0300.0004:0500 + betatunnel = 0600.FFFF:0700 + +\fIBe carefull using abbreviations.\fR +.RE + +.PP +\fBemmreassembly\fP = \fB0\fP|\fB1\fP||\fB2\fP +.RS 3n +EMM reassembly, should be set for Viaccess and Cryptoworks readers if the +client that you are using to send EMMs is reassembling them instead of +just sending them to OSCam for processing. + + \fB0\fP: disabled + \fB1\fP: enabled for DVB API + \fB2\fP: enabled (default) +.RE +.PP +\fBservices\fP = \fB[!]services[,[!]]...\fP +.RS 3n +user [de]assingment to service group, default:none +.RE +.PP +\fBident\fP = \fB:[,,...][;:[,,...]]...\fP +.RS 3n +user assingment to SC specific idents, default:none +.RE +.PP +\fBclass\fP = \fB[!]class[,[!]class]...\fP +.RS 3n +user [de]assingment to SC specific classes, default=none + + example: class = 01,02,!03,!04 +.RE +.PP +\fBchid\fP = \fB:[,:]...\fP +.RS 3n +user assingment to SC specific ChIDs, default:none +.RE +.PP +\fBmonlevel\fP = \fB0\fP|\fB1\fP|\fB2\fP|\fB3\fP|\fB4\fP +.RS 3n +monitor level: + + \fB0\fP = no access to monitor (default) + \fB1\fP = only server and own procs + \fB2\fP = all procs, but viewing only + \fB3\fP = all procs, reload of \fBoscam.user\fR possible + \fB4\fP = complete access +.RE +.PP +\fBcccmaxhops\fP = \fBhops\fP +.RS 3 +maximum hops limit for CCcam clients, default:10 + + \fB-1\fP = CCcam disabled for this user + \fB0\fP = local SCs only + \fB1\fP = local SCs + 1 hop + \fB2\fP = local SCs + 2 hops + and so on +.RE +.PP +\fBcccreshare\fP = \fBlevel\fP +.RS 3n +reshare level for CCcam clients + + \fB-1\fP = use reshare level of \fBoscam.conf\fR (default) + \fB0\fP = resharing for direct peer only + \fBx\fP = resharing for direct peer and share level x +.RE +.PP +\fBcccignorereshare\fP = \fB-1\fP|\fB0\fP|\fB1\fP +.RS 3n +CCcam ignore reshare setting: + + \fB-1\fP = use ignore reshare level of \fBoscam.conf\fR (default) + \fB0\fP = use ignore reshare setting of server + \fB1\fP = use ignore reshare setting of reader or user +.RE +.PP +\fBcccstealth\fP = \fB-1\fP|\fB1\fP +.RS 3n +CCcam stealth: + + \fB-1\fP = use CCcam stealth of \fBoscam.conf\fR (default) + \fB0\fP = use extended OSCam-CCcam protocol + \fB1\fP = behaviour like the original CCcam: no activate partner + detection and extended OSCam-CCcam protocol, prevent + other OSCam to detect the server as OSCam server +.RE +.SH EXAMPLES + [account] + user = username + pwd = password + group = 1 + au = myserialmousereader + services = myservice + betatunnel = 0100.0001:0101,0100.0002:0101 + caid = 0100 + ident = 0100:000000 + uniq = 1 +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.tiers\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5), \fBoscam.whitelist\fR(5) diff --git a/Distribution/doc/man/oscam.whitelist.5 b/Distribution/doc/man/oscam.whitelist.5 new file mode 100644 index 0000000..396e1d3 --- /dev/null +++ b/Distribution/doc/man/oscam.whitelist.5 @@ -0,0 +1,47 @@ +.TH oscam.whitelist 5 +.SH NAME +\fBoscam.whitelist\fR - global ECM length whitelisting configuration file for OSCam +.SH SYNOPSIS +ECM length whitelisting +.SH DESCRIPTIONS +.PP +\fBw\fP:\fB[CAID]\fP[:]\fB[provider ID]\fP[:]\fB[service ID]\fP[:]\fB[ECM PID]\fP[:]\fB[CHID]\fP[:]\fB[ECM length 1[,ECM length 2]...]]\fP +.RS 3n + ECM length whitelisting +.RE +.PP +\fBl\fP:\fB[CAID]\fP[:]\fB[provider ID]\fP[:]\fB[service ID]\fP[:]\fB[ECM PID]\fP[:]\fB[CHID]\fP[:]\fB[ECM length 1[,ECM length 2]...]]\fP +.RS 3n + ECM length whitelisting, does not proceed with any other ECM length + whitelisting when matching, abbreviation for normal ECM length + whitelisting using w parameter +.RE +.PP +\fBi\fP:\fB[CAID]\fP[:]\fB[provider ID]\fP[:]\fB[service ID]\fP[:]\fB[ECM PID]\fP[:]\fB[CHID]\fP[:]\fB[ECM length 1[,ECM length 2]...]]\fP +.RS 3n + ignore ECM length +.RE +.PP +\fBm\fP:\fB[CAID]\fP[:]\fB[provider ID]\fP[:]\fB[service ID]\fP[:]\fB[ECM PID]\fP[:]\fB[CHID]\fP[:]\fB[ECM length 1[,ECM length 2]...]]\fP \fB[new CAID]\fP[:]\fB[new provider ID]\fP +.RS 3n + CAID und provider ID mapping, \fIfirst matching rules\fR, \fImapping is + preferred over all other whitelistings\fR +.RE +.SH ANNONTATIONS +\fIPlease use Unix text file format only.\fR +.SH EXAMPLES + w:0100 # whitelisting for CAID 0100 + + i:0200::1234 # ignore CAID 0200 with + # service ID 1234 + + i:::::2345 # ignore CHID 2345 + + m:3456:123456::::: 4567:234567 # mapping + + w: # allow all others (blacklist) + + l:0300 # whitelisting for CAID 0300 not + # proceeding if matching +.SH "SEE ALSO" +\fBlist_smargo\fR(1), \fBoscam\fR(1), \fBoscam.ac\fR(5), \fBoscam.cacheex\fR(5), \fBoscam.cert\fR(5), \fBoscam.conf\fR(5), \fBoscam.dvbapi\fR(5), \fBoscam.fakecws\fR(5), \fBoscam.guess\fR(5), \fBoscam.ird\fR(5), \fBoscam.provid\fR(5), \fBoscam.ratelimit\fR(5), \fBoscam.server\fR(5), \fBoscam.services\fR(5), \fBoscam.srvid\fR(5), \fBoscam.srvid2\fR(5),c\fBoscam.user\fR(5) diff --git a/Distribution/doc/txt/list_smargo.txt b/Distribution/doc/txt/list_smargo.txt new file mode 100644 index 0000000..d66d6c6 --- /dev/null +++ b/Distribution/doc/txt/list_smargo.txt @@ -0,0 +1,21 @@ +list_smargo(1) General Commands Manual list_smargo(1) + + + +NAME + list_smargo - list all connected Smartreader+ + +SYNOPSIS + list_smargo + +DESCRIPTIONS + The list_smargo software lists all connected Smartreader+ with bus number and device address. + +SEE ALSO + oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), + oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), + oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + + + + list_smargo(1) diff --git a/Distribution/doc/txt/oscam.ac.txt b/Distribution/doc/txt/oscam.ac.txt new file mode 100644 index 0000000..3ed76eb --- /dev/null +++ b/Distribution/doc/txt/oscam.ac.txt @@ -0,0 +1,29 @@ +oscam.ac(5) File Formats Manual oscam.ac(5) + + + +NAME + oscam.ac - anti-cascading table for OSCam + +SYNOPSIS + anti-cascading table + +DESCRIPTIONS + := + define time cycles between CWs changes relating to CAID and provider ID + + *= + default time cycles between CWs changes required + +EXAMPLES + 0100:000000=10 + *=7 + +SEE ALSO + list_smargo(1), oscam(1), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), + oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + + + + oscam.ac(5) diff --git a/Distribution/doc/txt/oscam.cacheex.txt b/Distribution/doc/txt/oscam.cacheex.txt new file mode 100644 index 0000000..dc4daa5 --- /dev/null +++ b/Distribution/doc/txt/oscam.cacheex.txt @@ -0,0 +1,31 @@ +oscam.cacheex(5) File Formats Manual oscam.cacheex(5) + + + +NAME + oscam..cacheex - global ECM length matching configuration file for OSCam + +SYNOPSIS + ECM length matching + +DESCRIPTIONS + m:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]]=[CAID][:][provider + ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]] + ECM length matching from remote cache exchange partner to local + cache, for cache exchange pull mode (cacheex = 1) only + +ANNONTATIONS + Please use Unix text file format only. + +EXAMPLES + m:1234:::::93=5678:::::93 # matching CAID 1234 and CAID 5678 with + # ECM length 93 + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), + oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid (5), + oscam.srvid2(5), oscam.user(5) + + + + oscam.cacheex(5) diff --git a/Distribution/doc/txt/oscam.cert.txt b/Distribution/doc/txt/oscam.cert.txt new file mode 100644 index 0000000..8eb9d36 --- /dev/null +++ b/Distribution/doc/txt/oscam.cert.txt @@ -0,0 +1,25 @@ +oscam.cert(5) File Formats Manual oscam.cert(5) + + + +NAME + oscam.cert - Issuer Public Keys (IPK) for OSCam + +SYNOPSIS + Issuer Public Keys (IPK) + +DESCRIPTIONS + CAID:reserved:IPK + mapping between CAID and IPK/sessions keys in hex, currently for Cryptoworks only + +EXAMPLES + 0100:00000000:0102030405060708090A0B0C0D0E0F + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), + oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), + oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + + + + oscam.cert(5) diff --git a/Distribution/doc/txt/oscam.conf.txt b/Distribution/doc/txt/oscam.conf.txt new file mode 100644 index 0000000..916ce44 --- /dev/null +++ b/Distribution/doc/txt/oscam.conf.txt @@ -0,0 +1,1086 @@ +oscam.conf(5) File Formats Manual oscam.conf(5) + + + +NAME + oscam.conf - main configuration file for OSCam + +SYNOPSIS + The main configuration file for OSCam contains global parameters such as debugging, logging, monitor, protocols and anti-cascading. sections in + oscam.conf are nonrecurring. The [global] section is required. All other sections are optional. + +DESCRIPTIONS + The [global] section + nice = -20..+20 + system priority, default:99 + + pidfile = filename + set PID file, default:none + + logfile = [filename][;syslog][;stdout] + logging targets, default:/var/log/oscam.log. You can define a maximum of one filename and additionally to log to stdout or syslog (you can also + only log to stdout or syslog and omit the filename). + PP initial_debuglevel = level + set initial debug level for OSCam start, default:0 + PP sysloghost = hostname + set remote syslog host, default:none + + syslogport = port + set TCP/IP port for remote syslog host, default:none + + ecmfmt = format + define ECM log format, default:c&p/i/s/l:h + + possible variables: + + c = CAID + d = PID + e = CSP hash + g = ID of origin gbox peer + h = checksum + i = channel ID + j = distance of gbox hops + l = length + o = ONID + p = provider ID + s = service ID + w = CW + y = payload + + use a value as prefix to hide variable with this value, control characters will be escaped by "\" + + example: ecmfmt = c&0p/i/d/s/l:h.e_w + (hide provider ID if 0) + + loghistorylines = lines + size of log message history in web interface or monitor, 0 = disabled, default:256 + + maxlogsize = kbytes + maximum log file size, 0 = unlimited, default:10 + + logduplicatelines = 0|1 + 1 = enable logging of duplicate lines in the log, default:0 + + disablelog = 0|1 + 1 = disable log file, default:0 + + cwlogdir = path + directory for CW logging, default:config dir + + emmlogdir = path + directory for EMM logging, default:config dir + + usrfile = filename + log file for user logging, default:none + + log file format: + + date + time + CWs per second + username + IP address of client + TCP/IP port + CWs found + CWs from cache + CWs not found + CWs ignored + CWs timed out + CWs tunneled + login time in unix/POSIX format + logout time in unix/POSIX format + protocol + + disableuserfile = 0|1 + 1 = avoid logging although userfile is set, default:1 (also set automatically if userfile is empty) + + usrfileflag = 0|1 + usrfile logging mode: + + 0 = only client logon/logoff will be logged in usrfile (default) + 1 = each zapping of a client will be logged in usrfile + + disablemail = 0|1 + 1 = disable saving NDS Videoguard mail messages from provider, default:1 + + mailfile = file + define file saving NDS Videoguard mail messages from provider, default:none + + enableled = 0|1|2 + 0 = LED support disabled (default) + 1 = LED support enabled for routers + 2 = LED support enabled for Qbox HD + + waitforcards = 0|1 + 1 = wait for local SCs on startup before opening network ports, default:1 + + waitforcards_extra_delay = delay + additional delay in milli-seconds after waiting for local SCs on startup before opening network ports, default:500 + + preferlocalcards = 0|1 + SC decoding behavior: + + 0 = local SCs used like a remote reader + 1 = prefer cache exchange based SCs (default) + 2 = prefer local SCs + + readerrestartseconds = seconds + seconds beetween restarts, 0 = disable reader restart, default:5 + + block_same_ip = 0|1 + 1 = reject looping ECMs from clients to readers with the same IP address, default:1 + + block_same_name = 0|1 + 1 = reject looping ECMs from clients to readers with the same name, default:1 + + clienttimeout = milli-seconds|seconds + value (clienttimeout in seconds < 100, else milli-seconds) for client process to wait for key, default:5 + + clientmaxidle = seconds + value for client process being idle before disconnect, 0 = idle disconnect disabled, default:120 + + suppresscmd08 = 0|1 + 0 = tell camd 3.5x, 3.57x and 3.78x clients not to request again for rejected CAID, service ID and provider ID combination, 1 = disable, can be + overwritten per user in oscam.user, default:0 + + fallbacktimeout = milli-seconds + time falling back to fallback reader, default:2500 + + fallbacktimeout_percaid = milli-seconds + time falling back to CAID restricted fallback reader, default:2500 + + sleep = minutes + time waiting for inactive users, default:none, can be overwritten per user in oscam.user + + serverip = IP address + bind service to specified IP address, default:none + + bindwait = seconds + value to wait for bind request to complete, default:120 + + netprio = 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20 + value for network priority: + IPP value will be applied to SO_PRIORITY (system internal prioritization) + DSCP value will be applied to IP_TOS/IPV6_TCLASS (the TOS field in the IP packet header) + + 0 = IPP=0; DSCP=CS0 (default) + 1 = IPP=1; DSCP=CS1 + 2 = IPP=1; DSCP=AF11 + 3 = IPP=1; DSCP=AF12 + 4 = IPP=1; DSCP=AF13 + 5 = IPP=2; DSCP=CS2 + 6 = IPP=2; DSCP=AF21 + 7 = IPP=2; DSCP=AF22 + 8 = IPP=2; DSCP=AF23 + 9 = IPP=3; DSCP=CS3 + 10 = IPP=3; DSCP=AF31 + 11 = IPP=3; DSCP=AF32 + 12 = IPP=3; DSCP=AF33 + 13 = IPP=4; DSCP=CS4 + 14 = IPP=4; DSCP=AF41 + 15 = IPP=4; DSCP=AF42 + 16 = IPP=4; DSCP=AF43 + 17 = IPP=5; DSCP=CS5 + 18 = IPP=5; DSCP=EF + 19 = IPP=6; DSCP=CS6 + 20 = IPP=7; DSCP=CS7 + + resolvegethostbyname = 0|1 + set mode for DNS resolving: + + 0 = getadressinfo (default) + 1 = gethostbyname + + failbancount = count + number of incorrect logins after an ip address will be blocked, default:0 + + failbantime = minutes + time for IP based blocking for clients with an invalid login attempt, 0 = failban is disabled, default:0 + + dropdups = 0|1 + mode for duplicate client connections (requirement: uniq > 0): + + 0 = mark client as duplicate, but don't disconnect them (default) + 1 = drop duplicate connections instead of marking as duplicate + + unlockparental = 0|1 + 1 = unlock parental mode option to disable Seca and Viaccess pin code request for adult movie, default:0 + + double_check = 0|1 + 1 = ECM will be send to two or more readers with the same SC and the CWs will be verified against each other, lb_nbest_readers must be set to 2 or + higher, default:0 + + double_check_caid = [CAID1|first two digits of CAID1],[CAID2|first two digits of CAID2]... + ECM will be send to two or more readers with the same SC and the CWs will be verified against each other for defined CAID or first two bytes of + CAID, lb_nbest_readers must be set to 2 or higher, default:none + getblockemmauprovid = 0|1 + 1 = server overrides EMM blocking defined on client site, default:0 + + lb_mode = mode + load balancing mode: + + 0 = load balance disabled, ECMs go to all readers (default) + 1 = fastest reader first, after 5 ECMs the reader with the fastest + response time will be selected + 2 = oldest reader first, reader with the longest no answer + 3 = lowest usage level, the usage level will be calculated by the + sum of 5 ECMS response times, the higher a reader is busy, the + higher is usage level + + lb_save = 0|counts + save auto load balance statistics: + + 0 = saving of auto load balance statistics disabled (default) + counts = save auto load balance statistics every counts ECMs + (minimum 100) + + To save CPU power a minimum counts of 100 is recommended. + + lb_nbest_readers = counts + set count of best readers for load balancing, default:1 + + lb_nfb_readers = counts + set count of fallback readers for load balancing, default:1 + + lb_nbest_percaid = CAID1:count1[,CAID2:count2]... + set count of best readers per CAIDs for load balancing, wildcard CAIDs with two-digit CAIDs possible, default:none + + example: lb_nbest_percaid = 0100:4,0200:3,03:2,04:1 + (wildcard CAIDs 03xx and 04xx) + + lb_min_ecmcount = counts + minimal ECM count to evaluate load balancing values, default:5 + + lb_max_ecmcount = counts + maximum ECM count before resetting load balancing values, default:500 + + lb_reopen_seconds = seconds + time between retrying failed load balanced readers/CAIDs/providers/services, default:900 + + lb_reopen_invalid = 0|fB1 + 0 = E_INVALID will be blocked until statistics has been cleaned, default:1 + + lb_force_reopen_always = 0|1 + 1 = force reopening immediately all failing readers if no matching reader was found, default:0 + + lb_retrylimit = milli-seconds + retry next load balanced reader only if response time is higher then lb_retrylimit, default:0 + + lb_savepath = filename + filenanme for saving load balancing statistics, default:/tmp/.oscam/stat + + lb_stat_cleanup = hour + hours after the load balancing statistics will be deleted, default:336 + + lb_retrylimits = CAID1:time1[,CAID2:time2]... + load balancing retry limit time per CAID, wildcard CAIDs with two-digit CAIDs possible, default:none + + example: lb_retrylimits = 12:0100,34:0200,5678:0300 + (wildcard CAIDs 12xx and 34xx) + + lb_noproviderforcaid = CAID1[,CAID2]... + ignore provider information for CAIDs to reduce load balancing statistic data, wildcard CAIDs with two-digit CAIDs possible, default:none + + example: lb_noproviderforcaid = 0100,02,0300,04 + (wildcard CAIDs 02xx and 04xx) + + lb_max_readers = limit + restrict the reader count to limit during load balancing learning: + + 0 = unlimited (default) + limit = restrict load balancer readers to limit + + lb_auto_timeout = 0|1 + 1 = enable automatic timeout based on load balancing statistics, default:0 + + lb_auto_timeout_p = percent + percent added to average time as timeout time, default:30 + + lb_auto_timeout_t = milli seconds + minimal time added to average time as timeout time, default:300 + + lb_auto_betatunnel = 0|1 + 1 = enable automatic Betacrypt tunneling detection for CAIDs 1801, 1833, 1834, and 1835 for load balancing, Betacrypt defintion in oscam.user with + betatunnel will be prefered, default:1 + + lb_auto_betatunnel_mode = 0|1|2 + set mode for automatic Betacrypt tunneling: + + 0 = CAID 18XX tunneling to CAID 17X2 only (default) + 1 = CAID 18XX tunneling to CAID 17X2 and CAID 17X2 tunneling to CAID 18XX (CAID 1833/1801) + 2 = CAID 18XX tunneling to CAID 17X2 and CAID 17X2 tunneling to CAID 18XX (CAID 1833/1834) + 3 = CAID 18XX tunneling to CAID 17X2 and CAID 17X2 tunneling to CAID 18XX (CAID 1833/1835) + 4 = CAID 17X2 tunneling to CAID 18XX (CAID 1833/1801 only) + 5 = CAID 17X2 tunneling to CAID 18XX (CAID 1833/1834 only) + 6 = CAID 17X2 tunneling to CAID 18XX (CAID 1833/1835 only) + + lb_auto_betatunnel_prefer_beta = direction + set direction for automatic Betacrypt/Nagravision selection: + + 0 = disabled (default) + 1 = always Betacrypt + 105 = represents the middle + 200 = always Nagravision + + The [monitor] section + port = 0|port + UDP port for monitor, 0 = monitor disabled, default:0 + + serverip = IP address + bind service to specified IP address, default:all + + nocrypt = IP address|IP address range[,IP address|IP address range]... + unsecured monitor connection, default:none + + example: nocrypt = 127.0.0.1,192.168.0.0-192.168.255.255 + + aulow = minutes + time no EMM occurs so that client is set to low, switch from status "active" to "on", default:30 + + monlevel = 0|1|2|3|4 + monitor level: + + 0 = no access to monitor + 1 = only server and own procs + 2 = all procs, but viewing only (default) + 3 = all procs, reload of oscam.user possible + 4 = complete access + + monlevel can be overwritten per user in oscam.user. + + hideclient_to = seconds + time to hide clients in the monitor if not sending requests, 0 = disabled, default:25 + + The [webif] section + httpport = [+]port + port for web interface, 0 = disabled, praefix + = enable SSL, default:none, required + + httpcert = file + file for http SSL certificate, default:oscam.pem + + httpforcesslv3 = 0|1 + 1 = force using SSLv3, default:0 + + httpuser = username + username for password protection, default:none + + httppwd = password + password for password protection, default:none + + httpcss = path + path for external CSS file, default:none + + http_prepend_embedded_css = 0|1 + 1 = embedded CSS will be added before external CSS , default:0 + + httptpl = path + path for external templates and picons, multiple simultaneously templates and picons are possible by creating sub folders (maximum length of 32 + alphanumeric characters), sub folders naming is corresponding to sub folder in URL, default:none + + example: httptpl = /this/is/my/path + + folder with multiple templates: + /this/is/my/path/template1 + /this/is/my/path/template2 + + valid URLs: + http://host:port/template1 + http://host:port/template2 + + httpjscript = path + path for oscam.js javascript, default:none + + httprefresh = seconds + status refresh in seconds, default:none + + httphideidleclients = 0|1 + 1 = enables hiding clients after idle time set in parameter hideclient_to, default:0 + + httphidetype = type[type]... + characters defining columns to hide in web interface status page (see type column), default:none + + types: + + 'c': client + 'h': http + 'm': monitor + 'p': proxy + 'r': reader + 's': server + 'x': cache exchange + + httpscript = path + path to an executable script which you wish to start from web interface, default:none + + httpallowed = IP address|IP address range[,IP address|IP address range]... + http web interface connections allowed, default:127.0.0.1,192.168.0.0-192.168.255.255,10.0.0.0-10.255.255.255,::1 + + example: httpallowed = 127.0.0.1,192.168.0.0-192.168.255.255 + + httpdyndns = hostname[,hostname][,hostname] + http web interface connections allowed, default:none + + example: httpdyndns = host.example.com + httpdyndns = host1.example.com,host2.example.com + + httpsavefullcfg = 0|1 + write config: + + 0 = all not empty parameters, all not default parameters, all + parameters not containing the same value as the same + parameter in global configuration (default) + 1 = all parameters + + httpoverwritebakfile = 0|1 + 1 = overwrite backup configuration files, default:0 + + httpreadonly = 0|1 + 1 = read only modus for web interface, default:0 + + httpshowpicons = 0|1 + 1 = show picons in user list, default:0 + + httppiconpath = path + path to picons, default:none + + httphelplang = en|de|fr| + set right language for wiki entry point, default:en + + httplocale = environment + set the locale environment, default:none + + The [lcd] section + httposcamlabel = text + set individual label in web interface header, default:OSCam + + enablelcd = 0|1 + 1 =enable LCD output, default:0 + + lcd_outputpath = path + path for LCD output, default:/tmp + + lcd_hideidle = 0|1 + 1 = hide reader in LCD output if reader idle > 20 seconds, default:0 + + lcd_writeintervall = seconds + LCD refresh interval (minimum 5), default:10 + + The [cache] section + delay = milli-seconds + value to delay cached requests, default:0 + + max_time = seconds + maximum time CWs resist in cache, the time must be 2 seconds highter than the parameter clienttimeout, default:15 + + max_hit_time = seconds + maximum time for cache exchange hits resist in cache for evaluating wait_time, default:15 + + wait_time = [caid][&mask][@provid][$servid][:awtime][:]dwtime[,[caid][&mask][@provid][$servid][:awtime][:]dwtime]... + wait time in milli-seconds for cache exchange and Cardservproxy before sending ECMs to reader or proxy, default:none + + example: wait_time = 0:50:250,0200@00009X:50:150,15:950,0500@000001:150,1602&ffdf:1200 + + cacheexenablestats = 0|1 + 1 = enable statistics for cache exchange mode, default:0 + + Please consider memory consumption. + + cacheex_cw_check = [caid][&mask][@provid][$servid]:mode:counter[,[caid][&mask][@provid][$servid]:mode:counter]... + + mode = specify behaviour for counter: + + 0 = when wait_time expires, serve highest counter's CW + got anyway, even if no counter reached (default) + 1 = never serve CW from cache exchange stored in cache, + if it's counter not reaches counter. When wait_time + expires, requests will go to normal readers + + counter = set minimum CW counter to allow CW is used, default:1 + + cacheex_mode1_delay = CAID1:time,[BCAID2:time]... + delay in milli-seconds for asking cache exchange mode 1 readers, default:none + + csp_port = port + UDP port of Cardservproxy for cache exchange, default:none + + csp_serverip = IP + bind Cardservproxy for cache exchange to specified IP address, default:none + + csp_ecm_filter = [caid][&mask][@provid][$servid][,[caid][&mask][@provid][$servid]]... + Cardservproxy incoming ECM filter setting, default:none + + csp_allow_request = 0|1 + allow incoming ECM request from Cardservproxy, default:1 + + csp_allow_reforward = 0|1 + 1 = reforward other cacheex updates to Cardservproxy peers, option could cause loops, default:0 + + cwcycle_check_enable = 0|1 + 1 = enable CW cycle check, default:0 + + cwcycle_check_caid = CAID[,CAID]... + CAID enabled for CW cycle check, default:none + + cwcycle_maxlist = count + maximum CW cycle list entries, default:500, maximum:4000 + + cwcycle_keeptime = minutes + minimum time a learned cycle time resists in memory, default:15, maximum:15 + + cwcycle_onbad = 0|1 + 0 = log bad CW cycle only, 1 = drop bad CW cycle, default:1 + + cwcycle_dropold = 0|1 + 1= drop old CW cycle, default:1 + + cwcycle_sensitive = 0|2|3|4 + drop CW mode: + + 0 = disabled + 2 = 2 (or more) same bytes and drop new CW + 3 = 3 (or more) same bytes and drop new CW + 4 = 4 (or more) same bytes and drop new CW (default) + + cwcycle_allowbadfromffb = 0|1 + 1 = allow bad cycles from a fixed fallback reader, default:0 + + cwcycle_usecwcfromce = 0|1 + 1 = use CW info from cache exchange, default:0 + + wait_until_ctimeout = 0|1 + answer when cache exchange timeout expires, if no normal readers are available for sending ECMs: + + 0 = immediately send 'not found' to client (default) + 1 = wait for cache exchange answer until client timeout expires + + The [camd33] section + port = 0|port + TCP port for camd 3.3x clients, 0 = disabled, default:0 + + serverip = IP address + bind service to specified IP address, default:all + + nocrypt = IP address|IP address range[,IP address|IP address range]... + unsecured camd 3.3x client connection, default:none + + example: nocrypt = 127.0.0.1,192.168.0.0-192.168.255.255 + + passive = 0|1 + 1 = force passive camd 3.3x client, default:0 + + key = 128 bit key + key for camd 3.3x client encryption, default:none + + example: key = 01020304050607080910111213141516 + + The [cs357x] section + port = 0|port + UDP port for camd 3.57x clients, 0 = disabled, default:0 + + serverip = IP address + bind service to specified IP address, default:all + + suppresscmd08 = 0|1 + 0 = tell camd 3.5x / 3.57x clients not to request again for rejected CAID, service ID and provider ID combination, 1 = disable, can be overwritten + per user in oscam.user, default:0 + + The [cs378x] section + port = 0|port[@CAID][:provid][,provid]...[;port@CAID[:provid][,provid]...]... + TCP port/CAID/provid definitions for camd 3.78x clients, 0 = disabled, default:0 + + examples: port = 10000@0100:100000;20000@0200:200000,300000,400000 + port = 30000 + + serverip = IP address + bind service to specified IP address, default:all + + keepalive = 0|1 + 0 = disable camd 3.78x keepalive modus, default:0 + + suppresscmd08 = 0|1 + 0 = tell camd 3.78x clients not to request again for rejected CAID, service ID and provider ID combination, 1 = disable, can be overwritten per + user in oscam.user, default:0 + + The [newcamd] section + key = DES key + default key for newcamd client encryption, default:none + + example: key = 0102030405060708091011121314 + + port = port[{DES key}]@CAID[:provid][,provid]...[;port[{DES key}]@CAID[:provid][,provid]...]... + TCP port/DES key/CAID/provid definitions, default:none + + example: port = 10000@0100:100000;20000{0102030405060708091011121314}@0200:200000,300000 + + Each CAID requires a separate port. If you don't specify a DES key for a port, the default DES key will be used. + + serverip = IP address + bind newcamd service to specified IP address, default:all + + allowed = IP address|IP address range[,IP address|IP address range]... + newcamd client connections allowed from, default:none + + example: allowed = 127.0.0.1,192.168.0.0-192.168.255.255,::1 + + keepalive = 0|1 + 0 = disable newcamd keepalive modus, default:0 + + mgclient = 0|1 + 1 = provide share information of all available CAIDs and provider IDs to mgcamd clients, default:0 + + The [radegast] section + port = 0|port + TCP/IP port for radegast clients, 0 = disabled, default:0 + + serverip = IP address + bind service to specified IP address, default:all + + allowed = IP address|IP address range[,IP address|IP address range]... + client connections allowed from, default:none + + example: allowed = 127.0.0.1,192.168.0.0-192.168.255.255 + + user = username + user name for radegast client + + The [serial] section + device = @[:baud][?option1=value1[&option2=value2]...] + [;@[:baud][?option1=value1[&option2=value2]...]]... + + parameters: + user = account + device = serial device name|hostname|IP,port + baud = serial port speed (for serial devices only) + option = timeout = milli-seconds, timeout for connection, + default:50 + delay = milli-seconds, additional delay between two + characters, default:0 + + supported serial devices (autodection): + HSIC (humax sharing interface client) + SSSP (simple serial sharing protocol) + bomba (BOMBA firmware) + dsr9500 (DSR 9500) + + example: user1@/dev/ttyS1:115200?delay=1&timeout=5000 + user2@192.160.0.1,12345?delay=1&timeout=5000 + + The [cccam] section + port = 0|port[,0|port]... + TCP/IP ports for CCcam clients, 0 = disabled, default:0 + + version =
.. + define CCcam version, minimum CCcam version 2.0.11, used with original CCcam only, default:none + + example: version = 1.2.34 + + reshare = level + reshare level for CCcam clients (default:10): + + -1 = no resharing + 0 = resharing for direct peer only + 1 = resharing for direct peer and next level + x = resharing for direct peer and next x level + + reshare_mode = mode + CCcam reshare mode: + + 0 = reader reshares only received SCs for CCcam readers, + defined filters/CAIDs/provids on other readers + 1 = reader reshares received SCs (like=0) and defined services + 2 = reader reshares only defined reader services as virtual SCs + 3 = reader reshares only defined user services as virtual SCs + 4 = reader reshares only received SCs (default) + + Every server is shared as hop = 0 and with defined reshare values. + + Service reshare only works if positive services defined: no service - no reshare! + + ignorereshare = 0|1 + CCcam reshare setting: + + 0 = use reshare setting of server (default) + 1 = use reshare setting of reader or user + + stealth = 0|1 + 1 = behaviour like the original CCcam: no activate partner detection and extended OSCam-CCcam protocol, prevent other OSCam to detect the server + as OSCam server, default:0 + + minimizecards = mode + mode how to provide CCcam servers to CCcam clients: + + 0 = no aggregation, remove duplicates only (default) + 1 = based on minimum hop: two SCs with different hops are + summarized, new SCs get a smaller hop + 2 = aggregation based on CAIDs: all SCs with the same CAIDs + will be merged, provider (maximum 32) will be merged, too + + updateinterval = seconds + interval to provide share list update to CCcam clients, values <= 10 are invalid and will be set to 30, default:240 + + keepconnected = 0|1 + set CCcam keepalive modus: + + 0 = disconnect client when maximum idle time is reached + 1 = keep client connected (default) + + recv_timeout = milli-seconds + set network timeout for receiving data, default:2000 + + forward_origin_card = 0|1 + 1 = forward ECM request to reader holding this card, load balancer, fallback and caching will be disabled, default:0 + + nodeid = ID + set CCcam node ID in hex, default:none + + example: nodeid = 0a0b0c0d0e0f1011 + + The [gbox] section + hostname = hostname| IP address + set hostname or IP address for gbox protocol, default:none + + port = port[,port]... + UDP port for gbox server, default:0 + + my_password = password + password for connection to local gbox peer, default:none + + proxy_card = [,]... + proxy reader SCs to be reshared into gbox network, default:none + + ccc_reshare = 0|1 + 1 = enable CCCam reshare, default:0 + + my_vers = version + set gbox version in hexadecimal low byte, default:2A + + my_cpu_api = byte + set gbox CPU and API byte in hexadecimal, default:40 + + gbox_reconnect = time + send message to peers in seconds, default:180, min:60, max:300 + + log_hello = 0|1 + 1 = log hello messages ,default:1 + + dis_attack_txt = 0|1 + 1 = disable creation of file attack.txt ,default:0 + + gsms_disable = 0|1 + 1 = disable gbox short message service (GSMS),default:1 + + sending a messeage: /tmp/gsms.txt: <1=mormal message|2=OSD/TV message> , status will be stored in + '/tmp/gsms.ack' respective 'gsms.nack', receiving a message: The message will be stored in /tmp/gsms.log + + tmp_dir = path + temporary directory for gbox, default:/tmp/.oscam + + accept_remm_peer = peer-id1[,peer-id2]... + accept REMM requests from gbox peer(s), default:none + + The [scam] section + port = port + UDP port for scam server, default:0 + + The [dvbapi] section + enabled = 0|1 + 1 = DVB API enabled, default:0 + + Create file /tmp/.pauseoscam to pause DVB API, e.g. if STB goes into standby and OSCam remains as SC server only. + + listen_port = 0|port + TCP/IP port for SAT IP clients, filtering has to be done on client site, 0 = disabled, default:0 + + serverip = IP address + bind service to specified IP address, default:none + + user = username + user name for DVB API client, default:anonymous + + ignore = [,]... (detached by oscam.dvbapi, obsolete) + CAIDs to be ignored, default:none + + services = [,]... (detached by oscam.dvbapi, obsolete) + services to be prioritized, default:none + + priority = :[,CAID:]... (detached by oscam.dvbapi, obsolete) + CAIDs and provider IDs to be prioritized, default:CAIDs and provider IDs of local SCs will be prioritized + + au = 0|1 + AU mode: + + 0 = disable AU (default) + 1 = enable AU + + pmt_mode = 0|1|2|3|4|5 + PMT mode: + + 0 = use camd.socket and PMT file, default + 1 = disable reading PMT file + 2 = disable camd.socket + 3 = read PMT file on startup only + 4 = do not use signal handler for monitoring /tmp + 5 = do not use signal handler for monitoring /tmp, + disable camd.socket + + ecminfo_file = 0|1 + ecm.info types: + + 0 = Disable ecm.info file + 1 = Enable ecm.info file (default) + + ecminfo_type = 0|1|2|3|4|5 + ecm.info types: + + 0 = OSCam syntax (default) + 1 = OSCam syntax with ECM time in ms instead of seconds + 2 = WiCardd + 3 = mgcamd + 4 = CCcam + 5 = camd3 + + request_mode = 0|1 + CAID request mode: + + 0 = try all possible CAIDs one by one (default) + 1 = try all CAIDs simultaneously + + boxtype = dbox2|dreambox|dm7000|duckbox|ufs910|ipbox|ipbox-pmt|qboxhd|coolstream|neumo|samygo|pc + set boxtype, auto detection of DVB API will be aspired, default:dreambox + + ipbox with camd.socket support, currently only with PGI image version 0.6 or above, verified on HD models only + + ipbox-pmt can be used on any DGS based images (with or without camd.socket support), verified on HD models only + + pc is for generic pc support (currently supported on VDR with vdr-plugin-dvbapi) + + read_sdt = 0|1|2 + mode of provider, channel name and service type auto detection via SDT: + + 0 = disabled (default) + 1 = enabled for non FTA channels only + 2 = enabled for all channels + + write_sdt_prov = 0|1 + mode writing provider name into oscam.srvid2 file: + + 0 = disabled (default) + 1 = enabled + + demuxer_fix = 0|1 + try fixing audio/video sync errors: + + 0 = disabled (default) + 1 = enabled + + cw_delay = milli-seconds + delay of CW writing, default:none + + delayer = milli-seconds + minimum time to write CW, default:0 + + reopenonzap = 0|1 + 1 = reopen demux devices on each channel switching, default:0 + + The [anticasc] section + enabled = 0|1 + 1 = enable anti-cascading, default:0 + + numusers = quantity + anti-cascading: user per account, 0 = anti-cascading disabled, default:0 + + sampletime = minutes + duration of sample, default:2 + + samples = quantity + quantity of samples over limit, default:10 + + penalty = 0|1|2|3 + level of penalty: + + 0 = only logging (default) + 1 = send fake CWs + 2 = temporary user ban + 3 = send delayed CWs + + penalty can be overwritten per user in oscam.user. + + aclogfile = filename + file for anti-cascading logging, default:none + + fakedelay = milli-seconds + fake delay time, default:1000, minimum value is 100, maximum value is 3000 + + denysamples = quantity + how many samples should be penalized, default:8 + + acosc_enabled = 0|1 + 1 = enable anti-cascading over SID count, default:0 + + acosc_max_ecms_per_minute = count + maximum ecms per minute, 0 = unlimited, default:0 + + acosc_max_active_sids = count + maximum active SIDs with anti-cascading over SID, 0 = unlimited, default:0 + + Can be overwritten per user in oscam.user. + + acosc_zap_limit = count + zap limit for anti-cascading over SID, 0 = unlimited, default:0 + + Can be overwritten per user in oscam.user. + + acosc_penalty = 0|1|2|3|4 + level of penalty with anti-cascading over SID count: + + 0 = only logging (default) + 1 = send fake CWs + 2 = temporary user ban + 3 = send delayed CWs + 4 = temporary hidecards to the client + + Can be overwritten per user in oscam.user. + + acosc_penalty_duration = seconds + penalty duration for anti-cascading over SID count, default:0 + + Can be overwritten per user in oscam.user. + + acosc_delay = milli-seconds + delay for anti-cascading over SID count, default:0 + + Can be overwritten per user in oscam.user. + +LOGGING + · reader stages + + 1 = cacheex (=1) reader (C) + 2 = local SCs (L) + 3 = other reader / proxies (P) + 4 = fallback reader (F) + + · logging format + + stage/used/chosen/possible + +MONITOR + monitor commands: + + · login + + login (for unencrypted connections only) + + + · getuser = + + get parameter for user + + + · setuser = + + set parameter for user + + + · setserver = + + set parameter for server + + + · exit + + exit monitor + + + · log + + enable|enable without hitory|disable logging for 2 minutes + + + · status + + list of current processes and clients + + + · shutdown + + shutdown OSCam + + + · restart + + restart OSCam + + + · keepalive + + send keepalive + + + · reload + + reinit user db, clients and anti-cascading, for newcamd connections: after reloading the provid, please restart newcamd client + + + · details + + details about selected PID + + + · reread + + read again + + + · debug + + set debug level (monlevel > 3 required) + + debug level mask: + 0 = no debugging (default) + 1 = detailed error messages + 2 = ATR parsing info, ECM dumps, CW dumps + 4 = traffic from/to the reader + 8 = traffic from/to the clients + 16 = traffic to the reader-device on IFD layer + 32 = traffic to the reader-device on I/O layer + 64 = EMM logging + 128 = DVB API logging + 256 = load balacing logging + 512 = cache exchange logging + 1024 = client ECM logging + 65535 = debug all + + · version + + show OSCam version + + + · commands + + show all valid monitor commands + +WEB INTERFACE + · template system + + The web interface allows you to create your own template. For developing your own template request the orignal template with the non-linked page + savetemplates.html. Store your own template in the directory specified by httptpl. + +CACHING + types of ECM caching: + + · cache1 + + ECM and CW in cache already. + + · cache2 + + ECM and checksum in cache already. + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.dvbapi(5), oscam.fakecws(5), oscam.guess(5), oscam.ird(5), + oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), oscam.tiers(5), oscam.user(5), + oscam.whitelist(5) + + + + oscam.conf(5) diff --git a/Distribution/doc/txt/oscam.dvbapi.txt b/Distribution/doc/txt/oscam.dvbapi.txt new file mode 100644 index 0000000..323a93b --- /dev/null +++ b/Distribution/doc/txt/oscam.dvbapi.txt @@ -0,0 +1,82 @@ +oscam.dvbapi(5) File Formats Manual oscam.dvbapi(5) + + + +NAME + oscam.dvbapi - DVB API configuration file for OSCam + +SYNOPSIS + DVBAPI settings, first match - first used + +DESCRIPTIONS + P: [CAID]:[provider ID]:[service ID]:[ECM PID]:[CHID] [force]:[PIDx] priority + set priority, continue = 1: proceed with priority, recommended for + pay-per-view services / EMMs (use carefully), although local SCs + will be prioritised higher, default:none + + I: [CAID]:[provider ID]:[service ID]:[ECM PID]:[CHID] [PIDx] ignore + set ignore + + J: [CAID]:[provider ID]:[service ID]:[ECM PID] joined CAID:joined provider ID:joined ECM PID + join to another ECM PID + + A: ::service ID:[PMT PID] :[provider ID][:][ECM PID] + set a dummy ECM request with CAID FFFF for services with a constant CW shown as unencrypted service (for STBs with PMT + PID support only) + + A: ::service ID:[video PID] :[provider ID][:][ECM PID] + set a dummy ECM request with CAID FFFF for services with a constant CW shown as unencrypted service (for STBs without + PMT PID support only) + + X: [CAID]:[provider ID]:[service ID]:[ECM PID] + add decoding on an extra demux index on the same CA device (Multi ECM) (not support on all STBs) + + D: [CAID]:[provider ID]:[service ID]:[ECM PID] delay + set delay in milli-seconds writing CWs + + M: [CAID]:[provider ID]:[service ID]:[ECM PID] target CAID[:][target provider ID] + mapping + + S: [device] [PMT file name] + set DVB API device name and PMT file name (valid for STAPI only) + + L: [CAID]:[provider ID]:[service ID]:[ECM PID] length + set ECM length in hexadecimal + +ANNONTATIONS + Please use Unix text file format only. + +EXAMPLES + P: 0100:123456 # prioritise CAID 0100 with provider 123456 + + P: :1234 # prioritise ECM with provider ID 1234 on + # on any CAID and service + + P: 0200 # prioritise CAID 0200 + + P: 0300::9ABC # prioritise CAID 0300 on service 9ABC only + + P: 0400 1 # prioritise CAID 0400 for pay-per-view services + + P: : 1 # prioritise for EMMs + + M: 0500 0600:123456 # map CAID 0500 to provider ID 123456 with + # CAID 0600 + + D: 0700 200 # wait 200 ms before writing CW for CAID 0700 + + I: :654321 # ignore provider ID 654321 for every CAID and + # service + + I: 0 # ignore every CAID that was not handled before + + L: 0800 8e # ECM length for CAID 0800 to 8e (hexadecimal) + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.fakecws(5), oscam.guess(5), + oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), + oscam.user(5), oscam.whitelist(5) + + + + oscam.dvbapi(5) diff --git a/Distribution/doc/txt/oscam.guess.txt b/Distribution/doc/txt/oscam.guess.txt new file mode 100644 index 0000000..e7c02f2 --- /dev/null +++ b/Distribution/doc/txt/oscam.guess.txt @@ -0,0 +1,26 @@ +oscam.guess(5) File Formats Manual oscam.guess(5) + + + +NAME + oscam.guess - CAID guessing table for OSCam + +SYNOPSIS + CAID guessing table + +DESCRIPTIONS + : + CAID guessing table by len in hex, only needed for protocols (at the moment BOMBA protocol only) that does not pass + CAIDs + +EXAMPLES + 12:3456 + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), + oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + + + + oscam.guess(5) diff --git a/Distribution/doc/txt/oscam.ird.txt b/Distribution/doc/txt/oscam.ird.txt new file mode 100644 index 0000000..ed35072 --- /dev/null +++ b/Distribution/doc/txt/oscam.ird.txt @@ -0,0 +1,25 @@ +oscam.ird(5) File Formats Manual oscam.ird(5) + + + +NAME + oscam.ird - Irdeto guessing table for OSCam + +SYNOPSIS + Irdeto guessing table + +DESCRIPTIONS + ::: + Irdeto guessing table by signature + +EXAMPLES + 12:0000000a:12ab:cd01 + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), + oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + + + + oscam.ird(5) diff --git a/Distribution/doc/txt/oscam.provid.txt b/Distribution/doc/txt/oscam.provid.txt new file mode 100644 index 0000000..c480e1a --- /dev/null +++ b/Distribution/doc/txt/oscam.provid.txt @@ -0,0 +1,25 @@ +oscam.provid(5) File Formats Manual oscam.provid(5) + + + +NAME + oscam.provid - provider table for OSCam + +SYNOPSIS + provider table + +DESCRIPTIONS + :||| + provider table + +EXAMPLES + 0100:012345|MyPay-TV|Astra 19E|German + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.ird(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), + oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + + + + oscam.provid(5) diff --git a/Distribution/doc/txt/oscam.ratelimit.txt b/Distribution/doc/txt/oscam.ratelimit.txt new file mode 100644 index 0000000..d6177ed --- /dev/null +++ b/Distribution/doc/txt/oscam.ratelimit.txt @@ -0,0 +1,33 @@ +oscam.ratelimit(5) File Formats Manual oscam.ratelimit(5) + + + +NAME + oscam.ratelimit - ECMs ratelimit for OSCam + +SYNOPSIS + limit rate of ECMs allowed for an interval + +DESCRIPTIONS + CAID:provider ID:service ID:ChID:ratelimitecm:ratelimitseconds:srvidholdseconds + + ratelimitecm + number of different SIDs in ECMs allowed for an interval + + ratelimitseconds + interval in seconds for ratelimit + + srvidholdseconds + extra time in seconds this service ID is kept in a slot before another service ID can take its place + +EXAMPLES + 0100:00002A:3A3A:4A00:0002:0010:0004 + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.server(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), + oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + + + + oscam.ratelimit(5) diff --git a/Distribution/doc/txt/oscam.server.txt b/Distribution/doc/txt/oscam.server.txt new file mode 100644 index 0000000..837835a --- /dev/null +++ b/Distribution/doc/txt/oscam.server.txt @@ -0,0 +1,709 @@ +oscam.server(5) File Formats Manual oscam.server(5) + + + +NAME + oscam.server - reader configuration file for OSCam + +SYNOPSIS + The server configuration file for OSCam contains reader parameters. sections in oscam.server are recurring (more than one + reader possible). At least one [reader] section is required. + +DESCRIPTIONS + The [reader] section + label = name + name for reader, required + + enable = 0|1 + 0 = deactivate reader, default:1 + + description = text + description of reader, default:none + + protocol = reader protocol + reader protocol, required: + + camd35|cs357x + cccam + cs378x + constcw + gbox + ghttp + internal + mouse + mp35 + newcamd|newcamd525 + newcamd524 + pcsc + radegast + scam + sc8in1 + serial + smargo + smartreader + + device = [;]serial:serialnum|bus:device| + | + ,[,]| + ,| + ,| + pcsc| + <0|1>>| + constantcw + define local or remote reader + + readertype: set reader type + + SR: Smartreader+ (default) + Infinity: Infinity USB + TripleP1: Smargo Triple Reader port 1 + TripleP2: Smargo Triple Reader port 2 + TripleP3: Smargo Triple Reader port 3 + + bus:device: bus name and device name of the Smartreader+ or + Infinity USB (get the names with lsusb 'Bus' + and 'Device') + + serialnum: serial number of reader of the Smartreader+ or + Infinity USB + + device: device name + + device:slot: device name and slot number sc8in1 [1-8] + (only one SC8in1 reader supported) + + ip|hostname: IP address or host name + + port: TCP/IP port + + lport: remapping to local TCP/IP port + + gboxpport: UDP port for remote gbox peer + + PCSC: number of PCSC reader, starting with 0 + + 0|1: for Coolstream HD-1 STB only: select reader 0 or + reader 1 + + constantcw: constant CW file name + + constant CW file format: + + · standard format + + CAID:Provider ID:Service ID:PMT ID:ECM PID::key (16 Bytes seperated by spaces) + + example: 1234:123456:1234:2345:3456::00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + + · extended OScam format + + CAID:Provider ID:Service ID:PMT ID:ECM PID:Video PID:key (16 Bytes seperated by spaces) + + example: 1234:123456:1234:2345:3456:7890:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + + detect = [!]CD|[!]DSR|[!]CTS|[!]RING|[!]NONE|[!]gpio[1-7] + status detect of card, NONE = no detection, ! = inverse, default:CD + + cardmhz = mhz + set standard SC frequency in units of 10 kHz, for Irdeto SC set to 600 mhz, for Dreambox DM800 / DM8000 set to 2700 + mhz, for Dreambox DM7025 set to 8300 mhz, for older PowerPC Dreambox STBs set to 3150 mhz, refer to OVERCLOCKING, + default:357 + + mhz = frequency + set reader frequency in units of 10 kHz, if mhz > cardmhz you are in overclocking mode. For Smargo readers and Dreambox + internal readers frequency will be set by ATR if autospeed is set to 1, default:357 + + autospeed = 0|1 + 1 = sets mhz according to ATR. Currently only used for smartreader, Smargo and Dreambox internal protocol, other read‐ + ers will be adapted to use this parameter as well. If You wan't to overclock you're card set it to 0, default:1 + + deprecated = 0|1 + First the SC will be initialized in normal mode. If it fails, the SC will be automatically reverted to deprecated mode, + so that the SC speed will not be changed and the communication will remain on normal ATR speed of 9600 baud. + + 1 = use deprecated SC mode only, default:0 + + mode = mode + set card init mode for AzBox internal reader, default:none + + smargopatch = 0|1 + 1 = enable workaround for Smartreader+ reader until native mode works, default:0 + + sc8in1_dtrrts_patch = 0|1 + 1 = enable fix for SC8in1/MCR DTR/RTS kernel bug, default:0 + + use_gpio = 0|1 + 1 = use GPIO to init the reader. This needs to be set on WRT54G router, default:0 + + ins2e06 = payload + add check control for pin payload (4 hex bytes) for NDS Videoguard 2 SCs, valid for physical readers only, default:none + + ins7e = payload + add 26 hex-bytes payload for NDS Videoguard 2 SCs, valid for physical readers only, default:none + + ins7e11 = TA1 byte + set TA1 byte for NDS Videoguard 2 SCs, valid for physical readers only, default:none + + fix07 = 0|1 + 1=enable 0x07 fix for NDS Videoguard 2 SCs, valid for physical readers only, default:1 + + force_irdeto = 0|1 + 1 = force Irdeto SC mode even if RSA key is set for Irdeto tunnled Nagravion SC, default:0 + + nagra_read = 0|1|2 + read Nagravison records (on NCMED SCs only): + + 0 = disabled (default) + 1 = read all records with expired rights + 2 = read records with valid rights only + + rsakey = RSA key + RSA key for Nagravision/Tiger SCs, CAM key data for Irdeto SCs, Conax SCs, default:none + + deskey = DES key + DES key for Viaccess SCs post-processing, default:none + + boxkey = box key + box key for Nagravision SCs / CAM key for Irdeto SCs + + pincode = pincode + pincode for Conax, Cryptoworks and Viaccess SCs, default:none + + fix9993 = 0|1 + 1 = enable fix for 9993 error with CAID 0919 Videoguard SCs, default:0 + + readtiers = 0|1|2 + method to get tiers of NDS Videoguard SCs: + + 0 = disabled (default) + 1 = ins70 method + 2 = ins76 method + + boxid = NDS box ID + NDS receiver box id + + ndsversion = 0|1|12|2 + set NDS Videoguard version + + 0 = autodetection (default) + 1 = NDS Videoguard 1 + 12 = NDS Videoguard 1+ + 2 = NDS Videoguard 2 + + aeskeys = CAID #0@provid:AES key #0 CAID #0[,AES key #1 CAID #0],...[;CAID #1@provid:AES key #0 CAID #1[,AES key #1 CAID + #1],...]... + multiple 16 bytes AES keys for Viaccess SCs (the used postprocessing AES key is specified through the D2 nano of the + ECM) + + special AES keys: + + 00 = do not return any CW, no AES key specified + FF = return CW received from the S, no AES key specified + + example: + + aeskeys = + 0500@012345:000102030405060708090a0b0c0d0e0f;0500@543210:000102030405060708090a0b0c0d0e0f,0,0f0e0d0c0b0a090807060504030201 + + key = DES key + key for newcamd remote reader encryption + + user = name + user for remote reader + + password = password + password for remote reader + + services = [!]services[,[!]]... + reader [de]assignment to service group, default=none + + caid = [&][:][,[&][:target ]]... + define and mapping of CAIDs for reader, default:all CAIDs with mask FFFF + + example: caid = 0100 + caid = 0200&ffee:0300 + caid = 0400&ff00:0500,0600 + caid = 0702,0722 + caid = 0702&ffdf (shortcut for the example above) + + ident = :[,provid]...[;:[,provid]...]... + set CAID and SC specific ident for reader + + example: ident = 0100:123456,234567;0200:345678,456789 + + class = [!]class[,[!]class]... + set SC specific class in hex for reader + + example: class = 01,02,!1b,!2b + + chid = CAID:ChID + set SC specific ChIDs for reader, default:none + + example: chid = 0100:12 + + group = 1..64[,1..64]... + reader assingment to groups, default:none, required + + audisabled = 0|1 + 1 = exclude reader from auto AU, default:0 + + auprovid = provider ID + set provider ID to use the right reader for auto AU + + example: auprovid = 123456 + + disableserverfilter = 0|1 + 1 = ignore caid and provid settings of reader due faulty clients, default:0 + + inactivitytimeout = seconds + inactivity timeout for all TCP based remote readers, -1 = reconnect on network failure for newcamd, even in idle, + default:0 + + reconnecttimeout = seconds + reconnect if missing answers from a remote reader, default:30 + + reconnectdelay = milli-seconds + set maximum TCP connection block delay, default:60000 + + connectoninit = 0|1 + 1 = allow newcamd connections to be established on startup although there isn't a request yet, default:0 + + keepalive = 0|1 + 1 = allow cs378x TCP socket to be always connected, default:0. Always on if cacheex reader type. + + fallback = 0|1 + 1 = define reader as fallback, standard and fallback reader must have the same group, default:0 + + fallback_percaid = [:[,ident]]...[;[:[,ident]]...].... + use reader as fallback for defined CAIDs only, two-digit wildcard CAIDs are possible, fallback_percaid overrules fall‐ + back, default:none + + example: fallback_percaid = 1234:234567;89;10:345678 + + emmcache = usecache,rewrite,logging + set EMM cache of local reader: + + usecache = 0|1||2 + + 0 = EMM caching disabeld (default) + 1 = enable EMM caching and save EMMs to file after + stopping OSCam + 2 = enable EMM caching, don't save EMMs to file + after stopping OSCam + + rewrite = determines how often one and the same EMM is + written, default:0 + + logging = EMM logging mask: + + 0 = EMM logging disabled (default) + 1 = logging EMM errors + 2 = logging written EMMs + 4 = logging skipped EMMs + 8 = logging blocked EMMs + 16 = logging disabled AU + + example: emmcache = 1,3,2 + + cacheex = 0|1|2|3 + set cache exchange mode + + 0: disable cache exchange mode (default) + 1: enable cache exchange pull mode + 2: enable cache exchange push mode for camd 3.5x / 3.57x and CCcam + protocol + 3: enable reverse cache exchange push mode for camd 3.5x / 3.57x + and CCcam protocol + + Identical cache exchange modes must be set on local OSCam server and remote OSCam user asccount. + + Please consider memory consumption. + + cacheex_maxhop = hops + define maximum hops for cache exchange, default=10 + + csp_ecm_filter = [caid][&mask][@provid][$servid],n + cache exchange incoming ECM filter setting (mode 2 only) for Cardservproxy, default:none + + cacheex_drop_csp = 0|1 + 1 = drop incoming Cardservproxy cache (mode 2 only), detection is zero ecmd5, default:0 + + cacheex_allow_request = 0|1 + 1 = allow incoming ECM request (mode 2), default:1 + + cacheex_allow_filter = 0|1 + 1 = allow cache exchange filter (for cache exchange mode 2 only), default:1 + + cacheex_block_fakecws = 0|1 + 1 = enable fake DCWs blocking (for cache exchange mode 2 only), get fake DCWs form oscam.fakecws, default:0 + + ecmwhitelist = [CAID[@provid]:]length[,length]...[;[CAID[@provid]:]length[,length]...]... + set valid ECM length per CAID and provid in hex, default:none,provid=000000 + + example: ecmwhitelist = 10,20,0a,0b + ecmwhitelist = 0100:10,20;0200@123456:0a,4b + + In normal operation mode this parameter is not required. + + ecmheaderwhitelist = [CAID[@provid]:]header[,header]...[;[CAID[@provid]:]header[,header]...]... + set vaild ECM header per CAID and provid in hex, default:none,provid=000000 + + ratelimitecm = count + number of different SIDs in ECMs allowed for an interval, default:0 + + ecmnotfoundlimit = count + number of ECMs with "not found" answer until the reader will be restarted, 0 = no limit, default:0 + + resetcycle = count + number of ECMs until SC reset is performed, 0 = disabled, valid for physical readers only, default:0 + + ratelimitseconds = seconds + interval for rate limit, default:0 + + ecmunique = 0|1 + 1 = enable check for matching ECM hash in ratelimit slot , default:0 + + srvidholdseconds = seconds + time to keep service ID in ratelimit slot, during this time checkeding for ecmunique is disbaled, default:0 + + cooldown = delay,duration + define cooldown: + + delay: delay in seconds for which the reader is allowed to do + more ECM requests than defined by ecmratelimit, + default: none + + duration: duration in seconds the reader needs to cooldown, + default:none + + ratelimitecm and ratelimitseconds are required + + maxparallel = count + maximum number of parallel active services allowed for this reader, + 0 = unlimited (default). Useful for limiting load on readers with + slot restrictions. When the limit is reached, the reader is + temporarily skipped and other readers are tried. A service slot + expires when no ECM is received within the measured ECM interval + plus the paralleltimeout buffer. + + example: maxparallel = 2 + + paralleltimeout = milliseconds + timeout buffer in milliseconds added to the measured ECM interval + to determine when a service slot expires, default: 1000 + + example: paralleltimeout = 1500 + + parallelfactor = factor + multiplier for pending slots used during zapping. Pending + slots allow temporary capacity overrun while switching channels. + Formula: pending_slots = round(maxparallel * parallelfactor) + Supports decimal values (e.g. 1.5 or 2.0), default: 1.5 + + When zapping, new services go to pending slots if active slots + are full. Pending are promoted to active when slots free up, + or dropped when active services prove they're still running. + + Set to 0 to disable pending slots (strict limit, may cause + black screen when zapping with single reader). + + example: parallelfactor = 1.5 (with maxparallel=2: 3 pending slots) + + blocknano = nano[,nano]...|all + list of EMM-nanos to block (in hex w/o 0x) or all EMM-nanos, valid for physical readers only, default:none + + example: blocknano = 45,93,7a,ff + blocknano = all + + blockemm-u = 0|1 + 1 = block unique EMMs, default:0 + + blockemm-s = 0|1 + 1 = block shared EMMs, default:0 + + blockemm-g = 0|1 + 1 = block global EMMs, default:0 + + blockemm-unknown = 0|1 + 1 = block unknown types of EMMs, default:0 + + blockemm-bylen = [length range,length range]... + block all types of EMMs by length, default:none + + example: blockemm-bylen = 1-10,11- + + read_old_classes = 0|1 (Viaccess SCs only) + 0 = read only active entitlements 1 = read all entitlements (default) + + saveemm-u = 0|1 + 1 = save unique EMMs to log file, default:0 + + saveemm-s = 0|1 + 1 = save shared EMMs to log file, default:0 + + saveemm-g = 0|1 + 1= save global EMMs to log file, default:0 + + saveemm-unknown = 0|1 + 1 = save unknown types of EMMs to log file, default:0 + + savenano = nano[,nano]....|all (obsolete) + list of EMM-nanos to save (in hex w/o 0x) or all EMM-nanos, only valid for physical readers, default:none + + example: savenano = 45,93,7a,ff + savenano = all + + readnano = [path]filename + write file (usually a copy of a file saved by savenano) to your smartcard, if no path is specified, the specified file + is searched for in the configuration directory, only valid for physical readers, default:none + + example: readnano = write.emm + readnano = /var/oscam/write.emm + + dropbadcws = 0|1 + 1 = reject bad CWs, send "not found" instead of bad CWs, default:0 + + disablecrccws = 0|1 + 1 = disable CRC for CW, default: 0 + + In normal operation mode this parameter is not required. Parameter is incompatible with DVB standard. + + ident = [:[,]...][;[:[,]...]]... + use this reader as local in loadbalancer's reader selection, default:none + + lb_whitelist_services = ,... + reader assignement to service group for channels which may never be blocked by the loadbalancer to the reader , + default=none + + lb_weight = weight + the higher the value the higher the probability for reader selection in load balacing mode, default:100 + + It's an divider for the average responstime. + + lb_force_fallback = 0|1 + 1 = set the reader always as fallaback for load balacing without considering the reader's statistics, default:0 + + cccversion =
.. + set CCcam version, default:none + + example: cccversion = 1.2.34 + + cccmaxhops = hops + set CCcam maximum SC distance hops, default:10 + + -1 = disabled + 0 = remote local SCs only + 1 = remote local SCs and + 1 hop + 2 = remote local SCs and + 2 hops + and so on + + After reading this SC hop will be incremented by one. + + ccchop = hop + set hop for non CCCam readers, default:0 + + cccreshare = hop + set reader's CCcam reshare hop, default:0 + + -1 = reshare value off cccam in global config + 0 = resharing for direct peer only + x = resharing for direct peer and share level x + + cccwantemu = 0|1 + 1 = request to provide emu from CCCam server, too, default:0 + + ccckeepalive = 0|1 + 1 = send keepalive messages to keep connection to remote CCCam server up, default:0 + + cccreconnect = timeout + reconnect again after ECM request timeout in milli-seconds, default:4000 + + cccmindown = number + filters all readers with hops smaller than number, default:0 + + gbox_reshare = level + gbox reshare level of local cards, default:0 + + gbox_max_distance = distance + maximum distance to receive gbox peer cards, default:2 + + gbox_max_ecm_send = number + maximum of gbox peers ECMs will be send to, default:3 + + use_ssl = 0|1 + 1 = use SSL for ghttp protocol, default:0 + +OVERCLOCKING + · Dreambox and other internal readers + + For Dreambox and other internal readers the highest possible clockrate will be auto detected. The mhz parameter lets + you override the values chosen by OSCam, if it differs from 357 and 358, but usually you will not set any value for + mhz. + + For certain Dreamboxes (especially PPC clones) the default mhz parameter leads to slow ECM times and/or "not found" + ECMs. By setting mhz to values like 200, 300, 400, ... 1600 you can find a value that works for your receiver and your + card. The higher the mhz value, the slower the ECM time (strange enough). + + If you choose the value too low, your card is not recognized (no ATR or "card not supported"). If you choose the value + too high, you get slow ECM times. Our experience is that either no mhz line, or a line mhz = 1000 works best. + + · Phoenix / Smartmouse reader + + Overclocking does not work with Windows and Mac OS X. Set mhz equivalent to the frequency of the reader. OSCam can + not set the frequency of the reader. + + · Smargo Smartreader+ + + Use protocol = smargo for the FDDI kernel drivers (no libusb needed) or (not recommended) use protocol = smartreader + for OSCam's driver implementation based on libusb. + + Set the reader frequency with the native Smargo Smartreader+ tool (srp_tools). If not setting mhz and cardmhz, OSCam + tries to set the baudrate automatically, according to the maximum speed indicated by ATR. Overclocking is possible. + + OSCam tries to set the baudrate automatically. A standard serial port has limited baudrate settings, so SC overclocking + might not work. When using a serial reader the best way for overclocking is connecting it to a FTDI based USB to serial + port adapter. + + If overclocking does not work, verify the effective baudrate in the logfile. If it deviates too much from the requested + baudrate, the SC will not be recognized (no ATR) and the value for mhz should be adjusted again. The higher the baudrate, + the more accurate the effective baudrate can be. + +CACHE EXCHANGE + · pull mode (on request: cache exchange from remote to local OSCam) + + ECM requests will be forwarded to the remote cache exchange partner. If the CW could not be found in the cache of the + remote exchange partner, a not found will be answered. If the CW could not be found in the cache of the remote exchange + partner but a pending ECM request is open, the request will be re-initiated after the wait time defined in cacheexwait‐ + time. + + · push mode (continuous: cache exchange from remote to local OSCam) + + CWs from the remote cache exchange partner will be forwarded to the local cache. Forwarding only works while the camd + camd 3.5x / 3.57x or CCcam protocol connection between the local and remote OSCam has been established. + + · reverse push mode (continuous: cache exchange from local to remote OSCam) + + CWs from the local cache will be forwarded to the remote cache exchange partner. Forwarding only works while the camd + camd 3.5x / 3.57x or CCcam protocol connection between the remote and local OSCam has been established. + +EXAMPLES + · serial mouse compatible reader + + [reader] + label = myserialmousereader + detect = cd + protocol = mouse + device = /dev/ttyS1 + group = 1 + caid = 0100 + services = myservice,!thisservice + + · USB mouse compatible reader + + [reader] + label = myusbmousereader + detect = cd + protocol = mouse + device = /dev/ttyUSB0 + aeskey = 0102030405060708090a0b0c0d0e0f10 + group = 2 + caid = 0200 + + · camd 3.78x reader + + [reader] + label = mycamd378xreader + protocol = cs378x + device = 192.168.0.1,1234 + user = user1 + password = password1 + group = 3 + + · newcamd reader + + [reader] + label = mynewcamdreader + protocol = newcamd + key = 0102030405060708091011121314 + device = 192.168.0.2,2345 + user = user2 + password = password2 + group = 4 + + · CCcam reader + + [reader] + label = mycccamreader + protocol = cccam + device = 192.168.0.3,3456 + user = user3 + password = password3 + group = 5 + caid = 0300,0400,0500 + cccversion = 1.2.3 + + · PCSC reader + + [reader] + label = mypcscreader + protocol = pcsc + device = 0 + aeskey = 0102030405060708090a0b0c0d0e0f10 + group = 6 + caid = 0600 + + · Smargo Smartreader+ + + [reader] + label = mysmartreader + protocol = smartreader + device = 001:002 + aeskey = 0102030405060708090a0b0c0d0e0f10 + group = 7 + caid = 0700 + + · internal reader + + [reader] + label = myinternalreader + protocol = internal + device = /dev/sci0 + group = 8 + caid = 0800 + + · sc8in1 reader + + [reader] + label = mysc8in1reader + protocol = sc8in1 + device = /dev/ttyUSB0:1 + group = 9 + caid = 0900 + + · constant CW + + [reader] + label = myconstantcw + protocol = constcw + device = /var/keys/constant.cw + group = 10 + + · gbox reader + + [reader] + label = mygboxreader + protocol = gbox + device = 192.168.0.4,45678,56789 + user = user4 + password = password4 + group = 11 + caid = 1100 + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.services(5), oscam.srvid(5), oscam.srvid2(5), + oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + + + + oscam.server(5) diff --git a/Distribution/doc/txt/oscam.services.txt b/Distribution/doc/txt/oscam.services.txt new file mode 100644 index 0000000..3b07288 --- /dev/null +++ b/Distribution/doc/txt/oscam.services.txt @@ -0,0 +1,37 @@ +oscam.services(5) File Formats Manual oscam.services(5) + + + +NAME + oscam.services - definition of services for OSCam + +SYNOPSIS + service definitions + +DESCRIPTIONS + The [] section + service name section, service name sections are recurring, required, maximum 64 services are allowed + + caid = CAID[,CAID]... + listing of CAIDs in hex + + provid = provider ID[,provider ID]... + listing of provider IDs in hex + + srvid = service ID[,service ID]... + listing of service IDs in hex + +EXAMPLES + [myservice] + CAID=0100,0200,000A + provid=000001,ABCDEF + srvid=0001,0002,000A,000B + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.srvid(5), oscam.srvid2(5), + oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + + + + oscam.services(5) diff --git a/Distribution/doc/txt/oscam.srvid.txt b/Distribution/doc/txt/oscam.srvid.txt new file mode 100644 index 0000000..9f5774d --- /dev/null +++ b/Distribution/doc/txt/oscam.srvid.txt @@ -0,0 +1,34 @@ +oscam.srvid(5) File Formats Manual oscam.srvid(5) + + + +NAME + oscam.srvid - service ID configuration file for OSCam + +SYNOPSIS + service ID mappings + +DESCRIPTIONS + CAID[,CAID]...:service ID|[provider]|[name]|[type]|[description] + + mapping between CAID, service ID, provider, name, type and description of service + +ANNONTATIONS + Please use Unix text file format only. + + You only need the oscam.srvid when using the monitor or the web interface. For saving memory consumption only insert the + service IDs you really need. Some external programs use their own oscam.srvid and do not need the oscam.srvid of OSCam. + +EXAMPLES + 0001,0002,0003:000a|my provider 1|tv name 1|tv|my tv package + 0004,0005,0006:000a|my provider 2|radio name 2|radio|my radio package + 0006:000b|my provider 3|tv name 3| + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid2(5), + oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + + + + oscam.srvid(5) diff --git a/Distribution/doc/txt/oscam.srvid2.txt b/Distribution/doc/txt/oscam.srvid2.txt new file mode 100644 index 0000000..013a40b --- /dev/null +++ b/Distribution/doc/txt/oscam.srvid2.txt @@ -0,0 +1,30 @@ +oscam.srvid2(5) File Formats Manual oscam.srvid2(5) + + + +NAME + oscam.srvid2 - service ID configuration file for OSCam + +SYNOPSIS + service ID mappings + +DESCRIPTIONS + service ID:CAID[:@provider ID[@provider ID]...][,:CAID[:@provider ID[@provider ID]...]][name]|[type]|[descrip‐ + tion]|[provider] + + mapping between service ID, CAID, provider ID, name, type, description and proivder + +ANNONTATIONS + Please use Unix text file format only. + + You only need the oscam.srvid2 when using the monitor or the web interface. For saving memory consumption only insert the + service IDs you really need. Some external programs use their own oscam.srvid2 and do not need the oscam.srvid2 of OSCam. + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), + oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + + + + oscam.srvid2(5) diff --git a/Distribution/doc/txt/oscam.tiers.txt b/Distribution/doc/txt/oscam.tiers.txt new file mode 100644 index 0000000..01179e1 --- /dev/null +++ b/Distribution/doc/txt/oscam.tiers.txt @@ -0,0 +1,31 @@ +oscam.tiers(5) File Formats Manual oscam.tiers(5) + + + +NAME + oscam.tiers - TIER configuration file for OSCam + +SYNOPSIS + TIER mappings + +DESCRIPTIONS + CAID[,CAID]...:TIER ID|description + + mapping between CAID, TIER ID and description of TIER + +ANNONTATIONS + Please use Unix text file format only. + +EXAMPLES + 0001,0002,0003:000a|my TIER 1 + 0004:000b|my TIER 2 + 0005:000b|my TIER 3 + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), + oscam.srvid2(5), oscam.user(5), oscam.whitelist(5) + + + + oscam.tiers(5) diff --git a/Distribution/doc/txt/oscam.txt b/Distribution/doc/txt/oscam.txt new file mode 100644 index 0000000..503c141 --- /dev/null +++ b/Distribution/doc/txt/oscam.txt @@ -0,0 +1,150 @@ +oscam(1) General Commands Manual oscam(1) + + + +NAME + OSCam - SC server + +DESCRIPTIONS + The OSCam software is an open source multi-protocol/multi-platform SC server. + + Please check the compile options for included features in the binary. + + OSCam supports the following protocols: + + · newcamd with cascading/remote server ECM support + + · camd 3.3x TCP + + · camd camd 3.5x / 3.57x UDP with cascading/remote server ECM support, ECM and EMM support with size > 256 bytes + + · camd 3.78x TCP with cascading/remote server ECM support, ECM and EMM support with size > 256 bytes + + · CCcam with cascading/remote server ECM support, ECM and EMM support with size > 256 bytes + + · DVB API with multi tuner and PIP support + + · gbox with cascading/remote server ECM support + + · serial (HSIC, SSSP, BOMBA, DSR 9500) + + · radegast + + OSCam works on the following platforms: + + · Linux (Tuxbox, ARM, MIPS, MIPSel, SH-4, PowerPC, ...) + + · Windows (based on cygwin1.dll) + + · Mac OS X + +OPTIONS + -a|--crash-dump + write oscam.crash on segfault (needs installed GDB and OSCam compiled with debug infos -ggdb) + + -b|--daemon + starts in background, writing oscam.version with starttime and version info in temporary directory + + -B|--pidfile + set PID file, overrides pidfile of oscam.conf, default:none + + -c|--config-dir + read configuration from , default:see CS_CONFDIR in globals.h, while starting OSCam prints warnings on + invalid keywords, comment lines start with # character. + + Autodiscover of the following directories will be done: + + + · /etc/tuxbox/config + + · /etc/tuxbox/config/oscam + + · /config/oscam + + · /usr/keys + + · /var/etc + + · /var/etc/oscam + + · /var/keys + + · /var/oscam + + · /var/tuxbox/config + + + -d|--debug + debug level mask: + + 0 = no debugging (default) + 2 = ATR parsing info, ECM dumps, CW dumps + 4 = traffic from/to the reader + 8 = traffic from/to the clients + 16 = traffic to the reader-device on IFD layer + 32 = traffic to the reader-device on I/O layer + 64 = EMM logging + 128 = DVBAPI logging + 256 = load balancing logging + 512 = cache exchange logging + 1024 = client ECM logging + 2048 = CSP logging + 4096 = CWC logging + 65535 = debug all + + -g|--gcollect + garbage collector debug mode, default:none: + + 1 = immediate free + 2 = check for double frees + + -h|--help + usage + + -I|--syslog-ident + set syslog ident, default:oscam + + -p|--pending-ecm + maximum number of pending ECM packets, default:32, maximum:255 + + -r|--restart + restart level: + + 0 = disabled, restart request sets exit status to 99 + 1 = restart activated, web interface can restart oscam (default) + 2 = like 1, but also restart on segmentation faults + + -S|--show-sensitive + do not filter sensitive info (card serial numbers) in the logs + + -s|--capture-segfaults + capture segmentation faults + + -t|--temp-dir + use for temporary data, default:temporary directory of OS + + -V|--build-info + show OSCam version info + + -w|--wait + time waiting for system time to be set correctly + +SIGNALS + SIGHUP + reinit user db, readers, TIERs, services, clients and anti-cascading, for newcamd connections: after reloading the + ident, please restart newcamd client + + SIGUSR1 + shift debug level to next level (see debug level mask above) + + SIGUSR2 + get reader SC info + +SEE ALSO + list_smargo(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), + oscam.srvid2(5), oscam.tiers(5), oscam.user(5), oscam.whitelist(5) + + + + oscam(1) diff --git a/Distribution/doc/txt/oscam.user.txt b/Distribution/doc/txt/oscam.user.txt new file mode 100644 index 0000000..b1e4dd5 --- /dev/null +++ b/Distribution/doc/txt/oscam.user.txt @@ -0,0 +1,309 @@ +oscam.user(5) File Formats Manual oscam.user(5) + + + +NAME + oscam.user - user configuration file for OSCam + +SYNOPSIS + The user configuration file for OSCam contains user definitions. [account] sections in oscam.user are recurring (more than + one account). + +DESCRIPTIONS + The [account] section + user = name + account name, required + + pwd = password + password for account, required + + description = text + description of user account + + disabled = 0|1 + 1 = account disabled, default:0 + + hostname = hostname + host from which user connection is allowed + + expdate = --|// + expiration date for account, default:none + + example: expdate = 2001-11-21 + expdate = 2002/12/22 + + allowedprotocols = [camd33][,][camd35][,][cs357x][,][cs378x][,][newcamd][,][cccam][,][gbox][,][radegast] + list of all allowed connection protocols, default:all connection protocols + + allowedtimeframe = DAY@HH:MM-HH:MM[,HH:MM-HH:MM][,HH:MM-HH:MM][;DAY@HH:MM-HH:MM[,HH:MM-HH:MM][,HH:MM-HH:MM]] + where DAY=SUN,MON,TUE,WED,THU,FRI,SAT or ALL (for all possible days) + + account enabled from hh:mm to hh:mm for the specified day(s), default:none + + comma (,) to separate times and semicolon(; ) to separate the different days. + You can use ALL@ if you want the same time frames for everyday. + + Example: + allowedtimeframe = ALL@10:00-22:00;MON@00:00-02:00,02:45-04:37;FRI@00:00-10:00,22:00-24:00;SAT@00:00-24:00 + + If you use: DAY@22:00-05:00 this will be turned into DAY@00:00-05:00,22:00-24:00 + + ALL@ is always checked and used, so you can watch TV the whole day on FRIday in this exemple. There is no problem to overlap ALL@ in a day definition, like for SAT@ definition. + + max_connections = count + maximum allowed connections per user when unique level will be adducted, default:1 + + uniq = 0|1|2|4 + unique level: + + 0 = disabled (default) + 1 = only one connection per user is allowed + 2 = set user to fake if source ip is different + (e.g. for newcamd clients with different CAIDs and ports) + 3 = only one connection per user, but only the last login + will survive (old MpCS behavior) + 4 = set user only to fake if source ip is different, + but only the last login will survive + + numusers = quantity + anti-cascading: user per account, 0 = anti-cascading disabled, -1 = global value from oscam.conf, default:-1 + + penalty = 0|1|2 + level of penalty: + + -1 = level of oscam.conf (default) + 0 = only logging + 1 = send fake CWs + 2 = temporary user ban + 3 = send delayed CWs + + fakedelay = 0|1|milli-seconds + set fake delay time individually for user: + + 0 = disable fake delay + -1 = fake delay of oscam.conf (default) + + acosc_max_ecms_per_minute = count + maximum ecms per minute, 0 = unlimited, default:0 + + acosc_max_active_sids = count + maximum active SIDs with anti-cascading over SID, 0 = unlimited, -1 = use global setting, default:0 + + acosc_zap_limit = count + zap limit for anti-cascading over SID, 0 = unlimited, -1 = use global setting, default:0 + + acosc_penalty = 0|1|2|3|4|-1 + level of penalty with anti-cascading over SID count: + + 0 = only logging (default) + 1 = send fake CWs + 2 = temporary user ban + 3 = send delayed CWs + 4 = temporary hidecards to the client + -1 = use global setting + + acosc_penalty_duration = seconds + penalty duration for anti-cascading over SID count, -1 = use global setting, default:0 + + acosc_delay = milli-seconds + delay for anti-cascading over SID count, -1 = use global setting, default:0 + + failban = 0|2|4|8 + mask for IP address based blocking: + + 0 = ignore (default) + 2 = block IP address of a disabled account on connecting + 4 = block IP address of a sleeping account while sleeping comes up + 8 = block duplicate IP address + + lb_nbest_readers = counts + set count of best readers for load balancing, -1 = use global lb_nbest_readers, default:-1 + + lb_nfb_readers = counts + set count of fallback readers for load balancing, -1 = use global lb_nfb_readers, default:1 + + lb_nbest_percaid = CAID1:count1[,CAID2:count2]... + set count of best readers per CAIDs for load balancing, wildcard CAIDs with two-digit CAIDs possible, default:none + + example: lb_nbest_percaid = 0100:4,0200:3,03:2,04:1 + (wildcard CAIDs 03xx and 04xx) + + preferlocalcards = 0|1 + SC decoding behavior:. + + -1 = global value from oscam.conf (default) + 0 = local SCs used like a remote reader + 1 = prefer cache exchange based SCs + 2 = prefer local SCs + + cwc_disable = 0|1 + 1 = disbale CW cycle check, default:0 + + cacheex = 0|1|2|3 + set cache exchange mode + + 0: disable cache exchange mode (default) + 1: enable cache exchange pull mode + 2: enable cache exchange push mode for camd 3.5x / 3.57x and + CCcam protocol + 3: enable reverse cache exchange push mode for camd 3.5x / 3.57x + and CCcam protocol + + Identical cache exchange modes must be set on local OSCam user account and remote OSCam server. + + Please consider memory consumption. + + cacheex_maxhop = hops + define maximum hops for cache exchange, default=10 + + no_wait_time = 0|1 + set wait time behaviour: + + 0: use wait_time set in oscam.conf (default) + 1: do not use wait_time set in oscam.conf + + csp_ecm_filter = [caid][&mask][@provid][$servid],n + cache exchange incoming ECM filter setting (mode 3 only) for Cardservproxy, default:none + + cacheex_drop_csp = 0|1 + 1 = drop incoming Cardservproxy cache (mode 3 only), detection is zero ecmd5, default:0 + + cacheex_allow_request = 0|1 + 1 = allow incoming ECM request (mode 3 only), default:1 + + cacheex_allow_filter = 0|1 + 1= allow cache exchange filter (for cache exchange mode 3 only), default:1 + + cacheex_block_fakecws = 0|1 + 1 = enable fake DCWs blocking (for cache exchange mode 3 only), get fake DCWs form oscam.fakecws, default:0 + + sleep = minutes + time waiting for inactive user, default:none + + sleepsend = 0|255 + 255 = OSCam client only: stopping requests until next zap, 255 = camd 3.x only: stopping requests until restart of camd + 3.x client, default:0 + + suppresscmd08 = 0|1 + 0 = tell camd 3.5x, 3.57x and 3.78x clients not to request again for rejected CAID, service ID and provider ID combina‐ + tion, 1 = disable, default:0 + + keepalive = 0|1 + 0 = disable keepalive between server and client for newcamd or CCcam protocol, default:1 + + umaxidle = seconds + value for user being idle before disconnect, 0 = idle disconnect disabled, -1 use clientmaxidle in global section, + default:-1 + + caid = [&][:][,[&][:]]... + limit and mapping of CAIDs, default:all CAIDs with mask FFFF + + example: caid = 0100 + caid = 0200&ffee:0300 + caid = 0400&ff00:0500,0600 + caid = 0702,0722 + caid = 0702&ffdf (shortcut for the example above) + + au = label of reader[,label of reader]...|1 + AU setting, default:none: + + label of reader = sending EMMs to specified reader + (security issue: clients can see SC data!) + 1 = auto AU is sending EMMs to all readers + (security issue: clients can see SC data!) + + group = 1..64[,1..64]... + user assingment to reader groups, default:none, required + + betatunnel = .:[,.: ]... + Define Betacrypt tunneling. The ServiceID can also be used for wildcarded CAIDs. + + example: betatunnel = 0100.0001:0200,0300.0004:0500 + betatunnel = 0600.FFFF:0700 + + Be carefull using abbreviations. + + + emmreassembly = 0|1||2 + EMM reassembly, should be set for Viaccess and Cryptoworks readers if the client that you are using to send EMMs is + reassembling them instead of just sending them to OSCam for processing. + + 0: disabled + 1: enabled for DVB API + 2: enabled (default) + + services = [!]services[,[!]]... + user [de]assingment to service group, default:none + + ident = :[,,...][;:[,,...]]... + user assingment to SC specific idents, default:none + + class = [!]class[,[!]class]... + user [de]assingment to SC specific classes, default=none + + example: class = 01,02,!03,!04 + + chid = :[,:]... + user assingment to SC specific ChIDs, default:none + + monlevel = 0|1|2|3|4 + monitor level: + + 0 = no access to monitor (default) + 1 = only server and own procs + 2 = all procs, but viewing only + 3 = all procs, reload of oscam.user possible + 4 = complete access + + cccmaxhops = hops + maximum hops limit for CCcam clients, default:10 + + -1 = CCcam disabled for this user + 0 = local SCs only + 1 = local SCs + 1 hop + 2 = local SCs + 2 hops + and so on + + cccreshare = level + reshare level for CCcam clients + + -1 = use reshare level of oscam.conf (default) + 0 = resharing for direct peer only + x = resharing for direct peer and share level x + + cccignorereshare = -1|0|1 + CCcam ignore reshare setting: + + -1 = use ignore reshare level of oscam.conf (default) + 0 = use ignore reshare setting of server + 1 = use ignore reshare setting of reader or user + + cccstealth = -1|1 + CCcam stealth: + + -1 = use CCcam stealth of oscam.conf (default) + 0 = use extended OSCam-CCcam protocol + 1 = behaviour like the original CCcam: no activate partner + detection and extended OSCam-CCcam protocol, prevent + other OSCam to detect the server as OSCam server + +EXAMPLES + [account] + user = username + pwd = password + group = 1 + au = myserialmousereader + services = myservice + betatunnel = 0100.0001:0101,0100.0002:0101 + caid = 0100 + ident = 0100:000000 + uniq = 1 + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.tiers(5), + oscam.srvid(5), oscam.srvid2(5), oscam.whitelist(5) + + + + oscam.user(5) diff --git a/Distribution/doc/txt/oscam.whitelist.txt b/Distribution/doc/txt/oscam.whitelist.txt new file mode 100644 index 0000000..e00fb5d --- /dev/null +++ b/Distribution/doc/txt/oscam.whitelist.txt @@ -0,0 +1,53 @@ +oscam.whitelist(5) File Formats Manual oscam.whitelist(5) + + + +NAME + oscam.whitelist - global ECM length whitelisting configuration file for OSCam + +SYNOPSIS + ECM length whitelisting + +DESCRIPTIONS + w:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]] + ECM length whitelisting + + l:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]] + ECM length whitelisting, does not proceed with any other ECM length + whitelisting when matching, abbreviation for normal ECM length + whitelisting using w parameter + + i:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]] + ignore ECM length + + m:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]] [new CAID][:][new + provider ID] + CAID und provider ID mapping, first matching rules, mapping is + preferred over all other whitelistings + +ANNONTATIONS + Please use Unix text file format only. + +EXAMPLES + w:0100 # whitelisting for CAID 0100 + + i:0200::1234 # ignore CAID 0200 with + # service ID 1234 + + i:::::2345 # ignore CHID 2345 + + m:3456:123456::::: 4567:234567 # mapping + + w: # allow all others (blacklist) + + l:0300 # whitelisting for CAID 0300 not + # proceeding if matching + +SEE ALSO + list_smargo(1), oscam(1), oscam.ac(5), oscam.cacheex(5), oscam.cert(5), oscam.conf(5), oscam.dvbapi(5), oscam.fakecws(5), + oscam.guess(5), oscam.ird(5), oscam.provid(5), oscam.ratelimit(5), oscam.server(5), oscam.services(5), oscam.srvid(5), + oscam.srvid2(5),coscam.user(5) + + + + oscam.whitelist(5) diff --git a/Distribution/monitor/mpcsmon-src-0.6.tar.bz2 b/Distribution/monitor/mpcsmon-src-0.6.tar.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..432b07d129119a9f77563ef8f55960547c5cc596 GIT binary patch literal 40331 zcmZs?Wl*I*(DsSD9N@s=cXv3ryEDk(F!h#@LM0KrsSVVNF)pQgs@l_$|1A|BSJ<_2k&z3IRdTG7kYC>qpN%ww)C#0GULz2B9{#Vhsra z;ivJJL~rN2o#2|!w)2+YqdFMtum1t;GsOS!uzBU{vSYb@{(bf`Q6Ow;TglIm{?u=T zB;c9d@Fr^M_k(64AIMc#3|6KqY zy2g{cSAgO}f8js&6{zR1fjV0_!;2GWma)xNtSz`v#E zTEK6^&>u~`j>O8yx8cXLT#ZBDW!NMb)|JNbYW>P_>oIEEOEBN1xYu1)_{N})%ZbEN zAm7D_`?5XjsM~I9)uXL>(?{oMwpW0lbD`bg+RU%A;6e#*t?RP6uS3AA?b`ExrlX&M zDHeS9HqMk*aa}Oh{qDDIXL#S$x9VHx)%EOuaXz%}))QWlL<%zx2|=v@iOLFrMJ>~+ zz^s7E!Cp`qnh5$-`wcPAJ`aGAPRUC3rDKy+Ijg<|2`CUNv;XhjAs}EvqXB?mgyEb5 z5jM$K-yBn4+rsLkM1G zSpbXXJja(>Og`9pVjhAWLOj+wSb-IRyzmV8FkM;p%Qq2b;oKHoO}-H9fS;>!j>;~e zARw6o0he7xE*%0P3}usq1`9mHBMxOUSc4IrZefh*4nahH4g@bqqwnk&8&f=UO z5uiJ^=9Lf;ZG1UkjG>q3Yd-sSM{Y-ELO)dV?T&$(&3rIBiLU0!!s_|xqt(Qd+vSuO zilVc@7b;ge*n#|tG;eQ$7?wOE*apMe6pEcx;=40w2~pI)NXpp~SKC~CJq;Q%4Gseg zG82I?ibYL2QVp*A)IINR_E8kjb>C5viGMk1NWVYd8oRB*m~tW(gH-+nm4H;&Y5XyP z`VW*pZuE-vW%a7qhwpB;+9S}z^3^cS^fzsw=Gk1&>7<+V8>#W1aF$xgL80HS7+j1& zl~#{)gh7@1lHkN1&r99bzw1B#7G^M&wy-i+BG@by_?-Obp7`u-c29p!B;WinLGb18 zOuPT|1>I+oxeN5A_8En&BZ9XOKDgoVu(e5cx0u-pg9PEgS+drEvS>J1b}3vNlD`p(_|g z@Nls(IG9hv7jt**%>PBkE52G|&5~gN#3f@xWo%?^O2A<6A}~@B7@em!-|gPcSfL5o zW|Bu9AG;_NiwrT9gdnZ{a);zMa|z@%*8$VW1~d|NKeF| zPe_g#u&@+AJ4?EHyE?NZ^tf#I`Mli6aZhc9tS7_qWVS(LHw|%`#tKuFG2tSgU=|55 zM-oJgVI>v{?*C_he(da;%nVn?L%dyJkr#n$i2)*##4t6@We15fipV5zzMcPEO2BFc zyU)Z*c0U}S=eCTK-YM!=Bv|4Xh+0ws)uu;qqeH0Od8YHYdRnN35#f4hB#Z_o#THht z|0y(gxA7{CKfXV0K1h^*{yY7bn%!>LoZM&7KKrMAVf$|Nv%s)WXgcc6z`5Z{Qxf}^ zIAPLZ-D|~YDy72h5n`=(kV9X|76Qn4?zFmfoUIFhW%TVu%YbV>qT#6F`M1A^b9aZ5 z`YqX8BJ}e?^V2&p9HUntYrKGNUMeD(jZ{mMWh?z@gv7Qu8vwnfD~W>??RQfIff0tl z%&*Hj{=q84Py9I8m?9L(X++0}yAqOZ2Kp|LPPoFww<@c9?~b z6!|+^2;$#Y=7HCH0gl?nL49 zvnSXu+AmUc$Ht&Qg|WArU#Ueo#xIOItwD)TaTt}!iqP_2JehE(7N#oh-f)d=dZSvLk{ z?OI3e{3+b&IX?P(q_t-trJ-aTSRxwdOW9N?fj7g zq44Fx8%rkdrGn$92`)a}xk?A(U?0p{vNIH@t$s9LnrI)!eOKpFKo+plo1CZFD1dxX80*p zAX+ra8>)|i)O(-yzYPtW4{*U3$Nyl_?P`yZp_|6sC90?y)ndX%mlXbZ&3)$r*N&N) zZKIF*j8vT`(vWUu2{Vf%ol2u-vny9uZ4Qq9#cpDlE{k+GH_CWN_igle%XRwxz<%a4 zCMhYr=t)_d(&#$K2Y~#WQU%jvFCm`{O`V3BjFaS^o6jnqDkfu^bi50T^sazau z^z#_X>1$&S0tP;bs;rzVJZKh>R#B!apAP>^mQR%FFTjM9MC3Cby{fAvB-+SmGU(gx zYhcFZ_-ApR73(Hr^AvarCMsS`I2aq_0PyW{*DDw|RA26+hL#lK=8GKy`#_pPt*cHG zUH;~yN+|p~lzK007*jf|^YL=ur>BDlKI?gG>Gq4&DkeHcIYqdVxMAYUkndxB+b3}H zzk`)|=qS8}{m@<{MMB+#eqs>=txB8(5c0EP-(%Vu5K26J5i+Vhm{pk=VgCV#ndUi$ z4@U*1hM5@shgmvNTY+;gJQ3{rZ8rqZIv)5M0u85+%%b8@B+Chq`$?{^LWi)=9mD!_ zUSC=nj+_UBqW79!SyVX$B6 zKw&&KlnE?hh#;Di_W}OqDTe=FZ{Pg?j32VLb0jqIp|C3ny3;5?lV#@FByj|N(`|zxi#pd?H|#L?(O4mF7?UcWdd2oR}Jcb8S6Y@qq(x= z@So{MnWNy|!0;dmtXBi@hC+%!uy5uBsetC2A;VW6hGlZ<#`9EsHTXUGtg)*c5Z=G2 z|KLUbRm~Os1fa@E92`I#i8ij=VkbWA8E z>)9-~2|iTtU7#!T3KO2hwmovTo$PdI)4t8~?o?x4A)Y3{@CC*j=@df&mL-j`EKg5P zgwPc#3U@%StY!QvP-1yTIc)MlN)$2?2SXu4Tfy%F%kWYX=_7KsnBQ0(_Yz9qlu5J^ zl4!$v5ZxaEZQ`PQ>Vv++`i>Gsr-Kfh89v1M(b8`5AKP`d!(R?ERRc8psV&_yR8g8t zHwMm=37SR?o5dS&p*7Kg%bA^6bv|#AA3~+O?}B}&YgIyw*G@}QXCaG`PCpZVTXN=& zCpsFyKMO`z5KxlEAZ3I@ zVK_kOnB-?vWx({{4O!JH@eN@y1YKDP0~m3zITf&uu3A`?3Q{;!IW1N>avl#-I7Ye- z0!YWSj#On%8cIN`B14bB!i-sr0Z^4;hY^KIiI>K}6r}>8Ne8O}p_IQb#w8&X_+U|D z1|vzxD%-$Y3e#dnuwv*I$x5cNYPf<3gGEClV}T{1u@DtG<#f@JplZ-JI$)W#24`5b zIJ9b*D3ocVEE4nQLdWVYiBH2iPvzqpcc#_-6z@|iZZ^E7n}&G=jyWxcqKAA$RA;-x zQ#W(hpx{7e$ayb>@PpfFH;V1-2>1H>Zvc9>92OC0dKB`k3qFM$hKfFJ4hl8*j4`Bj z88|g~gj_`hgU}Wv$o?JUaL@6sG?U{O)k9F+L)5`ETi04h=mT&3I2<{p4X3D4Vu!!n z{E|c(`-+aWxB86AGAj(c+F3Y$a|QJb1npoU`Ukq~?ciuL?04*GP>b{HO-O1Qh=a`Z z2OX(iU0W*1_|Oy&b>(OZb$s%PWIiC!sj{U+pv|`2oaid*=!|0^$}{!caR0;WBk+r* zdOBQ{A`$;z<$J%}9Sh_uC&rtPZ&1aiL;L@D5QLA=>PzWAYOhvtJB)FDo}_^8frEeu z-;B+UPgurow;w@VTw9fO9Gd7=-`^ZNjr=%+3Kd^Wi~4DNdj46Reo$a(^9}p{>&8e) ziICmc`jgwv?tWhD0*x_dTJ8mMjcJ!w`Y?r|tR(G(^278r3r^u+hZ6^`F)zgS~a4X>{ zPJCezx+twe;-TH#gC)}C7D`zDsZ61FLvQ};_`gYg^q4d+IjxKguJC%hOVPSN1Plkv zJokJ@cxD~idK}uy7OT~(s;bt0#@X4K{m1%WoBz@_s%^!Pc(ZR_t_E9M|IZg3#=5!$ zY;2rf{Ky=eQqObTF&2lqO^5DeP=;K}+JF2%6XS{gp@(j}UCSkkv%kdtMQiX;{;Z1j zdJ)@&wePP-H@O{aMdCAI<@D1>o?+hbxw$iG#U2G~>7%dGFFkdcry!8MGb8WgE&r(V zdzOz46vMa8g7vedf{P0%ES-|xD(;VMpt9%}?bS|xw-!x@W>Y`k2+Y5_lVK}7-3*c< z)~-53VPZs+-*%PwWsqraWHo8ixGi&UYZLyHPqdcJ z!vZxR)7+9j@KQxPic-weSb%jbx-LBiQE3)=$}<`R`tN}` zm8j2`KW1R>s@fWTV2~&aX+lUha4&>p3aEuL%bP6vg&#bOJ$Iw3Om#F^dE-vNzjqX{ zeE(ol^@y*3au1nBBdnxRL~siZ4VFnJ8A;D+E1 zN~>88Y|?SFj#ILBY-K&(xk-^flxEug%;N&Y_N1@v% z9yuJjC*?XLyKbt!eCSNp&YW4mog@m?jh;UmIt{^fOBj2s zVul$stAm(iWKIdVePvPH(#*z2L$w{b^Nh?X4k!Fe{EqH&w3?$O*T9(!0fsSynNRzS zAJII;-khIZd7KApP2h%0U&Fp>fNr6)HH2kA*l+xo$;CpR3};`jLLA;1^OG#NyMIaI z<^2sh8(PcC3dQIf&r6z6HUpNw1{+t#GZXlVv6XkvGU)?`9&Q95V-*D$>Y!|lTA^G# z8*cW-atsd_b%7sD{@B0-za@p?<1BXA)a|O2$2Q!X?hHjnxZ0d zL~~q^0r>ElnZ`<3qQ1{bK%W-YD70BK-u$_h)U_-O-&*pvL}oppBR_)TU7PIRm_A_q zF(E^5KH^;xOYW4n%EL*CE8~OfwK~dsJhOPEWTB;g%U>Hl9g6myp8Xl9N}m* zdCc!YA~alP$x8lw%uq-3u8&jDa%VY0(2Hepqki?*XyGXDzn8B2Bs>n{bR zP{xbia^`Fm1x14AnJU5s9A3 z%uVY7N)yP1DJ=+m(QG8>KPaO`meSOb?8W%3ta=KRmT=6-HxAfHnmle;XD zm>K}75?lew89#XbiCny~KEGUV_u_Wik7j_4(E2-+Z?bqX7M9YglRVaf#o!P%Ar@IU z8L4vqaV$7q|BuI~**D2dlr{L{H&xEz;|Lv>Z4ZUr8z@W8ZTWFiHZNc>#U z{{juOwb$s$%z5H!L5-Z+IkZ(|Rov~v6^mzFa136Dq1n=H;f{58f+j7%USrK^(iPeB zbQ*|E-p@7-(9SK<=qlv55_O9kXY z*bw@O|BhXU{(|;m=)VXZh{JIRLH=&?EvC*(VL=K_h7Cc^Dl>!FL~z(NE3LRp7FaBu zj4r65p`lf{bBA!pw>_ts5)76z=x@+V7!_WcSO(9+3;4_);K$)Fb016Y2Q+d%$t4jg z(VBmho(&ILpctZ>(NSA&bm4TG$&N6$x@{3@5zf=|Hh_^#H~3*<6F5Z1`l@}QM4(|~V^ z$*lZxZhKxTHk=9?tyq9Qm2&K4mPo5`wj{+Kg+%o|q)K-QLpk9{n|%F%h`PcWN9H?l z&@AlTt!|PK`KZ#vKPPw^c4rD=jU+?r$?s(&z-?>b?ogO_nyp_FT#dMhucG;F`%!)6 z16`wS*u$*DNZ~||dd~rTnuUWq6peJcm}glEnUlJsBDW|e=xDu~iQxM83xx}{NL3ng z;yRXB{E#MBE48rvtpG!#tE3_%XAbm>;boBcT+6_u?R)}+hR4j-*3`&kU0G*+Aa<^w zX+hMgUa&%L9;Y2y7_)1GIwkLHwZ6xM*Dr~_wK*`&bGp!GTVy}(jd)0nV#YHK6qk`S zw@qcI!kH%%NaHyvVTbixiItuNj0Jy~I34oYO?)Hx%c=c4&?xz+bDOD!X-lph4k|AM z7D0&EyN}tMMB1Of_whL>U4be30n=9~b@TR>q2utrBo;~h*2^>ZUp*2!O!C9X`zL|l zu79?XuXRj5@0^sAJ3qNkzxSA`{5P6~aDohbMZ&Xgnq}M}uBDlYkRIvR_Z87Y%ZQ>) zxEhp5Y4i3Zv@U~)xB+wY8m-C1ghD|SE>1Y+e?ZaSd)bm|Sa{}v9eXx0w2Vnh00nUe zWpfFaApGJ_<{|>=Vs&~21Uy7cvVznH5}t`K3gQmayW9na zQSFv_@Jp1|BR&aZ+IIbw}78o^8ZMrbDt3i zk#GZK$YV#;Q3!Z z<=NycVhq|v9yk9`Ob?S%g61+ce$eydD^c^kA)IY@G4Wpdi{~UgjS>18Q?jyxqkhN; z`B>;6$l-W+@3Y18&)?qc>od=&dNiw@_rSg%u2<>=Gdu#EUIX^fE-e3KxRAbSq4Lka z2*l}Imq;8Cmni}ikODeCiZ0OcUX$E1xZ2)jxpffRG031TV&TmdgYx*v)^_gY<|VSh z;h&kXG-4WIkAYN4y6^;w4=f?VA`2j6=}h(D&0ON0Ugs_lkLIa}ZDu(D3U_1P_#v2R z5dCqLBE@n$YZCX1B(Z!y$ky`sEzt2Am!H9huh;R=4(qp5Q&=4WP*WD?arvy}(XFoF zmzUy2R`mpI@X_6WMLJ=hBynMqBc5lx5-H|IGZl2$7!silTzA(F4T~o(yB!m9;^MV^ z(lxqo!*C01((H{>I%KFdw~?U2A9@FbZW3cqmJK|6*`&9U8wJIL*9Zh>a#!NW_+GZu z{FvyvW{x3Xj)i#jX_%FL+~IooOl@;50hFJ`uHDnWU?CaslE)z@eoJg>w^aijQFtKj zJy;|rj4DiqB;k38X(qA(wX(w+zk0vYu3STid62G*D|Z<^w~;$kHMincy4YU&k0v~r zec4~-u5mg%_mK8tjB2y*7zrP5+X`R*>NbgoM(aau*-5jhG0i*gvjVMsHox?*1WV7P zZlIx85hvb>wPdKn8za2VzQVSb7_oKC1C=Im0dZBxdRuzuFTQj_OtiC{!xIF2Oew4v z!GBJIYP)vS8D|qLvj#KW+1~R1dG>m&GK~A>KjQByPF*j^+bODRS4}wC4fHiHeiPYk z)d~ccQ7QT%-6tDO{r#mHk+>`4*qf-bo&DN+xo6125Ru#y9uz<&(|ruqk;JJ3pZzm( zxapXGT->yId*_^+T(lRWtZY3DM*a6}vNfL<=YQHL@0IL8axea5zciMgXLo12BIE`U zEjb7PfsmjeA&Ss65!NUmaS>kmhR)9lLsi*Mx4!Ug(X$qo{2qoxiahpUN=;YiUkD^S zJyGTiU-gzIct5%+C^~+83uexb#H)MH#W$WhhgW+b1bgWJ#DZ zr=HWIdLKxHWF9ZW+6zS6W|5><^%)IyxgoW--mO>A~pTHf}b0hgzH zggoA`QiI}dCe7f|mU{R+)9Ve91C=Z9q;%)PJqU#aRE8i3lVjCl{0Bf;~`v~+pm3YhfjWnj`oG9IV{zJp{z%i)TU%?PyQT#%1 zV&zu%vRkhy48pgcLaU$BH?b1n)>xjh&^)pHLwmhulW$Yi7_X(85?L)4RDX@66G&jO z>gac3;ZQRfgVfg5{cNu)y4s$H9i5$%t|6UiDUP7WM!ZoRwso|g-fq1R`9tqJr^0~I;lnGdm$tl;LtGBrvK@`A?sF>ci>ep&lKDX;C!9lIM1h=5qR->D3 zf{pHAi69OB)Nd5j+P~`V35vUCCIlQZBBx;jUs=-SYRUQUCuLalKGHfMy4MW6Me zg$W0t)E@YCTk=hHR#}~3U*;H6!h6H|C$EO#SWix+m2T2Bh z@_u4SP>VUj|VUQ=qP!-ObA-PYwjFU;u`u#!3ZO>ywXehIE` zhwB_wSu9f6?7rhwjLm!bxo~@WKb*Athv4Dnq6ekH6hFg1-|TcmKBY12oHT5qG1sjN zd{LdogQ~*W=h925Oz#-J zwfAksZm|%TR>#~1Cnq5!+=?;&g1?a5p#@@eScOgnS>;GbJjK!)IHpma6e^1d!a72b zReS9g${kR!!g{FUiG?mhcQ*)uNme71fGk_I3-pqr9a!=m$l^zCg!VymA&d>5Sl+$TsrYjx$1of(&Rbj=+zR?_&yWU)14H(s= z_3+ZnZ@D^;4Ip_2$}BSxtao)%qM|B>g#0n>j&x-1gpe`zG*a?ww*O<$x0DQIx9f18s-G1HpH9s zV^oW8KBV*wrV+6sJZbyZMhASaPeVss_1B?6 z@pntYNt{#81#lLbm!h(3D%EK!X^|WAy0)pSHJWpzdcxKedjzT8OO^Ub?t(b1cOBT< zJEWLgxgxVwnCoEDQ?Xvz!VV>dbvd*IwxszRnta}_Wn^uq6!Q>Erc*h+UvW?PFeLK? zVH07q%cQKjV@C{g*WKxoDt(5O#l1LVHf=sbUNh6R|Hiu=Wf@O+7p)pP_+bFlOhKhwtF0#im6{XT? zkyWckDVW$oGlzu{32F736_bjDMpnk-7fDivg;J3Pwh{xUF;miuE4xur!D979OZL9z z3}Ye1w5t}fu&N2F5vu9JmPOX(NV+1G-Gn`)v%T{9(hjxO6Rwz%5k=*H=hMky@r$+1yH@#{k|LnYA{le2V13V1!y}hciBW^w99mt@Po7)4c=^aP;C;Lv zKVcddcPQ>#tEt4K;W_6m{{2@7&`EJ{O-RqnoXM5Mpjwn; zgWYB=UYoW~F4@Zzk^&k-q)9~)a1byNa7il%0zbj`nsaRT1%U+x%uBv6zkY0T*9HV$ z&fgjPzWb36rGGE{sr~x=_hTC0WXI3#$FsfnDb3Fj<2UEQ^DDjDT8;vgpUIZ8iwyq? z9^1k!{5`{25^zlUoS3z5@ZRk2&`bVk3PFx5MC2=3-ffLOUQ1RU#D)o}JsKX&{Mj*5 z5EG-p;c{IlsJ82&Kr+x`XU#-?EVuNmV5y@%od=n;p5`ff%_rsjMGngRmADiCbx8x- z`O;&nQED*MHx#T}d6>yc8!v{|H-RFOCE%Vr950XYpm2)0dkUnMgbTd~oE><6Nw!~O zV*MNt5<06VGkC2FwT|M`8FvdROr4nucp_}7)zjxkktT?z6NEFx`|$D8@K`P3cTM=6 zEjlJZK%yB6i7uJG6;9FZ&4!ct@KD!CI2lhcTl@_byYXAbTTJ}ODhDzo3n~U61c)Ll zJQ^Y_t_l=aRpk&agS@5J^cO4AKP*;UQ>BV2H4UaE)-YxB?7dz6_{3-9#xtrJ|}P zq5v(1g33NDzAP>yCL=w>j!c@860JZhD+(=NjwGrKEL##4!BkdN6=7|b0Af&%h)ZC= zlCfdX0T)ChfKB2WDv)H%z+_Z$SoI+h={8yn^66+{)j=#MaRyAWyO%!O!Xb3cSL622 zB^~hD`~I+-aL`#y!f;6rkj4S-Cjr%r;R&NC9m$Lb@RbKTFI#`R@T`G1Td&P{@b2f2 zqm%~&CpKOvG( zn1hUHS0qRH)z4K!XlIw_``t(=VQ2VlJMMx<-|wGH!oH!yV|eDM-vpF2c%4$i{H%5{ ziMlOxj%j~;Y@=HGPPN0Kj4eXFagpR2RKBJ%d{B{d1_Y_@n!G6DNUh(0cr8lyDc?R! zUGu&=Jh%DvF!l&6OQ5|Z5gK~sGmg|;f?_4)sqf6;bX6$J;@5r$lfWb*7DD1J?&oukxe(ce5ZpJ^`QK>QKB z!9wq~>W?ri`162=^{?5Z#J?Z$3>mSkPaRKloH|as!03_*^t)o_uK02^)ywsu;T85Q z^>P<*!=``^q@D|;dNSj9c4vb~)^$ij@^7z^KrU88RFS%qyZU({hIm{m0A!LXgmB>j zKQ}u!W*5Fcaoau`RBU0NPvF4=O5|BWYfvO7Alwxt!9-gNjwKmG>hl$9GVv{22bJj& z{yi)J{2%pbR6##rkD7tl(&3ak$+$l!hGPoQvP3Z0eZP7?-q1xKMr>vfqJB|dC-Jo7 zN&i`08VbPnof3>d3`DK^d#7?+3e0pc*X({Q_st$5BwaJRHb75VDj4i=cMh3*JWPY^ z^XCd9Lz(8f6`(WXO{C)=)mGZ!^@xN89E$%B?yt2RY7DF+mwV}QD2>>vBMh+WjZdGt?9Z#=}lw1@6{ks8v@^)-Y<$I{KnOu4ltYE^(z@r z0ro786Mq)1wD#$@Q9FQJ1WRRg7?)BJyih!pm6DZ$YA5S6lmGTo2+)`5j?I&f?gw7kkGA&MJ$xfCBAHituho}&iBzmmaTGN)nFk*RL#|E#%-K%l1 z0fuUL*?yi>Vi~b>ip;h##4-R-Lv3n!$Kc}l2_4`7=*1IMeMrf7!#Tg%2G_3 zaP0$^sQKGA?PQM zt4roSjZ_~8h>`bKnXQ-uq&NJusDR!(r%d>sj%275%w_B_b?e(V?MGDC6z@XF=6=pw z=iy&n^DuwWDeWEEB|SzcBU$!4#z+>1`w)r!Zljp1MeJ~6KI)!wniX7y9{?jTpf~8# z2z3=p))s=}h>voYLeGMMXF?`ubTb7OCGAye)sbJr|5meja1GAxR8&VGONFjalkebT7L( zHS!|`BuZ5ClW^m6`I6AyKAz(H=P-zQ}cPSAPqv0LAU|t;!Vd?54-={H@ zhec#bf&>0S1FgU2o2Z%u6Ghg5ixBeHa`kTQ0Kc#UI-w%1L4a_5nq&^UWF} zAG;|_h8o)`vV;g3Z7m8|#);kst4;tH|Z$sdiDbP;v7rL(}(2z5Chgl>c2pQe?zoEi!a0GH^{IaZRGS zPQkPt1zIK!#H?bvGdNN|0`g*%Y70uxrEfTNZyQ*Sv#JKiYaPuQ z8Kx`F>gaz6u^!;J*tE^x=M{{@-Fb2xuDRa7u2qej*{<}yw>g_t8^m7={J}^2>C~EQ zJ^TH)!`iXOQ|5$c@e0Tu8tQ5pR#;iYT~$4nwv74sO4z3XU#1rst1O-WgfM!e*fbU@ z6ippl#bW;4d!gze|C#KmGTtYBVsA!|aa+BCkHQxo!k!idw;D18o=!HYdfbV$*~w9Z zk(10WqpvMi5fZY>xCdPoy|~ zzrBXK(PcPG8WKbw_fOLbc4|rCJv=mQ@xyWo^+*+1JP2xaI7t*uc|0^UHAZ#}GHH2O zC`|xJB|f{^e_Vs&+CHFe79l8Q7!i;!%o%8Ee6F{-gp(ZSZr#k9E5DWRcRIpW3S zbdh8wIl9rHoU9UZy4aX9T3J;(+L9DxMH5UhRPhCD_6XH-_SOh$KUXyT91v7e30ZLo zX|!ohC~Y`7opCH>RfUa>H8~D>l4yxFQVF$fnT@QhFgV&e%Larms|!U(7fcr#UMxi^ zMhOPd$^ulI#SrijBR~K!2n0hb9buhX$?m9bU7Ir8R2MEEO7}j1$7Z1mU>zm zJ0qf)xIF7Jv;rAG90OuOcyU3rO<0@-VH8DLgA4#SZ1r0zlmEOoR}9d{s-cQnhqw5rat-q0^OR${|R}J z8VsyM4=6FG0TBW_e3x)1j@+-O_Um_6j#5J@pKiM@Kfb=pnHN=$XldnL-!Ep-lg2sN zV^Crm%b35%TQFkfXC5(Hi%K{0fcr#i+SDd^a_oO!uYT`LOh-hIHC%HEAp%h1^88J$ z@$aYT?f{t=vJ2T`?*bg0=hL6CpI}rubAcBcjG~L2LUB?zs zcN4*nD(u;}Np72L11pu^832Plj0D1`vTIOip=sJF)6tueq5*rtn-p-1Qz(P|Wkjpm z6n^i<^7qdg8{G7!R(Znymo%E%fDov?3`N==z&sPEiEgx##QuUts6Qd2b-97udZnG6 zK$39XBBsP+xHGW@1lnV<-{6tx+`5)7EwP$8d!pphn%h%MW*pkzF zJZ4mk_fCueuE7Marj`OZHyUIsECM)JynUJ!tcmFI(=*i?f@%&+&ShYo($8Dt$qV5U zb!>JHf=kQa46dG78=T*f!_Tht({K3l4dxHdbb1RjKF)sdqbS?iw)El0Neh^zIMH+knZ)k-PXi(&6Lu(P?KQkRZ*1N#7lACZmir{FMNOBGO|9ywO%%N7@^d1 zuiYI_3#S>YD7PeJIU44BPC>-Ssfg-S%Er*~LrkG;ji3wsW~=F4N%T z8+_MRMD>REASKvbp}gj^6^(RL731aLl=$^Z+g+-xA&Lmd98c~77Z)c9pq#avw03Ez zDF5n^wzl3M&_9%O{5_Y;sDM}{*sdEwr~^@PPRy(POH@2d5-NTl5-^K7LvBa7Rv^|~&0qV2q$70vv?q@F7~9zX|bVY3T=|X<>foesnqFpY$_qzZcC962o>SMFgj#7 zlXPB%Xg=vh17?)%6G9_x>O)hJL&_$NHQplMs(1vtB5>q}jU`f|N-Kr+Jh6|b#VG8N zqNX!bEptDKY6G&+<^*09A({cg|&x#a6xyg|@mu5=$aW=k#fG zwaele9-d;;VhWQ#Va(C?N{T#^DlSt@m`+3+%VZxjZNj&ntw{I4L{h(FQ9@BbI2l6p z4{og)zrx>t_A9h4qWoE_O){Xs+7`j!3_O!9z|o_74thi?%F3IAmXtdP)CI^>+{&8@ z&|^E(WWq2glk#%%Y)$!*Y~swEPZ>?6e^tcAot-c`kp=qgz#E4z$%cbG!ejDaH5Gh5 z0TnIU8>MKKO5~A_Vrp-QYG*W9-Os60<+XHx^l&dR&b4r2-qfnG1(z4rnFic4`8mOt zAJJNWM_5yk(+Ow}YZIPE9}FC$M;BQU zO`POv^EaJpTaSZ~O8)JbhkLU)#N&R;eonnYWJVXWVc{TFEtFD9NuxH<7^of(Wf{^j zIGVs?)RLj)U`U_5BQa13 zgyG7xKEWF;uaZHW;7t`=h!Mf0Y~@>MrckYLDEU)i%KDqnG6cIdTE-@oG_py23Pg*) zt*avUpE{a>KGe15K$M;pxp%Vlhlkr^-Wy(r+Hf;Rtb}YYN6l5-Y;$oZ2bas7=ZuE^ z(zxUoLFXKj8GX6ho_(5A`w^yA@5T7Hu{e(ir=AlE)b_2r?lHX{gQ&?)u9X$k@>5VmpAf$V!SIZvL*&R#-VU%@%aA*z*@ zbFpH|rYYC#$*eaaRyc~;8T?vL#;>#JTDGwcNmFrN3?5h!1A2+~uk>Qk9bc~%C z2OLHQrRE-@543GteQT4!fQ?YPD)K% z&RPJGMC%wF&#i3Z;&10m5p^yrG@aEQiQ?A?g zJn_)UO2;{k5U0b@&ABXuIwv?IgYp=(mZK6vrqUez$ILVrY;rRBlb2p6bUwQaz)hg5 zEL}8Z7Xu$FSv6R%;Nq|s_LATrqvB@vW3%&HQKQ}823S1{6U?XS`Q%&-yd;{U=Q)+! zS1RbhBC${zxii|?wCDP@w(%iO4or(QOK01SnWc%t9aInOmNf=(?kTkx79uWH^TGX8 zG$OjGjhldt3322tma%t0EP~iFOwj>`%KOB>U~@OxeOnY4ElmT<%LTiX!x@^T=rtM& zS69k=^wHZWGC`Fl12N#eMTD=Fj;{Hi_XR#_B{wp)gSAkh-dlQ*6clka8S5`1Md0WN z4#j}Rf+e8xj7wyE)HNmQ?>i>Ym_<|o!M7}Y*8=&}4t8}Ct*759_SD@(GSXWhiaiG( z*WTi0pWCfAV8;DFMRS^CtCM*oFk7L^svWPAUWl8C{D7ufMusvl{0)a>f;_2DWWhF3iHSk~F`C}{p zIbG9lk+Uk*y{fZ@yR^IWd|lW7S*PW960_uS*5O%~$&bfPFufhg;dek0mek4Dootzl zd_a&S1Cu1Lp01&x{+|%*KmJc6@xL9h|Af&_zcap4sG0wsuJrv)$xoQDM! z$5!`^E(%MhVvR2zF2pG8M|p_GH(l5L+>;_F}#v?koD^!N?#f zn*$7FRB74fuR6>MnnFyE7wI$;E)m4m@MU4>)C4qY$_zOqIf3064GXU=2L!)(s6qDb zn9uT4q#7UB`9y9r7Iu0(kY-Vq^#^}7Zswg_|6aB1RrJl2bN}~e{cRu_#$0&qiuoAB zl~+Iv8!DL)3`S{!7wi92^vL+xl}Y@gVwVsJnM@DOe$tCbD~M{>Y$A2a`!#77WgKaV z{y68wMYund2X3RpCSmQD5((zKSH*q zlhPnIMC*t2P05abr&@qG>`Gz*7Yv3op{U-)k;VAi)TnXZ5kL&Fzk*`rn-{x$}d4FI1dz8am0ja!JPGygdj<#q3p6^ zIwUn4MV9|;Yge_~lgOJ!$W1P`W)v$q+Gw=5FC^J?9~G)Hi9veDt8-vCYnfuEI6~XK zc5Hh{Z4xn~)<{GVePI+85{8xXn6G$@GR2p1?)>3aceHEks9&MsYDW+mNR!`me-!u- z^#^^QIQqT6KTwFoDDL7};O+gR2XEV{b(^bxwqauV_WuHyKxe;~9WoSLXy$`;h#6i^ z+cT3I)%!W$Rrc#9)*Z$y{n5DXWMcXnyw19|z~;|(mI@jB8nEUmI41nQnQw}AYi3ie zI}&-RW!eA6>k&>xU3o|>4LeqqT?1b!V?L=9cQCM3L{or-Ju7ypA)t;XM z)~GZy;K|YuU_OWvER$R{^0HaUDf%N7teuJLT?n2Gh-|?+)f(Jw`toB0>8XXwRV57K z6ZH?(bee*aAg;Re305N47g1mXg;9V|`@rN^*k%@D^eszq_847owisO{l=%!-U%}vP zw8_dH0EF5U&K_#e@-@=l#mL~~))r;O*Q50(67nHTrJo5Y#PK+n5E4i#qv1u6dBVCE z_>8E}HAXJC=z(!U6u?$*KX++ANm&{AE{Bjk0C*KpN_!4R6#5qG|03BqH~1MB1kgH4 zN@L%eMEZafbX=cY`i_}Qd1M4gpgsAm_@8=(wOxJ&7}Nz8M@68hxX;>9QU}y*R0zQI zku)x)Qp_?F!V0Pn0^B*Rg1gXg$$Y!b(_$MM2;`gtU$PjsCH%o!k zpv=T`XpRUO3Zukd0s02RDfMekCpX;DON=;J`ua(eSd|1QESQ5@V{)A!FeyZ;iVEpu zz-h$=bxc`A{SF`8@gKKJV|+G3jrx)}hIBB=?+^$CdO=?S&{qry>~T4K$%PkH7Eo!F zm=Fvp3OfFXQ*5Ttbb$b)5~DE$5w|!C-rnYpEo{__^(xGE*h&$SN{uDZT!7$#(7X-_ zQZkf)Yut&m+ewK`5={uQ5Yn4Xrw2eK1Of%ZhzyqE_R1*EQmQ1TSY)Xbk#kpF zl0*w4OBOZqMn)1zBRVz}7pvV2G=u{<<>PR?nLS*KfKpFXlfu;l3C{3r!`gVpq{-UQ8istHD#%iEhYnx zNHFnBd#eLE6iP6qG*Qs|6dkB62~wiWp?RHY{7u-Lg+hxU=w(GBNh+u~nxN2V+RwSU zl|C?#>q6ZMU0U2lTdlbNqf(jX)|VtpLt$%5B|tdt#!i$=PFhcqPh;MG1%Z5(Z2}&c zis^Mjk&D_q;(vrge8+o+7NI41b8F`Y&ft?Nq(}*l?5}f3fk0E>%m&@gGDwBND2B={|#D zTgL>=uh+@+EhF--dd0>CY3E z7Yd-!k-CY*GZAB>V|on$xj8CmEt81pm=IWH3bC@=WfwtyBV-jF2ow+$E!|DUZDs9d z1>RVsEf^a3mbzsXBT2+Ta7G9#&~6U0(eAaZmOoY3n2=Mj>;*po)8I=5@$N=c%6MJ`kRi)4ZR@k@=(?t zH=So}yE|#%vL`6@l0(G?d$sC|QAQe~

VyE~RCy6AH}5fmvpc)o~^}jL5vCkHMp} z!{(zqUALIui^C15%?fNcwjUViW5K+s)Z#bP-A;st1LbuvUrzN?};=>xJ{{00+@BoD5$it z9HYc|r52$Ajee%UO~=WV7-j{kmFdxjcjM>Z*WcgXt=h_?TG6}e`WF=@<95bl4VD}2 zt8_2JH;UzG zNpJmoZlmht(lca3L+~BZ47HjS=v<^)|#XX;~rfNWzi=g*!Bz7lO?$ZEcKAc{f41Oh!$po=yG zseB{07SQ8U5GlgYZy`+C3xVjVD!v1dY%sSON*O>%4hYhyTPqx09IH02)Nm%Z z(jrhG(i9~n1tkPR($D}>L@_jgPz4PDP^An*OA?GtLPb)kLPXMy6%vbEibIkBLvCWqt{D!Vd>T1gBXA-a-awBkbJV#*-Mz_&!S33j9RrnH z;r1GQt7kzy<9z`HqY>=Pl~9ZSm%ggz)G10yJ& zQ%k6}U(u8{xjaK4gg^!XvT-uS!zGlDEJdiojs!bUP@y46e-+4#Ml*CbCKxTktOm+A zLC8BQosFZ(DP82Sx{3FV^CTlxo318b1KSv^oQOF~WYVG_aX3sl(n}drOMyKMFc9o0 z3{i-R6tv=vszrkhl{bsBql<+rE{r%aC0VXdfkXr@q@ZHNX-Wm6C9UP7N1@DI^)Ap3 z0-#hxL_|au2IdCh10}{}<+Y+Lf^10=kQ-K>Viv^6b%C(F3R4MpCGw=?5E`p@6LUte~7_XBgmRywY@(_ORGxjUWK zH^F@MyJ*0+0^hmDGtDBs`_;qUUEspPK;zVTC%h*MowYv=SyzJ&Q zC2}}6ItW>}bKGNDamwru+4ME~19Xt+>vm>ol08ges^Z*U3srVpFw@NybQY#i2SShVV}K`qA3$+F{}W>p?A?%HAA4vDNi8l2$)3PUCDHh@G+ zR0vWGiEps`OevJ6I!gs&tQfc;o{}2O0)hhfwhw~cgw!jc0!?VlDe0tUNgEBE$*bVwNX*lmZ<%AYl++G!#7<{#OxGrxO{Uc5 zwy%`f$aUUkrXzsJ_e@~)5KpM$03IpeWF$94G?v2A#|4RDv|A%7$~g2tqUi({DyOOI z8kAuuid9tyNL#L|n)ka8Y{f;G$XW_tg2B~eqWn_tynYeBu|v9BIXV?KGjn@}X}0;> zCW>IXIyHrD1YR;Z);_6~G>SHfa;pr3T(j~V=9e7921iDcdKSVXdrG*Z;%pQlPh2Pp zio)ofDyo9@4Cbv2(@ZoVa;3V8JNnDOO2cO>EGgP3~oV2X1G_+oWnNf%SyJ$b#~F48}wT&YykL- z;gliBP%@6GC=75BDo2sCYe)4S#K|<28hQfq-?*P(+>OXN#pmmr=RNuFzdpr<-&x&; zO|VYp&pJE0-!k`=nZ3p49!SDfwqu;d;a@zloHr)gZB8;V(7nF%oi>*kz>9R4-3F~4 z94;v4th(ID!|kzbO|#Lcebg@vyM(@VyY8pp{eRL5Zup)Bz0AeYZPUqy$140UW5nd+ zBQzNwld7X7N%@S7#Yie0Ndz)1i`guXk?!>g2aZjRRmLsY9NwP!2`u3z{@aOCP?6BG!?KFg79z>QS2_b8&iD| zhb%Ne$-1d20H;c=yohq@KN<~|B{RvKo#qi!d`qw2;mKqDk7 z_|$3E_z@y1Lv1&U3Bp3$XfIO=X(U+;Balfc0D#JPTHL!majZd7L2zVv)fpESlqO_k zVt(V$f6(}kwf^u#544Bd0xuKye-HW){m1tsg~2+;;L4H!QaJQ3*bGMLVVuHOZdAPR)2p)eVgAxORag9iRYAWSHidvz3xS6qnnsM zmV%ONR2WQmc`xgffsvBG@5h~$ z78k}GEzu2Gd>Vne+Y{;#^MF?P#+sq%z!S|DKs-sDqR^}MpK9hWM4qE^fdik9Ry44Y0J z@P5oQj})1bM3W>@5QHEB*1fOiS*ZF8?2PtJCWcNdwOPK$D{&ZM^$h~L4`@K`@ zi$|^BudWFTsl#SmAtkf3$y{eFpnKRrfV9#F1bC=6yt%Z$KOKS4iYP(er+*o3lWD6Z zYd(nK!3}bwyCjKG?TC2zD^PIvQA^@n`IqNGJs94ShCH;LLLh^k-1RZ^Hj3lGD`lywfughnSkEd_FU3J%8 zb=O^aWue{eF7p>Ywu#ZhDR%TxpYDG1{mv`&Ey58Jy?;MVZ`aYP5zM^qA@=KX%X#6! z-LMEtLhw#bGDDp=9A2LR3VTnkzgm5F- z5A!si#Bb2u10*H{kS)+1t1LKHXC@;^8_eWU2;o~mDnkNwBV8uFA18|TwlMOaY*0kM zuL;h7Ewt)PXKN!+7)u^F75NiyLwCpWYC80EDkDV= zX4OsE&yC%&d?%h5+t_QaE=k2L2x>}DD#|Y?!~L?2Bd>fYr3#$go+E7Pwzjpk_{RNu z9$T%`sxdZO2$~!3Gv%v9i!wE=V;gy|*ZVSmgOWiQ#q9=3(^B zzi+qu{sZ!A;h;DnQ|$;h;&O@f1+%)R+OaD`1|!f?(#ZtaRnFsiCvoB^Lwob{qcU)Y6Z%iPCiTorF?*iT_u3SC4-@$JbOo^yG(x{-qK@ z*F~>LUi3+Nz)|8JWTQE#eC!W+{~%lfcC}|%*?l5x_1z$S-kA6k+!6bQj7-BRXrRk=>qdHnaSZK}0_>cT5M+JfRk3}Kry zqeB+F5177>SYvDg5Mr2ZGChydn7SU*`;1N2-6Bb>xqA-kt@pJuiy@bP2NCGonchGx zFc9y!6K1+hX&y>};DCE&NR%Wh5Fvr;husa&rHl@k39Az{Jaz#tDVUCf1*5K=y=0*2 z$#rvc=n1n8XFX|_S!tR!4;Fz=z>aBzXQUX_vb9MLW3#ig=Pm~Y?G7n$;*K50o6TA{ zH!qpn204ht8m&Oos4hJh8$o<{fNZ|n__VUOYs2%i{n zn5tn#Fc6thlaJZ&;i!?yItPrdC{Rx&tG6XjKPe>5d9FfuT)#BY&tZVW6e1A}I!68< zUfUAA#~i>tjZg%DA2MzL>o0%rw)3+MfVo z^~GVc4FZUJ$)LakkJ|M*c1a5r$t`GHAk?+;yM|LKQxJb3d}lRzdlYaVJEOJ&THSu9 z9Qimn0vI&ua3x_eItU2hR4_!^5uI(~8H;})mNn~>tkx%PzPSB-PN*2k2e70zxWPEU zWGVr(dGI1~WQSa2n#$JrV?uA7(g1O5mSnm@R^pPZb>oO!N+|gPi$#I#)Y}g6f2cIw z9*Mj^Q}%H{qhR%i)T(Qnoj;fLW!eX3{#CZr)SR|bMnw4+{Ya4rSXwFsxKe1OK(i3= za@rUjJ!LDzPW`7xI4-BK!c7E>`jScmy-k3}xDBIxCXnc0M?Xw{zR?qcZVDbfVumnK z9+ytG4(%LEy9MbV!C%XNX}p612PqTk05*x~dnaKiVnO9PgwagJJy3TTgR>9ThWL=N z`+1ntgM(dzrP_xX9~b>s+wS?ltHSt0xZ9<{q?eAVx+ z-+axxt#| z1?C5KWtW5BE0q@JdkBHi0!7VD11&lyMNdFo4y~r(VEM~Lfh2`g*dSw|_dzE98VtTu zsgh4|0b16rK`hyK9-Yk*L~r9a(-f@mve1y7%rFpuU8{$GOM!e21wY@&ZyV#*?2$I& zAU7yVMJl2yqB)55o9}=$(NUBLi%*=uaSl^?`uiR%IbOhTZ8~gBr#rG=ca`2O%yS(bHSDGBzQzS*uj9}q5%4;SjVC$*?P z!IK<7fZ=(t(^<|!?_V)Ts0=YIxkdEF{-S%pA<9jYz5@J)Dfs$*mxHp)Fp5sh@Hpmx zqJg0NSPm4NChvE(?DIZl4g+3izrj^iRaI40RaI40RaIUyJNhCzcF34x)}u-a%E!=+6y$%=rvK$FSaLRNo%n$`a7-h~}QJ=ccEbfAaKHEP0|o6<=addCgv3zOw^y zqXVp8FP43>pCx^J0m1OoeRYTp8NzW6b-VIX3dzWcQEb!Q4XGwYN1LL28jB*xw6rudG`JayOG`^bLqkh} zfMx^;`G73}A~dwLw6rudG_}Q1;itt`Baw=4ZUcreS9M#;#Hg zZXElWKDC$4Uhw&ahP7T>kwN^n=N>)8ReY13FD|h+%il)pyX%X)IGW_g4y3iG$+5EZa`h3sZ??A1Ieoddy_&hV6yr;U zxrnz;uV>bLe>+R=Qw>Xfc)Ia-meXf}&b#ObQ!9nPJ(^kd^)s(LYTEZX*37LncfB(0 zi16XAw!PKx8=khUfNZkVtKC%2t*!40&7!*2eCsnZv-6nTE{$2OS*U?})Z> z+p;)f9x&=d?XlK@&Z^phm%}pT??=Nn#yYU9a50=tx!yIe;bG>_a0^=!sO@cYmRk>2 z-F4H=^LTAHA5VKU&7x>xD#UTeT?n1wROjgcF|eZp+;lAVXIdjM)b>_Yjmwx@bviG>uX0eXzgvL z=AW7C(XSu1Rx^C%uJ6(3i#!vB!}mLD)>}?$3oP%>viSI{J(s*u-y0NUoHN!iIeA#uL9J4cb%h}g< zRqlgb*InS2Yb4BsV>3n z(zrUeaMd#!&V|-=|L`8b`Zt#e3TD z%2#%L^8R*{EZV*m=(T5C9Kx$aol4sq!L6qH@N$+>+*Yy7T6*?l+MO!UH83k)8s;$l zSg_x0${+lk_Z}RabL2sfZOSUMH_Y?V20I?#=gQOg<*aYFj&y_D4oUMi(cQAWX;;+8 z%9o`XNM74mH?rRNtCPmRxLaE~s`s8xv8=tVs58z<-puQ@+b>qJ;^B`pDg3gH#o602 z?(^L6WQEjmFCxU-I}IE4o6juzs{(s|yz3XaB=e5qSI$H2*J7&Bm%A0SUc9`O%HH;)p`tmunr;~13oTjp zypJhm^Bv8utyy}VktUw{)+fenS!6$$%RH_^;4aqv;jMR7twtT9MW|?sP@X4o8$IIJ$&K=75&rH*%UpKZ&@QtpDw>6zQ-tXeZ+8-)}yTl?Kb`q69bN2)6gF8jrY zHw}EOZEt46h|g(dchtysCu5Rql7{1)`p4(h@dh3X`;!EJ#?VjC1 z;hh@0_YI3?GWl!;z})h-to7o@j5EJ4jB9sT+Ui5u-_I*Gh|AvkM{u{@bJ`;3mozeF1!)lyN35`D9BJ_i=duS*xz&<8cZlx4qR2+o6(XxyV zucG0BES2uXuzM36Q-RD zKcnqGz0db+up2b%#f%$FZ8XN%_UkGM*7fU|BwGPA#GbYeGV<63et zO4DX!8Oum$wix^4$Hv=jw%c{%N9N~zr=|4PYb5_BI~(l%ejN|f^5#=}Y$(~R3d_@6 z=L_&_K3p$+UU7mXngOcI=^!_C)-PDFSS%I`1%lA)uyhOz3=9km4&40eAFW^A`ael* zgDk(ZZR8H_lATlNKNp|U^%Oqdcs@T*<)rlYdY1Obq0{+#*UbD3{kY*yMFv@!R8D1N zQT=(;g#|{lF~bWi%=dlYM;{NQA8mkSU{h1#&kOqL#}Z?WKYYfQLRjDQ3CoO3+imF9djjqQ0AMr$0SSPF!3lyw z)ta+g3y5Q!2F=;cfP+>9skjjUH`0f(>weY~Mr`>M%))mL{(o2fhy9`Ri}FV)mdnbx z;Z*RtszWLfjAL6A@pnRThk4Cced+m!iG){WU+h3_sn-5NtYBXm6Px}LYVVNZ;^CR9 z)Rj|=&9Q4+7PW<)-e&i_UiZ9J?RR%^`}J7HEmyuG_r1fryv^@;z3+PgA%L(D1c7iY z0S+uKC{VbtAzSp*J2N)Kt?z8rh_kG!tX9_o}ltF{r0^4wkK#Ze2dk<`jOjO$|+%yohWhi@IN+ zdD*u!NJ!L7y_n8hT4l=jc7q({%vTzib5Uu2tMDdhO#uyQVR}ip<@Lad(C!V9C&v+4 z4FQrGvuOik+9D#h!yS>}8}0oB(AD^xTA+c`yRo=KR8$h#$iD*BQDB<@R!(f4(=lTS zrBil)INY<(9i38QWihI5W-+IdbP4h!(F3gZiS(3l^pAM`Yv7(j;h4TSeT7L-G)L^F zO#^ek9FAkwIw9LhqBZoYzt5?Ja$k|jw8NAE(^eO_AL7TH950$qU>Y&9g7=T>mKpED<5^D z+YQ4J@viz@99{OIS+K#y94W^VJ4tss7z%9XNV*Ugw5H?0Y!K<3h|PNm^MYAY&=I}DrqgJ z@IsYFOLZf}%X4|j7|xx+BLIL^P%DFAyV^t;z{8ch6;qjN7e$08sPI7#0_s-vXJ0iW zToox`Kq{zgU5TZ8EY|6?+4no0tIcK6ZXCFR#n&=Xc+~Jo)$Uzdru{{-&5Q#=#75FfnKq z*$~-ixyEygZy`bh{iAA&x7P3j7>7E?a4Q0eH|zvy|e!O8=I zK|`8aNmKblWdZshgxD&-Zd5xRh9mInQ&gwzVB8cWDG5lrhAuqPcT;UFF>1Tiod?ap z&-Ph~ZSAT#zV@6{oj#2fR+W}{-Eo7HS=268Eo(eTezhmd%{d70SO_k$V%R}`JN&Z* z4;Hh?-9305>8UnWA=4#Q*Ei-p`O3#r+o zg4w7Zooyr}lkCXkC53BVOf;gU)DeiZvgl$R2HuVFTlXOgX26mx!93q9XDlNiCN% zy`kJflePAsc9_5qL+db8YBwl|Fd%#i(hjd+JpdHEc`8pZ24}b1#Cig}tS19vUA8(w z^B%)<6OinVMH}Pcy6vK;^+*_IQK<_k1EOsX%|#(9oVjPh z1#JXh)O|NK--z@;` zGGPoEow+6YX1PTsR`JXO$OpdqF*ltl7)A$_-Kb0t2pLIQeChJ1Y5dss6zKLk@!MNQ zNI=D3i3mkX05#oVNOb!9!r9e$XY2%r+pYY+c0VfHGJabBHP;Wn2ZgIR{~vAgu+4@J zLqa$ezsnZ~$UFtghYdk#jnv0_ zIwa1=iIt}+z6ghlxG>Fwz(f&XKr0L!s3FuUpB71#E{}c)=(xiZH&b+*CuyUhxV`3t zWUL7wI3N{KW7P^2R2EY>7jr6ZGK!8IToo`X${Q) zuuZPVaF;n{&K{hW{D(Bc>{M(pZ@q|vuUO}AK-X|jEvbmB^E&6Ad+TSGz3;2vj{{N` zTegNxty-0?ds{g?DEGw)Kt1^L+oL6W*c^MFaET;(ElWE*Hi*^*26Lx3~&$NF;!y zCJ|I8=fLbwl*cD2(4K-^Oe(QAHBS4X>LlZuXEkd$%wl0oC0GxRZ%b!@PV1$@-Jl?xXBdK zekaIt@jYTK=4EhW%w@%j2_Y_ucxQmFM5$LI?rfQxE@+;r@Oo>I)GN)<@wXw+@ZWjK zP~QkcoY2EQHpX^%j-B$M$?*$7(MJtqglCWP9gk7<4yd9kK96|*4=Qm#CfXrC7Ev<& zP~rKCI{BXnRB|C?E_;Rr#xQ|_=l9si@8}@5G711ov_D_J{NO}DCO?>;^sqbM;y_VA z_^2hgfk%cz1CXD%e06<(fe{P9^m+sRl%D3E@m35o3Y!`+?~&sQo{K-9Rj#_D86m6A z1Y&&8$a^$4@GT6s)JOSD*hs^g#HG4cCJ8Aa2BZzK?$!0POn~AA6usN%IY3YwKXy}) zj()-NN5q{99!LXCA5Uj@pR5hTXK<&0E-tLs5F0@6G@zVt!}!0e_6DKhv^eEPtC0MD zDHy%mU`DeLcXk}`2Sia7-?#H0Yx{o-{IjcLYPiw*Gq!QFTQTbX;-^ej)EJvPHLCps z!RF~(Re|D=?sUYv z3ISz3P8K*vVxtIY%Jg&-ks2-{_p=dO&0JC)J`anG0z!g-9;OuODOl+tEalYFZH@;+ zJ30-LH%Xh57hK{2EJ`RLGEjxJl)5djrs`@~fZ=jH4r{nB1zv;Tf+;S*(3sG2-oDc( zxF3n=kIY^P1JjFuLZ8{3JbK#V{a4$sX1zJ>+plKaGJEYIg>M`Rr-WeT@FYU2tg4Ff z5H(8S^34|By1e9T92tRBnb5UcqO*5*?z70`40DkC>*meZ+S81Vioxd}kD*Ohu-(QZ z7Mv_GglA?e30-6kxuNHlF`m^^C)?DqA(ej`)JA&CIs{p}TY(DhI0^1BD{?dbfz zEZtY}mBX-m9WzP;fk07#oV}-+))Gtw8{=NTkb?$3qjXc_a12IV9U>r}}5wSbCpV<{6Y{ zl{}D_A^jsA+Z2Q(l1O)Rv&vLSu=ay6_#-z75G0@2`5Q%TwmVxnht*|rK;aER!HJPu zmP^H16dbc{ZwXWcjLfb5;$)=#vT{Cb6G7xc*K)up zAuv-Yx<_GzmmffgRWvkuk7S;Lp<9JboHjUs03L~AUJ{(p3JL+JV(7D_Kd2Qe(AE|N zhE~Aix^!Ob2sBs5aY)4l0HrqKVJ)sXqGM~& z@JFHCpX1|qivreLTa_$n=*I~*XoAJWLo-S`f(O-_?7-DU{_{&h2Qj zlu%F$sj@p9Wr8vpCe(_JL0khR!*?hb6+?ew~T$L9;Y*99dWQ^%rV zb+|f{WwGDjvT~8yRdnTjM;4_i8zDum$OUjEuS-xTHysS(H8$$vs&y#zChXwecVp@J zyT0#$Dg$ks8#7}*OMgRjHwN5DoJGBrWC`ps#)g`4bbG&wM@{r+5Yqe_awT|E zhH8pJnnGyVW!z74f9`MO@PktKJ7Ed$h_z-Y93(iJouJ1{hWFVMgou4OIB7WyB z*S{M=%gtKGsMUBtSOz<@4bjU)$a z7S)~o99{rLwj2dq2HSY1!dx=vPTl-4aq<{1X!@KmphzzZ0$CsFG8wSr&h!nL79t&7 zlJ@dBWF3GHiFCnNqk$UG(W#ruEK;a&8Jnu?v?IL!M~W()!;@w1TB4$tCOXSYvmjFe zHUb&=(m=}DTyFgs{hssZJ?GEyE%9X&1jxcsX!K!~tpnwM)QaC5l8J#(q7PJcyBpJ} ztFy$or6enZSx?rOifgU^EN%9I5fwFH>?nsU0)l{bEXMo}kRA&Dt_Shm8bOE$0d9)t zY&kmI&pmj-)d?;5+-An)dIT7K!$Vmcz60+rL^#!7xY;rNB^8DN2tLT;JKyj!;%YEFmx(1;#psbIQL+%&&=y z3<3#VS!5{+2og|XjH2Kf1;x|EaQIFSfShPtY_5}n^S6ABn;h6kH%U_fC@2M!#HTEe zVCtDk4TLum0+XTE%qx*rTrHsaI<}89Dm4a|PAfC1bm(wd(@NYXG$HUZ1qO6@q9xXE zHJhZdKtVt-F2#P(e&Dl#ymOt?JFBHAKP=v@=^UvkQ=EQub2$e&)5w)~hEG z9$4z%p6%N>Z{f=BT)1XzlUmK4cHeESlex9`wcuPD7*`8%5#wi0F16sqj6$v>((1?K zi{f#+&4trrs; z>|2I(El_h~JlUkj0Nc@6ac_k0qa0{m$Qdde-5f>EYUDyZN6bT+9O)b)s*zGfRZ6;U zZ0l~J z2vk})r*Y$}@H+^3p2UC2%Q0OJ-AN>c1Ym!+nGx{Q;U=4>Q$C_$@-vhI;y}!U%845e z;B&!<5|p(0+NL04Jn0d;xt{CiJawFTHEl9o9EVIy=T041+R2SwmUGSUs!9>1u{Dp( z_Z;aJ$#f>+)wZ%+JJ8?P$6oPVJ_3utn~>4=H$!BgJ0O1YTTU$4&XZ%r=`V>b=h1p? zo;i9AMW&dSsmqAbQ?6k8IMR ztbJXZ!SfsJH$3RfjMh0co|Z;fprW%w<@c{C*_age4dsIPnzWOYJ^bJysDA#mDj_0} z>VQ%|w!gG_QPjMK#7qvSym;5)$F5keR5_)HQW7O|1(OfFJzk{&O&qf03pgF1#44CX za|?8_4omw(mo269aH(X5`_Nc}6zJc)y%v&MrAxi&JO50?zzn1qVBz3o%DpA~baROu zn9-c2rguOm zJ#+wi04YwsCK^*TEd;|-3T^K6cAwm5?R=5XWO>e&dcRj~asISWH=@DAgBqiXz^uPy zh5v{!gP`+P4wH%)oFfQBXbRWuMkeqqP?%_ytU8cHrrYjl;xVKc#!NBEa(@W$ zqzB?ZU+K^rNHh8ZPp*8)eTfvUQ!GLfU>pR6Bok)yxJ0zp<_yt7GRRFNNrf?yNfkP> zCMGFOMiK;bR8D{Z`oQ@0JcWCj^V{BZJg9=eWM4}fU5ok(cBrd^#7IaduDwq}( z;#M_{L55?Sux7?LUAJX6D$>I3Upvn`?9D5@8&kcx8QE1fCa$b32ABGG)4tapjAzKpzc6>)n8Z9v=M?}n~FSBETa^K11~t@+p(XS+eTnYN5> zja#BCnsPZV-R?!YH85p!fil@E6U+?kPTw6C8M)R4@csk|C;|it-$up4+r*ho?LDst zLb@tbP(2>T;a#^xP4XQNiHIMnyY)IgU!(KP!LOrVYmSC-5@WRirD|+}PE^hX%iXRFMijXxD8Uk-=os#|KC)cO4AnX*oZVu_I68 zt``;aRD@-zv}-4bh?)n?MNx&5(@1Hp_!dlhnisU%_)&_T$3r>X82i0L2F8~0T42z~ zjIu`N=TeOuU32D;Qv1ca4akBUjc&t`XVM4YD}$F7LER+&MwEvnU425hKzRV zdJH!?HIdNkQ2IrgMJUeOs*~A0u+nd3Hmc;L#NFL9IdA}R%7FAczmObNR;NfZfq=+x zA+IMi!AJCQ8P&oCdb?VpPy`4P+m?D^3H0ewR<^3R0i~qMOel~y9-!c!NZYLIM=<^A za3uo@0)$lb1fU>SZG+(i_6M_w9YL8aJFsRj03ruZg7%=bfqaO9X%&jUFA}cqAJO;< zA1ehgNeIRhf(Zoazwt_;#+h{l%?kml)pngvj~OhQyc)<wC+ z06PJghwTKT5@fCvLIo2dV*548k|`D%f(UU1cSbNuatl7s!TqBn#k`H2G!+J|{152Q z@Wb1=6KI;vVG<-E069^F8=DNDUl2JxJ0#N_5`rV5)MV^ak&KCk98bJ?pybum_#cYP zJs}FDfJkH#pcRwqHhb+^6XxwG48V+CLg@!?_k15h`%pMD{)nU^0N@SO3c<`7fsDl* z%oK1v<2xh391Xd-;X7lu3X3xr*<{sGRaNbJv#-jj@%{`2)P_Ft;pXl1U=k0zOSF_g z*$)^WqIHDCDX5}segVu-hgWZWLD#r;)iCy`sFxA`KUd_zkTVi~3n-Av{Nysip_ec) zM+GEWH5Ld)U}SNFD~*pv5n;A5keX7u+cl9JqoKfxlZ#_ZkTD^*s|`VIXKT5?Sf4qz z%0Y<}LHmg5Jz@`tB@2h|&R)al2}pN8D<`-CVL?#F7)+)FWzm#BzsNLP|DQ1N`H?lG z+sYrKWmNGhnlT`n(VkwIC8s$WHheUR%*<2l4Zj!JgMBAlntO1K&L2Nj5`8(9O$8uO zQ=EHWCwI@0)F-GqljYV3pQ0d#G+t<(WT{tT1Qqm=FTWB-3+ej$F!m7esV2)i`n}nF zt+Tq&xDtw^T(mnm>jT^D%*zsD=&n%$Nf3h)U=)V_0|SR^#0m>;!>lndEx7`_#Javi z42bezOwlG`kL5ErQpNWm_ClF9*-5X5!ftG|@diOj!5l%Ti7J*gfNC_p@B!kJumspU zC!ig3vF7*=MYeVuLziAqgdik}!U+f+Q9HL!SZ%XSa&@M~5cd~E=`Gq)(07OU2y;5% zD5?S`A*p1DAPc1NW+~X41BrXQg_4yo0pdmbzo#P%GW1-%A1Re1z@e8D#KO=M#IiJu zrnN>zz%PItPH@YpFhKMWxdS9OF&%orGa*M`-F)6F*c!tp!ncP>7=M<6m4W|g2WTdQ z7!U!~0(}5?_+q}#N#pXEoyfsO52%F3DbfX^C{b{x$wDaFr~^*)E^qstpkYx`E<}(7 z7-U)2eC~FTbul<0k6-dIfm9E?S}(k)L5!jq>RlAw^WZv;4`q3>6YzL*4Tob?xY$`0 zl^V3k4!Da8A^wt{d>=}%BNdQbSEI;kCzjN?zaDoZ=zj9F2xnOK>4rL4S6;c@sId#W zvmzJJJHf{yJC!@U!K|Fqdl=ZjO!BS@JOt%dyMAS!bHwv5I-X~$$)tNQ3Oc##0!D7C zAp@&ll3FFE429 zN}H6`m_&eONM_3WGi2JidknyVm_<#xn~VDT&FO>9kM!;C!x{Km6jNrrfWo)xO;LCb z&WA!lF(l!L|3sM2DIiF=!j`admcVYn{Y8y=P`#D=opW%XG{$w&pKNvmYG&n;t(}AF zjDcju`Td8DtaVj-H>}#1I<1+s-}??GOSJ>$Gunq5_b`Qld$5TB+l49-7=TPmxfr7s zYeRRAjiJYkGZ-kzgo64N#9{CbO{40Y{a%E*#~Q@5ivZ__FulroNVW(t-lO3L`z$&@ zpwIGj7{NPC&;v7a41=rJn`Y2g3Z33k6J-Z787HP4#{LxGlyzFzDtW}nAJM{vpE5n+ zMNO%AM5re&RblVfvYizeWJw7Xkn|_btcV0)9KWz>G)o>wizanLW~HC$ z*lov*IM;~SAi?u1qh^ZTHOUy4DaAVAvE;wANY|RywrH5mLo# z9;nEUY*4vt_1Gf|#^`@@HH=_1KkcxFHkG$%TiWApX#z4NVmG}?j7iXomY&VUDv`+| zZw05HIJ(0nYkVTV=I5c!tbWd}ptJ>yK}i%%s2f_#<@XUjiShE+cK&Tuu9sXecP6iB=exSO#2Lv;@JI{q?0ZdW@Edz#>5ogP52q+thN0M&K9-nw1mL zCQ%SY1i(ornEc9Tjvxj=giwN6;1sEvgio-)>^p0;>L`BhwN79MYz7cel+6)LO;oUv zF&QeJ2ni&5kl6B?%_4bE2y7H!oP@xjpza_bxJK4@L8-CjN?2aQbF|lyY0x-k{tCRPj5nJbGr&SuBf|5bC4iF%_5ru1jFAZ+hH4E(k?-?0%RM= zWPw8LQ_$V}3*2PhEOx-GsWrkNTsa3jPY$n)czKRwBtvY3i9_|%>}eT$Xh_lY5B(A^p$r4!#g=KB zI}A0x@|DciNkj|PrWY(@N&I7RNTZ-YzG?eT0SwsA@(rml~+-A(qpf=!hf` zjecNlc7mLDfl3*Oe~4Af1_SSjXGfo&7kJr0o0x0F0@A}cA^dq8JYw5GxKZXA&x~H} zj(urzQQ#!=K75)#RCr_+ier2LP-8~q2^g>f8ZBy|RFJ?#Ng_x}l0-s83`jAl+j#VZ z5@-|mW7-4J2W$l%b6()u5(eMZgRM)#8{udV4qw9nYywD@Q$v_+-XA8hcc^d?>RW;6 zKq&*M|0e+T?Vl?r^nh?qAu;RJQHd2c#eoW7Mk>!Q!-C*soi3Ro%WV2awYl)yDGniy z=>@7PQz_mSxpei0E^uM)+FclQxo0dsf2?L??jeQc#zpL;b>m=g)C|L-=e5mg$N6p~goJ|dfNFZcpbk_E^=vD&n!3ygIuu!%dF%&>di!*q zEYU)2HSaC8h_;~$0MQNN0ul|luz(O03H$)P^7cP=Q8o#C?ZRx=RX779nhaRSg!X*L z>aLuesW}0ifTRoI9yxM%j5%e)A$!we$g@?e@zRR{*wgWszopYpQPQzSs(!FS-2_xo+Qg79{XlJl@iriodi$0>=$z&yv z+4`$KYgn)Cjh?we@EA}+z2-c3z%I(w{pE^BDO`{N* zVqJ3m)Lb{keJ7Ob=Aq|EhCX5Ovk2-iJG}jurc8Zz24Nv&2Z!l+&=9oK9RjWZ9#P`D zfIn{AMCAWAqj*F2a&(oqW%k5~*wDQp){+Jf1kjbF^1Z1GQJE0Oj25AiNZ2?`N-`$0GtFV8b6KrXftd|OqSmm-6H%p& zQw~8D?8lsb*?h&p!I=<$bR2E2k}}pDSn@m!&p(6j}~c(93(e1hU3&~XG>eGaWKJ$$*^N?R>I&5 zxIhNmSw~HgNdPg41uU1K$b*Fs1&VePMAgyoGaO7vk~aE@WY`D6`am*ouC)fi#d|FX z$J1DL9%*DTF(*t3F1}~7M23dR`|)WKH3JP87{>AbyI7F0b*gbXP3zW#0HmiAb8x}t zW)9%sB~aM>&_=_fMB2Aq#EdAgjK^ellbTOgv;{XI*5!gbp4I8dEDr|)!?3QfJGTWd zQZ0Cf09f&_Uy_NqF)VyMXf6r6qu25o9x@PwB0^@HI9ffVPZ4??Jz7ltlMSh)`ZBOT zlLLQyINVH#hyn-;Sm0R>DcAecOLX-}&XVW?VBJs8tD&W}j+2f`tQH<0Xm38+IzzCW zusC9J2Py^NNRb3VZV2ehi8Ab@SY7nFnAmG74!V(Z<0Q!;O zx`Mu-(TVPsK+#D80R%z@VFC2`ojzb7rUK6`-)i$YBlM%JM^Vw>*6ptTK!=eS!=ItE)t=m z03>?AKXMMOo?ffG=C5ZId%tgurE!0Q-OY4uHbI@>7b??#wf91jYSVZa#>mZD)wn?$ z=hlbiecP{T0qIfEw!=`a7nlLS1Qb#p3QHi{Y>5I{h0o}qyaPD>a5N-^3VxTExZ~Ju zZ3Pp#3gDsYg%ssa`t@tOytvuqdH$V40<7VO4%$+{kE*D*~T)u*e3;V|9KyO$lO4p1LE0gh)_9N1>_{aKV<2K_mn7WGB+rRe~R5315V z4icnS-bLAZ@r>F_c&FbWX0tKMYsK>#dDt^k6~CA@hfjN%8tB)&(XPOty1+bji)#vm%I6 zae-AS9@!lsT~Z=9gc+wTmir^!@!yTn17vR|!!*nmGMo>@PMuCRo8JkqiUo z$Q4*7uj2p(YYOi5hPWaQl2Vf(U_)sg-uo6{i4rufK((V^=er`Sl_2oBZjOy!Td`DL z5T*`uFGm^KmS-3&iUGhp$PUyvRBWKZf`med7*q+HErQ`?Gtnxr2w)u!n+pd9i8IkEun1rs zO1g-hd#kScCr(q5T)Abam@!l$12$==g~qzf;u_(iq@qhiC4e;WhE%zUF-Ss!Qu1VF zd-M#H+`1L9*eA1RPN++V_UrS3#?&4?I2=@UOuGTxsNPyAe*!2eurWwuECJcmm$Tnn zCs1WT^YsF33!wBx4cy!u3r}1#mP>Hb%a%hBhEj?P(%tLKE%V*Wl*Zj|A(5}RH~|6P zI_|Ni^A=C7pfzd|9JjASSXRMC=F9>~fu*+{@S+>Y4=8!f-^?tl7p~*3~aPmCL@jVceX0Z*X zVsuP!<-y3-Z9QK~IsP8KVe?MQQvCf78P5>g&8bavoo<263;BwwqNPfXrRK1>#x5o- z8*Vu(FOoYPdHoTIw1pX+6rj#F?fpH4w+1SQR10q0RlRY7ut<(-| zIpIxSVeN5&)2I;ofUm9pMsn5RAw>j?4^Rqu*eV+vr|&E#y?jM!Gegj8@P3_RWZGTJ zlhBY@#L0tzM2haC-dA>=`fX-D#9$0Jov_%504q_+yv?xiiI~_wrAfJ<+6D{-x7RPm z18KAz&>uj56(gSd$WWG8r zf*6JrLt~+H;<;a+mziafS6nRm&S$Uaz{?XIi}48Y!+oSF8!TN~()P);I`8)vC94D+SA=Ny#z$U*^9KnBz5j)vIhg}w#@Bu$xVg@B--7BKWc_L~lXAsaqa z?G}dGhL1EeA>!Dyv^r2s1*WL;BFfMk>9bsA-Jcr{w_CpJA>+{@imh z$kG!ULQ<$7`~1E}7&TJ>LRsMwGWU^EECeU#c!WTL8~wr^2p(vPg0NNsigyXF2EreX z(iCi>DXq1!TMC462c1on%23!Blqx#bDk$nz8UPuPQbJ-FfJg(5IYY+6$fWDe?hPID z@OQJdvsHH-bsb~a5;Ww9VJMP>q7huN0dA>q0X(EZP~yZ9G=K$VC7LE717HYI7033b z6HZOfHtIPHVF7{!U5`3>3Pki5|s!+m{>5A-~zi_?HNplH26+eqtHR zhU;au)RRpKmzZIwi3l+^3^yBL%cFVbt!{+GVktu`^-od#;h_W;0c)u;f6s@E0}TP_ zvxu9p>$$3$=ODxyqmYkoBz)`zk|FD|)=_ZsMowET7+G$@?+4&Ycq(HqWfUB*q_O1u z+$12ukzB;ysnBsxnA@6Z(>b|-!=LT22K5&swE#GgLxk@&`u%RZQ@#8BX@5Zi3_v7N z$dOaa+$Ld!#87#woq`h>w#Y$kK_~RgEJW0iBtd@8I0>|rziY=&KtyklZmxhF4v1vw zgL^>G%LEMuk+$uLH*zY`f~yQyMpaNz**zAc_r#?=L?@T{QFVJTA4Jp%(0>{icmIpI MBAh5lCo>Hx0Bv+OAOHXW literal 0 HcmV?d00001 diff --git a/Distribution/monitor/mpcsmon.sh b/Distribution/monitor/mpcsmon.sh new file mode 100644 index 0000000..8fe1083 --- /dev/null +++ b/Distribution/monitor/mpcsmon.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +CSHOST="localhost" +CSPORT="988" +USR="user" +PWD="passwd" +NETCAT="nc" +DELAY=5 + +get_geo() +{ + eval "`echo "$2" | sed -e \"s/^.*${1} /${1}=/g\" -e 's/;.*$//g'`" +} + +do_init() +{ + clear + sline="`stty -a 2>/dev/null | grep rows 2>/dev/null`" + get_geo rows "$sline" + get_geo columns "$sline" + [ "$rows" -eq 0 ] && rows=25 + [ "$columns" -eq 0 ] && columns=80 + rows=`expr $rows - 1` + export rows columns + + tput init 2>/dev/null + TI_ED="`tput ed 2>/dev/null`" + TI_SC="`tput sc 2>/dev/null`" + TI_RC="`tput rc 2>/dev/null`" + TI_B0="`tput setb 0 2>/dev/null`" + TI_B1="`tput setb 5 2>/dev/null`" + TI_B2="`tput setb 1 2>/dev/null`" + TI_IL="`tput il1 2>/dev/null`" + TI_DL="`tput dl1 1 2>/dev/null`" + TI_EL="`tput el 2>/dev/null`" + export TI_ED TI_B0 TI_B1 TI_B2 TI_IL TI_DL TI_SC TI_RC TI_EL +} + +monitor() +{ + $NETCAT -u $CSHOST $CSPORT | awk -W interactive -F"|" ' + BEGIN{ + line="---------------------------------------------------------------------"; + nuser=0; + tabsize=(ENVIRON["columns"]-length(line))/2; + tab=sprintf("%-*.*s", tabsize, tabsize, ""); + rows=ENVIRON["rows"]; + il=ENVIRON["TI_IL"]; + dl=ENVIRON["TI_DL"]; + sc=ENVIRON["TI_SC"]; + rc=ENVIRON["TI_RC"]; + b0=ENVIRON["TI_B0"]; + b1=ENVIRON["TI_B1"]; + b2=ENVIRON["TI_B2"]; + ed=ENVIRON["TI_ED"]; + el=ENVIRON["TI_EL"]; + csr(0, rows); + printf("\n%s%s\n", b2, ed); + print(tab "Nr User A C Modus Online Sender"); + print(tab line); + csr(5+nuser, rows); + cup(5+nuser, 0); + printf("%s%s", b0, ed); + cup(rows, 0); + } + + function csr(row1, row2) + { + system("tput csr "row1" "row2); + } + + function cup(crow, ccol) + { + system("tput cup "crow" "ccol); + } + + /^\[IB....\]/{ + nuser=0; + } + /^\[I.....\]/{ + if (($2!="c") && ($2!="m")) + next; + printf("%s", sc); + cup(4+nuser, 0); + ot=$12/60; + otm=ot%60; ot/=60; + oth=ot%24; ot/=24; + if (ot<1) + ots=sprintf("%d:%02dh", oth, otm); + else + ots=sprintf("%dt %dh", ot, oth); + + austate=0+$5; + if (austate<0) austate=-austate; + printf("%s%s%s%2d %-12.12s%d %d %-10.10s %8.8s %s\n", b2, el, + tab, $3, $4, austate, $6, $9, ots, $14); + printf("%s", el); + nuser++; + csr(5+nuser, rows); + printf("%s%s", rc, b0); + next; + } + /^\[LOG...\]/{ + printf("%s%s\n", substr($0, 20, 8), substr($0, 35)); + next; + } + { + next; + }' +} + +do_exit() +{ + trap - 1 2 15 + tput csr 0 $rows 2>/dev/null + tput sgr0 2>/dev/null + clear + exit 0 +} + +do_init +trap do_exit 1 2 15 + +[ -n "$1" ] && CSHOST="$1" +[ -n "$2" ] && CSPORT="$2" + +while true +do + ( + while true + do + echo "login $USR $PWD" + sleep 1 + echo "log on" + sleep 1 + echo "status" + sleep $DELAY + done + ) | monitor +done diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0417904 --- /dev/null +++ b/LICENSE @@ -0,0 +1,232 @@ +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS + +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + +7. Additional Terms. +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + +8. Termination. +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + +14. Revised Versions of this License. +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. + + oscam-2.26.01-11942-802-with-Advanced-fake-dcw-detection + Copyright (C) 2026 mardock2009 + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: + + oscam-2.26.01-11942-802-with-Advanced-fake-dcw-detection Copyright (C) 2026 mardock2009 + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . + +The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..21e4a1d --- /dev/null +++ b/Makefile @@ -0,0 +1,962 @@ +SHELL = /bin/sh + +.SUFFIXES: +.SUFFIXES: .o .c +.PHONY: all tests help README.build README.config simple default debug config menuconfig allyesconfig allnoconfig defconfig clean distclean submodules + +VER := $(shell ./config.sh --oscam-version) +GIT_SHA := $(shell ./config.sh --oscam-commit) +BUILD_DATE := $(shell date +"%d.%m.%Y %T") + +uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') + +# This let's us use uname_S tests to detect cygwin +ifneq (,$(findstring CYGWIN,$(uname_S))) + uname_S := Cygwin +endif + +LINKER_VER_OPT:=-Wl,--version + +# Find OSX SDK +ifeq ($(uname_S),Darwin) + # Setting OSX_VER allows you to choose prefered version if you have + # two SDKs installed. For example if you have 10.6 and 10.5 installed + # you can choose 10.5 by using 'make USE_PCSC=1 OSX_VER=10.5' + # './config.sh --detect-osx-sdk-version' returns the newest SDK if + # SDK_VER is not set. + OSX_SDK := $(shell ./config.sh --detect-osx-sdk-version $(OSX_VER)) + LINKER_VER_OPT:=-Wl,-v +endif + +ifeq "$(shell ./config.sh --enabled WITH_SSL)" "Y" + override USE_SSL=1 + override USE_LIBCRYPTO=1 +endif +ifeq "$(shell ./config.sh --enabled WITH_EMU)" "Y" + override USE_LIBCRYPTO=1 +endif +ifdef USE_SSL + override USE_LIBCRYPTO=1 +endif + +CONF_DIR = /usr/local/etc + +LIB_PTHREAD = -lpthread +LIB_DL = -ldl + +ifeq ($(uname_S),FreeBSD) + LIB_DL := +endif + +ifeq "$(shell ./config.sh --enabled MODULE_STREAMRELAY)" "Y" + override USE_LIBDVBCSA=1 + ifeq "$(notdir ${LIBDVBCSA_LIB})" "libdvbcsa.a" + override CFLAGS += -DSTATIC_LIBDVBCSA=1 + else + override CFLAGS += -DSTATIC_LIBDVBCSA=0 + endif +endif + +override STD_LIBS := $(LIB_PTHREAD) $(LIB_DL) +override STD_DEFS := -D'CS_VERSION="$(VER)"' +override STD_DEFS += -D'CS_GIT_COMMIT="$(GIT_SHA)"' +override STD_DEFS += -D'CS_BUILD_DATE="$(BUILD_DATE)"' +override STD_DEFS += -D'CS_CONFDIR="$(CONF_DIR)"' + +CC = $(CROSS_DIR)$(CROSS)gcc +STRIP = $(CROSS_DIR)$(CROSS)strip +UPX = $(shell which upx 2>/dev/null || true) +SSL = $(shell which openssl 2>/dev/null || true) +STAT = $(shell which gnustat 2>/dev/null || which stat 2>/dev/null) +SPLIT = $(shell which gsplit 2>/dev/null || which split 2>/dev/null) +GREP = $(shell which ggrep 2>/dev/null || which grep 2>/dev/null) +GIT = $(shell which git 2>/dev/null || true) + +# Compiler warnings +CC_WARN = -W -Wall -Wshadow -Wredundant-decls -Wstrict-prototypes -Wold-style-definition + +# Compiler optimizations +CCVERSION := $(shell $(CC) --version 2>/dev/null | head -n 1) +ifneq (,$(findstring clang,$(CCVERSION))) + CC_OPTS = -O2 -ggdb -pipe -ffunction-sections -fdata-sections -fomit-frame-pointer +else + CC_OPTS = -O2 -ggdb -pipe -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-schedule-insns +endif + +LDFLAGS = -Wl,--gc-sections + +# Enable sse2 on x86, neon on arm +TARGETHELP := $(shell $(CC) --target-help 2>&1) +ifneq (,$(findstring sse2,$(TARGETHELP))) +override CFLAGS += -mmmx -msse -msse2 -msse3 +else ifneq (,$(findstring neon,$(TARGETHELP))) + ifeq "$(shell ./config.sh --enabled WITH_ARM_NEON)" "Y" + override CFLAGS += -mfpu=neon + endif +endif + +# Enable upx compression +UPX_VER = $(shell ($(UPX) --version 2>/dev/null || echo "n.a.") | head -n 1) +COMP_LEVEL = --best +ifdef USE_COMPRESS + ifeq ($(UPX_VER),n.a.) + override USE_COMPRESS = + UPX_COMMAND_OSCAM = $(SAY) "UPX Disabled due to missing upx binary in PATH!"; + else + override STD_DEFS += -D'USE_COMPRESS="$(USE_COMPRESS)"' -D'COMP_LEVEL="$(COMP_LEVEL)"' -D'COMP_VERSION="$(UPX_VER)"' + UPX_SPLIT_PREFIX = $(OBJDIR)/signing/upx. + UPX_INFO_TOOL = $(shell echo '| UPX = $(UPX)\n') + UPX_INFO = $(shell echo '| Packer : $(UPX_VER) (compression level $(COMP_LEVEL))\n') + UPX_COMMAND_OSCAM = $(UPX) -q $(COMP_LEVEL) $@ | $(GREP) '^[[:space:]]*[[:digit:]]* ->' | xargs | cat | xargs -0 printf 'UPX \t%s'; + endif +endif + +# Enable binary signing +ifeq "$(shell ./config.sh --enabled WITH_SIGNING)" "Y" + SIGN_CERT := $(shell ./config.sh --create-cert ecdsa prime256v1 ca 2>/dev/null || false) + SIGN_CERT = $(shell ./config.sh --cert-file cert || echo "n.a.") + + ifeq ($(SIGN_CERT),n.a.) + override WITH_SIGNING = "N" + SIGN_COMMAND_OSCAM = $(SAY) "SIGN Disabled due to missing of certificate files!"; + else + override USE_SSL=1 + + SIGN_PRIVKEY = $(shell ./config.sh --cert-file privkey) + SIGN_MARKER = $(shell ./config.sh --sign-marker) + SIGN_UPXMARKER = $(shell ./config.sh --upx-marker) + SIGN_PUBKEY = $(OBJDIR)/signing/pkey + SIGN_HASH = $(OBJDIR)/signing/sha256 + SIGN_DIGEST = $(OBJDIR)/signing/digest + SIGN_SUBJECT = $(subst $\',$\'$\"$\'$\"$\',$(shell ./config.sh --cert-info | head -n 1)) + SIGN_SIGALGO = $(shell ./config.sh --cert-info | tail -n 1) + SIGN_VALID = $(shell ./config.sh --cert-info | head -n 4 | tail -n 1) + SIGN_PUBALGO = $(shell ./config.sh --cert-info | head -n 5 | tail -n 1) + SIGN_PUBBIT = $(shell ./config.sh --cert-info | head -n 6 | tail -n 1) + SIGN_VER = ${shell ($(SSL) version 2>/dev/null || echo "n.a.") | head -n 1 | awk -F'(' '{ print $$1 }' | xargs} + SIGN_INFO = $(shell echo '| Signing : $(SIGN_VER)\n| $(SIGN_PUBALGO), $(SIGN_PUBBIT), $(SIGN_SIGALGO),\n| Valid $(SIGN_VALID), $(SIGN_SUBJECT)\n') + SIGN_INFO_TOOL = $(shell echo '| SSL = $(SSL)\n') + override STD_DEFS += -DCERT_ALGO_$(shell ./config.sh --cert-info | head -n 5 | tail -n 1 | awk -F':|-' '{ print toupper($$2) }' | xargs) + SIGN_COMMAND_OSCAM += sha256sum $@ | awk '{ print $$1 }' | tr -d '\n' > $(SIGN_HASH); + SIGN_COMMAND_OSCAM += printf 'SIGN SHA256('; $(STAT) -c %s $(SIGN_HASH) | tr -d '\n'; printf '): '; cat $(SIGN_HASH); printf ' -> '; + SIGN_COMMAND_OSCAM += $(SSL) x509 -pubkey -noout -in $(SIGN_CERT) -out $(SIGN_PUBKEY); + SIGN_COMMAND_OSCAM += $(SSL) dgst -sha256 -sign $(SIGN_PRIVKEY) -out $(SIGN_DIGEST) $(SIGN_HASH); + SIGN_COMMAND_OSCAM += $(SSL) dgst -sha256 -verify $(SIGN_PUBKEY) -signature $(SIGN_DIGEST) $(SIGN_HASH) | tr -d '\n'; + SIGN_COMMAND_OSCAM += [ -f $(UPX_SPLIT_PREFIX)aa ] && cat $(UPX_SPLIT_PREFIX)aa > $@; + SIGN_COMMAND_OSCAM += printf '$(SIGN_MARKER)' | cat - $(SIGN_DIGEST) >> $@; + SIGN_COMMAND_OSCAM += [ -f $(UPX_SPLIT_PREFIX)ab ] && cat $(UPX_SPLIT_PREFIX)ab >> $@; + SIGN_COMMAND_OSCAM += printf ' <- DIGEST('; $(STAT) -c %s $(SIGN_DIGEST) | tr -d '\n'; printf ')\n'; + ifdef USE_COMPRESS + ifneq ($(UPX_VER),n.a.) + UPX_COMMAND_OSCAM += $(SPLIT) --bytes=$$($(GREP) -oba '$(SIGN_UPXMARKER)' $@ | tail -1 | awk -F':' '{ print $$1 }') $@ $(UPX_SPLIT_PREFIX); + UPX_COMMAND_OSCAM += $(SIGN_COMMAND_OSCAM) + endif + endif + endif +endif + +# The linker for powerpc have bug that prevents --gc-sections from working +# Check for the linker version and if it matches disable --gc-sections +# For more information about the bug see: +# http://cygwin.com/ml/binutils/2005-01/msg00103.html +# The LD output is saved into variable and then processed, because if +# the output is piped directly into another command LD creates 4 files +# in your /tmp directory and doesn't delete them. +LINKER_VER := $(shell set -e; VER="`$(CC) $(LINKER_VER_OPT) 2>&1`"; echo $$VER | head -1 | cut -d' ' -f5) + +# dm500 toolchain +ifeq "$(LINKER_VER)" "20040727" + LDFLAGS := +endif +# dm600/7000/7020 toolchain +ifeq "$(LINKER_VER)" "20041121" + LDFLAGS := +endif +# The OS X linker do not support --gc-sections +ifeq ($(uname_S),Darwin) + LDFLAGS := +endif + +# The compiler knows for what target it compiles, so use this information +TARGET := $(shell $(CC) -dumpmachine 2>/dev/null) + +# Process USE_ variables +DEFAULT_STAPI_LIB = -L./stapi -loscam_stapi +DEFAULT_STAPI5_LIB = -L./stapi -loscam_stapi5 +DEFAULT_COOLAPI_LIB = -lnxp -lrt +DEFAULT_COOLAPI2_LIB = -llnxUKAL -llnxcssUsr -llnxscsUsr -llnxnotifyqUsr -llnxplatUsr -lrt +DEFAULT_SU980_LIB = -lentropic -lrt +DEFAULT_AZBOX_LIB = -Lextapi/openxcas -lOpenXCASAPI +DEFAULT_LIBCRYPTO_LIB = -lcrypto +DEFAULT_SSL_LIB = -lssl +DEFAULT_LIBDVBCSA_LIB = -ldvbcsa +DEFAULT_LIBUSB_LIB = -lusb-1.0 + +# Since FreeBSD 8 (released in 2010) they are using their own +# libusb that is API compatible to libusb but with different soname +ifeq ($(uname_S),FreeBSD) + DEFAULT_SSL_FLAGS = -I/usr/include + DEFAULT_LIBUSB_LIB = -lusb + DEFAULT_PCSC_FLAGS = -I/usr/local/include/PCSC + DEFAULT_PCSC_LIB = -L/usr/local/lib -lpcsclite +else ifeq ($(uname_S),Darwin) + DEFAULT_SSL_FLAGS = -I/usr/local/opt/openssl/include + DEFAULT_SSL_LIB = -L/usr/local/opt/openssl/lib -lssl + DEFAULT_LIBCRYPTO_LIB = -L/usr/local/opt/openssl/lib -lcrypto + DEFAULT_LIBDVBCSA_FLAGS = -I/usr/local/opt/libdvbcsa/include + DEFAULT_LIBDVBCSA_LIB = -L/usr/local/opt/libdvbcsa/lib -ldvbcsa + DEFAULT_LIBUSB_FLAGS = -I/usr/local/opt/libusb/include + DEFAULT_LIBUSB_LIB = -L/usr/local/opt/libusb/lib -lusb-1.0 -framework IOKit -framework CoreFoundation -framework Security + DEFAULT_PCSC_FLAGS = -I/usr/local/opt/pcsc-lite/include/PCSC + DEFAULT_PCSC_LIB = -L/usr/local/opt/pcsc-lite/lib -lpcsclite -framework IOKit -framework CoreFoundation -framework PCSC +else + # Get the compiler's last include PATHs. Basicaly it is /usr/include + # but in case of cross compilation it might be something else. + # + # Since using -Iinc_path instructs the compiler to use inc_path + # (without add the toolchain system root) we need to have this hack + # to get the "real" last include path. Why we needs this? + # Well, the PCSC headers are broken and rely on having the directory + # that they are installed it to be in the include PATH. + # + # We can't just use -I/usr/include/PCSC because it won't work in + # case of cross compilation. + TOOLCHAIN_INC_DIR := $(strip $(shell echo | $(CC) -Wp,-v -xc - -fsyntax-only 2>&1 | $(GREP) include$ | tail -n 1)) + DEFAULT_SSL_FLAGS = -I$(TOOLCHAIN_INC_DIR) -I$(TOOLCHAIN_INC_DIR)/../../include -I$(TOOLCHAIN_INC_DIR)/../local/include + DEFAULT_PCSC_FLAGS = -I$(TOOLCHAIN_INC_DIR)/PCSC -I$(TOOLCHAIN_INC_DIR)/../../include/PCSC -I$(TOOLCHAIN_INC_DIR)/../local/include/PCSC + DEFAULT_PCSC_LIB = -lpcsclite +endif + +ifeq ($(uname_S),Cygwin) + DEFAULT_PCSC_LIB += -lwinscard +endif + +# Function to initialize USE related variables +# Usage: $(eval $(call prepare_use_flags,FLAG_NAME,PLUS_TARGET_TEXT)) +define prepare_use_flags +override DEFAULT_$(1)_FLAGS:=$$(strip -DWITH_$(1)=1 $$(DEFAULT_$(1)_FLAGS)) +ifdef USE_$(1) + $(1)_FLAGS:=$$(DEFAULT_$(1)_FLAGS) + $(1)_CFLAGS:=$$($(1)_FLAGS) + $(1)_LDFLAGS:=$$($(1)_FLAGS) + $(1)_LIB:=$$(DEFAULT_$(1)_LIB) + ifneq "$(2)" "" + override PLUS_TARGET:=$$(PLUS_TARGET)-$(2) + endif + override USE_CFLAGS+=$$($(1)_CFLAGS) + override USE_LDFLAGS+=$$($(1)_LDFLAGS) + override USE_LIBS+=$$($(1)_LIB) + override USE_FLAGS+=$$(if $$(USE_$(1)),USE_$(1)) + endif +endef + +# Initialize USE variables +$(eval $(call prepare_use_flags,STAPI,stapi)) +$(eval $(call prepare_use_flags,STAPI5,stapi5)) +$(eval $(call prepare_use_flags,COOLAPI,coolapi)) +$(eval $(call prepare_use_flags,COOLAPI2,coolapi2)) +$(eval $(call prepare_use_flags,SU980,su980)) +$(eval $(call prepare_use_flags,AZBOX,azbox)) +$(eval $(call prepare_use_flags,AMSMC,amsmc)) +$(eval $(call prepare_use_flags,MCA,mca)) +$(eval $(call prepare_use_flags,SSL,ssl)) +$(eval $(call prepare_use_flags,LIBCRYPTO,)) +$(eval $(call prepare_use_flags,LIBUSB,libusb)) +$(eval $(call prepare_use_flags,PCSC,pcsc)) +$(eval $(call prepare_use_flags,LIBDVBCSA,libdvbcsa)) +$(eval $(call prepare_use_flags,COMPRESS,upx)) + +ifdef USE_SSL + SSL_HEADER = $(shell find $(subst -DWITH_SSL=1,,$(subst -I,,$(SSL_FLAGS))) -name opensslv.h -print 2>/dev/null | tail -n 1) + SSL_VER = ${shell ($(GREP) 'OpenSSL [[:digit:]][^ ]*' $(SSL_HEADER) /dev/null 2>/dev/null || echo '"n.a."') | tail -n 1 | awk -F'"' '{ print $$2 }' | xargs} + SSL_INFO = $(shell echo ', $(SSL_VER)') +endif + +# Add PLUS_TARGET and EXTRA_TARGET to TARGET +ifdef NO_PLUS_TARGET + override TARGET := $(TARGET)$(EXTRA_TARGET) +else + override TARGET := $(TARGET)$(PLUS_TARGET)$(EXTRA_TARGET) +endif + +EXTRA_CFLAGS = $(EXTRA_FLAGS) +EXTRA_LDFLAGS = $(EXTRA_FLAGS) + +# Add USE_xxx, EXTRA_xxx and STD_xxx vars +override CC_WARN += $(EXTRA_CC_WARN) +override CC_OPTS += $(EXTRA_CC_OPTS) +override CFLAGS += $(USE_CFLAGS) $(EXTRA_CFLAGS) +override LDFLAGS += $(USE_LDFLAGS) $(EXTRA_LDFLAGS) +override LIBS += $(USE_LIBS) $(EXTRA_LIBS) $(STD_LIBS) + +override STD_DEFS += -D'CS_TARGET="$(TARGET)"' + +# Setup quiet build +Q = +SAY = @true +ifndef V + Q = @ + NP = --no-print-directory + SAY = @echo +endif + +BINDIR := Distribution +override BUILD_DIR := build +OBJDIR := $(BUILD_DIR)/$(TARGET) + +# Include config.mak which contains variables for all enabled modules +# These variables will be used to select only needed files for compilation +-include $(OBJDIR)/config.mak + +OSCAM_BIN := $(BINDIR)/oscam-$(VER)@$(GIT_SHA)-$(subst cygwin,cygwin.exe,$(TARGET)) +TESTS_BIN := tests.bin +LIST_SMARGO_BIN := $(BINDIR)/list_smargo-$(VER)@$(GIT_SHA)-$(subst cygwin,cygwin.exe,$(TARGET)) + +# Build list_smargo-.... only when WITH_LIBUSB build is requested. +ifndef USE_LIBUSB + override LIST_SMARGO_BIN = +endif + +SRC-$(CONFIG_LIB_AES) += cscrypt/aes.c +SRC-$(CONFIG_LIB_BIGNUM) += cscrypt/bn_add.c +SRC-$(CONFIG_LIB_BIGNUM) += cscrypt/bn_asm.c +SRC-$(CONFIG_LIB_BIGNUM) += cscrypt/bn_ctx.c +SRC-$(CONFIG_LIB_BIGNUM) += cscrypt/bn_div.c +SRC-$(CONFIG_LIB_BIGNUM) += cscrypt/bn_exp.c +SRC-$(CONFIG_LIB_BIGNUM) += cscrypt/bn_lib.c +SRC-$(CONFIG_LIB_BIGNUM) += cscrypt/bn_mul.c +SRC-$(CONFIG_LIB_BIGNUM) += cscrypt/bn_print.c +SRC-$(CONFIG_LIB_BIGNUM) += cscrypt/bn_shift.c +SRC-$(CONFIG_LIB_BIGNUM) += cscrypt/bn_sqr.c +SRC-$(CONFIG_LIB_BIGNUM) += cscrypt/bn_word.c +SRC-$(CONFIG_LIB_BIGNUM) += cscrypt/mem.c +SRC-$(CONFIG_LIB_DES) += cscrypt/des.c +SRC-$(CONFIG_LIB_IDEA) += cscrypt/i_cbc.c +SRC-$(CONFIG_LIB_IDEA) += cscrypt/i_ecb.c +SRC-$(CONFIG_LIB_IDEA) += cscrypt/i_skey.c +SRC-y += cscrypt/md5.c +SRC-$(CONFIG_LIB_RC6) += cscrypt/rc6.c +SRC-$(CONFIG_LIB_SHA1) += cscrypt/sha1.c +SRC-$(CONFIG_LIB_MDC2) += cscrypt/mdc2.c +SRC-$(CONFIG_LIB_FAST_AES) += cscrypt/fast_aes.c +SRC-$(CONFIG_LIB_SHA256) += cscrypt/sha256.c + +SRC-$(CONFIG_WITH_CARDREADER) += csctapi/atr.c +SRC-$(CONFIG_WITH_CARDREADER) += csctapi/icc_async.c +SRC-$(CONFIG_WITH_CARDREADER) += csctapi/io_serial.c +SRC-$(CONFIG_WITH_CARDREADER) += csctapi/protocol_t0.c +SRC-$(CONFIG_WITH_CARDREADER) += csctapi/protocol_t1.c +SRC-$(CONFIG_CARDREADER_INTERNAL_AZBOX) += csctapi/ifd_azbox.c +SRC-$(CONFIG_CARDREADER_INTERNAL_COOLAPI) += csctapi/ifd_cool.c +SRC-$(CONFIG_CARDREADER_INTERNAL_COOLAPI2) += csctapi/ifd_cool.c +SRC-$(CONFIG_CARDREADER_DB2COM) += csctapi/ifd_db2com.c +SRC-$(CONFIG_CARDREADER_MP35) += csctapi/ifd_mp35.c +SRC-$(CONFIG_CARDREADER_PCSC) += csctapi/ifd_pcsc.c +SRC-$(CONFIG_CARDREADER_PHOENIX) += csctapi/ifd_phoenix.c +SRC-$(CONFIG_CARDREADER_DRECAS) += csctapi/ifd_drecas.c +SRC-$(CONFIG_CARDREADER_SC8IN1) += csctapi/ifd_sc8in1.c +SRC-$(CONFIG_CARDREADER_INTERNAL_SCI) += csctapi/ifd_sci.c +SRC-$(CONFIG_CARDREADER_SMARGO) += csctapi/ifd_smargo.c +SRC-$(CONFIG_CARDREADER_SMART) += csctapi/ifd_smartreader.c +SRC-$(CONFIG_CARDREADER_STINGER) += csctapi/ifd_stinger.c +SRC-$(CONFIG_CARDREADER_STAPI) += csctapi/ifd_stapi.c +SRC-$(CONFIG_CARDREADER_STAPI5) += csctapi/ifd_stapi.c +SRC-$(CONFIG_CARDREADER_INTERNAL_AMSMC) += csctapi/ifd_amsmc.c + +SRC-$(CONFIG_LIB_MINILZO) += minilzo/minilzo.c + +SRC-$(CONFIG_CS_ANTICASC) += module-anticasc.c +SRC-$(CONFIG_CS_CACHEEX) += module-cacheex.c +SRC-$(CONFIG_MODULE_CAMD33) += module-camd33.c +SRC-$(CONFIG_CS_CACHEEX) += module-camd35-cacheex.c +SRC-$(sort $(CONFIG_MODULE_CAMD35) $(CONFIG_MODULE_CAMD35_TCP)) += module-camd35.c +SRC-$(CONFIG_CS_CACHEEX) += module-cccam-cacheex.c +SRC-$(CONFIG_MODULE_CCCAM) += module-cccam.c +SRC-$(CONFIG_MODULE_CCCSHARE) += module-cccshare.c +SRC-$(CONFIG_MODULE_CONSTCW) += module-constcw.c +SRC-$(CONFIG_WITH_EMU) += module-emulator.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-osemu.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-biss.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-cryptoworks.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-director.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-irdeto.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-nagravision.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-omnicrypt.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-powervu.c +SRC-$(CONFIG_WITH_EMU) += module-emulator-viaccess.c +ifeq "$(CONFIG_WITH_EMU)" "y" +ifeq "$(CONFIG_WITH_SOFTCAM)" "y" +UNAME := $(shell uname -s) +ifneq ($(UNAME),Darwin) +ifndef ANDROID_NDK +ifndef ANDROID_STANDALONE_TOOLCHAIN +TOUCH_SK := $(shell touch SoftCam.Key) +override LDFLAGS += -Wl,--format=binary -Wl,SoftCam.Key -Wl,--format=default +ifneq ($(uname_S),Cygwin) +override LDFLAGS += -Wl,-z,noexecstack +endif +endif +endif +endif +endif +endif +SRC-$(CONFIG_CS_CACHEEX) += module-csp.c +SRC-$(CONFIG_CW_CYCLE_CHECK) += module-cw-cycle-check.c +SRC-$(CONFIG_WITH_AZBOX) += module-dvbapi-azbox.c +SRC-$(CONFIG_WITH_MCA) += module-dvbapi-mca.c +### SRC-$(CONFIG_WITH_COOLAPI) += module-dvbapi-coolapi.c +### experimental reversed API +SRC-$(CONFIG_WITH_COOLAPI) += module-dvbapi-coolapi-legacy.c +SRC-$(CONFIG_WITH_COOLAPI2) += module-dvbapi-coolapi.c +SRC-$(CONFIG_WITH_SU980) += module-dvbapi-coolapi.c +SRC-$(CONFIG_WITH_STAPI) += module-dvbapi-stapi.c +SRC-$(CONFIG_WITH_STAPI5) += module-dvbapi-stapi5.c +SRC-$(CONFIG_HAVE_DVBAPI) += module-dvbapi-chancache.c +SRC-$(CONFIG_HAVE_DVBAPI) += module-dvbapi.c +SRC-$(CONFIG_MODULE_GBOX) += module-gbox-helper.c +SRC-$(CONFIG_MODULE_GBOX) += module-gbox-sms.c +SRC-$(CONFIG_MODULE_GBOX) += module-gbox-remm.c +SRC-$(CONFIG_MODULE_GBOX) += module-gbox-cards.c +SRC-$(CONFIG_MODULE_GBOX) += module-gbox.c +SRC-$(CONFIG_LCDSUPPORT) += module-lcd.c +SRC-$(CONFIG_LEDSUPPORT) += module-led.c +SRC-$(CONFIG_MODULE_MONITOR) += module-monitor.c +SRC-$(CONFIG_MODULE_NEWCAMD) += module-newcamd.c +SRC-$(CONFIG_MODULE_NEWCAMD) += module-newcamd-des.c +SRC-$(CONFIG_MODULE_PANDORA) += module-pandora.c +SRC-$(CONFIG_MODULE_GHTTP) += module-ghttp.c +SRC-$(CONFIG_MODULE_RADEGAST) += module-radegast.c +SRC-$(CONFIG_MODULE_SCAM) += module-scam.c +SRC-$(CONFIG_MODULE_SERIAL) += module-serial.c +SRC-$(CONFIG_MODULE_STREAMRELAY) += module-streamrelay.c +SRC-$(CONFIG_WITH_LB) += module-stat.c +SRC-$(CONFIG_WEBIF) += module-webif-lib.c +SRC-$(CONFIG_WEBIF) += module-webif-tpl.c +SRC-$(CONFIG_WEBIF) += module-webif.c +SRC-$(CONFIG_WEBIF) += webif/pages.c +SRC-$(CONFIG_WEBIF_WIKI) += webif/pages_wiki.c +SRC-$(CONFIG_WITH_CARDREADER) += reader-common.c +SRC-$(CONFIG_READER_BULCRYPT) += reader-bulcrypt.c +SRC-$(CONFIG_READER_CONAX) += reader-conax.c +SRC-$(CONFIG_READER_CRYPTOWORKS) += reader-cryptoworks.c +SRC-$(CONFIG_READER_DGCRYPT) += reader-dgcrypt.c +SRC-$(CONFIG_READER_DRE) += reader-dre.c +SRC-$(CONFIG_READER_DRE) += reader-dre-cas.c +SRC-$(CONFIG_READER_DRE) += reader-dre-common.c +SRC-$(CONFIG_READER_DRE) += reader-dre-st20.c +SRC-$(CONFIG_READER_GRIFFIN) += reader-griffin.c +SRC-$(CONFIG_READER_IRDETO) += reader-irdeto.c +SRC-$(CONFIG_READER_NAGRA_COMMON) += reader-nagra-common.c +SRC-$(CONFIG_READER_NAGRA) += reader-nagra.c +SRC-$(CONFIG_READER_NAGRA_MERLIN) += reader-nagracak7.c +SRC-$(CONFIG_READER_SECA) += reader-seca.c +SRC-$(CONFIG_READER_TONGFANG) += reader-tongfang.c +SRC-$(CONFIG_READER_VIACCESS) += reader-viaccess.c +SRC-$(CONFIG_READER_VIDEOGUARD) += reader-videoguard-common.c +SRC-$(CONFIG_READER_VIDEOGUARD) += reader-videoguard1.c +SRC-$(CONFIG_READER_VIDEOGUARD) += reader-videoguard12.c +SRC-$(CONFIG_READER_VIDEOGUARD) += reader-videoguard2.c +SRC-$(CONFIG_WITH_SIGNING) += oscam-signing.c +SRC-y += oscam-aes.c +SRC-y += oscam-array.c +SRC-y += oscam-hashtable.c +SRC-y += oscam-cache.c +SRC-y += oscam-chk.c +SRC-y += oscam-client.c +SRC-y += oscam-conf.c +SRC-y += oscam-conf-chk.c +SRC-y += oscam-conf-mk.c +SRC-y += oscam-config-account.c +SRC-y += oscam-config-global.c +SRC-y += oscam-config-reader.c +SRC-y += oscam-config.c +SRC-y += oscam-ecm.c +SRC-y += oscam-emm.c +SRC-y += oscam-emm-cache.c +SRC-y += oscam-failban.c +SRC-y += oscam-files.c +SRC-y += oscam-garbage.c +SRC-y += oscam-lock.c +SRC-y += oscam-log.c +SRC-y += oscam-log-reader.c +SRC-y += oscam-net.c +SRC-y += oscam-llist.c +SRC-y += oscam-reader.c +SRC-y += oscam-simples.c +SRC-y += oscam-string.c +SRC-y += oscam-time.c +SRC-y += oscam-work.c +SRC-y += oscam.c +# config.c is automatically generated by config.sh in OBJDIR +SRC-y += config.c +ifdef BUILD_TESTS + SRC-y += tests.c + override STD_DEFS += -DBUILD_TESTS=1 +endif + +SRC := $(SRC-y) +OBJ := $(addprefix $(OBJDIR)/,$(subst .c,.o,$(SRC))) +SRC := $(subst config.c,$(OBJDIR)/config.c,$(SRC)) + +# The default build target rebuilds the config.mak if needed and then +# starts the compilation. +all: submodules + @./config.sh --use-flags "$(USE_FLAGS)" --objdir "$(OBJDIR)" --make-config.mak + @-mkdir -p $(OBJDIR)/cscrypt $(OBJDIR)/csctapi $(OBJDIR)/minilzo $(OBJDIR)/webif $(OBJDIR)/signing + @-printf "\ ++-------------------------------------------------------------------------------\n\ +| OSCam Ver.: $(VER) sha: $(GIT_SHA) target: $(TARGET)\n\ +| Build Date: $(BUILD_DATE)\n\ +| Tools:\n\ +| CROSS = $(CROSS_DIR)$(CROSS)\n\ +| CC = $(CC)\n\ +| STRIP = $(STRIP)\n\ +$(UPX_INFO_TOOL)\ +$(SIGN_INFO_TOOL)\ +| Settings:\n\ +| CONF_DIR = $(CONF_DIR)\n\ +| CC_OPTS = $(strip $(CC_OPTS))\n\ +| CC_WARN = $(strip $(CC_WARN))\n\ +| CFLAGS = $(strip $(CFLAGS))\n\ +| LDFLAGS = $(strip $(LDFLAGS))\n\ +| LIBS = $(strip $(LIBS))\n\ +| UseFlags = $(addsuffix =1,$(USE_FLAGS))\n\ +| Config:\n\ +| Addons : $(shell ./config.sh --use-flags "$(USE_FLAGS)" --show-enabled addons)\n\ +| Protocols: $(shell ./config.sh --use-flags "$(USE_FLAGS)" --show-enabled protocols | sed -e 's|MODULE_||g')\n\ +| Readers : $(shell ./config.sh --use-flags "$(USE_FLAGS)" --show-enabled readers | sed -e 's|READER_||g')\n\ +| CardRdrs : $(shell ./config.sh --use-flags "$(USE_FLAGS)" --show-enabled card_readers | sed -e 's|CARDREADER_||g')\n\ +| Compiler : $(CCVERSION)$(SSL_INFO)\n\ +$(UPX_INFO)\ +$(SIGN_INFO)\ +| Config : $(OBJDIR)/config.mak\n\ +| Binary : $(OSCAM_BIN)\n\ ++-------------------------------------------------------------------------------\n" +ifeq "$(shell ./config.sh --enabled WEBIF)" "Y" + @$(MAKE) --no-print-directory --quiet -C webif +endif + @$(MAKE) --no-print-directory $(OSCAM_BIN) $(LIST_SMARGO_BIN) + +$(OSCAM_BIN).debug: $(OBJ) + $(SAY) "LINK $@" + $(Q)$(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + $(Q)$(SIGN_COMMAND_OSCAM) + +$(OSCAM_BIN): $(OSCAM_BIN).debug + $(SAY) "STRIP $@" + $(Q)cp $(OSCAM_BIN).debug $(OSCAM_BIN) + $(Q)$(STRIP) $(OSCAM_BIN) + $(Q)$(SIGN_COMMAND_OSCAM) + $(Q)$(UPX_COMMAND_OSCAM) + +$(LIST_SMARGO_BIN): utils/list_smargo.c + $(SAY) "BUILD $@" + $(Q)$(CC) $(STD_DEFS) $(CC_OPTS) $(CC_WARN) $(CFLAGS) $(LDFLAGS) utils/list_smargo.c $(LIBS) -o $@ + +$(OBJDIR)/config.o: $(OBJDIR)/config.c + $(SAY) "CONF $<" + $(Q)$(CC) $(STD_DEFS) $(CC_OPTS) $(CC_WARN) $(CFLAGS) -c $< -o $@ + +$(OBJDIR)/%.o: %.c Makefile + @$(CC) $(CFLAGS) -MP -MM -MT $@ -o $(subst .o,.d,$@) $< + $(SAY) "CC $<" + $(Q)$(CC) $(STD_DEFS) $(CC_OPTS) $(CC_WARN) $(CFLAGS) -c $< -o $@ + +-include $(subst .o,.d,$(OBJ)) + +tests: + @-$(MAKE) --no-print-directory BUILD_TESTS=1 OSCAM_BIN=$(TESTS_BIN) + @-touch oscam.c +# The above is really hideous hack :-) If we don't force oscam.c recompilation +# after we've build the tests binary, the next "normal" compilation would fail +# because there would be no run_tests() function. So the touch is there to +# ensure oscam.c would be recompiled. + +config: + $(SHELL) ./config.sh --gui + +menuconfig: config + +allyesconfig: + @echo "Enabling all config options." + @-$(SHELL) ./config.sh --enable all + +allnoconfig: + @echo "Disabling all config options." + @-$(SHELL) ./config.sh --disable all + +defconfig: + @echo "Restoring default config." + @-$(SHELL) ./config.sh --restore + +clean: + @-for FILE in $(BUILD_DIR)/* $(TESTS_BIN) $(TESTS_BIN).debug; do \ + echo "RM $$FILE"; \ + rm -rf $$FILE; \ + done + @-rm -rf $(BUILD_DIR) lib + +distclean: clean + @-for FILE in $(BINDIR)/list_smargo-* $(BINDIR)/oscam-$(VER)*; do \ + echo "RM $$FILE"; \ + rm -rf $$FILE; \ + done + @-$(MAKE) --no-print-directory --quiet -C webif clean + +submodules: + @./config.sh --submodule + +README.build: + @echo "Extracting 'make help' into $@ file." + @-printf "\ +** This file is generated from 'make help' output, do not edit it. **\n\ +\n\ +" > $@ + @-$(MAKE) --no-print-directory help >> $@ + @echo "Done." + +README.config: + @echo "Extracting 'config.sh --help' into $@ file." + @-printf "\ +** This file is generated from 'config.sh --help' output, do not edit it. **\n\ +\n\ +" > $@ + @-./config.sh --help >> $@ + @echo "Done." + +help: + @-printf "\ +OSCam build system documentation\n\ +================================\n\ +\n\ + Build variables:\n\ + The build variables are set on the make command line and control the build\n\ + process. Setting the variables lets you enable additional features, request\n\ + extra libraries and more. Currently recognized build variables are:\n\ +\n\ + CROSS=prefix - Set tools prefix. This variable is used when OScam is being\n\ + cross compiled. For example if you want to cross compile\n\ + for SH4 architecture you can run: 'make CROSS=sh4-linux-'\n\ + If you don't have the directory where cross compilers are\n\ + in your PATH you can run:\n\ + 'make CROSS=/opt/STM/STLinux-2.3/devkit/sh4/bin/sh4-linux-'\n\ +\n\ + CROSS_DIR=dir - Set tools directory. This variable is added in front of\n\ + CROSS variable. CROSS_DIR is useful if you want to use\n\ + predefined targets that are setting CROSS, but you don't have\n\ + the cross compilers in your PATH. For example:\n\ + 'make sh4 CROSS_DIR=/opt/STM/STLinux-2.3/devkit/sh4/bin/'\n\ + 'make dm500 CROSS_DIR=/opt/cross/dm500/cdk/bin/'\n\ +\n\ + CONF_DIR=/dir - Set OSCam config directory. For example to change config\n\ + directory to /etc run: 'make CONF_DIR=/etc'\n\ + The default config directory is: '$(CONF_DIR)'\n\ +\n\ + CC_OPTS=text - This variable holds compiler optimization parameters.\n\ + Default CC_OPTS value is:\n\ + '$(CC_OPTS)'\n\ + To add text to this variable set EXTRA_CC_OPTS=text.\n\ +\n\ + CC_WARN=text - This variable holds compiler warning parameters.\n\ + Default CC_WARN value is:\n\ + '$(CC_WARN)'\n\ + To add text to this variable set EXTRA_CC_WARN=text.\n\ +\n\ + V=1 - Request build process to print verbose messages. By\n\ + default the only messages that are shown are simple info\n\ + what is being compiled. To request verbose build run:\n\ + 'make V=1'\n\ +\n\ + COMP_LEVEL=text - This variable holds the upx compression level and can be\n\ + used in combination with USE_COMPRESS=1\n\ + For example to change compression level to brute\n\ + you can run: 'make USE_COMPRESS=1 COMP_LEVEL=--brute'\n\ + To get a list of available compression levels run: 'upx --help'\n\ + The default upx compression level is: '$(COMP_LEVEL)'\n\ +\n\ + Extra build variables:\n\ + These variables add text to build variables. They are useful if you want\n\ + to add additional options to already set variables without overwriting them\n\ + Currently defined EXTRA_xxx variables are:\n\ +\n\ + EXTRA_CC_OPTS - Add text to CC_OPTS.\n\ + Example: 'make EXTRA_CC_OPTS=-Os'\n\ +\n\ + EXTRA_CC_WARN - Add text to CC_WARN.\n\ + Example: 'make EXTRA_CC_WARN=-Wshadow'\n\ +\n\ + EXTRA_TARGET - Add text to TARGET.\n\ + Example: 'make EXTRA_TARGET=-private'\n\ +\n\ + EXTRA_CFLAGS - Add text to CFLAGS (affects compilation).\n\ + Example: 'make EXTRA_CFLAGS=\"-DBLAH=1 -I/opt/local\"'\n\ +\n\ + EXTRA_LDFLAGS - Add text to LDFLAGS (affects linking).\n\ + Example: 'make EXTRA_LDFLAGS=-Llibdir'\n\ +\n\ + EXTRA_FLAGS - Add text to both EXTRA_CFLAGS and EXTRA_LDFLAGS.\n\ + Example: 'make EXTRA_FLAGS=-DBLAH=1'\n\ +\n\ + EXTRA_LIBS - Add text to LIBS (affects linking).\n\ + Example: 'make EXTRA_LIBS=\"-L./stapi -loscam_stapi\"'\n\ +\n\ + Use flags:\n\ + Use flags are used to request additional libraries or features to be used\n\ + by OSCam. Currently defined USE_xxx flags are:\n\ +\n\ + USE_COMPRESS=1 - Request compressing oscam binary with upx.\n\ +\n\ + USE_LIBUSB=1 - Request linking with libusb. The variables that control\n\ + USE_LIBUSB=1 build are:\n\ + LIBUSB_FLAGS='$(DEFAULT_LIBUSB_FLAGS)'\n\ + LIBUSB_CFLAGS='$(DEFAULT_LIBUSB_FLAGS)'\n\ + LIBUSB_LDFLAGS='$(DEFAULT_LIBUSB_FLAGS)'\n\ + LIBUSB_LIB='$(DEFAULT_LIBUSB_LIB)'\n\ + Using USE_LIBUSB=1 adds to '-libusb' to PLUS_TARGET.\n\ + To build with static libusb, set the variable LIBUSB_LIB\n\ + to contain full path of libusb library. For example:\n\ + make USE_LIBUSB=1 LIBUSB_LIB=/usr/lib/libusb-1.0.a\n\ +\n\ + USE_PCSC=1 - Request linking with PCSC. The variables that control\n\ + USE_PCSC=1 build are:\n\ + PCSC_FLAGS='$(DEFAULT_PCSC_FLAGS)'\n\ + PCSC_CFLAGS='$(DEFAULT_PCSC_FLAGS)'\n\ + PCSC_LDFLAGS='$(DEFAULT_PCSC_FLAGS)'\n\ + PCSC_LIB='$(DEFAULT_PCSC_LIB)'\n\ + Using USE_PCSC=1 adds to '-pcsc' to PLUS_TARGET.\n\ + To build with static PCSC, set the variable PCSC_LIB\n\ + to contain full path of PCSC library. For example:\n\ + make USE_PCSC=1 PCSC_LIB=/usr/local/lib/libpcsclite.a\n\ +\n\ + USE_STAPI=1 - Request linking with STAPI. The variables that control\n\ + USE_STAPI=1 build are:\n\ + STAPI_FLAGS='$(DEFAULT_STAPI_FLAGS)'\n\ + STAPI_CFLAGS='$(DEFAULT_STAPI_FLAGS)'\n\ + STAPI_LDFLAGS='$(DEFAULT_STAPI_FLAGS)'\n\ + STAPI_LIB='$(DEFAULT_STAPI_LIB)'\n\ + Using USE_STAPI=1 adds to '-stapi' to PLUS_TARGET.\n\ + In order for USE_STAPI to work you have to create stapi\n\ + directory and put liboscam_stapi.a file in it.\n\ +\n\ + USE_STAPI5=1 - Request linking with STAPI5. The variables that control\n\ + USE_STAPI5=1 build are:\n\ + STAPI5_FLAGS='$(DEFAULT_STAPI5_FLAGS)'\n\ + STAPI5_CFLAGS='$(DEFAULT_STAPI5_FLAGS)'\n\ + STAPI5_LDFLAGS='$(DEFAULT_STAPI5_FLAGS)'\n\ + STAPI5_LIB='$(DEFAULT_STAPI5_LIB)'\n\ + Using USE_STAPI5=1 adds to '-stapi' to PLUS_TARGET.\n\ + In order for USE_STAPI5 to work you have to create stapi\n\ + directory and put liboscam_stapi5.a file in it.\n\ +\n\ + USE_COOLAPI=1 - Request support for Coolstream API (libnxp) aka NeutrinoHD\n\ + box. The variables that control the build are:\n\ + COOLAPI_FLAGS='$(DEFAULT_COOLAPI_FLAGS)'\n\ + COOLAPI_CFLAGS='$(DEFAULT_COOLAPI_FLAGS)'\n\ + COOLAPI_LDFLAGS='$(DEFAULT_COOLAPI_FLAGS)'\n\ + COOLAPI_LIB='$(DEFAULT_COOLAPI_LIB)'\n\ + Using USE_COOLAPI=1 adds to '-coolapi' to PLUS_TARGET.\n\ + In order for USE_COOLAPI to work you have to have libnxp.so\n\ + library in your cross compilation toolchain.\n\ +\n\ + USE_COOLAPI2=1 - Request support for Coolstream API aka NeutrinoHD\n\ + box. The variables that control the build are:\n\ + COOLAPI_FLAGS='$(DEFAULT_COOLAPI2_FLAGS)'\n\ + COOLAPI_CFLAGS='$(DEFAULT_COOLAPI2_FLAGS)'\n\ + COOLAPI_LDFLAGS='$(DEFAULT_COOLAPI2_FLAGS)'\n\ + COOLAPI_LIB='$(DEFAULT_COOLAPI2_LIB)'\n\ + Using USE_COOLAPI2=1 adds to '-coolapi2' to PLUS_TARGET.\n\ + In order for USE_COOLAPI2 to work you have to have liblnxUKAL.so,\n\ + liblnxcssUsr.so, liblnxscsUsr.so, liblnxnotifyqUsr.so, liblnxplatUsr.so\n\ + library in your cross compilation toolchain.\n\ +\n\ + USE_SU980=1 - Request support for SU980 API (libentropic) aka Enimga2 arm\n\ + box. The variables that control the build are:\n\ + COOLAPI_FLAGS='$(DEFAULT_SU980_FLAGS)'\n\ + COOLAPI_CFLAGS='$(DEFAULT_SU980_FLAGS)'\n\ + COOLAPI_LDFLAGS='$(DEFAULT_SU980_FLAGS)'\n\ + COOLAPI_LIB='$(DEFAULT_SU980_LIB)'\n\ + Using USE_SU980=1 adds to '-su980' to PLUS_TARGET.\n\ + In order for USE_SU980 to work you have to have libentropic.a\n\ + library in your cross compilation toolchain.\n\ +\n\ + USE_AZBOX=1 - Request support for AZBOX (openxcas)\n\ + box. The variables that control the build are:\n\ + AZBOX_FLAGS='$(DEFAULT_AZBOX_FLAGS)'\n\ + AZBOX_CFLAGS='$(DEFAULT_AZBOX_FLAGS)'\n\ + AZBOX_LDFLAGS='$(DEFAULT_AZBOX_FLAGS)'\n\ + AZBOX_LIB='$(DEFAULT_AZBOX_LIB)'\n\ + Using USE_AZBOX=1 adds to '-azbox' to PLUS_TARGET.\n\ + extapi/openxcas/libOpenXCASAPI.a library that is shipped\n\ + with OSCam is compiled for MIPSEL.\n\ +\n\ + USE_AMSMC=1 - Request support for Amlogic SMC internal smartcard reader.\n\ + Using USE_AMSMC=1 adds '-amsmc' to PLUS_TARGET.\n\ + This enables support for the internal smartcard slot\n\ + on set-top boxes using the Amlogic SMC driver (/dev/smc0).\n\ +\n\ + USE_MCA=1 - Request support for Matrix Cam Air (MCA).\n\ + The variables that control the build are:\n\ + MCA_FLAGS='$(DEFAULT_MCA_FLAGS)'\n\ + MCA_CFLAGS='$(DEFAULT_MCA_FLAGS)'\n\ + MCA_LDFLAGS='$(DEFAULT_MCA_FLAGS)'\n\ + Using USE_MCA=1 adds to '-mca' to PLUS_TARGET.\n\ +\n\ + USE_LIBCRYPTO=1 - Request linking with libcrypto instead of using OSCam\n\ + internal crypto functions. USE_LIBCRYPTO is automatically\n\ + enabled if the build is configured with SSL support. The\n\ + variables that control USE_LIBCRYPTO=1 build are:\n\ + LIBCRYPTO_FLAGS='$(DEFAULT_LIBCRYPTO_FLAGS)'\n\ + LIBCRYPTO_CFLAGS='$(DEFAULT_LIBCRYPTO_FLAGS)'\n\ + LIBCRYPTO_LDFLAGS='$(DEFAULT_LIBCRYPTO_FLAGS)'\n\ + LIBCRYPTO_LIB='$(DEFAULT_LIBCRYPTO_LIB)'\n\ +\n\ + USE_SSL=1 - Request linking with libssl. USE_SSL is automatically\n\ + enabled if the build is configured with SSL support. The\n\ + variables that control USE_SSL=1 build are:\n\ + SSL_FLAGS='$(DEFAULT_SSL_FLAGS)'\n\ + SSL_CFLAGS='$(DEFAULT_SSL_FLAGS)'\n\ + SSL_LDFLAGS='$(DEFAULT_SSL_FLAGS)'\n\ + SSL_LIB='$(DEFAULT_SSL_LIB)'\n\ + Using USE_SSL=1 adds to '-ssl' to PLUS_TARGET.\n\ +\n\ + USE_LIBDVBCSA=1 - Request linking with libdvbcsa. USE_LIBDVBCSA is automatically\n\ + enabled if the build is configured with STREAMRELAY support. The\n\ + variables that control USE_LIBDVBCSA=1 build are:\n\ + LIBDVBCSA_FLAGS='$(DEFAULT_LIBDVBCSA_FLAGS)'\n\ + LIBDVBCSA_CFLAGS='$(DEFAULT_LIBDVBCSA_FLAGS)'\n\ + LIBDVBCSA_LDFLAGS='$(DEFAULT_LIBDVBCSA_FLAGS)'\n\ + LIBDVBCSA_LIB='$(DEFAULT_LIBDVBCSA_LIB)'\n\ +\n\ + Automatically intialized variables:\n\ +\n\ + TARGET=text - This variable is auto detected by using the compiler's\n\ + -dumpmachine output. To see the target on your machine run:\n\ + 'gcc -dumpmachine'\n\ +\n\ + PLUS_TARGET - This variable is added to TARGET and it is set depending\n\ + on the chosen USE_xxx flags. To disable adding\n\ + PLUS_TARGET to TARGET, set NO_PLUS_TARGET=1\n\ +\n\ + BINDIR - The directory where final oscam binary would be put. The\n\ + default is: $(BINDIR)\n\ +\n\ + OSCAM_BIN=text - This variable controls how the oscam binary will be named.\n\ + Default OSCAM_BIN value is:\n\ + 'BINDIR/oscam-VER@$GIT_SHA-TARGET'\n\ + Once the variables (BINDIR, VER, GIT_SHA and TARGET) are\n\ + replaced, the resulting filename can look like this:\n\ + 'Distribution/oscam-1.20-unstable_svn7404-i486-slackware-linux-static'\n\ + For example you can run: 'make OSCAM_BIN=my-oscam'\n\ +\n\ + Binaries compiled and run during the OSCam build:\n\ +\n\ + OSCam builds webif/pages_gen binary that is run by the build system to\n\ + generate file that holds web pages. To build this binary two variables\n\ + are used:\n\ +\n\ + HOSTCC=gcc - The compiler used for building binaries that are run on\n\ + the build machine (the host). Default: gcc\n\ + To use clang for example run: make CC=clang HOSTCC=clang\n\ +\n\ + HOSTCFLAGS=xxx - The CFLAGS passed to HOSTCC. See webif/Makefile for the\n\ + default host cflags.\n\ +\n\ + Config targets:\n\ + make config - Start configuration utility.\n\ + make allyesconfig - Enable all configuration options.\n\ + make allnoconfig - Disable all configuration options.\n\ + make defconfig - Restore default configuration options.\n\ +\n\ + Cleaning targets:\n\ + make clean - Remove '$(BUILD_DIR)' directory which contains compiled\n\ + object files.\n\ + make distclean - Executes clean target and also removes binary files\n\ + located in '$(BINDIR)' directory.\n\ +\n\ + Build system files:\n\ + config.sh - OSCam configuration. Run 'config.sh --help' to see\n\ + available parameters or 'make config' to start GUI\n\ + configuratior.\n\ + Makefile - Main build system file.\n\ + Makefile.extra - Contains predefined targets. You can use this file\n\ + as example on how to use the build system.\n\ + Makefile.local - This file is included in Makefile and allows creation\n\ + of local build system targets. See Makefile.extra for\n\ + examples.\n\ +\n\ + Here are some of the interesting predefined targets in Makefile.extra.\n\ + To use them run 'make target ...' where ... can be any extra flag. For\n\ + example if you want to compile OSCam for Dreambox (DM500) but do not\n\ + have the compilers in the path, you can run:\n\ + make dm500 CROSS_DIR=/opt/cross/dm500/cdk/bin/\n\ +\n\ + Predefined targets in Makefile.extra:\n\ +\n\ + make libusb - Builds OSCam with libusb support\n\ + make pcsc - Builds OSCam with PCSC support\n\ + make pcsc-libusb - Builds OSCam with PCSC and libusb support\n\ + make dm500 - Builds OSCam for Dreambox (DM500)\n\ + make sh4 - Builds OSCam for SH4 boxes\n\ + make azbox - Builds OSCam for AZBox STBs\n\ + make mca - Builds OSCam for Matrix Cam Air (MCA)\n\ + make coolstream - Builds OSCam for Coolstream HD1\n\ + make coolstream2 - Builds OSCam for Coolstream HD2\n\ + make dockstar - Builds OSCam for Dockstar\n\ + make qboxhd - Builds OSCam for QBoxHD STBs\n\ + make opensolaris - Builds OSCam for OpenSolaris\n\ + make uclinux - Builds OSCam for m68k uClinux\n\ +\n\ + Predefined targets for static builds:\n\ + make static - Builds OSCam statically\n\ + make static-libusb - Builds OSCam with libusb linked statically\n\ + make static-libcrypto - Builds OSCam with libcrypto linked statically\n\ + make static-ssl - Builds OSCam with SSL support linked statically\n\ +\n\ + Developer targets:\n\ + make tests - Builds '$(TESTS_BIN)' binary\n\ +\n\ + Examples:\n\ + Build OSCam for SH4 (the compilers are in the path):\n\ + make CROSS=sh4-linux-\n\n\ + Build OSCam for SH4 (the compilers are in not in the path):\n\ + make sh4 CROSS_DIR=/opt/STM/STLinux-2.3/devkit/sh4/bin/\n\ + make CROSS_DIR=/opt/STM/STLinux-2.3/devkit/sh4/bin/ CROSS=sh4-linux-\n\ + make CROSS=/opt/STM/STLinux-2.3/devkit/sh4/bin/sh4-linux-\n\n\ + Build OSCam for SH4 with STAPI:\n\ + make CROSS=sh4-linux- USE_STAPI=1\n\n\ + Build OSCam for SH4 with STAPI and changed configuration directory:\n\ + make CROSS=sh4-linux- USE_STAPI=1 CONF_DIR=/var/tuxbox/config\n\n\ + Build OSCam for ARM with COOLAPI (coolstream aka NeutrinoHD):\n\ + make CROSS=arm-cx2450x-linux-gnueabi- USE_COOLAPI=1\n\n\ + Build OSCam for ARM with COOLAPI2 (coolstream aka NeutrinoHD):\n\ + make CROSS=arm-pnx8400-linux-uclibcgnueabi- USE_COOLAPI2=1\n\n\ + Build OSCam for MIPSEL with AZBOX support:\n\ + make CROSS=mipsel-linux-uclibc- USE_AZBOX=1\n\n\ + Build OSCam for ARM with Amlogic SMC internal reader support:\n\ + make CROSS=aarch64-linux-gnu- USE_AMSMC=1\n\n\ + Build OSCam for ARM with MCA support:\n\ + make CROSS=arm-none-linux-gnueabi- USE_MCA=1\n\n\ + Build OSCam with libusb and PCSC:\n\ + make USE_LIBUSB=1 USE_PCSC=1\n\n\ + Build OSCam with static libusb:\n\ + make USE_LIBUSB=1 LIBUSB_LIB=\"/usr/lib/libusb-1.0.a\"\n\n\ + Build OSCam with static libcrypto:\n\ + make USE_LIBCRYPTO=1 LIBCRYPTO_LIB=\"/usr/lib/libcrypto.a\"\n\n\ + Build OSCam with static libssl and libcrypto:\n\ + make USE_SSL=1 SSL_LIB=\"/usr/lib/libssl.a\" LIBCRYPTO_LIB=\"/usr/lib/libcrypto.a\"\n\n\ + Build OSCam with static libdvbcsa:\n\ + make USE_LIBDVBCSA=1 LIBDVBCSA_LIB=\"/usr/lib/libdvbcsa.a\"\n\n\ + Build with verbose messages and size optimizations:\n\ + make V=1 CC_OPTS=-Os\n\n\ + Build and set oscam file name:\n\ + make OSCAM_BIN=oscam\n\n\ + Build and set oscam file name depending on revision:\n\ + make OSCAM_BIN=oscam-\`./config.sh -r\`\n\n\ +" + +simple: all +default: all +debug: all + +-include Makefile.extra +-include Makefile.local diff --git a/Makefile.extra b/Makefile.extra new file mode 100644 index 0000000..d1e2562 --- /dev/null +++ b/Makefile.extra @@ -0,0 +1,301 @@ +i386-pc-linux: simple +i386-pc-linux-debug: debug +i386-pc-freebsd: simple +hppa1.1-hp-hpux10.20: simple +alpha-dec-osf5.1: simple +linux: simple +freebsd: simple +tuxbox: cross-powerpc-tuxbox-linux +tripledragon: cross-powerpc-405-linux +win: cross-i386-pc-cygwin +cygwin: i386-pc-cygwin +macosx: macosx-native +linux-pcsc: i386-pc-linux-pcsc +dm500: cross-powerpc-tuxbox-linux +sh4: cross-sh4-linux +sh4-stapi: cross-sh4-linux-stapi +i386-pc-linux-libusb: libusb +i386-pc-linux-pcsc: pcsc +i386-pc-linux-pcsc-libusb: pcsc-libusb +libusb-pcsc: pcsc-libusb + +libusb: + $(MAKE) --no-print-directory \ + USE_LIBUSB=1 \ + $(MAKEFLAGS) + +pcsc: + $(MAKE) --no-print-directory \ + USE_PCSC=1 \ + $(MAKEFLAGS) + +pcsc-libusb: + $(MAKE) --no-print-directory \ + USE_LIBUSB=1 \ + USE_PCSC=1 \ + $(MAKEFLAGS) + +macosx-native: + $(MAKE) --no-print-directory \ + USE_PCSC=1 \ + $(MAKEFLAGS) + +macosx-libusb: + $(MAKE) --no-print-directory \ + USE_LIBUSB=1 \ + USE_PCSC=1 \ + $(MAKEFLAGS) + +cross-i386-pc-freebsd: + $(MAKE) --no-print-directory \ + CROSS=i386-pc-freebsd5.4- \ + EXTRA_FLAGS="-static-libgcc" \ + $(MAKEFLAGS) + +cross-powerpc-tuxbox-linux: + $(MAKE) --no-print-directory \ + CROSS=powerpc-tuxbox-linux-gnu- \ + CONF_DIR=/var/tuxbox/config \ + $(MAKEFLAGS) + +cross-powerpc-tuxbox-linux-uclibc: + $(MAKE) --no-print-directory \ + CROSS=powerpc-tuxbox-linux-uclibc- \ + CONF_DIR=/var/tuxbox/config \ + $(MAKEFLAGS) + +cross-powerpc-405-linux: + $(MAKE) --no-print-directory \ + CROSS=powerpc-405-linux-gnu- \ + CONF_DIR=/var/tuxbox/config \ + EXTRA_FLAGS="-DSTB04SCI=1" + $(MAKEFLAGS) + +cross-sh4-linux: + $(MAKE) --no-print-directory \ + CROSS=sh4-linux- \ + CONF_DIR=/var/tuxbox/config \ + $(MAKEFLAGS) + +cross-sh4-linux-stapi: + $(MAKE) --no-print-directory \ + CROSS=sh4-linux- \ + CONF_DIR=/var/tuxbox/config \ + USE_STAPI=1 \ + $(MAKEFLAGS) + +cross-i386-pc-cygwin: + $(MAKE) --no-print-directory \ + CROSS=i686-pc-cygwin- \ + EXTRA_FLAGS="-static" \ + $(MAKEFLAGS) + +i386-pc-cygwin: + $(MAKE) --no-print-directory \ + EXTRA_FLAGS="-I /tmp/include" \ + $(MAKEFLAGS) + +i386-pc-cygwin-pcsc: + $(MAKE) --no-print-directory \ + USE_PCSC=1 \ + PCSC_LIB="-lwinscard" \ + EXTRA_FLAGS="-D_WIN32 -I /tmp/include -I ./cygwin -I/usr/include/w32api" \ + EXTRA_LDFLAGS="-L/cygdrive/c/WINDOWS/system32/" \ + $(MAKEFLAGS) + +i386-pc-cygwin-libusb: + $(MAKE) --no-print-directory \ + USE_LIBUSB=1 \ + LIBUSB_LIB="/usr/lib/libusb-1.0.a" \ + USE_PCSC=1 \ + PCSC_LIB="-lwinscard" \ + EXTRA_LIBS="-lSetupAPI -lOle32 -lshell32" \ + EXTRA_FLAGS="-D_WIN32 -I /tmp/include -I ./cygwin -I/usr/include/w32api" \ + EXTRA_LDFLAGS="-L/cygdrive/c/WINDOWS/system32/" \ + $(MAKEFLAGS) + +cross-sparc-sun-solaris2.7: + $(MAKE) --no-print-directory \ + CROSS=sparc-sun-solaris2.7- \ + EXTRA_LIBS="-lsocket" \ + EXTRA_FLAGS="-static-libgcc" \ + $(MAKEFLAGS) + +opensolaris: + $(MAKE) --no-print-directory \ + EXTRA_LIBS="-lsocket -lnsl" \ + EXTRA_FLAGS="-static-libgcc" \ + $(MAKEFLAGS) + +cross-rs6000-ibm-aix4.2: + $(MAKE) --no-print-directory \ + CROSS=rs6000-ibm-aix4.2- \ + LIB_PTHREAD=-lpthreads \ + EXTRA_FLAGS="-static-libgcc" \ + $(MAKEFLAGS) + +cross-mips-sgi-irix6.5: + $(MAKE) --no-print-directory \ + CROSS=mips-sgi-irix6.5- \ + EXTRA_FLAGS="-static-libgcc" \ + $(MAKEFLAGS) + +cross-mipsel-router-linux-uclibc: + $(MAKE) --no-print-directory \ + CROSS=mipsel-linux-uclibc- \ + EXTRA_FLAGS="-static-libgcc" \ + $(MAKEFLAGS) + +cross-mipsel-router-linux-uclibc927: cross-mipsel-router-linux-uclibc +cross-mipsel-router-linux-uclibc928: cross-mipsel-router-linux-uclibc +cross-mipsel-router-linux-uclibc929: cross-mipsel-router-linux-uclibc + +cross-mipsel-router-linux-uclibc929-static: + $(MAKE) --no-print-directory \ + CROSS=mipsel-linux-uclibc- \ + EXTRA_FLAGS="-static-libgcc" \ + EXTRA_LDFLAGS="-static" \ + $(MAKEFLAGS) + +cross-mips-router-linux-uclibc: + $(MAKE) --no-print-directory \ + CROSS=mips-linux-uclibc- \ + EXTRA_FLAGS="-static-libgcc" \ + $(MAKEFLAGS) + +cross-mips-router-linux-uclibc930: cross-mips-router-linux-uclibc +cross-mips-router-linux-uclibc931: cross-mips-router-linux-uclibc930 +cross-mipsel-fonera2: cross-mips-router-linux-uclibc + +cross-mipsel-tuxbox-linux: + $(MAKE) --no-print-directory \ + CROSS=mipsel-unknown-linux-gnu- \ + CONF_DIR=/var/tuxbox/config \ + EXTRA_FLAGS="-static-libgcc" \ + $(MAKEFLAGS) + +cross-mipsel-tuxbox-linux-glibc: + $(MAKE) --no-print-directory \ + CROSS=mipsel-linux-glibc- \ + CONF_DIR=/var/tuxbox/config \ + EXTRA_FLAGS="-static-libgcc" \ + $(MAKEFLAGS) + +cross-arm-nslu2-linux: + $(MAKE) --no-print-directory \ + CROSS=armv5b-softfloat-linux- \ + $(MAKEFLAGS) + +cross-armBE-unknown-linux: + $(MAKE) --no-print-directory \ + CROSS=arm-linux- \ + EXTRA_FLAGS="-mbig-endian" \ + $(MAKEFLAGS) + +cross-armBE-unkown-linux: cross-armBE-unknown-linux + +cross-armLE-unknown-linux: + $(MAKE) --no-print-directory \ + CROSS=arm-linux- \ + EXTRA_FLAGS="-mlittle-endian" \ + $(MAKEFLAGS) + +cross-armLE-unkown-linux: cross-armLE-unknown-linux + +azbox: + $(MAKE) --no-print-directory \ + CROSS=mipsel-linux-uclibc- \ + CONF_DIR=/PLUGINS/OpenXCAS/oscamCAS \ + USE_AZBOX=1 \ + $(MAKEFLAGS) + +mca: + $(MAKE) --no-print-directory \ + CROSS=arm-none-linux-gnueabi- \ + CONF_DIR=/var/mca/d1 \ + USE_MCA=1 \ + $(MAKEFLAGS) + +cool: + $(MAKE) --no-print-directory \ + CROSS=arm-cx2450x-linux-gnueabi- \ + CONF_DIR=/var/tuxbox/config \ + USE_COOLAPI=1 \ + $(MAKEFLAGS) + +cool2: + $(MAKE) --no-print-directory \ + CROSS=arm-pnx8400-linux-uclibcgnueabi- \ + CONF_DIR=/var/tuxbox/config \ + USE_COOLAPI2=1 \ + $(MAKEFLAGS) + +neutrinohd: cool +coolstream: cool + +neutrinohd2: cool2 +coolstream2: cool2 + +su980: + $(MAKE) --no-print-directory \ + CROSS=arm-cortex-linux-gnueabi- \ + CONF_DIR=/var/tuxbox/config \ + USE_SU980=1 \ + $(MAKEFLAGS) + +hypercube: su980 + +dockstar: + $(MAKE) --no-print-directory \ + CROSS=arm-openwrt-linux-uclibcgnueabi- \ + CONF_DIR=/jffs/etc/config/oscam \ + $(MAKEFLAGS) + +qboxhd: + $(MAKE) --no-print-directory \ + CROSS=sh4-linux- \ + EXTRA_FLAGS="-DQBOXHD=1" \ + PLUS_TARGET=-qboxhd \ + CONF_DIR=/var/tuxbox/config \ + $(MAKEFLAGS) + +static: + $(MAKE) --no-print-directory \ + EXTRA_FLAGS=-static \ + EXTRA_TARGET=-static \ + $(MAKEFLAGS) + +static-libusb: + $(MAKE) --no-print-directory \ + USE_LIBUSB=1 \ + LIBUSB_LIB=/usr/lib/libusb-1.0.a \ + EXTRA_TARGET=-static \ + $(MAKEFLAGS) + +static-libcrypto: + $(MAKE) --no-print-directory \ + USE_LIBCRYPTO=1 \ + LIBCRYPTO_LIB=/usr/lib/libcrypto.a \ + EXTRA_TARGET=-static \ + $(MAKEFLAGS) + +static-ssl: + $(MAKE) --no-print-directory \ + USE_SSL=1 \ + SSL_LIB=/usr/lib/libssl.a \ + LIBCRYPTO_LIB=/usr/lib/libcrypto.a \ + EXTRA_TARGET=-static \ + $(MAKEFLAGS) + +android-arm: + $(MAKE) --no-print-directory \ + LIB_RT= \ + LIB_PTHREAD= \ + CROSS=arm-linux-androideabi- \ + $(MAKEFLAGS) + +uclinux: + $(MAKE) --no-print-directory \ + LIB_DL= STRIP=true \ + CROSS=m68k-uclinux- \ + $(MAKEFLAGS) diff --git a/README.build b/README.build new file mode 100644 index 0000000..bf68e95 --- /dev/null +++ b/README.build @@ -0,0 +1,302 @@ +** This file is generated from 'make help' output, do not edit it. ** + +OSCam build system documentation +================================ + + Build variables: + The build variables are set on the make command line and control the build + process. Setting the variables lets you enable additional features, request + extra libraries and more. Currently recognized build variables are: + + CROSS=prefix - Set tools prefix. This variable is used when OScam is being + cross compiled. For example if you want to cross compile + for SH4 architecture you can run: 'make CROSS=sh4-linux-' + If you don't have the directory where cross compilers are + in your PATH you can run: + 'make CROSS=/opt/STM/STLinux-2.3/devkit/sh4/bin/sh4-linux-' + + CROSS_DIR=dir - Set tools directory. This variable is added in front of + CROSS variable. CROSS_DIR is useful if you want to use + predefined targets that are setting CROSS, but you don't have + the cross compilers in your PATH. For example: + 'make sh4 CROSS_DIR=/opt/STM/STLinux-2.3/devkit/sh4/bin/' + 'make dm500 CROSS_DIR=/opt/cross/dm500/cdk/bin/' + + CONF_DIR=/dir - Set OSCam config directory. For example to change config + directory to /etc run: 'make CONF_DIR=/etc' + The default config directory is: '/usr/local/etc' + + CC_OPTS=text - This variable holds compiler optimization parameters. + Default CC_OPTS value is: + '-O2 -ggdb -pipe -ffunction-sections -fdata-sections ' + To add text to this variable set EXTRA_CC_OPTS=text. + + CC_WARN=text - This variable holds compiler warning parameters. + Default CC_WARN value is: + '-W -Wall -Wshadow -Wredundant-decls -Wstrict-prototypes -Wold-style-definition ' + To add text to this variable set EXTRA_CC_WARN=text. + + V=1 - Request build process to print verbose messages. By + default the only messages that are shown are simple info + what is being compiled. To request verbose build run: + 'make V=1' + + Extra build variables: + These variables add text to build variables. They are useful if you want + to add additional options to already set variables without overwriting them + Currently defined EXTRA_xxx variables are: + + EXTRA_CC_OPTS - Add text to CC_OPTS. + Example: 'make EXTRA_CC_OPTS=-Os' + + EXTRA_CC_WARN - Add text to CC_WARN. + Example: 'make EXTRA_CC_WARN=-Wshadow' + + EXTRA_TARGET - Add text to TARGET. + Example: 'make EXTRA_TARGET=-private' + + EXTRA_CFLAGS - Add text to CFLAGS (affects compilation). + Example: 'make EXTRA_CFLAGS="-DBLAH=1 -I/opt/local"' + + EXTRA_LDFLAGS - Add text to LDFLAGS (affects linking). + Example: 'make EXTRA_LDFLAGS=-Llibdir' + + EXTRA_FLAGS - Add text to both EXTRA_CFLAGS and EXTRA_LDFLAGS. + Example: 'make EXTRA_FLAGS=-DBLAH=1' + + EXTRA_LIBS - Add text to LIBS (affects linking). + Example: 'make EXTRA_LIBS="-L./stapi -loscam_stapi"' + + Use flags: + Use flags are used to request additional libraries or features to be used + by OSCam. Currently defined USE_xxx flags are: + + USE_LIBUSB=1 - Request linking with libusb. The variables that control + USE_LIBUSB=1 build are: + LIBUSB_FLAGS='-DWITH_LIBUSB=1' + LIBUSB_CFLAGS='-DWITH_LIBUSB=1' + LIBUSB_LDFLAGS='-DWITH_LIBUSB=1' + LIBUSB_LIB='-lusb-1.0 -lrt' + Using USE_LIBUSB=1 adds to '-libusb' to PLUS_TARGET. + To build with static libusb, set the variable LIBUSB_LIB + to contain full path of libusb library. For example: + make USE_LIBUSB=1 LIBUSB_LIB=/usr/lib/libusb-1.0.a + + USE_PCSC=1 - Request linking with PCSC. The variables that control + USE_PCSC=1 build are: + PCSC_FLAGS='-DWITH_PCSC=1 -I/usr/include/PCSC -I/usr/include/../local/include/PCSC' + PCSC_CFLAGS='-DWITH_PCSC=1 -I/usr/include/PCSC -I/usr/include/../local/include/PCSC' + PCSC_LDFLAGS='-DWITH_PCSC=1 -I/usr/include/PCSC -I/usr/include/../local/include/PCSC' + PCSC_LIB='-lpcsclite' + Using USE_PCSC=1 adds to '-pcsc' to PLUS_TARGET. + To build with static PCSC, set the variable PCSC_LIB + to contain full path of PCSC library. For example: + make USE_PCSC=1 PCSC_LIB=/usr/local/lib/libpcsclite.a + + USE_STAPI=1 - Request linking with STAPI. The variables that control + USE_STAPI=1 build are: + STAPI_FLAGS='-DWITH_STAPI=1' + STAPI_CFLAGS='-DWITH_STAPI=1' + STAPI_LDFLAGS='-DWITH_STAPI=1' + STAPI_LIB='-L./stapi -loscam_stapi' + Using USE_STAPI=1 adds to '-stapi' to PLUS_TARGET. + In order for USE_STAPI to work you have to create stapi + directory and put liboscam_stapi.a file in it. + + USE_STAPI5=1 - Request linking with STAPI5. The variables that control + USE_STAPI5=1 build are: + STAPI5_FLAGS='-DWITH_STAPI5=1' + STAPI5_CFLAGS='-DWITH_STAPI5=1' + STAPI5_LDFLAGS='-DWITH_STAPI5=1' + STAPI5_LIB='-L./stapi -loscam_stapi5' + Using USE_STAPI5=1 adds to '-stapi' to PLUS_TARGET. + In order for USE_STAPI5 to work you have to create stapi + directory and put liboscam_stapi5.a file in it. + + USE_COOLAPI=1 - Request support for Coolstream API (libnxp) aka NeutrinoHD + box. The variables that control the build are: + COOLAPI_FLAGS='-DWITH_COOLAPI=1' + COOLAPI_CFLAGS='-DWITH_COOLAPI=1' + COOLAPI_LDFLAGS='-DWITH_COOLAPI=1' + COOLAPI_LIB='-lnxp -lrt' + Using USE_COOLAPI=1 adds to '-coolapi' to PLUS_TARGET. + In order for USE_COOLAPI to work you have to have libnxp.so + library in your cross compilation toolchain. + + USE_SU980=1 - Request support for SU980 API (libentropic) aka Enimga2 arm + box. The variables that control the build are: + COOLAPI_FLAGS='-DWITH_SU980=1' + COOLAPI_CFLAGS='-DWITH_SU980=1' + COOLAPI_LDFLAGS='-DWITH_SU980=1' + COOLAPI_LIB='-lentropic -lrt' + Using USE_SU980=1 adds to '-su980' to PLUS_TARGET. + In order for USE_SU980 to work you have to have libentropic.a + library in your cross compilation toolchain. + + USE_AZBOX=1 - Request support for AZBOX (openxcas) + box. The variables that control the build are: + AZBOX_FLAGS='-DWITH_AZBOX=1' + AZBOX_CFLAGS='-DWITH_AZBOX=1' + AZBOX_LDFLAGS='-DWITH_AZBOX=1' + AZBOX_LIB='-Lextapi/openxcas -lOpenXCASAPI' + Using USE_AZBOX=1 adds to '-azbox' to PLUS_TARGET. + extapi/openxcas/libOpenXCASAPI.a library that is shipped + with OSCam is compiled for MIPSEL. + + USE_MCA=1 - Request support for Matrix Cam Air (MCA). + The variables that control the build are: + MCA_FLAGS='-DWITH_MCA=1' + MCA_CFLAGS='-DWITH_MCA=1' + MCA_LDFLAGS='-DWITH_MCA=1' + Using USE_MCA=1 adds to '-mca' to PLUS_TARGET. + + USE_LIBCRYPTO=1 - Request linking with libcrypto instead of using OSCam + internal crypto functions. USE_LIBCRYPTO is automatically + enabled if the build is configured with SSL support. The + variables that control USE_LIBCRYPTO=1 build are: + LIBCRYPTO_FLAGS='-DWITH_LIBCRYPTO=1' + LIBCRYPTO_CFLAGS='-DWITH_LIBCRYPTO=1' + LIBCRYPTO_LDFLAGS='-DWITH_LIBCRYPTO=1' + LIBCRYPTO_LIB='-lcrypto' + + USE_SSL=1 - Request linking with libssl. USE_SSL is automatically + enabled if the build is configured with SSL support. The + variables that control USE_SSL=1 build are: + SSL_FLAGS='-DWITH_SSL=1' + SSL_CFLAGS='-DWITH_SSL=1' + SSL_LDFLAGS='-DWITH_SSL=1' + SSL_LIB='-lssl' + Using USE_SSL=1 adds to '-ssl' to PLUS_TARGET. + + Automatically intialized variables: + + TARGET=text - This variable is auto detected by using the compiler's + -dumpmachine output. To see the target on your machine run: + 'gcc -dumpmachine' + + PLUS_TARGET - This variable is added to TARGET and it is set depending + on the chosen USE_xxx flags. To disable adding + PLUS_TARGET to TARGET, set NO_PLUS_TARGET=1 + + BINDIR - The directory where final oscam binary would be put. The + default is: Distribution + + OSCAM_BIN=text - This variable controls how the oscam binary will be named. + Default OSCAM_BIN value is: + 'BINDIR/oscam-VERSVN_REV-TARGET' + Once the variables (BINDIR, VER, SVN_REV and TARGET) are + replaced, the resulting filename can look like this: + 'Distribution/oscam-1.20-unstable_svn7404-i486-slackware-linux-static' + For example you can run: 'make OSCAM_BIN=my-oscam' + + Binaries compiled and run during the OSCam build: + + OSCam builds webif/pages_gen binary that is run by the build system to + generate file that holds web pages. To build this binary two variables + are used: + + HOSTCC=gcc - The compiler used for building binaries that are run on + the build machine (the host). Default: gcc + To use clang for example run: make CC=clang HOSTCC=clang + + HOSTCFLAGS=xxx - The CFLAGS passed to HOSTCC. See webif/Makefile for the + default host cflags. + + Config targets: + make config - Start configuration utility. + make allyesconfig - Enable all configuration options. + make allnoconfig - Disable all configuration options. + make defconfig - Restore default configuration options. + + Cleaning targets: + make clean - Remove 'build' directory which contains compiled + object files. + make distclean - Executes clean target and also removes binary files + located in 'Distribution' directory. + + Build system files: + config.sh - OSCam configuration. Run 'config.sh --help' to see + available parameters or 'make config' to start GUI + configuratior. + Makefile - Main build system file. + Makefile.extra - Contains predefined targets. You can use this file + as example on how to use the build system. + Makefile.local - This file is included in Makefile and allows creation + of local build system targets. See Makefile.extra for + examples. + + Here are some of the interesting predefined targets in Makefile.extra. + To use them run 'make target ...' where ... can be any extra flag. For + example if you want to compile OSCam for Dreambox (DM500) but do not + have the compilers in the path, you can run: + make dm500 CROSS_DIR=/opt/cross/dm500/cdk/bin/ + + Predefined targets in Makefile.extra: + + make libusb - Builds OSCam with libusb support + make pcsc - Builds OSCam with PCSC support + make pcsc-libusb - Builds OSCam with PCSC and libusb support + make dm500 - Builds OSCam for Dreambox (DM500) + make sh4 - Builds OSCam for SH4 boxes + make azbox - Builds OSCam for AZBox STBs + make mca - Builds OSCam for Matrix Cam Air (MCA) + make coolstream - Builds OSCam for Coolstream + make dockstar - Builds OSCam for Dockstar + make qboxhd - Builds OSCam for QBoxHD STBs + make opensolaris - Builds OSCam for OpenSolaris + make uclinux - Builds OSCam for m68k uClinux + + Predefined targets for static builds: + make static - Builds OSCam statically + make static-libusb - Builds OSCam with libusb linked statically + make static-libcrypto - Builds OSCam with libcrypto linked statically + make static-ssl - Builds OSCam with SSL support linked statically + + Developer targets: + make tests - Builds 'tests.bin' binary + + Examples: + Build OSCam for SH4 (the compilers are in the path): + make CROSS=sh4-linux- + + Build OSCam for SH4 (the compilers are in not in the path): + make sh4 CROSS_DIR=/opt/STM/STLinux-2.3/devkit/sh4/bin/ + make CROSS_DIR=/opt/STM/STLinux-2.3/devkit/sh4/bin/ CROSS=sh4-linux- + make CROSS=/opt/STM/STLinux-2.3/devkit/sh4/bin/sh4-linux- + + Build OSCam for SH4 with STAPI: + make CROSS=sh4-linux- USE_STAPI=1 + + Build OSCam for SH4 with STAPI and changed configuration directory: + make CROSS=sh4-linux- USE_STAPI=1 CONF_DIR=/var/tuxbox/config + + Build OSCam for ARM with COOLAPI (coolstream aka NeutrinoHD): + make CROSS=arm-cx2450x-linux-gnueabi- USE_COOLAPI=1 + + Build OSCam for MIPSEL with AZBOX support: + make CROSS=mipsel-linux-uclibc- USE_AZBOX=1 + + Build OSCam for ARM with MCA support: + make CROSS=arm-none-linux-gnueabi- USE_MCA=1 + + Build OSCam with libusb and PCSC: + make USE_LIBUSB=1 USE_PCSC=1 + + Build OSCam with static libusb: + make USE_LIBUSB=1 LIBUSB_LIB="/usr/lib/libusb-1.0.a" + + Build OSCam with static libcrypto: + make USE_LIBCRYPTO=1 LIBCRYPTO_LIB="/usr/lib/libcrypto.a" + + Build OSCam with static libssl and libcrypto: + make USE_SSL=1 SSL_LIB="/usr/lib/libssl.a" LIBCRYPTO_LIB="/usr/lib/libcrypto.a" + + Build with verbose messages and size optimizations: + make V=1 CC_OPTS=-Os + + Build and set oscam file name: + make OSCAM_BIN=oscam + + Build and set oscam file name depending on revision: + make OSCAM_BIN=oscam-`./config.sh -r` + diff --git a/README.config b/README.config new file mode 100644 index 0000000..9b99c3c --- /dev/null +++ b/README.config @@ -0,0 +1,87 @@ +** This file is generated from 'config.sh --help' output, do not edit it. ** + +OSCam config +Usage: config.sh [parameters] + + -g, --gui Start interactive configuration + + -s, --show-enabled [param] Show enabled configuration options. + -Z, --show-disabled [param] Show disabled configuration options. + -S, --show-valid [param] Show valid configuration options. + Possible params: all, addons, protocols, + readers, card_readers + + -l, --list-config List active configuration variables. + -e, --enabled [option] Check if certain option is enabled. + -d, --disabled [option] Check if certain option is disabled. + + -E, --enable [option] Enable config option. + -D, --disable [option] Disable config option. + + The following [option]s enable or disable multiple settings. + all - Everything. + addons - All addons. + protocols - All protocols. + readers - All readers. + card_readers - All card readers. + + -R, --restore Restore default config. + + -cc, --create-cert [option] Create a new self signed X.509 certificate and private key. + + The following [option]s in this order are supported: + ecdsa|rsa - key type (default: ecdsa) + prime256v1|4096 - key length (default: prime256v1), any ecdsa curve or rsa length should work + ca - create Root CA certificates + subject - X.509 certificate subject e.g. 'My OSCam Distribution' + + -cf, --cert-file [option] Get filename of requested (cert|privkey) type. + -ci, --cert-info Get a list of useful certificate information. + -cl, --add-cert [option] Create symlinks to use a custom, pre-created set of X.509 certificate and private key. + + The following [option]s in this order are mandatory: + certificate filename - relative/absolute path to certificate file + private key filename - relative/absolute path to private key file + + -sm, --sign-marker Get Oscam binary signature marker. + -um, --upx-marker Get Oscam binary upx marker. + -v, --oscam-version Display OSCam version. + -c, --oscam-commit Display OSCam GIT short commit sha 8-digits. + + -O, --detect-osx-sdk-version Find where OS X SDK is located + + -h, --help Display this help text. + +Examples: + # Enable WEBIF and SSL + ./config.sh --enable WEBIF WITH_SSL + + # Disable WEBIF but enable WITH_SSL + ./config.sh --disable WEBIF --enable WITH_SSL + + # Restore defaults and disable WEBIF and READER_NAGRA + ./config.sh --restore --disable WEBIF READER_NAGRA + + # Use default config with only one enabled reader + ./config.sh --restore --disable readers --enable READER_BULCRYPT + + # Disable everything and enable webif one module and one card reader + ./config.sh --disable all --enable WEBIF MODULE_NEWCAMD READER_BULCRYPT + + # Disable all card readers except INTERNAL + ./config.sh -D card_readers -E CARDREADER_INTERNAL + + # Create new self signed private key and certificate with defaults + ./config.sh --create-cert + + # Create new self signed private key and certificate with custom settings + ./config.sh --create-cert rsa 4096 + + # Create new Root CA with private key and certificate with custom settings + ./config.sh --create-cert ecdsa prime256v1 ca 'My OSCam Distribution' + +Available options: + addons: WEBIF WEBIF_LIVELOG WEBIF_JQUERY WITH_COMPRESS_WEBIF WITH_SSL HAVE_DVBAPI WITH_EXTENDED_CW WITH_NEUTRINO READ_SDT_CHARSETS CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_ARM_NEON WITH_SIGNING WITH_EMU WITH_SOFTCAM + protocols: MODULE_CAMD33 MODULE_CAMD35 MODULE_CAMD35_TCP MODULE_NEWCAMD MODULE_CCCAM MODULE_CCCSHARE MODULE_GBOX MODULE_RADEGAST MODULE_SCAM MODULE_SERIAL MODULE_CONSTCW MODULE_PANDORA MODULE_GHTTP MODULE_STREAMRELAY + readers: READER_NAGRA READER_NAGRA_MERLIN READER_IRDETO READER_CONAX READER_CRYPTOWORKS READER_SECA READER_VIACCESS READER_VIDEOGUARD READER_DRE READER_TONGFANG READER_BULCRYPT READER_GRIFFIN READER_DGCRYPT + card_readers: CARDREADER_PHOENIX CARDREADER_INTERNAL CARDREADER_SC8IN1 CARDREADER_MP35 CARDREADER_SMARGO CARDREADER_DB2COM CARDREADER_STAPI CARDREADER_STAPI5 CARDREADER_STINGER CARDREADER_DRECAS diff --git a/README.dvbapi_protocol b/README.dvbapi_protocol new file mode 100644 index 0000000..ca77bb6 --- /dev/null +++ b/README.dvbapi_protocol @@ -0,0 +1,418 @@ +DVBAPI +====== +DVB API stands for Linux DVB Application Programming Interface, so in short it is a set of API calls which are used on +linux to handle DVB hardware. From the OSCam point of view the most interesting part is to be able to provide all data +necessary for channel decryption. The OSCam DVBAPI module was written to handle this work. + +Architecture +============ +A DVBAPI module needs the following information to decrypt a channel: +- PMT table (information from TV receiver software about the requested channel for livetv/recording and the ECM PIDs) +- CAT table (needed to get information about EMM type and PIDs) +- Filtered ECM/EMM data + +If OSCam is able to decrypt a service, information about the decrypted PIDs (audio, video, etc) and CW (keys) +is sent back to the TV receiver software from the CAM device. + +History +======= +The first and "standard" use case is probably Enigma. OSCam creates a /tmp/camd.socket. Enigma sends the PMT data to +this socket and as a result OSCam opens the necessary DVB demux devices (e.g. /dev/dvb/adapter0/demux0) and filters +for ECM, CAT and EMM data. These data are then parsed by the OSCam dvbapi module and as a result the CA_SET_PID and +CA_SET_DESCR ioctl calls are made, leading to proper decryption. All this was working on the same hardware and the +same DVB devices were used. This kind of usage was mainly for linux STB. + +Next step was generic PC support, by extending the dvbapi module to send PIDs and keys back to TV software (initially +via a special UDP socket, later via the same /tmp/camd.socket). The TV software was able to use this information in +software decryption (DeCSA). + +At some point, the OpenPLi team created a new CaPMT interface, which was then implemented in OSCam (as pmt_mode=6). +It is described here: http://wiki.openpli.org/caPMT +The main feature was reverting the roles: OSCam now acts as a client and connects to /tmp/.listen.camd.socket created +by Enigma. This way multiple Software CAMs could be running and connecting to Enigma's .listen.camd.socket. Another +important improvement in this mode (also implemented in OSCam) was the ability to handle extra CA_PMT list managements. +This allows to use one socket connection to handle more than one channel at a time (previously clients had to manage +a single connection to /tmp/camd.socket per subscribed channel). + +As the .listen.camd.socket mode makes less sense on generic PC platform (the OSCam is still server, while the client +could be any PC software used), the second feature which allows handling multiple channels on single socket connection +was extended to cover other modes (not only pmt_mode=6) in OSCam. + +Network mode +============ +The last feature that was added was a network mode. The change was made to be able to connect to an OSCam instance +which is not running on the same machine where the TV receiver software (and a DVB hardware) runs. + +Why not use dedicated protocols like newcamd/camd in such cases? +- To have ECM/EMM handling in OSCam, where it belongs. It is better maintained and fixes come in quicker. +- OSCam knows what readers it has, so it could do load balance/filtering/priorities etc. + +As a result, instead of /tmp/camd.socket (which could be used only on the same machine) a listening_socket parameter +was added. So the unix domain socket switched to a fully-featured TCP socket which can be connected from any network +client. + +As a result besides CA_SET_PID and CA_SET_DESCR new calls were passed to socket: DMX_SET_FILTER and DMX_STOP. The TV +receiver software has to filter the demux itself (according to the new calls above) and send results like ECM/EMM/CAT +data back to OSCam using the same connection. Because OSCam was only aware of PMT data on the socket, a new +DVBAPI_FILTER_DATA command (0xffff0000) was added to handle client data from filters. + +This way, communication between the TV receiver software and OSCam could be finally done using only one single TCP +connection. Moreover, the demux is only accessed by a single TV receiver software process, which from the architecture's +point of view is definitely a better solution. + +New protocol description (socket commands) +=========================================== +As there are more and more dvbapi clients, some problems start to appear. First of all there was some kind of mess +because OSCam's network mode doesn't take into account the endianness in first form of the network protocol. Second, +it was not consistant (e.g. PID was always send as little endian in DMX_STOP, while the rest depend on OSCam's host +architecture). Finally the standard API ioctl codes for CA_SET_PID, CA_SET_DESCR, DMX_SET_FILTER and DMX_STOP behave +differently. These codes are composed by a macro which takes into account the length of the associated structures and +on some hardware the first bits of the MSB was different. So the clients had to do some strange workarounds with +the MSB byte: fix 0x80 -> 0x40 and 0x20 -> 0x00 when needed + +Finally, the first byte sent to client was an adapter index, which was not always needed in all commands. Now the +first 4-byte integer is unique operation code, so a client will know what is the request type and could read the rest +of data according to the following description (and field sizes). + +To address all above problems and additionally make smooth transitions when updating the protocol in the future there +was added some kind of "handshake" for clients. All new implementations should use it. Currently the old and new +implementations should work fine, but in the future a network client which will not introduce itself (thus not providing +it's supported protocol version) may be dropped/ignored by OSCam. + +All multibyte integers (if not specified otherwise) should be send using network byte order, so your client should use +ntoh() functions when receiving. OSCam is doing hton() before sending and vice versa. + +Just right after a client connects to an OSCam network socket, it should send a greeting in format: + +-= DVBAPI_CLIENT_INFO =- +----------------------------------------------------------------------- +type/size description +----------------------------------------------------------------------- +uint32_t operation code -> DVBAPI_CLIENT_INFO +uint16_t protocol version supported by client +uint8_t size of followed string (255 bytes max) +string name and version of the client (string length should be max 255 bytes) + +The server will respond with a similar reply: + +-= DVBAPI_SERVER_INFO =- +----------------------------------------------------------------------- +type/size description +----------------------------------------------------------------------- +uint32_t operation code -> DVBAPI_SERVER_INFO +uint16_t protocol version supported by OSCam +uint8_t size of followed string (255 bytes max) +string OSCam version and build (string length should be max 255 bytes) + +Next, when a client wants to start a channel, it should send the PMT data (program map table). The PMT data structure +starts with constant AOT_CA_PMT (0x9F8032). The data format of the CA_PMT is described in chapter 8.4.3.4 (page 30) of +the EN 50221 PDF (european standard). + +------------------------------------------------------------------------------- + +Please note that OSCam is expecting a number of private descriptors injected +into the CA PMT data. They shall be located inside the program info loop. All +supported descriptors follow the structure defined in 2.6 of Rec. ITU H.222.0 +and they are the following: + +--------------------------------------------------------------------- +| descriptor_tag | Identification | Usage | +--------------------------------------------------------------------- +| 0x81 | enigma_namespace_descriptor | optional | +| 0x82 | demux_ca_mask_device_descriptor | deprecated | +| 0x83 | adapter_device_descriptor | mandatory | +| 0x84 | pmt_pid_descriptor | mandatory | +| 0x85 | service_type_mask_descriptor | optional | +| 0x86 | demux_device_descriptor | mandatory | +| 0x87 | ca_device_descriptor | mandatory | +--------------------------------------------------------------------- + +Descriptors marked as "mandatory" shall be present in the CA PMT message, in +order OSCam to get all necessary information for the specified program. Below +is a detailed description for each of the supported descriptors with its +structure and usage. + +------------------------------------------------------------------------------- + +1. Enigma namespace descriptor + +Provides additional information for the program specified, such as enigma +namespace (orbital position, frequency and polarization), transport stream id +and original network id. Although its presense in the CA PMT is optional, it +is advised to be included as OSCam utilizes these information in many aspects. + +--------------------------------------------------------------------- +| Syntax | No. of bits | Mnemonic | +--------------------------------------------------------------------- +| enigma_namespace_descriptor(){ | | | +| descriptor_tag | 8 | uimsbf | +| descriptor_length | 8 | uimsbf | +| enigma_namespace | 32 | uimsbf | +| transport_stream_id | 16 | uimsbf | +| original_network_id | 16 | uimsbf | +| } | | | +--------------------------------------------------------------------- + +decriptor_tag - The tag of the descriptor is equal to 0x81. + +descriptor_length - The length of the descriptor is equal to 0x08. + +ens - This is the enigma namespace as defined in OpenPLi, openATV and other +open enigma2 images. An example implementation can be found at: +https://github.com/OpenPLi/enigma2/blob/develop/lib/dvb/frontend.cpp#L476 + +transport_stream_id – The transport stream id as defined in various DVB SI +tables. + +original_network_id - The original network id as defined in various DVB SI +tables. + +------------------------------------------------------------------------------- + +2. Demux and ca mask device descriptor + +It was used to provide information about the demux device as well as the ca +device(s) carrying the specified program. It was created for the DM7025 set top +box and it is now considered deprecated. Many hosts use this descriptor in a +different way (carrying different information) than it was originaly designed +leading to confusion. OSCam will continue to support this descriptor as some +legacy hosts still use it. For newer hosts, the adapter_device_descriptor (tag +0x83), the demux_device_descriptor (tag 0x86) and the ca_device_descriptor (tag +0x87) shall be used for sending the necessary data to OSCam. + +--------------------------------------------------------------------- +| Syntax | No. of bits | Mnemonic | +--------------------------------------------------------------------- +| demux_ca_mask_device_descriptor(){ | | | +| descriptor_tag | 8 | uimsbf | +| descriptor_length | 8 | uimsbf | +| ca_mask | 8 | uimsbf | +| demux_device | 8 | uimsbf | +| } | | | +--------------------------------------------------------------------- + +decriptor_tag - The tag of the descriptor is equal to 0x82. + +descriptor_length - The length of the descriptor is equal to 0x02. + +ca_mask - It is a bit mask of the ca device(s) carrying the specified program. +Bit 0 corresonds to ca0, bit 1 corresponds to ca1 and so on. + +demux_device - The demux device that carries the specified program. It is +limited to values between 0x00 and 0x08 in older enigma2 images. + +------------------------------------------------------------------------------- + +3. Adapter device descriptor + +Provides information about the adapter device carrying the specified program. + +--------------------------------------------------------------------- +| Syntax | No. of bits | Mnemonic | +--------------------------------------------------------------------- +| adapter_device_descriptor(){ | | | +| descriptor_tag | 8 | uimsbf | +| descriptor_length | 8 | uimsbf | +| adapter_device | 8 | uimsbf | +| } | | | +--------------------------------------------------------------------- + +decriptor_tag - The tag of the descriptor is equal to 0x83. + +descriptor_length - The length of the descriptor is equal to 0x01. + +adapter_device - The adapter device that carries the specified program. It can +take values from 0x00 to 0xFF, thus a maximum number of different 256 adapters +are supported. + +------------------------------------------------------------------------------- + +4. PMT pid descriptor + +Provides information about the PMT pid of the specified program. + +--------------------------------------------------------------------- +| Syntax | No. of bits | Mnemonic | +--------------------------------------------------------------------- +| pmt_pid_descriptor(){ | | | +| descriptor_tag | 8 | uimsbf | +| descriptor_length | 8 | uimsbf | +| pmt_pid | 16 | uimsbf | +| } | | | +--------------------------------------------------------------------- + +decriptor_tag - The tag of the descriptor is equal to 0x84. + +descriptor_length - The length of the descriptor is equal to 0x02. + +pmt_pid - The pid that carries the PMT table of the specified program. + +------------------------------------------------------------------------------- + +5. Service type mask descriptor + +It provides information about the type (live tv, recording, streaming service, +or any combination) of the program specified. It's currently not unitilized in +OSCam and its usage in considered optional. + +--------------------------------------------------------------------- +| Syntax | No. of bits | Mnemonic | +--------------------------------------------------------------------- +| service_type_mask_descriptor(){ | | | +| descriptor_tag | 8 | uimsbf | +| descriptor_length | 8 | uimsbf | +| service_type_mask | 32 | uimsbf | +| } | | | +--------------------------------------------------------------------- + +decriptor_tag - The tag of the descriptor is equal to 0x85. + +descriptor_length - The length of the descriptor is equal to 0x04. + +service_type_mask - This is a bit mask of the different service types enabled +for the specified program. Service type values are defined in enigma2 and can +be found at https://github.com/OpenPLi/enigma2/blob/develop/lib/dvb/pmt.h#L135 + +------------------------------------------------------------------------------- + +6. Demux device descriptor + +It is used to provide information about the demux device carrying the specified +program. It is a replacement to the deprecated demux_ca_mask_device_descriptor +(tag 0x82), as it supports up to 256 different demux devices. + +--------------------------------------------------------------------- +| Syntax | No. of bits | Mnemonic | +--------------------------------------------------------------------- +| demux_device_descriptor(){ | | | +| descriptor_tag | 8 | uimsbf | +| descriptor_length | 8 | uimsbf | +| demux_device | 8 | uimsbf | +| } | | | +--------------------------------------------------------------------- + +decriptor_tag - The tag of the descriptor is equal to 0x86. + +descriptor_length - The length of the descriptor is equal to 0x01. + +demux_device - The demux device that carries the specified program. Its value +can be between 0x00 and 0xFF. + +------------------------------------------------------------------------------- + +7. Ca device descriptor + +This descriptor provides OSCam with information about the ca device carrying +the specified program. It is created as a replacement to the deprecated +demux_ca_mask_device_descriptor (tag 0x82). It has the following syntax: + +--------------------------------------------------------------------- +| Syntax | No. of bits | Mnemonic | +--------------------------------------------------------------------- +| ca_device_descriptor(){ | | | +| descriptor_tag | 8 | uimsbf | +| descriptor_length | 8 | uimsbf | +| ca_device | 8 | uimsbf | +| } | | | +--------------------------------------------------------------------- + +decriptor_tag - The tag of the descriptor is equal to 0x87. + +descriptor_length - The length of the descriptor is equal to 0x01. + +ca_device - The ca device that carries the specified program. Its value can be +between 0x00 and 0xFF (256 different ca devices supported). + +------------------------------------------------------------------------------- + +After OSCam parses the PMT data, it starts filtering ECM PIDs. It sends the following request to the client: + +-= DVBAPI_DMX_SET_FILTER =- +----------------------------------------------------------------------- +type/size description +----------------------------------------------------------------------- +uint32_t operation code -> DVBAPI_DMX_SET_FILTER +uint8_t adapter index +uint8_t demux index +uint8_t filter number +*** The following data are the fields from the dmx_sct_filter_params structure (added separately to avoid padding problems) +uint16_t pid +uint8_t[16] filter data (filter.filter) +uint8_t[16] filter mask (filter.mask) +uint8_t[16] filter mode (filter.mode) +uint32_t timeout +uint32_t flags + +The client should then filter the data and pass it back to OSCam using the following frame: + +-= DVBAPI_FILTER_DATA =- +----------------------------------------------------------------------- +type/size description +----------------------------------------------------------------------- +uint32_t operation code -> DVBAPI_FILTER_DATA +uint8_t demux index +uint8_t filter number +uint8_t[] filtered data from demux + +When OSCam is able to decrypt a channel, it initially sends a list of PIDs associated with the descrambler index using +this packet: + +-= DVBAPI_CA_SET_PID =- +----------------------------------------------------------------------- +type/size description +----------------------------------------------------------------------- +uint32_t operation code -> DVBAPI_CA_SET_PID +uint8_t adapter index +ca_pid_t 8-byte ca_pid_t structure (the pid and index fields are in network byte order) + +And also sends the CW for decryption: + +-= DVBAPI_CA_SET_DESCR =- +----------------------------------------------------------------------- +type/size description +----------------------------------------------------------------------- +uint32_t operation code -> DVBAPI_CA_SET_DESCR +uint8_t adapter index +ca_descr_t 16-byte ca_descr_t structure (the index and parity fields are in network byte order) + +When OSCam wants to inform the client about stopping a filter, it sends the following packet: + +-= DVBAPI_DMX_STOP =- +----------------------------------------------------------------------- +type/size description +----------------------------------------------------------------------- +uint32_t operation code -> DVBAPI_DMX_STOP +uint8_t adapter index +uint8_t demux index +uint8_t filter number +uint16_t PID to stop filtering + +When the client closes connection, all associated channels are stopped in OSCam. + +Alternatively when there is a need to stop channel decoding, while having the connection still open you can send a +special '3f' packed to OSCam. To stop decoding the specified demux, the following CA_PMT data should be sent to OSCam: +9F 80 3f 04 83 02 00 +If is 0xff, then it is parsed as a wildcard and all demuxers associated with the connection are stopped. + +In protocol version 2 the new packet with ECM info data was introduced: + +-= DVBAPI_ECM_INFO =- +----------------------------------------------------------------------- +type/size description +----------------------------------------------------------------------- +uint32_t operation code -> DVBAPI_ECM_INFO +uint8_t adapter index +uint16_t Service ID +uint16_t CAID +uint16_t PID +uint32_t Provider ID +uint32_t ECM time (ms) +uint8_t size of followed string (255 bytes max) +string cardsystem name (string length should be max 255 bytes) +uint8_t size of followed string (255 bytes max) +string reader name (string length should be max 255 bytes) +uint8_t size of followed string (255 bytes max) +string from - source name (string length should be max 255 bytes) +uint8_t size of followed string (255 bytes max) +string protocol name (string length should be max 255 bytes) +uint8_t hops (cccam & gbox; set to 0 otherwise) diff --git a/README.md b/README.md new file mode 100644 index 0000000..1df4a04 --- /dev/null +++ b/README.md @@ -0,0 +1,188 @@ +# OSCam with AI Fake DCW Detector + +![AI Fake DCW Detector](images/image1.jpg) + +## Overview + +This repository contains a modified version of **OSCam** enhanced with an advanced **AI-inspired Fake DCW Detection and Voting System**. + +The goal of this system is to improve stability and reliability when multiple Control Word (CW) sources are available, such as: + +- Local readers +- CacheEx peers +- CSP sources +- Virtual readers + +Instead of accepting the first CW received, this implementation collects multiple candidates and selects the most reliable one using weighted voting logic. + +--- + +# 🔍 Problem It Solves + +In multi-reader or CacheEx environments, fake or unstable DCWs can appear. + +Default behavior: +- First CW wins +- Possible glitches, freezing, or unstable decoding + +With AI Fake DCW Detector: +- Multiple CWs are collected +- Sources are tracked +- Votes are counted +- Local readers can be weighted higher +- Majority or timeout logic is applied +- A reliable CW is selected + +Result: + +✔ Reduced fake DCWs +✔ Increased decoding stability +✔ Better CacheEx reliability +✔ Smarter CW selection + +--- + +# 🧠 Core Functions + +## 1️⃣ `cw_vote_add()` + +This function is called whenever a new CW is received. + +It: + +- Compares the CW with existing candidates +- Increases vote count if it matches +- Adds a new candidate if it is unique +- Tracks: + - Total votes + - Local votes + - Reader source + - CW value +- Identifies whether the source is: + - Local reader + - Virtual reader + - CacheEx client + - CSP source + +Each CW candidate is stored in a voting pool. + +--- + +## 2️⃣ `cw_vote_decide()` + +This function determines the final CW to use. + +### Decision Process + +1. Count total votes +2. Verify minimum vote requirement +3. Calculate effective score: +4. Check for majority (> 50%) +5. Apply timeout logic +6. Apply fallback strategy if necessary + +If a winner is found: + +- CW is copied into `er->cw` +- CacheEx hit statistics are updated +- Optional logging is performed + +If no clear winner exists, the system waits for more votes unless timeout rules apply. + +--- + +# ⚙ Configuration Options + +| Parameter | Description | +|------------|------------| +| `cwvote_enabled` | Enable/disable voting system | +| `cwvote_max_candidates` | Maximum CW candidates stored | +| `cwvote_compare_len` | Number of bytes used for CW comparison | +| `cwvote_local_weight` | Weight multiplier for local votes | +| `cwvote_min_votes` | Minimum required votes before decision | +| `cwvote_timeout` | Timeout in milliseconds | +| `cwvote_fallback` | Fallback mode (1 = best candidate, 2 = first candidate) | +| `cwvote_log_enabled` | Enable detailed debug logging | + +--- + +# 🏆 Winner Selection Logic + +A CW is selected if: + +- It has a strict majority (> 50%) +OR +- Timeout is reached and fallback mode allows selection + +If timeout occurs and: + +- `fallback = 1` → best scoring candidate is selected +- `fallback = 2` → first candidate (slot 0) is selected + +--- + +# 📊 Intelligent Source Awareness + +The system distinguishes between: + +- Trusted local readers +- Virtual readers +- CacheEx clients +- CSP sources + +Local readers can be weighted higher using `cwvote_local_weight`, improving reliability in hybrid environments. + +--- + +# 📈 CacheEx Integration + +If the winning CW originates from CacheEx: + +- `cwcacheexhit` counters are incremented +- Client and account statistics are updated +- Global hit statistics are updated + +This improves monitoring accuracy and performance analysis. + +--- + +# 🚀 Advantages + +- Detects and filters fake DCWs +- Improves stability in multi-reader setups +- Reduces glitching and freezing +- Configurable and flexible logic +- Seamlessly integrated into OSCam core + +--- + +# 🧩 AI Concept + +Although not based on machine learning, this system applies AI-style decision logic: + +- Aggregates multiple inputs +- Applies weighted scoring +- Uses majority validation +- Implements adaptive timeout fallback + +It behaves like deterministic intelligence for CW selection. + +--- + +# Intended Use + +Designed for: + +- Emulated environments +- CacheEx-heavy setups +- Multi-reader configurations +- Advanced OSCam tuning +- High-stability streaming environments + +--- + +# License + +Based on the OSCam open-source project. +This repository includes custom modifications implementing advanced CW voting and fake DCW detection logic. + diff --git a/SoftCam.Key b/SoftCam.Key new file mode 100644 index 0000000..e69de29 diff --git a/config.h b/config.h new file mode 100644 index 0000000..7391dc3 --- /dev/null +++ b/config.h @@ -0,0 +1,110 @@ +#ifndef CONFIG_H_ +#define CONFIG_H_ + +#define WITH_EMU 1 +#define WITH_SOFTCAM 1 +#define WEBIF 1 +#define WEBIF_LIVELOG 1 +#define WEBIF_JQUERY 1 +#define WEBIF_WIKI 1 +//#define WITH_COMPRESS_WEBIF 1 +//#define WITH_SSL 1 +#if defined(__linux__) || defined(__CYGWIN__) +//#define HAVE_DVBAPI 1 +#define WITH_EXTENDED_CW 1 +#endif +//#define WITH_NEUTRINO 1 +#define READ_SDT_CHARSETS 1 +//#define CLOCKFIX 1 +//#define CS_ANTICASC 1 +#define WITH_DEBUG 1 +#define WITH_LB 1 +#define CS_CACHEEX 1 +#define CS_CACHEEX_AIO 1 +#define CW_CYCLE_CHECK 1 +//#define LCDSUPPORT 1 +//#define LEDSUPPORT 1 +#define IPV6SUPPORT 1 +//#define WITH_ARM_NEON 1 +//#define WITH_SIGNING 1 +//#define MODULE_MONITOR 1 + +#define MODULE_CAMD33 1 +//#define MODULE_CAMD35 1 +#define MODULE_CAMD35_TCP 1 +#define MODULE_NEWCAMD 1 +#define MODULE_CCCAM 1 +#define MODULE_CCCSHARE 1 +//#define MODULE_GBOX 1 +//#define MODULE_RADEGAST 1 +//#define MODULE_SERIAL 1 +//#define MODULE_CONSTCW 1 +//#define MODULE_PANDORA 1 +//#define MODULE_GHTTP 1 +//#define MODULE_SCAM 1 +#define MODULE_STREAMRELAY 1 + +#define WITH_CARDREADER 1 + +#ifdef WITH_CARDREADER + +//#define READER_NAGRA 1 +//#define READER_NAGRA_MERLIN 1 +//#define READER_IRDETO 1 +//#define READER_CONAX 1 +//#define READER_CRYPTOWORKS 1 +//#define READER_SECA 1 +#define READER_VIACCESS 1 +//#define READER_VIDEOGUARD 1 +//#define READER_DRE 1 +//#define READER_TONGFANG 1 +//#define READER_BULCRYPT 1 +//#define READER_GRIFFIN 1 +//#define READER_DGCRYPT 1 + +//#define CARDREADER_PHOENIX 1 +//#define CARDREADER_INTERNAL 1 +//#define CARDREADER_MP35 1 +//#define CARDREADER_SC8IN1 1 +//#define CARDREADER_SMARGO 1 +//#define CARDREADER_DB2COM 1 +//#define CARDREADER_STINGER 1 +//#define CARDREADER_DRECAS 1 + +#ifdef WITH_PCSC +//#define CARDREADER_PCSC 1 +#endif + +#ifdef WITH_LIBUSB +#define CARDREADER_SMART 1 +#endif + +// CARDREADER_INTERNAL_{AZBOX,COOLAPI,AMSMC,SCI} are internal variables +// do not touch them +#if (defined(CARDREADER_INTERNAL) && defined(WITH_AZBOX)) +#define CARDREADER_INTERNAL_AZBOX 1 +#elif (defined(CARDREADER_INTERNAL) && (defined(WITH_COOLAPI) || defined(WITH_SU980))) +#define CARDREADER_INTERNAL_COOLAPI 1 +#elif defined(CARDREADER_INTERNAL) && defined(WITH_COOLAPI2) +#define CARDREADER_INTERNAL_COOLAPI2 1 +#elif defined(CARDREADER_INTERNAL) && defined(WITH_AMSMC) +#define CARDREADER_INTERNAL_AMSMC 1 +#elif defined(CARDREADER_INTERNAL) +#define CARDREADER_INTERNAL_SCI 1 +#endif + +#ifdef WITH_STAPI +//#define CARDREADER_STAPI 1 +#endif + +#ifdef WITH_STAPI5 +//#define CARDREADER_STAPI5 1 +#endif + +#ifdef READER_DRE +#define READER_DRECAS 1 +#endif + +#endif // WITH_CARDREADER + +#endif //OSCAM_CONFIG_H_ diff --git a/config.sh b/config.sh new file mode 100644 index 0000000..e84089a --- /dev/null +++ b/config.sh @@ -0,0 +1,1015 @@ +#!/bin/sh + +addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY WEBIF_WIKI WITH_COMPRESS_WEBIF WITH_SSL HAVE_DVBAPI WITH_EXTENDED_CW WITH_NEUTRINO READ_SDT_CHARSETS CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_ARM_NEON WITH_SIGNING WITH_EMU WITH_SOFTCAM" +protocols="MODULE_CAMD33 MODULE_CAMD35 MODULE_CAMD35_TCP MODULE_NEWCAMD MODULE_CCCAM MODULE_CCCSHARE MODULE_GBOX MODULE_RADEGAST MODULE_SCAM MODULE_SERIAL MODULE_CONSTCW MODULE_PANDORA MODULE_GHTTP MODULE_STREAMRELAY" +readers="READER_NAGRA READER_NAGRA_MERLIN READER_IRDETO READER_CONAX READER_CRYPTOWORKS READER_SECA READER_VIACCESS READER_VIDEOGUARD READER_DRE READER_TONGFANG READER_BULCRYPT READER_GRIFFIN READER_DGCRYPT" +card_readers="CARDREADER_PHOENIX CARDREADER_INTERNAL CARDREADER_SC8IN1 CARDREADER_MP35 CARDREADER_SMARGO CARDREADER_DB2COM CARDREADER_STAPI CARDREADER_STAPI5 CARDREADER_STINGER CARDREADER_DRECAS" + +defconfig=" +CONFIG_WEBIF=y +CONFIG_WEBIF_LIVELOG=y +CONFIG_WEBIF_JQUERY=y +# CONFIG_WEBIF_WIKI=n +CONFIG_WITH_COMPRESS_WEBIF=y +# CONFIG_WITH_SSL=n +CONFIG_HAVE_DVBAPI=y +CONFIG_WITH_EXTENDED_CW=y +# CONFIG_WITH_NEUTRINO=n +CONFIG_READ_SDT_CHARSETS=y +# CONFIG_CS_ANTICASC=n +CONFIG_WITH_DEBUG=y +CONFIG_MODULE_MONITOR=y +CONFIG_WITH_LB=y +# CONFIG_CS_CACHEEX=n +# CONFIG_CS_CACHEEX_AIO=n +# CONFIG_CW_CYCLE_CHECK=n +# CONFIG_LCDSUPPORT=n +# CONFIG_LEDSUPPORT=n +# CONFIG_CLOCKFIX=n +# CONFIG_IPV6SUPPORT=n +# CONFIG_WITH_ARM_NEON=n +# CONFIG_WITH_SIGNING=n +CONFIG_WITH_EMU=y +CONFIG_WITH_SOFTCAM=y +# CONFIG_MODULE_CAMD33=n +CONFIG_MODULE_CAMD35=y +CONFIG_MODULE_CAMD35_TCP=y +CONFIG_MODULE_NEWCAMD=y +CONFIG_MODULE_CCCAM=y +CONFIG_MODULE_CCCSHARE=y +CONFIG_MODULE_GBOX=y +# CONFIG_MODULE_RADEGAST=n +# CONFIG_MODULE_SERIAL=n +# CONFIG_MODULE_CONSTCW=n +# CONFIG_MODULE_PANDORA=n +# CONFIG_MODULE_SCAM=n +# CONFIG_MODULE_GHTTP=n +# CONFIG_MODULE_STREAMRELAY=n +CONFIG_WITH_CARDREADER=y +CONFIG_READER_NAGRA_COMMON=y +CONFIG_READER_NAGRA=y +CONFIG_READER_NAGRA_MERLIN=y +CONFIG_READER_IRDETO=y +CONFIG_READER_CONAX=y +CONFIG_READER_CRYPTOWORKS=y +CONFIG_READER_SECA=y +CONFIG_READER_VIACCESS=y +CONFIG_READER_VIDEOGUARD=y +CONFIG_READER_DRE=y +CONFIG_READER_TONGFANG=y +CONFIG_READER_BULCRYPT=y +CONFIG_READER_GRIFFIN=y +CONFIG_READER_DGCRYPT=y +CARDREADER_PHOENIX=y +# CARDREADER_DRECAS=n +CARDREADER_INTERNAL=y +# CARDREADER_SC8IN1=n +# CARDREADER_MP35=n +# CARDREADER_SMARGO=n +# CARDREADER_DB2COM=n +# CARDREADER_STAPI=n +# CARDREADER_STAPI5=n +# CARDREADER_STINGER=n +" + +usage() { + echo \ +"OSCam config +Usage: `basename $0` [parameters] + + -g, --gui Start interactive configuration + + -s, --show-enabled [param] Show enabled configuration options. + -Z, --show-disabled [param] Show disabled configuration options. + -S, --show-valid [param] Show valid configuration options. + Possible params: all, addons, protocols, + readers, card_readers + + -l, --list-config List active configuration variables. + -e, --enabled [option] Check if certain option is enabled. + -d, --disabled [option] Check if certain option is disabled. + + -E, --enable [option] Enable config option. + -D, --disable [option] Disable config option. + + The following [option]s enable or disable multiple settings. + all - Everything. + addons - All addons. + protocols - All protocols. + readers - All readers. + card_readers - All card readers. + + -R, --restore Restore default config. + + -cc, --create-cert [option] Create a new self signed X.509 certificate and private key. + + The following [option]s in this order are supported: + ecdsa|rsa - key type (default: ecdsa) + prime256v1|4096 - key length (default: prime256v1), any ecdsa curve or rsa length should work + ca - create Root CA certificates + subject - X.509 certificate subject e.g. 'My OSCam Distribution' + + -cf, --cert-file [option] Get filename of requested (cert|privkey) type. + -ci, --cert-info Get a list of useful certificate information. + -cl, --add-cert [option] Create symlinks to use a custom, pre-created set of X.509 certificate and private key. + + The following [option]s in this order are mandatory: + certificate filename - relative/absolute path to certificate file + private key filename - relative/absolute path to private key file + + -sm, --sign-marker Get Oscam binary signature marker. + -um, --upx-marker Get Oscam binary upx marker. + -v, --oscam-version Display OSCam version. + -c, --oscam-commit Display OSCam GIT short commit sha 8-digits. + + -O, --detect-osx-sdk-version Find where OS X SDK is located + + -h, --help Display this help text. + +Examples: + # Enable WEBIF and SSL + ./config.sh --enable WEBIF WITH_SSL + + # Disable WEBIF but enable WITH_SSL + ./config.sh --disable WEBIF --enable WITH_SSL + + # Restore defaults and disable WEBIF and READER_NAGRA + ./config.sh --restore --disable WEBIF READER_NAGRA + + # Use default config with only one enabled reader + ./config.sh --restore --disable readers --enable READER_BULCRYPT + + # Disable everything and enable webif one module and one card reader + ./config.sh --disable all --enable WEBIF MODULE_NEWCAMD READER_BULCRYPT + + # Disable all card readers except INTERNAL + ./config.sh -D card_readers -E CARDREADER_INTERNAL + + # Create new self signed private key and certificate with defaults + ./config.sh --create-cert + + # Create new self signed private key and certificate with custom settings + ./config.sh --create-cert rsa 4096 + + # Create new Root CA with private key and certificate with custom settings + ./config.sh --create-cert ecdsa prime256v1 ca 'My OSCam Distribution' + +Available options: + addons: $addons + protocols: $protocols + readers: $readers + card_readers: $card_readers +" +} + +# Output directory for config.mak set by --objdir parameter +OBJDIR=. + +# Use flags set by --use-flags parameter +USE_FLAGS= + +# Used by --cert, --get-cert parameter +CERT_DIR="./certs" +CERT_PRIVATE_KEY="private.key" +CERT_X509="certificate.crt" + +have_flag() { + for FLAG in $USE_FLAGS + do + [ "$FLAG" = "$1" ] && return 0 + done + return 1 +} + +have_all_flags() { + for opt ; do + have_flag $opt || return 1 + done + return 0 +} + +have_any_flags() { + for opt ; do + have_flag $opt && return 0 + done + return 1 +} + +not_have_flag() { + for FLAG in $USE_FLAGS + do + [ "$FLAG" = "$1" ] && return 1 + done + return 0 +} + +not_have_all_flags() { + for opt ; do + not_have_flag $opt || return 1 + done + return 0 +} + +not_have_any_flags() { + for opt ; do + not_have_flag $opt && return 0 + done + return 1 +} + +# Config functions +enabled() { + grep "^\#define $1 1$" config.h >/dev/null 2>/dev/null + return $? +} + +disabled() { + grep "^\#define $1 1$" config.h >/dev/null 2>/dev/null + test $? = 0 && return 1 + return 0 +} + +enabled_all() { + for opt ; do + enabled $opt || return 1 + done + return 0 +} + +disabled_all() { + for opt ; do + disabled $opt || return 1 + done + return 0 +} + +enabled_any() { + for opt ; do + enabled $opt && return 0 + done + return 1 +} + +disabled_any() { + for opt ; do + disabled $opt && return 0 + done + return 1 +} + +list_enabled() { + for OPT in $@ + do + enabled $OPT && echo $OPT + done +} + +list_disabled() { + for OPT in $@ + do + disabled $OPT && echo $OPT + done +} + +write_enabled() { + defined_file="webif/is_defined.txt" + pages_c="webif/pages.c" + rm -f $defined_file $pages_c 2>/dev/null + for OPT in $(get_opts) WITH_CARDREADER + do + enabled $OPT && printf "%s\n" $OPT >> $defined_file + done + # Handle USE_FLAG based card readers for webif template filtering + have_flag USE_PCSC && printf "%s\n" CARDREADER_PCSC >> $defined_file +} + +valid_opt() { + [ "$1" = "WITH_CARDREADER" ] && return 0 # Special case + echo $addons $protocols $readers $card_readers | grep -w "$1" >/dev/null + return $? +} + +enable_opt() { + valid_opt $1 && disabled $1 && { + sed -e "s|//#define $1 1$|#define $1 1|g" config.h > config.h.tmp && \ + mv config.h.tmp config.h + echo "Enable $1" + } +} + +enable_opts() { + for OPT in $@ + do + enable_opt $OPT + done +} + +disable_opt() { + valid_opt $1 && enabled $1 && { + sed -e "s|#define $1 1$|//#define $1 1|g" config.h > config.h.tmp && \ + mv config.h.tmp config.h + echo "Disable $1" + } +} + +disable_opts() { + for OPT in $@ + do + disable_opt $OPT + done +} + +get_opts() { + OPTS="" + case "$1" in + 'addons') OPTS="$addons" ; ;; + 'protocols') OPTS="$protocols" ; ;; + 'readers') OPTS="$readers" ; ;; + 'card_readers') OPTS="$card_readers" ; ;; + *) OPTS="$addons $protocols $readers $card_readers" ; ;; + esac + echo $OPTS +} + +update_deps() { + # Calculate dependencies + enabled_any $(get_opts readers) $(get_opts card_readers) WITH_EMU && enable_opt WITH_CARDREADER >/dev/null + disabled_all $(get_opts readers) $(get_opts card_readers) WITH_EMU && disable_opt WITH_CARDREADER >/dev/null + disabled WEBIF && disable_opt WEBIF_LIVELOG >/dev/null + disabled WEBIF && disable_opt WEBIF_JQUERY >/dev/null + disabled WEBIF && disable_opt WEBIF_WIKI >/dev/null + have_flag USE_COMPRESS && disable_opt WITH_COMPRESS_WEBIF >/dev/null + enabled MODULE_CCCSHARE && enable_opt MODULE_CCCAM >/dev/null + enabled_any CARDREADER_DB2COM CARDREADER_MP35 CARDREADER_SC8IN1 CARDREADER_STINGER && enable_opt CARDREADER_PHOENIX >/dev/null + enabled CS_CACHEEX_AIO && enable_opt CS_CACHEEX >/dev/null + enabled WITH_SIGNING && enable_opt WITH_SSL >/dev/null + enabled WITH_EMU && enable_opt READER_VIACCESS >/dev/null + enabled WITH_EMU && enable_opt MODULE_NEWCAMD >/dev/null + disabled WITH_EMU && disable_opt WITH_SOFTCAM >/dev/null +} + +list_config() { + update_deps + # Handle use flags + have_flag USE_STAPI && echo "CONFIG_WITH_STAPI=y" || echo "# CONFIG_WITH_STAPI=n" + have_flag USE_STAPI5 && echo "CONFIG_WITH_STAPI5=y" || echo "# CONFIG_WITH_STAPI5=n" + have_flag USE_COOLAPI && echo "CONFIG_WITH_COOLAPI=y" || echo "# CONFIG_WITH_COOLAPI=n" + have_flag USE_COOLAPI2 && echo "CONFIG_WITH_COOLAPI2=y" || echo "# CONFIG_WITH_COOLAPI2=n" + have_flag USE_SU980 && echo "CONFIG_WITH_SU980=y" || echo "# CONFIG_WITH_SU980=n" + have_flag USE_AZBOX && echo "CONFIG_WITH_AZBOX=y" || echo "# CONFIG_WITH_AZBOX=n" + have_flag USE_AMSMC && echo "CONFIG_WITH_AMSMC=y" || echo "# CONFIG_WITH_AMSMC=n" + have_flag USE_MCA && echo "CONFIG_WITH_MCA=y" || echo "# CONFIG_WITH_MCA=n" + have_flag USE_LIBCRYPTO && echo "CONFIG_WITH_LIBCRYPTO=y" || echo "# CONFIG_WITH_LIBCRYPTO=n" + for OPT in $addons $protocols WITH_CARDREADER $readers + do + enabled $OPT && echo "CONFIG_$OPT=y" || echo "# CONFIG_$OPT=n" + done + for OPT in $card_readers + do + if [ $OPT = CARDREADER_INTERNAL ] + then + # Internal card reader is actually three different readers depending on USE flags + enabled $OPT && have_flag USE_AZBOX && echo "CONFIG_${OPT}_AZBOX=y" || echo "# CONFIG_${OPT}_AZBOX=n" + enabled $OPT && have_any_flags USE_COOLAPI USE_SU980 && echo "CONFIG_${OPT}_COOLAPI=y" || echo "# CONFIG_${OPT}_COOLAPI=n" + enabled $OPT && have_flag USE_COOLAPI2 && echo "CONFIG_${OPT}_COOLAPI2=y" || echo "# CONFIG_${OPT}_COOLAPI2=n" + enabled $OPT && have_flag USE_AMSMC && echo "CONFIG_${OPT}_AMSMC=y" || echo "# CONFIG_${OPT}_AMSMC=n" + enabled $OPT && not_have_all_flags USE_AZBOX USE_COOLAPI USE_COOLAPI2 USE_SU980 USE_AMSMC && echo "CONFIG_${OPT}_SCI=y" || echo "# CONFIG_${OPT}_SCI=n" + continue + fi + if [ $OPT = CARDREADER_STAPI ] + then + # Enable CARDREADER_STAPI only if USE_STAPI is set + enabled $OPT && have_flag USE_STAPI && echo "CONFIG_$OPT=y" || echo "# CONFIG_$OPT=n" + continue + fi + if [ $OPT = CARDREADER_STAPI5 ] + then + # Enable CARDREADER_STAPI5 only if USE_STAPI5 is set + enabled $OPT && have_flag USE_STAPI5 && echo "CONFIG_$OPT=y" || echo "# CONFIG_$OPT=n" + continue + fi + enabled $OPT && echo "CONFIG_$OPT=y" || echo "# CONFIG_$OPT=n" + done + have_flag USE_LIBUSB && echo "CONFIG_CARDREADER_SMART=y" || echo "# CONFIG_CARDREADER_SMART=n" + have_flag USE_PCSC && echo "CONFIG_CARDREADER_PCSC=y" || echo "# CONFIG_CARDREADER_PCSC=n" + # Extra modules/libraries + enabled_any MODULE_GBOX WITH_COMPRESS_WEBIF && echo "CONFIG_LIB_MINILZO=y" || echo "# CONFIG_LIB_MINILZO=n" + not_have_flag USE_LIBCRYPTO && echo "CONFIG_LIB_AES=y" || echo "# CONFIG_LIB_AES=n" + enabled MODULE_CCCAM && echo "CONFIG_LIB_RC6=y" || echo "# CONFIG_LIB_RC6=n" + not_have_flag USE_LIBCRYPTO && enabled MODULE_CCCAM && echo "CONFIG_LIB_SHA1=y" || echo "# CONFIG_LIB_SHA1=n" + enabled_any READER_DRE MODULE_SCAM READER_VIACCESS READER_NAGRA READER_NAGRA_MERLIN READER_VIDEOGUARD READER_CONAX READER_TONGFANG WITH_EMU && echo "CONFIG_LIB_DES=y" || echo "# CONFIG_LIB_DES=n" + enabled_any MODULE_CCCAM READER_NAGRA READER_NAGRA_MERLIN READER_SECA WITH_EMU && echo "CONFIG_LIB_IDEA=y" || echo "# CONFIG_LIB_IDEA=n" + not_have_flag USE_LIBCRYPTO && enabled_any READER_CONAX READER_CRYPTOWORKS READER_NAGRA READER_NAGRA_MERLIN WITH_EMU && echo "CONFIG_LIB_BIGNUM=y" || echo "# CONFIG_LIB_BIGNUM=n" + enabled READER_NAGRA_MERLIN && echo "CONFIG_LIB_MDC2=y" || echo "# CONFIG_LIB_MDC2=n" + enabled READER_NAGRA_MERLIN && echo "CONFIG_LIB_FAST_AES=y" || echo "# CONFIG_LIB_FAST_AES=n" + enabled_any READER_NAGRA_MERLIN WITH_SIGNING && echo "CONFIG_LIB_SHA256=y" || echo "# CONFIG_LIB_SHA256=n" + enabled_any READER_NAGRA READER_NAGRA_MERLIN && echo "CONFIG_READER_NAGRA_COMMON=y" || echo "# CONFIG_READER_NAGRA_COMMON=n" +} + +make_config_c() { + OPENSSL=$(which openssl 2>/dev/null) + if [ "$OPENSSL" = "" ] + then + echo "// openssl not found!" + echo "const char *config_mak = \"CFG: openssl not found in PATH!\";" + else + echo "// This file is generated by ./config.sh --objdir $OBJDIR --make-config.mak" + printf "const char *config_ssl = \"$($OPENSSL version | head -n 1 | awk -F'(' '{ print $1 }' | xargs)\";\n\n" + echo "const char *config_mak =" + printf " \"\\\nCFG: strings FILE | sed -n 's/^CFG~//p' | openssl enc -d -base64 | gzip -d\\\n\"\n" + gzip -9 < $OBJDIR/config.mak | $OPENSSL enc -base64 | while read LINE + do + printf " \"CFG~%s\\\\n\"\n" "$LINE" + done + echo " ;" + fi + + if enabled WITH_SIGNING + then + printf "\nconst char *config_cert =\n" + cat $(cert_file 'cert') | while read LINE + do + printf "\"%s\\\\n\"\n" "$LINE" + done + echo " ;" + fi +} + +make_config_mak() { + TMPFILE=$(mktemp -t config.mak.XXXXXX) || exit 1 + list_config > $TMPFILE + [ ! -d $OBJDIR ] && mkdir -p $OBJDIR 2>/dev/null + cmp $TMPFILE $OBJDIR/config.mak >/dev/null 2>/dev/null + if [ $? != 0 ] + then + cat $TMPFILE > $OBJDIR/config.mak + make_config_c > $OBJDIR/config.c + else + make_config_c > $TMPFILE + cmp $TMPFILE $OBJDIR/config.c >/dev/null 2>/dev/null + [ $? != 0 ] && cat $TMPFILE > $OBJDIR/config.c + fi + rm -rf $TMPFILE + write_enabled +} + +check_test() { + if [ "$(cat $configfile | grep "^#define $1 1$")" != "" ]; then + echo "on" + else + echo "off" + fi +} + +disable_all() { + for i in $1; do + sed -e "s/^#define ${i} 1$/\/\/#define ${i} 1/g" $tempfileconfig > ${tempfileconfig}.tmp && \ + mv ${tempfileconfig}.tmp $tempfileconfig + done +} + +enable_package() { + for i in $(cat $tempfile); do + strip=$(echo $i | sed "s/\"//g") + sed -e "s/\/\/#define ${strip} 1$/#define ${strip} 1/g" $tempfileconfig > ${tempfileconfig}.tmp && \ + mv ${tempfileconfig}.tmp $tempfileconfig + done +} + +print_components() { + clear + echo "You have selected the following components:" + echo + echo "Add-ons:" + for i in $addons; do + printf "\t%-20s: %s\n" $i $(check_test "$i") + done + + echo + echo "Protocols:" + for i in $protocols; do + printf "\t%-20s: %s\n" $i $(check_test "$i") + done + + echo + echo "Readers:" + for i in $readers; do + printf "\t%-20s: %s\n" $i $(check_test "$i") + done + + echo + echo "Card readers:" + for i in $card_readers; do + printf "\t%-20s: %s\n" $i $(check_test "$i") + done +} + +menu_addons() { + ${DIALOG} --checklist "\nChoose add-ons:\n " $height $width $listheight \ + WEBIF "Web Interface" $(check_test "WEBIF") \ + WEBIF_LIVELOG "LiveLog" $(check_test "WEBIF_LIVELOG") \ + WEBIF_JQUERY "Jquery onboard (if disabled webload)" $(check_test "WEBIF_JQUERY") \ + WEBIF_WIKI "Embedded wiki help system" $(check_test "WEBIF_WIKI") \ + WITH_COMPRESS_WEBIF "Compress webpages" $(check_test "WITH_COMPRESS_WEBIF") \ + WITH_SSL "OpenSSL support" $(check_test "WITH_SSL") \ + HAVE_DVBAPI "DVB API" $(check_test "HAVE_DVBAPI") \ + WITH_EXTENDED_CW "DVB API EXTENDED CW API" $(check_test "WITH_EXTENDED_CW") \ + WITH_NEUTRINO "Neutrino support" $(check_test "WITH_NEUTRINO") \ + READ_SDT_CHARSETS "DVB API read-sdt charsets" $(check_test "READ_SDT_CHARSETS") \ + CS_ANTICASC "Anti cascading" $(check_test "CS_ANTICASC") \ + WITH_DEBUG "Debug messages" $(check_test "WITH_DEBUG") \ + MODULE_MONITOR "Monitor" $(check_test "MODULE_MONITOR") \ + WITH_LB "Loadbalancing" $(check_test "WITH_LB") \ + CS_CACHEEX "Cache exchange" $(check_test "CS_CACHEEX") \ + CS_CACHEEX_AIO "Cache exchange aio (depend on Cache exchange)" $(check_test "CS_CACHEEX_AIO") \ + CW_CYCLE_CHECK "CW Cycle Check" $(check_test "CW_CYCLE_CHECK") \ + LCDSUPPORT "LCD support" $(check_test "LCDSUPPORT") \ + LEDSUPPORT "LED support" $(check_test "LEDSUPPORT") \ + CLOCKFIX "Clockfix (disable on old systems!)" $(check_test "CLOCKFIX") \ + IPV6SUPPORT "IPv6 support (experimental)" $(check_test "IPV6SUPPORT") \ + WITH_ARM_NEON "ARM NEON (SIMD/MPE) support" $(check_test "WITH_ARM_NEON") \ + WITH_SIGNING "Binary signing with X.509 certificate" $(check_test "WITH_SIGNING") \ + WITH_EMU "Emulator support" $(check_test "WITH_EMU") \ + WITH_SOFTCAM "Built-in SoftCam.Key" $(check_test "WITH_SOFTCAM") \ + 2> ${tempfile} + + opt=${?} + if [ $opt != 0 ]; then return; fi + + disable_all "$addons" + enable_package +} + +menu_protocols() { + ${DIALOG} --checklist "\nChoose protocols:\n " $height $width $listheight \ + MODULE_CAMD33 "camd 3.3" $(check_test "MODULE_CAMD33") \ + MODULE_CAMD35 "camd 3.5 UDP" $(check_test "MODULE_CAMD35") \ + MODULE_CAMD35_TCP "camd 3.5 TCP" $(check_test "MODULE_CAMD35_TCP") \ + MODULE_NEWCAMD "newcamd" $(check_test "MODULE_NEWCAMD") \ + MODULE_CCCAM "CCcam" $(check_test "MODULE_CCCAM") \ + MODULE_CCCSHARE "CCcam share" $(check_test "MODULE_CCCSHARE") \ + MODULE_GBOX "gbox" $(check_test "MODULE_GBOX") \ + MODULE_RADEGAST "radegast" $(check_test "MODULE_RADEGAST") \ + MODULE_SERIAL "Serial" $(check_test "MODULE_SERIAL") \ + MODULE_CONSTCW "constant CW" $(check_test "MODULE_CONSTCW") \ + MODULE_PANDORA "Pandora" $(check_test "MODULE_PANDORA") \ + MODULE_GHTTP "Ghttp" $(check_test "MODULE_GHTTP") \ + MODULE_SCAM "scam" $(check_test "MODULE_SCAM") \ + MODULE_STREAMRELAY "Stream Relay" $(check_test "MODULE_STREAMRELAY") \ + 2> ${tempfile} + + opt=${?} + if [ $opt != 0 ]; then return; fi + + disable_all "$protocols" + enable_package +} + +menu_readers() { + ${DIALOG} --checklist "\nChoose readers (CA systems):\n " $height $width $listheight \ + READER_NAGRA "Nagravision" $(check_test "READER_NAGRA") \ + READER_NAGRA_MERLIN "Nagra Merlin" $(check_test "READER_NAGRA_MERLIN") \ + READER_IRDETO "Irdeto" $(check_test "READER_IRDETO") \ + READER_CONAX "Conax" $(check_test "READER_CONAX") \ + READER_CRYPTOWORKS "Cryptoworks" $(check_test "READER_CRYPTOWORKS") \ + READER_SECA "Seca" $(check_test "READER_SECA") \ + READER_VIACCESS "Viaccess" $(check_test "READER_VIACCESS") \ + READER_VIDEOGUARD "NDS Videoguard" $(check_test "READER_VIDEOGUARD") \ + READER_DRE "DRE Crypt" $(check_test "READER_DRE") \ + READER_TONGFANG "Tongfang" $(check_test "READER_TONGFANG") \ + READER_BULCRYPT "Bulcrypt" $(check_test "READER_BULCRYPT") \ + READER_GRIFFIN "Griffin" $(check_test "READER_GRIFFIN") \ + READER_DGCRYPT "DGCrypt" $(check_test "READER_DGCRYPT") \ + 2> ${tempfile} + + opt=${?} + if [ $opt != 0 ]; then return; fi + + disable_all "$readers" + enable_package +} + +menu_card_readers() { + ${DIALOG} --checklist "\nChoose card reader drivers:\n " $height $width $listheight \ + CARDREADER_PHOENIX "Phoenix/mouse" $(check_test "CARDREADER_PHOENIX") \ + CARDREADER_INTERNAL "Internal (Sci,Azbox,Cool)" $(check_test "CARDREADER_INTERNAL") \ + CARDREADER_SC8IN1 "SC8in1" $(check_test "CARDREADER_SC8IN1") \ + CARDREADER_MP35 "AD-Teknik MP 3.6/USB Phoenix" $(check_test "CARDREADER_MP35") \ + CARDREADER_SMARGO "Argolis Smargo Smartreader" $(check_test "CARDREADER_SMARGO") \ + CARDREADER_DB2COM "dbox2" $(check_test "CARDREADER_DB2COM") \ + CARDREADER_STAPI "STAPI" $(check_test "CARDREADER_STAPI") \ + CARDREADER_STAPI5 "STAPI5" $(check_test "CARDREADER_STAPI5") \ + CARDREADER_STINGER "STINGER" $(check_test "CARDREADER_STINGER") \ + CARDREADER_DRECAS "DRECAS" $(check_test "CARDREADER_DRECAS") \ + 2> ${tempfile} + + opt=${?} + if [ $opt != 0 ]; then return; fi + + disable_all "$card_readers" + enable_package +} + + +config_dialog() { + height=30 + width=65 + listheight=16 + + DIALOG=${DIALOG:-`which dialog`} + if [ -z "${DIALOG}" ]; then + echo "Please install dialog package." 1>&2 + exit 1 + fi + + configfile=config.h + tempfile=$(mktemp -t oscam-config.dialog.XXXXXX) || exit 1 + tempfileconfig=$(mktemp -t oscam-config.h.XXXXXX) || exit 1 + trap 'rm -f $tempfile $tempfileconfig $tempfileconfig.bak 2>/dev/null' INT TERM EXIT + cp -f $configfile $tempfileconfig + + while true; do + ${DIALOG} --menu "\nSelect category:\n " $height $width $listheight \ + Add-ons "Add-ons" \ + Protocols "Network protocols" \ + Readers "Readers (CA systems)" \ + CardReaders "Card reader drivers" \ + Save "Save" \ + 2> ${tempfile} + + opt=${?} + if [ $opt != 0 ]; then clear; exit; fi + + menuitem=$(cat $tempfile) + case $menuitem in + Add-ons) menu_addons ;; + Protocols) menu_protocols ;; + Readers) menu_readers ;; + CardReaders) menu_card_readers ;; + Save) + cp -f $tempfileconfig $configfile + update_deps + write_enabled + print_components + exit 0 + ;; + esac + cp -f $tempfileconfig $configfile + done +} + +create_cert() { + # define certificate details + CERT_SUBJECT_DEFAULT="/CN=Private OSCam Self Signed Certificate" + CERT_DAYS=365 + + mkdir -p "$CERT_DIR" + + # define certificate type + case "$1" in + 'rsa') + CERT_TYPE="rsa" + [ -z $2 ] && CERT_ALGO="4096" || CERT_ALGO="$2" + PRIVATE_KEY_GEN="openssl genrsa -out "$CERT_DIR/$CERT_PRIVATE_KEY" ${CERT_ALGO}" + ;; + 'ecdsa'|*) + CERT_TYPE="ec -pkeyopt ec_paramgen_curve" + [ -z $2 ] && CERT_ALGO="prime256v1" || CERT_ALGO="$2" + PRIVATE_KEY_GEN="openssl ecparam -name ${CERT_ALGO} -genkey -noout -out "$CERT_DIR/$CERT_PRIVATE_KEY"" + ;; + esac + + # custom subject + if [ -z "$4" ]; then + CERT_SUBJECT="$CERT_SUBJECT_DEFAULT" + else + for param in $*; do + i=$(( i + 1 )) + [ $i -ge 4 ] && CERT_SUBJECT="$CERT_SUBJECT$param " + done + CERT_SUBJECT="/CN=$(echo "$CERT_SUBJECT" | sed 's/[[:space:]]*$//' | sed 's/CN=//g')" + fi + + if [ ! -f "$CERT_DIR/$CERT_PRIVATE_KEY" ] || [ ! -f "$CERT_DIR/$CERT_X509" ]; then + # create X.509 V3 extensions file + printf "authorityKeyIdentifier=keyid,issuer\nbasicConstraints=CA:FALSE\nkeyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment\n" > "$CERT_DIR/v3.ext" + + if [ "$3" != 'ca' ]; then + # create self signed certificate and private key + openssl req -nodes -x509 -utf8 -sha256 -newkey ${CERT_TYPE}:${CERT_ALGO} -addext "$(grep keyUsage "$CERT_DIR/v3.ext")" -keyout "$CERT_DIR/$CERT_PRIVATE_KEY" -out "$CERT_DIR/$CERT_X509" -days $CERT_DAYS -subj "${CERT_SUBJECT}" + else + if [ ! -f "$CERT_DIR/root-ca-$CERT_PRIVATE_KEY" ] || [ ! -f "$CERT_DIR/root-ca-$CERT_X509" ]; then + # create Root CA, CA certificate and private key + openssl req -nodes -x509 -utf8 -sha256 -newkey ${CERT_TYPE}:${CERT_ALGO} -extensions v3_ca -keyout "$CERT_DIR/root-ca-$CERT_PRIVATE_KEY" -out "$CERT_DIR/root-ca-$CERT_X509" -days $(($CERT_DAYS * 10)) -subj "$(echo "$CERT_SUBJECT" | sed "s/ Self Signed Certificate//g") Root CA" + fi + + # create private key + $($PRIVATE_KEY_GEN) + + # create csr + openssl req -new -utf8 -sha256 -key "$CERT_DIR/$CERT_PRIVATE_KEY" -out "$CERT_DIR/$CERT_X509".csr -subj "$(echo "$CERT_SUBJECT" | sed "s/Self Signed Certificate/CA Certificate/g")" + + # issue new certificate by Root CA + openssl x509 -req -nameopt utf8 -sha256 -in "$CERT_DIR/$CERT_X509".csr -extfile "$CERT_DIR/v3.ext" -CA "$CERT_DIR/root-ca-$CERT_X509" -CAkey "$CERT_DIR/root-ca-$CERT_PRIVATE_KEY" -CAcreateserial -out "$CERT_DIR/$CERT_X509" -days $CERT_DAYS + + # chain Root CA and CA certificate + cat "$CERT_DIR/$CERT_X509" "$CERT_DIR/root-ca-$CERT_X509" > "$CERT_DIR/$CERT_X509.chained" + mv "$CERT_DIR/$CERT_X509.chained" "$CERT_DIR/$CERT_X509" + fi + # cleanup + rm -f "$CERT_DIR/$CERT_X509".csr "$CERT_DIR/v3.ext" + else + echo "Private Key and/or Certificates still exists in $(realpath "$(pwd)/$CERT_DIR")!" 1>&2 + return 1 + fi +} + +add_cert() { + if [ ! -f "$1" ] || [ ! -f "$2" ]; then + echo "At least one of the provided files was not found!" 1>&2 + return 1 + else + mkdir -p "$CERT_DIR" + # Symlink certificate + ln -sf "$1" "$CERT_DIR/$CERT_X509" + # Symlink private key + ln -sf "$2" "$CERT_DIR/$CERT_PRIVATE_KEY" + fi +} + +cert_file() { + case "$1" in + 'cert') + CHECK_FILE="$CERT_DIR/$CERT_X509" + HINT="Certificate" + ;; + 'privkey') + CHECK_FILE="$CERT_DIR/$CERT_PRIVATE_KEY" + HINT="Private Key" + ;; + esac + + if [ -f "$CHECK_FILE" ]; then + echo "$(realpath "$CHECK_FILE")" + return 0; + else + echo "$HINT file not found in $(realpath $(pwd)/$CERT_DIR)!" 1>&2 + return 1; + fi +} + +cert_info() { + if [ -f "$CERT_DIR/$CERT_X509" ]; then + DATE=$(hash gdate 2>/dev/null && printf 'gdate' || printf 'date') + for attrib in 'Subject' 'Issuer' 'Not Before' 'Not After' 'Public Key Algorithm'\ + 'Public-Key' 'ASN1 OID' 'NIST CURVE' 'Exponent' 'Signature Algorithm'; do + if [ "$attrib" = 'Not Before' ]; then + openssl x509 -in "$CERT_DIR/$CERT_X509" -noout -nameopt oneline,-esc_msb -startdate | awk -F '=' '{print $2}' | $DATE +"%d.%m.%Y %H:%M:%S" -f - | xargs -0 printf "$attrib: %s" 2>/dev/null + elif [ "$attrib" = 'Not After' ]; then + openssl x509 -in "$CERT_DIR/$CERT_X509" -noout -nameopt oneline,-esc_msb -enddate | awk -F '=' '{print $2}' | $DATE +"%d.%m.%Y %H:%M:%S" -f - | xargs -0 printf "$attrib: %s" 2>/dev/null + else + openssl x509 -in "$CERT_DIR/$CERT_X509" -text -noout -nameopt oneline,-esc_msb | grep -m1 "$attrib" | xargs -0 printf "%s" | awk '{$1=$1};1' + fi + done + return 0 + else + echo "$HINT file not found in $(realpath $(pwd)/$CERT_DIR)!" 1>&2 + return 1 + fi +} + +# Change working directory to the directory where the script is +cd $(dirname $0) + +if [ $# = 0 ] +then + usage + exit 1 +fi + +while [ $# -gt 0 ] +do + case "$1" in + '-g'|'--gui'|'--config'|'--menuconfig') + config_dialog + break + ;; + '-s'|'--show-enabled'|'--show') + shift + list_enabled $(get_opts $1) + # Take special care of USE_xxx flags + if [ "$1" = "card_readers" ] + then + have_flag USE_LIBUSB && echo "CARDREADER_SMART" + have_flag USE_PCSC && echo "CARDREADER_PCSC" + fi + break + ;; + '-Z'|'--show-disabled') + shift + list_disabled $(get_opts $1) + break + ;; + '-S'|'--show-valid') + shift + for OPT in $(get_opts $1) + do + echo $OPT + done + break + ;; + '-E'|'--enable') + shift + while [ "$1" != "" ] + do + case "$1" in + -*) + update_deps + continue 2 + ;; + all|addons|protocols|readers|card_readers) + enable_opts $(get_opts $1) + ;; + *) + enable_opt "$1" + ;; + esac + shift + done + update_deps + write_enabled + ;; + '-D'|'--disable') + shift + while [ "$1" != "" ] + do + case "$1" in + -*) + update_deps + continue 2 + ;; + all|addons|protocols|readers|card_readers) + disable_opts $(get_opts $1) + ;; + *) + disable_opt "$1" + ;; + esac + shift + done + update_deps + write_enabled + ;; + '-R'|'--restore') + echo $defconfig | sed -e 's|# ||g' | xargs printf "%s\n" | grep "=y$" | sed -e 's|^CONFIG_||g;s|=.*||g' | + while read OPT + do + enable_opt "$OPT" + done + echo $defconfig | sed -e 's|# ||g' | xargs printf "%s\n" | grep "=n$" | sed -e 's|^CONFIG_||g;s|=.*||g' | + while read OPT + do + disable_opt "$OPT" + done + update_deps + write_enabled + ;; + '-e'|'--enabled') + enabled $2 && echo "Y" && exit 0 || echo "N" && exit 1 + break + ;; + '-d'|'--disabled') + disabled $2 && echo "Y" && exit 0 || echo "N" && exit 1 + break + ;; + '-cc'|'--create-cert') + shift + create_cert $@ + exit $? + ;; + '-cl'|'--add-cert') + shift + add_cert $@ + exit $? + ;; + '-cf'|'--cert-file') + shift + cert_file $1 + exit $? + ;; + '-ci'|'--cert-info') + cert_info + exit $? + ;; + '-sm'|'--sign-marker') + obsm=`grep '^#define OBSM' oscam-signing.h | cut -d\" -f2` + echo $obsm + break + ;; + '-um'|'--upx-marker') + upxm=`grep '^#define UPXM' oscam-signing.h | cut -d\" -f2` + echo $upxm + break + ;; + '-v'|'--oscam-version') + version=`grep '^#define CS_VERSION' globals.h | cut -d\" -f2` + emuversion=`grep EMU_VERSION module-emulator-osemu.h | awk '{ print $3 }'` + echo $version-$emuversion + break + ;; + '--submodule') + shift + name="$1" + if [ ! -f .gitmodules ]; then + exit 0 + fi + # If no name given, process all submodules + if [ -z "$name" ]; then + for sm in `grep '^\[submodule "' .gitmodules | sed 's/.*"\(.*\)".*/\1/'`; do + $0 --submodule "$sm" + done + exit 0 + fi + # Extract submodule section from .gitmodules + section=`sed -n "/\[submodule \"$name\"\]/,/^\[/p" .gitmodules | grep ' = '` + # Check if submodule is needed based on depends config option + depends=`echo "$section" | grep 'depends = ' | sed 's/.*depends = //'` + if [ -n "$depends" ]; then + enabled $depends || exit 0 + fi + if [ "`git rev-parse --show-toplevel 2>/dev/null`" = "`pwd`" ]; then + if echo "$section" | grep -q 'branch = '; then + echo "Updating git submodule '$name' (tracking)..." + git submodule update --init --remote "$name" || { + echo "Warning: failed to update submodule. Using existing version." >&2 + } + else + echo "Updating git submodule '$name' (pinned)..." + git submodule update --init "$name" || { + echo "Warning: failed to update submodule. Using existing version." >&2 + } + fi + else + url=`echo "$section" | grep 'url = ' | sed 's/.*url = //'` + if [ -z "$url" ]; then + echo "Error: submodule '$name' not found in .gitmodules" >&2 + exit 1 + fi + echo "Fetching $name repository..." + rm -rf "$name" + git clone --depth 1 "$url" "$name" || { + echo "Error: failed to clone $name repository." >&2 + exit 1 + } + fi + break + ;; + '-c'|'--oscam-commit') + sha=`git log --no-merges 2>/dev/null | sed -n 1p | cut -d ' ' -f2 | cut -c1-8` + echo $sha + break + ;; + '-O'|'--detect-osx-sdk-version') + shift + OSX_VER=${1:-10.10} + for DIR in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX{$OSX_VER,10.10,10.9,10.8,10.7}.sdk /Developer/SDKs/MacOSX{$OSX_VER,10.6,10.5}.sdk + + do + if test -d $DIR + then + echo $DIR + exit 0 + fi + done + echo Cant_find_OSX_SDK + break + ;; + '-l'|'--list-config') + list_config + exit 0 + ;; + '-m'|'--make-config.mak') + make_config_mak + exit 0 + ;; + '--use-flags') + shift + USE_FLAGS=$1 + ;; + '--objdir') + shift + OBJDIR=$1 + ;; + '-h'|'--help') + usage + break + ;; + *) + echo "[WARN] Unknown parameter: $1" >&2 + ;; + esac + # Some shells complain when there are no more parameters to shift + test $# -gt 0 && shift +done + +exit 0 diff --git a/cscrypt/CMakeLists.txt b/cscrypt/CMakeLists.txt new file mode 100644 index 0000000..80f88f1 --- /dev/null +++ b/cscrypt/CMakeLists.txt @@ -0,0 +1,16 @@ +project (cscrypt) +if (OSCamOperatingSystem MATCHES "Mac OS X" AND SWVER GREATER 10.5 AND HAVE_LIBCRYPTO) +file (GLOB cscrypt_mac "bn*" "mem*" "aes*" "sha1*") +file (GLOB cscrypt_srcs "*.c") +list(REMOVE_ITEM cscrypt_srcs ${cscrypt_mac}) +file (GLOB cscrypt_hdrs "*.h") +list(REMOVE_ITEM cscrypt_hdrs ${cscrypt_mac}) +else (OSCamOperatingSystem MATCHES "Mac OS X" AND SWVER GREATER 10.5 AND HAVE_LIBCRYPTO) +file (GLOB cscrypt_srcs "*.c") +file (GLOB cscrypt_hdrs "*.h") +endif (OSCamOperatingSystem MATCHES "Mac OS X" AND SWVER GREATER 10.5 AND HAVE_LIBCRYPTO) + +set (lib_name "cscrypt") + +add_library (${lib_name} STATIC ${cscrypt_srcs} ${cscrypt_hdrs}) + diff --git a/cscrypt/Makefile b/cscrypt/Makefile new file mode 100644 index 0000000..562f00b --- /dev/null +++ b/cscrypt/Makefile @@ -0,0 +1,2 @@ +parent: +@$(MAKE) --no-print-directory -C .. diff --git a/cscrypt/aes.c b/cscrypt/aes.c new file mode 100644 index 0000000..9d7e24f --- /dev/null +++ b/cscrypt/aes.c @@ -0,0 +1,1309 @@ +#include "aes.h" + +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +#include +#include +#include +#include "aes.h" + +const uint32_t Te0[256] = +{ + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +const uint32_t Te1[256] = +{ + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +const uint32_t Te2[256] = +{ + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +const uint32_t Te3[256] = +{ + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +const uint32_t Te4[256] = +{ + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; +const uint32_t Td0[256] = +{ + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +const uint32_t Td1[256] = +{ + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +const uint32_t Td2[256] = +{ + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +const uint32_t Td3[256] = +{ + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +const uint32_t Td4[256] = +{ + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; +const uint32_t rcon[] = +{ + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +/** + * Expand the cipher key into the encryption key schedule. + */ +int AES_set_encrypt_key(const uint8_t *userKey, const int bits, AES_KEY *key) +{ + uint32_t *rk; + int i = 0; + uint32_t temp; + + if(!userKey || !key) + { return -1; } + if(bits != 128 && bits != 192 && bits != 256) + { return -2; } + + rk = key->rd_key; + + if(bits == 128) + { key->rounds = 10; } + else if(bits == 192) + { key->rounds = 12; } + else + { key->rounds = 14; } + + rk[0] = GETU32(userKey); + rk[1] = GETU32(userKey + 4); + rk[2] = GETU32(userKey + 8); + rk[3] = GETU32(userKey + 12); + if(bits == 128) + { + while(1) + { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) & 0xff] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if(++i == 10) + { + return 0; + } + rk += 4; + } + } + rk[4] = GETU32(userKey + 16); + rk[5] = GETU32(userKey + 20); + if(bits == 192) + { + while(1) + { + temp = rk[ 5]; + rk[ 6] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) & 0xff] & 0x000000ff) ^ + rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if(++i == 8) + { + return 0; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(userKey + 24); + rk[7] = GETU32(userKey + 28); + if(bits == 256) + { + while(1) + { + temp = rk[ 7]; + rk[ 8] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) & 0xff] & 0x000000ff) ^ + rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if(++i == 7) + { + return 0; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ + (Te4[(temp >> 24) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(temp) & 0xff] & 0x000000ff); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + + rk += 8; + } + } + return 0; +} + +/** + * Expand the cipher key into the decryption key schedule. + */ +int AES_set_decrypt_key(const uint8_t *userKey, const int bits, AES_KEY *key) +{ + uint32_t *rk; + int i, j, status; + uint32_t temp; + + /* first, start with an encryption schedule */ + status = AES_set_encrypt_key(userKey, bits, key); + if(status < 0) + { return status; } + + rk = key->rd_key; + + /* invert the order of the round keys: */ + for(i = 0, j = 4 * (key->rounds); i < j; i += 4, j -= 4) + { + temp = rk[i ]; + rk[i ] = rk[j ]; + rk[j ] = temp; + temp = rk[i + 1]; + rk[i + 1] = rk[j + 1]; + rk[j + 1] = temp; + temp = rk[i + 2]; + rk[i + 2] = rk[j + 2]; + rk[j + 2] = temp; + temp = rk[i + 3]; + rk[i + 3] = rk[j + 3]; + rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for(i = 1; i < (key->rounds); i++) + { + rk += 4; + rk[0] = + Td0[Te4[(rk[0] >> 24) & 0xff] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0]) & 0xff] & 0xff]; + rk[1] = + Td0[Te4[(rk[1] >> 24) & 0xff] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1]) & 0xff] & 0xff]; + rk[2] = + Td0[Te4[(rk[2] >> 24) & 0xff] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2]) & 0xff] & 0xff]; + rk[3] = + Td0[Te4[(rk[3] >> 24) & 0xff] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3]) & 0xff] & 0xff]; + } + return 0; +} + +/* + * Encrypt a single block + * in and out can overlap + */ +void AES_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key) +{ + const uint32_t *rk; + uint32_t s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + assert(in && out && key); + rk = key->rd_key; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(in) ^ rk[0]; + s1 = GETU32(in + 4) ^ rk[1]; + s2 = GETU32(in + 8) ^ rk[2]; + s3 = GETU32(in + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + if(key->rounds > 10) + { + /* round 10: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; + if(key->rounds > 12) + { + /* round 12: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; + } + } + rk += key->rounds << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = key->rounds >> 1; + for(;;) + { + t0 = + Te0[(s0 >> 24) & 0xff] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) & 0xff] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) & 0xff] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) & 0xff] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2) & 0xff] ^ + rk[7]; + + rk += 8; + if(--r == 0) + { + break; + } + + s0 = + Te0[(t0 >> 24) & 0xff] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) & 0xff] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) & 0xff] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24) & 0xff] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(out , s0); + s1 = + (Te4[(t1 >> 24) & 0xff] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(out + 4, s1); + s2 = + (Te4[(t2 >> 24) & 0xff] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(out + 8, s2); + s3 = + (Te4[(t3 >> 24) & 0xff] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(out + 12, s3); +} + +/* + * Decrypt a single block + * in and out can overlap + */ +void AES_decrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key) +{ + const uint32_t *rk; + uint32_t s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + assert(in && out && key); + rk = key->rd_key; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(in) ^ rk[0]; + s1 = GETU32(in + 4) ^ rk[1]; + s2 = GETU32(in + 8) ^ rk[2]; + s3 = GETU32(in + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + if(key->rounds > 10) + { + /* round 10: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47]; + if(key->rounds > 12) + { + /* round 12: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; + } + } + rk += key->rounds << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = key->rounds >> 1; + for(;;) + { + t0 = + Td0[(s0 >> 24) & 0xff] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) & 0xff] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) & 0xff] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) & 0xff] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0) & 0xff] ^ + rk[7]; + + rk += 8; + if(--r == 0) + { + break; + } + + s0 = + Td0[(t0 >> 24) & 0xff] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) & 0xff] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) & 0xff] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) & 0xff] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) & 0xff] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(out , s0); + s1 = + (Td4[(t1 >> 24) & 0xff] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(out + 4, s1); + s2 = + (Td4[(t2 >> 24) & 0xff] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(out + 8, s2); + s3 = + (Td4[(t3 >> 24) & 0xff] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(out + 12, s3); +} + +void AES_cbc_encrypt(const uint8_t *in, uint8_t *out, const unsigned long length, + const AES_KEY *key, uint8_t *ivec, const int enc) +{ + unsigned long n; + unsigned long len = length; + uint8_t tmp[AES_BLOCK_SIZE]; + + assert(in && out && key && ivec); + assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc)); + + if(AES_ENCRYPT == enc) + { + while(len >= AES_BLOCK_SIZE) + { + for(n = 0; n < AES_BLOCK_SIZE; ++n) + tmp[n] = in[n] ^ ivec[n]; + + AES_encrypt(tmp, out, key); + memcpy(ivec, out, AES_BLOCK_SIZE); + len -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + + if(len) + { + for(n = 0; n < len; ++n) + tmp[n] = in[n] ^ ivec[n]; + + for(n = len; n < AES_BLOCK_SIZE; ++n) + tmp[n] = ivec[n]; + + AES_encrypt(tmp, tmp, key); + memcpy(out, tmp, AES_BLOCK_SIZE); + memcpy(ivec, tmp, AES_BLOCK_SIZE); + } + } + else + { + while(len >= AES_BLOCK_SIZE) + { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(in, out, key); + + for(n = 0; n < AES_BLOCK_SIZE; ++n) + out[n] ^= ivec[n]; + + memcpy(ivec, tmp, AES_BLOCK_SIZE); + len -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + + if(len) + { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(tmp, tmp, key); + + for(n = 0; n < len; ++n) + out[n] ^= ivec[n]; + + memcpy(ivec, tmp, AES_BLOCK_SIZE); + } + } +} +#endif diff --git a/cscrypt/aes.h b/cscrypt/aes.h new file mode 100644 index 0000000..e6de6b7 --- /dev/null +++ b/cscrypt/aes.h @@ -0,0 +1,45 @@ +#if defined(WITH_SSL) || defined(WITH_LIBCRYPTO) +# include +#else +#ifndef HEADER_AES_H +#define HEADER_AES_H + +#define AES_ENCRYPT 1 +#define AES_DECRYPT 0 + +/* Because array size can't be a const in C, the following two are macros. + Both sizes are in bytes. */ +#define AES_MAXNR 14 +#define AES_BLOCK_SIZE 16 + +# define GETU32(pt) (((uint32_t)(pt)[0] << 24) ^ ((uint32_t)(pt)[1] << 16) ^ ((uint32_t)(pt)[2] << 8) ^ ((uint32_t)(pt)[3])) +# define PUTU32(ct, st) { (ct)[0] = (uint8_t)((st) >> 24); (ct)[1] = (uint8_t)((st) >> 16); (ct)[2] = (uint8_t)((st) >> 8); (ct)[3] = (uint8_t)(st); } + +#include + +#define MAXKC (256/32) +#define MAXKB (256/8) +#define MAXNR 14 + +/* This controls loop-unrolling in aes_core.c */ +#undef FULL_UNROLL + +/* This should be a hidden type, but EVP requires that the size be known */ +struct aes_key_st +{ + uint32_t rd_key[4 * (AES_MAXNR + 1)]; + int rounds; +}; +typedef struct aes_key_st AES_KEY; + +int AES_set_encrypt_key(const uint8_t *userKey, const int bits, AES_KEY *key); +int AES_set_decrypt_key(const uint8_t *userKey, const int bits, AES_KEY *key); + +void AES_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key); +void AES_decrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key); + +void AES_cbc_encrypt(const uint8_t *in, uint8_t *out, const unsigned long length, + const AES_KEY *key, uint8_t *ivec, const int enc); +#endif /* !HEADER_AES_H */ + +#endif diff --git a/cscrypt/bn.h b/cscrypt/bn.h new file mode 100644 index 0000000..528dd78 --- /dev/null +++ b/cscrypt/bn.h @@ -0,0 +1,516 @@ +#ifndef CSCRYPT_BN_H_ +#define CSCRYPT_BN_H_ + +#if defined(WITH_SSL) || defined(WITH_LIBCRYPTO) +#include +#else +/* crypto/bn/bn.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#define I386_Only + +#ifdef __LP64__ +#define SIXTY_FOUR_BIT +#else +#define THIRTY_TWO_BIT +#endif + +#ifndef HEADER_BN_H +#define HEADER_BN_H + +#ifndef NO_FP_API +#include /* FILE */ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef VMS +#undef BN_LLONG /* experimental, so far... */ +#endif + +#define BN_MUL_COMBA +#define BN_SQR_COMBA +#define BN_RECURSION +#define RECP_MUL_MOD +#define MONT_MUL_MOD + +/* This next option uses the C libraries (2 word)/(1 word) function. + * If it is not defined, I use my C version (which is slower). + * The reason for this flag is that when the particular C compiler + * library routine is used, and the library is linked with a different + * compiler, the library is missing. This mostly happens when the + * library is built with gcc and then linked using normal cc. This would + * be a common occurrence because gcc normally produces code that is + * 2 times faster than system compilers for the big number stuff. + * For machines with only one compiler (or shared libraries), this should + * be on. Again this in only really a problem on machines + * using "long long's", are 32bit, and are not using my assembler code. */ +#if defined(MSDOS) || defined(WINDOWS) || defined(WIN32) || defined(linux) +# ifndef BN_DIV2W +# define BN_DIV2W +# endif +#endif + +/* assuming long is 64bit - this is the DEC Alpha + * unsigned long long is only 64 bits :-(, don't define + * BN_LLONG for the DEC Alpha */ +#ifdef SIXTY_FOUR_BIT_LONG +#define BN_ULLONG unsigned long long +#define BN_ULONG unsigned long +#define BN_LONG long +#define BN_BITS 128 +#define BN_BYTES 8 +#define BN_BITS2 64 +#define BN_BITS4 32 +#define BN_MASK (0xffffffffffffffffffffffffffffffffLL) +#define BN_MASK2 (0xffffffffffffffffL) +#define BN_MASK2l (0xffffffffL) +#define BN_MASK2h (0xffffffff00000000L) +#define BN_MASK2h1 (0xffffffff80000000L) +#define BN_TBIT (0x8000000000000000L) +#define BN_DEC_CONV (10000000000000000000UL) +#define BN_DEC_FMT1 "%lu" +#define BN_DEC_FMT2 "%019lu" +#define BN_DEC_NUM 19 +#endif + +/* This is where the long long data type is 64 bits, but long is 32. + * For machines where there are 64bit registers, this is the mode to use. + * IRIX, on R4000 and above should use this mode, along with the relevant + * assembler code :-). Do NOT define BN_LLONG. */ +#ifdef SIXTY_FOUR_BIT +#undef BN_LLONG +#undef BN_ULLONG +#define BN_ULONG unsigned long long +#define BN_LONG long long +#define BN_BITS 128 +#define BN_BYTES 8 +#define BN_BITS2 64 +#define BN_BITS4 32 +#define BN_MASK2 (0xffffffffffffffffLL) +#define BN_MASK2l (0xffffffffL) +#define BN_MASK2h (0xffffffff00000000LL) +#define BN_MASK2h1 (0xffffffff80000000LL) +#define BN_TBIT (0x8000000000000000LL) +#define BN_DEC_CONV (10000000000000000000UL) +#define BN_DEC_FMT1 "%llu" +#define BN_DEC_FMT2 "%019llu" +#define BN_DEC_NUM 19 +#endif + +#ifdef THIRTY_TWO_BIT +#if defined(WIN32) && !defined(__GNUC__) +#define BN_ULLONG unsigned _int64 +#else +#define BN_ULLONG unsigned long long +#endif +#define BN_ULONG unsigned long +#define BN_LONG long +#define BN_BITS 64 +#define BN_BYTES 4 +#define BN_BITS2 32 +#define BN_BITS4 16 +#ifdef WIN32 + +/* VC++ doesn't like the LL suffix */ +#define BN_MASK (0xffffffffffffffffL) +#else +#define BN_MASK (0xffffffffffffffffLL) +#endif +#define BN_MASK2 (0xffffffffL) +#define BN_MASK2l (0xffff) +#define BN_MASK2h1 (0xffff8000L) +#define BN_MASK2h (0xffff0000L) +#define BN_TBIT (0x80000000L) +#define BN_DEC_CONV (1000000000L) +#define BN_DEC_FMT1 "%lu" +#define BN_DEC_FMT2 "%09lu" +#define BN_DEC_NUM 9 +#endif + +#ifdef SIXTEEN_BIT +#ifndef BN_DIV2W +#define BN_DIV2W +#endif +#define BN_ULLONG unsigned long +#define BN_ULONG unsigned short +#define BN_LONG short +#define BN_BITS 32 +#define BN_BYTES 2 +#define BN_BITS2 16 +#define BN_BITS4 8 +#define BN_MASK (0xffffffff) +#define BN_MASK2 (0xffff) +#define BN_MASK2l (0xff) +#define BN_MASK2h1 (0xff80) +#define BN_MASK2h (0xff00) +#define BN_TBIT (0x8000) +#define BN_DEC_CONV (100000) +#define BN_DEC_FMT1 "%u" +#define BN_DEC_FMT2 "%05u" +#define BN_DEC_NUM 5 +#endif + +#ifdef EIGHT_BIT +#ifndef BN_DIV2W +#define BN_DIV2W +#endif +#define BN_ULLONG unsigned short +#define BN_ULONG unsigned char +#define BN_LONG char +#define BN_BITS 16 +#define BN_BYTES 1 +#define BN_BITS2 8 +#define BN_BITS4 4 +#define BN_MASK (0xffff) +#define BN_MASK2 (0xff) +#define BN_MASK2l (0xf) +#define BN_MASK2h1 (0xf8) +#define BN_MASK2h (0xf0) +#define BN_TBIT (0x80) +#define BN_DEC_CONV (100) +#define BN_DEC_FMT1 "%u" +#define BN_DEC_FMT2 "%02u" +#define BN_DEC_NUM 2 +#endif + +#define BN_DEFAULT_BITS 1280 + +#ifdef BIGNUM +#undef BIGNUM +#endif + +#define BN_FLG_MALLOCED 0x01 +#define BN_FLG_STATIC_DATA 0x02 +#define BN_FLG_FREE 0x8000 /* used for debuging */ +#define BN_set_flags(b,n) ((b)->flags|=(n)) +#define BN_get_flags(b,n) ((b)->flags&(n)) + +typedef struct bignum_st +{ + BN_ULONG *d; /* Pointer to an array of 'BN_BITS2' bit chunks. */ + int top; /* Index of last used d +1. */ + /* The next are internal book keeping for bn_expand. */ + int dmax; /* Size of the d array. */ + int neg; /* one if the number is negative */ + int flags; +} BIGNUM; + +/* Used for temp variables */ +#define BN_CTX_NUM 16 +#define BN_CTX_NUM_POS 12 +typedef struct bignum_ctx +{ + int tos; + BIGNUM bn[BN_CTX_NUM]; + int flags; + int depth; + int pos[BN_CTX_NUM_POS]; + int too_many; +} BN_CTX; + +typedef struct bn_blinding_st +{ + int init; + BIGNUM *A; + BIGNUM *Ai; + BIGNUM *mod; /* just a reference */ +} BN_BLINDING; + +/* Used for montgomery multiplication */ +typedef struct bn_mont_ctx_st +{ + int ri; /* number of bits in R */ + BIGNUM RR; /* used to convert to montgomery form */ + BIGNUM N; /* The modulus */ + BIGNUM Ni; /* R*(1/R mod N) - N*Ni = 1 */ + /* (Ni is only stored for bignum algorithm) */ + BN_ULONG n0; /* least significant word of Ni */ + int flags; +} BN_MONT_CTX; + +/* Used for reciprocal division/mod functions + * It cannot be shared between threads */ +typedef struct bn_recp_ctx_st +{ + BIGNUM N; /* the divisor */ + BIGNUM Nr; /* the reciprocal */ + int num_bits; + int shift; + int flags; +} BN_RECP_CTX; + +#define BN_to_montgomery(r,a,mont,ctx) BN_mod_mul_montgomery(\ + r,a,&((mont)->RR),(mont),ctx) + +#define BN_prime_checks 0 /* default: select number of iterations based on the size of the number */ + +/* number of Miller-Rabin iterations for an error rate of less than 2^-80 + * for random 'b'-bit input, b >= 100 (taken from table 4.4 in the Handbook + * of Applied Cryptography [Menezes, van Oorschot, Vanstone; CRC Press 1996]; + * original paper: Damgaard, Landrock, Pomerance: Average case error estimates + * for the strong probable prime test. -- Math. Comp. 61 (1993) 177-194) */ +#define BN_prime_checks_for_size(b) ((b) >= 1300 ? 2 : \ + (b) >= 850 ? 3 : \ + (b) >= 650 ? 4 : \ + (b) >= 550 ? 5 : \ + (b) >= 450 ? 6 : \ + (b) >= 400 ? 7 : \ + (b) >= 350 ? 8 : \ + (b) >= 300 ? 9 : \ + (b) >= 250 ? 12 : \ + (b) >= 200 ? 15 : \ + (b) >= 150 ? 18 : \ + /* b >= 100 */ 27) + +#define BN_num_bytes(a) ((BN_num_bits(a)+7)/8) +#define BN_is_word(a,w) (((a)->top == 1) && ((a)->d[0] == (BN_ULONG)(w))) +#define BN_is_zero(a) (((a)->top == 0) || BN_is_word(a,0)) +#define BN_is_one(a) (BN_is_word((a),1)) +#define BN_is_odd(a) (((a)->top > 0) && ((a)->d[0] & 1)) +#define BN_one(a) (BN_set_word((a),1)) +#define BN_zero(a) (BN_set_word((a),0)) + +/*#define BN_ascii2bn(a) BN_hex2bn(a) */ +/*#define BN_bn2ascii(a) BN_bn2hex(a) */ + +const BIGNUM *BN_value_one(void); +char *BN_options(void); +BN_CTX *BN_CTX_new(void); +void BN_CTX_init(BN_CTX *c); +void BN_CTX_free(BN_CTX *c); +void BN_CTX_start(BN_CTX *ctx); +BIGNUM *BN_CTX_get(BN_CTX *ctx); +void BN_CTX_end(BN_CTX *ctx); +int BN_rand(BIGNUM *rnd, int bits, int top, int bottom); +int BN_pseudo_rand(BIGNUM *rnd, int bits, int top, int bottom); +int BN_rand_range(BIGNUM *rnd, BIGNUM *range); +int BN_pseudo_rand_range(BIGNUM *rnd, BIGNUM *range); +int BN_num_bits(const BIGNUM *a); +int BN_num_bits_word(BN_ULONG); +BIGNUM *BN_new(void); +void BN_init(BIGNUM *); +void BN_clear_free(BIGNUM *a); +BIGNUM *BN_copy(BIGNUM *a, const BIGNUM *b); +BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); +int BN_bn2bin(const BIGNUM *a, unsigned char *to); +BIGNUM *BN_mpi2bn(unsigned char *s, int len, BIGNUM *ret); +int BN_bn2mpi(const BIGNUM *a, unsigned char *to); +int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); +int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); +int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); +int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); +int BN_mod(BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx); +int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx); +int BN_mul(BIGNUM *r, BIGNUM *a, BIGNUM *b, BN_CTX *ctx); +int BN_sqr(BIGNUM *r, BIGNUM *a, BN_CTX *ctx); +BN_ULONG BN_mod_word(const BIGNUM *a, BN_ULONG w); +BN_ULONG BN_div_word(BIGNUM *a, BN_ULONG w); +int BN_mul_word(BIGNUM *a, BN_ULONG w); +int BN_add_word(BIGNUM *a, BN_ULONG w); +int BN_sub_word(BIGNUM *a, BN_ULONG w); +int BN_set_word(BIGNUM *a, BN_ULONG w); +BN_ULONG BN_get_word(const BIGNUM *a); +int BN_cmp(const BIGNUM *a, const BIGNUM *b); +void BN_free(BIGNUM *a); +int BN_is_bit_set(const BIGNUM *a, int n); +int BN_lshift(BIGNUM *r, const BIGNUM *a, int n); +int BN_lshift1(BIGNUM *r, BIGNUM *a); +int BN_exp(BIGNUM *r, BIGNUM *a, BIGNUM *p, BN_CTX *ctx); +int BN_mod_exp(BIGNUM *r, BIGNUM *a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx); +int BN_mod_exp_mont(BIGNUM *r, BIGNUM *a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx); +int BN_mod_exp_mont_word(BIGNUM *r, BN_ULONG a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx); +int BN_mod_exp2_mont(BIGNUM *r, BIGNUM *a1, BIGNUM *p1, BIGNUM *a2, BIGNUM *p2, BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx); +int BN_mod_exp_simple(BIGNUM *r, BIGNUM *a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx); +int BN_mask_bits(BIGNUM *a, int n); +int BN_mod_mul(BIGNUM *ret, BIGNUM *a, BIGNUM *b, const BIGNUM *m, BN_CTX *ctx); +#ifndef NO_FP_API +int BN_print_fp(FILE *fp, const BIGNUM *a); +#endif +#ifdef HEADER_BIO_H +int BN_print(BIO *fp, const BIGNUM *a); +#else +int BN_print(void *fp, const BIGNUM *a); +#endif +int BN_reciprocal(BIGNUM *r, BIGNUM *m, int len, BN_CTX *ctx); +int BN_rshift(BIGNUM *r, BIGNUM *a, int n); +int BN_rshift1(BIGNUM *r, BIGNUM *a); +void BN_clear(BIGNUM *a); +BIGNUM *BN_dup(const BIGNUM *a); +int BN_ucmp(const BIGNUM *a, const BIGNUM *b); +int BN_set_bit(BIGNUM *a, int n); +int BN_clear_bit(BIGNUM *a, int n); +char *BN_bn2hex(const BIGNUM *a); +char *BN_bn2dec(const BIGNUM *a); +int BN_hex2bn(BIGNUM **a, const char *str); +int BN_dec2bn(BIGNUM **a, const char *str); +int BN_gcd(BIGNUM *r, BIGNUM *in_a, BIGNUM *in_b, BN_CTX *ctx); +BIGNUM *BN_mod_inverse(BIGNUM *ret, BIGNUM *a, const BIGNUM *n, BN_CTX *ctx); +BIGNUM *BN_generate_prime(BIGNUM *ret, int bits, int safe, BIGNUM *add, BIGNUM *rem, void (*callback)(int, int, void *), void *cb_arg); +int BN_is_prime(const BIGNUM *p, int nchecks, void (*callback)(int, int, void *), BN_CTX *ctx, void *cb_arg); +int BN_is_prime_fasttest(const BIGNUM *p, int nchecks, void (*callback)(int, int, void *), BN_CTX *ctx, void *cb_arg, int do_trial_division); +BN_MONT_CTX *BN_MONT_CTX_new(void); +void BN_MONT_CTX_init(BN_MONT_CTX *ctx); +int BN_mod_mul_montgomery(BIGNUM *r, BIGNUM *a, BIGNUM *b, BN_MONT_CTX *mont, BN_CTX *ctx); +int BN_from_montgomery(BIGNUM *r, BIGNUM *a, BN_MONT_CTX *mont, BN_CTX *ctx); +void BN_MONT_CTX_free(BN_MONT_CTX *mont); +int BN_MONT_CTX_set(BN_MONT_CTX *mont, const BIGNUM *modulus, BN_CTX *ctx); +BN_MONT_CTX *BN_MONT_CTX_copy(BN_MONT_CTX *to, BN_MONT_CTX *from); +BN_BLINDING *BN_BLINDING_new(BIGNUM *A, BIGNUM *Ai, BIGNUM *mod); +void BN_BLINDING_free(BN_BLINDING *b); +int BN_BLINDING_update(BN_BLINDING *b, BN_CTX *ctx); +int BN_BLINDING_convert(BIGNUM *n, BN_BLINDING *r, BN_CTX *ctx); +int BN_BLINDING_invert(BIGNUM *n, BN_BLINDING *b, BN_CTX *ctx); +void BN_set_params(int mul, int high, int low, int mont); +int BN_get_params(int which); /* 0, mul, 1 high, 2 low, 3 mont */ +void BN_RECP_CTX_init(BN_RECP_CTX *recp); +BN_RECP_CTX *BN_RECP_CTX_new(void); +void BN_RECP_CTX_free(BN_RECP_CTX *recp); +int BN_RECP_CTX_set(BN_RECP_CTX *recp, const BIGNUM *rdiv, BN_CTX *ctx); +int BN_mod_mul_reciprocal(BIGNUM *r, BIGNUM *x, BIGNUM *y, BN_RECP_CTX *recp, BN_CTX *ctx); +int BN_mod_exp_recp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx); +int BN_div_recp(BIGNUM *dv, BIGNUM *rem, BIGNUM *m, BN_RECP_CTX *recp, BN_CTX *ctx); + + /* library internal functions */ +#define bn_expand(a,bits) ((((((bits+BN_BITS2-1))/BN_BITS2)) <= (a)->dmax)?\ + (a):bn_expand2((a),(bits)/BN_BITS2+1)) +#define bn_wexpand(a,words) (((words) <= (a)->dmax)?(a):bn_expand2((a),(words))) + BIGNUM *bn_expand2(BIGNUM *a, int words); + +#define bn_fix_top(a) \ + { \ + BN_ULONG *ftl; \ + if ((a)->top > 0) \ + { \ + for (ftl= &((a)->d[(a)->top-1]); (a)->top > 0; (a)->top--) \ + if (*(ftl--)) break; \ + } \ + } + + BN_ULONG bn_mul_add_words(BN_ULONG *rp, BN_ULONG *ap, int num, BN_ULONG w); + BN_ULONG bn_mul_words(BN_ULONG *rp, BN_ULONG *ap, int num, BN_ULONG w); + void bn_sqr_words(BN_ULONG *rp, BN_ULONG *ap, int num); + BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d); + BN_ULONG bn_add_words(BN_ULONG *rp, BN_ULONG *ap, BN_ULONG *bp, int num); + BN_ULONG bn_sub_words(BN_ULONG *rp, BN_ULONG *ap, BN_ULONG *bp, int num); + +#ifdef BN_DEBUG + void bn_dump1(FILE *o, const char *a, BN_ULONG *b, int n); +# define bn_print(a) {fprintf(stderr, #a "="); BN_print_fp(stderr,a); \ + fprintf(stderr,"\n");} +# define bn_dump(a,n) bn_dump1(stderr,#a,a,n); +#else +# define bn_print(a) +# define bn_dump(a,b) +#endif + +int BN_bntest_rand(BIGNUM *rnd, int bits, int top, int bottom); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_BN_strings(void); + +/* Error codes for the BN functions. */ +/* Function codes. */ +#define BN_F_BN_BLINDING_CONVERT 100 +#define BN_F_BN_BLINDING_INVERT 101 +#define BN_F_BN_BLINDING_NEW 102 +#define BN_F_BN_BLINDING_UPDATE 103 +#define BN_F_BN_BN2DEC 104 +#define BN_F_BN_BN2HEX 105 +#define BN_F_BN_CTX_GET 116 +#define BN_F_BN_CTX_NEW 106 +#define BN_F_BN_DIV 107 +#define BN_F_BN_EXPAND2 108 +#define BN_F_BN_MOD_EXP2_MONT 118 +#define BN_F_BN_MOD_EXP_MONT 109 +#define BN_F_BN_MOD_EXP_MONT_WORD 117 +#define BN_F_BN_MOD_INVERSE 110 +#define BN_F_BN_MOD_MUL_RECIPROCAL 111 +#define BN_F_BN_MPI2BN 112 +#define BN_F_BN_NEW 113 +#define BN_F_BN_RAND 114 +#define BN_F_BN_RAND_RANGE 122 +#define BN_F_BN_USUB 115 + +/* Reason codes. */ +#define BN_R_ARG2_LT_ARG3 100 +#define BN_R_BAD_RECIPROCAL 101 +#define BN_R_BIGNUM_TOO_LONG 114 +#define BN_R_CALLED_WITH_EVEN_MODULUS 102 +#define BN_R_DIV_BY_ZERO 103 +#define BN_R_ENCODING_ERROR 104 +#define BN_R_EXPAND_ON_STATIC_BIGNUM_DATA 105 +#define BN_R_INVALID_LENGTH 106 +#define BN_R_INVALID_RANGE 115 +#define BN_R_NOT_INITIALIZED 107 +#define BN_R_NO_INVERSE 108 +#define BN_R_TOO_MANY_TEMPORARY_VARIABLES 109 + +#ifdef __cplusplus +} +#endif +#endif + +#endif + +#endif diff --git a/cscrypt/bn_add.c b/cscrypt/bn_add.c new file mode 100644 index 0000000..32cdd2a --- /dev/null +++ b/cscrypt/bn_add.c @@ -0,0 +1,327 @@ +#include "bn.h" + +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/bn/bn_add.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#include +#include "openssl_mods.h" +#include "bn_lcl.h" + +/* r can == a or b */ +int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) +{ + const BIGNUM *tmp; + + bn_check_top(a); + bn_check_top(b); + + /* a + b a+b + * a + -b a-b + * -a + b b-a + * -a + -b -(a+b) + */ + if(a->neg ^ b->neg) + { + /* only one is negative */ + if(a->neg) + { + tmp = a; + a = b; + b = tmp; + } + + /* we are now a - b */ + + if(BN_ucmp(a, b) < 0) + { + if(!BN_usub(r, b, a)) { return (0); } + r->neg = 1; + } + else + { + if(!BN_usub(r, a, b)) { return (0); } + r->neg = 0; + } + return (1); + } + + if(a->neg) /* both are neg */ + { r->neg = 1; } + else + { r->neg = 0; } + + if(!BN_uadd(r, a, b)) { return (0); } + return (1); +} + +/* unsigned add of b to a, r must be large enough */ +int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) +{ + register int i; + int max, min; + BN_ULONG *ap, *bp, *rp, carry, t1; + const BIGNUM *tmp; + + bn_check_top(a); + bn_check_top(b); + + if(a->top < b->top) + { + tmp = a; + a = b; + b = tmp; + } + max = a->top; + min = b->top; + + if(bn_wexpand(r, max + 1) == NULL) + { return (0); } + + r->top = max; + + + ap = a->d; + bp = b->d; + rp = r->d; + + carry = bn_add_words(rp, ap, bp, min); + rp += min; + ap += min; + i = min; + + if(carry) + { + while(i < max) + { + i++; + t1 = *(ap++); + if((*(rp++) = (t1 + 1)&BN_MASK2) >= t1) + { + carry = 0; + break; + } + } + if((i >= max) && carry) + { + *(rp++) = 1; + r->top++; + } + } + if(rp != ap) + { + for(; i < max; i++) + { *(rp++) = *(ap++); } + } + /* memcpy(rp,ap,sizeof(*ap)*(max-i));*/ + return (1); +} + +/* unsigned subtraction of b from a, a must be larger than b. */ +int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) +{ + int max, min; + register BN_ULONG t1, t2, *ap, *bp, *rp; + int i, carry; + + + bn_check_top(a); + bn_check_top(b); + + if(a->top < b->top) /* hmm... should not be happening */ + { + return (0); + } + + max = a->top; + min = b->top; + if(bn_wexpand(r, max) == NULL) { return (0); } + + ap = a->d; + bp = b->d; + rp = r->d; + +#if 1 + carry = 0; + for(i = 0; i < min; i++) + { + t1 = *(ap++); + t2 = *(bp++); + if(carry) + { + carry = (t1 <= t2); + t1 = (t1 - t2 - 1)&BN_MASK2; + } + else + { + carry = (t1 < t2); + t1 = (t1 - t2)&BN_MASK2; + } +#if defined(IRIX_CC_BUG) && !defined(LINT) + int dummy; + dummy = t1; +#endif + *(rp++) = t1 & BN_MASK2; + } +#else + carry = bn_sub_words(rp, ap, bp, min); + ap += min; + bp += min; + rp += min; + i = min; +#endif + if(carry) /* subtracted */ + { + while(i < max) + { + i++; + t1 = *(ap++); + t2 = (t1 - 1)&BN_MASK2; + *(rp++) = t2; + if(t1 > t2) { break; } + } + } +#if 0 + memcpy(rp, ap, sizeof(*rp) * (max - i)); +#else + if(rp != ap) + { + for(;;) + { + if(i++ >= max) { break; } + rp[0] = ap[0]; + if(i++ >= max) { break; } + rp[1] = ap[1]; + if(i++ >= max) { break; } + rp[2] = ap[2]; + if(i++ >= max) { break; } + rp[3] = ap[3]; + rp += 4; + ap += 4; + } + } +#endif + + r->top = max; + bn_fix_top(r); + return (1); +} + +int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) +{ + int max; + int add = 0, neg = 0; + const BIGNUM *tmp; + + bn_check_top(a); + bn_check_top(b); + + /* a - b a-b + * a - -b a+b + * -a - b -(a+b) + * -a - -b b-a + */ + if(a->neg) + { + if(b->neg) + { + tmp = a; + a = b; + b = tmp; + } + else + { + add = 1; + neg = 1; + } + } + else + { + if(b->neg) + { + add = 1; + neg = 0; + } + } + + if(add) + { + if(!BN_uadd(r, a, b)) { return (0); } + r->neg = neg; + return (1); + } + + /* We are actually doing a - b :-) */ + + max = (a->top > b->top) ? a->top : b->top; + if(bn_wexpand(r, max) == NULL) { return (0); } + if(BN_ucmp(a, b) < 0) + { + if(!BN_usub(r, b, a)) { return (0); } + r->neg = 1; + } + else + { + if(!BN_usub(r, a, b)) { return (0); } + r->neg = 0; + } + return (1); +} + +#endif diff --git a/cscrypt/bn_asm.c b/cscrypt/bn_asm.c new file mode 100644 index 0000000..4cce0dc --- /dev/null +++ b/cscrypt/bn_asm.c @@ -0,0 +1,854 @@ +#include "bn.h" + +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/bn/bn_asm.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#ifndef BN_DEBUG +# undef NDEBUG /* avoid conflicting definitions */ +# define NDEBUG +#endif + +#include +#include +#include "bn_lcl.h" +#include "openssl_mods.h" + +#if defined(BN_LLONG) || defined(BN_UMULT_HIGH) + +BN_ULONG bn_mul_add_words(BN_ULONG *rp, BN_ULONG *ap, int num, BN_ULONG w) +{ + BN_ULONG c1 = 0; + + assert(num >= 0); + if(num <= 0) { return (c1); } + + while(num&~3) + { + mul_add(rp[0], ap[0], w, c1); + mul_add(rp[1], ap[1], w, c1); + mul_add(rp[2], ap[2], w, c1); + mul_add(rp[3], ap[3], w, c1); + ap += 4; + rp += 4; + num -= 4; + } + if(num) + { + mul_add(rp[0], ap[0], w, c1); + if(--num == 0) { return c1; } + mul_add(rp[1], ap[1], w, c1); + if(--num == 0) { return c1; } + mul_add(rp[2], ap[2], w, c1); + return c1; + } + + return (c1); +} + +BN_ULONG bn_mul_words(BN_ULONG *rp, BN_ULONG *ap, int num, BN_ULONG w) +{ + BN_ULONG c1 = 0; + + assert(num >= 0); + if(num <= 0) { return (c1); } + + while(num&~3) + { + mul(rp[0], ap[0], w, c1); + mul(rp[1], ap[1], w, c1); + mul(rp[2], ap[2], w, c1); + mul(rp[3], ap[3], w, c1); + ap += 4; + rp += 4; + num -= 4; + } + if(num) + { + mul(rp[0], ap[0], w, c1); + if(--num == 0) { return c1; } + mul(rp[1], ap[1], w, c1); + if(--num == 0) { return c1; } + mul(rp[2], ap[2], w, c1); + } + return (c1); +} + +void bn_sqr_words(BN_ULONG *r, BN_ULONG *a, int n) +{ + assert(n >= 0); + if(n <= 0) { return; } + while(n&~3) + { + sqr(r[0], r[1], a[0]); + sqr(r[2], r[3], a[1]); + sqr(r[4], r[5], a[2]); + sqr(r[6], r[7], a[3]); + a += 4; + r += 8; + n -= 4; + } + if(n) + { + sqr(r[0], r[1], a[0]); + if(--n == 0) { return; } + sqr(r[2], r[3], a[1]); + if(--n == 0) { return; } + sqr(r[4], r[5], a[2]); + } +} + +#else /* !(defined(BN_LLONG) || defined(BN_UMULT_HIGH)) */ + +BN_ULONG bn_mul_add_words(BN_ULONG *rp, BN_ULONG *ap, int num, BN_ULONG w) +{ + BN_ULONG c = 0; + BN_ULONG bl, bh; + + assert(num >= 0); + if(num <= 0) { return ((BN_ULONG)0); } + + bl = LBITS(w); + bh = HBITS(w); + + for(;;) + { + mul_add(rp[0], ap[0], bl, bh, c); + if(--num == 0) { break; } + mul_add(rp[1], ap[1], bl, bh, c); + if(--num == 0) { break; } + mul_add(rp[2], ap[2], bl, bh, c); + if(--num == 0) { break; } + mul_add(rp[3], ap[3], bl, bh, c); + if(--num == 0) { break; } + ap += 4; + rp += 4; + } + return (c); +} + +BN_ULONG bn_mul_words(BN_ULONG *rp, BN_ULONG *ap, int num, BN_ULONG w) +{ + BN_ULONG carry = 0; + BN_ULONG bl, bh; + + assert(num >= 0); + if(num <= 0) { return ((BN_ULONG)0); } + + bl = LBITS(w); + bh = HBITS(w); + + for(;;) + { + mul(rp[0], ap[0], bl, bh, carry); + if(--num == 0) { break; } + mul(rp[1], ap[1], bl, bh, carry); + if(--num == 0) { break; } + mul(rp[2], ap[2], bl, bh, carry); + if(--num == 0) { break; } + mul(rp[3], ap[3], bl, bh, carry); + if(--num == 0) { break; } + ap += 4; + rp += 4; + } + return (carry); +} + +void bn_sqr_words(BN_ULONG *r, BN_ULONG *a, int n) +{ + assert(n >= 0); + if(n <= 0) { return; } + for(;;) + { + sqr64(r[0], r[1], a[0]); + if(--n == 0) { break; } + + sqr64(r[2], r[3], a[1]); + if(--n == 0) { break; } + + sqr64(r[4], r[5], a[2]); + if(--n == 0) { break; } + + sqr64(r[6], r[7], a[3]); + if(--n == 0) { break; } + + a += 4; + r += 8; + } +} + +#endif /* !(defined(BN_LLONG) || defined(BN_UMULT_HIGH)) */ + +#if defined(BN_LLONG) && defined(BN_DIV2W) + +BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d) +{ + return ((BN_ULONG)(((((BN_ULLONG)h) << BN_BITS2) | l) / (BN_ULLONG)d)); +} + +#else + +/* Divide h,l by d and return the result. */ +/* I need to test this some more :-( */ +BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d) +{ + BN_ULONG dh, dl, q, ret = 0, th, tl, t; + int i, count = 2; + + if(d == 0) { return (BN_MASK2); } + + i = BN_num_bits_word(d); + assert((i == BN_BITS2) || (h > (BN_ULONG)1 << i)); + + i = BN_BITS2 - i; + if(h >= d) { h -= d; } + + if(i) + { + d <<= i; + h = (h << i) | (l >> (BN_BITS2 - i)); + l <<= i; + } + dh = (d & BN_MASK2h) >> BN_BITS4; + dl = (d & BN_MASK2l); + for(;;) + { + if((h >> BN_BITS4) == dh) + { q = BN_MASK2l; } + else + { q = h / dh; } + + th = q * dh; + tl = dl * q; + for(;;) + { + t = h - th; + if((t & BN_MASK2h) || + ((tl) <= ( + (t << BN_BITS4) | + ((l & BN_MASK2h) >> BN_BITS4)))) + { break; } + q--; + th -= dh; + tl -= dl; + } + t = (tl >> BN_BITS4); + tl = (tl << BN_BITS4)&BN_MASK2h; + th += t; + + if(l < tl) { th++; } + l -= tl; + if(h < th) + { + h += d; + q--; + } + h -= th; + + if(--count == 0) { break; } + + ret = q << BN_BITS4; + h = ((h << BN_BITS4) | (l >> BN_BITS4))&BN_MASK2; + l = (l & BN_MASK2l) << BN_BITS4; + } + ret |= q; + return (ret); +} +#endif /* !defined(BN_LLONG) && defined(BN_DIV2W) */ + +#ifdef BN_LLONG +BN_ULONG bn_add_words(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n) +{ + BN_ULLONG ll = 0; + + assert(n >= 0); + if(n <= 0) { return ((BN_ULONG)0); } + + for(;;) + { + ll += (BN_ULLONG)a[0] + b[0]; + r[0] = (BN_ULONG)ll & BN_MASK2; + ll >>= BN_BITS2; + if(--n <= 0) { break; } + + ll += (BN_ULLONG)a[1] + b[1]; + r[1] = (BN_ULONG)ll & BN_MASK2; + ll >>= BN_BITS2; + if(--n <= 0) { break; } + + ll += (BN_ULLONG)a[2] + b[2]; + r[2] = (BN_ULONG)ll & BN_MASK2; + ll >>= BN_BITS2; + if(--n <= 0) { break; } + + ll += (BN_ULLONG)a[3] + b[3]; + r[3] = (BN_ULONG)ll & BN_MASK2; + ll >>= BN_BITS2; + if(--n <= 0) { break; } + + a += 4; + b += 4; + r += 4; + } + return ((BN_ULONG)ll); +} +#else /* !BN_LLONG */ +BN_ULONG bn_add_words(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n) +{ + BN_ULONG c, l, t; + + assert(n >= 0); + if(n <= 0) { return ((BN_ULONG)0); } + + c = 0; + for(;;) + { + t = a[0]; + t = (t + c)&BN_MASK2; + c = (t < c); + l = (t + b[0])&BN_MASK2; + c += (l < t); + r[0] = l; + if(--n <= 0) { break; } + + t = a[1]; + t = (t + c)&BN_MASK2; + c = (t < c); + l = (t + b[1])&BN_MASK2; + c += (l < t); + r[1] = l; + if(--n <= 0) { break; } + + t = a[2]; + t = (t + c)&BN_MASK2; + c = (t < c); + l = (t + b[2])&BN_MASK2; + c += (l < t); + r[2] = l; + if(--n <= 0) { break; } + + t = a[3]; + t = (t + c)&BN_MASK2; + c = (t < c); + l = (t + b[3])&BN_MASK2; + c += (l < t); + r[3] = l; + if(--n <= 0) { break; } + + a += 4; + b += 4; + r += 4; + } + return ((BN_ULONG)c); +} +#endif /* !BN_LLONG */ + +BN_ULONG bn_sub_words(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n) +{ + BN_ULONG t1, t2; + int c = 0; + + assert(n >= 0); + if(n <= 0) { return ((BN_ULONG)0); } + + for(;;) + { + t1 = a[0]; + t2 = b[0]; + r[0] = (t1 - t2 - c)&BN_MASK2; + if(t1 != t2) { c = (t1 < t2); } + if(--n <= 0) { break; } + + t1 = a[1]; + t2 = b[1]; + r[1] = (t1 - t2 - c)&BN_MASK2; + if(t1 != t2) { c = (t1 < t2); } + if(--n <= 0) { break; } + + t1 = a[2]; + t2 = b[2]; + r[2] = (t1 - t2 - c)&BN_MASK2; + if(t1 != t2) { c = (t1 < t2); } + if(--n <= 0) { break; } + + t1 = a[3]; + t2 = b[3]; + r[3] = (t1 - t2 - c)&BN_MASK2; + if(t1 != t2) { c = (t1 < t2); } + if(--n <= 0) { break; } + + a += 4; + b += 4; + r += 4; + } + return (c); +} + +#ifdef BN_MUL_COMBA + +#undef bn_mul_comba8 +#undef bn_mul_comba4 +#undef bn_sqr_comba8 +#undef bn_sqr_comba4 + +/* mul_add_c(a,b,c0,c1,c2) -- c+=a*b for three word number c=(c2,c1,c0) */ +/* mul_add_c2(a,b,c0,c1,c2) -- c+=2*a*b for three word number c=(c2,c1,c0) */ +/* sqr_add_c(a,i,c0,c1,c2) -- c+=a[i]^2 for three word number c=(c2,c1,c0) */ +/* sqr_add_c2(a,i,c0,c1,c2) -- c+=2*a[i]*a[j] for three word number c=(c2,c1,c0) */ + +#ifdef BN_LLONG +#define mul_add_c(a,b,c0,c1,c2) \ + t=(BN_ULLONG)a*b; \ + t1=(BN_ULONG)Lw(t); \ + t2=(BN_ULONG)Hw(t); \ + c0=(c0+t1)&BN_MASK2; if ((c0) < t1) t2++; \ + c1=(c1+t2)&BN_MASK2; if ((c1) < t2) c2++; + +#define mul_add_c2(a,b,c0,c1,c2) \ + t=(BN_ULLONG)a*b; \ + tt=(t+t)&BN_MASK; \ + if (tt < t) c2++; \ + t1=(BN_ULONG)Lw(tt); \ + t2=(BN_ULONG)Hw(tt); \ + c0=(c0+t1)&BN_MASK2; \ + if ((c0 < t1) && (((++t2)&BN_MASK2) == 0)) c2++; \ + c1=(c1+t2)&BN_MASK2; if ((c1) < t2) c2++; + +#define sqr_add_c(a,i,c0,c1,c2) \ + t=(BN_ULLONG)a[i]*a[i]; \ + t1=(BN_ULONG)Lw(t); \ + t2=(BN_ULONG)Hw(t); \ + c0=(c0+t1)&BN_MASK2; if ((c0) < t1) t2++; \ + c1=(c1+t2)&BN_MASK2; if ((c1) < t2) c2++; + +#define sqr_add_c2(a,i,j,c0,c1,c2) \ + mul_add_c2((a)[i],(a)[j],c0,c1,c2) + +#elif defined(BN_UMULT_HIGH) + +#define mul_add_c(a,b,c0,c1,c2) { \ + BN_ULONG ta=(a),tb=(b); \ + t1 = ta * tb; \ + t2 = BN_UMULT_HIGH(ta,tb); \ + c0 += t1; t2 += (c0 +#include +#include "bn.h" +#include "openssl_mods.h" + +BN_CTX *BN_CTX_new(void) +{ + BN_CTX *ret; + + ret = (BN_CTX *)OPENSSL_malloc(sizeof(BN_CTX)); + if(ret == NULL) + { + return (NULL); + } + + BN_CTX_init(ret); + ret->flags = BN_FLG_MALLOCED; + return (ret); +} + +void BN_CTX_init(BN_CTX *ctx) +{ + int i; + ctx->tos = 0; + ctx->flags = 0; + ctx->depth = 0; + ctx->too_many = 0; + for(i = 0; i < BN_CTX_NUM; i++) + { BN_init(&(ctx->bn[i])); } +} + +void BN_CTX_free(BN_CTX *ctx) +{ + int i; + + if(ctx == NULL) { return; } + assert(ctx->depth == 0); + + for(i = 0; i < BN_CTX_NUM; i++) + { BN_clear_free(&(ctx->bn[i])); } + if(ctx->flags & BN_FLG_MALLOCED) + { OPENSSL_free(ctx); } +} + +void BN_CTX_start(BN_CTX *ctx) +{ + if(ctx->depth < BN_CTX_NUM_POS) + { ctx->pos[ctx->depth] = ctx->tos; } + ctx->depth++; +} + +BIGNUM *BN_CTX_get(BN_CTX *ctx) +{ + if(ctx->depth > BN_CTX_NUM_POS || ctx->tos >= BN_CTX_NUM) + { + if(!ctx->too_many) + { + /* disable error code until BN_CTX_end is called: */ + ctx->too_many = 1; + } + return NULL; + } + return (&(ctx->bn[ctx->tos++])); +} + +void BN_CTX_end(BN_CTX *ctx) +{ + if(ctx == NULL) { return; } + assert(ctx->depth > 0); + if(ctx->depth == 0) + /* should never happen, but we can tolerate it if not in + * debug mode (could be a 'goto err' in the calling function + * before BN_CTX_start was reached) */ + { BN_CTX_start(ctx); } + + ctx->too_many = 0; + ctx->depth--; + if(ctx->depth < BN_CTX_NUM_POS) + { ctx->tos = ctx->pos[ctx->depth]; } +} +#endif diff --git a/cscrypt/bn_div.c b/cscrypt/bn_div.c new file mode 100644 index 0000000..8e70169 --- /dev/null +++ b/cscrypt/bn_div.c @@ -0,0 +1,391 @@ +#include "bn.h" + +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/bn/bn_div.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#include +#include "openssl_mods.h" +#include "bn_lcl.h" + +/* The old slow way */ +#if 0 +int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, + BN_CTX *ctx) +{ + int i, nm, nd; + int ret = 0; + BIGNUM *D; + + bn_check_top(m); + bn_check_top(d); + if(BN_is_zero(d)) + { + return (0); + } + + if(BN_ucmp(m, d) < 0) + { + if(rem != NULL) + { + if(BN_copy(rem, m) == NULL) { return (0); } + } + if(dv != NULL) { BN_zero(dv); } + return (1); + } + + BN_CTX_start(ctx); + D = BN_CTX_get(ctx); + if(dv == NULL) { dv = BN_CTX_get(ctx); } + if(rem == NULL) { rem = BN_CTX_get(ctx); } + if(D == NULL || dv == NULL || rem == NULL) + { goto end; } + + nd = BN_num_bits(d); + nm = BN_num_bits(m); + if(BN_copy(D, d) == NULL) { goto end; } + if(BN_copy(rem, m) == NULL) { goto end; } + + /* The next 2 are needed so we can do a dv->d[0]|=1 later + * since BN_lshift1 will only work once there is a value :-) */ + BN_zero(dv); + bn_wexpand(dv, 1); + dv->top = 1; + + if(!BN_lshift(D, D, nm - nd)) { goto end; } + for(i = nm - nd; i >= 0; i--) + { + if(!BN_lshift1(dv, dv)) { goto end; } + if(BN_ucmp(rem, D) >= 0) + { + dv->d[0] |= 1; + if(!BN_usub(rem, rem, D)) { goto end; } + } + /* CAN IMPROVE (and have now :=) */ + if(!BN_rshift1(D, D)) { goto end; } + } + rem->neg = BN_is_zero(rem) ? 0 : m->neg; + dv->neg = m->neg ^ d->neg; + ret = 1; +end: + BN_CTX_end(ctx); + return (ret); +} + +#else + +#if !defined(NO_ASM) && !defined(NO_INLINE_ASM) && !defined(PEDANTIC) && !defined(BN_DIV3W) +# if defined(__GNUC__) && __GNUC__>=2 +# if defined(__i386) || defined (__i386__) +/* + * There were two reasons for implementing this template: + * - GNU C generates a call to a function (__udivdi3 to be exact) + * in reply to ((((BN_ULLONG)n0)< + */ +# define bn_div_words(n0,n1,d0) \ + ({ __asm__ volatile ( \ + "divl %4" \ + : "=a"(q), "=d"(rem) \ + : "a"(n1), "d"(n0), "g"(d0) \ + : "cc"); \ + q; \ + }) +# define REMAINDER_IS_ALREADY_CALCULATED +# endif /* __ */ +# endif /* __GNUC__ */ +#endif /* NO_ASM */ + +int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor, + BN_CTX *ctx) +{ + int norm_shift, i, j, loop; + BIGNUM *tmp, wnum, *snum, *sdiv, *res; + BN_ULONG *resp, *wnump; + BN_ULONG d0, d1; + int num_n, div_n; + + bn_check_top(num); + bn_check_top(divisor); + + if(BN_is_zero(divisor)) + { + return (0); + } + + if(BN_ucmp(num, divisor) < 0) + { + if(rm != NULL) + { + if(BN_copy(rm, num) == NULL) { return (0); } + } + if(dv != NULL) { BN_zero(dv); } + return (1); + } + + BN_CTX_start(ctx); + tmp = BN_CTX_get(ctx); + snum = BN_CTX_get(ctx); + sdiv = BN_CTX_get(ctx); + if(dv == NULL) + { res = BN_CTX_get(ctx); } + else { res = dv; } + if(sdiv == NULL || res == NULL) { goto err; } + tmp->neg = 0; + + /* First we normalise the numbers */ + norm_shift = BN_BITS2 - ((BN_num_bits(divisor)) % BN_BITS2); + BN_lshift(sdiv, divisor, norm_shift); + sdiv->neg = 0; + norm_shift += BN_BITS2; + BN_lshift(snum, num, norm_shift); + snum->neg = 0; + div_n = sdiv->top; + num_n = snum->top; + loop = num_n - div_n; + + /* Lets setup a 'window' into snum + * This is the part that corresponds to the current + * 'area' being divided */ + BN_init(&wnum); + wnum.d = &(snum->d[loop]); + wnum.top = div_n; + wnum.dmax = snum->dmax + 1; /* a bit of a lie */ + + /* Get the top 2 words of sdiv */ + /* i=sdiv->top; */ + d0 = sdiv->d[div_n - 1]; + d1 = (div_n == 1) ? 0 : sdiv->d[div_n - 2]; + + /* pointer to the 'top' of snum */ + wnump = &(snum->d[num_n - 1]); + + /* Setup to 'res' */ + res->neg = (num->neg ^ divisor->neg); + if(!bn_wexpand(res, (loop + 1))) { goto err; } + res->top = loop; + resp = &(res->d[loop - 1]); + + /* space for temp */ + if(!bn_wexpand(tmp, (div_n + 1))) { goto err; } + + if(BN_ucmp(&wnum, sdiv) >= 0) + { + if(!BN_usub(&wnum, &wnum, sdiv)) { goto err; } + *resp = 1; + res->d[res->top - 1] = 1; + } + else + { res->top--; } + resp--; + + for(i = 0; i < loop - 1; i++) + { + BN_ULONG q, l0; +#if defined(BN_DIV3W) && !defined(NO_ASM) + BN_ULONG bn_div_3_words(BN_ULONG *, BN_ULONG, BN_ULONG); + q = bn_div_3_words(wnump, d1, d0); +#else + BN_ULONG n0, n1, rem = 0; + + n0 = wnump[0]; + n1 = wnump[-1]; + if(n0 == d0) + { q = BN_MASK2; } + else /* n0 < d0 */ + { +#ifdef BN_LLONG + BN_ULLONG t2; + +#if defined(BN_LLONG) && defined(BN_DIV2W) && !defined(bn_div_words) + q = (BN_ULONG)(((((BN_ULLONG)n0) << BN_BITS2) | n1) / d0); +#else + q = bn_div_words(n0, n1, d0); +#endif + +#ifndef REMAINDER_IS_ALREADY_CALCULATED + /* + * rem doesn't have to be BN_ULLONG. The least we + * know it's less that d0, isn't it? + */ + rem = (n1 - q * d0)&BN_MASK2; +#endif + t2 = (BN_ULLONG)d1 * q; + + for(;;) + { + if(t2 <= ((((BN_ULLONG)rem) << BN_BITS2) | wnump[-2])) + { break; } + q--; + rem += d0; + if(rem < d0) { break; } /* don't let rem overflow */ + t2 -= d1; + } +#else /* !BN_LLONG */ + BN_ULONG t2l, t2h, ql, qh; + + q = bn_div_words(n0, n1, d0); +#ifndef REMAINDER_IS_ALREADY_CALCULATED + rem = (n1 - q * d0)&BN_MASK2; +#endif + +#ifdef BN_UMULT_HIGH + t2l = d1 * q; + t2h = BN_UMULT_HIGH(d1, q); +#else + t2l = LBITS(d1); + t2h = HBITS(d1); + ql = LBITS(q); + qh = HBITS(q); + mul64(t2l, t2h, ql, qh); /* t2=(BN_ULLONG)d1*q; */ +#endif + + for(;;) + { + if((t2h < rem) || + ((t2h == rem) && (t2l <= wnump[-2]))) + { break; } + q--; + rem += d0; + if(rem < d0) { break; } /* don't let rem overflow */ + if(t2l < d1) { t2h--; } + t2l -= d1; + } +#endif /* !BN_LLONG */ + } +#endif /* !BN_DIV3W */ + + l0 = bn_mul_words(tmp->d, sdiv->d, div_n, q); + wnum.d--; + wnum.top++; + tmp->d[div_n] = l0; + for(j = div_n + 1; j > 0; j--) + if(tmp->d[j - 1]) { break; } + tmp->top = j; + + j = wnum.top; + BN_sub(&wnum, &wnum, tmp); + + snum->top = snum->top + wnum.top - j; + + if(wnum.neg) + { + q--; + j = wnum.top; + BN_add(&wnum, &wnum, sdiv); + snum->top += wnum.top - j; + } + *(resp--) = q; + wnump--; + } + if(rm != NULL) + { + BN_rshift(rm, snum, norm_shift); + rm->neg = num->neg; + } + BN_CTX_end(ctx); + return (1); +err: + BN_CTX_end(ctx); + return (0); +} + +#endif + +/* rem != m */ +int BN_mod(BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx) +{ +#if 0 /* The old slow way */ + int i, nm, nd; + BIGNUM *dv; + + if(BN_ucmp(m, d) < 0) + { return ((BN_copy(rem, m) == NULL) ? 0 : 1); } + + BN_CTX_start(ctx); + dv = BN_CTX_get(ctx); + + if(!BN_copy(rem, m)) { goto err; } + + nm = BN_num_bits(rem); + nd = BN_num_bits(d); + if(!BN_lshift(dv, d, nm - nd)) { goto err; } + for(i = nm - nd; i >= 0; i--) + { + if(BN_cmp(rem, dv) >= 0) + { + if(!BN_sub(rem, rem, dv)) { goto err; } + } + if(!BN_rshift1(dv, dv)) { goto err; } + } + BN_CTX_end(ctx); + return (1); +err: + BN_CTX_end(ctx); + return (0); +#else + return (BN_div(NULL, rem, m, d, ctx)); +#endif +} + +#endif diff --git a/cscrypt/bn_exp.c b/cscrypt/bn_exp.c new file mode 100644 index 0000000..6c5d585 --- /dev/null +++ b/cscrypt/bn_exp.c @@ -0,0 +1,529 @@ +#include "bn.h" + +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/bn/bn_exp.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ +/* ==================================================================== + * Copyright (c) 1998-2000 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + +#include +#include "bn_lcl.h" + +#define TABLE_SIZE 32 + +/* slow but works */ +int BN_mod_mul(BIGNUM *ret, BIGNUM *a, BIGNUM *b, const BIGNUM *m, BN_CTX *ctx) +{ + BIGNUM *t; + int r = 0; + + bn_check_top(a); + bn_check_top(b); + bn_check_top(m); + + BN_CTX_start(ctx); + if((t = BN_CTX_get(ctx)) == NULL) { goto err; } + if(a == b) + { + if(!BN_sqr(t, a, ctx)) { goto err; } + } + else + { + if(!BN_mul(t, a, b, ctx)) { goto err; } + } + if(!BN_mod(ret, t, m, ctx)) { goto err; } + r = 1; +err: + BN_CTX_end(ctx); + return (r); +} + + +int BN_mod_exp(BIGNUM *r, BIGNUM *a, const BIGNUM *p, const BIGNUM *m, + BN_CTX *ctx) +{ + int ret; + + bn_check_top(a); + bn_check_top(p); + bn_check_top(m); + + ret = BN_mod_exp_simple(r, a, p, m, ctx); + + return (ret); +} + + + + + +/* The old fallback, simple version :-) */ +int BN_mod_exp_simple(BIGNUM *r, BIGNUM *a, const BIGNUM *p, const BIGNUM *m, + BN_CTX *ctx) +{ + int i, j = 0, bits, ret = 0, wstart = 0, wend = 0, window, wvalue = 0, ts = 0; + int start = 1; + BIGNUM *d; + BIGNUM val[TABLE_SIZE]; + + bits = BN_num_bits(p); + + if(bits == 0) + { + BN_one(r); + return (1); + } + + BN_CTX_start(ctx); + if((d = BN_CTX_get(ctx)) == NULL) { goto err; } + + BN_init(&(val[0])); + ts = 1; + if(!BN_mod(&(val[0]), a, m, ctx)) { goto err; } /* 1 */ + + window = BN_window_bits_for_exponent_size(bits); + if(window > 1) + { + if(!BN_mod_mul(d, &(val[0]), &(val[0]), m, ctx)) + { goto err; } /* 2 */ + j = 1 << (window - 1); + for(i = 1; i < j; i++) + { + BN_init(&(val[i])); + if(!BN_mod_mul(&(val[i]), &(val[i - 1]), d, m, ctx)) + { goto err; } + } + ts = i; + } + + start = 1; /* This is used to avoid multiplication etc + * when there is only the value '1' in the + * buffer. */ + wstart = bits - 1; /* The top bit of the window */ + + if(!BN_one(r)) { goto err; } + + for(;;) + { + if(BN_is_bit_set(p, wstart) == 0) + { + if(!start) + if(!BN_mod_mul(r, r, r, m, ctx)) + { goto err; } + if(wstart == 0) { break; } + wstart--; + continue; + } + /* We now have wstart on a 'set' bit, we now need to work out + * how bit a window to do. To do this we need to scan + * forward until the last set bit before the end of the + * window */ + j = wstart; + wvalue = 1; + wend = 0; + for(i = 1; i < window; i++) + { + if(wstart - i < 0) { break; } + if(BN_is_bit_set(p, wstart - i)) + { + wvalue <<= (i - wend); + wvalue |= 1; + wend = i; + } + } + + /* wend is the size of the current window */ + j = wend + 1; + /* add the 'bytes above' */ + if(!start) + for(i = 0; i < j; i++) + { + if(!BN_mod_mul(r, r, r, m, ctx)) + { goto err; } + } + + /* wvalue will be an odd number < 2^window */ + if(!BN_mod_mul(r, r, &(val[wvalue >> 1]), m, ctx)) + { goto err; } + + /* move the 'window' down further */ + wstart -= wend + 1; + wvalue = 0; + start = 0; + if(wstart < 0) { break; } + } + ret = 1; +err: + BN_CTX_end(ctx); + for(i = 0; i < ts; i++) + { BN_clear_free(&(val[i])); } + return (ret); +} + +int +BN_nnmod(BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx) +{ + /* like BN_mod, but returns non-negative remainder + * (i.e., 0 <= r < |d| always holds) + */ + + if (!(BN_mod(r, m, d, ctx))) + return 0; + if (!r->neg) + return 1; + /* now -|d| < r < 0, so we have to set r : = r + |d| */ + return (d->neg ? BN_sub : BN_add)(r, r, d); +} + +/* solves ax == 1 (mod n) */ +BIGNUM * +BN_mod_inverse(BIGNUM *ret, BIGNUM *a, const BIGNUM *n, BN_CTX *ctx) +{ + BIGNUM *A, *B, *X, *Y, *M, *D, *T = NULL; + int sign; + + bn_check_top(a); + bn_check_top(n); + + BN_CTX_start(ctx); + A = BN_CTX_get(ctx); + B = BN_CTX_get(ctx); + X = BN_CTX_get(ctx); + D = BN_CTX_get(ctx); + M = BN_CTX_get(ctx); + Y = BN_CTX_get(ctx); + T = BN_CTX_get(ctx); + if (T == NULL) goto err; + + if (ret == NULL) goto err; + + BN_one(X); + BN_zero(Y); + if (BN_copy(B, a) == NULL) goto err; + if (BN_copy(A, n) == NULL) goto err; + A->neg = 0; + if (B->neg || (BN_ucmp(B, A) >= 0)) { + if (!BN_nnmod(B, B, A, ctx)) goto err; + } + sign = -1; + /* From B = a mod |n|, A = |n| it follows that + * + * 0 <= B < A, + * -sign*X*a == B (mod |n|), + * sign*Y*a == A (mod |n|). + */ + + if (BN_is_odd(n) && (BN_num_bits(n) <= (BN_BITS <= 32 ? 450 : 2048))) { + /* Binary inversion algorithm; requires odd modulus. + * This is faster than the general algorithm if the modulus + * is sufficiently small (about 400 .. 500 bits on 32-bit + * sytems, but much more on 64-bit systems) + */ + int shift; + + while (!BN_is_zero(B)) { + /* + * 0 < B < |n|, + * 0 < A <= |n|, + * (1) -sign*X*a == B (mod |n|), + * (2) sign*Y*a == A (mod |n|) + */ + + /* Now divide B by the maximum possible power of two in the integers, + * and divide X by the same value mod |n|. + * When we're done, (1) still holds. + */ + shift = 0; + while (!BN_is_bit_set(B, shift)) /* note that 0 < B */ { + shift++; + + if (BN_is_odd(X)) { + if (!BN_uadd(X, X, n)) goto err; + } + /* now X is even, so we can easily divide it by two */ + if (!BN_rshift1(X, X)) goto err; + } + if (shift > 0) { + if (!BN_rshift(B, B, shift)) goto err; + } + + + /* Same for A and Y. Afterwards, (2) still holds. */ + shift = 0; + while (!BN_is_bit_set(A, shift)) /* note that 0 < A */ { + shift++; + + if (BN_is_odd(Y)) { + if (!BN_uadd(Y, Y, n)) goto err; + } + /* now Y is even */ + if (!BN_rshift1(Y, Y)) goto err; + } + if (shift > 0) { + if (!BN_rshift(A, A, shift)) goto err; + } + + /* We still have (1) and (2). + * Both A and B are odd. + * The following computations ensure that + * + * 0 <= B < |n|, + * 0 < A < |n|, + * (1) -sign*X*a == B (mod |n|), + * (2) sign*Y*a == A (mod |n|), + * + * and that either A or B is even in the next iteration. + */ + if (BN_ucmp(B, A) >= 0) { + /* -sign*(X + Y)*a == B - A (mod |n|) */ + if (!BN_uadd(X, X, Y)) goto err; + /* NB: we could use BN_mod_add_quick(X, X, Y, n), but that + * actually makes the algorithm slower + */ + if (!BN_usub(B, B, A)) goto err; + } else { + /* sign*(X + Y)*a == A - B (mod |n|) */ + if (!BN_uadd(Y, Y, X)) goto err; + /* as above, BN_mod_add_quick(Y, Y, X, n) would slow things down */ + if (!BN_usub(A, A, B)) goto err; + } + } + } else { + /* general inversion algorithm */ + + while (!BN_is_zero(B)) { + BIGNUM *tmp; + + /* + * 0 < B < A, + * (*) -sign*X*a == B (mod |n|), + * sign*Y*a == A (mod |n|) + */ + + /* (D, M) : = (A/B, A%B) ... */ + if (BN_num_bits(A) == BN_num_bits(B)) { + if (!BN_one(D)) goto err; + if (!BN_sub(M, A, B)) goto err; + } else if (BN_num_bits(A) == BN_num_bits(B) + 1) { + /* A/B is 1, 2, or 3 */ + if (!BN_lshift1(T, B)) goto err; + if (BN_ucmp(A, T) < 0) { + /* A < 2*B, so D = 1 */ + if (!BN_one(D)) goto err; + if (!BN_sub(M, A, B)) goto err; + } else { + /* A >= 2*B, so D = 2 or D = 3 */ + if (!BN_sub(M, A, T)) goto err; + if (!BN_add(D, T, B)) goto err; + /* use D ( := 3 * B) as temp */ + if (BN_ucmp(A, D) < 0) { + /* A < 3*B, so D = 2 */ + if (!BN_set_word(D, 2)) goto err; + /* M ( = A - 2*B) already has the correct value */ + } else { + /* only D = 3 remains */ + if (!BN_set_word(D, 3)) goto err; + /* currently M = A - 2 * B, + * but we need M = A - 3 * B + */ + if (!BN_sub(M, M, B)) goto err; + } + } + } else { + if (!BN_div(D, M, A, B, ctx)) goto err; + } + + /* Now + * A = D*B + M; + * thus we have + * (**) sign*Y*a == D*B + M (mod |n|). + */ + + tmp = A; /* keep the BIGNUM object, the value does not matter */ + + /* (A, B) : = (B, A mod B) ... */ + A = B; + B = M; + /* ... so we have 0 <= B < A again */ + + /* Since the former M is now B and the former B is now A, + * (**) translates into + * sign*Y*a == D*A + B (mod |n|), + * i.e. + * sign*Y*a - D*A == B (mod |n|). + * Similarly, (*) translates into + * -sign*X*a == A (mod |n|). + * + * Thus, + * sign*Y*a + D*sign*X*a == B (mod |n|), + * i.e. + * sign*(Y + D*X)*a == B (mod |n|). + * + * So if we set (X, Y, sign) : = (Y + D*X, X, -sign), we arrive back at + * -sign*X*a == B (mod |n|), + * sign*Y*a == A (mod |n|). + * Note that X and Y stay non-negative all the time. + */ + + /* most of the time D is very small, so we can optimize tmp : = D*X+Y */ + if (BN_is_one(D)) { + if (!BN_add(tmp, X, Y)) goto err; + } else { + if (BN_is_word(D, 2)) { + if (!BN_lshift1(tmp, X)) goto err; + } else if (BN_is_word(D, 4)) { + if (!BN_lshift(tmp, X, 2)) goto err; + } else if (D->top == 1) { + if (!BN_copy(tmp, X)) goto err; + if (!BN_mul_word(tmp, D->d[0])) goto err; + } else { + if (!BN_mul(tmp, D, X, ctx)) goto err; + } + if (!BN_add(tmp, tmp, Y)) goto err; + } + + M = Y; /* keep the BIGNUM object, the value does not matter */ + Y = X; + X = tmp; + sign = -sign; + } + } + + /* + * The while loop (Euclid's algorithm) ends when + * A == gcd(a, n); + * we have + * sign*Y*a == A (mod |n|), + * where Y is non-negative. + */ + + if (sign < 0) { + if (!BN_sub(Y, n, Y)) goto err; + } + /* Now Y*a == A (mod |n|). */ + + + if (BN_is_one(A)) { + /* Y*a == 1 (mod |n|) */ + if (!Y->neg && BN_ucmp(Y, n) < 0) { + if (!BN_copy(ret, Y)) goto err; + } else { + if (!BN_nnmod(ret, Y, n, ctx)) goto err; + } + } else { + goto err; + } +err: + BN_CTX_end(ctx); + return (ret); +} + +#endif diff --git a/cscrypt/bn_lcl.h b/cscrypt/bn_lcl.h new file mode 100644 index 0000000..71e4204 --- /dev/null +++ b/cscrypt/bn_lcl.h @@ -0,0 +1,419 @@ +/* crypto/bn/bn_lcl.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ +/* ==================================================================== + * Copyright (c) 1998-2000 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_BN_LCL_H +#define HEADER_BN_LCL_H + +#include "bn.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + /* + * BN_window_bits_for_exponent_size -- macro for sliding window mod_exp functions + * + * + * For window size 'w' (w >= 2) and a random 'b' bits exponent, + * the number of multiplications is a constant plus on average + * + * 2^(w-1) + (b-w)/(w+1); + * + * here 2^(w-1) is for precomputing the table (we actually need + * entries only for windows that have the lowest bit set), and + * (b-w)/(w+1) is an approximation for the expected number of + * w-bit windows, not counting the first one. + * + * Thus we should use + * + * w >= 6 if b > 671 + * w = 5 if 671 > b > 239 + * w = 4 if 239 > b > 79 + * w = 3 if 79 > b > 23 + * w <= 2 if 23 > b + * + * (with draws in between). Very small exponents are often selected + * with low Hamming weight, so we use w = 1 for b <= 23. + */ +#if 1 +#define BN_window_bits_for_exponent_size(b) \ + ((b) > 671 ? 6 : \ + (b) > 239 ? 5 : \ + (b) > 79 ? 4 : \ + (b) > 23 ? 3 : 1) +#else + /* Old SSLeay/OpenSSL table. + * Maximum window size was 5, so this table differs for b==1024; + * but it coincides for other interesting values (b==160, b==512). + */ +#define BN_window_bits_for_exponent_size(b) \ + ((b) > 255 ? 5 : \ + (b) > 127 ? 4 : \ + (b) > 17 ? 3 : 1) +#endif + + + + /* Pentium pro 16,16,16,32,64 */ + /* Alpha 16,16,16,16.64 */ +#define BN_MULL_SIZE_NORMAL (16) /* 32 */ +#define BN_MUL_RECURSIVE_SIZE_NORMAL (16) /* 32 less than */ +#define BN_SQR_RECURSIVE_SIZE_NORMAL (16) /* 32 */ +#define BN_MUL_LOW_RECURSIVE_SIZE_NORMAL (32) /* 32 */ +#define BN_MONT_CTX_SET_SIZE_WORD (64) /* 32 */ + +#if !defined(NO_ASM) && !defined(NO_INLINE_ASM) && !defined(PEDANTIC) + /* + * BN_UMULT_HIGH section. + * + * No, I'm not trying to overwhelm you when stating that the + * product of N-bit numbers is 2*N bits wide:-) No, I don't expect + * you to be impressed when I say that if the compiler doesn't + * support 2*N integer type, then you have to replace every N*N + * multiplication with 4 (N/2)*(N/2) accompanied by some shifts + * and additions which unavoidably results in severe performance + * penalties. Of course provided that the hardware is capable of + * producing 2*N result... That's when you normally start + * considering assembler implementation. However! It should be + * pointed out that some CPUs (most notably Alpha, PowerPC and + * upcoming IA-64 family:-) provide *separate* instruction + * calculating the upper half of the product placing the result + * into a general purpose register. Now *if* the compiler supports + * inline assembler, then it's not impossible to implement the + * "bignum" routines (and have the compiler optimize 'em) + * exhibiting "native" performance in C. That's what BN_UMULT_HIGH + * macro is about:-) + * + * + */ +# if defined(__alpha) && (defined(SIXTY_FOUR_BIT_LONG) || defined(SIXTY_FOUR_BIT)) +# if defined(__DECC) +# include +# define BN_UMULT_HIGH(a,b) (BN_ULONG)asm("umulh %a0,%a1,%v0",(a),(b)) +# elif defined(__GNUC__) +# define BN_UMULT_HIGH(a,b) ({ \ + register BN_ULONG ret; \ + __asm__ ("umulh %1,%2,%0" \ + : "=r"(ret) \ + : "r"(a), "r"(b)); \ + ret; }) +# endif /* compiler */ +# elif defined(_ARCH_PPC) && defined(__64BIT__) && defined(SIXTY_FOUR_BIT_LONG) +# if defined(__GNUC__) +# define BN_UMULT_HIGH(a,b) ({ \ + register BN_ULONG ret; \ + __asm__ ("mulhdu %0,%1,%2" \ + : "=r"(ret) \ + : "r"(a), "r"(b)); \ + ret; }) +# endif /* compiler */ +# endif /* cpu */ +#endif /* NO_ASM */ + + /************************************************************* + * Using the long long type + */ +#define Lw(t) (((BN_ULONG)(t))&BN_MASK2) +#define Hw(t) (((BN_ULONG)((t)>>BN_BITS2))&BN_MASK2) + + /* This is used for internal error checking and is not normally used */ +#ifdef BN_DEBUG +# include +# define bn_check_top(a) assert ((a)->top >= 0 && (a)->top <= (a)->dmax); +#else +# define bn_check_top(a) +#endif + + /* This macro is to add extra stuff for development checking */ +#ifdef BN_DEBUG +#define bn_set_max(r) ((r)->max=(r)->top,BN_set_flags((r),BN_FLG_STATIC_DATA)) +#else +#define bn_set_max(r) +#endif + + /* These macros are used to 'take' a section of a bignum for read only use */ +#define bn_set_low(r,a,n) \ + { \ + (r)->top=((a)->top > (n))?(n):(a)->top; \ + (r)->d=(a)->d; \ + (r)->neg=(a)->neg; \ + (r)->flags|=BN_FLG_STATIC_DATA; \ + bn_set_max(r); \ + } + +#define bn_set_high(r,a,n) \ + { \ + if ((a)->top > (n)) \ + { \ + (r)->top=(a)->top-n; \ + (r)->d= &((a)->d[n]); \ + } \ + else \ + (r)->top=0; \ + (r)->neg=(a)->neg; \ + (r)->flags|=BN_FLG_STATIC_DATA; \ + bn_set_max(r); \ + } + +#ifdef BN_LLONG +#define mul_add(r,a,w,c) { \ + BN_ULLONG t; \ + t=(BN_ULLONG)w * (a) + (r) + (c); \ + (r)= Lw(t); \ + (c)= Hw(t); \ + } + +#define mul(r,a,w,c) { \ + BN_ULLONG t; \ + t=(BN_ULLONG)w * (a) + (c); \ + (r)= Lw(t); \ + (c)= Hw(t); \ + } + +#define sqr(r0,r1,a) { \ + BN_ULLONG t; \ + t=(BN_ULLONG)(a)*(a); \ + (r0)=Lw(t); \ + (r1)=Hw(t); \ + } + +#elif defined(BN_UMULT_HIGH) +#define mul_add(r,a,w,c) { \ + BN_ULONG high,low,ret,tmp=(a); \ + ret = (r); \ + high= BN_UMULT_HIGH(w,tmp); \ + ret += (c); \ + low = (w) * tmp; \ + (c) = (ret<(c))?1:0; \ + (c) += high; \ + ret += low; \ + (c) += (ret>BN_BITS4)&BN_MASK2l) +#define L2HBITS(a) ((BN_ULONG)((a)&BN_MASK2l)<>BN_BITS2)&BN_MASKl) +#define LL2HBITS(a) ((BN_ULLONG)((a)&BN_MASKl)<>(BN_BITS4-1); \ + m =(m&BN_MASK2l)<<(BN_BITS4+1); \ + l=(l+m)&BN_MASK2; if (l < m) h++; \ + (lo)=l; \ + (ho)=h; \ + } + +#define mul_add(r,a,bl,bh,c) { \ + BN_ULONG l,h; \ + \ + h= (a); \ + l=LBITS(h); \ + h=HBITS(h); \ + mul64(l,h,(bl),(bh)); \ + \ + /* non-multiply part */ \ + l=(l+(c))&BN_MASK2; if (l < (c)) h++; \ + (c)=(r); \ + l=(l+(c))&BN_MASK2; if (l < (c)) h++; \ + (c)=h&BN_MASK2; \ + (r)=l; \ + } + +#define mul(r,a,bl,bh,c) { \ + BN_ULONG l,h; \ + \ + h= (a); \ + l=LBITS(h); \ + h=HBITS(h); \ + mul64(l,h,(bl),(bh)); \ + \ + /* non-multiply part */ \ + l+=(c); if ((l&BN_MASK2) < (c)) h++; \ + (c)=h&BN_MASK2; \ + (r)=l&BN_MASK2; \ + } +#endif /* !BN_LLONG */ + + void bn_mul_normal(BN_ULONG *r, BN_ULONG *a, int na, BN_ULONG *b, int nb); + void bn_mul_comba8(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b); + void bn_mul_comba4(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b); + void bn_sqr_normal(BN_ULONG *r, BN_ULONG *a, int n, BN_ULONG *tmp); + void bn_sqr_comba8(BN_ULONG *r, BN_ULONG *a); + void bn_sqr_comba4(BN_ULONG *r, BN_ULONG *a); + int bn_cmp_words(BN_ULONG *a, BN_ULONG *b, int n); + void bn_mul_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n2, BN_ULONG *t); + void bn_mul_part_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, + int tn, int n, BN_ULONG *t); + void bn_sqr_recursive(BN_ULONG *r, BN_ULONG *a, int n2, BN_ULONG *t); + void bn_mul_low_normal(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n); + void bn_mul_low_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n2, + BN_ULONG *t); + void bn_mul_high(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, BN_ULONG *l, int n2, + BN_ULONG *t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cscrypt/bn_lib.c b/cscrypt/bn_lib.c new file mode 100644 index 0000000..5210643 --- /dev/null +++ b/cscrypt/bn_lib.c @@ -0,0 +1,775 @@ +#include "bn.h" + +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/bn/bn_lib.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#ifndef BN_DEBUG +# undef NDEBUG /* avoid conflicting definitions */ +# define NDEBUG +#endif + +#include +#include +#include +#include +#include "bn_lcl.h" +#include "openssl_mods.h" + +//const char *BN_version="Big Number 42"; + +/* For a 32 bit machine + * 2 - 4 == 128 + * 3 - 8 == 256 + * 4 - 16 == 512 + * 5 - 32 == 1024 + * 6 - 64 == 2048 + * 7 - 128 == 4096 + * 8 - 256 == 8192 + */ +static int bn_limit_bits = 0; +static int bn_limit_num = 8; /* (1<= 0) + { + if(mult > (int)(sizeof(int) * 8) - 1) + { mult = sizeof(int) * 8 - 1; } + bn_limit_bits = mult; + bn_limit_num = 1 << mult; + } + if(high >= 0) + { + if(high > (int)(sizeof(int) * 8) - 1) + { high = sizeof(int) * 8 - 1; } + bn_limit_bits_high = high; + bn_limit_num_high = 1 << high; + } + if(low >= 0) + { + if(low > (int)(sizeof(int) * 8) - 1) + { low = sizeof(int) * 8 - 1; } + bn_limit_bits_low = low; + bn_limit_num_low = 1 << low; + } + if(mont >= 0) + { + if(mont > (int)(sizeof(int) * 8) - 1) + { mont = sizeof(int) * 8 - 1; } + bn_limit_bits_mont = mont; + bn_limit_num_mont = 1 << mont; + } +} + +int BN_get_params(int which) +{ + if(which == 0) { return (bn_limit_bits); } + else if(which == 1) { return (bn_limit_bits_high); } + else if(which == 2) { return (bn_limit_bits_low); } + else if(which == 3) { return (bn_limit_bits_mont); } + else { return (0); } +} + +const BIGNUM *BN_value_one(void) +{ + static const BN_ULONG data_one = 1L; + static const BIGNUM const_one = {(BN_ULONG *) &data_one, 1, 1, 0, BN_FLG_STATIC_DATA}; + + return (&const_one); +} + +char *BN_options(void) +{ + static int init = 0; + static char data[16]; + + if(!init) + { + init++; +#ifdef BN_LLONG + snprintf(data, sizeof(data), "bn(%d,%d)", (int)sizeof(BN_ULLONG) * 8, + (int)sizeof(BN_ULONG) * 8); +#else + snprintf(data, sizeof(data), "bn(%d,%d)", (int)sizeof(BN_ULONG) * 8, + (int)sizeof(BN_ULONG) * 8); +#endif + } + return (data); +} + +int BN_num_bits_word(BN_ULONG l) +{ + static const char bits[256] = + { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + }; + +#if defined(SIXTY_FOUR_BIT_LONG) + if(l & 0xffffffff00000000L) + { + if(l & 0xffff000000000000L) + { + if(l & 0xff00000000000000L) + { + return (bits[(int)(l >> 56)] + 56); + } + else { return (bits[(int)(l >> 48)] + 48); } + } + else + { + if(l & 0x0000ff0000000000L) + { + return (bits[(int)(l >> 40)] + 40); + } + else { return (bits[(int)(l >> 32)] + 32); } + } + } + else +#else +#ifdef SIXTY_FOUR_BIT + if(l & 0xffffffff00000000LL) + { + if(l & 0xffff000000000000LL) + { + if(l & 0xff00000000000000LL) + { + return (bits[(int)(l >> 56)] + 56); + } + else { return (bits[(int)(l >> 48)] + 48); } + } + else + { + if(l & 0x0000ff0000000000LL) + { + return (bits[(int)(l >> 40)] + 40); + } + else { return (bits[(int)(l >> 32)] + 32); } + } + } + else +#endif +#endif + { +#if defined(THIRTY_TWO_BIT) || defined(SIXTY_FOUR_BIT) || defined(SIXTY_FOUR_BIT_LONG) + if(l & 0xffff0000L) + { + if(l & 0xff000000L) + { return (bits[(int)(l >> 24L)] + 24); } + else { return (bits[(int)(l >> 16L)] + 16); } + } + else +#endif + { +#if defined(SIXTEEN_BIT) || defined(THIRTY_TWO_BIT) || defined(SIXTY_FOUR_BIT) || defined(SIXTY_FOUR_BIT_LONG) + if(l & 0xff00L) + { return (bits[(int)(l >> 8)] + 8); } + else +#endif + return (bits[(int)(l)]); + } + } +} + +int BN_num_bits(const BIGNUM *a) +{ + BN_ULONG l; + int i; + + bn_check_top(a); + + if(a->top == 0) { return (0); } + l = a->d[a->top - 1]; + assert(l != 0); + i = (a->top - 1) * BN_BITS2; + return (i + BN_num_bits_word(l)); +} + +void BN_clear_free(BIGNUM *a) +{ + int i; + + if(a == NULL) { return; } + if(a->d != NULL) + { + memset(a->d, 0, a->dmax * sizeof(a->d[0])); + if(!(BN_get_flags(a, BN_FLG_STATIC_DATA))) + { OPENSSL_free(a->d); } + } + i = BN_get_flags(a, BN_FLG_MALLOCED); + memset(a, 0, sizeof(BIGNUM)); + if(i) + { OPENSSL_free(a); } +} + +void BN_free(BIGNUM *a) +{ + if(a == NULL) { return; } + if((a->d != NULL) && !(BN_get_flags(a, BN_FLG_STATIC_DATA))) + { OPENSSL_free(a->d); } + a->flags |= BN_FLG_FREE; /* REMOVE? */ + if(a->flags & BN_FLG_MALLOCED) + { OPENSSL_free(a); } +} + +void BN_init(BIGNUM *a) +{ + memset(a, 0, sizeof(BIGNUM)); +} + +BIGNUM *BN_new(void) +{ + BIGNUM *ret; + + if((ret = (BIGNUM *)OPENSSL_malloc(sizeof(BIGNUM))) == NULL) + { + return (NULL); + } + ret->flags = BN_FLG_MALLOCED; + ret->top = 0; + ret->neg = 0; + ret->dmax = 0; + ret->d = NULL; + return (ret); +} + +/* This is an internal function that should not be used in applications. + * It ensures that 'b' has enough room for a 'words' word number number. + * It is mostly used by the various BIGNUM routines. If there is an error, + * NULL is returned. If not, 'b' is returned. */ + +BIGNUM *bn_expand2(BIGNUM *b, int words) +{ + BN_ULONG *A, *a; + const BN_ULONG *B; + int i; + + bn_check_top(b); + + if(words > b->dmax) + { + if(words > (INT_MAX / (4 * BN_BITS2))) + { + return NULL; + } + + bn_check_top(b); + if(BN_get_flags(b, BN_FLG_STATIC_DATA)) + { + return (NULL); + } + a = A = (BN_ULONG *)OPENSSL_malloc(sizeof(BN_ULONG) * (words + 1)); + if(A == NULL) + { + return (NULL); + } +#if 1 + B = b->d; + /* Check if the previous number needs to be copied */ + if(B != NULL) + { +#if 0 + /* This lot is an unrolled loop to copy b->top + * BN_ULONGs from B to A + */ + /* + * I have nothing against unrolling but it's usually done for + * several reasons, namely: + * - minimize percentage of decision making code, i.e. branches; + * - avoid cache trashing; + * - make it possible to schedule loads earlier; + * Now let's examine the code below. The cornerstone of C is + * "programmer is always right" and that's what we love it for:-) + * For this very reason C compilers have to be paranoid when it + * comes to data aliasing and assume the worst. Yeah, but what + * does it mean in real life? This means that loop body below will + * be compiled to sequence of loads immediately followed by stores + * as compiler assumes the worst, something in A==B+1 style. As a + * result CPU pipeline is going to starve for incoming data. Secondly + * if A and B happen to share same cache line such code is going to + * cause severe cache trashing. Both factors have severe impact on + * performance of modern CPUs and this is the reason why this + * particular piece of code is #ifdefed away and replaced by more + * "friendly" version found in #else section below. This comment + * also applies to BN_copy function. + * + * + */ + for(i = b->top & (~7); i > 0; i -= 8) + { + A[0] = B[0]; + A[1] = B[1]; + A[2] = B[2]; + A[3] = B[3]; + A[4] = B[4]; + A[5] = B[5]; + A[6] = B[6]; + A[7] = B[7]; + A += 8; + B += 8; + } + switch(b->top & 7) + { + case 7: + A[6] = B[6]; + case 6: + A[5] = B[5]; + case 5: + A[4] = B[4]; + case 4: + A[3] = B[3]; + case 3: + A[2] = B[2]; + case 2: + A[1] = B[1]; + case 1: + A[0] = B[0]; + case 0: + /* I need the 'case 0' entry for utrix cc. + * If the optimizer is turned on, it does the + * switch table by doing + * a=top&7 + * a--; + * goto jump_table[a]; + * If top is 0, this makes us jump to 0xffffffc + * which is rather bad :-(. + * eric 23-Apr-1998 + */ + ; + } +#else + for(i = b->top >> 2; i > 0; i--, A += 4, B += 4) + { + /* + * The fact that the loop is unrolled + * 4-wise is a tribute to Intel. It's + * the one that doesn't have enough + * registers to accomodate more data. + * I'd unroll it 8-wise otherwise:-) + * + * + */ + BN_ULONG a0, a1, a2, a3; + a0 = B[0]; + a1 = B[1]; + a2 = B[2]; + a3 = B[3]; + A[0] = a0; + A[1] = a1; + A[2] = a2; + A[3] = a3; + } + switch(b->top & 3) + { + case 3: + A[2] = B[2]; /* fallthrough */ + case 2: + A[1] = B[1]; /* fallthrough */ + case 1: + A[0] = B[0]; /* fallthrough */ + case 0: ; /* ultrix cc workaround, see above */ + } +#endif + OPENSSL_free(b->d); + } + + b->d = a; + b->dmax = words; + + /* Now need to zero any data between b->top and b->max */ + + A = &(b->d[b->top]); + for(i = (b->dmax - b->top) >> 3; i > 0; i--, A += 8) + { + A[0] = 0; + A[1] = 0; + A[2] = 0; + A[3] = 0; + A[4] = 0; + A[5] = 0; + A[6] = 0; + A[7] = 0; + } + for(i = (b->dmax - b->top) & 7; i > 0; i--, A++) + { A[0] = 0; } +#else + memset(A, 0, sizeof(BN_ULONG) * (words + 1)); + memcpy(A, b->d, sizeof(b->d[0])*b->top); + b->d = a; + b->max = words; +#endif + + /* memset(&(p[b->max]),0,((words+1)-b->max)*sizeof(BN_ULONG)); */ + /* { int i; for (i=b->max; itop) == NULL) { return (NULL); } + +#if 1 + A = a->d; + B = b->d; + for(i = b->top >> 2; i > 0; i--, A += 4, B += 4) + { + BN_ULONG a0, a1, a2, a3; + a0 = B[0]; + a1 = B[1]; + a2 = B[2]; + a3 = B[3]; + A[0] = a0; + A[1] = a1; + A[2] = a2; + A[3] = a3; + } + switch(b->top & 3) + { + case 3: + A[2] = B[2]; /* fallthrough */ + case 2: + A[1] = B[1]; /* fallthrough */ + case 1: + A[0] = B[0]; /* fallthrough */ + case 0: ; /* ultrix cc workaround, see comments in bn_expand2 */ + } +#else + memcpy(a->d, b->d, sizeof(b->d[0])*b->top); +#endif + + /* memset(&(a->d[b->top]),0,sizeof(a->d[0])*(a->max-b->top));*/ + a->top = b->top; + if((a->top == 0) && (a->d != NULL)) + { a->d[0] = 0; } + a->neg = b->neg; + return (a); +} + +void BN_clear(BIGNUM *a) +{ + if(a->d != NULL) + { memset(a->d, 0, a->dmax * sizeof(a->d[0])); } + a->top = 0; + a->neg = 0; +} + +BN_ULONG BN_get_word(const BIGNUM *a) +{ + if(a->top > 1) + { return BN_MASK2; } + else if(a->top == 1) + { return a->d[0]; } + /* a->top == 0 */ + return 0; +} + +int BN_set_word(BIGNUM *a, BN_ULONG w) +{ + bn_check_top(a); + if(bn_expand(a, (int)sizeof(BN_ULONG) * 8) == NULL) { return (0); } + a->neg = 0; + a->d[0] = w; + a->top = (w ? 1 : 0); + bn_check_top(a); + return (1); +} + +/* ignore negative */ +BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret) +{ + unsigned int i, m; + unsigned int n; + BN_ULONG l; + + if(ret == NULL) { ret = BN_new(); } + if(ret == NULL) { return (NULL); } + l = 0; + n = len; + if(n == 0) + { + ret->top = 0; + return (ret); + } + if(bn_expand(ret, (int)(n + 2) * 8) == NULL) + { return (NULL); } + i = ((n - 1) / BN_BYTES) + 1; + m = ((n - 1) % (BN_BYTES)); + ret->top = i; + while(n-- > 0) + { + l = (l << 8L) | *(s++); + if(m-- == 0) + { + ret->d[--i] = l; + l = 0; + m = BN_BYTES - 1; + } + } + /* need to call this due to clear byte at top if avoiding + * having the top bit set (-ve number) */ + bn_fix_top(ret); + return (ret); +} + +/* ignore negative */ +int BN_bn2bin(const BIGNUM *a, unsigned char *to) +{ + int n, i; + BN_ULONG l; + + n = i = BN_num_bytes(a); + while(i-- > 0) + { + l = a->d[i / BN_BYTES]; + *(to++) = (unsigned char)(l >> (8 * (i % BN_BYTES))) & 0xff; + } + return (n); +} + +int BN_ucmp(const BIGNUM *a, const BIGNUM *b) +{ + int i; + BN_ULONG t1, t2, *ap, *bp; + + bn_check_top(a); + bn_check_top(b); + + i = a->top - b->top; + if(i != 0) { return (i); } + ap = a->d; + bp = b->d; + for(i = a->top - 1; i >= 0; i--) + { + t1 = ap[i]; + t2 = bp[i]; + if(t1 != t2) + { return (t1 > t2 ? 1 : -1); } + } + return (0); +} + +int BN_cmp(const BIGNUM *a, const BIGNUM *b) +{ + int i; + int gt, lt; + BN_ULONG t1, t2; + + if((a == NULL) || (b == NULL)) + { + if(a != NULL) + { return (-1); } + else if(b != NULL) + { return (1); } + else + { return (0); } + } + + bn_check_top(a); + bn_check_top(b); + + if(a->neg != b->neg) + { + if(a->neg) + { return (-1); } + else { return (1); } + } + if(a->neg == 0) + { + gt = 1; + lt = -1; + } + else + { + gt = -1; + lt = 1; + } + + if(a->top > b->top) { return (gt); } + if(a->top < b->top) { return (lt); } + for(i = a->top - 1; i >= 0; i--) + { + t1 = a->d[i]; + t2 = b->d[i]; + if(t1 > t2) { return (gt); } + if(t1 < t2) { return (lt); } + } + return (0); +} + +int BN_set_bit(BIGNUM *a, int n) +{ + int i, j, k; + + i = n / BN_BITS2; + j = n % BN_BITS2; + if(a->top <= i) + { + if(bn_wexpand(a, i + 1) == NULL) { return (0); } + for(k = a->top; k < i + 1; k++) + { a->d[k] = 0; } + a->top = i + 1; + } + + a->d[i] |= (((BN_ULONG)1) << j); + return (1); +} + +int BN_clear_bit(BIGNUM *a, int n) +{ + int i, j; + + i = n / BN_BITS2; + j = n % BN_BITS2; + if(a->top <= i) { return (0); } + + a->d[i] &= (~(((BN_ULONG)1) << j)); + bn_fix_top(a); + return (1); +} + +int BN_is_bit_set(const BIGNUM *a, int n) +{ + int i, j; + + if(n < 0) { return (0); } + i = n / BN_BITS2; + j = n % BN_BITS2; + if(a->top <= i) { return (0); } + return ((a->d[i] & (((BN_ULONG)1) << j)) ? 1 : 0); +} + +int BN_mask_bits(BIGNUM *a, int n) +{ + int b, w; + + w = n / BN_BITS2; + b = n % BN_BITS2; + if(w >= a->top) { return (0); } + if(b == 0) + { a->top = w; } + else + { + a->top = w + 1; + a->d[w] &= ~(BN_MASK2 << b); + } + bn_fix_top(a); + return (1); +} + +int bn_cmp_words(BN_ULONG *a, BN_ULONG *b, int n) +{ + int i; + BN_ULONG aa, bb; + + aa = a[n - 1]; + bb = b[n - 1]; + if(aa != bb) { return ((aa > bb) ? 1 : -1); } + for(i = n - 2; i >= 0; i--) + { + aa = a[i]; + bb = b[i]; + if(aa != bb) { return ((aa > bb) ? 1 : -1); } + } + return (0); +} + +#endif diff --git a/cscrypt/bn_mul.c b/cscrypt/bn_mul.c new file mode 100644 index 0000000..beb5917 --- /dev/null +++ b/cscrypt/bn_mul.c @@ -0,0 +1,804 @@ +#include "bn.h" + +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/bn/bn_mul.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#include +#include +#include "bn_lcl.h" +#include "openssl_mods.h" + +#ifdef BN_RECURSION +/* Karatsuba recursive multiplication algorithm + * (cf. Knuth, The Art of Computer Programming, Vol. 2) */ + +/* r is 2*n2 words in size, + * a and b are both n2 words in size. + * n2 must be a power of 2. + * We multiply and return the result. + * t must be 2*n2 words in size + * We calculate + * a[0]*b[0] + * a[0]*b[0]+a[1]*b[1]+(a[0]-a[1])*(b[1]-b[0]) + * a[1]*b[1] + */ +void bn_mul_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n2, + BN_ULONG *t) +{ + int n = n2 / 2, c1, c2; + unsigned int neg, zero; + BN_ULONG ln, lo, *p; + +# ifdef BN_COUNT + printf(" bn_mul_recursive %d * %d\n", n2, n2); +# endif +# ifdef BN_MUL_COMBA +# if 0 + if(n2 == 4) + { + bn_mul_comba4(r, a, b); + return; + } +# endif + if(n2 == 8) + { + bn_mul_comba8(r, a, b); + return; + } +# endif /* BN_MUL_COMBA */ + if(n2 < BN_MUL_RECURSIVE_SIZE_NORMAL) + { + /* This should not happen */ + bn_mul_normal(r, a, n2, b, n2); + return; + } + /* r=(a[0]-a[1])*(b[1]-b[0]) */ + c1 = bn_cmp_words(a, &(a[n]), n); + c2 = bn_cmp_words(&(b[n]), b, n); + zero = neg = 0; + switch(c1 * 3 + c2) + { + case -4: + bn_sub_words(t, &(a[n]), a, n); /* - */ + bn_sub_words(&(t[n]), b, &(b[n]), n); /* - */ + break; + case -3: + zero = 1; + break; + case -2: + bn_sub_words(t, &(a[n]), a, n); /* - */ + bn_sub_words(&(t[n]), &(b[n]), b, n); /* + */ + neg = 1; + break; + case -1: + case 0: + case 1: + zero = 1; + break; + case 2: + bn_sub_words(t, a, &(a[n]), n); /* + */ + bn_sub_words(&(t[n]), b, &(b[n]), n); /* - */ + neg = 1; + break; + case 3: + zero = 1; + break; + case 4: + bn_sub_words(t, a, &(a[n]), n); + bn_sub_words(&(t[n]), &(b[n]), b, n); + break; + } + +# ifdef BN_MUL_COMBA + if(n == 4) + { + if(!zero) + { bn_mul_comba4(&(t[n2]), t, &(t[n])); } + else + { memset(&(t[n2]), 0, 8 * sizeof(BN_ULONG)); } + + bn_mul_comba4(r, a, b); + bn_mul_comba4(&(r[n2]), &(a[n]), &(b[n])); + } + else if(n == 8) + { + if(!zero) + { bn_mul_comba8(&(t[n2]), t, &(t[n])); } + else + { memset(&(t[n2]), 0, 16 * sizeof(BN_ULONG)); } + + bn_mul_comba8(r, a, b); + bn_mul_comba8(&(r[n2]), &(a[n]), &(b[n])); + } + else +# endif /* BN_MUL_COMBA */ + { + p = &(t[n2 * 2]); + if(!zero) + { bn_mul_recursive(&(t[n2]), t, &(t[n]), n, p); } + else + { memset(&(t[n2]), 0, n2 * sizeof(BN_ULONG)); } + bn_mul_recursive(r, a, b, n, p); + bn_mul_recursive(&(r[n2]), &(a[n]), &(b[n]), n, p); + } + + /* t[32] holds (a[0]-a[1])*(b[1]-b[0]), c1 is the sign + * r[10] holds (a[0]*b[0]) + * r[32] holds (b[1]*b[1]) + */ + + c1 = (int)(bn_add_words(t, r, &(r[n2]), n2)); + + if(neg) /* if t[32] is negative */ + { + c1 -= (int)(bn_sub_words(&(t[n2]), t, &(t[n2]), n2)); + } + else + { + /* Might have a carry */ + c1 += (int)(bn_add_words(&(t[n2]), &(t[n2]), t, n2)); + } + + /* t[32] holds (a[0]-a[1])*(b[1]-b[0])+(a[0]*b[0])+(a[1]*b[1]) + * r[10] holds (a[0]*b[0]) + * r[32] holds (b[1]*b[1]) + * c1 holds the carry bits + */ + c1 += (int)(bn_add_words(&(r[n]), &(r[n]), &(t[n2]), n2)); + if(c1) + { + p = &(r[n + n2]); + lo = *p; + ln = (lo + c1)&BN_MASK2; + *p = ln; + + /* The overflow will stop before we over write + * words we should not overwrite */ + if(ln < (BN_ULONG)c1) + { + do + { + p++; + lo = *p; + ln = (lo + 1)&BN_MASK2; + *p = ln; + } + while(ln == 0); + } + } +} + +/* n+tn is the word length + * t needs to be n*4 is size, as does r */ +void bn_mul_part_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int tn, + int n, BN_ULONG *t) +{ + int c1, c2, i, j, n2 = n * 2; + unsigned int neg; + BN_ULONG ln, lo, *p; + +# ifdef BN_COUNT + printf(" bn_mul_part_recursive %d * %d\n", tn + n, tn + n); +# endif + if(n < 8) + { + i = tn + n; + bn_mul_normal(r, a, i, b, i); + return; + } + + /* r=(a[0]-a[1])*(b[1]-b[0]) */ + c1 = bn_cmp_words(a, &(a[n]), n); + c2 = bn_cmp_words(&(b[n]), b, n); + neg = 0; + switch(c1 * 3 + c2) + { + case -4: + bn_sub_words(t, &(a[n]), a, n); /* - */ + bn_sub_words(&(t[n]), b, &(b[n]), n); /* - */ + break; + case -3: + case -2: + bn_sub_words(t, &(a[n]), a, n); /* - */ + bn_sub_words(&(t[n]), &(b[n]), b, n); /* + */ + neg = 1; + break; + case -1: + case 0: + case 1: + case 2: + bn_sub_words(t, a, &(a[n]), n); /* + */ + bn_sub_words(&(t[n]), b, &(b[n]), n); /* - */ + neg = 1; + break; + case 3: + case 4: + bn_sub_words(t, a, &(a[n]), n); + bn_sub_words(&(t[n]), &(b[n]), b, n); + break; + } + /* The zero case isn't yet implemented here. The speedup + would probably be negligible. */ +# if 0 + if(n == 4) + { + bn_mul_comba4(&(t[n2]), t, &(t[n])); + bn_mul_comba4(r, a, b); + bn_mul_normal(&(r[n2]), &(a[n]), tn, &(b[n]), tn); + memset(&(r[n2 + tn * 2]), 0, sizeof(BN_ULONG) * (n2 - tn * 2)); + } + else +# endif + if(n == 8) + { + bn_mul_comba8(&(t[n2]), t, &(t[n])); + bn_mul_comba8(r, a, b); + bn_mul_normal(&(r[n2]), &(a[n]), tn, &(b[n]), tn); + memset(&(r[n2 + tn * 2]), 0, sizeof(BN_ULONG) * (n2 - tn * 2)); + } + else + { + p = &(t[n2 * 2]); + bn_mul_recursive(&(t[n2]), t, &(t[n]), n, p); + bn_mul_recursive(r, a, b, n, p); + i = n / 2; + /* If there is only a bottom half to the number, + * just do it */ + j = tn - i; + if(j == 0) + { + bn_mul_recursive(&(r[n2]), &(a[n]), &(b[n]), i, p); + memset(&(r[n2 + i * 2]), 0, sizeof(BN_ULONG) * (n2 - i * 2)); + } + else if(j > 0) /* eg, n == 16, i == 8 and tn == 11 */ + { + bn_mul_part_recursive(&(r[n2]), &(a[n]), &(b[n]), + j, i, p); + memset(&(r[n2 + tn * 2]), 0, + sizeof(BN_ULONG) * (n2 - tn * 2)); + } + else /* (j < 0) eg, n == 16, i == 8 and tn == 5 */ + { + memset(&(r[n2]), 0, sizeof(BN_ULONG)*n2); + if(tn < BN_MUL_RECURSIVE_SIZE_NORMAL) + { + bn_mul_normal(&(r[n2]), &(a[n]), tn, &(b[n]), tn); + } + else + { + for(;;) + { + i /= 2; + if(i < tn) + { + bn_mul_part_recursive(&(r[n2]), + &(a[n]), &(b[n]), + tn - i, i, p); + break; + } + else if(i == tn) + { + bn_mul_recursive(&(r[n2]), + &(a[n]), &(b[n]), + i, p); + break; + } + } + } + } + } + + /* t[32] holds (a[0]-a[1])*(b[1]-b[0]), c1 is the sign + * r[10] holds (a[0]*b[0]) + * r[32] holds (b[1]*b[1]) + */ + + c1 = (int)(bn_add_words(t, r, &(r[n2]), n2)); + + if(neg) /* if t[32] is negative */ + { + c1 -= (int)(bn_sub_words(&(t[n2]), t, &(t[n2]), n2)); + } + else + { + /* Might have a carry */ + c1 += (int)(bn_add_words(&(t[n2]), &(t[n2]), t, n2)); + } + + /* t[32] holds (a[0]-a[1])*(b[1]-b[0])+(a[0]*b[0])+(a[1]*b[1]) + * r[10] holds (a[0]*b[0]) + * r[32] holds (b[1]*b[1]) + * c1 holds the carry bits + */ + c1 += (int)(bn_add_words(&(r[n]), &(r[n]), &(t[n2]), n2)); + if(c1) + { + p = &(r[n + n2]); + lo = *p; + ln = (lo + c1)&BN_MASK2; + *p = ln; + + /* The overflow will stop before we over write + * words we should not overwrite */ + if(ln < (BN_ULONG)c1) + { + do + { + p++; + lo = *p; + ln = (lo + 1)&BN_MASK2; + *p = ln; + } + while(ln == 0); + } + } +} + +/* a and b must be the same size, which is n2. + * r needs to be n2 words and t needs to be n2*2 + */ +void bn_mul_low_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n2, + BN_ULONG *t) +{ + int n = n2 / 2; + +# ifdef BN_COUNT + printf(" bn_mul_low_recursive %d * %d\n", n2, n2); +# endif + + bn_mul_recursive(r, a, b, n, &(t[0])); + if(n >= BN_MUL_LOW_RECURSIVE_SIZE_NORMAL) + { + bn_mul_low_recursive(&(t[0]), &(a[0]), &(b[n]), n, &(t[n2])); + bn_add_words(&(r[n]), &(r[n]), &(t[0]), n); + bn_mul_low_recursive(&(t[0]), &(a[n]), &(b[0]), n, &(t[n2])); + bn_add_words(&(r[n]), &(r[n]), &(t[0]), n); + } + else + { + bn_mul_low_normal(&(t[0]), &(a[0]), &(b[n]), n); + bn_mul_low_normal(&(t[n]), &(a[n]), &(b[0]), n); + bn_add_words(&(r[n]), &(r[n]), &(t[0]), n); + bn_add_words(&(r[n]), &(r[n]), &(t[n]), n); + } +} + +/* a and b must be the same size, which is n2. + * r needs to be n2 words and t needs to be n2*2 + * l is the low words of the output. + * t needs to be n2*3 + */ +void bn_mul_high(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, BN_ULONG *l, int n2, BN_ULONG *t) +{ + int i, n; + int c1, c2; + int neg = 0, oneg; + BN_ULONG ll, lc, *lp, *mp; + +# ifdef BN_COUNT + printf(" bn_mul_high %d * %d\n", n2, n2); +# endif + n = n2 / 2; + + /* Calculate (al-ah)*(bh-bl) */ + c1 = bn_cmp_words(&(a[0]), &(a[n]), n); + c2 = bn_cmp_words(&(b[n]), &(b[0]), n); + switch(c1 * 3 + c2) + { + case -4: + bn_sub_words(&(r[0]), &(a[n]), &(a[0]), n); + bn_sub_words(&(r[n]), &(b[0]), &(b[n]), n); + break; + case -3: + break; + case -2: + bn_sub_words(&(r[0]), &(a[n]), &(a[0]), n); + bn_sub_words(&(r[n]), &(b[n]), &(b[0]), n); + neg = 1; + break; + case -1: + case 0: + case 1: + break; + case 2: + bn_sub_words(&(r[0]), &(a[0]), &(a[n]), n); + bn_sub_words(&(r[n]), &(b[0]), &(b[n]), n); + neg = 1; + break; + case 3: + break; + case 4: + bn_sub_words(&(r[0]), &(a[0]), &(a[n]), n); + bn_sub_words(&(r[n]), &(b[n]), &(b[0]), n); + break; + } + + oneg = neg; + /* t[10] = (a[0]-a[1])*(b[1]-b[0]) */ + /* r[10] = (a[1]*b[1]) */ +# ifdef BN_MUL_COMBA + if(n == 8) + { + bn_mul_comba8(&(t[0]), &(r[0]), &(r[n])); + bn_mul_comba8(r, &(a[n]), &(b[n])); + } + else +# endif + { + bn_mul_recursive(&(t[0]), &(r[0]), &(r[n]), n, &(t[n2])); + bn_mul_recursive(r, &(a[n]), &(b[n]), n, &(t[n2])); + } + + /* s0 == low(al*bl) + * s1 == low(ah*bh)+low((al-ah)*(bh-bl))+low(al*bl)+high(al*bl) + * We know s0 and s1 so the only unknown is high(al*bl) + * high(al*bl) == s1 - low(ah*bh+s0+(al-ah)*(bh-bl)) + * high(al*bl) == s1 - (r[0]+l[0]+t[0]) + */ + if(l != NULL) + { + lp = &(t[n2 + n]); + c1 = (int)(bn_add_words(lp, &(r[0]), &(l[0]), n)); + } + else + { + c1 = 0; + lp = &(r[0]); + } + + if(neg) + { neg = (int)(bn_sub_words(&(t[n2]), lp, &(t[0]), n)); } + else + { + bn_add_words(&(t[n2]), lp, &(t[0]), n); + } + + if(l != NULL) + { + bn_sub_words(&(t[n2 + n]), &(l[n]), &(t[n2]), n); + } + else + { + lp = &(t[n2 + n]); + mp = &(t[n2]); + for(i = 0; i < n; i++) + { lp[i] = ((~mp[i]) + 1)&BN_MASK2; } + } + + /* s[0] = low(al*bl) + * t[3] = high(al*bl) + * t[10] = (a[0]-a[1])*(b[1]-b[0]) neg is the sign + * r[10] = (a[1]*b[1]) + */ + /* R[10] = al*bl + * R[21] = al*bl + ah*bh + (a[0]-a[1])*(b[1]-b[0]) + * R[32] = ah*bh + */ + /* R[1]=t[3]+l[0]+r[0](+-)t[0] (have carry/borrow) + * R[2]=r[0]+t[3]+r[1](+-)t[1] (have carry/borrow) + * R[3]=r[1]+(carry/borrow) + */ + if(l != NULL) + { + lp = &(t[n2]); + c1 = (int)(bn_add_words(lp, &(t[n2 + n]), &(l[0]), n)); + } + else + { + lp = &(t[n2 + n]); + c1 = 0; + } + c1 += (int)(bn_add_words(&(t[n2]), lp, &(r[0]), n)); + if(oneg) + { c1 -= (int)(bn_sub_words(&(t[n2]), &(t[n2]), &(t[0]), n)); } + else + { c1 += (int)(bn_add_words(&(t[n2]), &(t[n2]), &(t[0]), n)); } + + c2 = (int)(bn_add_words(&(r[0]), &(r[0]), &(t[n2 + n]), n)); + c2 += (int)(bn_add_words(&(r[0]), &(r[0]), &(r[n]), n)); + if(oneg) + { c2 -= (int)(bn_sub_words(&(r[0]), &(r[0]), &(t[n]), n)); } + else + { c2 += (int)(bn_add_words(&(r[0]), &(r[0]), &(t[n]), n)); } + + if(c1 != 0) /* Add starting at r[0], could be +ve or -ve */ + { + i = 0; + if(c1 > 0) + { + lc = c1; + do + { + ll = (r[i] + lc)&BN_MASK2; + r[i++] = ll; + lc = (lc > ll); + } + while(lc); + } + else + { + lc = -c1; + do + { + ll = r[i]; + r[i++] = (ll - lc)&BN_MASK2; + lc = (lc > ll); + } + while(lc); + } + } + if(c2 != 0) /* Add starting at r[1] */ + { + i = n; + if(c2 > 0) + { + lc = c2; + do + { + ll = (r[i] + lc)&BN_MASK2; + r[i++] = ll; + lc = (lc > ll); + } + while(lc); + } + else + { + lc = -c2; + do + { + ll = r[i]; + r[i++] = (ll - lc)&BN_MASK2; + lc = (lc > ll); + } + while(lc); + } + } +} +#endif /* BN_RECURSION */ + +int BN_mul(BIGNUM *r, BIGNUM *a, BIGNUM *b, BN_CTX *ctx) +{ + int top, al, bl; + BIGNUM *rr; + int ret = 0; +#if defined(BN_MUL_COMBA) || defined(BN_RECURSION) + int i; +#endif +#ifdef BN_RECURSION + BIGNUM *t; + int j, k; +#endif + +#ifdef BN_COUNT + printf("BN_mul %d * %d\n", a->top, b->top); +#endif + + bn_check_top(a); + bn_check_top(b); + bn_check_top(r); + + al = a->top; + bl = b->top; + + if((al == 0) || (bl == 0)) + { + BN_zero(r); + return (1); + } + top = al + bl; + + BN_CTX_start(ctx); + if((r == a) || (r == b)) + { + if((rr = BN_CTX_get(ctx)) == NULL) { goto err; } + } + else + { rr = r; } + rr->neg = a->neg ^ b->neg; + +#if defined(BN_MUL_COMBA) || defined(BN_RECURSION) + i = al - bl; +#endif +#ifdef BN_MUL_COMBA + if(i == 0) + { +# if 0 + if(al == 4) + { + if(bn_wexpand(rr, 8) == NULL) { goto err; } + rr->top = 8; + bn_mul_comba4(rr->d, a->d, b->d); + goto end; + } +# endif + if(al == 8) + { + if(bn_wexpand(rr, 16) == NULL) { goto err; } + rr->top = 16; + bn_mul_comba8(rr->d, a->d, b->d); + goto end; + } + } +#endif /* BN_MUL_COMBA */ +#ifdef BN_RECURSION + if((al >= BN_MULL_SIZE_NORMAL) && (bl >= BN_MULL_SIZE_NORMAL)) + { + if(i == 1 && !BN_get_flags(b, BN_FLG_STATIC_DATA)) + { + if(bn_wexpand(b, al) == NULL) { goto err; } + b->d[bl] = 0; + bl++; + i--; + } + else if(i == -1 && !BN_get_flags(a, BN_FLG_STATIC_DATA)) + { + if(bn_wexpand(a, bl) == NULL) { goto err; } + a->d[al] = 0; + al++; + i++; + } + if(i == 0) + { + /* symmetric and > 4 */ + /* 16 or larger */ + j = BN_num_bits_word((BN_ULONG)al); + j = 1 << (j - 1); + k = j + j; + t = BN_CTX_get(ctx); + if(al == j) /* exact multiple */ + { + if(bn_wexpand(t, k * 2) == NULL) { goto err; } + if(bn_wexpand(rr, k * 2) == NULL) { goto err; } + bn_mul_recursive(rr->d, a->d, b->d, al, t->d); + } + else + { + if(bn_wexpand(a, k) == NULL) { goto err; } + if(bn_wexpand(b, k) == NULL) { goto err; } + if(bn_wexpand(t, k * 4) == NULL) { goto err; } + if(bn_wexpand(rr, k * 4) == NULL) { goto err; } + for(i = a->top; i < k; i++) + { a->d[i] = 0; } + for(i = b->top; i < k; i++) + { b->d[i] = 0; } + bn_mul_part_recursive(rr->d, a->d, b->d, al - j, j, t->d); + } + rr->top = top; + goto end; + } + } +#endif /* BN_RECURSION */ + if(bn_wexpand(rr, top) == NULL) { goto err; } + rr->top = top; + bn_mul_normal(rr->d, a->d, al, b->d, bl); + +#if defined(BN_MUL_COMBA) || defined(BN_RECURSION) +end: +#endif + bn_fix_top(rr); + if(r != rr) { BN_copy(r, rr); } + ret = 1; +err: + BN_CTX_end(ctx); + return (ret); +} + +void bn_mul_normal(BN_ULONG *r, BN_ULONG *a, int na, BN_ULONG *b, int nb) +{ + BN_ULONG *rr; + +#ifdef BN_COUNT + printf(" bn_mul_normal %d * %d\n", na, nb); +#endif + + if(na < nb) + { + int itmp; + BN_ULONG *ltmp; + + itmp = na; + na = nb; + nb = itmp; + ltmp = a; + a = b; + b = ltmp; + + } + rr = &(r[na]); + rr[0] = bn_mul_words(r, a, na, b[0]); + + for(;;) + { + if(--nb <= 0) { return; } + rr[1] = bn_mul_add_words(&(r[1]), a, na, b[1]); + if(--nb <= 0) { return; } + rr[2] = bn_mul_add_words(&(r[2]), a, na, b[2]); + if(--nb <= 0) { return; } + rr[3] = bn_mul_add_words(&(r[3]), a, na, b[3]); + if(--nb <= 0) { return; } + rr[4] = bn_mul_add_words(&(r[4]), a, na, b[4]); + rr += 4; + r += 4; + b += 4; + } +} + +void bn_mul_low_normal(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n) +{ +#ifdef BN_COUNT + printf(" bn_mul_low_normal %d * %d\n", n, n); +#endif + bn_mul_words(r, a, n, b[0]); + + for(;;) + { + if(--n <= 0) { return; } + bn_mul_add_words(&(r[1]), a, n, b[1]); + if(--n <= 0) { return; } + bn_mul_add_words(&(r[2]), a, n, b[2]); + if(--n <= 0) { return; } + bn_mul_add_words(&(r[3]), a, n, b[3]); + if(--n <= 0) { return; } + bn_mul_add_words(&(r[4]), a, n, b[4]); + r += 4; + b += 4; + } +} +#endif diff --git a/cscrypt/bn_print.c b/cscrypt/bn_print.c new file mode 100644 index 0000000..e01463c --- /dev/null +++ b/cscrypt/bn_print.c @@ -0,0 +1,299 @@ +#include "bn.h" + +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/bn/bn_print.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#include +#include +#include "bn_lcl.h" +#include "buffer.h" +#include "openssl_mods.h" + +static const char *Hex = "0123456789ABCDEF"; + +/* Must 'OPENSSL_free' the returned data */ +char *BN_bn2hex(const BIGNUM *a) +{ + int i, j, v, z = 0; + char *buf; + char *p; + + buf = (char *)OPENSSL_malloc(a->top * BN_BYTES * 2 + 2); + if(buf == NULL) + { + goto err; + } + p = buf; + if(a->neg) { *(p++) = '-'; } + if(a->top == 0) { *(p++) = '0'; } + for(i = a->top - 1; i >= 0; i--) + { + for(j = BN_BITS2 - 8; j >= 0; j -= 8) + { + /* strip leading zeros */ + v = ((int)(a->d[i] >> (long)j)) & 0xff; + if(z || (v != 0)) + { + *(p++) = Hex[v >> 4]; + *(p++) = Hex[v & 0x0f]; + z = 1; + } + } + } + *p = '\0'; +err: + return (buf); +} + +/* Must 'OPENSSL_free' the returned data */ +char *BN_bn2dec(const BIGNUM *a) +{ + int i = 0, num; + char *buf = NULL; + char *p; + BIGNUM *t = NULL; + BN_ULONG *bn_data = NULL, *lp; + + i = BN_num_bits(a) * 3; + num = (i / 10 + i / 1000 + 3) + 1; + bn_data = (BN_ULONG *)OPENSSL_malloc((num / BN_DEC_NUM + 1) * sizeof(BN_ULONG)); + buf = (char *)OPENSSL_malloc(num + 3); + if((buf == NULL) || (bn_data == NULL)) + { + goto err; + } + if((t = BN_dup(a)) == NULL) { goto err; } + + p = buf; + lp = bn_data; + if(t->neg) { *(p++) = '-'; } + if(t->top == 0) + { + *(p++) = '0'; + *(p++) = '\0'; + } + else + { + while(!BN_is_zero(t)) + { + *lp = BN_div_word(t, BN_DEC_CONV); + lp++; + } + lp--; + /* We now have a series of blocks, BN_DEC_NUM chars + * in length, where the last one needs truncation. + * The blocks need to be reversed in order. */ + snprintf(p, num + 3 - (p - buf), BN_DEC_FMT1, *lp); + while(*p) { p++; } + while(lp != bn_data) + { + lp--; + snprintf(p, num + 3 - (p - buf), BN_DEC_FMT2, *lp); + while(*p) { p++; } + } + } +err: + if(bn_data != NULL) { OPENSSL_free(bn_data); } + if(t != NULL) { BN_free(t); } + return (buf); +} + +int BN_hex2bn(BIGNUM **bn, const char *a) +{ + BIGNUM *ret = NULL; + BN_ULONG l = 0; + int neg = 0, h, i, j, k, c; + int m = 0; + int num; + + if((a == NULL) || (*a == '\0')) { return (0); } + + if(*a == '-') + { + neg = 1; + a++; + } + + for(i = 0; isxdigit((unsigned char) a[i]); i++) + { ; } + + num = i + neg; + if(bn == NULL) { return (num); } + + /* a is the start of the hex digits, and it is 'i' long */ + if(*bn == NULL) + { + if((ret = BN_new()) == NULL) { return (0); } + } + else + { + ret = *bn; + BN_zero(ret); + } + + /* i is the number of hex digests; */ + if(bn_expand(ret, i * 4) == NULL) { goto err; } + + j = i; /* least significant 'hex' */ + h = 0; + while(j > 0) + { + m = ((BN_BYTES * 2) <= j) ? (BN_BYTES * 2) : j; + l = 0; + for(;;) + { + c = a[j - m]; + if((c >= '0') && (c <= '9')) { k = c - '0'; } + else if((c >= 'a') && (c <= 'f')) { k = c - 'a' + 10; } + else if((c >= 'A') && (c <= 'F')) { k = c - 'A' + 10; } + else { k = 0; } /* paranoia */ + l = (l << 4) | k; + + if(--m <= 0) + { + ret->d[h++] = l; + break; + } + } + j -= (BN_BYTES * 2); + } + ret->top = h; + bn_fix_top(ret); + ret->neg = neg; + + *bn = ret; + return (num); +err: + if(*bn == NULL) { BN_free(ret); } + return (0); +} + +int BN_dec2bn(BIGNUM **bn, const char *a) +{ + BIGNUM *ret = NULL; + BN_ULONG l = 0; + int neg = 0, i, j; + int num; + + if((a == NULL) || (*a == '\0')) { return (0); } + if(*a == '-') + { + neg = 1; + a++; + } + + for(i = 0; isdigit((unsigned char) a[i]); i++) + { ; } + + num = i + neg; + if(bn == NULL) { return (num); } + + /* a is the start of the digits, and it is 'i' long. + * We chop it into BN_DEC_NUM digits at a time */ + if(*bn == NULL) + { + if((ret = BN_new()) == NULL) { return (0); } + } + else + { + ret = *bn; + BN_zero(ret); + } + + /* i is the number of digests, a bit of an over expand; */ + if(bn_expand(ret, i * 4) == NULL) { goto err; } + + j = BN_DEC_NUM - (i % BN_DEC_NUM); + if(j == BN_DEC_NUM) { j = 0; } + l = 0; + while(*a) + { + l *= 10; + l += *a - '0'; + a++; + if(++j == BN_DEC_NUM) + { + BN_mul_word(ret, BN_DEC_CONV); + BN_add_word(ret, l); + l = 0; + j = 0; + } + } + ret->neg = neg; + + bn_fix_top(ret); + *bn = ret; + return (num); +err: + if(*bn == NULL) { BN_free(ret); } + return (0); +} + +#ifdef BN_DEBUG +void bn_dump1(FILE *o, const char *a, BN_ULONG *b, int n) +{ + int i; + fprintf(o, "%s=", a); + for(i = n - 1; i >= 0; i--) + { fprintf(o, "%08lX", b[i]); } /* assumes 32-bit BN_ULONG */ + fprintf(o, "\n"); +} +#endif +#endif diff --git a/cscrypt/bn_shift.c b/cscrypt/bn_shift.c new file mode 100644 index 0000000..d020da0 --- /dev/null +++ b/cscrypt/bn_shift.c @@ -0,0 +1,211 @@ +#include "bn.h" + +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/bn/bn_shift.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#include +#include +#include "bn_lcl.h" +#include "openssl_mods.h" + +int BN_lshift1(BIGNUM *r, BIGNUM *a) +{ + register BN_ULONG *ap, *rp, t, c; + int i; + + if(r != a) + { + r->neg = a->neg; + if(bn_wexpand(r, a->top + 1) == NULL) { return (0); } + r->top = a->top; + } + else + { + if(bn_wexpand(r, a->top + 1) == NULL) { return (0); } + } + ap = a->d; + rp = r->d; + c = 0; + for(i = 0; i < a->top; i++) + { + t = *(ap++); + *(rp++) = ((t << 1) | c)&BN_MASK2; + c = (t & BN_TBIT) ? 1 : 0; + } + if(c) + { + *rp = 1; + r->top++; + } + return (1); +} + +int BN_rshift1(BIGNUM *r, BIGNUM *a) +{ + BN_ULONG *ap, *rp, t, c; + int i; + + if(BN_is_zero(a)) + { + BN_zero(r); + return (1); + } + if(a != r) + { + if(bn_wexpand(r, a->top) == NULL) { return (0); } + r->top = a->top; + r->neg = a->neg; + } + ap = a->d; + rp = r->d; + c = 0; + for(i = a->top - 1; i >= 0; i--) + { + t = ap[i]; + rp[i] = ((t >> 1)&BN_MASK2) | c; + c = (t & 1) ? BN_TBIT : 0; + } + bn_fix_top(r); + return (1); +} + +int BN_lshift(BIGNUM *r, const BIGNUM *a, int n) +{ + int i, nw, lb, rb; + BN_ULONG *t, *f; + BN_ULONG l; + + r->neg = a->neg; + if(bn_wexpand(r, a->top + (n / BN_BITS2) + 1) == NULL) { return (0); } + nw = n / BN_BITS2; + lb = n % BN_BITS2; + rb = BN_BITS2 - lb; + f = a->d; + t = r->d; + t[a->top + nw] = 0; + if(lb == 0) + for(i = a->top - 1; i >= 0; i--) + { t[nw + i] = f[i]; } + else + for(i = a->top - 1; i >= 0; i--) + { + l = f[i]; + t[nw + i + 1] |= (l >> rb)&BN_MASK2; + t[nw + i] = (l << lb)&BN_MASK2; + } + memset(t, 0, nw * sizeof(t[0])); + /* for (i=0; itop = a->top + nw + 1; + bn_fix_top(r); + return (1); +} + +int BN_rshift(BIGNUM *r, BIGNUM *a, int n) +{ + int i, j, nw, lb, rb; + BN_ULONG *t, *f; + BN_ULONG l, tmp; + + nw = n / BN_BITS2; + rb = n % BN_BITS2; + lb = BN_BITS2 - rb; + if(nw > a->top || a->top == 0) + { + BN_zero(r); + return (1); + } + if(r != a) + { + r->neg = a->neg; + if(bn_wexpand(r, a->top - nw + 1) == NULL) { return (0); } + } + else + { + if(n == 0) + { return 1; } /* or the copying loop will go berserk */ + } + + f = &(a->d[nw]); + t = r->d; + j = a->top - nw; + r->top = j; + + if(rb == 0) + { + for(i = j + 1; i > 0; i--) + { *(t++) = *(f++); } + } + else + { + l = *(f++); + for(i = 1; i < j; i++) + { + tmp = (l >> rb)&BN_MASK2; + l = *(f++); + *(t++) = (tmp | (l << lb))&BN_MASK2; + } + *(t++) = (l >> rb)&BN_MASK2; + } + *t = 0; + bn_fix_top(r); + return (1); +} +#endif diff --git a/cscrypt/bn_sqr.c b/cscrypt/bn_sqr.c new file mode 100644 index 0000000..0c1c147 --- /dev/null +++ b/cscrypt/bn_sqr.c @@ -0,0 +1,296 @@ +#include "bn.h" + +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/bn/bn_sqr.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#include +#include +#include "bn_lcl.h" +#include "openssl_mods.h" + +/* r must not be a */ +/* I've just gone over this and it is now %20 faster on x86 - eay - 27 Jun 96 */ +int BN_sqr(BIGNUM *r, BIGNUM *a, BN_CTX *ctx) +{ + int max, al; + int ret = 0; + BIGNUM *tmp, *rr; + +#ifdef BN_COUNT + printf("BN_sqr %d * %d\n", a->top, a->top); +#endif + bn_check_top(a); + + al = a->top; + if(al <= 0) + { + r->top = 0; + return (1); + } + + BN_CTX_start(ctx); + rr = (a != r) ? r : BN_CTX_get(ctx); + tmp = BN_CTX_get(ctx); + if(tmp == NULL) { goto err; } + + max = (al + al); + if(bn_wexpand(rr, max + 1) == NULL) { goto err; } + + r->neg = 0; + if(al == 4) + { +#ifndef BN_SQR_COMBA + BN_ULONG t[8]; + bn_sqr_normal(rr->d, a->d, 4, t); +#else + bn_sqr_comba4(rr->d, a->d); +#endif + } + else if(al == 8) + { +#ifndef BN_SQR_COMBA + BN_ULONG t[16]; + bn_sqr_normal(rr->d, a->d, 8, t); +#else + bn_sqr_comba8(rr->d, a->d); +#endif + } + else + { +#if defined(BN_RECURSION) + if(al < BN_SQR_RECURSIVE_SIZE_NORMAL) + { + BN_ULONG t[BN_SQR_RECURSIVE_SIZE_NORMAL * 2]; + bn_sqr_normal(rr->d, a->d, al, t); + } + else + { + int j, k; + + j = BN_num_bits_word((BN_ULONG)al); + j = 1 << (j - 1); + k = j + j; + if(al == j) + { + if(bn_wexpand(a, k * 2) == NULL) { goto err; } + if(bn_wexpand(tmp, k * 2) == NULL) { goto err; } + bn_sqr_recursive(rr->d, a->d, al, tmp->d); + } + else + { + if(bn_wexpand(tmp, max) == NULL) { goto err; } + bn_sqr_normal(rr->d, a->d, al, tmp->d); + } + } +#else + if(bn_wexpand(tmp, max) == NULL) { goto err; } + bn_sqr_normal(rr->d, a->d, al, tmp->d); +#endif + } + + rr->top = max; + if((max > 0) && (rr->d[max - 1] == 0)) { rr->top--; } + if(rr != r) { BN_copy(r, rr); } + ret = 1; +err: + BN_CTX_end(ctx); + return (ret); +} + +/* tmp must have 2*n words */ +void bn_sqr_normal(BN_ULONG *r, BN_ULONG *a, int n, BN_ULONG *tmp) +{ + int i, j, max; + BN_ULONG *ap, *rp; + + max = n * 2; + ap = a; + rp = r; + rp[0] = rp[max - 1] = 0; + rp++; + j = n; + + if(--j > 0) + { + ap++; + rp[j] = bn_mul_words(rp, ap, j, ap[-1]); + rp += 2; + } + + for(i = n - 2; i > 0; i--) + { + j--; + ap++; + rp[j] = bn_mul_add_words(rp, ap, j, ap[-1]); + rp += 2; + } + + bn_add_words(r, r, r, max); + + /* There will not be a carry */ + + bn_sqr_words(tmp, a, n); + + bn_add_words(r, r, tmp, max); +} + +#ifdef BN_RECURSION +/* r is 2*n words in size, + * a and b are both n words in size. (There's not actually a 'b' here ...) + * n must be a power of 2. + * We multiply and return the result. + * t must be 2*n words in size + * We calculate + * a[0]*b[0] + * a[0]*b[0]+a[1]*b[1]+(a[0]-a[1])*(b[1]-b[0]) + * a[1]*b[1] + */ +void bn_sqr_recursive(BN_ULONG *r, BN_ULONG *a, int n2, BN_ULONG *t) +{ + int n = n2 / 2; + int zero, c1; + BN_ULONG ln, lo, *p; + +#ifdef BN_COUNT + printf(" bn_sqr_recursive %d * %d\n", n2, n2); +#endif + if(n2 == 4) + { +#ifndef BN_SQR_COMBA + bn_sqr_normal(r, a, 4, t); +#else + bn_sqr_comba4(r, a); +#endif + return; + } + else if(n2 == 8) + { +#ifndef BN_SQR_COMBA + bn_sqr_normal(r, a, 8, t); +#else + bn_sqr_comba8(r, a); +#endif + return; + } + if(n2 < BN_SQR_RECURSIVE_SIZE_NORMAL) + { + bn_sqr_normal(r, a, n2, t); + return; + } + /* r=(a[0]-a[1])*(a[1]-a[0]) */ + c1 = bn_cmp_words(a, &(a[n]), n); + zero = 0; + if(c1 > 0) + { bn_sub_words(t, a, &(a[n]), n); } + else if(c1 < 0) + { bn_sub_words(t, &(a[n]), a, n); } + else + { zero = 1; } + + /* The result will always be negative unless it is zero */ + p = &(t[n2 * 2]); + + if(!zero) + { bn_sqr_recursive(&(t[n2]), t, n, p); } + else + { memset(&(t[n2]), 0, n2 * sizeof(BN_ULONG)); } + bn_sqr_recursive(r, a, n, p); + bn_sqr_recursive(&(r[n2]), &(a[n]), n, p); + + /* t[32] holds (a[0]-a[1])*(a[1]-a[0]), it is negative or zero + * r[10] holds (a[0]*b[0]) + * r[32] holds (b[1]*b[1]) + */ + + c1 = (int)(bn_add_words(t, r, &(r[n2]), n2)); + + /* t[32] is negative */ + c1 -= (int)(bn_sub_words(&(t[n2]), t, &(t[n2]), n2)); + + /* t[32] holds (a[0]-a[1])*(a[1]-a[0])+(a[0]*a[0])+(a[1]*a[1]) + * r[10] holds (a[0]*a[0]) + * r[32] holds (a[1]*a[1]) + * c1 holds the carry bits + */ + c1 += (int)(bn_add_words(&(r[n]), &(r[n]), &(t[n2]), n2)); + if(c1) + { + p = &(r[n + n2]); + lo = *p; + ln = (lo + c1)&BN_MASK2; + *p = ln; + + /* The overflow will stop before we over write + * words we should not overwrite */ + if(ln < (BN_ULONG)c1) + { + do + { + p++; + lo = *p; + ln = (lo + 1)&BN_MASK2; + *p = ln; + } + while(ln == 0); + } + } +} +#endif +#endif diff --git a/cscrypt/bn_word.c b/cscrypt/bn_word.c new file mode 100644 index 0000000..cd8b804 --- /dev/null +++ b/cscrypt/bn_word.c @@ -0,0 +1,204 @@ +#include "bn.h" + +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/bn/bn_word.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#include +#include "bn_lcl.h" +#include "openssl_mods.h" + +BN_ULONG BN_mod_word(const BIGNUM *a, BN_ULONG w) +{ +#ifndef BN_LLONG + BN_ULONG ret = 0; +#else + BN_ULLONG ret = 0; +#endif + int i; + + w &= BN_MASK2; + for(i = a->top - 1; i >= 0; i--) + { +#ifndef BN_LLONG + ret = ((ret << BN_BITS4) | ((a->d[i] >> BN_BITS4)&BN_MASK2l)) % w; + ret = ((ret << BN_BITS4) | (a->d[i] & BN_MASK2l)) % w; +#else + ret = (BN_ULLONG)(((ret << (BN_ULLONG)BN_BITS2) | a->d[i]) % + (BN_ULLONG)w); +#endif + } + return ((BN_ULONG)ret); +} + +BN_ULONG BN_div_word(BIGNUM *a, BN_ULONG w) +{ + BN_ULONG ret; + int i; + + if(a->top == 0) { return (0); } + ret = 0; + w &= BN_MASK2; + for(i = a->top - 1; i >= 0; i--) + { + BN_ULONG l, d; + + l = a->d[i]; + d = bn_div_words(ret, l, w); + ret = (l - ((d * w)&BN_MASK2))&BN_MASK2; + a->d[i] = d; + } + if((a->top > 0) && (a->d[a->top - 1] == 0)) + { a->top--; } + return (ret); +} + +int BN_add_word(BIGNUM *a, BN_ULONG w) +{ + BN_ULONG l; + int i; + + if(a->neg) + { + a->neg = 0; + i = BN_sub_word(a, w); + if(!BN_is_zero(a)) + { a->neg = !(a->neg); } + return (i); + } + w &= BN_MASK2; + if(bn_wexpand(a, a->top + 1) == NULL) { return (0); } + i = 0; + for(;;) + { + l = (a->d[i] + (BN_ULONG)w)&BN_MASK2; + a->d[i] = l; + if(w > l) + { w = 1; } + else + { break; } + i++; + } + if(i >= a->top) + { a->top++; } + return (1); +} + +int BN_sub_word(BIGNUM *a, BN_ULONG w) +{ + int i; + + if(BN_is_zero(a) || a->neg) + { + a->neg = 0; + i = BN_add_word(a, w); + a->neg = 1; + return (i); + } + + w &= BN_MASK2; + if((a->top == 1) && (a->d[0] < w)) + { + a->d[0] = w - a->d[0]; + a->neg = 1; + return (1); + } + i = 0; + for(;;) + { + if(a->d[i] >= w) + { + a->d[i] -= w; + break; + } + else + { + a->d[i] = (a->d[i] - w)&BN_MASK2; + i++; + w = 1; + } + } + if((a->d[i] == 0) && (i == (a->top - 1))) + { a->top--; } + return (1); +} + +int BN_mul_word(BIGNUM *a, BN_ULONG w) +{ + BN_ULONG ll; + + w &= BN_MASK2; + if(a->top) + { + if(w == 0) + { BN_zero(a); } + else + { + ll = bn_mul_words(a->d, a->d, a->top, w); + if(ll) + { + if(bn_wexpand(a, a->top + 1) == NULL) { return (0); } + a->d[a->top++] = ll; + } + } + } + return (1); +} + +#endif diff --git a/cscrypt/buffer.h b/cscrypt/buffer.h new file mode 100644 index 0000000..d5a3bf3 --- /dev/null +++ b/cscrypt/buffer.h @@ -0,0 +1,98 @@ +/* crypto/buffer/buffer.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#ifndef HEADER_BUFFER_H +#define HEADER_BUFFER_H + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct buf_mem_st + { + int length; /* current number of bytes */ + char *data; + int max; /* size of buffer */ + } BUF_MEM; + + BUF_MEM *BUF_MEM_new(void); + void BUF_MEM_free(BUF_MEM *a); + int BUF_MEM_grow(BUF_MEM *str, int len); + char *BUF_strdup(const char *str); + + void ERR_load_BUF_strings(void); + + /* BEGIN ERROR CODES */ + /* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ + + /* Error codes for the BUF functions. */ + + /* Function codes. */ +#define BUF_F_BUF_MEM_GROW 100 +#define BUF_F_BUF_MEM_NEW 101 +#define BUF_F_BUF_STRDUP 102 + + /* Reason codes. */ + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/cscrypt/des.c b/cscrypt/des.c new file mode 100644 index 0000000..e9edd9e --- /dev/null +++ b/cscrypt/des.c @@ -0,0 +1,920 @@ +// Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) +// Java port Copyright 1996 Frank O'Dwyer (fod@brd.ie) +// Copyright 1996 Rainbow Diamond Limited +// All rights reserved. +// +// The ie.brd.crypto.algorithms.DES package is substantially derived from +// part of an SSL implementation written in 'C' by Eric Young (eay@mincom.oz.au). +// See below for the terms and conditions that apply to that code. This section +// describes the additional terms and conditions for this Java port only: +// +// NOTICE TO USER: +// THIS IS A CONTRACT BETWEEN YOU AND RAINBOW DIAMOND LIMITED ("RAINBOW DIAMOND"), +// AN IRISH LIMITED COMPANY. BY INSTALLING THIS SOFTWARE, YOU ACCEPT ALL THE +// TERMS AND CONDITIONS OF THIS AGREEMENT. ADDITIONALLY, NOTHING OTHER THAN +// ACCEPTING THE TERMS OF THIS AGREEMENT ENTITLES YOU TO COPY OR REDISTRIBUTE +// THIS SOFTWARE. +// +// This set of classes is FREE FOR COMMERCIAL AND NON-COMMERCIAL USE +// as long as the following conditions are adhered to: +// +// Copyright remains with the authors and as such any Copyright notices in +// the code are not to be removed. If this code is used in a product, +// Eric Young and Rainbow Diamond Limited should be given attribution as the +// authors of the parts used. This can be in the form of a textual message at +// program startup or in documentation (online or textual) provided with the +// package. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by Eric Young (eay@mincom.oz.au) +// Java port by Frank O'Dwyer (fod@brd.ie) for Rainbow Diamond Limited. +// 4. You agree that the software will not be shipped, transferred or exported +// into any country or used in any manner prohibited by applicable export +// laws, restrictions or regulations. You agree to indemnify and save +// harmless Rainbow Diamond Limited, its employees, and suppliers against +// any loss, injury, damage or expense whatsover either to it, or any third +// party as a result of your own acts, defaults, or neglect in exporting +// or transferring the software. +// 5. RAINBOW DIAMOND LIMITED IS PROVIDING YOU WITH THIS SOFTWARE FREE OF CHARGE +// FOR DEMONSTRATION PURPOSES ON AN "AS IS" BASIS. RAINBOW DIAMOND AND ITS +// SUPPLIERS DO NOT AND CANNOT WARRANT THE PERFORMANCE OR RESULTS YOU MAY +// OBTAIN BY USING THE SOFTWARE OR DOCUMENTATION. SAVE FOR ANY WARRANTY WHICH +// CANNOT BE EXCLUDED BY COMPULSORY LAW IN IRELAND, RAINBOW DIAMOND AND ITS +// SUPPLIERS MAKE NO WARRANTIES OR CONDITIONS, EXPRESS OR IMPLIED, AS TO +// NONINFRINGEMENT OF THIRD PARTY RIGHTS, MERCHANTIBILITY, SATISFACTORY QUALITY +// OR FITNESS FOR ANY PARTICULAR PURPOSE. IN NO EVENT WILL RAINBOW DIAMOND +// OR ITS SUPPLIERS BE LIABLE TO YOU FOR ANY DAMAGES WHATSOEVER (INCLUDING, +// WITHOUT LIMITATION CONSEQUENTIAL, INCIDENTAL OR SPECIAL DAMAGES, INCLUDING +// ANY LOST PROFITS OR LOST SAVINGS) ARISING OUT OF THE USE OR INABILITY TO +// USE THE SOFTWARE EVEN IF A RAINBOW DIAMOND REPRESENTATIVE HAS BEEN ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY A THIRD PARTY. WHERE +// LEGALLY LIABILITY CANNOT BE EXCLUDED, BUT IT MAY BE LIMITED, RAINBOW +// DIAMOND'S LIABILITY AND THAT OF ITS SUPPLIERS SHALL BE LIMITED TO THE SUM +// OF TWENTY FIVE POUNDS (�25) IN TOTAL. +// +// The contractual rights which you enjoy by virtue of Section 12, 13, 14, and +// 15 of the Sale of Goods Act, 1893 (as amended) are in no way prejudiced +// by anything contained in this Agreement save (if you are not dealing as +// a consumer or in the case of an international sale of goods) to the extent +// permitted by law. +// +// Section 39 of the Sale of Goods and Supply of Services Act, 1980 is hereby +// excluded with respect to the supply of this software. The contractual rights +// which you enjoy by virtue of the provisions of Section 39 of the Sale of Goods +// and Supply of Services Act, 1980 are in no way prejudiced by anything contained +// in these terms and conditions save to the extent permitted by law. +// +// Rainbow Diamond Limited is acting on behalf its suppliers for the purpose of +// disclaiming, excluding and/or restricting obligations, warranties and +// liability as provided in this clause 5, but in no other respects and for +// no other purpose. +// 6. This agreeement is governed by Irish law and you submit to the jurisdiction +// of the Irish courts in relation to any matter or dispute arising hereunder. +// +// The licence and distribution terms for any publically available version or +// derivative of this code cannot be changed. i.e. this code cannot simply be +// copied and put under another distribution licence +// [including the GNU Public Licence.] + +/* original eay copyright notice follows:*/ + +/* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) + * All rights reserved. + * + * This file is part of an SSL implementation written + * by Eric Young (eay@mincom.oz.au). + * The implementation was written so as to conform with Netscapes SSL + * specification. This library and applications are + * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE + * as long as the following conditions are aheared to. + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. If this code is used in a product, + * Eric Young should be given attribution as the author of the parts used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eric Young (eay@mincom.oz.au) + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include "../globals.h" +#include "../oscam-string.h" +#include "des.h" + +static const uint8_t weak_keys[16][8] = +{ + // weak keys + {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}, + {0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE}, + {0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F}, + {0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0}, + // semi-weak keys + {0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE}, + {0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01}, + {0x1F,0xE0,0x1F,0xE0,0x0E,0xF1,0x0E,0xF1}, + {0xE0,0x1F,0xE0,0x1F,0xF1,0x0E,0xF1,0x0E}, + {0x01,0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1}, + {0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1,0x01}, + {0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E,0xFE}, + {0xFE,0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E}, + {0x01,0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E}, + {0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E,0x01}, + {0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE}, + {0xFE,0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1} +}; + +static const uint8_t odd_parity[] = +{ + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98, 100,100,103,103,104,104,107,107,109,109,110,110, + 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, + 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, + 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, + 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, + 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, + 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, + 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, + 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, + 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 +}; + +static const uint8_t shifts2[16] = {0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0}; + +static const uint32_t des_skb[8][64] = +{ + { + 0x00000000,0x00000010,0x20000000,0x20000010, + 0x00010000,0x00010010,0x20010000,0x20010010, + 0x00000800,0x00000810,0x20000800,0x20000810, + 0x00010800,0x00010810,0x20010800,0x20010810, + 0x00000020,0x00000030,0x20000020,0x20000030, + 0x00010020,0x00010030,0x20010020,0x20010030, + 0x00000820,0x00000830,0x20000820,0x20000830, + 0x00010820,0x00010830,0x20010820,0x20010830, + 0x00080000,0x00080010,0x20080000,0x20080010, + 0x00090000,0x00090010,0x20090000,0x20090010, + 0x00080800,0x00080810,0x20080800,0x20080810, + 0x00090800,0x00090810,0x20090800,0x20090810, + 0x00080020,0x00080030,0x20080020,0x20080030, + 0x00090020,0x00090030,0x20090020,0x20090030, + 0x00080820,0x00080830,0x20080820,0x20080830, + 0x00090820,0x00090830,0x20090820,0x20090830, + },{ + + 0x00000000,0x02000000,0x00002000,0x02002000, + 0x00200000,0x02200000,0x00202000,0x02202000, + 0x00000004,0x02000004,0x00002004,0x02002004, + 0x00200004,0x02200004,0x00202004,0x02202004, + 0x00000400,0x02000400,0x00002400,0x02002400, + 0x00200400,0x02200400,0x00202400,0x02202400, + 0x00000404,0x02000404,0x00002404,0x02002404, + 0x00200404,0x02200404,0x00202404,0x02202404, + 0x10000000,0x12000000,0x10002000,0x12002000, + 0x10200000,0x12200000,0x10202000,0x12202000, + 0x10000004,0x12000004,0x10002004,0x12002004, + 0x10200004,0x12200004,0x10202004,0x12202004, + 0x10000400,0x12000400,0x10002400,0x12002400, + 0x10200400,0x12200400,0x10202400,0x12202400, + 0x10000404,0x12000404,0x10002404,0x12002404, + 0x10200404,0x12200404,0x10202404,0x12202404, + },{ + + 0x00000000,0x00000001,0x00040000,0x00040001, + 0x01000000,0x01000001,0x01040000,0x01040001, + 0x00000002,0x00000003,0x00040002,0x00040003, + 0x01000002,0x01000003,0x01040002,0x01040003, + 0x00000200,0x00000201,0x00040200,0x00040201, + 0x01000200,0x01000201,0x01040200,0x01040201, + 0x00000202,0x00000203,0x00040202,0x00040203, + 0x01000202,0x01000203,0x01040202,0x01040203, + 0x08000000,0x08000001,0x08040000,0x08040001, + 0x09000000,0x09000001,0x09040000,0x09040001, + 0x08000002,0x08000003,0x08040002,0x08040003, + 0x09000002,0x09000003,0x09040002,0x09040003, + 0x08000200,0x08000201,0x08040200,0x08040201, + 0x09000200,0x09000201,0x09040200,0x09040201, + 0x08000202,0x08000203,0x08040202,0x08040203, + 0x09000202,0x09000203,0x09040202,0x09040203, + },{ + + 0x00000000,0x00100000,0x00000100,0x00100100, + 0x00000008,0x00100008,0x00000108,0x00100108, + 0x00001000,0x00101000,0x00001100,0x00101100, + 0x00001008,0x00101008,0x00001108,0x00101108, + 0x04000000,0x04100000,0x04000100,0x04100100, + 0x04000008,0x04100008,0x04000108,0x04100108, + 0x04001000,0x04101000,0x04001100,0x04101100, + 0x04001008,0x04101008,0x04001108,0x04101108, + 0x00020000,0x00120000,0x00020100,0x00120100, + 0x00020008,0x00120008,0x00020108,0x00120108, + 0x00021000,0x00121000,0x00021100,0x00121100, + 0x00021008,0x00121008,0x00021108,0x00121108, + 0x04020000,0x04120000,0x04020100,0x04120100, + 0x04020008,0x04120008,0x04020108,0x04120108, + 0x04021000,0x04121000,0x04021100,0x04121100, + 0x04021008,0x04121008,0x04021108,0x04121108, + },{ + + 0x00000000,0x10000000,0x00010000,0x10010000, + 0x00000004,0x10000004,0x00010004,0x10010004, + 0x20000000,0x30000000,0x20010000,0x30010000, + 0x20000004,0x30000004,0x20010004,0x30010004, + 0x00100000,0x10100000,0x00110000,0x10110000, + 0x00100004,0x10100004,0x00110004,0x10110004, + 0x20100000,0x30100000,0x20110000,0x30110000, + 0x20100004,0x30100004,0x20110004,0x30110004, + 0x00001000,0x10001000,0x00011000,0x10011000, + 0x00001004,0x10001004,0x00011004,0x10011004, + 0x20001000,0x30001000,0x20011000,0x30011000, + 0x20001004,0x30001004,0x20011004,0x30011004, + 0x00101000,0x10101000,0x00111000,0x10111000, + 0x00101004,0x10101004,0x00111004,0x10111004, + 0x20101000,0x30101000,0x20111000,0x30111000, + 0x20101004,0x30101004,0x20111004,0x30111004, + },{ + + 0x00000000,0x08000000,0x00000008,0x08000008, + 0x00000400,0x08000400,0x00000408,0x08000408, + 0x00020000,0x08020000,0x00020008,0x08020008, + 0x00020400,0x08020400,0x00020408,0x08020408, + 0x00000001,0x08000001,0x00000009,0x08000009, + 0x00000401,0x08000401,0x00000409,0x08000409, + 0x00020001,0x08020001,0x00020009,0x08020009, + 0x00020401,0x08020401,0x00020409,0x08020409, + 0x02000000,0x0A000000,0x02000008,0x0A000008, + 0x02000400,0x0A000400,0x02000408,0x0A000408, + 0x02020000,0x0A020000,0x02020008,0x0A020008, + 0x02020400,0x0A020400,0x02020408,0x0A020408, + 0x02000001,0x0A000001,0x02000009,0x0A000009, + 0x02000401,0x0A000401,0x02000409,0x0A000409, + 0x02020001,0x0A020001,0x02020009,0x0A020009, + 0x02020401,0x0A020401,0x02020409,0x0A020409, + },{ + + 0x00000000,0x00000100,0x00080000,0x00080100, + 0x01000000,0x01000100,0x01080000,0x01080100, + 0x00000010,0x00000110,0x00080010,0x00080110, + 0x01000010,0x01000110,0x01080010,0x01080110, + 0x00200000,0x00200100,0x00280000,0x00280100, + 0x01200000,0x01200100,0x01280000,0x01280100, + 0x00200010,0x00200110,0x00280010,0x00280110, + 0x01200010,0x01200110,0x01280010,0x01280110, + 0x00000200,0x00000300,0x00080200,0x00080300, + 0x01000200,0x01000300,0x01080200,0x01080300, + 0x00000210,0x00000310,0x00080210,0x00080310, + 0x01000210,0x01000310,0x01080210,0x01080310, + 0x00200200,0x00200300,0x00280200,0x00280300, + 0x01200200,0x01200300,0x01280200,0x01280300, + 0x00200210,0x00200310,0x00280210,0x00280310, + 0x01200210,0x01200310,0x01280210,0x01280310, + },{ + + 0x00000000,0x04000000,0x00040000,0x04040000, + 0x00000002,0x04000002,0x00040002,0x04040002, + 0x00002000,0x04002000,0x00042000,0x04042000, + 0x00002002,0x04002002,0x00042002,0x04042002, + 0x00000020,0x04000020,0x00040020,0x04040020, + 0x00000022,0x04000022,0x00040022,0x04040022, + 0x00002020,0x04002020,0x00042020,0x04042020, + 0x00002022,0x04002022,0x00042022,0x04042022, + 0x00000800,0x04000800,0x00040800,0x04040800, + 0x00000802,0x04000802,0x00040802,0x04040802, + 0x00002800,0x04002800,0x00042800,0x04042800, + 0x00002802,0x04002802,0x00042802,0x04042802, + 0x00000820,0x04000820,0x00040820,0x04040820, + 0x00000822,0x04000822,0x00040822,0x04040822, + 0x00002820,0x04002820,0x00042820,0x04042820, + 0x00002822,0x04002822,0x00042822,0x04042822, + } +}; + +static const uint32_t des_SPtrans[8][64] = +{ + { + 0x00820200, 0x00020000, 0x80800000, 0x80820200, + 0x00800000, 0x80020200, 0x80020000, 0x80800000, + 0x80020200, 0x00820200, 0x00820000, 0x80000200, + 0x80800200, 0x00800000, 0x00000000, 0x80020000, + 0x00020000, 0x80000000, 0x00800200, 0x00020200, + 0x80820200, 0x00820000, 0x80000200, 0x00800200, + 0x80000000, 0x00000200, 0x00020200, 0x80820000, + 0x00000200, 0x80800200, 0x80820000, 0x00000000, + 0x00000000, 0x80820200, 0x00800200, 0x80020000, + 0x00820200, 0x00020000, 0x80000200, 0x00800200, + 0x80820000, 0x00000200, 0x00020200, 0x80800000, + 0x80020200, 0x80000000, 0x80800000, 0x00820000, + 0x80820200, 0x00020200, 0x00820000, 0x80800200, + 0x00800000, 0x80000200, 0x80020000, 0x00000000, + 0x00020000, 0x00800000, 0x80800200, 0x00820200, + 0x80000000, 0x80820000, 0x00000200, 0x80020200, + },{ + + 0x10042004, 0x00000000, 0x00042000, 0x10040000, + 0x10000004, 0x00002004, 0x10002000, 0x00042000, + 0x00002000, 0x10040004, 0x00000004, 0x10002000, + 0x00040004, 0x10042000, 0x10040000, 0x00000004, + 0x00040000, 0x10002004, 0x10040004, 0x00002000, + 0x00042004, 0x10000000, 0x00000000, 0x00040004, + 0x10002004, 0x00042004, 0x10042000, 0x10000004, + 0x10000000, 0x00040000, 0x00002004, 0x10042004, + 0x00040004, 0x10042000, 0x10002000, 0x00042004, + 0x10042004, 0x00040004, 0x10000004, 0x00000000, + 0x10000000, 0x00002004, 0x00040000, 0x10040004, + 0x00002000, 0x10000000, 0x00042004, 0x10002004, + 0x10042000, 0x00002000, 0x00000000, 0x10000004, + 0x00000004, 0x10042004, 0x00042000, 0x10040000, + 0x10040004, 0x00040000, 0x00002004, 0x10002000, + 0x10002004, 0x00000004, 0x10040000, 0x00042000, + },{ + + 0x41000000, 0x01010040, 0x00000040, 0x41000040, + 0x40010000, 0x01000000, 0x41000040, 0x00010040, + 0x01000040, 0x00010000, 0x01010000, 0x40000000, + 0x41010040, 0x40000040, 0x40000000, 0x41010000, + 0x00000000, 0x40010000, 0x01010040, 0x00000040, + 0x40000040, 0x41010040, 0x00010000, 0x41000000, + 0x41010000, 0x01000040, 0x40010040, 0x01010000, + 0x00010040, 0x00000000, 0x01000000, 0x40010040, + 0x01010040, 0x00000040, 0x40000000, 0x00010000, + 0x40000040, 0x40010000, 0x01010000, 0x41000040, + 0x00000000, 0x01010040, 0x00010040, 0x41010000, + 0x40010000, 0x01000000, 0x41010040, 0x40000000, + 0x40010040, 0x41000000, 0x01000000, 0x41010040, + 0x00010000, 0x01000040, 0x41000040, 0x00010040, + 0x01000040, 0x00000000, 0x41010000, 0x40000040, + 0x41000000, 0x40010040, 0x00000040, 0x01010000, + },{ + + 0x00100402, 0x04000400, 0x00000002, 0x04100402, + 0x00000000, 0x04100000, 0x04000402, 0x00100002, + 0x04100400, 0x04000002, 0x04000000, 0x00000402, + 0x04000002, 0x00100402, 0x00100000, 0x04000000, + 0x04100002, 0x00100400, 0x00000400, 0x00000002, + 0x00100400, 0x04000402, 0x04100000, 0x00000400, + 0x00000402, 0x00000000, 0x00100002, 0x04100400, + 0x04000400, 0x04100002, 0x04100402, 0x00100000, + 0x04100002, 0x00000402, 0x00100000, 0x04000002, + 0x00100400, 0x04000400, 0x00000002, 0x04100000, + 0x04000402, 0x00000000, 0x00000400, 0x00100002, + 0x00000000, 0x04100002, 0x04100400, 0x00000400, + 0x04000000, 0x04100402, 0x00100402, 0x00100000, + 0x04100402, 0x00000002, 0x04000400, 0x00100402, + 0x00100002, 0x00100400, 0x04100000, 0x04000402, + 0x00000402, 0x04000000, 0x04000002, 0x04100400, + },{ + + 0x02000000, 0x00004000, 0x00000100, 0x02004108, + 0x02004008, 0x02000100, 0x00004108, 0x02004000, + 0x00004000, 0x00000008, 0x02000008, 0x00004100, + 0x02000108, 0x02004008, 0x02004100, 0x00000000, + 0x00004100, 0x02000000, 0x00004008, 0x00000108, + 0x02000100, 0x00004108, 0x00000000, 0x02000008, + 0x00000008, 0x02000108, 0x02004108, 0x00004008, + 0x02004000, 0x00000100, 0x00000108, 0x02004100, + 0x02004100, 0x02000108, 0x00004008, 0x02004000, + 0x00004000, 0x00000008, 0x02000008, 0x02000100, + 0x02000000, 0x00004100, 0x02004108, 0x00000000, + 0x00004108, 0x02000000, 0x00000100, 0x00004008, + 0x02000108, 0x00000100, 0x00000000, 0x02004108, + 0x02004008, 0x02004100, 0x00000108, 0x00004000, + 0x00004100, 0x02004008, 0x02000100, 0x00000108, + 0x00000008, 0x00004108, 0x02004000, 0x02000008, + },{ + + 0x20000010, 0x00080010, 0x00000000, 0x20080800, + 0x00080010, 0x00000800, 0x20000810, 0x00080000, + 0x00000810, 0x20080810, 0x00080800, 0x20000000, + 0x20000800, 0x20000010, 0x20080000, 0x00080810, + 0x00080000, 0x20000810, 0x20080010, 0x00000000, + 0x00000800, 0x00000010, 0x20080800, 0x20080010, + 0x20080810, 0x20080000, 0x20000000, 0x00000810, + 0x00000010, 0x00080800, 0x00080810, 0x20000800, + 0x00000810, 0x20000000, 0x20000800, 0x00080810, + 0x20080800, 0x00080010, 0x00000000, 0x20000800, + 0x20000000, 0x00000800, 0x20080010, 0x00080000, + 0x00080010, 0x20080810, 0x00080800, 0x00000010, + 0x20080810, 0x00080800, 0x00080000, 0x20000810, + 0x20000010, 0x20080000, 0x00080810, 0x00000000, + 0x00000800, 0x20000010, 0x20000810, 0x20080800, + 0x20080000, 0x00000810, 0x00000010, 0x20080010, + },{ + + 0x00001000, 0x00000080, 0x00400080, 0x00400001, + 0x00401081, 0x00001001, 0x00001080, 0x00000000, + 0x00400000, 0x00400081, 0x00000081, 0x00401000, + 0x00000001, 0x00401080, 0x00401000, 0x00000081, + 0x00400081, 0x00001000, 0x00001001, 0x00401081, + 0x00000000, 0x00400080, 0x00400001, 0x00001080, + 0x00401001, 0x00001081, 0x00401080, 0x00000001, + 0x00001081, 0x00401001, 0x00000080, 0x00400000, + 0x00001081, 0x00401000, 0x00401001, 0x00000081, + 0x00001000, 0x00000080, 0x00400000, 0x00401001, + 0x00400081, 0x00001081, 0x00001080, 0x00000000, + 0x00000080, 0x00400001, 0x00000001, 0x00400080, + 0x00000000, 0x00400081, 0x00400080, 0x00001080, + 0x00000081, 0x00001000, 0x00401081, 0x00400000, + 0x00401080, 0x00000001, 0x00001001, 0x00401081, + 0x00400001, 0x00401080, 0x00401000, 0x00001001, + },{ + + 0x08200020, 0x08208000, 0x00008020, 0x00000000, + 0x08008000, 0x00200020, 0x08200000, 0x08208020, + 0x00000020, 0x08000000, 0x00208000, 0x00008020, + 0x00208020, 0x08008020, 0x08000020, 0x08200000, + 0x00008000, 0x00208020, 0x00200020, 0x08008000, + 0x08208020, 0x08000020, 0x00000000, 0x00208000, + 0x08000000, 0x00200000, 0x08008020, 0x08200020, + 0x00200000, 0x00008000, 0x08208000, 0x00000020, + 0x00200000, 0x00008000, 0x08000020, 0x08208020, + 0x00008020, 0x08000000, 0x00000000, 0x00208000, + 0x08200020, 0x08008020, 0x08008000, 0x00200020, + 0x08208000, 0x00000020, 0x00200020, 0x08008000, + 0x08208020, 0x00200000, 0x08200000, 0x08000020, + 0x00208000, 0x00008020, 0x08008020, 0x08200000, + 0x00000020, 0x08208000, 0x00208020, 0x00000000, + 0x08000000, 0x08200020, 0x00008000, 0x00208020, + } +}; + +static const int32_t DES_KEY_SZ=8; + +void des_set_odd_parity(uint8_t* key) +{ + int32_t i; + + for (i=0; i < DES_KEY_SZ; i++) + key[i]=odd_parity[key[i]&0xff]; +} + +int8_t check_parity(const uint8_t* key) +{ + int32_t i; + + for (i=0; i < DES_KEY_SZ; i++) + { + if (key[i] != odd_parity[key[i]&0xff]) + return 0; + } + return 1; +} + +int8_t des_is_weak_key(const uint8_t* key) +{ + int32_t i, j; + + for (i=0; i < 16; i++) + { + for(j=0; j < DES_KEY_SZ; j++) + { + if (weak_keys[i][j] != key[j]) + { + // not weak + continue; + } + } + // weak + return 1; + } + return 0; +} + +static uint32_t Get32bits(const uint8_t* key, int32_t kindex) +{ + return(((key[kindex+3]&0xff)<<24) + ((key[kindex+2]&0xff)<<16) + ((key[kindex+1]&0xff)<<8) + (key[kindex]&0xff)); +} + +int8_t des_set_key(const uint8_t* key, uint32_t* schedule) +{ + uint32_t c,d,t,s; + int32_t inIndex; + int32_t kIndex; + int32_t i; + inIndex=0; + kIndex=0; + c =Get32bits(key, inIndex); + d =Get32bits(key, inIndex+4); + t=(((d>>4)^c)&0x0f0f0f0f); + c^=t; + d^=(t<<4); + t=(((c<<(16-(-2)))^c)&0xcccc0000); + c=c^t^(t>>(16-(-2))); + t=((d<<(16-(-2)))^d)&0xcccc0000; + d=d^t^(t>>(16-(-2))); + t=((d>>1)^c)&0x55555555; + c^=t; + d^=(t<<1); + t=((c>>8)^d)&0x00ff00ff; + d^=t; + c^=(t<<8); + t=((d>>1)^c)&0x55555555; + c^=t; + d^=(t<<1); + d= (((d&0x000000ff)<<16)| (d&0x0000ff00) |((d&0x00ff0000)>>16)|((c&0xf0000000)>>4)); + c&=0x0fffffff; + for (i=0; i < 16; i++) + { + if (shifts2[i]) + { + c=((c>>2)|(c<<26)); + d=((d>>2)|(d<<26)); + } + else + { + c=((c>>1)|(c<<27)); + d=((d>>1)|(d<<27)); + } + c&=0x0fffffff; + d&=0x0fffffff; + s= des_skb[0][ (c )&0x3f ]| + des_skb[1][((c>> 6)&0x03)|((c>> 7)&0x3c)]| + des_skb[2][((c>>13)&0x0f)|((c>>14)&0x30)]| + des_skb[3][((c>>20)&0x01)|((c>>21)&0x06) | + ((c>>22)&0x38)]; + t= des_skb[4][ (d )&0x3f ]| + des_skb[5][((d>> 7)&0x03)|((d>> 8)&0x3c)]| + des_skb[6][ (d>>15)&0x3f ]| + des_skb[7][((d>>21)&0x0f)|((d>>22)&0x30)]; + schedule[kIndex++]=((t<<16)|(s&0x0000ffff))&0xffffffff; + s=((s>>16)|(t&0xffff0000)); + s=(s<<4)|(s>>28); + schedule[kIndex++]=s&0xffffffff; + } + return 1; +} + +static uint32_t _lrotr(uint32_t i) +{ + return((i>>4) | ((i&0xff)<<28)); +} + +static void des_encrypt_int(uint32_t* data, const uint32_t* ks, int8_t do_encrypt) +{ + uint32_t l=0,r=0,t=0,u=0; + int32_t i; + + u=data[0]; + r=data[1]; + + { + uint32_t tt; + + tt=((r>>4)^u)&0x0f0f0f0f; + u^=tt; + r^=(tt<<4); + tt=(((u>>16)^r)&0x0000ffff); + r^=tt; + u^=(tt<<16); + tt=(((r>>2)^u)&0x33333333); + u^=tt; + r^=(tt<<2); + tt=(((u>>8)^r)&0x00ff00ff); + r^=tt; + u^=(tt<<8); + tt=(((r>>1)^u)&0x55555555); + u^=tt; + r^=(tt<<1); + } + + l=(r<<1)|(r>>31); + r=(u<<1)|(u>>31); + l&=0xffffffff; + r&=0xffffffff; + + if (do_encrypt) + { + for (i=0; i < 32; i+=8) + { + { + u=(r^ks[i+0 ]); + t=r^ks[i+0+1]; + t=(_lrotr(t)); + l^= des_SPtrans[1][(t )&0x3f]| des_SPtrans[3][(t>> 8)&0x3f]| des_SPtrans[5][(t>>16)&0x3f]| des_SPtrans[7][(t>>24)&0x3f]| des_SPtrans[0][(u )&0x3f]| des_SPtrans[2][(u>> 8)&0x3f]| des_SPtrans[4][(u>>16)&0x3f]| des_SPtrans[6][(u>>24)&0x3f]; + }; + { + u=(l^ks[i+2 ]); + t=l^ks[i+2+1]; + t=(_lrotr(t)); + r^= des_SPtrans[1][(t )&0x3f]| des_SPtrans[3][(t>> 8)&0x3f]| des_SPtrans[5][(t>>16)&0x3f]| des_SPtrans[7][(t>>24)&0x3f]| des_SPtrans[0][(u )&0x3f]| des_SPtrans[2][(u>> 8)&0x3f]| des_SPtrans[4][(u>>16)&0x3f]| des_SPtrans[6][(u>>24)&0x3f]; + }; + { + u=(r^ks[i+4 ]); + t=r^ks[i+4+1]; + t=(_lrotr(t)); + l^= des_SPtrans[1][(t )&0x3f]| des_SPtrans[3][(t>> 8)&0x3f]| des_SPtrans[5][(t>>16)&0x3f]| des_SPtrans[7][(t>>24)&0x3f]| des_SPtrans[0][(u )&0x3f]| des_SPtrans[2][(u>> 8)&0x3f]| des_SPtrans[4][(u>>16)&0x3f]| des_SPtrans[6][(u>>24)&0x3f]; + }; + { + u=(l^ks[i+6 ]); + t=l^ks[i+6+1]; + t=(_lrotr(t)); + r^= des_SPtrans[1][(t )&0x3f]| des_SPtrans[3][(t>> 8)&0x3f]| des_SPtrans[5][(t>>16)&0x3f]| des_SPtrans[7][(t>>24)&0x3f]| des_SPtrans[0][(u )&0x3f]| des_SPtrans[2][(u>> 8)&0x3f]| des_SPtrans[4][(u>>16)&0x3f]| des_SPtrans[6][(u>>24)&0x3f]; + }; + } + } + else + { + for (i=30; i > 0; i-=8) + { + { + u=(r^ks[i-0 ]); + t=r^ks[i-0+1]; + t=(_lrotr(t)); + l^= des_SPtrans[1][(t )&0x3f]| des_SPtrans[3][(t>> 8)&0x3f]| des_SPtrans[5][(t>>16)&0x3f]| des_SPtrans[7][(t>>24)&0x3f]| des_SPtrans[0][(u )&0x3f]| des_SPtrans[2][(u>> 8)&0x3f]| des_SPtrans[4][(u>>16)&0x3f]| des_SPtrans[6][(u>>24)&0x3f]; + }; + { + u=(l^ks[i-2 ]); + t=l^ks[i-2+1]; + t=(_lrotr(t)); + r^= des_SPtrans[1][(t )&0x3f]| des_SPtrans[3][(t>> 8)&0x3f]| des_SPtrans[5][(t>>16)&0x3f]| des_SPtrans[7][(t>>24)&0x3f]| des_SPtrans[0][(u )&0x3f]| des_SPtrans[2][(u>> 8)&0x3f]| des_SPtrans[4][(u>>16)&0x3f]| des_SPtrans[6][(u>>24)&0x3f]; + }; + { + u=(r^ks[i-4 ]); + t=r^ks[i-4+1]; + t=(_lrotr(t)); + l^= des_SPtrans[1][(t )&0x3f]| des_SPtrans[3][(t>> 8)&0x3f]| des_SPtrans[5][(t>>16)&0x3f]| des_SPtrans[7][(t>>24)&0x3f]| des_SPtrans[0][(u )&0x3f]| des_SPtrans[2][(u>> 8)&0x3f]| des_SPtrans[4][(u>>16)&0x3f]| des_SPtrans[6][(u>>24)&0x3f]; + }; + { + u=(l^ks[i-6 ]); + t=l^ks[i-6+1]; + t=(_lrotr(t)); + r^= des_SPtrans[1][(t )&0x3f]| des_SPtrans[3][(t>> 8)&0x3f]| des_SPtrans[5][(t>>16)&0x3f]| des_SPtrans[7][(t>>24)&0x3f]| des_SPtrans[0][(u )&0x3f]| des_SPtrans[2][(u>> 8)&0x3f]| des_SPtrans[4][(u>>16)&0x3f]| des_SPtrans[6][(u>>24)&0x3f]; + }; + } + } + + l=(l>>1)|(l<<31); + r=(r>>1)|(r<<31); + l&=0xffffffff; + r&=0xffffffff; + + { + uint32_t tt; + tt=(((r>>1)^l)&0x55555555); + l^=tt; + r^=(tt<<1); + tt=(((l>>8)^r)&0x00ff00ff); + r^=tt; + l^=(tt<<8); + tt=(((r>>2)^l)&0x33333333); + l^=tt; + r^=(tt<<2); + tt=(((l>>16)^r)&0x0000ffff); + r^=tt; + l^=(tt<<16); + tt=(((r>>4)^l)&0x0f0f0f0f); + l^=tt; + r^=(tt<<4); + } + + data[0]=l; + data[1]=r; +} + +void des(uint8_t* data, const uint32_t* schedule, int8_t do_encrypt) +{ + uint32_t l, ll[2]; + int32_t inIndex; + int32_t outIndex; + + inIndex=0; + outIndex=0; + + l = Get32bits(data, inIndex); + ll[0]=l; + + l = Get32bits(data, inIndex+4); + ll[1]=l; + + des_encrypt_int(ll, schedule, do_encrypt); + + l=ll[0]; + + data[outIndex++] = (l&0xff); + data[outIndex++] = ((l>>8)&0xff); + data[outIndex++] = ((l>>16)&0xff); + data[outIndex++] = ((l>>24)&0xff); + l=ll[1]; + data[outIndex++] = (l&0xff); + data[outIndex++] = ((l>>8) &0xff); + data[outIndex++] = ((l>>16) &0xff); + data[outIndex++] = ((l>>24) &0xff); +} + +static inline void xxor(uint8_t *data, int32_t len, const uint8_t *v1, const uint8_t *v2) +{ + uint32_t i; + switch(len) + { + case 16: + for(i = 0; i < 16; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + case 8: + for(i = 0; i < 8; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + case 4: + for(i = 0; i < 4; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + default: + while(len--) + { + *data++ = *v1++ ^ *v2++; + } + break; + } +} + +void des_ecb_encrypt(uint8_t* data, const uint8_t* key, int32_t len) +{ + uint32_t schedule[32]; + int32_t i; + + des_set_key(key, schedule); + + len&=~7; + + for(i=0; i encrypt + // encrypt = 0 -> decrypt + void des(uint8_t* data, const uint32_t* schedule, int8_t do_encrypt); + + // these functions take a 8-byte des key and crypt data of any length ("len") + void des_ecb_encrypt(uint8_t* data, const uint8_t* key, int32_t len); + void des_ecb_decrypt(uint8_t* data, const uint8_t* key, int32_t len); + + void des_cbc_encrypt(uint8_t* data, const uint8_t* iv, const uint8_t* key, int32_t len); + void des_cbc_decrypt(uint8_t* data, const uint8_t* iv, const uint8_t* key, int32_t len); + + void des_ede2_cbc_encrypt(uint8_t* data, const uint8_t* iv, const uint8_t* key1, const uint8_t* key2, int32_t len); + void des_ede2_cbc_decrypt(uint8_t* data, const uint8_t* iv, const uint8_t* key1, const uint8_t* key2, int32_t len); + + void des_ecb3_encrypt(uint8_t* data, const uint8_t* key); + void des_ecb3_decrypt(uint8_t* data, const uint8_t* key); + +#endif diff --git a/cscrypt/fast_aes.c b/cscrypt/fast_aes.c new file mode 100644 index 0000000..e0e135d --- /dev/null +++ b/cscrypt/fast_aes.c @@ -0,0 +1,1187 @@ +#include "stdio.h" +#include "fast_aes.h" + +#ifndef WITH_LIBCRYPTO +extern const unsigned int Te0[256]; +extern const unsigned int Te1[256]; +extern const unsigned int Te2[256]; +extern const unsigned int Te3[256]; +extern const unsigned int Te4[256]; +extern const unsigned int Td0[256]; +extern const unsigned int Td1[256]; +extern const unsigned int Td2[256]; +extern const unsigned int Td3[256]; +extern const unsigned int Td4[256]; +extern const unsigned int rcon[]; +#else +static const unsigned int Te0[256] = +{ + 0xc66363a5UL, 0xf87c7c84UL, 0xee777799UL, 0xf67b7b8dUL, + 0xfff2f20dUL, 0xd66b6bbdUL, 0xde6f6fb1UL, 0x91c5c554UL, + 0x60303050UL, 0x02010103UL, 0xce6767a9UL, 0x562b2b7dUL, + 0xe7fefe19UL, 0xb5d7d762UL, 0x4dababe6UL, 0xec76769aUL, + 0x8fcaca45UL, 0x1f82829dUL, 0x89c9c940UL, 0xfa7d7d87UL, + 0xeffafa15UL, 0xb25959ebUL, 0x8e4747c9UL, 0xfbf0f00bUL, + 0x41adadecUL, 0xb3d4d467UL, 0x5fa2a2fdUL, 0x45afafeaUL, + 0x239c9cbfUL, 0x53a4a4f7UL, 0xe4727296UL, 0x9bc0c05bUL, + 0x75b7b7c2UL, 0xe1fdfd1cUL, 0x3d9393aeUL, 0x4c26266aUL, + 0x6c36365aUL, 0x7e3f3f41UL, 0xf5f7f702UL, 0x83cccc4fUL, + 0x6834345cUL, 0x51a5a5f4UL, 0xd1e5e534UL, 0xf9f1f108UL, + 0xe2717193UL, 0xabd8d873UL, 0x62313153UL, 0x2a15153fUL, + 0x0804040cUL, 0x95c7c752UL, 0x46232365UL, 0x9dc3c35eUL, + 0x30181828UL, 0x379696a1UL, 0x0a05050fUL, 0x2f9a9ab5UL, + 0x0e070709UL, 0x24121236UL, 0x1b80809bUL, 0xdfe2e23dUL, + 0xcdebeb26UL, 0x4e272769UL, 0x7fb2b2cdUL, 0xea75759fUL, + 0x1209091bUL, 0x1d83839eUL, 0x582c2c74UL, 0x341a1a2eUL, + 0x361b1b2dUL, 0xdc6e6eb2UL, 0xb45a5aeeUL, 0x5ba0a0fbUL, + 0xa45252f6UL, 0x763b3b4dUL, 0xb7d6d661UL, 0x7db3b3ceUL, + 0x5229297bUL, 0xdde3e33eUL, 0x5e2f2f71UL, 0x13848497UL, + 0xa65353f5UL, 0xb9d1d168UL, 0x00000000UL, 0xc1eded2cUL, + 0x40202060UL, 0xe3fcfc1fUL, 0x79b1b1c8UL, 0xb65b5bedUL, + 0xd46a6abeUL, 0x8dcbcb46UL, 0x67bebed9UL, 0x7239394bUL, + 0x944a4adeUL, 0x984c4cd4UL, 0xb05858e8UL, 0x85cfcf4aUL, + 0xbbd0d06bUL, 0xc5efef2aUL, 0x4faaaae5UL, 0xedfbfb16UL, + 0x864343c5UL, 0x9a4d4dd7UL, 0x66333355UL, 0x11858594UL, + 0x8a4545cfUL, 0xe9f9f910UL, 0x04020206UL, 0xfe7f7f81UL, + 0xa05050f0UL, 0x783c3c44UL, 0x259f9fbaUL, 0x4ba8a8e3UL, + 0xa25151f3UL, 0x5da3a3feUL, 0x804040c0UL, 0x058f8f8aUL, + 0x3f9292adUL, 0x219d9dbcUL, 0x70383848UL, 0xf1f5f504UL, + 0x63bcbcdfUL, 0x77b6b6c1UL, 0xafdada75UL, 0x42212163UL, + 0x20101030UL, 0xe5ffff1aUL, 0xfdf3f30eUL, 0xbfd2d26dUL, + 0x81cdcd4cUL, 0x180c0c14UL, 0x26131335UL, 0xc3ecec2fUL, + 0xbe5f5fe1UL, 0x359797a2UL, 0x884444ccUL, 0x2e171739UL, + 0x93c4c457UL, 0x55a7a7f2UL, 0xfc7e7e82UL, 0x7a3d3d47UL, + 0xc86464acUL, 0xba5d5de7UL, 0x3219192bUL, 0xe6737395UL, + 0xc06060a0UL, 0x19818198UL, 0x9e4f4fd1UL, 0xa3dcdc7fUL, + 0x44222266UL, 0x542a2a7eUL, 0x3b9090abUL, 0x0b888883UL, + 0x8c4646caUL, 0xc7eeee29UL, 0x6bb8b8d3UL, 0x2814143cUL, + 0xa7dede79UL, 0xbc5e5ee2UL, 0x160b0b1dUL, 0xaddbdb76UL, + 0xdbe0e03bUL, 0x64323256UL, 0x743a3a4eUL, 0x140a0a1eUL, + 0x924949dbUL, 0x0c06060aUL, 0x4824246cUL, 0xb85c5ce4UL, + 0x9fc2c25dUL, 0xbdd3d36eUL, 0x43acacefUL, 0xc46262a6UL, + 0x399191a8UL, 0x319595a4UL, 0xd3e4e437UL, 0xf279798bUL, + 0xd5e7e732UL, 0x8bc8c843UL, 0x6e373759UL, 0xda6d6db7UL, + 0x018d8d8cUL, 0xb1d5d564UL, 0x9c4e4ed2UL, 0x49a9a9e0UL, + 0xd86c6cb4UL, 0xac5656faUL, 0xf3f4f407UL, 0xcfeaea25UL, + 0xca6565afUL, 0xf47a7a8eUL, 0x47aeaee9UL, 0x10080818UL, + 0x6fbabad5UL, 0xf0787888UL, 0x4a25256fUL, 0x5c2e2e72UL, + 0x381c1c24UL, 0x57a6a6f1UL, 0x73b4b4c7UL, 0x97c6c651UL, + 0xcbe8e823UL, 0xa1dddd7cUL, 0xe874749cUL, 0x3e1f1f21UL, + 0x964b4bddUL, 0x61bdbddcUL, 0x0d8b8b86UL, 0x0f8a8a85UL, + 0xe0707090UL, 0x7c3e3e42UL, 0x71b5b5c4UL, 0xcc6666aaUL, + 0x904848d8UL, 0x06030305UL, 0xf7f6f601UL, 0x1c0e0e12UL, + 0xc26161a3UL, 0x6a35355fUL, 0xae5757f9UL, 0x69b9b9d0UL, + 0x17868691UL, 0x99c1c158UL, 0x3a1d1d27UL, 0x279e9eb9UL, + 0xd9e1e138UL, 0xebf8f813UL, 0x2b9898b3UL, 0x22111133UL, + 0xd26969bbUL, 0xa9d9d970UL, 0x078e8e89UL, 0x339494a7UL, + 0x2d9b9bb6UL, 0x3c1e1e22UL, 0x15878792UL, 0xc9e9e920UL, + 0x87cece49UL, 0xaa5555ffUL, 0x50282878UL, 0xa5dfdf7aUL, + 0x038c8c8fUL, 0x59a1a1f8UL, 0x09898980UL, 0x1a0d0d17UL, + 0x65bfbfdaUL, 0xd7e6e631UL, 0x844242c6UL, 0xd06868b8UL, + 0x824141c3UL, 0x299999b0UL, 0x5a2d2d77UL, 0x1e0f0f11UL, + 0x7bb0b0cbUL, 0xa85454fcUL, 0x6dbbbbd6UL, 0x2c16163aUL, +}; +static const unsigned int Te1[256] = +{ + 0xa5c66363UL, 0x84f87c7cUL, 0x99ee7777UL, 0x8df67b7bUL, + 0x0dfff2f2UL, 0xbdd66b6bUL, 0xb1de6f6fUL, 0x5491c5c5UL, + 0x50603030UL, 0x03020101UL, 0xa9ce6767UL, 0x7d562b2bUL, + 0x19e7fefeUL, 0x62b5d7d7UL, 0xe64dababUL, 0x9aec7676UL, + 0x458fcacaUL, 0x9d1f8282UL, 0x4089c9c9UL, 0x87fa7d7dUL, + 0x15effafaUL, 0xebb25959UL, 0xc98e4747UL, 0x0bfbf0f0UL, + 0xec41adadUL, 0x67b3d4d4UL, 0xfd5fa2a2UL, 0xea45afafUL, + 0xbf239c9cUL, 0xf753a4a4UL, 0x96e47272UL, 0x5b9bc0c0UL, + 0xc275b7b7UL, 0x1ce1fdfdUL, 0xae3d9393UL, 0x6a4c2626UL, + 0x5a6c3636UL, 0x417e3f3fUL, 0x02f5f7f7UL, 0x4f83ccccUL, + 0x5c683434UL, 0xf451a5a5UL, 0x34d1e5e5UL, 0x08f9f1f1UL, + 0x93e27171UL, 0x73abd8d8UL, 0x53623131UL, 0x3f2a1515UL, + 0x0c080404UL, 0x5295c7c7UL, 0x65462323UL, 0x5e9dc3c3UL, + 0x28301818UL, 0xa1379696UL, 0x0f0a0505UL, 0xb52f9a9aUL, + 0x090e0707UL, 0x36241212UL, 0x9b1b8080UL, 0x3ddfe2e2UL, + 0x26cdebebUL, 0x694e2727UL, 0xcd7fb2b2UL, 0x9fea7575UL, + 0x1b120909UL, 0x9e1d8383UL, 0x74582c2cUL, 0x2e341a1aUL, + 0x2d361b1bUL, 0xb2dc6e6eUL, 0xeeb45a5aUL, 0xfb5ba0a0UL, + 0xf6a45252UL, 0x4d763b3bUL, 0x61b7d6d6UL, 0xce7db3b3UL, + 0x7b522929UL, 0x3edde3e3UL, 0x715e2f2fUL, 0x97138484UL, + 0xf5a65353UL, 0x68b9d1d1UL, 0x00000000UL, 0x2cc1ededUL, + 0x60402020UL, 0x1fe3fcfcUL, 0xc879b1b1UL, 0xedb65b5bUL, + 0xbed46a6aUL, 0x468dcbcbUL, 0xd967bebeUL, 0x4b723939UL, + 0xde944a4aUL, 0xd4984c4cUL, 0xe8b05858UL, 0x4a85cfcfUL, + 0x6bbbd0d0UL, 0x2ac5efefUL, 0xe54faaaaUL, 0x16edfbfbUL, + 0xc5864343UL, 0xd79a4d4dUL, 0x55663333UL, 0x94118585UL, + 0xcf8a4545UL, 0x10e9f9f9UL, 0x06040202UL, 0x81fe7f7fUL, + 0xf0a05050UL, 0x44783c3cUL, 0xba259f9fUL, 0xe34ba8a8UL, + 0xf3a25151UL, 0xfe5da3a3UL, 0xc0804040UL, 0x8a058f8fUL, + 0xad3f9292UL, 0xbc219d9dUL, 0x48703838UL, 0x04f1f5f5UL, + 0xdf63bcbcUL, 0xc177b6b6UL, 0x75afdadaUL, 0x63422121UL, + 0x30201010UL, 0x1ae5ffffUL, 0x0efdf3f3UL, 0x6dbfd2d2UL, + 0x4c81cdcdUL, 0x14180c0cUL, 0x35261313UL, 0x2fc3ececUL, + 0xe1be5f5fUL, 0xa2359797UL, 0xcc884444UL, 0x392e1717UL, + 0x5793c4c4UL, 0xf255a7a7UL, 0x82fc7e7eUL, 0x477a3d3dUL, + 0xacc86464UL, 0xe7ba5d5dUL, 0x2b321919UL, 0x95e67373UL, + 0xa0c06060UL, 0x98198181UL, 0xd19e4f4fUL, 0x7fa3dcdcUL, + 0x66442222UL, 0x7e542a2aUL, 0xab3b9090UL, 0x830b8888UL, + 0xca8c4646UL, 0x29c7eeeeUL, 0xd36bb8b8UL, 0x3c281414UL, + 0x79a7dedeUL, 0xe2bc5e5eUL, 0x1d160b0bUL, 0x76addbdbUL, + 0x3bdbe0e0UL, 0x56643232UL, 0x4e743a3aUL, 0x1e140a0aUL, + 0xdb924949UL, 0x0a0c0606UL, 0x6c482424UL, 0xe4b85c5cUL, + 0x5d9fc2c2UL, 0x6ebdd3d3UL, 0xef43acacUL, 0xa6c46262UL, + 0xa8399191UL, 0xa4319595UL, 0x37d3e4e4UL, 0x8bf27979UL, + 0x32d5e7e7UL, 0x438bc8c8UL, 0x596e3737UL, 0xb7da6d6dUL, + 0x8c018d8dUL, 0x64b1d5d5UL, 0xd29c4e4eUL, 0xe049a9a9UL, + 0xb4d86c6cUL, 0xfaac5656UL, 0x07f3f4f4UL, 0x25cfeaeaUL, + 0xafca6565UL, 0x8ef47a7aUL, 0xe947aeaeUL, 0x18100808UL, + 0xd56fbabaUL, 0x88f07878UL, 0x6f4a2525UL, 0x725c2e2eUL, + 0x24381c1cUL, 0xf157a6a6UL, 0xc773b4b4UL, 0x5197c6c6UL, + 0x23cbe8e8UL, 0x7ca1ddddUL, 0x9ce87474UL, 0x213e1f1fUL, + 0xdd964b4bUL, 0xdc61bdbdUL, 0x860d8b8bUL, 0x850f8a8aUL, + 0x90e07070UL, 0x427c3e3eUL, 0xc471b5b5UL, 0xaacc6666UL, + 0xd8904848UL, 0x05060303UL, 0x01f7f6f6UL, 0x121c0e0eUL, + 0xa3c26161UL, 0x5f6a3535UL, 0xf9ae5757UL, 0xd069b9b9UL, + 0x91178686UL, 0x5899c1c1UL, 0x273a1d1dUL, 0xb9279e9eUL, + 0x38d9e1e1UL, 0x13ebf8f8UL, 0xb32b9898UL, 0x33221111UL, + 0xbbd26969UL, 0x70a9d9d9UL, 0x89078e8eUL, 0xa7339494UL, + 0xb62d9b9bUL, 0x223c1e1eUL, 0x92158787UL, 0x20c9e9e9UL, + 0x4987ceceUL, 0xffaa5555UL, 0x78502828UL, 0x7aa5dfdfUL, + 0x8f038c8cUL, 0xf859a1a1UL, 0x80098989UL, 0x171a0d0dUL, + 0xda65bfbfUL, 0x31d7e6e6UL, 0xc6844242UL, 0xb8d06868UL, + 0xc3824141UL, 0xb0299999UL, 0x775a2d2dUL, 0x111e0f0fUL, + 0xcb7bb0b0UL, 0xfca85454UL, 0xd66dbbbbUL, 0x3a2c1616UL, +}; +static const unsigned int Te2[256] = +{ + 0x63a5c663UL, 0x7c84f87cUL, 0x7799ee77UL, 0x7b8df67bUL, + 0xf20dfff2UL, 0x6bbdd66bUL, 0x6fb1de6fUL, 0xc55491c5UL, + 0x30506030UL, 0x01030201UL, 0x67a9ce67UL, 0x2b7d562bUL, + 0xfe19e7feUL, 0xd762b5d7UL, 0xabe64dabUL, 0x769aec76UL, + 0xca458fcaUL, 0x829d1f82UL, 0xc94089c9UL, 0x7d87fa7dUL, + 0xfa15effaUL, 0x59ebb259UL, 0x47c98e47UL, 0xf00bfbf0UL, + 0xadec41adUL, 0xd467b3d4UL, 0xa2fd5fa2UL, 0xafea45afUL, + 0x9cbf239cUL, 0xa4f753a4UL, 0x7296e472UL, 0xc05b9bc0UL, + 0xb7c275b7UL, 0xfd1ce1fdUL, 0x93ae3d93UL, 0x266a4c26UL, + 0x365a6c36UL, 0x3f417e3fUL, 0xf702f5f7UL, 0xcc4f83ccUL, + 0x345c6834UL, 0xa5f451a5UL, 0xe534d1e5UL, 0xf108f9f1UL, + 0x7193e271UL, 0xd873abd8UL, 0x31536231UL, 0x153f2a15UL, + 0x040c0804UL, 0xc75295c7UL, 0x23654623UL, 0xc35e9dc3UL, + 0x18283018UL, 0x96a13796UL, 0x050f0a05UL, 0x9ab52f9aUL, + 0x07090e07UL, 0x12362412UL, 0x809b1b80UL, 0xe23ddfe2UL, + 0xeb26cdebUL, 0x27694e27UL, 0xb2cd7fb2UL, 0x759fea75UL, + 0x091b1209UL, 0x839e1d83UL, 0x2c74582cUL, 0x1a2e341aUL, + 0x1b2d361bUL, 0x6eb2dc6eUL, 0x5aeeb45aUL, 0xa0fb5ba0UL, + 0x52f6a452UL, 0x3b4d763bUL, 0xd661b7d6UL, 0xb3ce7db3UL, + 0x297b5229UL, 0xe33edde3UL, 0x2f715e2fUL, 0x84971384UL, + 0x53f5a653UL, 0xd168b9d1UL, 0x00000000UL, 0xed2cc1edUL, + 0x20604020UL, 0xfc1fe3fcUL, 0xb1c879b1UL, 0x5bedb65bUL, + 0x6abed46aUL, 0xcb468dcbUL, 0xbed967beUL, 0x394b7239UL, + 0x4ade944aUL, 0x4cd4984cUL, 0x58e8b058UL, 0xcf4a85cfUL, + 0xd06bbbd0UL, 0xef2ac5efUL, 0xaae54faaUL, 0xfb16edfbUL, + 0x43c58643UL, 0x4dd79a4dUL, 0x33556633UL, 0x85941185UL, + 0x45cf8a45UL, 0xf910e9f9UL, 0x02060402UL, 0x7f81fe7fUL, + 0x50f0a050UL, 0x3c44783cUL, 0x9fba259fUL, 0xa8e34ba8UL, + 0x51f3a251UL, 0xa3fe5da3UL, 0x40c08040UL, 0x8f8a058fUL, + 0x92ad3f92UL, 0x9dbc219dUL, 0x38487038UL, 0xf504f1f5UL, + 0xbcdf63bcUL, 0xb6c177b6UL, 0xda75afdaUL, 0x21634221UL, + 0x10302010UL, 0xff1ae5ffUL, 0xf30efdf3UL, 0xd26dbfd2UL, + 0xcd4c81cdUL, 0x0c14180cUL, 0x13352613UL, 0xec2fc3ecUL, + 0x5fe1be5fUL, 0x97a23597UL, 0x44cc8844UL, 0x17392e17UL, + 0xc45793c4UL, 0xa7f255a7UL, 0x7e82fc7eUL, 0x3d477a3dUL, + 0x64acc864UL, 0x5de7ba5dUL, 0x192b3219UL, 0x7395e673UL, + 0x60a0c060UL, 0x81981981UL, 0x4fd19e4fUL, 0xdc7fa3dcUL, + 0x22664422UL, 0x2a7e542aUL, 0x90ab3b90UL, 0x88830b88UL, + 0x46ca8c46UL, 0xee29c7eeUL, 0xb8d36bb8UL, 0x143c2814UL, + 0xde79a7deUL, 0x5ee2bc5eUL, 0x0b1d160bUL, 0xdb76addbUL, + 0xe03bdbe0UL, 0x32566432UL, 0x3a4e743aUL, 0x0a1e140aUL, + 0x49db9249UL, 0x060a0c06UL, 0x246c4824UL, 0x5ce4b85cUL, + 0xc25d9fc2UL, 0xd36ebdd3UL, 0xacef43acUL, 0x62a6c462UL, + 0x91a83991UL, 0x95a43195UL, 0xe437d3e4UL, 0x798bf279UL, + 0xe732d5e7UL, 0xc8438bc8UL, 0x37596e37UL, 0x6db7da6dUL, + 0x8d8c018dUL, 0xd564b1d5UL, 0x4ed29c4eUL, 0xa9e049a9UL, + 0x6cb4d86cUL, 0x56faac56UL, 0xf407f3f4UL, 0xea25cfeaUL, + 0x65afca65UL, 0x7a8ef47aUL, 0xaee947aeUL, 0x08181008UL, + 0xbad56fbaUL, 0x7888f078UL, 0x256f4a25UL, 0x2e725c2eUL, + 0x1c24381cUL, 0xa6f157a6UL, 0xb4c773b4UL, 0xc65197c6UL, + 0xe823cbe8UL, 0xdd7ca1ddUL, 0x749ce874UL, 0x1f213e1fUL, + 0x4bdd964bUL, 0xbddc61bdUL, 0x8b860d8bUL, 0x8a850f8aUL, + 0x7090e070UL, 0x3e427c3eUL, 0xb5c471b5UL, 0x66aacc66UL, + 0x48d89048UL, 0x03050603UL, 0xf601f7f6UL, 0x0e121c0eUL, + 0x61a3c261UL, 0x355f6a35UL, 0x57f9ae57UL, 0xb9d069b9UL, + 0x86911786UL, 0xc15899c1UL, 0x1d273a1dUL, 0x9eb9279eUL, + 0xe138d9e1UL, 0xf813ebf8UL, 0x98b32b98UL, 0x11332211UL, + 0x69bbd269UL, 0xd970a9d9UL, 0x8e89078eUL, 0x94a73394UL, + 0x9bb62d9bUL, 0x1e223c1eUL, 0x87921587UL, 0xe920c9e9UL, + 0xce4987ceUL, 0x55ffaa55UL, 0x28785028UL, 0xdf7aa5dfUL, + 0x8c8f038cUL, 0xa1f859a1UL, 0x89800989UL, 0x0d171a0dUL, + 0xbfda65bfUL, 0xe631d7e6UL, 0x42c68442UL, 0x68b8d068UL, + 0x41c38241UL, 0x99b02999UL, 0x2d775a2dUL, 0x0f111e0fUL, + 0xb0cb7bb0UL, 0x54fca854UL, 0xbbd66dbbUL, 0x163a2c16UL, +}; +static const unsigned int Te3[256] = +{ + 0x6363a5c6UL, 0x7c7c84f8UL, 0x777799eeUL, 0x7b7b8df6UL, + 0xf2f20dffUL, 0x6b6bbdd6UL, 0x6f6fb1deUL, 0xc5c55491UL, + 0x30305060UL, 0x01010302UL, 0x6767a9ceUL, 0x2b2b7d56UL, + 0xfefe19e7UL, 0xd7d762b5UL, 0xababe64dUL, 0x76769aecUL, + 0xcaca458fUL, 0x82829d1fUL, 0xc9c94089UL, 0x7d7d87faUL, + 0xfafa15efUL, 0x5959ebb2UL, 0x4747c98eUL, 0xf0f00bfbUL, + 0xadadec41UL, 0xd4d467b3UL, 0xa2a2fd5fUL, 0xafafea45UL, + 0x9c9cbf23UL, 0xa4a4f753UL, 0x727296e4UL, 0xc0c05b9bUL, + 0xb7b7c275UL, 0xfdfd1ce1UL, 0x9393ae3dUL, 0x26266a4cUL, + 0x36365a6cUL, 0x3f3f417eUL, 0xf7f702f5UL, 0xcccc4f83UL, + 0x34345c68UL, 0xa5a5f451UL, 0xe5e534d1UL, 0xf1f108f9UL, + 0x717193e2UL, 0xd8d873abUL, 0x31315362UL, 0x15153f2aUL, + 0x04040c08UL, 0xc7c75295UL, 0x23236546UL, 0xc3c35e9dUL, + 0x18182830UL, 0x9696a137UL, 0x05050f0aUL, 0x9a9ab52fUL, + 0x0707090eUL, 0x12123624UL, 0x80809b1bUL, 0xe2e23ddfUL, + 0xebeb26cdUL, 0x2727694eUL, 0xb2b2cd7fUL, 0x75759feaUL, + 0x09091b12UL, 0x83839e1dUL, 0x2c2c7458UL, 0x1a1a2e34UL, + 0x1b1b2d36UL, 0x6e6eb2dcUL, 0x5a5aeeb4UL, 0xa0a0fb5bUL, + 0x5252f6a4UL, 0x3b3b4d76UL, 0xd6d661b7UL, 0xb3b3ce7dUL, + 0x29297b52UL, 0xe3e33eddUL, 0x2f2f715eUL, 0x84849713UL, + 0x5353f5a6UL, 0xd1d168b9UL, 0x00000000UL, 0xeded2cc1UL, + 0x20206040UL, 0xfcfc1fe3UL, 0xb1b1c879UL, 0x5b5bedb6UL, + 0x6a6abed4UL, 0xcbcb468dUL, 0xbebed967UL, 0x39394b72UL, + 0x4a4ade94UL, 0x4c4cd498UL, 0x5858e8b0UL, 0xcfcf4a85UL, + 0xd0d06bbbUL, 0xefef2ac5UL, 0xaaaae54fUL, 0xfbfb16edUL, + 0x4343c586UL, 0x4d4dd79aUL, 0x33335566UL, 0x85859411UL, + 0x4545cf8aUL, 0xf9f910e9UL, 0x02020604UL, 0x7f7f81feUL, + 0x5050f0a0UL, 0x3c3c4478UL, 0x9f9fba25UL, 0xa8a8e34bUL, + 0x5151f3a2UL, 0xa3a3fe5dUL, 0x4040c080UL, 0x8f8f8a05UL, + 0x9292ad3fUL, 0x9d9dbc21UL, 0x38384870UL, 0xf5f504f1UL, + 0xbcbcdf63UL, 0xb6b6c177UL, 0xdada75afUL, 0x21216342UL, + 0x10103020UL, 0xffff1ae5UL, 0xf3f30efdUL, 0xd2d26dbfUL, + 0xcdcd4c81UL, 0x0c0c1418UL, 0x13133526UL, 0xecec2fc3UL, + 0x5f5fe1beUL, 0x9797a235UL, 0x4444cc88UL, 0x1717392eUL, + 0xc4c45793UL, 0xa7a7f255UL, 0x7e7e82fcUL, 0x3d3d477aUL, + 0x6464acc8UL, 0x5d5de7baUL, 0x19192b32UL, 0x737395e6UL, + 0x6060a0c0UL, 0x81819819UL, 0x4f4fd19eUL, 0xdcdc7fa3UL, + 0x22226644UL, 0x2a2a7e54UL, 0x9090ab3bUL, 0x8888830bUL, + 0x4646ca8cUL, 0xeeee29c7UL, 0xb8b8d36bUL, 0x14143c28UL, + 0xdede79a7UL, 0x5e5ee2bcUL, 0x0b0b1d16UL, 0xdbdb76adUL, + 0xe0e03bdbUL, 0x32325664UL, 0x3a3a4e74UL, 0x0a0a1e14UL, + 0x4949db92UL, 0x06060a0cUL, 0x24246c48UL, 0x5c5ce4b8UL, + 0xc2c25d9fUL, 0xd3d36ebdUL, 0xacacef43UL, 0x6262a6c4UL, + 0x9191a839UL, 0x9595a431UL, 0xe4e437d3UL, 0x79798bf2UL, + 0xe7e732d5UL, 0xc8c8438bUL, 0x3737596eUL, 0x6d6db7daUL, + 0x8d8d8c01UL, 0xd5d564b1UL, 0x4e4ed29cUL, 0xa9a9e049UL, + 0x6c6cb4d8UL, 0x5656faacUL, 0xf4f407f3UL, 0xeaea25cfUL, + 0x6565afcaUL, 0x7a7a8ef4UL, 0xaeaee947UL, 0x08081810UL, + 0xbabad56fUL, 0x787888f0UL, 0x25256f4aUL, 0x2e2e725cUL, + 0x1c1c2438UL, 0xa6a6f157UL, 0xb4b4c773UL, 0xc6c65197UL, + 0xe8e823cbUL, 0xdddd7ca1UL, 0x74749ce8UL, 0x1f1f213eUL, + 0x4b4bdd96UL, 0xbdbddc61UL, 0x8b8b860dUL, 0x8a8a850fUL, + 0x707090e0UL, 0x3e3e427cUL, 0xb5b5c471UL, 0x6666aaccUL, + 0x4848d890UL, 0x03030506UL, 0xf6f601f7UL, 0x0e0e121cUL, + 0x6161a3c2UL, 0x35355f6aUL, 0x5757f9aeUL, 0xb9b9d069UL, + 0x86869117UL, 0xc1c15899UL, 0x1d1d273aUL, 0x9e9eb927UL, + 0xe1e138d9UL, 0xf8f813ebUL, 0x9898b32bUL, 0x11113322UL, + 0x6969bbd2UL, 0xd9d970a9UL, 0x8e8e8907UL, 0x9494a733UL, + 0x9b9bb62dUL, 0x1e1e223cUL, 0x87879215UL, 0xe9e920c9UL, + 0xcece4987UL, 0x5555ffaaUL, 0x28287850UL, 0xdfdf7aa5UL, + 0x8c8c8f03UL, 0xa1a1f859UL, 0x89898009UL, 0x0d0d171aUL, + 0xbfbfda65UL, 0xe6e631d7UL, 0x4242c684UL, 0x6868b8d0UL, + 0x4141c382UL, 0x9999b029UL, 0x2d2d775aUL, 0x0f0f111eUL, + 0xb0b0cb7bUL, 0x5454fca8UL, 0xbbbbd66dUL, 0x16163a2cUL, +}; +static const unsigned int Te4[256] = +{ + 0x63636363UL, 0x7c7c7c7cUL, 0x77777777UL, 0x7b7b7b7bUL, + 0xf2f2f2f2UL, 0x6b6b6b6bUL, 0x6f6f6f6fUL, 0xc5c5c5c5UL, + 0x30303030UL, 0x01010101UL, 0x67676767UL, 0x2b2b2b2bUL, + 0xfefefefeUL, 0xd7d7d7d7UL, 0xababababUL, 0x76767676UL, + 0xcacacacaUL, 0x82828282UL, 0xc9c9c9c9UL, 0x7d7d7d7dUL, + 0xfafafafaUL, 0x59595959UL, 0x47474747UL, 0xf0f0f0f0UL, + 0xadadadadUL, 0xd4d4d4d4UL, 0xa2a2a2a2UL, 0xafafafafUL, + 0x9c9c9c9cUL, 0xa4a4a4a4UL, 0x72727272UL, 0xc0c0c0c0UL, + 0xb7b7b7b7UL, 0xfdfdfdfdUL, 0x93939393UL, 0x26262626UL, + 0x36363636UL, 0x3f3f3f3fUL, 0xf7f7f7f7UL, 0xccccccccUL, + 0x34343434UL, 0xa5a5a5a5UL, 0xe5e5e5e5UL, 0xf1f1f1f1UL, + 0x71717171UL, 0xd8d8d8d8UL, 0x31313131UL, 0x15151515UL, + 0x04040404UL, 0xc7c7c7c7UL, 0x23232323UL, 0xc3c3c3c3UL, + 0x18181818UL, 0x96969696UL, 0x05050505UL, 0x9a9a9a9aUL, + 0x07070707UL, 0x12121212UL, 0x80808080UL, 0xe2e2e2e2UL, + 0xebebebebUL, 0x27272727UL, 0xb2b2b2b2UL, 0x75757575UL, + 0x09090909UL, 0x83838383UL, 0x2c2c2c2cUL, 0x1a1a1a1aUL, + 0x1b1b1b1bUL, 0x6e6e6e6eUL, 0x5a5a5a5aUL, 0xa0a0a0a0UL, + 0x52525252UL, 0x3b3b3b3bUL, 0xd6d6d6d6UL, 0xb3b3b3b3UL, + 0x29292929UL, 0xe3e3e3e3UL, 0x2f2f2f2fUL, 0x84848484UL, + 0x53535353UL, 0xd1d1d1d1UL, 0x00000000UL, 0xededededUL, + 0x20202020UL, 0xfcfcfcfcUL, 0xb1b1b1b1UL, 0x5b5b5b5bUL, + 0x6a6a6a6aUL, 0xcbcbcbcbUL, 0xbebebebeUL, 0x39393939UL, + 0x4a4a4a4aUL, 0x4c4c4c4cUL, 0x58585858UL, 0xcfcfcfcfUL, + 0xd0d0d0d0UL, 0xefefefefUL, 0xaaaaaaaaUL, 0xfbfbfbfbUL, + 0x43434343UL, 0x4d4d4d4dUL, 0x33333333UL, 0x85858585UL, + 0x45454545UL, 0xf9f9f9f9UL, 0x02020202UL, 0x7f7f7f7fUL, + 0x50505050UL, 0x3c3c3c3cUL, 0x9f9f9f9fUL, 0xa8a8a8a8UL, + 0x51515151UL, 0xa3a3a3a3UL, 0x40404040UL, 0x8f8f8f8fUL, + 0x92929292UL, 0x9d9d9d9dUL, 0x38383838UL, 0xf5f5f5f5UL, + 0xbcbcbcbcUL, 0xb6b6b6b6UL, 0xdadadadaUL, 0x21212121UL, + 0x10101010UL, 0xffffffffUL, 0xf3f3f3f3UL, 0xd2d2d2d2UL, + 0xcdcdcdcdUL, 0x0c0c0c0cUL, 0x13131313UL, 0xececececUL, + 0x5f5f5f5fUL, 0x97979797UL, 0x44444444UL, 0x17171717UL, + 0xc4c4c4c4UL, 0xa7a7a7a7UL, 0x7e7e7e7eUL, 0x3d3d3d3dUL, + 0x64646464UL, 0x5d5d5d5dUL, 0x19191919UL, 0x73737373UL, + 0x60606060UL, 0x81818181UL, 0x4f4f4f4fUL, 0xdcdcdcdcUL, + 0x22222222UL, 0x2a2a2a2aUL, 0x90909090UL, 0x88888888UL, + 0x46464646UL, 0xeeeeeeeeUL, 0xb8b8b8b8UL, 0x14141414UL, + 0xdedededeUL, 0x5e5e5e5eUL, 0x0b0b0b0bUL, 0xdbdbdbdbUL, + 0xe0e0e0e0UL, 0x32323232UL, 0x3a3a3a3aUL, 0x0a0a0a0aUL, + 0x49494949UL, 0x06060606UL, 0x24242424UL, 0x5c5c5c5cUL, + 0xc2c2c2c2UL, 0xd3d3d3d3UL, 0xacacacacUL, 0x62626262UL, + 0x91919191UL, 0x95959595UL, 0xe4e4e4e4UL, 0x79797979UL, + 0xe7e7e7e7UL, 0xc8c8c8c8UL, 0x37373737UL, 0x6d6d6d6dUL, + 0x8d8d8d8dUL, 0xd5d5d5d5UL, 0x4e4e4e4eUL, 0xa9a9a9a9UL, + 0x6c6c6c6cUL, 0x56565656UL, 0xf4f4f4f4UL, 0xeaeaeaeaUL, + 0x65656565UL, 0x7a7a7a7aUL, 0xaeaeaeaeUL, 0x08080808UL, + 0xbabababaUL, 0x78787878UL, 0x25252525UL, 0x2e2e2e2eUL, + 0x1c1c1c1cUL, 0xa6a6a6a6UL, 0xb4b4b4b4UL, 0xc6c6c6c6UL, + 0xe8e8e8e8UL, 0xddddddddUL, 0x74747474UL, 0x1f1f1f1fUL, + 0x4b4b4b4bUL, 0xbdbdbdbdUL, 0x8b8b8b8bUL, 0x8a8a8a8aUL, + 0x70707070UL, 0x3e3e3e3eUL, 0xb5b5b5b5UL, 0x66666666UL, + 0x48484848UL, 0x03030303UL, 0xf6f6f6f6UL, 0x0e0e0e0eUL, + 0x61616161UL, 0x35353535UL, 0x57575757UL, 0xb9b9b9b9UL, + 0x86868686UL, 0xc1c1c1c1UL, 0x1d1d1d1dUL, 0x9e9e9e9eUL, + 0xe1e1e1e1UL, 0xf8f8f8f8UL, 0x98989898UL, 0x11111111UL, + 0x69696969UL, 0xd9d9d9d9UL, 0x8e8e8e8eUL, 0x94949494UL, + 0x9b9b9b9bUL, 0x1e1e1e1eUL, 0x87878787UL, 0xe9e9e9e9UL, + 0xcecececeUL, 0x55555555UL, 0x28282828UL, 0xdfdfdfdfUL, + 0x8c8c8c8cUL, 0xa1a1a1a1UL, 0x89898989UL, 0x0d0d0d0dUL, + 0xbfbfbfbfUL, 0xe6e6e6e6UL, 0x42424242UL, 0x68686868UL, + 0x41414141UL, 0x99999999UL, 0x2d2d2d2dUL, 0x0f0f0f0fUL, + 0xb0b0b0b0UL, 0x54545454UL, 0xbbbbbbbbUL, 0x16161616UL, +}; +static const unsigned int Td0[256] = +{ + 0x51f4a750UL, 0x7e416553UL, 0x1a17a4c3UL, 0x3a275e96UL, + 0x3bab6bcbUL, 0x1f9d45f1UL, 0xacfa58abUL, 0x4be30393UL, + 0x2030fa55UL, 0xad766df6UL, 0x88cc7691UL, 0xf5024c25UL, + 0x4fe5d7fcUL, 0xc52acbd7UL, 0x26354480UL, 0xb562a38fUL, + 0xdeb15a49UL, 0x25ba1b67UL, 0x45ea0e98UL, 0x5dfec0e1UL, + 0xc32f7502UL, 0x814cf012UL, 0x8d4697a3UL, 0x6bd3f9c6UL, + 0x038f5fe7UL, 0x15929c95UL, 0xbf6d7aebUL, 0x955259daUL, + 0xd4be832dUL, 0x587421d3UL, 0x49e06929UL, 0x8ec9c844UL, + 0x75c2896aUL, 0xf48e7978UL, 0x99583e6bUL, 0x27b971ddUL, + 0xbee14fb6UL, 0xf088ad17UL, 0xc920ac66UL, 0x7dce3ab4UL, + 0x63df4a18UL, 0xe51a3182UL, 0x97513360UL, 0x62537f45UL, + 0xb16477e0UL, 0xbb6bae84UL, 0xfe81a01cUL, 0xf9082b94UL, + 0x70486858UL, 0x8f45fd19UL, 0x94de6c87UL, 0x527bf8b7UL, + 0xab73d323UL, 0x724b02e2UL, 0xe31f8f57UL, 0x6655ab2aUL, + 0xb2eb2807UL, 0x2fb5c203UL, 0x86c57b9aUL, 0xd33708a5UL, + 0x302887f2UL, 0x23bfa5b2UL, 0x02036abaUL, 0xed16825cUL, + 0x8acf1c2bUL, 0xa779b492UL, 0xf307f2f0UL, 0x4e69e2a1UL, + 0x65daf4cdUL, 0x0605bed5UL, 0xd134621fUL, 0xc4a6fe8aUL, + 0x342e539dUL, 0xa2f355a0UL, 0x058ae132UL, 0xa4f6eb75UL, + 0x0b83ec39UL, 0x4060efaaUL, 0x5e719f06UL, 0xbd6e1051UL, + 0x3e218af9UL, 0x96dd063dUL, 0xdd3e05aeUL, 0x4de6bd46UL, + 0x91548db5UL, 0x71c45d05UL, 0x0406d46fUL, 0x605015ffUL, + 0x1998fb24UL, 0xd6bde997UL, 0x894043ccUL, 0x67d99e77UL, + 0xb0e842bdUL, 0x07898b88UL, 0xe7195b38UL, 0x79c8eedbUL, + 0xa17c0a47UL, 0x7c420fe9UL, 0xf8841ec9UL, 0x00000000UL, + 0x09808683UL, 0x322bed48UL, 0x1e1170acUL, 0x6c5a724eUL, + 0xfd0efffbUL, 0x0f853856UL, 0x3daed51eUL, 0x362d3927UL, + 0x0a0fd964UL, 0x685ca621UL, 0x9b5b54d1UL, 0x24362e3aUL, + 0x0c0a67b1UL, 0x9357e70fUL, 0xb4ee96d2UL, 0x1b9b919eUL, + 0x80c0c54fUL, 0x61dc20a2UL, 0x5a774b69UL, 0x1c121a16UL, + 0xe293ba0aUL, 0xc0a02ae5UL, 0x3c22e043UL, 0x121b171dUL, + 0x0e090d0bUL, 0xf28bc7adUL, 0x2db6a8b9UL, 0x141ea9c8UL, + 0x57f11985UL, 0xaf75074cUL, 0xee99ddbbUL, 0xa37f60fdUL, + 0xf701269fUL, 0x5c72f5bcUL, 0x44663bc5UL, 0x5bfb7e34UL, + 0x8b432976UL, 0xcb23c6dcUL, 0xb6edfc68UL, 0xb8e4f163UL, + 0xd731dccaUL, 0x42638510UL, 0x13972240UL, 0x84c61120UL, + 0x854a247dUL, 0xd2bb3df8UL, 0xaef93211UL, 0xc729a16dUL, + 0x1d9e2f4bUL, 0xdcb230f3UL, 0x0d8652ecUL, 0x77c1e3d0UL, + 0x2bb3166cUL, 0xa970b999UL, 0x119448faUL, 0x47e96422UL, + 0xa8fc8cc4UL, 0xa0f03f1aUL, 0x567d2cd8UL, 0x223390efUL, + 0x87494ec7UL, 0xd938d1c1UL, 0x8ccaa2feUL, 0x98d40b36UL, + 0xa6f581cfUL, 0xa57ade28UL, 0xdab78e26UL, 0x3fadbfa4UL, + 0x2c3a9de4UL, 0x5078920dUL, 0x6a5fcc9bUL, 0x547e4662UL, + 0xf68d13c2UL, 0x90d8b8e8UL, 0x2e39f75eUL, 0x82c3aff5UL, + 0x9f5d80beUL, 0x69d0937cUL, 0x6fd52da9UL, 0xcf2512b3UL, + 0xc8ac993bUL, 0x10187da7UL, 0xe89c636eUL, 0xdb3bbb7bUL, + 0xcd267809UL, 0x6e5918f4UL, 0xec9ab701UL, 0x834f9aa8UL, + 0xe6956e65UL, 0xaaffe67eUL, 0x21bccf08UL, 0xef15e8e6UL, + 0xbae79bd9UL, 0x4a6f36ceUL, 0xea9f09d4UL, 0x29b07cd6UL, + 0x31a4b2afUL, 0x2a3f2331UL, 0xc6a59430UL, 0x35a266c0UL, + 0x744ebc37UL, 0xfc82caa6UL, 0xe090d0b0UL, 0x33a7d815UL, + 0xf104984aUL, 0x41ecdaf7UL, 0x7fcd500eUL, 0x1791f62fUL, + 0x764dd68dUL, 0x43efb04dUL, 0xccaa4d54UL, 0xe49604dfUL, + 0x9ed1b5e3UL, 0x4c6a881bUL, 0xc12c1fb8UL, 0x4665517fUL, + 0x9d5eea04UL, 0x018c355dUL, 0xfa877473UL, 0xfb0b412eUL, + 0xb3671d5aUL, 0x92dbd252UL, 0xe9105633UL, 0x6dd64713UL, + 0x9ad7618cUL, 0x37a10c7aUL, 0x59f8148eUL, 0xeb133c89UL, + 0xcea927eeUL, 0xb761c935UL, 0xe11ce5edUL, 0x7a47b13cUL, + 0x9cd2df59UL, 0x55f2733fUL, 0x1814ce79UL, 0x73c737bfUL, + 0x53f7cdeaUL, 0x5ffdaa5bUL, 0xdf3d6f14UL, 0x7844db86UL, + 0xcaaff381UL, 0xb968c43eUL, 0x3824342cUL, 0xc2a3405fUL, + 0x161dc372UL, 0xbce2250cUL, 0x283c498bUL, 0xff0d9541UL, + 0x39a80171UL, 0x080cb3deUL, 0xd8b4e49cUL, 0x6456c190UL, + 0x7bcb8461UL, 0xd532b670UL, 0x486c5c74UL, 0xd0b85742UL, +}; +static const unsigned int Td1[256] = +{ + 0x5051f4a7UL, 0x537e4165UL, 0xc31a17a4UL, 0x963a275eUL, + 0xcb3bab6bUL, 0xf11f9d45UL, 0xabacfa58UL, 0x934be303UL, + 0x552030faUL, 0xf6ad766dUL, 0x9188cc76UL, 0x25f5024cUL, + 0xfc4fe5d7UL, 0xd7c52acbUL, 0x80263544UL, 0x8fb562a3UL, + 0x49deb15aUL, 0x6725ba1bUL, 0x9845ea0eUL, 0xe15dfec0UL, + 0x02c32f75UL, 0x12814cf0UL, 0xa38d4697UL, 0xc66bd3f9UL, + 0xe7038f5fUL, 0x9515929cUL, 0xebbf6d7aUL, 0xda955259UL, + 0x2dd4be83UL, 0xd3587421UL, 0x2949e069UL, 0x448ec9c8UL, + 0x6a75c289UL, 0x78f48e79UL, 0x6b99583eUL, 0xdd27b971UL, + 0xb6bee14fUL, 0x17f088adUL, 0x66c920acUL, 0xb47dce3aUL, + 0x1863df4aUL, 0x82e51a31UL, 0x60975133UL, 0x4562537fUL, + 0xe0b16477UL, 0x84bb6baeUL, 0x1cfe81a0UL, 0x94f9082bUL, + 0x58704868UL, 0x198f45fdUL, 0x8794de6cUL, 0xb7527bf8UL, + 0x23ab73d3UL, 0xe2724b02UL, 0x57e31f8fUL, 0x2a6655abUL, + 0x07b2eb28UL, 0x032fb5c2UL, 0x9a86c57bUL, 0xa5d33708UL, + 0xf2302887UL, 0xb223bfa5UL, 0xba02036aUL, 0x5ced1682UL, + 0x2b8acf1cUL, 0x92a779b4UL, 0xf0f307f2UL, 0xa14e69e2UL, + 0xcd65daf4UL, 0xd50605beUL, 0x1fd13462UL, 0x8ac4a6feUL, + 0x9d342e53UL, 0xa0a2f355UL, 0x32058ae1UL, 0x75a4f6ebUL, + 0x390b83ecUL, 0xaa4060efUL, 0x065e719fUL, 0x51bd6e10UL, + 0xf93e218aUL, 0x3d96dd06UL, 0xaedd3e05UL, 0x464de6bdUL, + 0xb591548dUL, 0x0571c45dUL, 0x6f0406d4UL, 0xff605015UL, + 0x241998fbUL, 0x97d6bde9UL, 0xcc894043UL, 0x7767d99eUL, + 0xbdb0e842UL, 0x8807898bUL, 0x38e7195bUL, 0xdb79c8eeUL, + 0x47a17c0aUL, 0xe97c420fUL, 0xc9f8841eUL, 0x00000000UL, + 0x83098086UL, 0x48322bedUL, 0xac1e1170UL, 0x4e6c5a72UL, + 0xfbfd0effUL, 0x560f8538UL, 0x1e3daed5UL, 0x27362d39UL, + 0x640a0fd9UL, 0x21685ca6UL, 0xd19b5b54UL, 0x3a24362eUL, + 0xb10c0a67UL, 0x0f9357e7UL, 0xd2b4ee96UL, 0x9e1b9b91UL, + 0x4f80c0c5UL, 0xa261dc20UL, 0x695a774bUL, 0x161c121aUL, + 0x0ae293baUL, 0xe5c0a02aUL, 0x433c22e0UL, 0x1d121b17UL, + 0x0b0e090dUL, 0xadf28bc7UL, 0xb92db6a8UL, 0xc8141ea9UL, + 0x8557f119UL, 0x4caf7507UL, 0xbbee99ddUL, 0xfda37f60UL, + 0x9ff70126UL, 0xbc5c72f5UL, 0xc544663bUL, 0x345bfb7eUL, + 0x768b4329UL, 0xdccb23c6UL, 0x68b6edfcUL, 0x63b8e4f1UL, + 0xcad731dcUL, 0x10426385UL, 0x40139722UL, 0x2084c611UL, + 0x7d854a24UL, 0xf8d2bb3dUL, 0x11aef932UL, 0x6dc729a1UL, + 0x4b1d9e2fUL, 0xf3dcb230UL, 0xec0d8652UL, 0xd077c1e3UL, + 0x6c2bb316UL, 0x99a970b9UL, 0xfa119448UL, 0x2247e964UL, + 0xc4a8fc8cUL, 0x1aa0f03fUL, 0xd8567d2cUL, 0xef223390UL, + 0xc787494eUL, 0xc1d938d1UL, 0xfe8ccaa2UL, 0x3698d40bUL, + 0xcfa6f581UL, 0x28a57adeUL, 0x26dab78eUL, 0xa43fadbfUL, + 0xe42c3a9dUL, 0x0d507892UL, 0x9b6a5fccUL, 0x62547e46UL, + 0xc2f68d13UL, 0xe890d8b8UL, 0x5e2e39f7UL, 0xf582c3afUL, + 0xbe9f5d80UL, 0x7c69d093UL, 0xa96fd52dUL, 0xb3cf2512UL, + 0x3bc8ac99UL, 0xa710187dUL, 0x6ee89c63UL, 0x7bdb3bbbUL, + 0x09cd2678UL, 0xf46e5918UL, 0x01ec9ab7UL, 0xa8834f9aUL, + 0x65e6956eUL, 0x7eaaffe6UL, 0x0821bccfUL, 0xe6ef15e8UL, + 0xd9bae79bUL, 0xce4a6f36UL, 0xd4ea9f09UL, 0xd629b07cUL, + 0xaf31a4b2UL, 0x312a3f23UL, 0x30c6a594UL, 0xc035a266UL, + 0x37744ebcUL, 0xa6fc82caUL, 0xb0e090d0UL, 0x1533a7d8UL, + 0x4af10498UL, 0xf741ecdaUL, 0x0e7fcd50UL, 0x2f1791f6UL, + 0x8d764dd6UL, 0x4d43efb0UL, 0x54ccaa4dUL, 0xdfe49604UL, + 0xe39ed1b5UL, 0x1b4c6a88UL, 0xb8c12c1fUL, 0x7f466551UL, + 0x049d5eeaUL, 0x5d018c35UL, 0x73fa8774UL, 0x2efb0b41UL, + 0x5ab3671dUL, 0x5292dbd2UL, 0x33e91056UL, 0x136dd647UL, + 0x8c9ad761UL, 0x7a37a10cUL, 0x8e59f814UL, 0x89eb133cUL, + 0xeecea927UL, 0x35b761c9UL, 0xede11ce5UL, 0x3c7a47b1UL, + 0x599cd2dfUL, 0x3f55f273UL, 0x791814ceUL, 0xbf73c737UL, + 0xea53f7cdUL, 0x5b5ffdaaUL, 0x14df3d6fUL, 0x867844dbUL, + 0x81caaff3UL, 0x3eb968c4UL, 0x2c382434UL, 0x5fc2a340UL, + 0x72161dc3UL, 0x0cbce225UL, 0x8b283c49UL, 0x41ff0d95UL, + 0x7139a801UL, 0xde080cb3UL, 0x9cd8b4e4UL, 0x906456c1UL, + 0x617bcb84UL, 0x70d532b6UL, 0x74486c5cUL, 0x42d0b857UL, +}; +static const unsigned int Td2[256] = +{ + 0xa75051f4UL, 0x65537e41UL, 0xa4c31a17UL, 0x5e963a27UL, + 0x6bcb3babUL, 0x45f11f9dUL, 0x58abacfaUL, 0x03934be3UL, + 0xfa552030UL, 0x6df6ad76UL, 0x769188ccUL, 0x4c25f502UL, + 0xd7fc4fe5UL, 0xcbd7c52aUL, 0x44802635UL, 0xa38fb562UL, + 0x5a49deb1UL, 0x1b6725baUL, 0x0e9845eaUL, 0xc0e15dfeUL, + 0x7502c32fUL, 0xf012814cUL, 0x97a38d46UL, 0xf9c66bd3UL, + 0x5fe7038fUL, 0x9c951592UL, 0x7aebbf6dUL, 0x59da9552UL, + 0x832dd4beUL, 0x21d35874UL, 0x692949e0UL, 0xc8448ec9UL, + 0x896a75c2UL, 0x7978f48eUL, 0x3e6b9958UL, 0x71dd27b9UL, + 0x4fb6bee1UL, 0xad17f088UL, 0xac66c920UL, 0x3ab47dceUL, + 0x4a1863dfUL, 0x3182e51aUL, 0x33609751UL, 0x7f456253UL, + 0x77e0b164UL, 0xae84bb6bUL, 0xa01cfe81UL, 0x2b94f908UL, + 0x68587048UL, 0xfd198f45UL, 0x6c8794deUL, 0xf8b7527bUL, + 0xd323ab73UL, 0x02e2724bUL, 0x8f57e31fUL, 0xab2a6655UL, + 0x2807b2ebUL, 0xc2032fb5UL, 0x7b9a86c5UL, 0x08a5d337UL, + 0x87f23028UL, 0xa5b223bfUL, 0x6aba0203UL, 0x825ced16UL, + 0x1c2b8acfUL, 0xb492a779UL, 0xf2f0f307UL, 0xe2a14e69UL, + 0xf4cd65daUL, 0xbed50605UL, 0x621fd134UL, 0xfe8ac4a6UL, + 0x539d342eUL, 0x55a0a2f3UL, 0xe132058aUL, 0xeb75a4f6UL, + 0xec390b83UL, 0xefaa4060UL, 0x9f065e71UL, 0x1051bd6eUL, + 0x8af93e21UL, 0x063d96ddUL, 0x05aedd3eUL, 0xbd464de6UL, + 0x8db59154UL, 0x5d0571c4UL, 0xd46f0406UL, 0x15ff6050UL, + 0xfb241998UL, 0xe997d6bdUL, 0x43cc8940UL, 0x9e7767d9UL, + 0x42bdb0e8UL, 0x8b880789UL, 0x5b38e719UL, 0xeedb79c8UL, + 0x0a47a17cUL, 0x0fe97c42UL, 0x1ec9f884UL, 0x00000000UL, + 0x86830980UL, 0xed48322bUL, 0x70ac1e11UL, 0x724e6c5aUL, + 0xfffbfd0eUL, 0x38560f85UL, 0xd51e3daeUL, 0x3927362dUL, + 0xd9640a0fUL, 0xa621685cUL, 0x54d19b5bUL, 0x2e3a2436UL, + 0x67b10c0aUL, 0xe70f9357UL, 0x96d2b4eeUL, 0x919e1b9bUL, + 0xc54f80c0UL, 0x20a261dcUL, 0x4b695a77UL, 0x1a161c12UL, + 0xba0ae293UL, 0x2ae5c0a0UL, 0xe0433c22UL, 0x171d121bUL, + 0x0d0b0e09UL, 0xc7adf28bUL, 0xa8b92db6UL, 0xa9c8141eUL, + 0x198557f1UL, 0x074caf75UL, 0xddbbee99UL, 0x60fda37fUL, + 0x269ff701UL, 0xf5bc5c72UL, 0x3bc54466UL, 0x7e345bfbUL, + 0x29768b43UL, 0xc6dccb23UL, 0xfc68b6edUL, 0xf163b8e4UL, + 0xdccad731UL, 0x85104263UL, 0x22401397UL, 0x112084c6UL, + 0x247d854aUL, 0x3df8d2bbUL, 0x3211aef9UL, 0xa16dc729UL, + 0x2f4b1d9eUL, 0x30f3dcb2UL, 0x52ec0d86UL, 0xe3d077c1UL, + 0x166c2bb3UL, 0xb999a970UL, 0x48fa1194UL, 0x642247e9UL, + 0x8cc4a8fcUL, 0x3f1aa0f0UL, 0x2cd8567dUL, 0x90ef2233UL, + 0x4ec78749UL, 0xd1c1d938UL, 0xa2fe8ccaUL, 0x0b3698d4UL, + 0x81cfa6f5UL, 0xde28a57aUL, 0x8e26dab7UL, 0xbfa43fadUL, + 0x9de42c3aUL, 0x920d5078UL, 0xcc9b6a5fUL, 0x4662547eUL, + 0x13c2f68dUL, 0xb8e890d8UL, 0xf75e2e39UL, 0xaff582c3UL, + 0x80be9f5dUL, 0x937c69d0UL, 0x2da96fd5UL, 0x12b3cf25UL, + 0x993bc8acUL, 0x7da71018UL, 0x636ee89cUL, 0xbb7bdb3bUL, + 0x7809cd26UL, 0x18f46e59UL, 0xb701ec9aUL, 0x9aa8834fUL, + 0x6e65e695UL, 0xe67eaaffUL, 0xcf0821bcUL, 0xe8e6ef15UL, + 0x9bd9bae7UL, 0x36ce4a6fUL, 0x09d4ea9fUL, 0x7cd629b0UL, + 0xb2af31a4UL, 0x23312a3fUL, 0x9430c6a5UL, 0x66c035a2UL, + 0xbc37744eUL, 0xcaa6fc82UL, 0xd0b0e090UL, 0xd81533a7UL, + 0x984af104UL, 0xdaf741ecUL, 0x500e7fcdUL, 0xf62f1791UL, + 0xd68d764dUL, 0xb04d43efUL, 0x4d54ccaaUL, 0x04dfe496UL, + 0xb5e39ed1UL, 0x881b4c6aUL, 0x1fb8c12cUL, 0x517f4665UL, + 0xea049d5eUL, 0x355d018cUL, 0x7473fa87UL, 0x412efb0bUL, + 0x1d5ab367UL, 0xd25292dbUL, 0x5633e910UL, 0x47136dd6UL, + 0x618c9ad7UL, 0x0c7a37a1UL, 0x148e59f8UL, 0x3c89eb13UL, + 0x27eecea9UL, 0xc935b761UL, 0xe5ede11cUL, 0xb13c7a47UL, + 0xdf599cd2UL, 0x733f55f2UL, 0xce791814UL, 0x37bf73c7UL, + 0xcdea53f7UL, 0xaa5b5ffdUL, 0x6f14df3dUL, 0xdb867844UL, + 0xf381caafUL, 0xc43eb968UL, 0x342c3824UL, 0x405fc2a3UL, + 0xc372161dUL, 0x250cbce2UL, 0x498b283cUL, 0x9541ff0dUL, + 0x017139a8UL, 0xb3de080cUL, 0xe49cd8b4UL, 0xc1906456UL, + 0x84617bcbUL, 0xb670d532UL, 0x5c74486cUL, 0x5742d0b8UL, +}; +static const unsigned int Td3[256] = +{ + 0xf4a75051UL, 0x4165537eUL, 0x17a4c31aUL, 0x275e963aUL, + 0xab6bcb3bUL, 0x9d45f11fUL, 0xfa58abacUL, 0xe303934bUL, + 0x30fa5520UL, 0x766df6adUL, 0xcc769188UL, 0x024c25f5UL, + 0xe5d7fc4fUL, 0x2acbd7c5UL, 0x35448026UL, 0x62a38fb5UL, + 0xb15a49deUL, 0xba1b6725UL, 0xea0e9845UL, 0xfec0e15dUL, + 0x2f7502c3UL, 0x4cf01281UL, 0x4697a38dUL, 0xd3f9c66bUL, + 0x8f5fe703UL, 0x929c9515UL, 0x6d7aebbfUL, 0x5259da95UL, + 0xbe832dd4UL, 0x7421d358UL, 0xe0692949UL, 0xc9c8448eUL, + 0xc2896a75UL, 0x8e7978f4UL, 0x583e6b99UL, 0xb971dd27UL, + 0xe14fb6beUL, 0x88ad17f0UL, 0x20ac66c9UL, 0xce3ab47dUL, + 0xdf4a1863UL, 0x1a3182e5UL, 0x51336097UL, 0x537f4562UL, + 0x6477e0b1UL, 0x6bae84bbUL, 0x81a01cfeUL, 0x082b94f9UL, + 0x48685870UL, 0x45fd198fUL, 0xde6c8794UL, 0x7bf8b752UL, + 0x73d323abUL, 0x4b02e272UL, 0x1f8f57e3UL, 0x55ab2a66UL, + 0xeb2807b2UL, 0xb5c2032fUL, 0xc57b9a86UL, 0x3708a5d3UL, + 0x2887f230UL, 0xbfa5b223UL, 0x036aba02UL, 0x16825cedUL, + 0xcf1c2b8aUL, 0x79b492a7UL, 0x07f2f0f3UL, 0x69e2a14eUL, + 0xdaf4cd65UL, 0x05bed506UL, 0x34621fd1UL, 0xa6fe8ac4UL, + 0x2e539d34UL, 0xf355a0a2UL, 0x8ae13205UL, 0xf6eb75a4UL, + 0x83ec390bUL, 0x60efaa40UL, 0x719f065eUL, 0x6e1051bdUL, + 0x218af93eUL, 0xdd063d96UL, 0x3e05aeddUL, 0xe6bd464dUL, + 0x548db591UL, 0xc45d0571UL, 0x06d46f04UL, 0x5015ff60UL, + 0x98fb2419UL, 0xbde997d6UL, 0x4043cc89UL, 0xd99e7767UL, + 0xe842bdb0UL, 0x898b8807UL, 0x195b38e7UL, 0xc8eedb79UL, + 0x7c0a47a1UL, 0x420fe97cUL, 0x841ec9f8UL, 0x00000000UL, + 0x80868309UL, 0x2bed4832UL, 0x1170ac1eUL, 0x5a724e6cUL, + 0x0efffbfdUL, 0x8538560fUL, 0xaed51e3dUL, 0x2d392736UL, + 0x0fd9640aUL, 0x5ca62168UL, 0x5b54d19bUL, 0x362e3a24UL, + 0x0a67b10cUL, 0x57e70f93UL, 0xee96d2b4UL, 0x9b919e1bUL, + 0xc0c54f80UL, 0xdc20a261UL, 0x774b695aUL, 0x121a161cUL, + 0x93ba0ae2UL, 0xa02ae5c0UL, 0x22e0433cUL, 0x1b171d12UL, + 0x090d0b0eUL, 0x8bc7adf2UL, 0xb6a8b92dUL, 0x1ea9c814UL, + 0xf1198557UL, 0x75074cafUL, 0x99ddbbeeUL, 0x7f60fda3UL, + 0x01269ff7UL, 0x72f5bc5cUL, 0x663bc544UL, 0xfb7e345bUL, + 0x4329768bUL, 0x23c6dccbUL, 0xedfc68b6UL, 0xe4f163b8UL, + 0x31dccad7UL, 0x63851042UL, 0x97224013UL, 0xc6112084UL, + 0x4a247d85UL, 0xbb3df8d2UL, 0xf93211aeUL, 0x29a16dc7UL, + 0x9e2f4b1dUL, 0xb230f3dcUL, 0x8652ec0dUL, 0xc1e3d077UL, + 0xb3166c2bUL, 0x70b999a9UL, 0x9448fa11UL, 0xe9642247UL, + 0xfc8cc4a8UL, 0xf03f1aa0UL, 0x7d2cd856UL, 0x3390ef22UL, + 0x494ec787UL, 0x38d1c1d9UL, 0xcaa2fe8cUL, 0xd40b3698UL, + 0xf581cfa6UL, 0x7ade28a5UL, 0xb78e26daUL, 0xadbfa43fUL, + 0x3a9de42cUL, 0x78920d50UL, 0x5fcc9b6aUL, 0x7e466254UL, + 0x8d13c2f6UL, 0xd8b8e890UL, 0x39f75e2eUL, 0xc3aff582UL, + 0x5d80be9fUL, 0xd0937c69UL, 0xd52da96fUL, 0x2512b3cfUL, + 0xac993bc8UL, 0x187da710UL, 0x9c636ee8UL, 0x3bbb7bdbUL, + 0x267809cdUL, 0x5918f46eUL, 0x9ab701ecUL, 0x4f9aa883UL, + 0x956e65e6UL, 0xffe67eaaUL, 0xbccf0821UL, 0x15e8e6efUL, + 0xe79bd9baUL, 0x6f36ce4aUL, 0x9f09d4eaUL, 0xb07cd629UL, + 0xa4b2af31UL, 0x3f23312aUL, 0xa59430c6UL, 0xa266c035UL, + 0x4ebc3774UL, 0x82caa6fcUL, 0x90d0b0e0UL, 0xa7d81533UL, + 0x04984af1UL, 0xecdaf741UL, 0xcd500e7fUL, 0x91f62f17UL, + 0x4dd68d76UL, 0xefb04d43UL, 0xaa4d54ccUL, 0x9604dfe4UL, + 0xd1b5e39eUL, 0x6a881b4cUL, 0x2c1fb8c1UL, 0x65517f46UL, + 0x5eea049dUL, 0x8c355d01UL, 0x877473faUL, 0x0b412efbUL, + 0x671d5ab3UL, 0xdbd25292UL, 0x105633e9UL, 0xd647136dUL, + 0xd7618c9aUL, 0xa10c7a37UL, 0xf8148e59UL, 0x133c89ebUL, + 0xa927eeceUL, 0x61c935b7UL, 0x1ce5ede1UL, 0x47b13c7aUL, + 0xd2df599cUL, 0xf2733f55UL, 0x14ce7918UL, 0xc737bf73UL, + 0xf7cdea53UL, 0xfdaa5b5fUL, 0x3d6f14dfUL, 0x44db8678UL, + 0xaff381caUL, 0x68c43eb9UL, 0x24342c38UL, 0xa3405fc2UL, + 0x1dc37216UL, 0xe2250cbcUL, 0x3c498b28UL, 0x0d9541ffUL, + 0xa8017139UL, 0x0cb3de08UL, 0xb4e49cd8UL, 0x56c19064UL, + 0xcb84617bUL, 0x32b670d5UL, 0x6c5c7448UL, 0xb85742d0UL, +}; +static const unsigned int Td4[256] = +{ + 0x52525252UL, 0x09090909UL, 0x6a6a6a6aUL, 0xd5d5d5d5UL, + 0x30303030UL, 0x36363636UL, 0xa5a5a5a5UL, 0x38383838UL, + 0xbfbfbfbfUL, 0x40404040UL, 0xa3a3a3a3UL, 0x9e9e9e9eUL, + 0x81818181UL, 0xf3f3f3f3UL, 0xd7d7d7d7UL, 0xfbfbfbfbUL, + 0x7c7c7c7cUL, 0xe3e3e3e3UL, 0x39393939UL, 0x82828282UL, + 0x9b9b9b9bUL, 0x2f2f2f2fUL, 0xffffffffUL, 0x87878787UL, + 0x34343434UL, 0x8e8e8e8eUL, 0x43434343UL, 0x44444444UL, + 0xc4c4c4c4UL, 0xdedededeUL, 0xe9e9e9e9UL, 0xcbcbcbcbUL, + 0x54545454UL, 0x7b7b7b7bUL, 0x94949494UL, 0x32323232UL, + 0xa6a6a6a6UL, 0xc2c2c2c2UL, 0x23232323UL, 0x3d3d3d3dUL, + 0xeeeeeeeeUL, 0x4c4c4c4cUL, 0x95959595UL, 0x0b0b0b0bUL, + 0x42424242UL, 0xfafafafaUL, 0xc3c3c3c3UL, 0x4e4e4e4eUL, + 0x08080808UL, 0x2e2e2e2eUL, 0xa1a1a1a1UL, 0x66666666UL, + 0x28282828UL, 0xd9d9d9d9UL, 0x24242424UL, 0xb2b2b2b2UL, + 0x76767676UL, 0x5b5b5b5bUL, 0xa2a2a2a2UL, 0x49494949UL, + 0x6d6d6d6dUL, 0x8b8b8b8bUL, 0xd1d1d1d1UL, 0x25252525UL, + 0x72727272UL, 0xf8f8f8f8UL, 0xf6f6f6f6UL, 0x64646464UL, + 0x86868686UL, 0x68686868UL, 0x98989898UL, 0x16161616UL, + 0xd4d4d4d4UL, 0xa4a4a4a4UL, 0x5c5c5c5cUL, 0xccccccccUL, + 0x5d5d5d5dUL, 0x65656565UL, 0xb6b6b6b6UL, 0x92929292UL, + 0x6c6c6c6cUL, 0x70707070UL, 0x48484848UL, 0x50505050UL, + 0xfdfdfdfdUL, 0xededededUL, 0xb9b9b9b9UL, 0xdadadadaUL, + 0x5e5e5e5eUL, 0x15151515UL, 0x46464646UL, 0x57575757UL, + 0xa7a7a7a7UL, 0x8d8d8d8dUL, 0x9d9d9d9dUL, 0x84848484UL, + 0x90909090UL, 0xd8d8d8d8UL, 0xababababUL, 0x00000000UL, + 0x8c8c8c8cUL, 0xbcbcbcbcUL, 0xd3d3d3d3UL, 0x0a0a0a0aUL, + 0xf7f7f7f7UL, 0xe4e4e4e4UL, 0x58585858UL, 0x05050505UL, + 0xb8b8b8b8UL, 0xb3b3b3b3UL, 0x45454545UL, 0x06060606UL, + 0xd0d0d0d0UL, 0x2c2c2c2cUL, 0x1e1e1e1eUL, 0x8f8f8f8fUL, + 0xcacacacaUL, 0x3f3f3f3fUL, 0x0f0f0f0fUL, 0x02020202UL, + 0xc1c1c1c1UL, 0xafafafafUL, 0xbdbdbdbdUL, 0x03030303UL, + 0x01010101UL, 0x13131313UL, 0x8a8a8a8aUL, 0x6b6b6b6bUL, + 0x3a3a3a3aUL, 0x91919191UL, 0x11111111UL, 0x41414141UL, + 0x4f4f4f4fUL, 0x67676767UL, 0xdcdcdcdcUL, 0xeaeaeaeaUL, + 0x97979797UL, 0xf2f2f2f2UL, 0xcfcfcfcfUL, 0xcecececeUL, + 0xf0f0f0f0UL, 0xb4b4b4b4UL, 0xe6e6e6e6UL, 0x73737373UL, + 0x96969696UL, 0xacacacacUL, 0x74747474UL, 0x22222222UL, + 0xe7e7e7e7UL, 0xadadadadUL, 0x35353535UL, 0x85858585UL, + 0xe2e2e2e2UL, 0xf9f9f9f9UL, 0x37373737UL, 0xe8e8e8e8UL, + 0x1c1c1c1cUL, 0x75757575UL, 0xdfdfdfdfUL, 0x6e6e6e6eUL, + 0x47474747UL, 0xf1f1f1f1UL, 0x1a1a1a1aUL, 0x71717171UL, + 0x1d1d1d1dUL, 0x29292929UL, 0xc5c5c5c5UL, 0x89898989UL, + 0x6f6f6f6fUL, 0xb7b7b7b7UL, 0x62626262UL, 0x0e0e0e0eUL, + 0xaaaaaaaaUL, 0x18181818UL, 0xbebebebeUL, 0x1b1b1b1bUL, + 0xfcfcfcfcUL, 0x56565656UL, 0x3e3e3e3eUL, 0x4b4b4b4bUL, + 0xc6c6c6c6UL, 0xd2d2d2d2UL, 0x79797979UL, 0x20202020UL, + 0x9a9a9a9aUL, 0xdbdbdbdbUL, 0xc0c0c0c0UL, 0xfefefefeUL, + 0x78787878UL, 0xcdcdcdcdUL, 0x5a5a5a5aUL, 0xf4f4f4f4UL, + 0x1f1f1f1fUL, 0xddddddddUL, 0xa8a8a8a8UL, 0x33333333UL, + 0x88888888UL, 0x07070707UL, 0xc7c7c7c7UL, 0x31313131UL, + 0xb1b1b1b1UL, 0x12121212UL, 0x10101010UL, 0x59595959UL, + 0x27272727UL, 0x80808080UL, 0xececececUL, 0x5f5f5f5fUL, + 0x60606060UL, 0x51515151UL, 0x7f7f7f7fUL, 0xa9a9a9a9UL, + 0x19191919UL, 0xb5b5b5b5UL, 0x4a4a4a4aUL, 0x0d0d0d0dUL, + 0x2d2d2d2dUL, 0xe5e5e5e5UL, 0x7a7a7a7aUL, 0x9f9f9f9fUL, + 0x93939393UL, 0xc9c9c9c9UL, 0x9c9c9c9cUL, 0xefefefefUL, + 0xa0a0a0a0UL, 0xe0e0e0e0UL, 0x3b3b3b3bUL, 0x4d4d4d4dUL, + 0xaeaeaeaeUL, 0x2a2a2a2aUL, 0xf5f5f5f5UL, 0xb0b0b0b0UL, + 0xc8c8c8c8UL, 0xebebebebUL, 0xbbbbbbbbUL, 0x3c3c3c3cUL, + 0x83838383UL, 0x53535353UL, 0x99999999UL, 0x61616161UL, + 0x17171717UL, 0x2b2b2b2bUL, 0x04040404UL, 0x7e7e7e7eUL, + 0xbabababaUL, 0x77777777UL, 0xd6d6d6d6UL, 0x26262626UL, + 0xe1e1e1e1UL, 0x69696969UL, 0x14141414UL, 0x63636363UL, + 0x55555555UL, 0x21212121UL, 0x0c0c0c0cUL, 0x7d7d7d7dUL, +}; +static const unsigned int rcon[] = +{ + 0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, + 0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL, + 0x1B000000UL, 0x36000000UL, +}; +#endif + +#define GETU32(pt) (((unsigned int)(pt)[0] << 24) ^ \ + ((unsigned int)(pt)[1] << 16) ^ \ + ((unsigned int)(pt)[2] << 8) ^ \ + ((unsigned int)(pt)[3])) + +#define PUTU32(ct, st) { (ct)[0] = (unsigned char)((st) >> 24); \ + (ct)[1] = (unsigned char)((st) >> 16); \ + (ct)[2] = (unsigned char)((st) >> 8); \ + (ct)[3] = (unsigned char)(st); } + +/* + * Expand the cipher key into the encryption key schedule and return the + * number of rounds for the given cipher key size. + */ +int aes_setkey_enc(unsigned int rk[], const unsigned char cipherKey[], int keyBytes) +{ + int i = 0; + unsigned int temp; + + rk[0] = GETU32(cipherKey ); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + if (keyBytes == 16) // 128 bits + { + for (;;) + { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) + { + return 10; + } + rk += 4; + } + } + rk[4] = GETU32(cipherKey + 16); + rk[5] = GETU32(cipherKey + 20); + if (keyBytes == 24) // 192 bits + { + for (;;) + { + temp = rk[ 5]; + rk[ 6] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) + { + return 12; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(cipherKey + 24); + rk[7] = GETU32(cipherKey + 28); + if (keyBytes == 32) // 256 bits + { + for (;;) + { + temp = rk[ 7]; + rk[ 8] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) + { + return 14; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ + (Te4[(temp >> 24) ] & 0xff000000) ^ + (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(temp ) & 0xff] & 0x000000ff); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + + rk += 8; + } + } + return 0; +} + +/* + * Expand the cipher key into encryption and decryption key schedule and + * return the number of rounds for the given cipher key size. + */ +int AesGenKeySched(unsigned int rk[], unsigned int rrk[], const unsigned char cipherKey[], int keyBytes) +{ + int Nr, i; + + // expand the cipher key + Nr = aes_setkey_enc(rk, cipherKey, keyBytes); + // invert the order of the first round keys + rrk += Nr * 4; + rrk[0] = rk[0]; + rrk[1] = rk[1]; + rrk[2] = rk[2]; + rrk[3] = rk[3]; + + /* + * apply the inverse MixColumn transform to all round keys but the first + * and the last + */ + for (i = 1; i < Nr; i++) + { + rrk -= 4; + rk += 4; + rrk[0] = + Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0] ) & 0xff] & 0xff]; + rrk[1] = + Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1] ) & 0xff] & 0xff]; + rrk[2] = + Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2] ) & 0xff] & 0xff]; + rrk[3] = + Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3] ) & 0xff] & 0xff]; + } + // invert the order of the last round keys + rrk -= 4; + rk += 4; + rrk[0] = rk[0]; + rrk[1] = rk[1]; + rrk[2] = rk[2]; + rrk[3] = rk[3]; + + return Nr; +} + +/* + * Encrypt the plain text into cipher + */ +void AesEncBlk(AesCtx *pCtx, const unsigned char pt[], unsigned char ct[]) +{ + unsigned int s0, s1, s2, s3, t0, t1, t2, t3, *iv; + const unsigned int *rk; + int r; + + rk = pCtx->Ek; + iv = pCtx->Iv; + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(pt ) ^ rk[0]; + s1 = GETU32(pt + 4) ^ rk[1]; + s2 = GETU32(pt + 8) ^ rk[2]; + s3 = GETU32(pt + 12) ^ rk[3]; + if (pCtx->Mode) + { + s0 = s0 ^ iv[0]; + s1 = s1 ^ iv[1]; + s2 = s2 ^ iv[2]; + s3 = s3 ^ iv[3]; + } + /* + * Nr - 1 full rounds: + */ + r = pCtx->Nr >> 1; + for (;;) + { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) + { + break; + } + + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24) ] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(ct, s0); + s1 = + (Te4[(t1 >> 24) ] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(ct + 4, s1); + s2 = + (Te4[(t2 >> 24) ] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(ct + 8, s2); + s3 = + (Te4[(t3 >> 24) ] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(ct + 12, s3); + + if (pCtx->Mode) + { + iv[0] = s0; + iv[1] = s1; + iv[2] = s2; + iv[3] = s3; + } +} + +/* + * Decrypt the cipher into plain text + */ +void AesDecBlk(AesCtx *pCtx, const unsigned char ct[], unsigned char pt[]) +{ + unsigned int s0, s1, s2, s3, t0, t1, t2, t3, v0, v1, v2, v3, *iv; + const unsigned int *rk; + int r; + + rk = pCtx->Dk; + iv = pCtx->Iv; + /* + * map byte array block to cipher state + * and add initial round key: + */ + v0 = GETU32(ct ); + s0 = v0 ^ rk[0]; + v1 = GETU32(ct + 4); + s1 = v1 ^ rk[1]; + v2 = GETU32(ct + 8); + s2 = v2 ^ rk[2]; + v3 = GETU32(ct + 12); + s3 = v3 ^ rk[3]; + /* + * Nr - 1 full rounds: + */ + r = pCtx->Nr >> 1; + for (;;) + { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) + { + break; + } + + s0 = + Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]; + } + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) ] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[0]; + s1 = + (Td4[(t1 >> 24) ] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[1]; + s2 = + (Td4[(t2 >> 24) ] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[2]; + s3 = + (Td4[(t3 >> 24) ] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[3]; + + if (pCtx->Mode) + { + s0 = s0 ^ iv[0]; + iv[0] = v0; + s1 = s1 ^ iv[1]; + iv[1] = v1; + s2 = s2 ^ iv[2]; + iv[2] = v2; + s3 = s3 ^ iv[3]; + iv[3] = v3; + } + + PUTU32(pt, s0); + PUTU32(pt + 4, s1); + PUTU32(pt + 8, s2); + PUTU32(pt + 12, s3); +} + +////////////////////////////////////////////////////////////////////////////// +// API functions // +////////////////////////////////////////////////////////////////////////////// + +/* + * initialize AES context + */ +int AesCtxIni(AesCtx *pCtx, unsigned char *pIV, unsigned char *pKey, unsigned int KeyLen, unsigned char Mode) +{ + if (pKey == 0 || pCtx == 0 || (KeyLen != KEY128 && KeyLen != KEY192 && KeyLen != KEY256)) + return -1; + + // generate key schedule + pCtx->Nr = AesGenKeySched(pCtx->Ek, pCtx->Dk, pKey, KeyLen); + + // initialize IV + if (pIV != 0) + { + pCtx->Iv[0] = GETU32(pIV ); + pCtx->Iv[1] = GETU32(pIV + 4 ); + pCtx->Iv[2] = GETU32(pIV + 8 ); + pCtx->Iv[3] = GETU32(pIV + 12); + } + + // mode + pCtx->Mode = Mode; + + return 0; +} + +/* + * Encrypt plain text + */ +int AesEncrypt(AesCtx *pCtx, unsigned char *pData, unsigned char *pCipher, unsigned int DataLen) +{ + unsigned int i; + + if (pData == 0 || pCipher == 0 || pCtx == 0 || (DataLen & 0xf) != 0) + return -1; + + for (i = 0; i < DataLen; i += BLOCKSZ) + { + // encrypt block by block + AesEncBlk(pCtx, pData, pCipher); + pCipher += BLOCKSZ; + pData += BLOCKSZ; + } + return DataLen; +} + +/* + * Decrypt cipher + */ +int AesDecrypt(AesCtx *pCtx, unsigned char *pCipher, unsigned char *pData, unsigned int CipherLen) +{ + unsigned int i; + + if (pData == 0 || pCipher == 0 || pCtx == 0 || (CipherLen & 0xf) != 0) + return -1; + + for (i = 0; i < CipherLen; i += BLOCKSZ) + { + // decrypt block by block + AesDecBlk(pCtx, pCipher, pData); + pCipher += BLOCKSZ; + pData += BLOCKSZ; + } + return CipherLen; +} diff --git a/cscrypt/fast_aes.h b/cscrypt/fast_aes.h new file mode 100644 index 0000000..3179fd4 --- /dev/null +++ b/cscrypt/fast_aes.h @@ -0,0 +1,32 @@ +/* + * AES Cryptographic Algorithm Header File. Include this header file in + * your source which uses these given APIs. (This source is kept under + * public domain) + */ + +// AES context structure +typedef struct +{ + unsigned int Ek[60]; + unsigned int Dk[60]; + unsigned int Iv[4]; + unsigned char Nr; + unsigned char Mode; +} AesCtx; + +// key length in bytes +#define KEY128 16 +#define KEY192 24 +#define KEY256 32 +// block size in bytes +#define BLOCKSZ 16 +// mode +#define EBC 0 +#define CBC 1 + +// AES API function prototype + +int AesCtxIni(AesCtx *pCtx, unsigned char *pIV, unsigned char *pKey, unsigned int KeyLen, unsigned char Mode); +int AesEncrypt(AesCtx *pCtx, unsigned char *pData, unsigned char *pCipher, unsigned int DataLen); +int AesDecrypt(AesCtx *pCtx, unsigned char *pCipher, unsigned char *pData, unsigned int CipherLen); + diff --git a/cscrypt/i_cbc.c b/cscrypt/i_cbc.c new file mode 100644 index 0000000..c60ffe0 --- /dev/null +++ b/cscrypt/i_cbc.c @@ -0,0 +1,177 @@ +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/idea/i_cbc.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#include "idea.h" +#include "idea_lcl.h" + +void idea_cbc_encrypt(const unsigned char *in, unsigned char *out, long length, + IDEA_KEY_SCHEDULE *ks, unsigned char *iv, int encrypt) +{ + register unsigned long tin0=0, tin1=0; + register unsigned long tout0=0, tout1=0, xor0=0, xor1=0; + register long l = length; + unsigned long tin[2]; + + if(encrypt) + { + n2l(iv, tout0); + n2l(iv, tout1); + iv -= 8; + for(l -= 8; l >= 0; l -= 8) + { + n2l(in, tin0); + n2l(in, tin1); + tin0 ^= tout0; + tin1 ^= tout1; + tin[0] = tin0; + tin[1] = tin1; + idea_encrypt(tin, ks); + tout0 = tin[0]; + l2n(tout0, out); + tout1 = tin[1]; + l2n(tout1, out); + } + if(l != -8) + { + n2ln(in, tin0, tin1, l + 8); + tin0 ^= tout0; + tin1 ^= tout1; + tin[0] = tin0; + tin[1] = tin1; + idea_encrypt(tin, ks); + tout0 = tin[0]; + l2n(tout0, out); + tout1 = tin[1]; + l2n(tout1, out); + } + l2n(tout0, iv); + l2n(tout1, iv); + } + else + { + n2l(iv, xor0); + n2l(iv, xor1); + iv -= 8; + for(l -= 8; l >= 0; l -= 8) + { + n2l(in, tin0); + tin[0] = tin0; + n2l(in, tin1); + tin[1] = tin1; + idea_encrypt(tin, ks); + tout0 = tin[0] ^ xor0; + tout1 = tin[1] ^ xor1; + l2n(tout0, out); + l2n(tout1, out); + xor0 = tin0; + xor1 = tin1; + } + if(l != -8) + { + n2l(in, tin0); + tin[0] = tin0; + n2l(in, tin1); + tin[1] = tin1; + idea_encrypt(tin, ks); + tout0 = tin[0] ^ xor0; + tout1 = tin[1] ^ xor1; + l2nn(tout0, tout1, out, l + 8); + xor0 = tin0; + xor1 = tin1; + } + l2n(xor0, iv); + l2n(xor1, iv); + } + tin[0] = tin[1] = 0; +} + +void idea_encrypt(unsigned long *d, IDEA_KEY_SCHEDULE *key) +{ + register IDEA_INT *p; + register unsigned long x1, x2, x3, x4, t0, t1, ul; + + x2 = d[0]; + x1 = (x2 >> 16); + x4 = d[1]; + x3 = (x4 >> 16); + + p = &(key->data[0][0]); + + E_IDEA(0); + E_IDEA(1); + E_IDEA(2); + E_IDEA(3); + E_IDEA(4); + E_IDEA(5); + E_IDEA(6); + E_IDEA(7); + + x1 &= 0xffff; + idea_mul(x1, x1, *p, ul); + p++; + + t0 = x3 + *(p++); + t1 = x2 + *(p++); + + x4 &= 0xffff; + idea_mul(x4, x4, *p, ul); + + d[0] = (t0 & 0xffff) | ((x1 & 0xffff) << 16); + d[1] = (x4 & 0xffff) | ((t1 & 0xffff) << 16); +} diff --git a/cscrypt/i_ecb.c b/cscrypt/i_ecb.c new file mode 100644 index 0000000..56f838c --- /dev/null +++ b/cscrypt/i_ecb.c @@ -0,0 +1,89 @@ +/* crypto/idea/i_ecb.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#include "idea.h" +#include "idea_lcl.h" + +const char *idea_options(void) +{ + if(sizeof(short) != sizeof(IDEA_INT)) + { + return ("idea(int)"); + } + else + { + return ("idea(short)"); + } +} + +void idea_ecb_encrypt(const unsigned char *in, unsigned char *out, + IDEA_KEY_SCHEDULE *ks) +{ + unsigned long l0 = 0, l1 = 0, d[2]; + + n2l(in, l0); + d[0] = l0; + n2l(in, l1); + d[1] = l1; + idea_encrypt(d, ks); + l0 = d[0]; + l2n(l0, out); + l1 = d[1]; + l2n(l1, out); + d[0] = d[1] = 0; +} diff --git a/cscrypt/i_skey.c b/cscrypt/i_skey.c new file mode 100644 index 0000000..3ceeabf --- /dev/null +++ b/cscrypt/i_skey.c @@ -0,0 +1,167 @@ +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/idea/i_skey.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#include "idea.h" +#include "idea_lcl.h" + +static IDEA_INT inverse(unsigned int xin); +void idea_set_encrypt_key(const unsigned char *key, IDEA_KEY_SCHEDULE *ks) +{ + int i; + register IDEA_INT *kt, *kf, r0, r1, r2; + + kt = &(ks->data[0][0]); + n2s(key, kt[0]); + n2s(key, kt[1]); + n2s(key, kt[2]); + n2s(key, kt[3]); + n2s(key, kt[4]); + n2s(key, kt[5]); + n2s(key, kt[6]); + n2s(key, kt[7]); + + kf = kt; + kt += 8; + for(i = 0; i < 6; i++) + { + r2 = kf[1]; + r1 = kf[2]; + *(kt++) = ((r2 << 9) | (r1 >> 7)) & 0xffff; + r0 = kf[3]; + *(kt++) = ((r1 << 9) | (r0 >> 7)) & 0xffff; + r1 = kf[4]; + *(kt++) = ((r0 << 9) | (r1 >> 7)) & 0xffff; + r0 = kf[5]; + *(kt++) = ((r1 << 9) | (r0 >> 7)) & 0xffff; + r1 = kf[6]; + *(kt++) = ((r0 << 9) | (r1 >> 7)) & 0xffff; + r0 = kf[7]; + *(kt++) = ((r1 << 9) | (r0 >> 7)) & 0xffff; + r1 = kf[0]; + if(i >= 5) { break; } + *(kt++) = ((r0 << 9) | (r1 >> 7)) & 0xffff; + *(kt++) = ((r1 << 9) | (r2 >> 7)) & 0xffff; + kf += 8; + } +} + +void idea_set_decrypt_key(IDEA_KEY_SCHEDULE *ek, IDEA_KEY_SCHEDULE *dk) +{ + int r; + register IDEA_INT *fp, *tp, t; + + tp = &(dk->data[0][0]); + fp = &(ek->data[8][0]); + for(r = 0; r < 9; r++) + { + *(tp++) = inverse(fp[0]); + *(tp++) = ((int)(0x10000L - fp[2]) & 0xffff); + *(tp++) = ((int)(0x10000L - fp[1]) & 0xffff); + *(tp++) = inverse(fp[3]); + if(r == 8) { break; } + fp -= 6; + *(tp++) = fp[4]; + *(tp++) = fp[5]; + } + + tp = &(dk->data[0][0]); + t = tp[1]; + tp[1] = tp[2]; + tp[2] = t; + + t = tp[49]; + tp[49] = tp[50]; + tp[50] = t; +} + +/* taken directly from the 'paper' I'll have a look at it later */ +static IDEA_INT inverse(unsigned int xin) +{ + long n1, n2, q, r, b1, b2, t; + + if(xin == 0) + { b2 = 0; } + else + { + n1 = 0x10001; + n2 = xin; + b2 = 1; + b1 = 0; + + do + { + r = (n1 % n2); + q = (n1 - r) / n2; + if(r == 0) + { + if(b2 < 0) { b2 = 0x10001 + b2; } + } + else + { + n1 = n2; + n2 = r; + t = b2; + b2 = b1 - q * b2; + b1 = t; + } + } + while(r != 0); + } + return ((IDEA_INT)b2); +} diff --git a/cscrypt/idea.h b/cscrypt/idea.h new file mode 100644 index 0000000..03f245f --- /dev/null +++ b/cscrypt/idea.h @@ -0,0 +1,103 @@ +/* crypto/idea/idea.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#ifndef HEADER_IDEA_H +#define HEADER_IDEA_H + +/*#include */ /* IDEA_INT, OPENSSL_NO_IDEA */ +#define IDEA_INT unsigned int + +/* +#ifdef OPENSSL_NO_IDEA +#error IDEA is disabled. +#endif +*/ + +#define IDEA_ENCRYPT 1 +#define IDEA_DECRYPT 0 + +#define IDEA_BLOCK 8 +#define IDEA_KEY_LENGTH 16 + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct idea_key_st + { + IDEA_INT data[9][6]; + } IDEA_KEY_SCHEDULE; + + const char *idea_options(void); + void idea_ecb_encrypt(const unsigned char *in, unsigned char *out, + IDEA_KEY_SCHEDULE *ks); + void idea_set_encrypt_key(const unsigned char *key, IDEA_KEY_SCHEDULE *ks); + void idea_set_decrypt_key(IDEA_KEY_SCHEDULE *ek, IDEA_KEY_SCHEDULE *dk); + void idea_cbc_encrypt(const unsigned char *in, unsigned char *out, + long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv, int enc); + void idea_cfb64_encrypt(const unsigned char *in, unsigned char *out, + long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv, + int *num, int enc); + void idea_ofb64_encrypt(const unsigned char *in, unsigned char *out, + long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv, int *num); + void idea_encrypt(unsigned long *in, IDEA_KEY_SCHEDULE *ks); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cscrypt/idea_lcl.h b/cscrypt/idea_lcl.h new file mode 100644 index 0000000..97a386a --- /dev/null +++ b/cscrypt/idea_lcl.h @@ -0,0 +1,215 @@ +/* crypto/idea/idea_lcl.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +/* The new form of this macro (check if the a*b == 0) was suggested by + * Colin Plumb */ +/* Removal of the inner if from from Wei Dai 24/4/96 */ +#define idea_mul(r,a,b,ul) \ + ul=(unsigned long)a*b; \ + if (ul != 0) \ + { \ + r=(ul&0xffff)-(ul>>16); \ + r-=((r)>>16); \ + } \ + else \ + r=(-(int)a-b+1); /* assuming a or b is 0 and in range */ \ + +#ifdef undef +#define idea_mul(r,a,b,ul,sl) \ + if (a == 0) r=(0x10001-b)&0xffff; \ + else if (b == 0) r=(0x10001-a)&0xffff; \ + else { \ + ul=(unsigned long)a*b; \ + sl=(ul&0xffff)-(ul>>16); \ + if (sl <= 0) sl+=0x10001; \ + r=sl; \ + } +#endif + +/* 7/12/95 - Many thanks to Rhys Weatherley + * for pointing out that I was assuming little endian + * byte order for all quantities what idea + * actually used bigendian. No where in the spec does it mention + * this, it is all in terms of 16 bit numbers and even the example + * does not use byte streams for the input example :-(. + * If you byte swap each pair of input, keys and iv, the functions + * would produce the output as the old version :-(. + */ + +/* NOTE - c is not incremented as per n2l */ +#define n2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2 =((unsigned long)(*(--(c)))) ; /* fallthrough */ \ + case 7: l2|=((unsigned long)(*(--(c))))<< 8; /* fallthrough */ \ + case 6: l2|=((unsigned long)(*(--(c))))<<16; /* fallthrough */ \ + case 5: l2|=((unsigned long)(*(--(c))))<<24; /* fallthrough */ \ + case 4: l1 =((unsigned long)(*(--(c)))) ; /* fallthrough */ \ + case 3: l1|=((unsigned long)(*(--(c))))<< 8; /* fallthrough */ \ + case 2: l1|=((unsigned long)(*(--(c))))<<16; /* fallthrough */ \ + case 1: l1|=((unsigned long)(*(--(c))))<<24; /* fallthrough */ \ + } \ + } + +/* NOTE - c is not incremented as per l2n */ +#define l2nn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2) )&0xff); /* fallthrough */ \ + case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); /* fallthrough */ \ + case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); /* fallthrough */ \ + case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); /* fallthrough */ \ + case 4: *(--(c))=(unsigned char)(((l1) )&0xff); /* fallthrough */ \ + case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); /* fallthrough */ \ + case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); /* fallthrough */ \ + case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \ + } \ + } + +#undef n2l +#define n2l(c,l) (l =((unsigned long)(*((c)++)))<<24L, \ + l|=((unsigned long)(*((c)++)))<<16L, \ + l|=((unsigned long)(*((c)++)))<< 8L, \ + l|=((unsigned long)(*((c)++)))) + +#undef l2n +#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff)) + +#undef s2n +#define s2n(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff)) + +#undef n2s +#define n2s(c,l) (l =((IDEA_INT)(*((c)++)))<< 8L, \ + l|=((IDEA_INT)(*((c)++))) ) + +#ifdef undef +/* NOTE - c is not incremented as per c2l */ +#define c2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2 =((unsigned long)(*(--(c))))<<24; /* fallthrough */ \ + case 7: l2|=((unsigned long)(*(--(c))))<<16; /* fallthrough */ \ + case 6: l2|=((unsigned long)(*(--(c))))<< 8; /* fallthrough */ \ + case 5: l2|=((unsigned long)(*(--(c)))); /* fallthrough */ \ + case 4: l1 =((unsigned long)(*(--(c))))<<24; /* fallthrough */ \ + case 3: l1|=((unsigned long)(*(--(c))))<<16; /* fallthrough */ \ + case 2: l1|=((unsigned long)(*(--(c))))<< 8; /* fallthrough */ \ + case 1: l1|=((unsigned long)(*(--(c)))); /* fallthrough */ \ + } \ + } + +/* NOTE - c is not incremented as per l2c */ +#define l2cn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2)>>24)&0xff); /* fallthrough */ \ + case 7: *(--(c))=(unsigned char)(((l2)>>16)&0xff); /* fallthrough */ \ + case 6: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); /* fallthrough */ \ + case 5: *(--(c))=(unsigned char)(((l2) )&0xff); /* fallthrough */ \ + case 4: *(--(c))=(unsigned char)(((l1)>>24)&0xff); /* fallthrough */ \ + case 3: *(--(c))=(unsigned char)(((l1)>>16)&0xff); /* fallthrough */ \ + case 2: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); /* fallthrough */ \ + case 1: *(--(c))=(unsigned char)(((l1) )&0xff); /* fallthrough */ \ + } \ + } + +#undef c2s +#define c2s(c,l) (l =((unsigned long)(*((c)++))) , \ + l|=((unsigned long)(*((c)++)))<< 8L) + +#undef s2c +#define s2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff)) + +#undef c2l +#define c2l(c,l) (l =((unsigned long)(*((c)++))) , \ + l|=((unsigned long)(*((c)++)))<< 8L, \ + l|=((unsigned long)(*((c)++)))<<16L, \ + l|=((unsigned long)(*((c)++)))<<24L) + +#undef l2c +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24L)&0xff)) +#endif + +#define E_IDEA(num) \ + x1&=0xffff; \ + idea_mul(x1,x1,*p,ul); p++; \ + x2+= *(p++); \ + x3+= *(p++); \ + x4&=0xffff; \ + idea_mul(x4,x4,*p,ul); p++; \ + t0=(x1^x3)&0xffff; \ + idea_mul(t0,t0,*p,ul); p++; \ + t1=(t0+(x2^x4))&0xffff; \ + idea_mul(t1,t1,*p,ul); p++; \ + t0+=t1; \ + x1^=t1; \ + x4^=t0; \ + ul=x2^t0; /* do the swap to x3 */ \ + x2=x3^t1; \ + x3=ul; + diff --git a/cscrypt/md5.c b/cscrypt/md5.c new file mode 100644 index 0000000..7cab322 --- /dev/null +++ b/cscrypt/md5.c @@ -0,0 +1,402 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * ---------------------------------------------------------------------------- + * The md5_crypt() function was taken from freeBSD's libcrypt and contains + * this license: + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * + * $FreeBSD: src/lib/libcrypt/crypt.c,v 1.7.2.1 1999/08/29 14:56:33 peter Exp $ + * + * ---------------------------------------------------------------------------- + * On April 19th, 2001 md5_crypt() was modified to make it reentrant + * by Erik Andersen + */ + +#include "../globals.h" +#include "../oscam-string.h" + +#include "md5.h" + +#if !defined(WITH_SSL) && !defined(WITH_LIBCRYPTO) + +#ifdef __i386__ +#define byteReverse(a, b) +#else +/* + * Note: This code is harmless on little-endian machines. + * The ifdefs are just a small optimization + */ +static void byteReverse(unsigned char *buf, unsigned int longs) +{ + uint32_t t; + do + { + t = (uint32_t)((unsigned int)buf[3] << 8 | buf[2]) << 16 | + ((unsigned int)buf[1] << 8 | buf[0]); + memcpy(buf, &t, 4); + buf += 4; + } + while(--longs); +} +#endif + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5_Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5_Transform(uint32_t buf[4], uint32_t in[16]) +{ + uint32_t a = buf[0]; + uint32_t b = buf[1]; + uint32_t c = buf[2]; + uint32_t d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5_Init(MD5_CTX *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5_Update(MD5_CTX *ctx, const unsigned char *buf, unsigned int len) +{ + uint32_t t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) + { ctx->bits[1]++; } /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if(t) + { + unsigned char *p = ((unsigned char *)ctx->in) + t; + t = 64 - t; + if(len < t) + { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse((unsigned char *)ctx->in, 16); + MD5_Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + while(len >= 64) + { + memcpy(ctx->in, buf, 64); + byteReverse((unsigned char *)ctx->in, 16); + MD5_Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5_Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ((unsigned char *)ctx->in) + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if(count < 8) + { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse((unsigned char *)ctx->in, 16); + MD5_Transform(ctx->buf, ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } + else + { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse((unsigned char *)ctx->in, 14); + + /* Append length in bits and transform */ + uint32_t *c = ctx->in; + c[14] = ctx->bits[0]; + c[15] = ctx->bits[1]; + + MD5_Transform(ctx->buf, ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(struct MD5Context)); /* In case it's sensitive */ +} + +unsigned char *MD5(const unsigned char *input, unsigned long len, unsigned char *output) +{ + MD5_CTX ctx; + MD5_Init(&ctx); + MD5_Update(&ctx, input, len); + MD5_Final(output, &ctx); + memset(&ctx, 0, sizeof(ctx)); /* security consideration */ + return output; +} +#endif + +/* This string is magic for this algorithm. Having + it this way, we can get better later on */ +static const char __md5__magic[] = "$1$"; + +/* 0 ... 63 => ascii - 64 */ +static const unsigned char __md5_itoa64[] = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void __md5_to64(char *s, unsigned long v, int n) +{ + while(--n >= 0) + { + *s++ = __md5_itoa64[v & 0x3f]; + v >>= 6; + } +} + +/* + * UNIX password + * + * Use MD5 for what it is best at... + */ + +char *__md5_crypt(const char *pw, const char *salt, char *passwd) +{ + const char *sp, *ep; + char *p; + + unsigned char final[17]; /* final[16] exists only to aid in looping */ + int sl, pl, i, __md5__magic_len, pw_len; + MD5_CTX ctx, ctx1; + unsigned long l; + + /* Refine the Salt first */ + sp = salt; + + /* If it starts with the magic string, then skip that */ + __md5__magic_len = cs_strlen(__md5__magic); + if(!strncmp(sp, __md5__magic, __md5__magic_len)) + { sp += __md5__magic_len; } + + /* It stops at the first '$', max 8 chars */ + for(ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++) + { continue; } + + /* get the length of the true salt */ + sl = ep - sp; + + MD5_Init(&ctx); + + /* The password first, since that is what is most unknown */ + pw_len = cs_strlen(pw); + MD5_Update(&ctx, (const unsigned char *)pw, pw_len); + + /* Then our magic string */ + MD5_Update(&ctx, (const unsigned char *)__md5__magic, __md5__magic_len); + + /* Then the raw salt */ + MD5_Update(&ctx, (const unsigned char *)sp, sl); + + /* Then just as many characters of the MD5(pw,salt,pw) */ + MD5_Init(&ctx1); + MD5_Update(&ctx1, (const unsigned char *)pw, pw_len); + MD5_Update(&ctx1, (const unsigned char *)sp, sl); + MD5_Update(&ctx1, (const unsigned char *)pw, pw_len); + MD5_Final(final, &ctx1); + for(pl = pw_len; pl > 0; pl -= 16) + { MD5_Update(&ctx, (const unsigned char *)final, pl > 16 ? 16 : pl); } + + /* Don't leave anything around in vm they could use. */ + memset(final, 0, sizeof final); + + /* Then something really weird... */ + for(i = pw_len; i ; i >>= 1) + { + MD5_Update(&ctx, ((i & 1) ? final : (const unsigned char *) pw), 1); + } + + /* Now make the output string */ + strncpy(passwd, __md5__magic, 4); // This should be safe + strncat(passwd, sp, sl); + strcat(passwd, "$"); + + MD5_Final(final, &ctx); + + /* + * and now, just to make sure things don't run too fast + * On a 60 Mhz Pentium this takes 34 msec, so you would + * need 30 seconds to build a 1000 entry dictionary... + */ + for(i = 0; i < 1000; i++) + { + MD5_Init(&ctx1); + if(i & 1) + { MD5_Update(&ctx1, (const unsigned char *)pw, pw_len); } + else + { MD5_Update(&ctx1, (const unsigned char *)final, 16); } + + if(i % 3) + { MD5_Update(&ctx1, (const unsigned char *)sp, sl); } + + if(i % 7) + { MD5_Update(&ctx1, (const unsigned char *)pw, pw_len); } + + if(i & 1) + { MD5_Update(&ctx1, (const unsigned char *)final, 16); } + else + { MD5_Update(&ctx1, (const unsigned char *)pw, pw_len); } + MD5_Final(final, &ctx1); + } + + p = passwd + cs_strlen(passwd); + + final[16] = final[5]; + for(i = 0 ; i < 5 ; i++) + { + l = (final[i] << 16) | (final[i + 6] << 8) | final[i + 12]; + __md5_to64(p, l, 4); + p += 4; + } + l = final[11]; + __md5_to64(p, l, 2); + p += 2; + *p = '\0'; + + /* Don't leave anything around in vm they could use. */ + memset(final, 0, sizeof final); + + return passwd; +} diff --git a/cscrypt/md5.h b/cscrypt/md5.h new file mode 100644 index 0000000..40ce5f5 --- /dev/null +++ b/cscrypt/md5.h @@ -0,0 +1,22 @@ +#ifndef _CSCRYPT_MD5_H +#define _CSCRYPT_MD5_H + +#if defined(WITH_SSL) || defined(WITH_LIBCRYPTO) +#include +#else +#define MD5_DIGEST_LENGTH 16 + +unsigned char *MD5(const unsigned char *input, unsigned long len, unsigned char *output_hash); + +typedef struct MD5Context { + uint32_t buf[4]; + uint32_t bits[2]; + uint32_t in[16]; +} MD5_CTX; + +void MD5_Init(MD5_CTX *ctx); +void MD5_Update(MD5_CTX *ctx, const unsigned char *buf, unsigned int len); +void MD5_Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx); +#endif +char *__md5_crypt(const char *text_pass, const char *salt, char *crypted_passwd); +#endif diff --git a/cscrypt/mdc2.c b/cscrypt/mdc2.c new file mode 100644 index 0000000..4db5c56 --- /dev/null +++ b/cscrypt/mdc2.c @@ -0,0 +1,669 @@ +#include "../globals.h" +#include "mdc2.h" + + +#undef c2l +#define c2l(c,l) (l =((DES_LONG)(*((c)++))) , \ + l|=((DES_LONG)(*((c)++)))<< 8L, \ + l|=((DES_LONG)(*((c)++)))<<16L, \ + l|=((DES_LONG)(*((c)++)))<<24L) + +#undef l2c +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24L)&0xff)) + +#if !defined(WITH_LIBCRYPTO) +# define FP(l,r) \ + { \ + register DES_LONG tt; \ + PERM_OP(l,r,tt, 1,0x55555555L); \ + PERM_OP(r,l,tt, 8,0x00ff00ffL); \ + PERM_OP(l,r,tt, 2,0x33333333L); \ + PERM_OP(r,l,tt,16,0x0000ffffL); \ + PERM_OP(l,r,tt, 4,0x0f0f0f0fL); \ + } + +const DES_LONG DES_SPtrans[8][64] = +{ + { + /* nibble 0 */ + 0x02080800L, 0x00080000L, 0x02000002L, 0x02080802L, + 0x02000000L, 0x00080802L, 0x00080002L, 0x02000002L, + 0x00080802L, 0x02080800L, 0x02080000L, 0x00000802L, + 0x02000802L, 0x02000000L, 0x00000000L, 0x00080002L, + 0x00080000L, 0x00000002L, 0x02000800L, 0x00080800L, + 0x02080802L, 0x02080000L, 0x00000802L, 0x02000800L, + 0x00000002L, 0x00000800L, 0x00080800L, 0x02080002L, + 0x00000800L, 0x02000802L, 0x02080002L, 0x00000000L, + 0x00000000L, 0x02080802L, 0x02000800L, 0x00080002L, + 0x02080800L, 0x00080000L, 0x00000802L, 0x02000800L, + 0x02080002L, 0x00000800L, 0x00080800L, 0x02000002L, + 0x00080802L, 0x00000002L, 0x02000002L, 0x02080000L, + 0x02080802L, 0x00080800L, 0x02080000L, 0x02000802L, + 0x02000000L, 0x00000802L, 0x00080002L, 0x00000000L, + 0x00080000L, 0x02000000L, 0x02000802L, 0x02080800L, + 0x00000002L, 0x02080002L, 0x00000800L, 0x00080802L, + }, + { + /* nibble 1 */ + 0x40108010L, 0x00000000L, 0x00108000L, 0x40100000L, + 0x40000010L, 0x00008010L, 0x40008000L, 0x00108000L, + 0x00008000L, 0x40100010L, 0x00000010L, 0x40008000L, + 0x00100010L, 0x40108000L, 0x40100000L, 0x00000010L, + 0x00100000L, 0x40008010L, 0x40100010L, 0x00008000L, + 0x00108010L, 0x40000000L, 0x00000000L, 0x00100010L, + 0x40008010L, 0x00108010L, 0x40108000L, 0x40000010L, + 0x40000000L, 0x00100000L, 0x00008010L, 0x40108010L, + 0x00100010L, 0x40108000L, 0x40008000L, 0x00108010L, + 0x40108010L, 0x00100010L, 0x40000010L, 0x00000000L, + 0x40000000L, 0x00008010L, 0x00100000L, 0x40100010L, + 0x00008000L, 0x40000000L, 0x00108010L, 0x40008010L, + 0x40108000L, 0x00008000L, 0x00000000L, 0x40000010L, + 0x00000010L, 0x40108010L, 0x00108000L, 0x40100000L, + 0x40100010L, 0x00100000L, 0x00008010L, 0x40008000L, + 0x40008010L, 0x00000010L, 0x40100000L, 0x00108000L, + }, + { + /* nibble 2 */ + 0x04000001L, 0x04040100L, 0x00000100L, 0x04000101L, + 0x00040001L, 0x04000000L, 0x04000101L, 0x00040100L, + 0x04000100L, 0x00040000L, 0x04040000L, 0x00000001L, + 0x04040101L, 0x00000101L, 0x00000001L, 0x04040001L, + 0x00000000L, 0x00040001L, 0x04040100L, 0x00000100L, + 0x00000101L, 0x04040101L, 0x00040000L, 0x04000001L, + 0x04040001L, 0x04000100L, 0x00040101L, 0x04040000L, + 0x00040100L, 0x00000000L, 0x04000000L, 0x00040101L, + 0x04040100L, 0x00000100L, 0x00000001L, 0x00040000L, + 0x00000101L, 0x00040001L, 0x04040000L, 0x04000101L, + 0x00000000L, 0x04040100L, 0x00040100L, 0x04040001L, + 0x00040001L, 0x04000000L, 0x04040101L, 0x00000001L, + 0x00040101L, 0x04000001L, 0x04000000L, 0x04040101L, + 0x00040000L, 0x04000100L, 0x04000101L, 0x00040100L, + 0x04000100L, 0x00000000L, 0x04040001L, 0x00000101L, + 0x04000001L, 0x00040101L, 0x00000100L, 0x04040000L, + }, + { + /* nibble 3 */ + 0x00401008L, 0x10001000L, 0x00000008L, 0x10401008L, + 0x00000000L, 0x10400000L, 0x10001008L, 0x00400008L, + 0x10401000L, 0x10000008L, 0x10000000L, 0x00001008L, + 0x10000008L, 0x00401008L, 0x00400000L, 0x10000000L, + 0x10400008L, 0x00401000L, 0x00001000L, 0x00000008L, + 0x00401000L, 0x10001008L, 0x10400000L, 0x00001000L, + 0x00001008L, 0x00000000L, 0x00400008L, 0x10401000L, + 0x10001000L, 0x10400008L, 0x10401008L, 0x00400000L, + 0x10400008L, 0x00001008L, 0x00400000L, 0x10000008L, + 0x00401000L, 0x10001000L, 0x00000008L, 0x10400000L, + 0x10001008L, 0x00000000L, 0x00001000L, 0x00400008L, + 0x00000000L, 0x10400008L, 0x10401000L, 0x00001000L, + 0x10000000L, 0x10401008L, 0x00401008L, 0x00400000L, + 0x10401008L, 0x00000008L, 0x10001000L, 0x00401008L, + 0x00400008L, 0x00401000L, 0x10400000L, 0x10001008L, + 0x00001008L, 0x10000000L, 0x10000008L, 0x10401000L, + }, + { + /* nibble 4 */ + 0x08000000L, 0x00010000L, 0x00000400L, 0x08010420L, + 0x08010020L, 0x08000400L, 0x00010420L, 0x08010000L, + 0x00010000L, 0x00000020L, 0x08000020L, 0x00010400L, + 0x08000420L, 0x08010020L, 0x08010400L, 0x00000000L, + 0x00010400L, 0x08000000L, 0x00010020L, 0x00000420L, + 0x08000400L, 0x00010420L, 0x00000000L, 0x08000020L, + 0x00000020L, 0x08000420L, 0x08010420L, 0x00010020L, + 0x08010000L, 0x00000400L, 0x00000420L, 0x08010400L, + 0x08010400L, 0x08000420L, 0x00010020L, 0x08010000L, + 0x00010000L, 0x00000020L, 0x08000020L, 0x08000400L, + 0x08000000L, 0x00010400L, 0x08010420L, 0x00000000L, + 0x00010420L, 0x08000000L, 0x00000400L, 0x00010020L, + 0x08000420L, 0x00000400L, 0x00000000L, 0x08010420L, + 0x08010020L, 0x08010400L, 0x00000420L, 0x00010000L, + 0x00010400L, 0x08010020L, 0x08000400L, 0x00000420L, + 0x00000020L, 0x00010420L, 0x08010000L, 0x08000020L, + }, + { + /* nibble 5 */ + 0x80000040L, 0x00200040L, 0x00000000L, 0x80202000L, + 0x00200040L, 0x00002000L, 0x80002040L, 0x00200000L, + 0x00002040L, 0x80202040L, 0x00202000L, 0x80000000L, + 0x80002000L, 0x80000040L, 0x80200000L, 0x00202040L, + 0x00200000L, 0x80002040L, 0x80200040L, 0x00000000L, + 0x00002000L, 0x00000040L, 0x80202000L, 0x80200040L, + 0x80202040L, 0x80200000L, 0x80000000L, 0x00002040L, + 0x00000040L, 0x00202000L, 0x00202040L, 0x80002000L, + 0x00002040L, 0x80000000L, 0x80002000L, 0x00202040L, + 0x80202000L, 0x00200040L, 0x00000000L, 0x80002000L, + 0x80000000L, 0x00002000L, 0x80200040L, 0x00200000L, + 0x00200040L, 0x80202040L, 0x00202000L, 0x00000040L, + 0x80202040L, 0x00202000L, 0x00200000L, 0x80002040L, + 0x80000040L, 0x80200000L, 0x00202040L, 0x00000000L, + 0x00002000L, 0x80000040L, 0x80002040L, 0x80202000L, + 0x80200000L, 0x00002040L, 0x00000040L, 0x80200040L, + }, + { + /* nibble 6 */ + 0x00004000L, 0x00000200L, 0x01000200L, 0x01000004L, + 0x01004204L, 0x00004004L, 0x00004200L, 0x00000000L, + 0x01000000L, 0x01000204L, 0x00000204L, 0x01004000L, + 0x00000004L, 0x01004200L, 0x01004000L, 0x00000204L, + 0x01000204L, 0x00004000L, 0x00004004L, 0x01004204L, + 0x00000000L, 0x01000200L, 0x01000004L, 0x00004200L, + 0x01004004L, 0x00004204L, 0x01004200L, 0x00000004L, + 0x00004204L, 0x01004004L, 0x00000200L, 0x01000000L, + 0x00004204L, 0x01004000L, 0x01004004L, 0x00000204L, + 0x00004000L, 0x00000200L, 0x01000000L, 0x01004004L, + 0x01000204L, 0x00004204L, 0x00004200L, 0x00000000L, + 0x00000200L, 0x01000004L, 0x00000004L, 0x01000200L, + 0x00000000L, 0x01000204L, 0x01000200L, 0x00004200L, + 0x00000204L, 0x00004000L, 0x01004204L, 0x01000000L, + 0x01004200L, 0x00000004L, 0x00004004L, 0x01004204L, + 0x01000004L, 0x01004200L, 0x01004000L, 0x00004004L, + }, + { + /* nibble 7 */ + 0x20800080L, 0x20820000L, 0x00020080L, 0x00000000L, + 0x20020000L, 0x00800080L, 0x20800000L, 0x20820080L, + 0x00000080L, 0x20000000L, 0x00820000L, 0x00020080L, + 0x00820080L, 0x20020080L, 0x20000080L, 0x20800000L, + 0x00020000L, 0x00820080L, 0x00800080L, 0x20020000L, + 0x20820080L, 0x20000080L, 0x00000000L, 0x00820000L, + 0x20000000L, 0x00800000L, 0x20020080L, 0x20800080L, + 0x00800000L, 0x00020000L, 0x20820000L, 0x00000080L, + 0x00800000L, 0x00020000L, 0x20000080L, 0x20820080L, + 0x00020080L, 0x20000000L, 0x00000000L, 0x00820000L, + 0x20800080L, 0x20020080L, 0x20020000L, 0x00800080L, + 0x20820000L, 0x00000080L, 0x00800080L, 0x20020000L, + 0x20820080L, 0x00800000L, 0x20800000L, 0x20000080L, + 0x00820000L, 0x00020080L, 0x20020080L, 0x20800000L, + 0x00000080L, 0x20820000L, 0x00820080L, 0x00000000L, + 0x20000000L, 0x20800080L, 0x00020000L, 0x00820080L, + } +}; + +#define LOAD_DATA_tmp(a,b,c,d,e,f) LOAD_DATA(a,b,c,d,e,f,g) +#define LOAD_DATA(R,S,u,t,E0,E1,tmp) \ + u=R^s[S ]; \ + t=R^s[S+1] + +#define D_ENCRYPT(LL,R,S) { \ + LOAD_DATA_tmp(R,S,u,t,E0,E1); \ + t=ROTATE(t,4); \ + LL^= \ + DES_SPtrans[0][(u>> 2L)&0x3f]^ \ + DES_SPtrans[2][(u>>10L)&0x3f]^ \ + DES_SPtrans[4][(u>>18L)&0x3f]^ \ + DES_SPtrans[6][(u>>26L)&0x3f]^ \ + DES_SPtrans[1][(t>> 2L)&0x3f]^ \ + DES_SPtrans[3][(t>>10L)&0x3f]^ \ + DES_SPtrans[5][(t>>18L)&0x3f]^ \ + DES_SPtrans[7][(t>>26L)&0x3f]; } + +#define IP(l,r) \ + { \ + register DES_LONG tt; \ + PERM_OP(r,l,tt, 4,0x0f0f0f0fL); \ + PERM_OP(l,r,tt,16,0x0000ffffL); \ + PERM_OP(r,l,tt, 2,0x33333333L); \ + PERM_OP(l,r,tt, 8,0x00ff00ffL); \ + PERM_OP(r,l,tt, 1,0x55555555L); \ + } + +#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\ + (b)^=(t),\ + (a)^=((t)<<(n))) + +#define ROTATE(a,n) (((a)>>(n))+((a)<<(32-(n)))) + +static const unsigned char odd_parity[256] = +{ + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98, 100, 100, 103, 103, 104, 104, 107, 107, 109, 109, 110, + 110, + 112, 112, 115, 115, 117, 117, 118, 118, 121, 121, 122, 122, 124, 124, 127, + 127, + 128, 128, 131, 131, 133, 133, 134, 134, 137, 137, 138, 138, 140, 140, 143, + 143, + 145, 145, 146, 146, 148, 148, 151, 151, 152, 152, 155, 155, 157, 157, 158, + 158, + 161, 161, 162, 162, 164, 164, 167, 167, 168, 168, 171, 171, 173, 173, 174, + 174, + 176, 176, 179, 179, 181, 181, 182, 182, 185, 185, 186, 186, 188, 188, 191, + 191, + 193, 193, 194, 194, 196, 196, 199, 199, 200, 200, 203, 203, 205, 205, 206, + 206, + 208, 208, 211, 211, 213, 213, 214, 214, 217, 217, 218, 218, 220, 220, 223, + 223, + 224, 224, 227, 227, 229, 229, 230, 230, 233, 233, 234, 234, 236, 236, 239, + 239, + 241, 241, 242, 242, 244, 244, 247, 247, 248, 248, 251, 251, 253, 253, 254, + 254 +}; + +#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\ + (a)=(a)^(t)^(t>>(16-(n)))) + +# define ITERATIONS 16 +# define HALF_ITERATIONS 8 + +static const DES_LONG des_skb[8][64] = +{ + { + /* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ + 0x00000000L, 0x00000010L, 0x20000000L, 0x20000010L, + 0x00010000L, 0x00010010L, 0x20010000L, 0x20010010L, + 0x00000800L, 0x00000810L, 0x20000800L, 0x20000810L, + 0x00010800L, 0x00010810L, 0x20010800L, 0x20010810L, + 0x00000020L, 0x00000030L, 0x20000020L, 0x20000030L, + 0x00010020L, 0x00010030L, 0x20010020L, 0x20010030L, + 0x00000820L, 0x00000830L, 0x20000820L, 0x20000830L, + 0x00010820L, 0x00010830L, 0x20010820L, 0x20010830L, + 0x00080000L, 0x00080010L, 0x20080000L, 0x20080010L, + 0x00090000L, 0x00090010L, 0x20090000L, 0x20090010L, + 0x00080800L, 0x00080810L, 0x20080800L, 0x20080810L, + 0x00090800L, 0x00090810L, 0x20090800L, 0x20090810L, + 0x00080020L, 0x00080030L, 0x20080020L, 0x20080030L, + 0x00090020L, 0x00090030L, 0x20090020L, 0x20090030L, + 0x00080820L, 0x00080830L, 0x20080820L, 0x20080830L, + 0x00090820L, 0x00090830L, 0x20090820L, 0x20090830L, + }, + { + /* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */ + 0x00000000L, 0x02000000L, 0x00002000L, 0x02002000L, + 0x00200000L, 0x02200000L, 0x00202000L, 0x02202000L, + 0x00000004L, 0x02000004L, 0x00002004L, 0x02002004L, + 0x00200004L, 0x02200004L, 0x00202004L, 0x02202004L, + 0x00000400L, 0x02000400L, 0x00002400L, 0x02002400L, + 0x00200400L, 0x02200400L, 0x00202400L, 0x02202400L, + 0x00000404L, 0x02000404L, 0x00002404L, 0x02002404L, + 0x00200404L, 0x02200404L, 0x00202404L, 0x02202404L, + 0x10000000L, 0x12000000L, 0x10002000L, 0x12002000L, + 0x10200000L, 0x12200000L, 0x10202000L, 0x12202000L, + 0x10000004L, 0x12000004L, 0x10002004L, 0x12002004L, + 0x10200004L, 0x12200004L, 0x10202004L, 0x12202004L, + 0x10000400L, 0x12000400L, 0x10002400L, 0x12002400L, + 0x10200400L, 0x12200400L, 0x10202400L, 0x12202400L, + 0x10000404L, 0x12000404L, 0x10002404L, 0x12002404L, + 0x10200404L, 0x12200404L, 0x10202404L, 0x12202404L, + }, + { + /* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */ + 0x00000000L, 0x00000001L, 0x00040000L, 0x00040001L, + 0x01000000L, 0x01000001L, 0x01040000L, 0x01040001L, + 0x00000002L, 0x00000003L, 0x00040002L, 0x00040003L, + 0x01000002L, 0x01000003L, 0x01040002L, 0x01040003L, + 0x00000200L, 0x00000201L, 0x00040200L, 0x00040201L, + 0x01000200L, 0x01000201L, 0x01040200L, 0x01040201L, + 0x00000202L, 0x00000203L, 0x00040202L, 0x00040203L, + 0x01000202L, 0x01000203L, 0x01040202L, 0x01040203L, + 0x08000000L, 0x08000001L, 0x08040000L, 0x08040001L, + 0x09000000L, 0x09000001L, 0x09040000L, 0x09040001L, + 0x08000002L, 0x08000003L, 0x08040002L, 0x08040003L, + 0x09000002L, 0x09000003L, 0x09040002L, 0x09040003L, + 0x08000200L, 0x08000201L, 0x08040200L, 0x08040201L, + 0x09000200L, 0x09000201L, 0x09040200L, 0x09040201L, + 0x08000202L, 0x08000203L, 0x08040202L, 0x08040203L, + 0x09000202L, 0x09000203L, 0x09040202L, 0x09040203L, + }, + { + /* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */ + 0x00000000L, 0x00100000L, 0x00000100L, 0x00100100L, + 0x00000008L, 0x00100008L, 0x00000108L, 0x00100108L, + 0x00001000L, 0x00101000L, 0x00001100L, 0x00101100L, + 0x00001008L, 0x00101008L, 0x00001108L, 0x00101108L, + 0x04000000L, 0x04100000L, 0x04000100L, 0x04100100L, + 0x04000008L, 0x04100008L, 0x04000108L, 0x04100108L, + 0x04001000L, 0x04101000L, 0x04001100L, 0x04101100L, + 0x04001008L, 0x04101008L, 0x04001108L, 0x04101108L, + 0x00020000L, 0x00120000L, 0x00020100L, 0x00120100L, + 0x00020008L, 0x00120008L, 0x00020108L, 0x00120108L, + 0x00021000L, 0x00121000L, 0x00021100L, 0x00121100L, + 0x00021008L, 0x00121008L, 0x00021108L, 0x00121108L, + 0x04020000L, 0x04120000L, 0x04020100L, 0x04120100L, + 0x04020008L, 0x04120008L, 0x04020108L, 0x04120108L, + 0x04021000L, 0x04121000L, 0x04021100L, 0x04121100L, + 0x04021008L, 0x04121008L, 0x04021108L, 0x04121108L, + }, + { + /* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ + 0x00000000L, 0x10000000L, 0x00010000L, 0x10010000L, + 0x00000004L, 0x10000004L, 0x00010004L, 0x10010004L, + 0x20000000L, 0x30000000L, 0x20010000L, 0x30010000L, + 0x20000004L, 0x30000004L, 0x20010004L, 0x30010004L, + 0x00100000L, 0x10100000L, 0x00110000L, 0x10110000L, + 0x00100004L, 0x10100004L, 0x00110004L, 0x10110004L, + 0x20100000L, 0x30100000L, 0x20110000L, 0x30110000L, + 0x20100004L, 0x30100004L, 0x20110004L, 0x30110004L, + 0x00001000L, 0x10001000L, 0x00011000L, 0x10011000L, + 0x00001004L, 0x10001004L, 0x00011004L, 0x10011004L, + 0x20001000L, 0x30001000L, 0x20011000L, 0x30011000L, + 0x20001004L, 0x30001004L, 0x20011004L, 0x30011004L, + 0x00101000L, 0x10101000L, 0x00111000L, 0x10111000L, + 0x00101004L, 0x10101004L, 0x00111004L, 0x10111004L, + 0x20101000L, 0x30101000L, 0x20111000L, 0x30111000L, + 0x20101004L, 0x30101004L, 0x20111004L, 0x30111004L, + }, + { + /* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */ + 0x00000000L, 0x08000000L, 0x00000008L, 0x08000008L, + 0x00000400L, 0x08000400L, 0x00000408L, 0x08000408L, + 0x00020000L, 0x08020000L, 0x00020008L, 0x08020008L, + 0x00020400L, 0x08020400L, 0x00020408L, 0x08020408L, + 0x00000001L, 0x08000001L, 0x00000009L, 0x08000009L, + 0x00000401L, 0x08000401L, 0x00000409L, 0x08000409L, + 0x00020001L, 0x08020001L, 0x00020009L, 0x08020009L, + 0x00020401L, 0x08020401L, 0x00020409L, 0x08020409L, + 0x02000000L, 0x0A000000L, 0x02000008L, 0x0A000008L, + 0x02000400L, 0x0A000400L, 0x02000408L, 0x0A000408L, + 0x02020000L, 0x0A020000L, 0x02020008L, 0x0A020008L, + 0x02020400L, 0x0A020400L, 0x02020408L, 0x0A020408L, + 0x02000001L, 0x0A000001L, 0x02000009L, 0x0A000009L, + 0x02000401L, 0x0A000401L, 0x02000409L, 0x0A000409L, + 0x02020001L, 0x0A020001L, 0x02020009L, 0x0A020009L, + 0x02020401L, 0x0A020401L, 0x02020409L, 0x0A020409L, + }, + { + /* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */ + 0x00000000L, 0x00000100L, 0x00080000L, 0x00080100L, + 0x01000000L, 0x01000100L, 0x01080000L, 0x01080100L, + 0x00000010L, 0x00000110L, 0x00080010L, 0x00080110L, + 0x01000010L, 0x01000110L, 0x01080010L, 0x01080110L, + 0x00200000L, 0x00200100L, 0x00280000L, 0x00280100L, + 0x01200000L, 0x01200100L, 0x01280000L, 0x01280100L, + 0x00200010L, 0x00200110L, 0x00280010L, 0x00280110L, + 0x01200010L, 0x01200110L, 0x01280010L, 0x01280110L, + 0x00000200L, 0x00000300L, 0x00080200L, 0x00080300L, + 0x01000200L, 0x01000300L, 0x01080200L, 0x01080300L, + 0x00000210L, 0x00000310L, 0x00080210L, 0x00080310L, + 0x01000210L, 0x01000310L, 0x01080210L, 0x01080310L, + 0x00200200L, 0x00200300L, 0x00280200L, 0x00280300L, + 0x01200200L, 0x01200300L, 0x01280200L, 0x01280300L, + 0x00200210L, 0x00200310L, 0x00280210L, 0x00280310L, + 0x01200210L, 0x01200310L, 0x01280210L, 0x01280310L, + }, + { + /* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */ + 0x00000000L, 0x04000000L, 0x00040000L, 0x04040000L, + 0x00000002L, 0x04000002L, 0x00040002L, 0x04040002L, + 0x00002000L, 0x04002000L, 0x00042000L, 0x04042000L, + 0x00002002L, 0x04002002L, 0x00042002L, 0x04042002L, + 0x00000020L, 0x04000020L, 0x00040020L, 0x04040020L, + 0x00000022L, 0x04000022L, 0x00040022L, 0x04040022L, + 0x00002020L, 0x04002020L, 0x00042020L, 0x04042020L, + 0x00002022L, 0x04002022L, 0x00042022L, 0x04042022L, + 0x00000800L, 0x04000800L, 0x00040800L, 0x04040800L, + 0x00000802L, 0x04000802L, 0x00040802L, 0x04040802L, + 0x00002800L, 0x04002800L, 0x00042800L, 0x04042800L, + 0x00002802L, 0x04002802L, 0x00042802L, 0x04042802L, + 0x00000820L, 0x04000820L, 0x00040820L, 0x04040820L, + 0x00000822L, 0x04000822L, 0x00040822L, 0x04040822L, + 0x00002820L, 0x04002820L, 0x00042820L, 0x04042820L, + 0x00002822L, 0x04002822L, 0x00042822L, 0x04042822L, + } +}; + +void DES_set_key_unchecked(const_DES_cblock *key, DES_key_schedule *schedule) +{ + static const int shifts2[16] = + { 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0 }; + register DES_LONG c, d, t, s, t2; + register const unsigned char *in; + register DES_LONG *k; + register int i; + +#ifdef OPENBSD_DEV_CRYPTO + memcpy(schedule->key, key, sizeof schedule->key); + schedule->session = NULL; +#endif + k = &schedule->ks->deslong[0]; + in = &(*key)[0]; + + c2l(in, c); + c2l(in, d); + + /* + * do PC1 in 47 simple operations :-) Thanks to John Fletcher + * (john_fletcher@lccmail.ocf.llnl.gov) for the inspiration. :-) + */ + PERM_OP(d, c, t, 4, 0x0f0f0f0fL); + HPERM_OP(c, t, -2, 0xcccc0000L); + HPERM_OP(d, t, -2, 0xcccc0000L); + PERM_OP(d, c, t, 1, 0x55555555L); + PERM_OP(c, d, t, 8, 0x00ff00ffL); + PERM_OP(d, c, t, 1, 0x55555555L); + d = (((d & 0x000000ffL) << 16L) | (d & 0x0000ff00L) | + ((d & 0x00ff0000L) >> 16L) | ((c & 0xf0000000L) >> 4L)); + c &= 0x0fffffffL; + + for (i = 0; i < ITERATIONS; i++) + { + if (shifts2[i]) + { + c = ((c >> 2L) | (c << 26L)); + d = ((d >> 2L) | (d << 26L)); + } + else + { + c = ((c >> 1L) | (c << 27L)); + d = ((d >> 1L) | (d << 27L)); + } + c &= 0x0fffffffL; + d &= 0x0fffffffL; + /* + * could be a few less shifts but I am to lazy at this point in time + * to investigate + */ + s = des_skb[0][(c) & 0x3f] | + des_skb[1][((c >> 6L) & 0x03) | ((c >> 7L) & 0x3c)] | + des_skb[2][((c >> 13L) & 0x0f) | ((c >> 14L) & 0x30)] | + des_skb[3][((c >> 20L) & 0x01) | ((c >> 21L) & 0x06) | + ((c >> 22L) & 0x38)]; + t = des_skb[4][(d) & 0x3f] | + des_skb[5][((d >> 7L) & 0x03) | ((d >> 8L) & 0x3c)] | + des_skb[6][(d >> 15L) & 0x3f] | + des_skb[7][((d >> 21L) & 0x0f) | ((d >> 22L) & 0x30)]; + + /* table contained 0213 4657 */ + t2 = ((t << 16L) | (s & 0x0000ffffL)) & 0xffffffffL; + *(k++) = ROTATE(t2, 30) & 0xffffffffL; + + t2 = ((s >> 16L) | (t & 0xffff0000L)); + *(k++) = ROTATE(t2, 26) & 0xffffffffL; + } +} + +void DES_set_odd_parity(DES_cblock *key) +{ + unsigned int i; + + for (i = 0; i < DES_KEY_SZ; i++) + (*key)[i] = odd_parity[(*key)[i]]; +} + +void DES_encrypt1(DES_LONG *data, DES_key_schedule *ks, int enc) +{ + register DES_LONG l=0, r=0, t=0, u=0; + //l = r = t = u = 0; + + register DES_LONG *s; + + r = data[0]; + l = data[1]; + + IP(r, l); + /* + * Things have been modified so that the initial rotate is done outside + * the loop. This required the DES_SPtrans values in sp.h to be rotated + * 1 bit to the right. One perl script later and things have a 5% speed + * up on a sparc2. Thanks to Richard Outerbridge + * <71755.204@CompuServe.COM> for pointing this out. + */ + /* clear the top bits on machines with 8byte longs */ + /* shift left by 2 */ + r = ROTATE(r, 29) & 0xffffffffL; + l = ROTATE(l, 29) & 0xffffffffL; + + s = ks->ks->deslong; + /* + * I don't know if it is worth the effort of loop unrolling the inner + * loop + */ + if (enc) + { + D_ENCRYPT(l, r, 0); /* 1 */ + D_ENCRYPT(r, l, 2); /* 2 */ + D_ENCRYPT(l, r, 4); /* 3 */ + D_ENCRYPT(r, l, 6); /* 4 */ + D_ENCRYPT(l, r, 8); /* 5 */ + D_ENCRYPT(r, l, 10); /* 6 */ + D_ENCRYPT(l, r, 12); /* 7 */ + D_ENCRYPT(r, l, 14); /* 8 */ + D_ENCRYPT(l, r, 16); /* 9 */ + D_ENCRYPT(r, l, 18); /* 10 */ + D_ENCRYPT(l, r, 20); /* 11 */ + D_ENCRYPT(r, l, 22); /* 12 */ + D_ENCRYPT(l, r, 24); /* 13 */ + D_ENCRYPT(r, l, 26); /* 14 */ + D_ENCRYPT(l, r, 28); /* 15 */ + D_ENCRYPT(r, l, 30); /* 16 */ + } + else + { + D_ENCRYPT(l, r, 30); /* 16 */ + D_ENCRYPT(r, l, 28); /* 15 */ + D_ENCRYPT(l, r, 26); /* 14 */ + D_ENCRYPT(r, l, 24); /* 13 */ + D_ENCRYPT(l, r, 22); /* 12 */ + D_ENCRYPT(r, l, 20); /* 11 */ + D_ENCRYPT(l, r, 18); /* 10 */ + D_ENCRYPT(r, l, 16); /* 9 */ + D_ENCRYPT(l, r, 14); /* 8 */ + D_ENCRYPT(r, l, 12); /* 7 */ + D_ENCRYPT(l, r, 10); /* 6 */ + D_ENCRYPT(r, l, 8); /* 5 */ + D_ENCRYPT(l, r, 6); /* 4 */ + D_ENCRYPT(r, l, 4); /* 3 */ + D_ENCRYPT(l, r, 2); /* 2 */ + D_ENCRYPT(r, l, 0); /* 1 */ + } + + /* rotate and clear the top bits on machines with 8byte longs */ + l = ROTATE(l, 3) & 0xffffffffL; + r = ROTATE(r, 3) & 0xffffffffL; + + FP(r, l); + data[0] = l; + data[1] = r; +} +#endif + +static void mdc2_body(MDC2_CTX *c, const unsigned char *in, size_t len); +int MDC2_Init(MDC2_CTX *c) +{ + c->num = 0; + c->pad_type = 1; + memset(&(c->h[0]), 0x52, MDC2_BLOCK); + memset(&(c->hh[0]), 0x25, MDC2_BLOCK); + return 1; +} + +int MDC2_Update(MDC2_CTX *c, const unsigned char *in, size_t len) +{ + size_t i, j; + + i = c->num; + if (i != 0) + { + if (len < MDC2_BLOCK - i) + { + /* partial block */ + memcpy(&(c->data[i]), in, len); + c->num += (int)len; + return 1; + } + else + { + /* filled one */ + j = MDC2_BLOCK - i; + memcpy(&(c->data[i]), in, j); + len -= j; + in += j; + c->num = 0; + mdc2_body(c, &(c->data[0]), MDC2_BLOCK); + } + } + i = len & ~((size_t)MDC2_BLOCK - 1); + if (i > 0) + mdc2_body(c, in, i); + j = len - i; + if (j > 0) + { + memcpy(&(c->data[0]), &(in[i]), j); + c->num = (int)j; + } + return 1; +} + +static void mdc2_body(MDC2_CTX *c, const unsigned char *in, size_t len) +{ + register DES_LONG tin0, tin1; + register DES_LONG ttin0, ttin1; + DES_LONG d[2], dd[2]; + DES_key_schedule k; + unsigned char *p; + size_t i; + + for (i = 0; i < len; i += 8) + { + c2l(in, tin0); + d[0] = dd[0] = tin0; + c2l(in, tin1); + d[1] = dd[1] = tin1; + c->h[0] = (c->h[0] & 0x9f) | 0x40; + c->hh[0] = (c->hh[0] & 0x9f) | 0x20; + + DES_set_odd_parity(&c->h); + DES_set_key_unchecked(&c->h, &k); + DES_encrypt1(d, &k, 1); + + DES_set_odd_parity(&c->hh); + DES_set_key_unchecked(&c->hh, &k); + DES_encrypt1(dd, &k, 1); + + ttin0 = tin0 ^ dd[0]; + ttin1 = tin1 ^ dd[1]; + tin0 ^= d[0]; + tin1 ^= d[1]; + + p = c->h; + l2c(tin0, p); + l2c(ttin1, p); + p = c->hh; + l2c(ttin0, p); + l2c(tin1, p); + } +} + +int MDC2_Final(unsigned char *md, MDC2_CTX *c) +{ + unsigned int i; + int j; + + i = c->num; + j = c->pad_type; + if ((i > 0) || (j == 2)) + { + if (j == 2) + c->data[i++] = 0x80; + memset(&(c->data[i]), 0, MDC2_BLOCK - i); + mdc2_body(c, c->data, MDC2_BLOCK); + } + memcpy(md, (char *)c->h, MDC2_BLOCK); + memcpy(&(md[MDC2_BLOCK]), (char *)c->hh, MDC2_BLOCK); + return 1; +} diff --git a/cscrypt/mdc2.h b/cscrypt/mdc2.h new file mode 100644 index 0000000..e57c3ed --- /dev/null +++ b/cscrypt/mdc2.h @@ -0,0 +1,104 @@ +/* crypto/mdc2/mdc2.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_MDC2_H +#define HEADER_MDC2_H + +#if defined(WITH_SSL) || defined(WITH_LIBCRYPTO) +#include +#else +#define DES_KEY_SZ (sizeof(DES_cblock)) +typedef unsigned int DES_LONG; +typedef unsigned char DES_cblock[8]; +typedef unsigned char const_DES_cblock[8]; +typedef struct DES_ks { + union { + DES_cblock cblock; + DES_LONG deslong[2]; + } ks[16]; +} DES_key_schedule; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define MDC2_BLOCK 8 +#define MDC2_DIGEST_LENGTH 16 + +typedef struct mdc2_ctx_st +{ + unsigned int num; + unsigned char data[MDC2_BLOCK]; + DES_cblock h,hh; + int pad_type; /* either 1 or 2, default 1 */ +} MDC2_CTX; + +#ifdef OPENSSL_FIPS +int private_MDC2_Init(MDC2_CTX *c); +#endif +int MDC2_Init(MDC2_CTX *c); +int MDC2_Update(MDC2_CTX *c, const unsigned char *data, size_t len); +int MDC2_Final(unsigned char *md, MDC2_CTX *c); +unsigned char *MDC2(const unsigned char *d, size_t n, unsigned char *md); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cscrypt/mem.c b/cscrypt/mem.c new file mode 100644 index 0000000..ae99081 --- /dev/null +++ b/cscrypt/mem.c @@ -0,0 +1,276 @@ +#include "bn.h" + +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* crypto/mem.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The license and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution license + * [including the GNU Public License.] + */ + +#include +#include +#include "openssl_mods.h" + + +static int allow_customize = 1; /* we provide flexible functions for */ +static int allow_customize_debug = 1;/* exchanging memory-related functions at + * run-time, but this must be done + * before any blocks are actually + * allocated; or we'll run into huge + * problems when malloc/free pairs + * don't match etc. */ + +/* may be changed as long as `allow_customize' is set */ +static void *(*malloc_locked_func)(size_t) = malloc; +static void (*free_locked_func)(void *) = free; +static void *(*malloc_func)(size_t) = malloc; +static void *(*realloc_func)(void *, size_t) = realloc; +static void (*free_func)(void *) = free; + +/* may be changed as long as `allow_customize_debug' is set */ +/* XXX use correct function pointer types */ +#ifdef CRYPTO_MDEBUG +/* use default functions from mem_dbg.c */ +static void (*malloc_debug_func)(void *, int, const char *, int, int) += CRYPTO_dbg_malloc; +static void (*realloc_debug_func)(void *, void *, int, const char *, int, int) += CRYPTO_dbg_realloc; +static void (*free_debug_func)(void *, int) = CRYPTO_dbg_free; +static void (*set_debug_options_func)(long) = CRYPTO_dbg_set_options; +static long(*get_debug_options_func)(void) = CRYPTO_dbg_get_options; +#else +/* applications can use CRYPTO_malloc_debug_init() to select above case + * at run-time */ +static void (*malloc_debug_func)(void *, int, const char *, int, int) = NULL; +static void (*realloc_debug_func)(void *, void *, int, const char *, int, int) += NULL; +static void (*free_debug_func)(void *, int) = NULL; +static void (*set_debug_options_func)(long) = NULL; +static long(*get_debug_options_func)(void) = NULL; +#endif + + +int CRYPTO_set_mem_functions(void *(*m)(size_t), void *(*r)(void *, size_t), + void (*f)(void *)) +{ + if(!allow_customize) + { return 0; } + if((m == NULL) || (r == NULL) || (f == NULL)) + { return 0; } + malloc_func = m; + realloc_func = r; + free_func = f; + malloc_locked_func = m; + free_locked_func = f; + return 1; +} + +int CRYPTO_set_locked_mem_functions(void *(*m)(size_t), void (*f)(void *)) +{ + if(!allow_customize) + { return 0; } + if((m == NULL) || (f == NULL)) + { return 0; } + malloc_locked_func = m; + free_locked_func = f; + return 1; +} + +int CRYPTO_set_mem_debug_functions(void (*m)(void *, int, const char *, int, int), + void (*r)(void *, void *, int, const char *, int, int), + void (*f)(void *, int), + void (*so)(long), + long(*go)(void)) +{ + if(!allow_customize_debug) + { return 0; } + malloc_debug_func = m; + realloc_debug_func = r; + free_debug_func = f; + set_debug_options_func = so; + get_debug_options_func = go; + return 1; +} + +void CRYPTO_get_mem_functions(void *(**m)(size_t), void *(**r)(void *, size_t), + void (**f)(void *)) +{ + if(m != NULL) { *m = malloc_func; } + if(r != NULL) { *r = realloc_func; } + if(f != NULL) { *f = free_func; } +} + +void CRYPTO_get_locked_mem_functions(void *(**m)(size_t), void (**f)(void *)) +{ + if(m != NULL) { *m = malloc_locked_func; } + if(f != NULL) { *f = free_locked_func; } +} + +void CRYPTO_get_mem_debug_functions(void (**m)(void *, int, const char *, int, int), + void (**r)(void *, void *, int, const char *, int, int), + void (**f)(void *, int), + void (**so)(long), + long(**go)(void)) +{ + if(m != NULL) { *m = malloc_debug_func; } + if(r != NULL) { *r = realloc_debug_func; } + if(f != NULL) { *f = free_debug_func; } + if(so != NULL) { *so = set_debug_options_func; } + if(go != NULL) { *go = get_debug_options_func; } +} + + +void *CRYPTO_malloc_locked(int num, const char *file, int line) +{ + void *ret = NULL; + + allow_customize = 0; + if(malloc_debug_func != NULL) + { + allow_customize_debug = 0; + malloc_debug_func(NULL, num, file, line, 0); + } + ret = malloc_locked_func(num); +#ifdef LEVITTE_DEBUG + fprintf(stderr, "LEVITTE_DEBUG: > 0x%p (%d)\n", ret, num); +#endif + if(malloc_debug_func != NULL) + { malloc_debug_func(ret, num, file, line, 1); } + + return ret; +} + +void CRYPTO_free_locked(void *str) +{ + if(free_debug_func != NULL) + { free_debug_func(str, 0); } +#ifdef LEVITTE_DEBUG + fprintf(stderr, "LEVITTE_DEBUG: < 0x%p\n", str); +#endif + free_locked_func(str); + if(free_debug_func != NULL) + { free_debug_func(NULL, 1); } +} + +void *CRYPTO_malloc(int num, const char *file, int line) +{ + void *ret = NULL; + + allow_customize = 0; + if(malloc_debug_func != NULL) + { + allow_customize_debug = 0; + malloc_debug_func(NULL, num, file, line, 0); + } + ret = malloc_func(num); +#ifdef LEVITTE_DEBUG + fprintf(stderr, "LEVITTE_DEBUG: > 0x%p (%d)\n", ret, num); +#endif + if(malloc_debug_func != NULL) + { malloc_debug_func(ret, num, file, line, 1); } + + return ret; +} + +void *CRYPTO_realloc(void *str, int num, const char *file, int line) +{ + void *ret = NULL; + + if(realloc_debug_func != NULL) + { realloc_debug_func(str, NULL, num, file, line, 0); } + ret = realloc_func(str, num); +#ifdef LEVITTE_DEBUG + fprintf(stderr, "LEVITTE_DEBUG: | 0x%p -> 0x%p (%d)\n", str, ret, num); +#endif + if(realloc_debug_func != NULL) + { realloc_debug_func(str, ret, num, file, line, 1); } + + return ret; +} + +void CRYPTO_free(void *str) +{ + if(free_debug_func != NULL) + { free_debug_func(str, 0); } +#ifdef LEVITTE_DEBUG + fprintf(stderr, "LEVITTE_DEBUG: < 0x%p\n", str); +#endif + free_func(str); + if(free_debug_func != NULL) + { free_debug_func(NULL, 1); } +} + +void *CRYPTO_remalloc(void *a, int num) +{ + if(a != NULL) { OPENSSL_free(a); } + a = (char *)OPENSSL_malloc(num); + return (a); +} + + +void CRYPTO_set_mem_debug_options(long bits) +{ + if(set_debug_options_func != NULL) + { set_debug_options_func(bits); } +} + +long CRYPTO_get_mem_debug_options(void) +{ + if(get_debug_options_func != NULL) + { return get_debug_options_func(); } + return 0; +} +#endif diff --git a/cscrypt/openssl_mods.h b/cscrypt/openssl_mods.h new file mode 100644 index 0000000..2ef3724 --- /dev/null +++ b/cscrypt/openssl_mods.h @@ -0,0 +1,11 @@ +/* openssl_mods.h */ +#ifndef _OPENSSL_MODSH +#define _OPENSSL_MODSH + +#define OPENSSL_malloc(num) CRYPTO_malloc((int)num,__FILE__,__LINE__) +#define OPENSSL_free(addr) CRYPTO_free(addr) + +void *CRYPTO_malloc(int num, const char *file, int line); +void CRYPTO_free(void *); + +#endif diff --git a/cscrypt/rc6.c b/cscrypt/rc6.c new file mode 100644 index 0000000..4a561c4 --- /dev/null +++ b/cscrypt/rc6.c @@ -0,0 +1,224 @@ +/* rc6 (TM) + * Unoptimized sample implementation of Ron Rivest's submission to the + * AES bakeoff. + * + * Salvo Salasio, 19 June 1998 + * + * Intellectual property notes: The name of the algorithm (RC6) is + * trademarked; any property rights to the algorithm or the trademark + * should be discussed with discussed with the authors of the defining + * paper "The RC6(TM) Block Cipher": Ronald L. Rivest (MIT), + * M.J.B. Robshaw (RSA Labs), R. Sidney (RSA Labs), and Y.L. Yin (RSA Labs), + * distributed 18 June 1998 and available from the lead author's web site. + * + * This sample implementation is placed in the public domain by the author, + * Salvo Salasio. The ROTL and ROTR definitions were cribbed from RSA Labs' + * RC5 reference implementation. + */ + +#include +#include "rc6.h" + +#define w 32 /* word size in bits */ +#define r 20 /* based on security estimates */ + +#define P32 0xB7E15163 /* Magic constants for key setup */ +#define Q32 0x9E3779B9 + +/* derived constants */ +#define bytes (w / 8) /* bytes per word */ +#define c ((b + bytes - 1) / bytes) /* key in words, rounded up */ +#define R24 (2 * r + 4) +#define lgw 5 /* log2(w) -- wussed out */ + +/* Rotations */ +#define ROTL(x,y) (((x)<<((y)&(w-1))) | ((x)>>(w-((y)&(w-1))))) +#define ROTR(x,y) (((x)>>((y)&(w-1))) | ((x)<<(w-((y)&(w-1))))) + +void rc6_key_setup(unsigned char *K, int b, RC6KEY S) +{ + int i, j, s, v; + unsigned int L[(32 + bytes - 1) / bytes]; /* Big enough for max b */ + unsigned int A, B; + + L[c - 1] = 0; + for(i = b - 1; i >= 0; i--) + { L[i / bytes] = (L[i / bytes] << 8) + K[i]; } + + S[0] = P32; + for(i = 1; i <= 2 * r + 3; i++) + { S[i] = S[i - 1] + Q32; } + + A = B = i = j = 0; + v = R24; + if(c > v) { v = c; } + v *= 3; + + for(s = 1; s <= v; s++) + { + A = S[i] = ROTL(S[i] + A + B, 3); + B = L[j] = ROTL(L[j] + A + B, A + B); + i = (i + 1) % R24; + j = (j + 1) % c; + } +} + +void rc6_block_encrypt(unsigned int *pt, unsigned int *ct, int block_count, RC6KEY S) +{ + unsigned int A, B, C, D, t, u, x; + int i; + + while(block_count > 0) + { + A = pt[0]; + B = pt[1]; + C = pt[2]; + D = pt[3]; + B += S[0]; + D += S[1]; + for(i = 2; i <= 2 * r; i += 2) + { + t = ROTL(B * (2 * B + 1), lgw); + u = ROTL(D * (2 * D + 1), lgw); + A = ROTL(A ^ t, u) + S[i]; + C = ROTL(C ^ u, t) + S[i + 1]; + x = A; + A = B; + B = C; + C = D; + D = x; + } + A += S[2 * r + 2]; + C += S[2 * r + 3]; + ct[0] = A; + ct[1] = B; + ct[2] = C; + ct[3] = D; + + block_count--; + pt++; + ct++; + } +} + +void rc6_block_decrypt(unsigned int *ct, unsigned int *pt, int block_count, RC6KEY S) +{ + unsigned int A, B, C, D, t, u, x; + int i; + + while(block_count > 0) + { + A = ct[0]; + B = ct[1]; + C = ct[2]; + D = ct[3]; + C -= S[2 * r + 3]; + A -= S[2 * r + 2]; + for(i = 2 * r; i >= 2; i -= 2) + { + x = D; + D = C; + C = B; + B = A; + A = x; + u = ROTL(D * (2 * D + 1), lgw); + t = ROTL(B * (2 * B + 1), lgw); + C = ROTR(C - S[i + 1], t) ^ u; + A = ROTR(A - S[i], u) ^ t; + } + D -= S[1]; + B -= S[0]; + pt[0] = A; + pt[1] = B; + pt[2] = C; + pt[3] = D; + + block_count--; + ct++; + pt++; + } +} + +/* +struct test_struct +{ + int keylen; + unsigned char key[32]; + unsigned int pt[4]; + unsigned int ct[4]; +} tests[] = +{ + { 16, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x36a5c38f, 0x78f7b156, 0x4edf29c1, 0x1ea44898}, + }, + + { 16, {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78}, + {0x35241302, 0x79685746, 0xbdac9b8a, 0xf1e0dfce}, + {0x2f194e52, 0x23c61547, 0x36f6511f, 0x183fa47e}, + }, + + { 24, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0xcb1bd66c, 0x38300b19, 0x163f8a4e, 0x82ae9086}, + }, + + { 24, {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, + 0x89, 0x9a, 0xab, 0xbc, 0xcd, 0xde, 0xef, 0xf0}, + {0x35241302, 0x79685746, 0xbdac9b8a, 0xf1e0dfce}, + {0xd0298368, 0x0405e519, 0x2ae9521e, 0xd49152f9}, + }, + + { 32, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x05bd5f8f, 0xa85fd110, 0xda3ffa93, 0xc27e856e}, + }, + + { 32, {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, + 0x89, 0x9a, 0xab, 0xbc, 0xcd, 0xde, 0xef, 0xf0, + 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe}, + {0x35241302, 0x79685746, 0xbdac9b8a, 0xf1e0dfce}, + {0x161824c8, 0x89e4d7f0, 0xa116ad20, 0x485d4e67}, + }, + + { 0, + } +}; + +int +main() +{ + unsigned int ct[4], pt[4]; + int i; + struct test_struct *p; + RC6KEY S; + + for (p = tests, i = 1; p->keylen; p++, i++) + { + + rc6_key_setup(p->key, p->keylen, &S); + rc6_block_encrypt(p->pt, ct, &S); + printf("Test %d: %08x %08x %08x %08x\n", + i, ct[0], ct[1], ct[2], ct[3]); + printf("Should be: %08x %08x %08x %08x\n", + p->ct[0], p->ct[1], p->ct[2], p->ct[3]); + rc6_block_decrypt(ct, pt, &S); + printf("Plain: %08x %08x %08x %08x\n", + pt[0], pt[1], pt[2], pt[3]); + printf("Should be: %08x %08x %08x %08x\n\n", + p->pt[0], p->pt[1], p->pt[2], p->pt[3]); + } + + return 0; +} +*/ + diff --git a/cscrypt/rc6.h b/cscrypt/rc6.h new file mode 100644 index 0000000..1fe29a4 --- /dev/null +++ b/cscrypt/rc6.h @@ -0,0 +1,38 @@ +/* rc6 (TM) +* Unoptimized sample implementation of Ron Rivest's submission to the +* AES bakeoff. +* +* Salvo Salasio, 19 June 1998 +* +* Intellectual property notes: The name of the algorithm (RC6) is +* trademarked; any property rights to the algorithm or the trademark +* should be discussed with discussed with the authors of the defining +* paper "The RC6(TM) Block Cipher": Ronald L. Rivest (MIT), +* M.J.B. Robshaw (RSA Labs), R. Sidney (RSA Labs), and Y.L. Yin (RSA Labs), +* distributed 18 June 1998 and available from the lead author's web site. +* +* This sample implementation is placed in the public domain by the author, +* Salvo Salasio. The ROTL and ROTR definitions were cribbed from RSA Labs' +* RC5 reference implementation. +*/ + +/* RC6 is parameterized for w-bit words, b bytes of key, and + * r rounds. The AES version of RC6 specifies b=16, 24, or 32; + * w=32; and r=20. + */ + +#define rc6keylen 44 + +typedef unsigned int RC6KEY[rc6keylen]; + +/* + * K=plain key + * b=plain key len + * S=prepared key by setup + * pt=uncrypted data + * ct=crypted data + * block size is always 16bytes + */ +void rc6_key_setup(unsigned char *K, int b, RC6KEY S); +void rc6_block_encrypt(unsigned int *pt, unsigned int *ct, int block_count, RC6KEY S); +void rc6_block_decrypt(unsigned int *ct, unsigned int *pt, int block_count, RC6KEY S); diff --git a/cscrypt/sha1.c b/cscrypt/sha1.c new file mode 100644 index 0000000..dfabea8 --- /dev/null +++ b/cscrypt/sha1.c @@ -0,0 +1,320 @@ +#ifndef WITH_LIBCRYPTO +//FIXME Not checked on threadsafety yet; after checking please remove this line +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid +Still 100% public domain + +1- Removed #include and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 07/2002 +By Ralph Giles +Still 100% public domain +modified for use with stdint types, autoconf +code cleanup, removed attribution comments +switched SHA1Final() argument order for consistency +use SHA1_ prefix for public api +move public api to sha1.h +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define SHA1HANDSOFF */ + +#include +#include + +#include "sha1.h" + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +/* FIXME: can we do this in an endian-proof way? */ +#if __BYTE_ORDER == __BIG_ENDIAN +#define blk0(i) block->l[i] +#else +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +#ifdef VERBOSE /* SAK */ +void SHAPrintContext(SHA_CTX *context, char *msg) +{ + printf("%s (%d,%d) %x %x %x %x %x\n", + msg, + context->count[0], context->count[1], + context->state[0], + context->state[1], + context->state[2], + context->state[3], + context->state[4]); +} +#endif /* VERBOSE */ + +/* Hash a single 512-bit block. This is the core of the algorithm. */ +void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) +{ + uint32_t a = 0, b = 0, c = 0, d = 0, e = 0; + + typedef union + { + uint8_t c[64]; + uint32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16 *block; + +#ifdef SHA1HANDSOFF + static uint8_t workspace[64]; + block = (CHAR64LONG16 *)workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16 *)buffer; +#endif + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a, b, c, d, e, 0); + R0(e, a, b, c, d, 1); + R0(d, e, a, b, c, 2); + R0(c, d, e, a, b, 3); + R0(b, c, d, e, a, 4); + R0(a, b, c, d, e, 5); + R0(e, a, b, c, d, 6); + R0(d, e, a, b, c, 7); + R0(c, d, e, a, b, 8); + R0(b, c, d, e, a, 9); + R0(a, b, c, d, e, 10); + R0(e, a, b, c, d, 11); + R0(d, e, a, b, c, 12); + R0(c, d, e, a, b, 13); + R0(b, c, d, e, a, 14); + R0(a, b, c, d, e, 15); + R1(e, a, b, c, d, 16); + R1(d, e, a, b, c, 17); + R1(c, d, e, a, b, 18); + R1(b, c, d, e, a, 19); + R2(a, b, c, d, e, 20); + R2(e, a, b, c, d, 21); + R2(d, e, a, b, c, 22); + R2(c, d, e, a, b, 23); + R2(b, c, d, e, a, 24); + R2(a, b, c, d, e, 25); + R2(e, a, b, c, d, 26); + R2(d, e, a, b, c, 27); + R2(c, d, e, a, b, 28); + R2(b, c, d, e, a, 29); + R2(a, b, c, d, e, 30); + R2(e, a, b, c, d, 31); + R2(d, e, a, b, c, 32); + R2(c, d, e, a, b, 33); + R2(b, c, d, e, a, 34); + R2(a, b, c, d, e, 35); + R2(e, a, b, c, d, 36); + R2(d, e, a, b, c, 37); + R2(c, d, e, a, b, 38); + R2(b, c, d, e, a, 39); + R3(a, b, c, d, e, 40); + R3(e, a, b, c, d, 41); + R3(d, e, a, b, c, 42); + R3(c, d, e, a, b, 43); + R3(b, c, d, e, a, 44); + R3(a, b, c, d, e, 45); + R3(e, a, b, c, d, 46); + R3(d, e, a, b, c, 47); + R3(c, d, e, a, b, 48); + R3(b, c, d, e, a, 49); + R3(a, b, c, d, e, 50); + R3(e, a, b, c, d, 51); + R3(d, e, a, b, c, 52); + R3(c, d, e, a, b, 53); + R3(b, c, d, e, a, 54); + R3(a, b, c, d, e, 55); + R3(e, a, b, c, d, 56); + R3(d, e, a, b, c, 57); + R3(c, d, e, a, b, 58); + R3(b, c, d, e, a, 59); + R4(a, b, c, d, e, 60); + R4(e, a, b, c, d, 61); + R4(d, e, a, b, c, 62); + R4(c, d, e, a, b, 63); + R4(b, c, d, e, a, 64); + R4(a, b, c, d, e, 65); + R4(e, a, b, c, d, 66); + R4(d, e, a, b, c, 67); + R4(c, d, e, a, b, 68); + R4(b, c, d, e, a, 69); + R4(a, b, c, d, e, 70); + R4(e, a, b, c, d, 71); + R4(d, e, a, b, c, 72); + R4(c, d, e, a, b, 73); + R4(b, c, d, e, a, 74); + R4(a, b, c, d, e, 75); + R4(e, a, b, c, d, 76); + R4(d, e, a, b, c, 77); + R4(c, d, e, a, b, 78); + R4(b, c, d, e, a, 79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + +} + + +/* SHA1Init - Initialize new context */ +void SHA1_Init(SHA_CTX *context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ +void SHA1_Update(SHA_CTX *context, const uint8_t *data, const size_t len) +{ + size_t i, j; + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + + j = (context->count[0] >> 3) & 63; + if((context->count[0] += len << 3) < (len << 3)) { context->count[1]++; } + context->count[1] += (len >> 29); + if((j + len) > 63) + { + memcpy(&context->buffer[j], data, (i = 64 - j)); + SHA1_Transform(context->state, context->buffer); + for(; i + 63 < len; i += 64) + { + SHA1_Transform(context->state, data + i); + } + j = 0; + } + else { i = 0; } + memcpy(&context->buffer[j], &data[i], len - i); + +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + + +/* Add padding and return the message digest. */ +void SHA1_Final(uint8_t digest[SHA_DIGEST_LENGTH], SHA_CTX *context) +{ + uint32_t i; + uint8_t finalcount[8]; + + for(i = 0; i < 8; i++) + { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ + } + SHA1_Update(context, (uint8_t *)"\200", 1); + while((context->count[0] & 504) != 448) + { + SHA1_Update(context, (uint8_t *)"\0", 1); + } + SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ + for(i = 0; i < SHA_DIGEST_LENGTH; i++) + { + digest[i] = (uint8_t) + ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); + } + + /* Wipe variables */ + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(finalcount, 0, 8); /* SWR */ + +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ + SHA1_Transform(context->state, context->buffer); +#endif +} +#endif diff --git a/cscrypt/sha1.h b/cscrypt/sha1.h new file mode 100644 index 0000000..e5c8461 --- /dev/null +++ b/cscrypt/sha1.h @@ -0,0 +1,30 @@ +#if defined(WITH_SSL) || defined(WITH_LIBCRYPTO) +# include +#else +/* public api for steve reid's public domain SHA-1 implementation */ +/* this file is in the public domain */ + +#ifndef __SHA1_H +#define __SHA1_H + +#include +#include + +typedef struct +{ + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[64]; +} SHA_CTX; + +#define SHA_DIGEST_LENGTH 20 + +void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); + +void SHA1_Init(SHA_CTX *context); +void SHA1_Update(SHA_CTX *context, const uint8_t *data, const size_t len); +void SHA1_Final(uint8_t digest[SHA_DIGEST_LENGTH], SHA_CTX *context); + +#endif /* __SHA1_H */ + +#endif diff --git a/cscrypt/sha256.c b/cscrypt/sha256.c new file mode 100644 index 0000000..68ba3b5 --- /dev/null +++ b/cscrypt/sha256.c @@ -0,0 +1,317 @@ +/* + * FIPS-180-2 compliant SHA-256 implementation + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +/* + * The SHA-256 Secure Hash Standard was published by NIST in 2002. + * + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + */ + +#include "sha256.h" +#include + +/* Implementation that should never be optimized out by the compiler */ +static void mbedtls_zeroize( void *v, size_t n ) +{ + volatile unsigned char *p = v; + while( n-- ) *p++ = 0; +} + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +do { \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} while( 0 ) +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +do { \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} while( 0 ) +#endif + +void mbedtls_sha256_init( mbedtls_sha256_context *ctx ) +{ + memset( ctx, 0, sizeof( mbedtls_sha256_context ) ); +} + +void mbedtls_sha256_free( mbedtls_sha256_context *ctx ) +{ + if( ctx == NULL ) + return; + + mbedtls_zeroize( ctx, sizeof( mbedtls_sha256_context ) ); +} + +void mbedtls_sha256_clone( mbedtls_sha256_context *dst, const mbedtls_sha256_context *src ) +{ + *dst = *src; +} + +/* + * SHA-256 context setup + */ +void mbedtls_sha256_starts( mbedtls_sha256_context *ctx, int is224 ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + if( is224 == 0 ) + { + /* SHA-256 */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; + } + else + { + /* SHA-224 */ + ctx->state[0] = 0xC1059ED8; + ctx->state[1] = 0x367CD507; + ctx->state[2] = 0x3070DD17; + ctx->state[3] = 0xF70E5939; + ctx->state[4] = 0xFFC00B31; + ctx->state[5] = 0x68581511; + ctx->state[6] = 0x64F98FA7; + ctx->state[7] = 0xBEFA4FA4; + } + + ctx->is224 = is224; +} + +#if !defined(MBEDTLS_SHA256_PROCESS_ALT) +static const uint32_t K[] = +{ + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2, +}; + +#define SHR(x,n) ((x & 0xFFFFFFFF) >> n) +#define ROTR(x,n) (SHR(x,n) | (x << (32 - n))) + +#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3)) +#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10)) + +#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22)) +#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25)) + +#define F0(x,y,z) ((x & y) | (z & (x | y))) +#define F1(x,y,z) (z ^ (x & (y ^ z))) + +#define R(t) \ +( \ + W[t] = S1(W[t - 2]) + W[t - 7] + \ + S0(W[t - 15]) + W[t - 16] \ +) + +#define P(a,b,c,d,e,f,g,h,x,K) \ +{ \ + temp1 = h + S3(e) + F1(e,f,g) + K + x; \ + temp2 = S2(a) + F0(a,b,c); \ + d += temp1; h = temp1 + temp2; \ +} + +void mbedtls_sha256_process( mbedtls_sha256_context *ctx, const unsigned char data[64] ) +{ + uint32_t temp1, temp2, W[64]; + uint32_t A[8]; + unsigned int i; + + for( i = 0; i < 8; i++ ) + A[i] = ctx->state[i]; + +#if defined(MBEDTLS_SHA256_SMALLER) + for( i = 0; i < 64; i++ ) + { + if( i < 16 ) + GET_UINT32_BE( W[i], data, 4 * i ); + else + R( i ); + + P( A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[i], K[i] ); + + temp1 = A[7]; + A[7] = A[6]; + A[6] = A[5]; + A[5] = A[4]; + A[4] = A[3]; + A[3] = A[2]; + A[2] = A[1]; + A[1] = A[0]; + A[0] = temp1; + } +#else /* MBEDTLS_SHA256_SMALLER */ + for( i = 0; i < 16; i++ ) + GET_UINT32_BE( W[i], data, 4 * i ); + + for( i = 0; i < 16; i += 8 ) + { + P( A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[i+0], K[i+0] ); + P( A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], W[i+1], K[i+1] ); + P( A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], W[i+2], K[i+2] ); + P( A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], W[i+3], K[i+3] ); + P( A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], W[i+4], K[i+4] ); + P( A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], W[i+5], K[i+5] ); + P( A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], W[i+6], K[i+6] ); + P( A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], W[i+7], K[i+7] ); + } + + for( i = 16; i < 64; i += 8 ) + { + P( A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], R(i+0), K[i+0] ); + P( A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], R(i+1), K[i+1] ); + P( A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], R(i+2), K[i+2] ); + P( A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], R(i+3), K[i+3] ); + P( A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], R(i+4), K[i+4] ); + P( A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], R(i+5), K[i+5] ); + P( A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], R(i+6), K[i+6] ); + P( A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], R(i+7), K[i+7] ); + } +#endif /* MBEDTLS_SHA256_SMALLER */ + + for( i = 0; i < 8; i++ ) + ctx->state[i] += A[i]; +} +#endif /* !MBEDTLS_SHA256_PROCESS_ALT */ + +/* + * SHA-256 process buffer + */ +void mbedtls_sha256_update( mbedtls_sha256_context *ctx, const unsigned char *input, size_t ilen ) +{ + size_t fill; + uint32_t left; + + if( ilen == 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (uint32_t) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), input, fill ); + mbedtls_sha256_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + mbedtls_sha256_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + memcpy( (void *) (ctx->buffer + left), input, ilen ); +} + +static const unsigned char sha256_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * SHA-256 final digest + */ +void mbedtls_sha256_finish( mbedtls_sha256_context *ctx, unsigned char output[32] ) +{ + uint32_t last, padn; + uint32_t high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32_BE( high, msglen, 0 ); + PUT_UINT32_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + mbedtls_sha256_update( ctx, sha256_padding, padn ); + mbedtls_sha256_update( ctx, msglen, 8 ); + + PUT_UINT32_BE( ctx->state[0], output, 0 ); + PUT_UINT32_BE( ctx->state[1], output, 4 ); + PUT_UINT32_BE( ctx->state[2], output, 8 ); + PUT_UINT32_BE( ctx->state[3], output, 12 ); + PUT_UINT32_BE( ctx->state[4], output, 16 ); + PUT_UINT32_BE( ctx->state[5], output, 20 ); + PUT_UINT32_BE( ctx->state[6], output, 24 ); + + if( ctx->is224 == 0 ) + PUT_UINT32_BE( ctx->state[7], output, 28 ); +} + +/* + * output = SHA-256( input buffer ) + */ +void mbedtls_sha256( const unsigned char *input, size_t ilen, unsigned char output[32], int is224 ) +{ + mbedtls_sha256_context ctx; + + mbedtls_sha256_init( &ctx ); + mbedtls_sha256_starts( &ctx, is224 ); + mbedtls_sha256_update( &ctx, input, ilen ); + mbedtls_sha256_finish( &ctx, output ); + mbedtls_sha256_free( &ctx ); +} diff --git a/cscrypt/sha256.h b/cscrypt/sha256.h new file mode 100644 index 0000000..2ed95cd --- /dev/null +++ b/cscrypt/sha256.h @@ -0,0 +1,125 @@ +/** + * \file mbedtls_sha256.h + * + * \brief SHA-224 and SHA-256 cryptographic hash function + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +#ifndef MBEDTLS_SHA256_H +#define MBEDTLS_SHA256_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief SHA-256 context structure + */ +typedef struct +{ + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[8]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + int is224; /*!< 0 => SHA-256, else SHA-224 */ +} +mbedtls_sha256_context; + +/** + * \brief Initialize SHA-256 context + * + * \param ctx SHA-256 context to be initialized + */ +void mbedtls_sha256_init( mbedtls_sha256_context *ctx ); + +/** + * \brief Clear SHA-256 context + * + * \param ctx SHA-256 context to be cleared + */ +void mbedtls_sha256_free( mbedtls_sha256_context *ctx ); + +/** + * \brief Clone (the state of) a SHA-256 context + * + * \param dst The destination context + * \param src The context to be cloned + */ +void mbedtls_sha256_clone( mbedtls_sha256_context *dst, + const mbedtls_sha256_context *src ); + +/** + * \brief SHA-256 context setup + * + * \param ctx context to be initialized + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void mbedtls_sha256_starts( mbedtls_sha256_context *ctx, int is224 ); + +/** + * \brief SHA-256 process buffer + * + * \param ctx SHA-256 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void mbedtls_sha256_update( mbedtls_sha256_context *ctx, const unsigned char *input, size_t ilen ); + +/** + * \brief SHA-256 final digest + * + * \param ctx SHA-256 context + * \param output SHA-224/256 checksum result + */ +void mbedtls_sha256_finish( mbedtls_sha256_context *ctx, unsigned char output[32] ); + +/* Internal use */ +void mbedtls_sha256_process( mbedtls_sha256_context *ctx, const unsigned char data[64] ); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Output = SHA-256( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output SHA-224/256 checksum result + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void mbedtls_sha256( const unsigned char *input, size_t ilen, unsigned char output[32], int is224 ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mbedtls_sha256_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* mbedtls_sha256.h */ diff --git a/csctapi/CMakeLists.txt b/csctapi/CMakeLists.txt new file mode 100644 index 0000000..2ee4cf8 --- /dev/null +++ b/csctapi/CMakeLists.txt @@ -0,0 +1,62 @@ +project (csctapi) +if (CMAKE_COMPILER_IS_GNUCC) + execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + string(REGEX MATCHALL "[0-9]+" GCC_VERSION_COMPONENTS ${GCC_VERSION}) + list(GET GCC_VERSION_COMPONENTS 0 GCC_MAJOR) + list(GET GCC_VERSION_COMPONENTS 0 GCC_MINOR) + add_definitions ("-W -Wall ") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O2") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O2") + set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -ggdb") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ggdb") +endif (CMAKE_COMPILER_IS_GNUCC) +# Mac extra removes to avoid ranlib warnings in some situations +if (OSCamOperatingSystem MATCHES "Mac OS X" AND NOT HAVE_LIBUSB AND HAVE_PCSC) +set (mac_usbpcsc "ifd_smartreader*") +elseif(OSCamOperatingSystem MATCHES "Mac OS X" AND NOT HAVE_LIBUSB AND NOT HAVE_PCSC) +set (mac_usbpcsc "ifd_smartreader*" "ifd_pcsc*") +elseif (OSCamOperatingSystem MATCHES "Mac OS X" AND HAVE_LIBUSB AND NOT HAVE_PCSC) +set (mac_usbpcsc "ifd_pcsc*") +endif(OSCamOperatingSystem MATCHES "Mac OS X" AND NOT HAVE_LIBUSB AND HAVE_PCSC) +if (OSCamOperatingSystem MATCHES "Mac OS X") +file (GLOB csctapi_mac "ifd_azbox*" "ifd_cool*" "ifd_stapi*" ${mac_usbpcsc}) +file (GLOB csctapi_srcs "*.c") +list(REMOVE_ITEM csctapi_srcs ${csctapi_mac}) +file (GLOB csctapi_hdrs "*.h") +list(REMOVE_ITEM csctapi_hdrs ${csctapi_mac}) +else (OSCamOperatingSystem MATCHES "Mac OS X") +file (GLOB csctapi_srcs "*.c") +file (GLOB csctapi_hdrs "*.h") +endif (OSCamOperatingSystem MATCHES "Mac OS X") + +if (NOT OSCamOperatingSystem MATCHES "Mac OS X") + if (LIBRTDIR) + check_include_file ("${LIBRTDIR}/include/time.h" HAVE_LIBRT_STATIC) + if (HAVE_LIBRT_STATIC) + if (EXISTS ${LIBRTDIR}/lib/librt.a) + add_definitions ("-I${LIBRTDIR}/include/") + else (EXISTS ${LIBRTDIR}/lib/librt.a) + set (HAVE_LIBRT_STATIC False) + endif (EXISTS ${LIBRTDIR}/lib/librt.a) + elseif (HAVE_LIBRT_STATIC) + set (HAVE_LIBRT_STATIC False) + endif (HAVE_LIBRT_STATIC) + else (LIBRTDIR) + check_include_file ("time.h" HAVE_LIBRT) + if (HAVE_LIBRT) + set (HAVE_LIBRT True) + else (HAVE_LIBRT) + set (HAVE_LIBRT False) + endif (HAVE_LIBRT) + endif (LIBRTDIR) +else (NOT OSCamOperatingSystem MATCHES "Mac OS X") + set (HAVE_LIBRT True) +endif (NOT OSCamOperatingSystem MATCHES "Mac OS X") + +check_include_file ("libusb-1.0/libusb.h" HAVE_LIBUSB) +if (HAVE_LIBUSB AND HAVE_PTHREAD AND HAVE_LIBRT OR HAVE_LIBRT_STATIC) + add_definitions ("-DWITH_LIBUSB=1") +endif (HAVE_LIBUSB AND HAVE_PTHREAD AND HAVE_LIBRT OR HAVE_LIBRT_STATIC) + +set (lib_name "csctapi") +add_library (${lib_name} STATIC ${csctapi_srcs} ${csctapi_hdrs}) diff --git a/csctapi/Makefile b/csctapi/Makefile new file mode 100644 index 0000000..562f00b --- /dev/null +++ b/csctapi/Makefile @@ -0,0 +1,2 @@ +parent: +@$(MAKE) --no-print-directory -C .. diff --git a/csctapi/atr.c b/csctapi/atr.c new file mode 100644 index 0000000..e804804 --- /dev/null +++ b/csctapi/atr.c @@ -0,0 +1,453 @@ +/* + atr.c + ISO 7816 ICC's answer to reset abstract data type implementation + + This file is part of the Unix driver for Towitoko smartcard readers + Copyright (C) 2000 Carlos Prados + + This version is modified by doz21 to work in a special manner ;) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include "../globals.h" +#include "atr.h" +#define ERROR 1 + +const uint32_t atr_fs_table[16] = {5000000L, 5000000L, 6000000L, 8000000L, 12000000L, 16000000L, 20000000L, 0, 0, 5000000L, 7500000L, 10000000L, 15000000L, 20000000L, 0, 0}; +const uint32_t atr_f_table[16] = {372, 372, 558, 744, 1116, 1488, 1860, 0, 0, 512, 768, 1024, 1536, 2048, 0, 0}; +const double atr_d_table[16] = {0, 1, 2, 4, 8, 16, 32, 64, 12, 20, 0, 0, 0, 0, 0, 0}; + +#ifdef WITH_CARDREADER +int32_t ATR_InitFromArray(ATR *atr, const unsigned char atr_buffer[ATR_MAX_SIZE], uint32_t length) +{ + unsigned char TDi; + unsigned char buffer[ATR_MAX_SIZE] = "\x00"; + uint32_t pointer = 0, pn = 0; + static const uint32_t atr_num_ib_table[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; + + /* Check size of buffer */ + if(length < 2) + { + cs_log_dbg(D_ATR, "ERROR: this ATR length is %d and minimum length is 2", length); + return (ERROR); + } + + /* Check if ATR is from a inverse convention card */ + if(atr_buffer[0] == 0x03) // Readers of type R_MOUSE need this in case of inverse convention cards! + { + for(pointer = 0; pointer < length; pointer++) + { buffer[pointer] = ~(INVERT_BYTE(atr_buffer[pointer])); } + } + else + { + memcpy(buffer, atr_buffer, length); + } + + /* Store T0 and TS */ + atr->TS = buffer[0]; + + atr->T0 = TDi = buffer[1]; + pointer = 1; + + /* Store number of historical bytes */ + atr->hbn = TDi & 0x0F; + + /* TCK is not present by default */ + (atr->TCK).present = 0; + + /* Extract interface bytes */ + while(pointer < length) + { + /* Check buffer is long enought */ + if(pointer + atr_num_ib_table[(0xF0 & TDi) >> 4] >= length) + { + cs_log_dbg(D_ATR, "ERROR: this ATR the %d interface bytes for protocol %d are missing", pointer + atr_num_ib_table[(0xF0 & TDi) >> 4], pn + 1); + return (ERROR); + } + + /* Check TAi is present */ + if((TDi | 0xEF) == 0xFF) + { + pointer++; + atr->ib[pn][ATR_INTERFACE_BYTE_TA].value = buffer[pointer]; + atr->ib[pn][ATR_INTERFACE_BYTE_TA].present = 1; + } + else + { + atr->ib[pn][ATR_INTERFACE_BYTE_TA].present = 0; + } + + /* Check TBi is present */ + if((TDi | 0xDF) == 0xFF) + { + pointer++; + atr->ib[pn][ATR_INTERFACE_BYTE_TB].value = buffer[pointer]; + atr->ib[pn][ATR_INTERFACE_BYTE_TB].present = 1; + } + else + { + atr->ib[pn][ATR_INTERFACE_BYTE_TB].present = 0; + } + + /* Check TCi is present */ + if((TDi | 0xBF) == 0xFF) + { + pointer++; + atr->ib[pn][ATR_INTERFACE_BYTE_TC].value = buffer[pointer]; + atr->ib[pn][ATR_INTERFACE_BYTE_TC].present = 1; + } + else + { + atr->ib[pn][ATR_INTERFACE_BYTE_TC].present = 0; + } + + /* Read TDi if present */ + if((TDi | 0x7F) == 0xFF) + { + pointer++; + TDi = atr->ib[pn][ATR_INTERFACE_BYTE_TD].value = buffer[pointer]; + atr->ib[pn][ATR_INTERFACE_BYTE_TD].present = 1; + (atr->TCK).present = ((TDi & 0x0F) != ATR_PROTOCOL_TYPE_T0); + if(pn >= ATR_MAX_PROTOCOLS) + { + cs_log_dbg(D_ATR, "ERROR: this ATR reports %d protocols but the maximum value is %d", pn + 1, ATR_MAX_PROTOCOLS + 1); + return (ERROR); + } + pn++; + } + else + { + atr->ib[pn][ATR_INTERFACE_BYTE_TD].present = 0; + break; + } + } + + /* Store number of protocols */ + atr->pn = pn + 1; + + /* Store historical bytes */ + if(pointer + atr->hbn >= length) + { + cs_log_dbg(D_ATR, "ERROR: this ATR reports %i historical bytes but there are only %i", atr->hbn, length - pointer - 2); + if(length - pointer >= 2) + { atr->hbn = length - pointer - 2; } + else + { + atr->hbn = 0; + atr->length = pointer + 1; + return (ERROR); + } + + } + + memcpy(atr->hb, buffer + pointer + 1, atr->hbn); + pointer += (atr->hbn); + + /* Store TCK */ + if((atr->TCK).present) + { + if(pointer + 1 >= length) + { + cs_log_dbg(D_ATR, "ATR is malformed, this ATR should have a TCK byte but it was not received!"); + return (ATR_MALFORMED); + } + pointer++; + + (atr->TCK).value = buffer[pointer]; + } + + atr->length = pointer + 1; + + // check that TA1, if pn==1 , has a valid value for FI + if(atr->pn == 1 && atr->ib[pn][ATR_INTERFACE_BYTE_TA].present == 1) + { + uint8_t FI; + cs_log_dbg(D_ATR, "TA1 = %02x", atr->ib[pn][ATR_INTERFACE_BYTE_TA].value); + FI = (atr->ib[pn][ATR_INTERFACE_BYTE_TA].value & 0xF0) >> 4; + cs_log_dbg(D_ATR, "FI = %02x", FI); + if(atr_fs_table[FI] == 0) + { + cs_log_dbg(D_ATR, "ERROR: this ATR FI for protocol %d is not returning a valid cardfrequency value", pn + 1); + return (ERROR); + } + } + + // check that TB1 < 0x80 + if(atr->pn == 1 && atr->ib[pn][ATR_INTERFACE_BYTE_TB].present == 1) + { + if(atr->ib[pn][ATR_INTERFACE_BYTE_TB].value > 0x80) + { + cs_log_dbg(D_ATR, "ERROR: this ATR TB1 for protocol %d has an invalid value", pn + 1); + return (ERROR); + } + } + return (ATR_OK); +} + +int32_t ATR_GetConvention(ATR *atr, int32_t *convention) +{ + if(atr->TS == 0x3B || atr->TS == 0xDB) + { (*convention) = ATR_CONVENTION_DIRECT; } + else if(atr->TS == 0x3F) + { (*convention) = ATR_CONVENTION_INVERSE; } + else + { + cs_log_dbg(D_ATR, "ERROR: this ATR TS byte is %02X and that should be 3B for direct or 3F for inverse convention!", atr->TS); + return (ERROR); + } + + return (ATR_OK); +} + +int32_t ATR_GetSize(ATR *atr, uint32_t *size) +{ + (*size) = atr->length; + return (ATR_OK); +} + +int32_t ATR_GetNumberOfProtocols(ATR *atr, uint32_t *number_protocols) +{ + (*number_protocols) = atr->pn; + return (ATR_OK); +} + +int32_t ATR_GetProtocolType(ATR *atr, uint32_t number_protocol, unsigned char *protocol_type) +{ + if((number_protocol > atr->pn) || number_protocol < 1) + { return ATR_NOT_FOUND; } + + if(atr->ib[number_protocol - 1][ATR_INTERFACE_BYTE_TD].present) + { (*protocol_type) = (atr->ib[number_protocol - 1][ATR_INTERFACE_BYTE_TD].value & 0x0F); } + else + { (*protocol_type) = ATR_PROTOCOL_TYPE_T0; } + + return (ATR_OK); +} + +int32_t ATR_GetInterfaceByte(ATR *atr, uint32_t number, int32_t character, unsigned char *value) +{ + if(number > atr->pn || number < 1) + { return (ATR_NOT_FOUND); } + + if(atr->ib[number - 1][character].present && (character == ATR_INTERFACE_BYTE_TA || character == ATR_INTERFACE_BYTE_TB || character == ATR_INTERFACE_BYTE_TC || character == ATR_INTERFACE_BYTE_TD)) + { (*value) = atr->ib[number - 1][character].value; } + else + { return (ATR_NOT_FOUND); } + + return (ATR_OK); +} + +int32_t ATR_GetIntegerValue(ATR *atr, int32_t name, unsigned char *value) +{ + int32_t ret; + + if(name == ATR_INTEGER_VALUE_FI) + { + if(atr->ib[0][ATR_INTERFACE_BYTE_TA].present) + { + (*value) = (atr->ib[0][ATR_INTERFACE_BYTE_TA].value & 0xF0) >> 4; + ret = ATR_OK; + } + else + { + ret = ATR_NOT_FOUND; + } + } + else if(name == ATR_INTEGER_VALUE_DI) + { + if(atr->ib[0][ATR_INTERFACE_BYTE_TA].present) + { + (*value) = (atr->ib[0][ATR_INTERFACE_BYTE_TA].value & 0x0F); + ret = ATR_OK; + } + else + { + ret = ATR_NOT_FOUND; + } + } + else if(name == ATR_INTEGER_VALUE_II) + { + if(atr->ib[0][ATR_INTERFACE_BYTE_TB].present) + { + (*value) = (atr->ib[0][ATR_INTERFACE_BYTE_TB].value & 0x60) >> 5; + ret = ATR_OK; + } + else + { + ret = ATR_NOT_FOUND; + } + } + else if(name == ATR_INTEGER_VALUE_PI1) + { + if(atr->ib[0][ATR_INTERFACE_BYTE_TB].present) + { + (*value) = (atr->ib[0][ATR_INTERFACE_BYTE_TB].value & 0x1F); + ret = ATR_OK; + } + else + { + ret = ATR_NOT_FOUND; + } + } + else if(name == ATR_INTEGER_VALUE_PI2) + { + if(atr->ib[1][ATR_INTERFACE_BYTE_TB].present) + { + (*value) = atr->ib[1][ATR_INTERFACE_BYTE_TB].value; + ret = ATR_OK; + } + else + { + ret = ATR_NOT_FOUND; + } + } + else if(name == ATR_INTEGER_VALUE_N) + { + if(atr->ib[0][ATR_INTERFACE_BYTE_TC].present) + { + (*value) = atr->ib[0][ATR_INTERFACE_BYTE_TC].value; + ret = ATR_OK; + } + else + { + ret = ATR_NOT_FOUND; + } + } + else + { + ret = ATR_NOT_FOUND; + } + + return ret; +} + +int32_t ATR_GetParameter(ATR *atr, int32_t name, uint32_t *parameter) +{ + unsigned char FI, DI, II, PI1, PI2, N; + static const uint32_t atr_i_table[4] = {25, 50, 100, 0}; + if(name == ATR_PARAMETER_F) + { + if(ATR_GetIntegerValue(atr, ATR_INTEGER_VALUE_FI, &FI) != ATR_OK) + { FI = ATR_DEFAULT_FI; } + (*parameter) = (double)(atr_f_table[FI]); + return (ATR_OK); + } + else if(name == ATR_PARAMETER_D) + { + if(ATR_GetIntegerValue(atr, ATR_INTEGER_VALUE_DI, &DI) == ATR_OK) + { (*parameter) = (double)(atr_d_table[DI]); } + else + { (*parameter) = (double) ATR_DEFAULT_D; } + return (ATR_OK); + } + else if(name == ATR_PARAMETER_I) + { + if(ATR_GetIntegerValue(atr, ATR_INTEGER_VALUE_II, &II) == ATR_OK) + { (*parameter) = (double)(atr_i_table[II]); } + else + { (*parameter) = ATR_DEFAULT_I; } + return (ATR_OK); + } + else if(name == ATR_PARAMETER_P) + { + if(ATR_GetIntegerValue(atr, ATR_INTEGER_VALUE_PI2, &PI2) == ATR_OK) + { (*parameter) = (double) PI2; } + else if(ATR_GetIntegerValue(atr, ATR_INTEGER_VALUE_PI1, &PI1) == ATR_OK) + { (*parameter) = (double) PI1; } + else + { (*parameter) = (double) ATR_DEFAULT_P; } + return (ATR_OK); + } + else if(name == ATR_PARAMETER_N) + { + if(ATR_GetIntegerValue(atr, ATR_INTEGER_VALUE_N, &N) == ATR_OK) + { (*parameter) = (double) N; } + else + { (*parameter) = (double) ATR_DEFAULT_N; } + return (ATR_OK); + } + + return (ATR_NOT_FOUND); +} + +int32_t ATR_GetHistoricalBytes(ATR *atr, unsigned char *hist, uint32_t *length) +{ + if(atr->hbn == 0) + { return (ATR_NOT_FOUND); } + + (*length) = atr->hbn; + memcpy(hist, atr->hb, atr->hbn); + return (ATR_OK); +} + +int32_t ATR_GetRaw(ATR *atr, unsigned char *buffer, uint32_t *length) +{ + uint32_t i, j; + + buffer[0] = atr->TS; + buffer[1] = atr->T0; + + j = 2; + + for(i = 0; i < atr->pn; i++) + { + if(atr->ib[i][ATR_INTERFACE_BYTE_TA].present) + { buffer[j++] = atr->ib[i][ATR_INTERFACE_BYTE_TA].value; } + + if(atr->ib[i][ATR_INTERFACE_BYTE_TB].present) + { buffer[j++] = atr->ib[i][ATR_INTERFACE_BYTE_TB].value; } + + if(atr->ib[i][ATR_INTERFACE_BYTE_TC].present) + { buffer[j++] = atr->ib[i][ATR_INTERFACE_BYTE_TC].value; } + + if(atr->ib[i][ATR_INTERFACE_BYTE_TD].present) + { buffer[j++] = atr->ib[i][ATR_INTERFACE_BYTE_TD].value; } + } + + if(atr->hbn > 0) + { + memcpy(&(buffer[j]), atr->hb, atr->hbn); + j += atr->hbn; + } + + if((atr->TCK).present) + { buffer[j++] = (atr->TCK).value; } + + (*length) = j; + + return ATR_OK; +} + +int32_t ATR_GetCheckByte(ATR *atr, unsigned char *check_byte) +{ + if(!((atr->TCK).present)) + { return (ATR_NOT_FOUND); } + + (*check_byte) = (atr->TCK).value; + return (ATR_OK); +} + +int32_t ATR_GetFsMax(ATR *atr, uint32_t *fsmax) +{ + unsigned char FI; + + if(ATR_GetIntegerValue(atr, ATR_INTEGER_VALUE_FI, &FI) == ATR_OK) + { (*fsmax) = atr_fs_table[FI]; } + else + { (*fsmax) = atr_fs_table[1]; } + + return (ATR_OK); +} +#endif diff --git a/csctapi/atr.h b/csctapi/atr.h new file mode 100644 index 0000000..5880bf5 --- /dev/null +++ b/csctapi/atr.h @@ -0,0 +1,142 @@ +/* + atr.h + ISO 7816 ICC's answer to reset abstract data type definitions + + This file is part of the Unix driver for Towitoko smartcard readers + Copyright (C) 2000 Carlos Prados + + This version is modified by doz21 to work in a special manner ;) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _ATR_ +#define _ATR_ + +/* + * Exported constants definition + */ +#define ATR_TIMEOUT 1000000 +#define DEFAULT_BAUDRATE 9600 + +/* Return values */ +#define ATR_OK 0 /* ATR could be parsed and data returned */ +#define ATR_NOT_FOUND 1 /* Data not present in ATR */ +#define ATR_MALFORMED 2 /* ATR could not be parsed */ +#define ATR_IO_ERROR 2 /* I/O stream error */ + +/* Paramenters */ +#define ATR_MAX_SIZE 33 /* Maximum size of ATR byte array */ +#define ATR_MAX_HISTORICAL 15 /* Maximum number of historical bytes */ +#define ATR_MAX_PROTOCOLS 7 /* Maximun number of protocols */ +#define ATR_MAX_IB 4 /* Maximum number of interface bytes per protocol */ +#define ATR_CONVENTION_DIRECT 0 /* Direct convention */ +#define ATR_CONVENTION_INVERSE 1 /* Inverse convention */ +#define ATR_PROTOCOL_TYPE_T0 0 /* Protocol type T=0 */ +#define ATR_PROTOCOL_TYPE_T1 1 /* Protocol type T=1 */ +#define ATR_PROTOCOL_TYPE_T2 2 /* Protocol type T=2 */ +#define ATR_PROTOCOL_TYPE_T3 3 /* Protocol type T=3 */ +#define ATR_PROTOCOL_TYPE_T14 14 /* Protocol type T=14 */ +#define ATR_INTERFACE_BYTE_TA 0 /* Interface byte TAi */ +#define ATR_INTERFACE_BYTE_TB 1 /* Interface byte TBi */ +#define ATR_INTERFACE_BYTE_TC 2 /* Interface byte TCi */ +#define ATR_INTERFACE_BYTE_TD 3 /* Interface byte TDi */ +#define ATR_PARAMETER_F 0 /* Parameter F */ +#define ATR_PARAMETER_D 1 /* Parameter D */ +#define ATR_PARAMETER_I 2 /* Parameter I */ +#define ATR_PARAMETER_P 3 /* Parameter P */ +#define ATR_PARAMETER_N 4 /* Parameter N */ +#define ATR_INTEGER_VALUE_FI 0 /* Integer value FI */ +#define ATR_INTEGER_VALUE_DI 1 /* Integer value DI */ +#define ATR_INTEGER_VALUE_II 2 /* Integer value II */ +#define ATR_INTEGER_VALUE_PI1 3 /* Integer value PI1 */ +#define ATR_INTEGER_VALUE_N 4 /* Integer value N */ +#define ATR_INTEGER_VALUE_PI2 5 /* Integer value PI2 */ + +/* Default values for paramenters */ +#define ATR_DEFAULT_FI 1 +#define ATR_DEFAULT_D 1 +#define ATR_DEFAULT_I 50 +#define ATR_DEFAULT_N 0 +#define ATR_DEFAULT_P 5 + +/* + * Exported data types definition + */ + +typedef struct s_ATR +{ + unsigned length; + unsigned char TS; + unsigned char T0; + struct + { + unsigned char value; + bool present; + } + ib[ATR_MAX_PROTOCOLS][ATR_MAX_IB], TCK; + unsigned pn; + unsigned char hb[ATR_MAX_HISTORICAL]; + unsigned hbn; +} +ATR; + +/* + * Exported variables declaration + */ + +extern const uint32_t atr_fs_table[16]; +extern const uint32_t atr_f_table[16]; +extern const double atr_d_table[16]; + +/* + * Exported functions declaraton + */ + +/* Initialization */ +int32_t ATR_InitFromArray(ATR *atr, const unsigned char buffer[ATR_MAX_SIZE], uint32_t length); + +/* General smartcard characteristics */ +int32_t ATR_GetConvention(ATR *atr, int32_t *convention); +int32_t ATR_GetNumberOfProtocols(ATR *atr, uint32_t *number_protocols); +int32_t ATR_GetProtocolType(ATR *atr, uint32_t number_protocol, unsigned char *protocol_type); + +/* ATR parameters and integer values */ +int32_t ATR_GetInterfaceByte(ATR *atr, uint32_t number, int32_t character, unsigned char *ib); +int32_t ATR_GetIntegerValue(ATR *atr, int32_t name, unsigned char *value); +int32_t ATR_GetParameter(ATR *atr, int32_t name, uint32_t *parameter); +int32_t ATR_GetHistoricalBytes(ATR *atr, unsigned char *hist, uint32_t *length); +int32_t ATR_GetCheckByte(ATR *atr, unsigned char *check_byte); +int32_t ATR_GetFsMax(ATR *atr, uint32_t *fsmax); + +/* Raw ATR retrieving */ +int32_t ATR_GetRaw(ATR *atr, unsigned char *buffer, uint32_t *length); +int32_t ATR_GetSize(ATR *atr, uint32_t *size); + +/* Invert order of bits in a byte: b7->b0, b0->b7 */ +#ifndef INVERT_BYTE +#define INVERT_BYTE(a) ( \ + (((a) << 7) & 0x80) | \ + (((a) << 5) & 0x40) | \ + (((a) << 3) & 0x20) | \ + (((a) << 1) & 0x10) | \ + (((a) >> 1) & 0x08) | \ + (((a) >> 3) & 0x04) | \ + (((a) >> 5) & 0x02) | \ + (((a) >> 7) & 0x01) \ + ) +#endif + +#endif /* _ATR_ */ diff --git a/csctapi/cardreaders.h b/csctapi/cardreaders.h new file mode 100644 index 0000000..6cd27da --- /dev/null +++ b/csctapi/cardreaders.h @@ -0,0 +1,20 @@ +#ifndef CSCTAPI_CARDREADERS_H_ +#define CSCTAPI_CARDREADERS_H_ + +extern const struct s_cardreader cardreader_db2com; +extern const struct s_cardreader cardreader_internal_sci; +extern const struct s_cardreader cardreader_internal_cool; +extern const struct s_cardreader cardreader_internal_azbox; +extern const struct s_cardreader cardreader_internal_amsmc; +extern const struct s_cardreader cardreader_mp35; +extern const struct s_cardreader cardreader_mouse; +extern const struct s_cardreader cardreader_pcsc; +extern const struct s_cardreader cardreader_sc8in1; +extern const struct s_cardreader cardreader_smargo; +extern const struct s_cardreader cardreader_smartreader; +extern const struct s_cardreader cardreader_stapi; +extern const struct s_cardreader cardreader_stinger; +extern const struct s_cardreader cardreader_drecas; +extern const struct s_cardreader cardreader_emu; + +#endif diff --git a/csctapi/icc_async.c b/csctapi/icc_async.c new file mode 100644 index 0000000..d80ddfc --- /dev/null +++ b/csctapi/icc_async.c @@ -0,0 +1,1377 @@ +#include "../globals.h" +#ifdef WITH_CARDREADER +#include "../oscam-lock.h" +#include "../oscam-string.h" +#include "icc_async.h" +#include "protocol_t0.h" +#include "io_serial.h" +#include "ifd_phoenix.h" +#include "../oscam-time.h" +#ifdef READER_NAGRA_MERLIN +#include "../cscrypt/fast_aes.h" +#include "../cscrypt/sha256.h" +#include "../cscrypt/mdc2.h" +#include "../cscrypt/idea.h" +#endif + +#define OK 0 +#define ERROR 1 + +// Default T0/T14 settings +#define DEFAULT_WI 10 +// Default T1 settings +#define DEFAULT_IFSC 32 +#define MAX_IFSC 251 // Cannot send > 255 buffer +#define DEFAULT_CWI 13 +#define DEFAULT_BWI 4 +#define EDC_LRC 0 + +#define PPS_MAX_LENGTH 6 +#define PPS_HAS_PPS1(block) ((block[1] & 0x10) == 0x10) +#define PPS_HAS_PPS2(block) ((block[1] & 0x20) == 0x20) +#define PPS_HAS_PPS3(block) ((block[1] & 0x40) == 0x40) + +static uint16_t tempfi; // used to capture FI and use it for rounding or not +static void ICC_Async_InvertBuffer(struct s_reader *reader, uint32_t size, unsigned char *buffer); +static int32_t Parse_ATR(struct s_reader *reader, ATR *atr, uint16_t deprecated); +static int32_t PPS_Exchange(struct s_reader *reader, unsigned char *params, uint32_t *length); +static uint32_t PPS_GetLength(unsigned char *block); +static int32_t InitCard(struct s_reader *reader, ATR *atr, unsigned char FI, uint32_t D, unsigned char N, uint16_t deprecated); +static uint32_t ETU_to_us(struct s_reader *reader, uint32_t ETU); +static unsigned char PPS_GetPCK(unsigned char *block, uint32_t length); +static int32_t SetRightParity(struct s_reader *reader); + +#ifdef READER_NAGRA_MERLIN +static void calculate_cak7_vars(struct s_reader *reader, const ATR *atr) +{ + uint8_t aes_key[32]; + const uint8_t aes_iv[] = { 0x4E, 0x61, 0x67, 0x72, 0x61, 0x63, 0x61, 0x72, 0x64, 0x28, 0x63, 0x29, 0x32, 0x30, 0x30, 0x36 }; // Nagracard(c)2006 + mbedtls_sha256_context ctx_sha256; + mbedtls_sha256_init(&ctx_sha256); + mbedtls_sha256_starts(&ctx_sha256, 0); + mbedtls_sha256_update(&ctx_sha256, atr->hb, atr->hbn); + mbedtls_sha256_finish(&ctx_sha256, aes_key); + mbedtls_sha256_free(&ctx_sha256); + memcpy(reader->cak7_aes_key,aes_key,32); + memcpy(reader->cak7_aes_iv,aes_iv,16); + char tmp[128]; + rdr_log(reader, "Initial AES: %s", cs_hexdump(1, reader->cak7_aes_key + 16, 16, tmp, sizeof(tmp))); +} + +void calculate_cak7_cmd(struct s_reader *reader, uint8_t *cmdin,uint8_t cmdlen,uint8_t *cmdout) +{ + uint32_t crc = ccitt32_crc(cmdin+4, cmdlen-4); + i2b_buf(4, crc, cmdin); + rdr_log_dump_dbg(reader, D_READER, cmdin, cmdlen, "preparing data for writing to cardreader"); + AesCtx ctx; + AesCtxIni(&ctx, reader->cak7_aes_iv, &reader->cak7_aes_key[16], KEY128, CBC); + AesEncrypt(&ctx, cmdin, cmdout, cmdlen); +} + +void do_cak7_cmd(struct s_reader *reader,unsigned char *cta_res, uint16_t *p_cta_lr,uint8_t *data,uint8_t inlen,uint8_t resplen) +{ + reader->cak7_seq++; + + uint8_t req[inlen+5+1]; // +head+len + memset(req,0x00,sizeof(req)); + // head + req[0]=0x80; + req[1]=0xCA; + if(reader->protocol_type == ATR_PROTOCOL_TYPE_T0) + { + req[4]=inlen + 1; + } + else + { + req[4]=inlen; + } + req[sizeof(req)-1]=resplen; + data[4]=(reader->cak7_seq>>16)&0xFF; + data[5]=(reader->cak7_seq>>8)&0xFF; + data[6]=(reader->cak7_seq)&0xFF; + calculate_cak7_cmd(reader,data,inlen,&req[5]); + rdr_log_dump_dbg(reader, D_READER, req, sizeof(req), "write to cardreader"); + if(!ICC_Async_CardWrite(reader, req, sizeof(req), cta_res, p_cta_lr)) + { + if(reader->protocol_type == ATR_PROTOCOL_TYPE_T0) + { + if(cta_res[*p_cta_lr - 2] == 0x61) + { + uint8_t resp[] = {0x00,0xC0,0x00,0x00,0x00}; + memcpy(resp + 4,&cta_res[*p_cta_lr - 1],1); + rdr_log_dump_dbg(reader, D_READER, resp, sizeof(resp), "write to cardreader"); + if(!ICC_Async_CardWrite(reader, resp, sizeof(resp), cta_res, p_cta_lr)) + { + AesCtx ctx; + AesCtxIni(&ctx, reader->cak7_aes_iv, &reader->cak7_aes_key[16], KEY128, CBC); + AesDecrypt(&ctx, cta_res, cta_res, *p_cta_lr-2); + } + else + { + *p_cta_lr=0; + } + } + else if(cta_res[*p_cta_lr - 2] == 0x6F && cta_res[*p_cta_lr - 1] == 0x01) + { + rdr_log(reader, "card answered 6F01 - trying one more time"); + rdr_log_dump_dbg(reader, D_READER, req, sizeof(req), "write to cardreader"); + if(!ICC_Async_CardWrite(reader, req, sizeof(req), cta_res, p_cta_lr)) + { + if(cta_res[*p_cta_lr - 2] == 0x61) + { + uint8_t resp[] = {0x00,0xC0,0x00,0x00,0x00}; + memcpy(resp + 4,&cta_res[*p_cta_lr - 1],1); + rdr_log_dump_dbg(reader, D_READER, resp, sizeof(resp), "write to cardreader"); + if(!ICC_Async_CardWrite(reader, resp, sizeof(resp), cta_res, p_cta_lr)) + { + AesCtx ctx; + AesCtxIni(&ctx, reader->cak7_aes_iv, &reader->cak7_aes_key[16], KEY128, CBC); + AesDecrypt(&ctx, cta_res, cta_res, *p_cta_lr-2); + } + else + { + *p_cta_lr=0; + } + } + else if(cta_res[*p_cta_lr - 2] == 0x6F && cta_res[*p_cta_lr - 1] == 0x01) + { + rdr_log(reader, "card needs reinit"); + } + } + else + { + *p_cta_lr=0; + } + } + } + else + { + if(cta_res[*p_cta_lr - 2] == 0x6F && cta_res[*p_cta_lr - 1] == 0x01) + { + rdr_log(reader, "card answered 6F01 - trying one more time"); + rdr_log_dump_dbg(reader, D_READER, req, sizeof(req), "write to cardreader"); + if(!ICC_Async_CardWrite(reader, req, sizeof(req), cta_res, p_cta_lr)) + { + if(cta_res[*p_cta_lr - 2] == 0x6F && cta_res[*p_cta_lr - 1] == 0x01) + { + rdr_log(reader, "card needs reinit"); + } + else + { + AesCtx ctx; + AesCtxIni(&ctx, reader->cak7_aes_iv, &reader->cak7_aes_key[16], KEY128, CBC); + AesDecrypt(&ctx, cta_res, cta_res, *p_cta_lr-2); + } + } + else + { + *p_cta_lr=0; + } + } + else + { + AesCtx ctx; + AesCtxIni(&ctx, reader->cak7_aes_iv, &reader->cak7_aes_key[16], KEY128, CBC); + AesDecrypt(&ctx, cta_res, cta_res, *p_cta_lr-2); + } + } + } + else + { + *p_cta_lr=0; + } +} + +static void calculate_changerom_cmd(struct s_reader *reader, const ATR *atr, uint8_t *cmd) +{ + uint8_t cmd_data[] = { 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x01, 0x01, 0x01, 0x95, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC }; + calculate_cak7_vars(reader,atr); + calculate_cak7_cmd(reader,cmd_data,sizeof(cmd_data),cmd); +} +#endif + +int32_t ICC_Async_Device_Init(struct s_reader *reader) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + reader->fdmc = -1; + rdr_log_dbg(reader, D_IFD, "Opening device %s", reader->device); + reader->written = 0; + int32_t ret = crdr_ops->reader_init(reader); + if(ret == OK) + { + rdr_log_dbg(reader, D_IFD, "Device %s successfully opened", reader->device); + } + else + { + if(reader->typ != R_SC8in1) + { + NULLFREE(reader->crdr_data); + } + rdr_log_dbg(reader, D_IFD, "ERROR: Can't open %s device", reader->device); + } + return ret; +} + +int32_t ICC_Async_Init_Locks(void) +{ + // Init device specific locks here, called from init thread + // before reader threads are running + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + const struct s_cardreader *crdr_ops = rdr->crdr; + if (!crdr_ops || !crdr_ops->lock_init) continue; + crdr_ops->lock_init(rdr); + } + return OK; +} + +int32_t ICC_Async_GetStatus(struct s_reader *reader, int32_t *card) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) + { + return ERROR; + } + if (reader->typ == R_SMART && reader->smartdev_found >= 4) + { + reader->statuscnt = reader->statuscnt + 1; + if (reader->statuscnt == 6) + { + int32_t in = 0; + call(crdr_ops->get_status(reader, &in)); + if(in) + { + reader->modemstat = 1; + *card = 1; + reader->statuscnt = 0; + } + else + { + reader->modemstat = 0; + *card = 0; + reader->statuscnt = 0; + } + return OK; + } + else + { + *card = reader->modemstat; + return OK; + } + } + else + { + int32_t in = 0; + call(crdr_ops->get_status(reader, &in)); + if(in) + { + *card = 1; + } + else + { + *card = 0; + } + return OK; + } + +} + +int32_t ICC_Async_Activate(struct s_reader *reader, ATR *atr, uint16_t deprecated) +{ + rdr_log_dbg(reader, D_IFD, "Activating card"); + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + reader->current_baudrate = DEFAULT_BAUDRATE; + if(reader->atr[0] != 0 && !reader->ins7e11_fast_reset) + { + rdr_log(reader, "Using ATR from reader config"); + ATR_InitFromArray(atr, reader->atr, ATR_MAX_SIZE); + } + else + { + reader->crdr_flush = crdr_ops->flush; // Flush flag may be changed for each reader + call(crdr_ops->activate(reader, atr)); + if(crdr_ops->skip_extra_atr_parsing +#ifdef READER_NAGRA_MERLIN + && reader->cak7_mode == 0 +#endif + ) + { + return OK; + } + } + + uint8_t atrarr[ATR_MAX_SIZE]; + uint32_t atr_size; + ATR_GetRaw(atr, atrarr, &atr_size); + char tmp[atr_size * 3 + 1]; + rdr_log(reader, "ATR: %s", cs_hexdump(1, atrarr, atr_size, tmp, sizeof(tmp))); + memcpy(reader->card_atr, atrarr, atr_size); + reader->card_atr_length = atr_size; + + // Get ICC reader->convention + if(ATR_GetConvention(atr, &(reader->convention)) != ATR_OK) + { + rdr_log(reader, "ERROR: Could not read reader->convention"); + reader->convention = 0; + reader->protocol_type = 0; + return ERROR; + } + reader->protocol_type = ATR_PROTOCOL_TYPE_T0; + + // Parse_ATR and InitCard need to be included in lock because they change parity of serial port + if(crdr_ops->lock) + { + crdr_ops->lock(reader); + } + int32_t ret = Parse_ATR(reader, atr, deprecated); + if(crdr_ops->unlock) + { + crdr_ops->unlock(reader); + } + if(ret) + { + rdr_log(reader, "ERROR: Parse_ATR returned error"); + return ERROR; + } + +#ifdef READER_NAGRA_MERLIN + reader->cak7type = 0; + + ATR_GetRaw(atr, atrarr, &atr_size); + + if((memcmp(atrarr + 8, "DNASP40", 7) == 0) || (memcmp(atrarr + 11, "DNASP41", 7) == 0) || (memcmp(atrarr + 11, "DNASP48", 7) == 0)) + { + rdr_log(reader, "card needs reset before init"); + memset(atr, 0, 1); + call(crdr_ops->activate(reader, atr)); //try to read the atr of this layer + ATR_GetRaw(atr, atrarr, &atr_size); + rdr_log(reader,"ATR: %s", cs_hexdump(1, atrarr, atr_size, tmp, sizeof(tmp))); + // Parse_ATR and InitCard need to be included in lock because they change parity of serial port + if(crdr_ops->lock) + { + crdr_ops->lock(reader); + } + int32_t ret1 = Parse_ATR(reader, atr, deprecated); + if(crdr_ops->unlock) + { + crdr_ops->unlock(reader); + } + if(ret1) + { + rdr_log(reader, "ERROR: Parse_ATR returned error"); + return ERROR; + } + } + + if((memcmp(atrarr + 8, "DNASP4", 6) == 0) || (memcmp(atrarr + 11, "DNASP4", 6) == 0)) + { + rdr_log(reader, "detected card in CAK7 mode"); + calculate_cak7_vars(reader, atr); + if((atrarr[2] == 0x95) && (atrarr[3] == 0x00) && (atrarr[4] == 0xFF) && (atrarr[5] == 0x50) && (atrarr[6] == 0x80) && (atrarr[7] == 0x1C)) + { + reader->cak7type = 3; + } + else + { + reader->cak7type = 1; + } + } + else if(((memcmp(atrarr + 7, "pp", 2) == 0 && ((atrarr[9]&0x0F) >= 10)) || (memcmp(atrarr + 11, "DNASP18", 7) == 0) || (memcmp(atrarr + 11, "DNASP19", 7) == 0) || (memcmp(atrarr + 11, "DNASP1A", 7) == 0)) && reader->cak7_mode) + { + rdr_log(reader, "detected card in CAK6/Seca mode -> try switch to Nagra CAK7"); + uint8_t changerom_handshake[22]; + memset(changerom_handshake, 0x00, 22); + + calculate_changerom_cmd(reader, atr, &changerom_handshake[5]); + memset(reader->rom, 0, 15); + unsigned char cta_res[CTA_RES_LEN]; + memset(cta_res, 0, CTA_RES_LEN); + uint16_t cta_lr; + + changerom_handshake[0] = 0x80; + changerom_handshake[1] = 0xCA; + changerom_handshake[4] = 0x11; // 0x11: length of data we will send + uint8_t cta_res1_ok = 0x61; + uint8_t cta_res2_ok = 0x10; + + if(reader->protocol_type != ATR_PROTOCOL_TYPE_T0) + { + //changerom_handshake[0] = 0x80; // fix for mipsel router + changerom_handshake[4] = 0x10; // 0x10: length of data we will send + cta_res1_ok = 0x90; + cta_res2_ok = 0x00; + } + + changerom_handshake[21] = 0x10; + + reader->cak7type = 1; + rdr_log_dump_dbg(reader, D_READER, changerom_handshake, sizeof(changerom_handshake), "write to cardreader"); + if(!ICC_Async_CardWrite(reader, changerom_handshake, sizeof(changerom_handshake), cta_res, &cta_lr)) + { + if(cta_res[cta_lr-2] == cta_res1_ok && cta_res[cta_lr-1] == cta_res2_ok) + { + if(reader->protocol_type == ATR_PROTOCOL_TYPE_T0) + { + uint8_t resp[] = {0x00,0xC0,0x00,0x00,0x00}; + memcpy(resp + 4,&cta_res[cta_lr - 1],1); + rdr_log_dump_dbg(reader, D_READER, resp, sizeof(resp), "write to cardreader"); + if(!ICC_Async_CardWrite(reader, resp, sizeof(resp), cta_res, &cta_lr)) + { + AesCtx ctx; + AesCtxIni(&ctx, reader->cak7_aes_iv, &reader->cak7_aes_key[16], KEY128, CBC); + AesDecrypt(&ctx, cta_res, cta_res, cta_lr-2); + rdr_log_dump_dbg(reader, D_READER, cta_res, cta_lr, "Decrypted Answer:"); + } + else + { + return ERROR; + } + } + else + { + AesCtx ctx; + AesCtxIni(&ctx, reader->cak7_aes_iv, &reader->cak7_aes_key[16], KEY128, CBC); + AesDecrypt(&ctx, cta_res, cta_res, cta_lr-2); + rdr_log_dump_dbg(reader, D_READER, cta_res, cta_lr, "Decrypted Answer:"); + } + rdr_log(reader, "switch nagra layer OK"); + memset(atr, 0, 1); + call(crdr_ops->activate(reader, atr)); //try to read the atr of this layer + ATR_GetRaw(atr, atrarr, &atr_size); + rdr_log(reader,"ATR: %s", cs_hexdump(1, atrarr, atr_size, tmp, sizeof(tmp))); + calculate_cak7_vars(reader, atr); + if(reader->protocol_type == ATR_PROTOCOL_TYPE_T0) + { + reader->cak7type = 3; + } + else + { + reader->cak7type = 1; + } + + if(crdr_ops->lock) + { + crdr_ops->lock(reader); + } + int32_t ret2 = Parse_ATR(reader, atr, deprecated); + if(crdr_ops->unlock) + { + crdr_ops->unlock(reader); + } + if(ret2) + { + rdr_log(reader, "ERROR: Parse_ATR returned error"); + return ERROR; + } + } + else + { + rdr_log(reader,"Switch to nagra layer failed!"); + return ERROR; + } + } + else + { + rdr_log(reader,"Switch to nagra layer command failed!"); + return ERROR; + } + memcpy(reader->card_atr, atrarr, atr_size); + reader->card_atr_length = atr_size; + memcpy(reader->rom, atr->hb, (atr->hbn>15)?15:atr->hbn); // get historical bytes from atr + } +#endif + rdr_log_dbg(reader, D_READER, "Card successfully activated"); + + return OK; +} + +int32_t ICC_Async_CardWrite(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + int32_t ret; + *lr = 0; //will be returned in case of error + if(crdr_ops->card_write) + { + call(crdr_ops->card_write(reader, command, rsp, lr, command_len)); + rdr_log_dump_dbg(reader, D_READER, rsp, *lr, "Answer from cardreader:"); + return OK; + } + if(crdr_ops->lock) + { + crdr_ops->lock(reader); + } + int32_t try = 1; + uint16_t type = 0; + do + { + if(try > 1) + { + rdr_log(reader, "Warning: needed try nr %i, next ECM has some delay", try); + } + + switch(reader->protocol_type) + { + case ATR_PROTOCOL_TYPE_T0: + ret = Protocol_T0_Command(reader, command, command_len, rsp, lr); + type = 0; + break; + case ATR_PROTOCOL_TYPE_T1: + ret = Protocol_T1_Command(reader, command, command_len, rsp, lr); + type = 1; + if(ret != OK && !crdr_ops->skip_t1_command_retries +#ifdef READER_NAGRA_MERLIN + && reader->cak7type == 0 +#endif + ) + { + //try to resync + rdr_log(reader, "Resync error: readtimeouts %d/%d (max/min) us, writetimeouts %d/%d (max/min) us", reader->maxreadtimeout, reader->minreadtimeout, reader->maxwritetimeout, reader->minwritetimeout); + unsigned char resync[] = { 0x21, 0xC0, 0x00, 0xE1 }; + ret = Protocol_T1_Command(reader, resync, sizeof(resync), rsp, lr); + if(ret == OK) + { + //reader->ifsc = DEFAULT_IFSC; // tryfix cardtimeouts: ifsc is setup at card init, on resync it should not return to default_ifsc + rdr_log(reader, "T1 Resync command successful ifsc = %i", reader->ifsc); + ret = ERROR; + } + else + { + rdr_log(reader, "T1 Resync command error, trying to reactivate!"); + ATR atr; + ICC_Async_Activate(reader, &atr, reader->deprecated); + if(crdr_ops->unlock) + { + crdr_ops->unlock(reader); + } + return ERROR; + } + } + break; + case ATR_PROTOCOL_TYPE_T14: + ret = Protocol_T14_ExchangeTPDU(reader, command, command_len, rsp, lr); + type = 14; + break; + default: + rdr_log(reader, "ERROR: Unknown protocol type %i", reader->protocol_type); + type = 99; // use 99 for unknown. + ret = ERROR; + } + try++; + } + while((try < 3) && (ret != OK) && (((type == 0 || type == 1) +#ifdef READER_NAGRA_MERLIN + && reader->cak7type == 0 +#endif + ) || type == 14)); // always do one retry when failing + if(crdr_ops->unlock) + { + crdr_ops->unlock(reader); + } + if(ret) + { + rdr_log_dbg(reader, D_TRACE, "ERROR: Protocol_T%d_Command returns error", type); + return ERROR; + } + rdr_log_dump_dbg(reader, D_READER, rsp, *lr, "Answer from cardreader:"); + return OK; +} + +int32_t ICC_Async_GetTimings(struct s_reader *reader, uint32_t wait_etu) +{ + int32_t timeout = ETU_to_us(reader, wait_etu); + rdr_log_dbg(reader, D_IFD, "Setting timeout to %i ETU (%d us)", wait_etu, timeout); + return timeout; +} + +int32_t ICC_Async_Transmit(struct s_reader *reader, uint32_t size, uint32_t expectedlen, unsigned char *data, uint32_t delay, uint32_t timeout) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + if(expectedlen) + { + rdr_log_dbg(reader, D_IFD, "Transmit size %d bytes, expected len %d bytes, delay %d us, timeout=%d us", size, expectedlen, delay, timeout); + } + else + { + rdr_log_dbg(reader, D_IFD, "Transmit size %d bytes, delay %d us, timeout=%d us", size, delay, timeout); + } + rdr_log_dump_dbg(reader, D_IFD, data, size, "Transmit:"); + unsigned char *sent = data; + if(reader->convention == ATR_CONVENTION_INVERSE && crdr_ops->need_inverse) + { + ICC_Async_InvertBuffer(reader, size, sent); + } + call(crdr_ops->transmit(reader, sent, size, expectedlen, delay, timeout)); + rdr_log_dbg(reader, D_IFD, "Transmit successful"); + if(reader->convention == ATR_CONVENTION_INVERSE && crdr_ops->need_inverse) + { + // revert inversion cause the code in protocol_t0 is accessing buffer after transmit + ICC_Async_InvertBuffer(reader, size, sent); + } + return OK; +} + +int32_t ICC_Async_Receive(struct s_reader *reader, uint32_t size, unsigned char *data, uint32_t delay, uint32_t timeout) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + rdr_log_dbg(reader, D_IFD, "Receive size %d bytes, delay %d us, timeout=%d us", size, delay, timeout); + call(crdr_ops->receive(reader, data, size, delay, timeout)); + rdr_log_dbg(reader, D_IFD, "Receive successful"); + if(reader->convention == ATR_CONVENTION_INVERSE && crdr_ops->need_inverse == 1) + { + ICC_Async_InvertBuffer(reader, size, data); + } + return OK; +} + +int32_t ICC_Async_Close(struct s_reader *reader) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + rdr_log_dbg(reader, D_IFD, "Closing device %s", reader->device); + call(crdr_ops->close(reader)); + if(reader->typ != R_SC8in1) + { + NULLFREE(reader->crdr_data); + NULLFREE(reader->csystem_data); + } + rdr_log_dbg(reader, D_IFD, "Device %s successfully closed", reader->device); + return OK; +} + +void ICC_Async_DisplayMsg(struct s_reader *reader, char *msg) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops || !crdr_ops->display_msg) + { + return; + } + crdr_ops->display_msg(reader, msg); +} + +int32_t ICC_Async_Reset(struct s_reader *reader, struct s_ATR *atr, int32_t (*rdr_activate_card)(struct s_reader *, struct s_ATR *, uint16_t deprecated), int32_t (*rdr_get_cardsystem)(struct s_reader *, struct s_ATR *)) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops || !crdr_ops->do_reset) + { + return 0; + } + return crdr_ops->do_reset(reader, atr, rdr_activate_card, rdr_get_cardsystem); +} + +static uint32_t ICC_Async_GetClockRate(int32_t cardmhz) +{ + switch(cardmhz) + { + case 357: + case 358: + return (372L * 9600L); + case 368: + return (384L * 9600L); + default: + return (cardmhz * 10000L); + } +} + +static int32_t ICC_Async_GetPLL_Divider(struct s_reader *reader) +{ + if(reader->divider != 0) + { + return reader->divider; + } + if(reader->cardmhz != 8300) // Check dreambox is not DM7025 + { + float divider; + divider = ((float) reader->cardmhz) / ((float) reader->mhz); + if (tempfi == 9) reader->divider = (int32_t) divider; // some card's runs only when slightly oveclocked like HD02 + else + { + reader->divider = (int32_t) divider; + if(divider > reader->divider) + { + reader->divider++; // to prevent over clocking, ceil (round up) the divider + } + } + rdr_log_dbg(reader, D_DEVICE, "PLL maxmhz = %.2f, wanted mhz = %.2f, divider used = %d, actualcardclock=%.2f", (float) reader->cardmhz / 100, (float) reader->mhz / 100, reader->divider, (float) reader->cardmhz / reader->divider / 100); + reader->mhz = reader->cardmhz / reader->divider; + } + else // STB is DM7025 + { + int32_t i, dm7025_clock_freq[] = {518, 461, 395, 360, 319, 296, 267, 244, 230, 212, 197}, dm7025_PLL_setting[] = {6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, t_cardmhz = reader->mhz; + for(i = 0; i < 11; i++) + if(t_cardmhz >= dm7025_clock_freq[i]) + { + break; + } + if(i > 10) + { + i = 10; + } + reader->mhz = dm7025_clock_freq[i]; + reader->divider = dm7025_PLL_setting[i]; /*Nicer way of codeing is: reader->divider = i + 6;*/ + rdr_log_dbg(reader, D_DEVICE, "DM7025 PLL maxmhz = %.2f, wanted mhz = %.2f, PLL setting used = %d, actualcardclock=%.2f", (float) reader->cardmhz / 100, (float) t_cardmhz / 100, reader->divider, (float) reader->mhz / 100); + } + return reader->divider; +} + +static void ICC_Async_InvertBuffer(struct s_reader *reader, uint32_t size, unsigned char *buffer) +{ + uint32_t i; + rdr_log_dbg(reader, D_IFD, "%s: size=%u buf[0]=%02x", __func__, size, buffer[0]); + for(i = 0; i < size; i++) + { + buffer[i] = ~(INVERT_BYTE(buffer[i])); + } +} + +static int32_t Parse_ATR(struct s_reader *reader, ATR *atr, uint16_t deprecated) +{ + unsigned char FI = ATR_DEFAULT_FI; + uint32_t D = ATR_DEFAULT_D; + uint32_t N = ATR_DEFAULT_N; + int32_t ret; + char tmp[256]; + + int32_t numprot = atr->pn; + //if there is a trailing TD, this number is one too high + unsigned char tx; + if(ATR_GetInterfaceByte(atr, numprot - 1, ATR_INTERFACE_BYTE_TD, &tx) == ATR_OK) + if((tx & 0xF0) == 0) + { + numprot--; + } + int32_t i, point; + char txt[50]; + bool OffersT[3]; //T14 stored as T2 + for(i = 0; i <= 2; i++) + { + OffersT[i] = 0; + } + for(i = 1; i <= numprot; i++) + { + point = 0; + if(ATR_GetInterfaceByte(atr, i, ATR_INTERFACE_BYTE_TA, &tx) == ATR_OK) + { + snprintf((char *)txt + point, sizeof(txt) - point, "TA%i=%02X ", i, tx); + point += 7; + } + if(ATR_GetInterfaceByte(atr, i, ATR_INTERFACE_BYTE_TB, &tx) == ATR_OK) + { + snprintf((char *)txt + point, sizeof(txt) - point, "TB%i=%02X ", i, tx); + point += 7; + } + if(ATR_GetInterfaceByte(atr, i, ATR_INTERFACE_BYTE_TC, &tx) == ATR_OK) + { + snprintf((char *)txt + point, sizeof(txt) - point, "TC%i=%02X ", i, tx); + point += 7; + } + if(ATR_GetInterfaceByte(atr, i, ATR_INTERFACE_BYTE_TD, &tx) == ATR_OK) + { + snprintf((char *)txt + point, sizeof(txt) - point, "TD%i=%02X ", i, tx); + point += 7; + tx &= 0X0F; + snprintf((char *)txt + point, sizeof(txt) - point, "(T%i)", tx); + if(tx == 14) + { + OffersT[2] = 1; + } + else + { + OffersT[tx] = 1; + } + } + else + { + snprintf((char *)txt + point, sizeof(txt) - point, "no TD%i means T0", i); + OffersT[0] = 1; + } + rdr_log_dbg(reader, D_ATR, "%s", txt); + } + + int32_t numprottype = 0; + for(i = 0; i <= 2; i++) + if(OffersT[i]) + { + numprottype ++; + } + rdr_log_dbg(reader, D_ATR, "%i protocol types detected. Historical bytes: %s", numprottype, cs_hexdump(1, atr->hb, atr->hbn, tmp, sizeof(tmp))); + + ATR_GetParameter(atr, ATR_PARAMETER_N, &(N)); + ATR_GetProtocolType(atr, 1, &(reader->protocol_type)); // get protocol from TD1 + + unsigned char TA2; + bool SpecificMode = (ATR_GetInterfaceByte(atr, 2, ATR_INTERFACE_BYTE_TA, &TA2) == ATR_OK); // if TA2 present, specific mode, else negotiable mode + if(SpecificMode) + { + reader->protocol_type = TA2 & 0x0F; + if((TA2 & 0x10) != 0x10) // bit 5 set to 0 means F and D explicitly defined in interface characters + { + unsigned char TA1; + if(ATR_GetInterfaceByte(atr, 1, ATR_INTERFACE_BYTE_TA, &TA1) == ATR_OK) + { + FI = TA1 >> 4; + ATR_GetParameter(atr, ATR_PARAMETER_D, &(D)); + } + else + { + FI = ATR_DEFAULT_FI; + D = ATR_DEFAULT_D; + } + } + else + { + rdr_log(reader, "Specific mode: speed 'implicitly defined', not sure how to proceed, assuming default values"); + FI = ATR_DEFAULT_FI; + D = ATR_DEFAULT_D; + } + uint32_t F = atr_f_table[FI]; + rdr_log_dbg(reader, D_ATR, "Specific mode: T%i, F=%d, D=%d, N=%d", reader->protocol_type, F, D, N); + } + else // negotiable mode + { + + reader->read_timeout = 1000000; // in us + bool PPS_success = 0; + bool NeedsPTS = ((reader->protocol_type != ATR_PROTOCOL_TYPE_T14) && (numprottype > 1 || (atr->ib[0][ATR_INTERFACE_BYTE_TA].present == 1 && atr->ib[0][ATR_INTERFACE_BYTE_TA].value != 0x11) || N == 255)); //needs PTS according to old ISO 7816 + if(NeedsPTS && deprecated == 0) + { + // PTSS PTS0 PTS1 PCK + unsigned char req[6] = { 0xFF, 0x10, 0x00, 0x00 }; //we currently do not support PTS2, standard guardtimes or PTS3, + //but spare 2 bytes in arrayif card responds with it + req[1] = 0x10 | reader->protocol_type; //PTS0 always flags PTS1 to be sent always + if(ATR_GetInterfaceByte(atr, 1, ATR_INTERFACE_BYTE_TA, &req[2]) != ATR_OK) //PTS1 + { + req[2] = 0x11; // defaults FI and DI to 1 + } + uint32_t len = 0; + call(SetRightParity(reader)); + ret = PPS_Exchange(reader, req, &len); + if(ret == OK) + { + FI = req[2] >> 4; + unsigned char DI = req[2] & 0x0F; + D = atr_d_table[DI]; + uint32_t F = atr_f_table[FI]; + PPS_success = 1; + rdr_log_dbg(reader, D_ATR, "PTS successful, selected protocol: T%i, F=%d, D=%d, N=%d", reader->protocol_type, F, D, N); + } + else + { + rdr_log_dump_dbg(reader, D_ATR, req, len, "PTS Failure, response:"); + } + } + + //When for SCI, T14 protocol, TA1 is obeyed, this goes OK for mosts devices, but somehow on DM7025 Sky S02 card goes wrong when setting ETU (ok on DM800/DM8000) + if(!PPS_success) // last PPS not successful + { + unsigned char TA1; + if(ATR_GetInterfaceByte(atr, 1, ATR_INTERFACE_BYTE_TA, &TA1) == ATR_OK) + { + FI = TA1 >> 4; + ATR_GetParameter(atr, ATR_PARAMETER_D, &(D)); + } + else // do not obey TA1 + { + FI = ATR_DEFAULT_FI; + D = ATR_DEFAULT_D; + } + if(NeedsPTS) + { + if((D == 32) || (D == 12) || (D == 20)) //those values were RFU in old table + { + D = 0; // viaccess cards that fail PTS need this + } + } + uint32_t F = atr_f_table[FI]; + rdr_log_dbg(reader, D_ATR, "No PTS %s, selected protocol T%i, F=%d, D=%d, N=%d", NeedsPTS ? "happened" : "needed", reader->protocol_type, F, D, N); + } + }//end negotiable mode + + //make sure no zero values + uint32_t F = atr_f_table[FI]; + if(!F) + { + FI = ATR_DEFAULT_FI; + rdr_log(reader, "Warning: F=0 is invalid, forcing FI=%d", FI); + } + if(!D) + { + D = ATR_DEFAULT_D; + rdr_log(reader, "Warning: D=0 is invalid, forcing D=%d", D); + } + rdr_log_dbg(reader, D_ATR, "Init card protocol T%i, FI=%d, F=%d, D=%d, N=%d", reader->protocol_type, FI, F, D, N); + if(deprecated == 0) + { + return InitCard(reader, atr, FI, D, N, deprecated); + } + else + { + return InitCard(reader, atr, ATR_DEFAULT_FI, ATR_DEFAULT_D, N, deprecated); + } +} + +static int32_t PPS_Exchange(struct s_reader *reader, unsigned char *params, uint32_t *length) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + unsigned char confirm[PPS_MAX_LENGTH]; + uint32_t len_request, len_confirm; + char tmp[128]; + int32_t ret; + + len_request = PPS_GetLength(params); + params[len_request - 1] = PPS_GetPCK(params, len_request - 1); + rdr_log_dbg(reader, D_IFD, "PTS: Sending request: %s", cs_hexdump(1, params, len_request, tmp, sizeof(tmp))); + + if(crdr_ops->set_protocol) + { + ret = crdr_ops->set_protocol(reader, params, length, len_request); + return ret; + } + + // Send PPS request + call(ICC_Async_Transmit(reader, len_request, len_request, params, 0, 1000000)); + + // Get PPS confirm + call(ICC_Async_Receive(reader, 2, confirm, 0, 1000000)); + len_confirm = PPS_GetLength(confirm); + call(ICC_Async_Receive(reader, len_confirm - 2, confirm + 2, 0, 1000000)); + + rdr_log_dbg(reader, D_IFD, "PTS: Receiving confirm: %s", cs_hexdump(1, confirm, len_confirm, tmp, sizeof(tmp))); + if((len_request != len_confirm) || (memcmp(params, confirm, len_request))) + { + ret = ERROR; + } + else + { + ret = OK; + } + + // Copy PPS handshake + memcpy(params, confirm, len_confirm); + (*length) = len_confirm; + return ret; +} + +static uint32_t PPS_GetLength(unsigned char *block) +{ + uint32_t length = 3; + + if(PPS_HAS_PPS1(block)) + { + length++; + } + + if(PPS_HAS_PPS2(block)) + { + length++; + } + + if(PPS_HAS_PPS3(block)) + { + length++; + } + + return length; +} + +static uint32_t ETU_to_us(struct s_reader *reader, uint32_t ETU) +{ + return (uint32_t)((double) ETU * reader->worketu); // in us +} + +static int32_t ICC_Async_SetParity(struct s_reader *reader, uint16_t parity) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + if(crdr_ops->set_parity) + { + rdr_log_dbg(reader, D_IFD, "Setting right parity"); + call(crdr_ops->set_parity(reader, parity)); + } + return OK; +} + +static int32_t SetRightParity(struct s_reader *reader) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + //set right parity + uint16_t parity = PARITY_EVEN; + if(reader->convention == ATR_CONVENTION_INVERSE) + { + parity = PARITY_ODD; + } + else if(reader->protocol_type == ATR_PROTOCOL_TYPE_T14) + { + parity = PARITY_NONE; + } + + call(ICC_Async_SetParity(reader, parity)); + + if(crdr_ops->flush && reader->crdr_flush) + { + IO_Serial_Flush(reader); + } + + return OK; +} + +static int32_t InitCard(struct s_reader *reader, ATR *atr, unsigned char FI, uint32_t D, unsigned char N, uint16_t deprecated) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + uint32_t I, F, Fi, BGT = 0, edc, GT = 0, WWT = 0, EGT = 0; + unsigned char wi = 0; + + // set the amps and the volts according to ATR + if(ATR_GetParameter(atr, ATR_PARAMETER_I, &I) != ATR_OK) + { + I = 0; + } + + tempfi = FI; + + // set clock speed to max if internal reader + if(crdr_ops->max_clock_speed == 1 && reader->typ == R_INTERNAL) + { + if(reader->autospeed == 1) //no overclocking + { + reader->mhz = atr_fs_table[FI] / 10000; // we are going to clock the card to this nominal frequency + } + + if(reader->cardmhz > 2000 && reader->autospeed == 1) // -1 replaced by autospeed parameter is magic number pll internal reader set cardmhz according to optimal atr speed + { + reader->mhz = atr_fs_table[FI] / 10000 ; + if((!strncmp(boxtype_get(), "vu", 2 ))||(boxtype_is("ini-8000am"))) + { + reader->mhz = 450; + } + } + } + + if(reader->cardmhz > 2000) + { + reader->divider = 0; // reset pll divider so divider will be set calculated again. + ICC_Async_GetPLL_Divider(reader); // calculate pll divider for target cardmhz. + } + + Fi = atr_f_table[FI]; // get the frequency divider also called clock rate conversion factor + if(crdr_ops->set_baudrate) + { + reader->current_baudrate = DEFAULT_BAUDRATE; + + if(deprecated == 0) + { + + if(reader->protocol_type != ATR_PROTOCOL_TYPE_T14) // dont switch for T14 + { + uint32_t baud_temp = (double)D * ICC_Async_GetClockRate(reader->cardmhz) / (double)Fi; + uint32_t baud_temp2 = (double)D * ICC_Async_GetClockRate(reader->mhz) / (double)Fi; + rdr_log(reader, "Setting baudrate to %d bps", baud_temp2); + // set_baudrate() increases/decreases baud_temp to baud_temp2 in case of over/underclocking + call(crdr_ops->set_baudrate(reader, baud_temp)); + reader->current_baudrate = baud_temp2; + } + } + } + if(reader->cardmhz > 2000 && reader->typ == R_INTERNAL) + { + F = reader->mhz; // for PLL based internal readers + } + else + { + if (reader->typ == R_SMART || is_smargo_reader(reader)) + { + if (reader->autospeed == 1) + { + uint32_t Fsmart = atr_fs_table[FI]; + reader->mhz = Fsmart/10000; + if(reader->mhz >= 1600) + { + reader->mhz = 1600; + } + else if(reader->mhz >= 1200) + { + reader->mhz = 1200; + } + else if(reader->mhz >= 961) + { + reader->mhz = 961; + } + else if(reader->mhz >= 800) + { + reader->mhz = 800; + } + else if(reader->mhz >= 686) + { + reader->mhz = 686; + } + else if(reader->mhz >= 600) + { + reader->mhz = 600; + } + else if(reader->mhz >= 534) + { + reader->mhz = 534; + } + else if(reader->mhz >= 480) + { + reader->mhz = 534; + } + else if(reader->mhz >= 436) + { + reader->mhz = 436; + } + else if(reader->mhz >= 400) + { + reader->mhz = 400; + } + else if(reader->mhz >= 369) + { + reader->mhz = 369; + } + else if(reader->mhz >= 357) + { + reader->mhz = 369; // 357 not suported by smartreader + } + else if(reader->mhz >= 343) + { + reader->mhz = 343; + } + else + { + reader->mhz = 320; + } + } + } + F = reader->mhz; //all other readers + } + reader->worketu = (double)((double)(1 / (double)D) * ((double)Fi / (double)((double)F / 100))); + rdr_log_dbg(reader, D_ATR, "Calculated work ETU is %.2f us reader mhz = %u", reader->worketu, reader->mhz); + + //set timings according to ATR + reader->read_timeout = 0; + reader->block_delay = 0; + reader->char_delay = 0; + + switch(reader->protocol_type) + { + case ATR_PROTOCOL_TYPE_T0: + case ATR_PROTOCOL_TYPE_T14: + { + /* Integer value WI = TC2, by default 10 */ +#ifndef PROTOCOL_T0_USE_DEFAULT_TIMINGS + if(ATR_GetInterfaceByte(atr, 2, ATR_INTERFACE_BYTE_TC, &(wi)) != ATR_OK) +#endif + wi = DEFAULT_WI; + + WWT = (uint32_t) 960 * D * wi; //in work ETU + GT = 2; // standard guardtime + GT += 1; // start bit + GT += 8; // databits + GT += 1; // parity bit + + if(N != 255) //add extra Guard Time by ATR + { + EGT += N; // T0 protocol, if TC1 = 255 then dont add extra guardtime + } + reader->CWT = 0; // T0 protocol doesnt have char waiting time (used to detect errors within 1 single block of data) + reader->BWT = 0; // T0 protocol doesnt have block waiting time (used to detect unresponsive card, this is max time for starting a block answer) + + rdr_log_dbg(reader, D_ATR, "Protocol: T=%i, WWT=%u, Clockrate=%u", reader->protocol_type, WWT, F * 10000); + reader->read_timeout = WWT; // Work waiting time used in T0 (max time to signal unresponsive card!) + reader->char_delay = GT + EGT; // Character delay is used on T0 + rdr_log_dbg(reader, D_ATR, "Setting timings: timeout=%u ETU, block_delay=%u ETU, char_delay=%u ETU", reader->read_timeout, reader->block_delay, reader->char_delay); + break; + } + case ATR_PROTOCOL_TYPE_T1: + { + unsigned char ta, tb, tc, cwi, bwi; + + // Set IFSC + if(ATR_GetInterfaceByte(atr, 3, ATR_INTERFACE_BYTE_TA, &ta) == ATR_NOT_FOUND) + { + reader->ifsc = DEFAULT_IFSC; + } + else if((ta != 0x00) && (ta != 0xFF)) + { + reader->ifsc = ta; + } + else + { + reader->ifsc = DEFAULT_IFSC; + } + + // FIXME workaround for Smargo until native mode works + if(reader->smargopatch == 1) + { + reader->ifsc = MIN(reader->ifsc, 28); + } + else + // Towitoko and smartreaders dont allow IFSC > 251 + { + reader->ifsc = MIN(reader->ifsc, MAX_IFSC); + } + +#ifndef PROTOCOL_T1_USE_DEFAULT_TIMINGS + // Calculate CWI and BWI + if(ATR_GetInterfaceByte(atr, 3, ATR_INTERFACE_BYTE_TB, &tb) == ATR_NOT_FOUND) + { +#endif + cwi = DEFAULT_CWI; + bwi = DEFAULT_BWI; +#ifndef PROTOCOL_T1_USE_DEFAULT_TIMINGS + } + else + { + cwi = tb & 0x0F; + bwi = tb >> 4; + } +#endif + + // Set CWT = 11+(2^CWI) work etu + reader->CWT = (uint16_t) 11 + (1 << cwi); // in work ETU + + reader->BWT = (uint32_t) ((1<worketu) + 11; // BWT in work ETU + + BGT = 22L; // Block Guard Time in ETU used to interspace between block responses + GT = 2; // standard guardtime + GT += 1; // start bit + GT += 8; // databits + GT += 1; // parity bit + + if(N == 255) + { + GT -= 1; // special case, ATR says standard 2 etu guardtime is decreased by 1 (in ETU) EGT remains zero! + } + else + { + EGT += N; // ATR says add extra guardtime (in ETU) + } + + // Set the error detection code type + if(ATR_GetInterfaceByte(atr, 3, ATR_INTERFACE_BYTE_TC, &tc) == ATR_NOT_FOUND) + { + edc = EDC_LRC; + } + else + { + edc = tc & 0x01; + } + + // Set initial send sequence (NS) + reader->ns = 1; + + rdr_log_dbg(reader, D_ATR, "Protocol: T=%i: IFSC=%d, CWT=%d etu, BWT=%d etu, BGT=%d etu, EDC=%s, N=%d", reader->protocol_type, reader->ifsc, reader->CWT, reader->BWT, BGT, (edc == EDC_LRC) ? "LRC" : "CRC", N); + reader->read_timeout = reader->BWT; + reader->block_delay = BGT; + reader->char_delay = GT + EGT; + rdr_log_dbg(reader, D_ATR, "Setting timings: reader timeout=%u ETU, block_delay=%u ETU, char_delay=%u ETU", reader->read_timeout, reader->block_delay, reader->char_delay); + + break; + } + + default: + return ERROR; + break; + }//switch + SetRightParity(reader); // some reader devices need to get set the right parity + + uint32_t ETU = Fi / D; + if(atr->hbn >= 6 && !memcmp(atr->hb, "IRDETO", 6) && reader->protocol_type == ATR_PROTOCOL_TYPE_T14) + { + ETU = 0; + reader->worketu *= 2; // overclocked T14 needs this otherwise high ecm reponses + } + + struct s_cardreader_settings s = + { + .ETU = ETU, + .EGT = EGT, + .P = 5, + .I = I, + .F = Fi, + .Fi = (uint16_t) Fi, + .Ni = N, + .D = D, + .WWT = WWT, + .BGT = BGT, + }; + + if(crdr_ops->write_settings) + { + call(crdr_ops->write_settings(reader, &s)); + } + +/* + if(reader->typ == R_INTERNAL) + { + if(reader->cardmhz > 2000) + { + rdr_log(reader, "PLL Reader: ATR Fsmax is %i MHz, clocking card to %.2f Mhz (nearest possible mhz specified reader->mhz)", atr_fs_table[FI] / 1000000, (float) reader->mhz / 100); + } + else + { + rdr_log(reader, "ATR Fsmax is %i MHz, clocking card to %.2f (specified in reader->mhz)", atr_fs_table[FI] / 1000000, (float) reader->mhz / 100); + } + } + else + { + if ((reader->typ == R_SMART) && (reader->autospeed == 1)) + { + rdr_log(reader, "ATR Fsmax is %i MHz, clocking card to ATR Fsmax for smartreader cardspeed of %.2f MHz (specified in reader->mhz)", atr_fs_table[FI] / 1000000, (float) reader->mhz / 100); + } + else + { + rdr_log(reader, "ATR Fsmax is %i MHz, clocking card to wanted user cardclock of %.2f MHz (specified in reader->mhz)",atr_fs_table[FI] / 1000000, (float) reader->mhz / 100); + } + } +*/ + + //Communicate to T1 card IFSD -> we use same as IFSC + if(reader->protocol_type == ATR_PROTOCOL_TYPE_T1 && reader->ifsc != DEFAULT_IFSC && !crdr_ops->skip_setting_ifsc) + { + unsigned char rsp[CTA_RES_LEN]; + uint16_t lr = 0; + int32_t ret; + unsigned char tmp[] = { 0x21, 0xC1, 0x01, 0x00, 0x00 }; + tmp[3] = reader->ifsc; // Information Field size + tmp[4] = reader->ifsc ^ 0xE1; + ret = Protocol_T1_Command(reader, tmp, sizeof(tmp), rsp, &lr); + if(ret != OK) + { + rdr_log(reader, "Warning: Card returned error on setting ifsd value to %d", reader->ifsc); + } + else + { + rdr_log_dbg(reader, D_ATR, "Card responded ok for ifsd request of %d", reader->ifsc); + } + } + return OK; +} + +static unsigned char PPS_GetPCK(unsigned char *block, uint32_t length) +{ + unsigned char pck; + uint32_t i; + pck = block[0]; + for(i = 1; i < length; i++) + { + pck ^= block[i]; + } + return pck; +} +#endif diff --git a/csctapi/icc_async.h b/csctapi/icc_async.h new file mode 100644 index 0000000..d4577fc --- /dev/null +++ b/csctapi/icc_async.h @@ -0,0 +1,57 @@ +/* + icc_async.h + Asynchronous integrated circuit cards handling functions + + This file is part of the Unix driver for Towitoko smartcard readers + Copyright (C) 2000 2001 Carlos Prados + + This version is modified by doz21 to work in a special manner ;) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _ICC_ASYNC_ +#define _ICC_ASYNC_ + +#include "atr.h" + +/* + * Exported types definition + */ + +/* Initialization and Deactivation */ +int32_t ICC_Async_Activate(struct s_reader *reader, ATR *newatr, uint16_t deprecated); +int32_t ICC_Async_Close(struct s_reader *reader); +int32_t ICC_Async_Device_Init(struct s_reader *reader); +int32_t ICC_Async_Init_Locks(void); + +/* Attributes */ +int32_t ICC_Async_GetTimings(struct s_reader *reader, uint32_t wait_etu); +int32_t ICC_Async_GetStatus(struct s_reader *reader, int32_t *has_card); + +/* Operations */ +int32_t ICC_Async_CardWrite(struct s_reader *reader, unsigned char *cmd, uint16_t lc, unsigned char *rsp, uint16_t *lr); +int32_t ICC_Async_Transmit(struct s_reader *reader, uint32_t size, uint32_t expectedlen, unsigned char *buffer, uint32_t delay, uint32_t timeout); +int32_t ICC_Async_Receive(struct s_reader *reader, uint32_t size, unsigned char *buffer, uint32_t delay, uint32_t timeout); + +void ICC_Async_DisplayMsg(struct s_reader *, char *msg); +int32_t ICC_Async_Reset(struct s_reader *, struct s_ATR *, int32_t (*rdr_activate_card)(struct s_reader *, struct s_ATR *, uint16_t deprecated), int32_t (*rdr_get_cardsystem)(struct s_reader *, struct s_ATR *)); + +#ifdef READER_NAGRA_MERLIN +void calculate_cak7_cmd(struct s_reader *reader, uint8_t *cmdin,uint8_t cmdlen,uint8_t *cmdout); +void do_cak7_cmd(struct s_reader *reader,unsigned char *cta_res, uint16_t *p_cta_lr,uint8_t *data,uint8_t inlen,uint8_t resplen); +#endif + +#endif /* _ICC_ASYNC_ */ diff --git a/csctapi/ifd_amsmc.c b/csctapi/ifd_amsmc.c new file mode 100644 index 0000000..3826223 --- /dev/null +++ b/csctapi/ifd_amsmc.c @@ -0,0 +1,92 @@ +/* + ifd_amsmc.c + This module provides IFD handling functions for Amlogic SMC internal reader. +*/ + +#include "../globals.h" + +#ifdef CARDREADER_INTERNAL_AMSMC + +#include + +#include "atr.h" +#include "io_serial.h" + +#define AMSMC_MAX_ATR_LEN 33 + +struct am_smc_atr +{ + unsigned char atr[AMSMC_MAX_ATR_LEN]; + int atr_len; +}; + +#define AMSMC_IOC_MAGIC 'C' +#define AMSMC_IOC_RESET _IOR(AMSMC_IOC_MAGIC, 0x00, struct am_smc_atr) +#define AMSMC_IOC_GET_STATUS _IOR(AMSMC_IOC_MAGIC, 0x01, int) + +#define OK 0 +#define ERROR 1 + +static int32_t Amsmc_GetStatus(struct s_reader *reader, int32_t *status) +{ + call(ioctl(reader->handle, AMSMC_IOC_GET_STATUS, status) < 0); + return OK; +} + +static int32_t Amsmc_Activate(struct s_reader *reader, ATR *atr) +{ + struct am_smc_atr smc_atr; + if(ioctl(reader->handle, AMSMC_IOC_RESET, &smc_atr) < 0) + { + rdr_log(reader, "Error: %s ioctl(AMSMC_IOC_RESET) failed. (%d:%s)", __func__, errno, strerror(errno)); + return ERROR; + } + if(ATR_InitFromArray(atr, smc_atr.atr, smc_atr.atr_len) == ERROR) + { + rdr_log(reader, "WARNING: ATR is invalid!"); + return ERROR; + } + return OK; +} + +static int32_t Amsmc_Init(struct s_reader *reader) +{ + const int flags = O_RDWR | O_NOCTTY; + reader->handle = open(reader->device, flags); + if(reader->handle < 0) + { + rdr_log(reader, "ERROR: Opening device %s (errno=%d %s)", reader->device, errno, strerror(errno)); + return ERROR; + } + return OK; +} + +static int32_t Amsmc_Close(struct s_reader *reader) +{ + rdr_log_dbg(reader, D_DEVICE, "Closing AMSMC device %s", reader->device); + if(reader->handle >= 0) + { + if(close(reader->handle) != 0) + { + return ERROR; + } + reader->handle = -1; + } + return OK; +} + +const struct s_cardreader cardreader_internal_amsmc = +{ + .desc = "internal", + .typ = R_INTERNAL, + .flush = 1, + .max_clock_speed = 1, + .reader_init = Amsmc_Init, + .get_status = Amsmc_GetStatus, + .activate = Amsmc_Activate, + .transmit = IO_Serial_Transmit, + .receive = IO_Serial_Receive, + .close = Amsmc_Close, +}; + +#endif diff --git a/csctapi/ifd_azbox.c b/csctapi/ifd_azbox.c new file mode 100644 index 0000000..70872ef --- /dev/null +++ b/csctapi/ifd_azbox.c @@ -0,0 +1,172 @@ +#include"../globals.h" + +#ifdef CARDREADER_INTERNAL_AZBOX + +#include "../oscam-time.h" + +#include "atr.h" +#include "../extapi/openxcas/openxcas_api.h" +#include "../extapi/openxcas/openxcas_smartcard.h" +#include "io_serial.h" + +#define OK 0 +#define ERROR 1 + +static int32_t sc_mode; + +static int32_t _GetStatus(struct s_reader *reader) +{ + unsigned char buf[64]; + memset(buf, 0, sizeof(buf)); + + return ioctl(reader->handle, SCARD_IOC_CHECKCARD, &buf); +} + +static int32_t Azbox_Reader_Init(struct s_reader *reader) +{ + rdr_log_dbg(reader, D_DEVICE, "Init"); + + if((reader->handle = openxcas_get_smartcard_device(0)) < 0) + { + rdr_log_dbg(reader, D_DEVICE, "Init reader failed"); + return 0; + } + rdr_log_dbg(reader, D_DEVICE, "Init reader %d succeeded", reader->handle); + return OK; +} + +static int32_t Azbox_SetMode(struct s_reader *reader, int32_t mode) +{ + sc_mode = mode; + + rdr_log(reader, "sc_mode %d", sc_mode); + return OK; +} + +static int32_t Azbox_GetStatus(struct s_reader *reader, int32_t *status) +{ + int32_t card_status = _GetStatus(reader); + + if(card_status != 0x03 && card_status != 0x01) + { *status = 0; } + else + { *status = 1; } + + //rdr_log_dbg(reader, D_IFD, "openxcas sc: status = %d", *status); + return OK; +} + +static int32_t Azbox_Reset(struct s_reader *reader, ATR *atr) +{ + rdr_log_dbg(reader, D_IFD, "Azbox resetting card"); + unsigned char buf[ATR_MAX_SIZE]; + int32_t card_status; + + buf[0] = 0x03; + buf[1] = 0x01; + card_status = ioctl(reader->handle, SCARD_IOC_WARMRESET, &buf); + + while((card_status = _GetStatus(reader)) != 0x03) + { cs_sleepms(50); } + + buf[0] = 0x02; + buf[1] = sc_mode; + sc_mode = ioctl(reader->handle, SCARD_IOC_CHECKCARD, &buf); + + int32_t frequency; + frequency = reader->cardmhz * 10000L; + + rdr_log(reader, "Set reader mhz = %.2f", (float) frequency / 1000000L); + + int32_t n = 0; + buf[0] = 0x01; + n = ioctl(reader->handle, SCARD_IOC_CHECKCARD, &buf); + //cs_sleepms(50); + + rdr_log_dbg(reader, D_IFD, "Waiting for card ATR Response..."); + + int32_t FI = (buf[n] >> 4); + + int32_t Fi = atr_f_table[FI]; + float fmax = atr_fs_table[FI]; + + int32_t D = ATR_DEFAULT_D; + int32_t DI = (buf[n] & 0x0F); + D = atr_d_table[DI]; + + rdr_log_dbg(reader, D_ATR, "Advertised max cardfrequency is %.2f (Fmax), frequency divider is %d", fmax / 1000000L, Fi); + + if(D == 0) { D = 1;} + rdr_log_dbg(reader, D_ATR, "Bitrate adjustment is %d (D)", D); + + rdr_log_dbg(reader, D_ATR, "Work ETU = %.2f us assuming card runs at %.2f Mhz", (double)((double)(1 / (double)D) * ((double)Fi / (double)((double)frequency / 1000000))), (float) frequency / 1000000); + + rdr_log_dbg(reader, D_ATR, "Initial ETU = %.2f us", (double)372 / (double)frequency * 1000000); + + rdr_log_dbg(reader, D_IFD, "ATR Fsmax is %.2f MHz, Work ETU is %.2f us, clocking card to %.2f MHz", + fmax / 1000000, (double)((double)(1 / (double)D) * ((double)Fi / (double)((double)frequency / 1000000))), (float) frequency / 1000000); + + if(ATR_InitFromArray(atr, buf, n) == ERROR) + { + rdr_log(reader, "WARNING: ATR is invalid!"); + return ERROR; + } + + rdr_log_dbg(reader, D_IFD, "Card activated"); + return OK; +} + +static int32_t Azbox_Reader_Close(struct s_reader *reader) +{ + rdr_log_dbg(reader, D_IFD, "Deactivating card"); + + if((reader->handle = openxcas_release_smartcard_device(0)) > 0) + { + rdr_log_dbg(reader, D_DEVICE, "Closing reader %d", reader->handle); + return 0; + } + return OK; +} + +static int32_t Azbox_do_reset(struct s_reader *reader, struct s_ATR *atr, + int32_t (*rdr_activate_card)(struct s_reader *, struct s_ATR *, uint16_t deprecated), + int32_t (*rdr_get_cardsystem)(struct s_reader *, struct s_ATR *)) +{ + int32_t ret = 0; + int32_t i; + if(reader->azbox_mode != -1) + { + Azbox_SetMode(reader, reader->azbox_mode); + if(!rdr_activate_card(reader, atr, 0)) + { return -1; } + ret = rdr_get_cardsystem(reader, atr); + } + else + { + for(i = 0; i < AZBOX_MODES; i++) + { + Azbox_SetMode(reader, i); + if(!rdr_activate_card(reader, atr, 0)) + { return -1; } + ret = rdr_get_cardsystem(reader, atr); + if(ret) + { break; } + } + } + return ret; +} + +const struct s_cardreader cardreader_internal_azbox = +{ + .desc = "internal", + .typ = R_INTERNAL, + .reader_init = Azbox_Reader_Init, + .get_status = Azbox_GetStatus, + .activate = Azbox_Reset, + .transmit = IO_Serial_Transmit, + .receive = IO_Serial_Receive, + .close = Azbox_Reader_Close, + .do_reset = Azbox_do_reset, +}; + +#endif diff --git a/csctapi/ifd_cool.c b/csctapi/ifd_cool.c new file mode 100644 index 0000000..74860f2 --- /dev/null +++ b/csctapi/ifd_cool.c @@ -0,0 +1,333 @@ +/* + This module provides IFD handling functions for Coolstream internal reader. +*/ + +#include"../globals.h" + +#if defined(CARDREADER_INTERNAL_COOLAPI) || defined(CARDREADER_INTERNAL_COOLAPI2) +#include "../extapi/coolapi.h" +#include "../oscam-string.h" +#include "../oscam-time.h" +#include "atr.h" + +#define OK 0 +#define ERROR 1 + +extern int32_t cool_kal_opened; + +struct cool_data +{ + void *handle; //device handle for coolstream + uint8_t cardbuffer[512]; + uint32_t cardbuflen; + int8_t pps; +}; + +static int32_t Cool_Init(struct s_reader *reader) +{ + char *device = reader->device; + int32_t reader_nb = 0; + // this is to stay compatible with older config. + if(cs_strlen(device)) + { reader_nb = atoi((const char *)device); } + if(reader_nb > 1) + { + // there are only 2 readers in the coolstream : 0 or 1 + rdr_log(reader, "Coolstream reader device can only be 0 or 1"); + return 0; + } + if(!cs_malloc(&reader->crdr_data, sizeof(struct cool_data))) + { return ERROR; } + struct cool_data *crdr_data = reader->crdr_data; + if(cnxt_smc_open(&crdr_data->handle, &reader_nb, NULL, NULL)) + { return 0; } + + int32_t ret = cnxt_smc_enable_flow_control(crdr_data->handle, 0); + coolapi_check_error("cnxt_smc_enable_flow_control", ret); + + crdr_data->cardbuflen = 0; + crdr_data->pps = 0; + return OK; +} + +static int32_t Cool_FastReset(struct s_reader *reader) +{ + struct cool_data *crdr_data = reader->crdr_data; + int32_t n = ATR_MAX_SIZE, ret; + unsigned char buf[ATR_MAX_SIZE]; + + //reset card + ret = cnxt_smc_reset_card(crdr_data->handle, ATR_TIMEOUT, NULL, NULL); + coolapi_check_error("cnxt_smc_reset_card", ret); + cs_sleepms(50); + ret = cnxt_smc_get_atr(crdr_data->handle, buf, &n); + coolapi_check_error("cnxt_smc_get_atr", ret); + + return OK; +} + +static int32_t Cool_SetClockrate(struct s_reader *reader, int32_t mhz) +{ + struct cool_data *crdr_data = reader->crdr_data; + uint32_t clk; + clk = mhz * 10000; + int32_t ret = cnxt_smc_set_clock_freq(crdr_data->handle, clk); + coolapi_check_error("cnxt_smc_set_clock_freq", ret); + call(Cool_FastReset(reader)); + rdr_log_dbg(reader, D_DEVICE, "COOL: clock successfully set to %i", clk); + return OK; +} + +static int32_t Cool_GetStatus(struct s_reader *reader, int32_t *in) +{ + struct cool_data *crdr_data = reader->crdr_data; + if(cool_kal_opened) + { + int32_t state; + int32_t ret = cnxt_smc_get_state(crdr_data->handle, &state); + if(ret) + { + coolapi_check_error("cnxt_smc_get_state", ret); + return ERROR; + } + //state = 0 no card, 1 = not ready, 2 = ready + if(state) + { *in = 1; } //CARD, even if not ready report card is in, or it will never get activated + else + { *in = 0; } //NOCARD + } + else + { + *in = 0; + } + return OK; +} + +static int32_t Cool_Reset(struct s_reader *reader, ATR *atr) +{ + struct cool_data *crdr_data = reader->crdr_data; + int32_t ret; + + if(!reader->ins7e11_fast_reset) + { + //set freq to reader->cardmhz if necessary + uint32_t clk; + + ret = cnxt_smc_get_clock_freq(crdr_data->handle, &clk); + coolapi_check_error("cnxt_smc_get_clock_freq", ret); + if(clk / 10000 != (uint32_t)reader->cardmhz) + { + rdr_log_dbg(reader, D_DEVICE, "COOL: clock freq: %i, scheduling change to %i for card reset", + clk, reader->cardmhz * 10000); + call(Cool_SetClockrate(reader, reader->cardmhz)); + } + } + else + { + rdr_log(reader, "Doing fast reset"); + } + + //reset card + ret = cnxt_smc_reset_card(crdr_data->handle, ATR_TIMEOUT, NULL, NULL); + coolapi_check_error("cnxt_smc_reset_card", ret); + cs_sleepms(50); + int32_t n = ATR_MAX_SIZE; + unsigned char buf[ATR_MAX_SIZE]; + ret = cnxt_smc_get_atr(crdr_data->handle, buf, &n); + coolapi_check_error("cnxt_smc_get_atr", ret); + + call(!(ATR_InitFromArray(atr, buf, n) != ERROR)); + { + cs_sleepms(50); + return OK; + } +} + +static int32_t Cool_Transmit(struct s_reader *reader, unsigned char *sent, uint32_t size, uint32_t expectedlen, uint32_t UNUSED(delay), uint32_t UNUSED(timeout)) +{ + struct cool_data *crdr_data = reader->crdr_data; + int32_t ret; + memset(crdr_data->cardbuffer, 0, 512); + + if(reader->protocol_type == ATR_PROTOCOL_TYPE_T0) + { + crdr_data->cardbuflen = expectedlen; + ret = cnxt_smc_read_write(crdr_data->handle, 0, sent, size, crdr_data->cardbuffer, &crdr_data->cardbuflen, 0, NULL); + } + else + { + crdr_data->cardbuflen = 512; + ret = cnxt_smc_read_write(crdr_data->handle, 0, sent, size, crdr_data->cardbuffer, &crdr_data->cardbuflen, 4000, NULL); + } + + coolapi_check_error("cnxt_smc_read_write", ret); + + rdr_log_dump_dbg(reader, D_DEVICE, sent, size, "COOL Transmit:"); + + if(ret) + { return ERROR; } + return OK; +} + +static int32_t Cool_Receive(struct s_reader *reader, unsigned char *data, uint32_t size, uint32_t UNUSED(delay), uint32_t UNUSED(timeout)) +{ + struct cool_data *crdr_data = reader->crdr_data; + if(size > crdr_data->cardbuflen) + { size = crdr_data->cardbuflen; } //never read past end of buffer + memcpy(data, crdr_data->cardbuffer, size); + crdr_data->cardbuflen -= size; + memmove(crdr_data->cardbuffer, crdr_data->cardbuffer + size, crdr_data->cardbuflen); + rdr_log_dump_dbg(reader, D_DEVICE, data, size, "COOL Receive:"); + return OK; +} + +static void Cool_Print_Comm_Parameters(struct s_reader *reader) +{ + struct cool_data *crdr_data = reader->crdr_data; + uint16_t F; + uint8_t D; + int32_t ret = cnxt_smc_get_F_D_factors(crdr_data->handle, &F, &D); + coolapi_check_error("cnxt_smc_get_F_D_factors", ret); + + char *protocol; + CNXT_SMC_COMM comm; + ret = cnxt_smc_get_comm_parameters(crdr_data->handle, &comm); + coolapi_check_error("cnxt_smc_get_comm_parameters", ret); + if(comm.protocol == 0x01) + { protocol = "T0"; } + else if(comm.protocol == 0x02) + { protocol = "T1"; } + else if(comm.protocol == 0x04) + { protocol = "T14"; } + else + { protocol = "unknown"; } + + rdr_log(reader, "Driver Settings: Convention=%s, Protocol=%s, FI=%i, F=%i, N=%i, DI=%i, D=%i, PI1=%i, PI2=%i, II=%i, TXRetries=%i, RXRetries=%i, FilterProtocolBytes=%i", comm.convention ? "Inverse" : "Direct", protocol, comm.FI, F, comm.N, comm.DI, D, comm.PI1, comm.PI2, comm.II, comm.retries.TXRetries, comm.retries.RXRetries, comm.filterprotocolbytes); + + CNXT_SMC_TIMEOUT timeout; + ret = cnxt_smc_get_config_timeout(crdr_data->handle, &timeout); + coolapi_check_error("cnxt_smc_get_config_timeout", ret); + + rdr_log(reader, "Driver Timeouts: CardActTime=%i, CardDeactTime=%i, ATRSTime=%i, ATRDTime=%i, BLKTime=%i, CHTime=%i, CHGuardTime=%i, BKGuardTime=%i", timeout.CardActTime, timeout.CardDeactTime, timeout.ATRSTime, timeout.ATRDTime, timeout.BLKTime, timeout.CHTime, timeout.CHGuardTime, timeout.BKGuardTime); + +} + +static int32_t Cool_WriteSettings(struct s_reader *reader, struct s_cardreader_settings *s) +{ + struct cool_data *crdr_data = reader->crdr_data; + //first set freq back to reader->mhz if necessary + uint32_t clk; + int32_t ret = cnxt_smc_get_clock_freq(crdr_data->handle, &clk); + coolapi_check_error("cnxt_smc_get_clock_freq", ret); + if(clk / 10000 != (uint32_t)reader->mhz) + { + rdr_log_dbg(reader, D_DEVICE, "COOL: clock freq: %i, scheduling change to %i", clk, reader->mhz * 10000); + call(Cool_SetClockrate(reader, reader->mhz)); + } + + uint32_t BLKTime = 0, CHTime = 0; + uint8_t BKGuardTime = 0; + switch(reader->protocol_type) + { + case ATR_PROTOCOL_TYPE_T1: + if(reader->BWT > 11) + { BLKTime = (reader->BWT - 11); } + if(reader->CWT > 11) + { CHTime = (reader->CWT - 11); } + if(s->BGT > 11) + { BKGuardTime = (s->BGT - 11); } + else + { BKGuardTime = 11; } //For T1, the BGT minimum time shall be 22 work etus. BGT is effectively offset by 11 etus internally. + if(!crdr_data->pps) + { + ret = cnxt_smc_set_F_D_factors(crdr_data->handle, s->F, s->D); + coolapi_check_error("cnxt_smc_set_F_D_factors", ret); + } + break; + case ATR_PROTOCOL_TYPE_T0: + case ATR_PROTOCOL_TYPE_T14: + default: + BLKTime = 0; + if(s->WWT > 12) + { CHTime = (s->WWT - 12); } + if(s->BGT > 12) + { BKGuardTime = (s->BGT - 12); } + if(BKGuardTime < 4) + { BKGuardTime = 4; } //For T0, the BGT minimum time shall be 16 work etus. BGT is effectively offset by 12 etus internally. + if(!crdr_data->pps) + { + if(reader->protocol_type == ATR_PROTOCOL_TYPE_T14) + { + ret = cnxt_smc_set_F_D_factors(crdr_data->handle, 620, 1); + } + else + { + ret = cnxt_smc_set_F_D_factors(crdr_data->handle, s->F, s->D); + } + coolapi_check_error("cnxt_smc_set_F_D_factors", ret); + } + break; + } + ret = cnxt_smc_set_convention(crdr_data->handle, reader->convention); + coolapi_check_error("cnxt_smc_set_convention", ret); + + CNXT_SMC_TIMEOUT timeout; + ret = cnxt_smc_get_config_timeout(crdr_data->handle, &timeout); + coolapi_check_error("cnxt_smc_get_config_timeout", ret); + timeout.BLKTime = BLKTime; + timeout.CHTime = CHTime; + timeout.CHGuardTime = s->EGT; + timeout.BKGuardTime = BKGuardTime; + ret = cnxt_smc_set_config_timeout(crdr_data->handle, timeout); + coolapi_check_error("cnxt_smc_set_config_timeout", ret); + + Cool_Print_Comm_Parameters(reader); + + return OK; +} + +static int32_t Cool_Close(struct s_reader *reader) +{ + struct cool_data *crdr_data = reader->crdr_data; + if(cool_kal_opened) + { + int32_t ret = cnxt_smc_close(crdr_data->handle); + coolapi_check_error("cnxt_smc_close", ret); + } + return OK; +} + +static int32_t Cool_SetProtocol(struct s_reader *reader, unsigned char *params, uint32_t *UNUSED(length), uint32_t UNUSED(len_request)) +{ + struct cool_data *crdr_data = reader->crdr_data; + unsigned char pps[4], response[6]; + uint8_t len = 0; + + //Driver sets PTSS and PCK on its own + pps[0] = params[1]; //PPS0 + pps[1] = params[2]; //PPS1 + + int32_t ret = cnxt_smc_start_pps(crdr_data->handle, pps, response, &len, 1); + coolapi_check_error("cnxt_smc_start_pps", ret); + if(ret) + { return ERROR; } + crdr_data->pps = 1; + return OK; +} + +const struct s_cardreader cardreader_internal_cool = +{ + .desc = "internal", + .typ = R_INTERNAL, + .max_clock_speed = 1, + .reader_init = Cool_Init, + .get_status = Cool_GetStatus, + .activate = Cool_Reset, + .transmit = Cool_Transmit, + .receive = Cool_Receive, + .close = Cool_Close, + .write_settings = Cool_WriteSettings, + .set_protocol = Cool_SetProtocol, +}; + +#endif diff --git a/csctapi/ifd_db2com.c b/csctapi/ifd_db2com.c new file mode 100644 index 0000000..1cdf8ba --- /dev/null +++ b/csctapi/ifd_db2com.c @@ -0,0 +1,152 @@ +#include "../globals.h" + +#ifdef CARDREADER_DB2COM +#include "../oscam-time.h" +#include "icc_async.h" +#include "ifd_phoenix.h" +#include "io_serial.h" + +// Multicam defines +#define DEV_MULTICAM "/dev/multicam" +#define MULTICAM_GET_PCDAT 10 +#define MULTICAM_SET_PCDAT 13 + +#define OK 0 +#define ERROR 1 + +bool detect_db2com_reader(struct s_reader *reader) +{ + struct stat sb; + if(stat(DEV_MULTICAM, &sb) == -1) + { return false; } + if(stat(reader->device, &sb) == 0) + { + if(S_ISCHR(sb.st_mode)) + { + int32_t dev_major = major(sb.st_rdev); + int32_t dev_minor = minor(sb.st_rdev); + if(dev_major == 4 || dev_major == 5) + { + int32_t rc; + switch(dev_minor & 0x3F) + { + case 0: + rc = R_DB2COM1; + break; + case 1: + rc = R_DB2COM2; + break; + default: + return false; + } + reader->typ = rc; + } + rdr_log_dbg(reader, D_READER, "device is major: %d, minor: %d, typ=%d", dev_major, dev_minor, reader->typ); + } + } + return true; +} + +static int32_t db2com_init(struct s_reader *reader) +{ + if(reader->typ != R_DB2COM1 && reader->typ != R_DB2COM2) + { detect_db2com_reader(reader); } + + reader->handle = open(reader->device, O_RDWR | O_NOCTTY | O_SYNC); + if(reader->handle < 0) + { + rdr_log(reader, "ERROR: Opening device %s (errno=%d %s)", reader->device, errno, strerror(errno)); + return ERROR; + } + if((reader->fdmc = open(DEV_MULTICAM, O_RDWR)) < 0) + { + rdr_log(reader, "ERROR: Opening device %s (errno=%d %s)", DEV_MULTICAM, errno, strerror(errno)); + close(reader->handle); + return ERROR; + } + if(Phoenix_Init(reader)) + { + rdr_log(reader, "ERROR: Phoenix_Init returns error"); + Phoenix_Close(reader); + return ERROR; + } + return OK; +} + +static int32_t db2com_get_status(struct s_reader *reader, int32_t *status) +{ + *status = 0; + uint16_t msr = 1; + IO_Serial_Ioctl_Lock(reader, 1); + ioctl(reader->fdmc, MULTICAM_GET_PCDAT, &msr); + if(reader->typ == R_DB2COM2) + { *status = !(msr & 1); } + else + { *status = (msr & 0x0f00) == 0x0f00; } + IO_Serial_Ioctl_Lock(reader, 0); + return OK; +} + +static bool db2com_DTR_RTS(struct s_reader *reader, int32_t *dtr, int32_t *rts) +{ + int32_t rc; + uint16_t msr; + uint16_t rts_bits[2] = { 0x10, 0x800 }; + uint16_t dtr_bits[2] = {0x100, 0 }; + int32_t mcport = reader->typ == R_DB2COM2; + + rc = ioctl(reader->fdmc, MULTICAM_GET_PCDAT, &msr); + if(rc < 0) + { return ERROR; } + + if(dtr) + { + rdr_log_dbg(reader, D_DEVICE, "%s DTR:%s", __func__, *dtr ? "set" : "clear"); + if(dtr_bits[mcport]) + { + if(*dtr) + { msr &= (uint16_t)(~dtr_bits[mcport]); } + else + { msr |= dtr_bits[mcport]; } + rc = ioctl(reader->fdmc, MULTICAM_SET_PCDAT, &msr); + } + else + { + rc = 0; // Dummy, can't handle using multicam.o + } + } + + if(rts) + { + rdr_log_dbg(reader, D_DEVICE, "%s RTS:%s", __func__, *rts ? "set" : "clear"); + if(*rts) + { msr &= (uint16_t)(~rts_bits[mcport]); } + else + { msr |= rts_bits[mcport]; } + rc = ioctl(reader->fdmc, MULTICAM_SET_PCDAT, &msr); + } + + if(rc < 0) + { return ERROR; } + return OK; +} + +const struct s_cardreader cardreader_db2com = +{ + .desc = "db2com", + .typ = R_DB2COM1, + .flush = 1, + .need_inverse = 1, + .read_written = 1, + .reader_init = db2com_init, + .get_status = db2com_get_status, + .activate = Phoenix_Reset, + .transmit = IO_Serial_Transmit, + .receive = IO_Serial_Receive, + .close = Phoenix_Close, + .set_parity = IO_Serial_SetParity, + .set_baudrate = IO_Serial_SetBaudrate, + .set_DTS_RTS = db2com_DTR_RTS, +}; + +#endif diff --git a/csctapi/ifd_db2com.h b/csctapi/ifd_db2com.h new file mode 100644 index 0000000..d46b280 --- /dev/null +++ b/csctapi/ifd_db2com.h @@ -0,0 +1,12 @@ +#ifndef _CSCTAPI_IFD_DB2COM_H_ +#define _CSCTAPI_IFD_DB2COM_H_ + +#ifdef CARDREADER_DB2COM +bool detect_db2com_reader(struct s_reader *reader); +extern struct s_cardreader cardreader_db2com; +#else +static inline bool detect_db2com_reader(struct s_reader *UNUSED(reader)) { return false; } +static struct s_cardreader cardreader_db2com; +#endif + +#endif diff --git a/csctapi/ifd_drecas.c b/csctapi/ifd_drecas.c new file mode 100644 index 0000000..fe609a1 --- /dev/null +++ b/csctapi/ifd_drecas.c @@ -0,0 +1,130 @@ +/* + ifd_drecas.c + This module provides IFD handling functions for DreCas reader. +*/ + +#include "../globals.h" +#include "../oscam-string.h" + +#ifdef CARDREADER_DRECAS +#include "../oscam-time.h" +#include "icc_async.h" +#include "ifd_drecas.h" +#include "io_serial.h" + +#define OK 0 +#define ERROR 1 + +int32_t DreCas_Init(struct s_reader *reader) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + if(crdr_ops->flush) { IO_Serial_Flush(reader); } + + rdr_log_dbg(reader, D_IFD, "Initializing reader type=%d", reader->typ); + + /* Default serial port settings */ + if(reader->atr[0] == 0) + { + if(IO_Serial_SetParams(reader, DEFAULT_BAUDRATE, 8, PARITY_NONE, 1, NULL, NULL)) { return ERROR; } + if(crdr_ops->flush) { IO_Serial_Flush(reader); } + } + return OK; +} + +int32_t DreCas_GetStatus(struct s_reader *UNUSED(reader), int32_t *UNUSED(status)) +{ + return OK; +} + +int32_t DreCas_Reset(struct s_reader *reader, ATR *atr) +{ + rdr_log_dbg(reader, D_IFD, "Resetting card"); + rdr_log_dbg(reader, D_IFD, "DreCas_Reset"); + int32_t ret; + uint8_t buf[ATR_MAX_SIZE]; + uint8_t reset_cmd[5] = { 0xDB ,0x03 ,0x00 ,0xC1 ,0xC1 }; + + if(IO_Serial_SetParams(reader, DEFAULT_BAUDRATE, 8, PARITY_NONE, 2, NULL, NULL)) { return ERROR; } + + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + + if(crdr_ops->flush) { IO_Serial_Flush(reader); } + + ret = ERROR; + + IO_Serial_Ioctl_Lock(reader, 1); + + /* Module soft reset */ + + IO_Serial_Write(reader, 0, 0, (uint32_t)sizeof(reset_cmd), reset_cmd); + cs_sleepms(50); + + IO_Serial_Ioctl_Lock(reader, 0); + + int32_t n = 0; + + while(n < ATR_MAX_SIZE && !IO_Serial_Read(reader, 50, ATR_TIMEOUT, 1, buf + n)) + { n++; } + + if(ATR_InitFromArray(atr, buf, n) != ERROR) + { ret = OK; } + + return ret; +} + +int32_t DreCas_Close(struct s_reader *reader) +{ + rdr_log_dbg(reader, D_IFD, "Closing DreCas device %s", reader->device); + IO_Serial_Close(reader); + return OK; +} + +static int32_t mouse_init(struct s_reader *reader) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + reader->handle = open(reader->device, O_RDWR | O_NOCTTY | O_NONBLOCK); + if(reader->handle < 0) + { + rdr_log(reader, "ERROR: Opening device %s (errno=%d %s)", + reader->device, errno, strerror(errno)); + return ERROR; + } + if(DreCas_Init(reader)) + { + rdr_log(reader, "ERROR: DreCas_Init returns error"); + DreCas_Close(reader); + return ERROR; + } + return OK; +} + +static int32_t DreCas_SetParity(struct s_reader *reader, unsigned char UNUSED(parity)) +{ + return IO_Serial_SetParity(reader, PARITY_NONE); +} + +const struct s_cardreader cardreader_drecas = +{ + .desc = "drecas", + .typ = R_DRECAS, + .flush = 1, + .read_written = 0, + .need_inverse = 0, + .reader_init = mouse_init, + .get_status = DreCas_GetStatus, + .activate = DreCas_Reset, + .transmit = IO_Serial_Transmit, + .receive = IO_Serial_Receive, + .close = DreCas_Close, + .set_parity = DreCas_SetParity, + .set_baudrate = IO_Serial_SetBaudrate, +}; + +#endif + diff --git a/csctapi/ifd_drecas.h b/csctapi/ifd_drecas.h new file mode 100644 index 0000000..b39acef --- /dev/null +++ b/csctapi/ifd_drecas.h @@ -0,0 +1,14 @@ +/* + * Header file for DreCas reader. + */ +#ifndef _CSCTAPI_IFD_DRECAS_H_ +#define _CSCTAPI_IFD_DRECAS_H_ + +int32_t DreCas_Init(struct s_reader *reader); +int32_t DreCas_GetStatus(struct s_reader *reader, int32_t *status); +int32_t DreCas_Reset(struct s_reader *reader, ATR *atr); +int32_t DreCas_Close(struct s_reader *reader); +int32_t DreCas_FastReset(struct s_reader *reader, int32_t delay); + +#endif + diff --git a/csctapi/ifd_mp35.c b/csctapi/ifd_mp35.c new file mode 100644 index 0000000..80590b1 --- /dev/null +++ b/csctapi/ifd_mp35.c @@ -0,0 +1,265 @@ +#include "../globals.h" + +#ifdef CARDREADER_MP35 +#include "../oscam-time.h" +#include "atr.h" +#include "ifd_phoenix.h" +#include "io_serial.h" + +#define OK 0 +#define ERROR 1 + +#define ACK 0x06 +#define MP35_WRITE_DELAY 100000 +#define MP35_READ_DELAY 200000 +#define MP35_BREAK_LENGTH 1200000 + +typedef struct +{ + unsigned char current_product; + uint16_t product_fw_version; +} MP35_info; + +// Common command for AD-Teknik readers +static const unsigned char fw_version[] = {0x2a, 0x41}; + +// Commands for AD-Teknik MP3.5 and MP3.6 +//static const unsigned char power_always_on[] = {0x2a, 0x8a}; +//static const unsigned char set_vpp[] = {0x2a, 0x42}; +//static const unsigned char set_data[] = {0x2a, 0x43}; +//static const unsigned char set_oscillator[] = {0x2a, 0x5e}; +//static const unsigned char terminate_com[] = {0x2a, 0x7b}; +//static const unsigned char transthrough_mode[] = {0x2a, 0x7c}; +static const unsigned char phoenix_mode[] = {0x2a, 0x7d}; +//static const unsigned char smartmouse_mode[] = {0x2a, 0x7e}; +static const unsigned char phoenix_6mhz_mode[] = {0x2a, 0x9a}; +//static const unsigned char smartmouse_6mhz_mode[] = {0x2a, 0x9b}; +static const unsigned char fw_info[] = {0x2a, 0xa2}; + +// Commands for AD-Teknik USB Phoenix +static const unsigned char set_mode_osc[] = {0x2a, 0x42}; +static const unsigned char exit_program_mode[] = {0x2a, 0x43}; + +static const struct product +{ + unsigned char code; + const char *product_name; +} product_codes[] = +{ + {0x10, "USB Phoenix"}, + {0x40, "MP3.4"}, + {0x41, "MP3.5"}, + {0x42, "MP3.6 USB"} +}; + +static int32_t mp35_product_info(struct s_reader *reader, unsigned char high, unsigned char low, unsigned char code, MP35_info *info) +{ + int32_t i; + + for(i = 0; i < (int)(sizeof(product_codes) / sizeof(struct product)); i++) + { + if(product_codes[i].code == code) + { + rdr_log(reader, "%s: %s - FW:%02d.%02d", __func__, product_codes[i].product_name, high, low); + info->current_product = code; + info->product_fw_version = (high << 8) | low; + return OK; + } + } + + return ERROR; +} + +static int32_t mp35_reader_init(struct s_reader *reader) +{ + MP35_info reader_info; + unsigned char rec_buf[32]; + unsigned char parameter; + int32_t original_mhz; + int32_t original_cardmhz; + + rdr_log(reader, "%s: started", __func__); + + original_mhz = reader->mhz; + original_cardmhz = reader->cardmhz; + + // MP3.5 commands should be always be written using 9600 baud at 3.57MHz + reader->mhz = 357; + reader->cardmhz = 357; + + int32_t dtr = IO_SERIAL_HIGH; + int32_t cts = IO_SERIAL_HIGH; + + call(IO_Serial_SetParams(reader, 9600, 8, PARITY_NONE, 1, &dtr, &cts)); + + IO_Serial_Sendbreak(reader, MP35_BREAK_LENGTH); + IO_Serial_DTR_Clr(reader); + IO_Serial_DTR_Set(reader); + cs_sleepms(200); + IO_Serial_RTS_Set(reader); + IO_Serial_Flush(reader); + + memset(rec_buf, 0x00, sizeof(rec_buf)); + call(IO_Serial_Write(reader, MP35_WRITE_DELAY, 1000000, 2, fw_version)); + call(IO_Serial_Read(reader, MP35_READ_DELAY, 1000000, 4, rec_buf)); + if(rec_buf[3] != ACK) + { + rdr_log_dbg(reader, D_IFD, "Failed MP35 command: fw_version"); + return ERROR; + } + + if(mp35_product_info(reader, rec_buf[1], rec_buf[0], rec_buf[2], &reader_info) != OK) + { + rdr_log(reader, "%s: unknown product code", __func__); + return ERROR; + } + + if(reader_info.current_product == 0x10) // USB Phoenix + { + if(original_mhz == 357) + { + rdr_log(reader, "%s: Using oscillator 1 (3.57MHz)", __func__); + parameter = 0x01; + } + else if(original_mhz == 368) + { + rdr_log(reader, "%s: Using oscillator 2 (3.68MHz)", __func__); + parameter = 0x02; + } + else if(original_mhz == 600) + { + rdr_log(reader, "%s: Using oscillator 3 (6.00MHz)", __func__); + parameter = 0x03; + } + else + { + rdr_log(reader, "%s: MP35 support only mhz=357, mhz=368 or mhz=600", __func__); + rdr_log(reader, "%s: Forced oscillator 1 (3.57MHz)", __func__); + parameter = 0x01; + original_mhz = 357; + } + memset(rec_buf, 0x00, sizeof(rec_buf)); + call(IO_Serial_Write(reader, MP35_WRITE_DELAY, 1000000, 2, set_mode_osc)); + call(IO_Serial_Write(reader, MP35_WRITE_DELAY, 1000000, 1, ¶meter)); + call(IO_Serial_Read(reader, MP35_READ_DELAY, 1000000, 1, rec_buf)); // Read ACK from previous command + if(rec_buf[0] != ACK) + { + rdr_log_dbg(reader, D_IFD, "Failed MP35 command: set_mode_osc"); + return ERROR; + } + rdr_log_dbg(reader, D_IFD, "%s: Leaving programming mode", __func__); + memset(rec_buf, 0x00, sizeof(rec_buf)); + call(IO_Serial_Write(reader, MP35_WRITE_DELAY, 1000000, 2, exit_program_mode)); + call(IO_Serial_Read(reader, MP35_READ_DELAY, 1000000, 1, rec_buf)); + if(rec_buf[0] != ACK) + { + rdr_log_dbg(reader, D_IFD, "Failed MP35 command: exit_program_mode"); + return ERROR; + } + } + else //MP3.5 or MP3.6 + { + if(reader_info.product_fw_version >= 0x0500) + { + int32_t info_len; + char info[sizeof(rec_buf) - 2]; + + memset(rec_buf, 0x00, sizeof(rec_buf)); + call(IO_Serial_Write(reader, MP35_WRITE_DELAY, 1000000, 2, fw_info)); + call(IO_Serial_Read(reader, MP35_READ_DELAY, 1000000, 1, rec_buf)); + info_len = rec_buf[0]; + call(IO_Serial_Read(reader, MP35_READ_DELAY, 1000000, info_len + 1, rec_buf)); + if(rec_buf[info_len] != ACK) + { + rdr_log_dbg(reader, D_IFD, "Failed MP35 command: fw_info"); + return ERROR; + } + memcpy(info, rec_buf, info_len); + info[info_len] = '\0'; + rdr_log(reader, "%s: FW Info - %s", __func__, info); + } + + memset(rec_buf, 0x00, sizeof(rec_buf)); + if(original_mhz == 357) + { + rdr_log(reader, "%s: Using oscillator 1 (3.57MHz)", __func__); + call(IO_Serial_Write(reader, MP35_WRITE_DELAY, 1000000, 2, phoenix_mode)); + } + else if(original_mhz == 600) + { + rdr_log(reader, "%s: Using oscillator 2 (6.00MHz)", __func__); + call(IO_Serial_Write(reader, MP35_WRITE_DELAY, 1000000, 2, phoenix_6mhz_mode)); + } + else + { + rdr_log(reader, "%s: MP35 support only mhz=357 or mhz=600", __func__); + rdr_log(reader, "%s: Forced oscillator 1 (3.57MHz)", __func__); + call(IO_Serial_Write(reader, MP35_WRITE_DELAY, 1000000, 2, phoenix_mode)); + original_mhz = 357; + } + tcdrain(reader->handle); + } + + // We might have switched oscillator here + reader->mhz = original_mhz; + reader->cardmhz = original_cardmhz; + + /* Default serial port settings */ + if(reader->atr[0] == 0) + { + IO_Serial_Flush(reader); + call(IO_Serial_SetParams(reader, DEFAULT_BAUDRATE, 8, PARITY_EVEN, 2, NULL, NULL)); + } + + return OK; +} + +static int32_t mp35_close(struct s_reader *reader) +{ + rdr_log_dbg(reader, D_IFD, "Closing MP35 device %s", reader->device); + + IO_Serial_DTR_Clr(reader); + IO_Serial_Close(reader); + + return OK; +} + +static int32_t mp35_init(struct s_reader *reader) +{ + reader->handle = open(reader->device, O_RDWR | O_NOCTTY | O_NONBLOCK); + if(reader->handle < 0) + { + rdr_log(reader, "ERROR: Opening device %s (errno=%d %s)", + reader->device, errno, strerror(errno)); + return ERROR; + } + if(IO_Serial_SetParams(reader, DEFAULT_BAUDRATE, 8, PARITY_EVEN, 2, NULL, NULL)) + { return ERROR; } + + if(mp35_reader_init(reader)) + { + rdr_log(reader, "ERROR: mp35_reader_init returned error"); + mp35_close(reader); + return ERROR; + } + return OK; +} + +const struct s_cardreader cardreader_mp35 = +{ + .desc = "mp35", + .typ = R_MOUSE, + .flush = 1, + .need_inverse = 1, + .read_written = 1, + .reader_init = mp35_init, + .get_status = IO_Serial_GetStatus, + .activate = Phoenix_Reset, + .transmit = IO_Serial_Transmit, + .receive = IO_Serial_Receive, + .close = mp35_close, + .set_parity = IO_Serial_SetParity, + .set_baudrate = IO_Serial_SetBaudrate, +}; + +#endif diff --git a/csctapi/ifd_pcsc.c b/csctapi/ifd_pcsc.c new file mode 100644 index 0000000..0e6dcc6 --- /dev/null +++ b/csctapi/ifd_pcsc.c @@ -0,0 +1,390 @@ +#include "../globals.h" + +#ifdef CARDREADER_PCSC + +#include "atr.h" +#include "../oscam-string.h" + +#if defined(__CYGWIN__) +#define __reserved +#define __nullnullterminated +#include +#include +#endif + +#if !defined(__CYGWIN__) && !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) +#include +#include +#include +#include +#endif + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#include "pcsclite.h" +#include "winscard.h" +#include "wintypes.h" +#include "reader.h" +#endif + +#ifndef ERR_INVALID +#define ERR_INVALID -1 +#endif + +#if defined(__CYGWIN__) +#undef OK +#undef ERROR +#undef LOBYTE +#undef HIBYTE +#endif + +#define OK 0 +#define ERROR 1 + +struct pcsc_data +{ + bool pcsc_has_card; + char pcsc_name[128]; + SCARDCONTEXT hContext; + SCARDHANDLE hCard; + DWORD dwActiveProtocol; +}; + +static int32_t pcsc_init(struct s_reader *pcsc_reader) +{ + ULONG rv; + DWORD dwReaders = 0; + LPSTR mszReaders = NULL; + char *ptr, **readers = NULL; + char *device = pcsc_reader->device; + int32_t nbReaders; + int32_t reader_nb; + + rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC establish context for PCSC pcsc_reader %s", device); + SCARDCONTEXT hContext; + memset(&hContext, 0, sizeof(hContext)); + rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); + if(rv == SCARD_S_SUCCESS) + { + if(!cs_malloc(&pcsc_reader->crdr_data, sizeof(struct pcsc_data))) + { return ERROR; } + struct pcsc_data *crdr_data = pcsc_reader->crdr_data; + crdr_data->hContext = hContext; + + // here we need to list the pcsc readers and get the name from there, + // the pcsc_reader->device should contain the pcsc_reader number + // and after the actual device name is copied in crdr_data->pcsc_name . + rv = SCardListReaders(crdr_data->hContext, NULL, NULL, &dwReaders); + if(rv != SCARD_S_SUCCESS) + { + rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC failed listing readers [1] : (%lx)", (unsigned long)rv); + return ERROR; + } + if(!cs_malloc(&mszReaders, dwReaders)) + { return ERROR; } + rv = SCardListReaders(crdr_data->hContext, NULL, mszReaders, &dwReaders); + if(rv != SCARD_S_SUCCESS) + { + rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC failed listing readers [2]: (%lx)", (unsigned long)rv); + NULLFREE(mszReaders); + return ERROR; + } + /* Extract readers from the null separated string and get the total + * number of readers + */ + nbReaders = 0; + ptr = mszReaders; + while(*ptr != '\0') + { + ptr += cs_strlen(ptr) + 1; + nbReaders++; + } + + if(nbReaders == 0) + { + rdr_log(pcsc_reader, "PCSC : no pcsc_reader found"); + NULLFREE(mszReaders); + return ERROR; + } + + if(!cs_malloc(&readers, nbReaders * sizeof(char *))) + { + NULLFREE(mszReaders); + return ERROR; + } + + char* device_line; + char* device_first; + char* device_second; + + device_line = strdup((const char *)&pcsc_reader->device); + device_first = strsep(&device_line, ":"); + device_second = strsep(&device_line, ":"); + reader_nb = atoi(device_first); + + /* fill the readers table */ + nbReaders = 0; + ptr = mszReaders; + while(*ptr != '\0') + { + rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC pcsc_reader %d: %s", nbReaders, ptr); + readers[nbReaders] = ptr; + if ((reader_nb == -1) && (device_second != NULL) && strstr(ptr,device_second)){ + reader_nb = nbReaders; + } + ptr += cs_strlen(ptr) + 1; + nbReaders++; + } + + if(reader_nb < 0 || reader_nb >= nbReaders) + { + rdr_log(pcsc_reader, "Wrong pcsc_reader index: %d", reader_nb); + NULLFREE(mszReaders); + NULLFREE(readers); + NULLFREE(device_line); + return ERROR; + } + + if (readers) + { + snprintf(crdr_data->pcsc_name, sizeof(crdr_data->pcsc_name), "%s", readers[reader_nb]); + NULLFREE(readers); + } + NULLFREE(mszReaders); + NULLFREE(device_line); + } + else + { + rdr_log(pcsc_reader, "PCSC failed establish context (%lx)", (unsigned long)rv); + return ERROR; + } + return OK; +} + +static int32_t pcsc_do_api(struct s_reader *pcsc_reader, const uint8_t *buf, uint8_t *cta_res, uint16_t *cta_lr, int32_t l) +{ + LONG rv; + DWORD dwSendLength, dwRecvLength; + + *cta_lr = 0; + if(!l) + { + rdr_log(pcsc_reader, "ERROR: Data length to be send to the pcsc_reader is %d", l); + return ERROR; + } + + char tmp[l * 3]; + dwRecvLength = CTA_RES_LEN; + + struct pcsc_data *crdr_data = pcsc_reader->crdr_data; + + if(pcsc_reader->resetalways) + { + SCardReconnect(crdr_data->hCard, SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, SCARD_RESET_CARD, &crdr_data->dwActiveProtocol); + } + + if(crdr_data->dwActiveProtocol == SCARD_PROTOCOL_T0) + { + // explanantion as to why we do the test on buf[4] : + // Issuing a command without exchanging data : + //To issue a command to the card that does not involve the exchange of data (either sent or received), the send and receive buffers must be formatted as follows. + //The pbSendBuffer buffer must contain the CLA, INS, P1, and P2 values for the T=0 operation. The P3 value is not sent. (This is to differentiate the header from the case where 256 bytes are expected to be returned.) + //The cbSendLength parameter must be set to four, the size of the T=0 header information (CLA, INS, P1, and P2). + //The pbRecvBuffer will receive the SW1 and SW2 status codes from the operation. + //The pcbRecvLength should be at least two and will be set to two upon return. + if(buf[4]) + { dwSendLength = l; } + else + { dwSendLength = l - 1; } + rdr_log_dbg(pcsc_reader, D_DEVICE, "sending %lu bytes to PCSC : %s", (unsigned long)dwSendLength, cs_hexdump(1, buf, l, tmp, sizeof(tmp))); + rv = SCardTransmit(crdr_data->hCard, SCARD_PCI_T0, (LPCBYTE) buf, dwSendLength, NULL, (LPBYTE) cta_res, (LPDWORD) &dwRecvLength); + *cta_lr = dwRecvLength; + } + else if(crdr_data->dwActiveProtocol == SCARD_PROTOCOL_T1) + { + dwSendLength = l; + rdr_log_dbg(pcsc_reader, D_DEVICE, "sending %lu bytes to PCSC : %s", (unsigned long)dwSendLength, cs_hexdump(1, buf, l, tmp, sizeof(tmp))); + rv = SCardTransmit(crdr_data->hCard, SCARD_PCI_T1, (LPCBYTE) buf, dwSendLength, NULL, (LPBYTE) cta_res, (LPDWORD) &dwRecvLength); + *cta_lr = dwRecvLength; + } + else + { + rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC invalid protocol (T=%lu)", (unsigned long)crdr_data->dwActiveProtocol); + return ERROR; + } + + rdr_log_dbg(pcsc_reader, D_DEVICE, "received %d bytes from PCSC with rv=%lx : %s", *cta_lr, (unsigned long)rv, cs_hexdump(1, cta_res, *cta_lr, tmp, sizeof(tmp))); + + rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC doapi (%lx ) (T=%d), %d", (unsigned long)rv, (crdr_data->dwActiveProtocol == SCARD_PROTOCOL_T0 ? 0 : 1), *cta_lr); + + if(rv == SCARD_S_SUCCESS) + { + return OK; + } + else + { + return ERROR; + } + +} + +static int32_t pcsc_activate_card(struct s_reader *pcsc_reader, uint8_t *atr, uint16_t *atr_size) +{ + struct pcsc_data *crdr_data = pcsc_reader->crdr_data; + LONG rv; + DWORD dwState, dwAtrLen, dwReaderLen; + unsigned char pbAtr[ATR_MAX_SIZE]; + char tmp[sizeof(pbAtr) * 3 + 1]; + + rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC initializing card in (%s)", crdr_data->pcsc_name); + dwAtrLen = sizeof(pbAtr); + dwReaderLen = 0; + + rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC resetting card in (%s) with handle %" PRIuPTR, crdr_data->pcsc_name, (uintptr_t)crdr_data->hCard); + rv = SCardReconnect(crdr_data->hCard, SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, SCARD_RESET_CARD, &crdr_data->dwActiveProtocol); + + if(rv != SCARD_S_SUCCESS) + { + rdr_log_dbg(pcsc_reader, D_DEVICE, "ERROR: PCSC failed to reset card (%lx)", (unsigned long)rv); + return ERROR; + } + + rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC resetting done on card in (%s)", crdr_data->pcsc_name); + rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC Protocol (T=%d)", (crdr_data->dwActiveProtocol == SCARD_PROTOCOL_T0 ? 0 : 1)); + + rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC getting ATR for card in (%s)", crdr_data->pcsc_name); + rv = SCardStatus(crdr_data->hCard, NULL, &dwReaderLen, &dwState, &crdr_data->dwActiveProtocol, pbAtr, &dwAtrLen); + if(rv == SCARD_S_SUCCESS) + { + rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC Protocol (T=%d)", (crdr_data->dwActiveProtocol == SCARD_PROTOCOL_T0 ? 0 : 1)); + memcpy(atr, pbAtr, dwAtrLen); + *atr_size = dwAtrLen; + + rdr_log(pcsc_reader, "ATR: %s", cs_hexdump(1, (uint8_t *)pbAtr, dwAtrLen, tmp, sizeof(tmp))); + memcpy(pcsc_reader->card_atr, pbAtr, dwAtrLen); + pcsc_reader->card_atr_length = dwAtrLen; + return OK; + } + else + { + rdr_log_dbg(pcsc_reader, D_DEVICE, "ERROR: PCSC failed to get ATR for card (%lx)", (unsigned long)rv); + } + + return ERROR; +} + +static int32_t pcsc_activate(struct s_reader *reader, struct s_ATR *atr) +{ + unsigned char atrarr[ATR_MAX_SIZE]; + uint16_t atr_size = 0; + if(pcsc_activate_card(reader, atrarr, &atr_size) == OK) + { + if(ATR_InitFromArray(atr, atrarr, atr_size) != ERROR) // ATR is OK or softfail malformed + { return OK; } + else + { return ERROR; } + } + else + { return ERROR; } +} + +static int32_t pcsc_check_card_inserted(struct s_reader *pcsc_reader) +{ + struct pcsc_data *crdr_data = pcsc_reader->crdr_data; + DWORD dwState, dwAtrLen, dwReaderLen; + unsigned char pbAtr[64]; + SCARDHANDLE rv; + + dwAtrLen = sizeof(pbAtr); + rv = 0; + dwState = 0; + dwReaderLen = 0; + + // Do we have a card ? + if(!crdr_data->pcsc_has_card && !crdr_data->hCard) + { + // try connecting to the card + rv = SCardConnect(crdr_data->hContext, crdr_data->pcsc_name, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &crdr_data->hCard, &crdr_data->dwActiveProtocol); + if(rv == (SCARDHANDLE)SCARD_E_NO_SMARTCARD) + { + // no card in pcsc_reader + crdr_data->pcsc_has_card = 0; + if(crdr_data->hCard) + { + SCardDisconnect(crdr_data->hCard, SCARD_RESET_CARD); + crdr_data->hCard = 0; + } + // rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC card in %s removed / absent [dwstate=%lx rv=(%lx)]", crdr_data->pcsc_name, dwState, (unsigned long)rv ); + return OK; + } + else if(rv == (SCARDHANDLE)SCARD_W_UNRESPONSIVE_CARD) + { + // there is a problem with the card in the pcsc_reader + crdr_data->pcsc_has_card = 0; + crdr_data->hCard = 0; + rdr_log(pcsc_reader, "PCSC card in %s is unresponsive. Eject and re-insert please.", crdr_data->pcsc_name); + return ERROR; + } + else if(rv == SCARD_S_SUCCESS) + { + // we have a card + crdr_data->pcsc_has_card = 1; + rdr_log(pcsc_reader, "PCSC was opened with handle: %" PRIuPTR, (uintptr_t)crdr_data->hCard); + } + else + { + // if we get here we have a bigger problem -> display status and debug + // rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC pcsc_reader %s status [dwstate=%lx rv=(%lx)]", crdr_data->pcsc_name, dwState, (unsigned long)rv ); + return ERROR; + } + + } + + // if we get there the card is ready, check its status + rv = SCardStatus(crdr_data->hCard, NULL, &dwReaderLen, &dwState, &crdr_data->dwActiveProtocol, pbAtr, &dwAtrLen); + + if(rv == SCARD_S_SUCCESS && (dwState & (SCARD_PRESENT | SCARD_NEGOTIABLE | SCARD_POWERED))) + { + return OK; + } + else + { + SCardDisconnect(crdr_data->hCard, SCARD_RESET_CARD); + crdr_data->hCard = 0; + crdr_data->pcsc_has_card = 0; + } + + return ERROR; +} + +static int32_t pcsc_get_status(struct s_reader *reader, int32_t *in) +{ + struct pcsc_data *crdr_data = reader->crdr_data; + int32_t ret = pcsc_check_card_inserted(reader); + *in = crdr_data->pcsc_has_card; + return ret; +} + +static int32_t pcsc_close(struct s_reader *pcsc_reader) +{ + struct pcsc_data *crdr_data = pcsc_reader->crdr_data; + rdr_log_dbg(pcsc_reader, D_IFD, "PCSC : Closing device %s", pcsc_reader->device); + SCardDisconnect(crdr_data->hCard, SCARD_LEAVE_CARD); + SCardReleaseContext(crdr_data->hContext); + return OK; +} + +const struct s_cardreader cardreader_pcsc = +{ + .desc = "pcsc", + .typ = R_PCSC, + .skip_extra_atr_parsing = 1, + .skip_t1_command_retries = 1, + .skip_setting_ifsc = 1, + .reader_init = pcsc_init, + .get_status = pcsc_get_status, + .activate = pcsc_activate, + .card_write = pcsc_do_api, + .close = pcsc_close, +}; + +#endif diff --git a/csctapi/ifd_phoenix.c b/csctapi/ifd_phoenix.c new file mode 100644 index 0000000..f7b87c4 --- /dev/null +++ b/csctapi/ifd_phoenix.c @@ -0,0 +1,247 @@ +/* + ifd_phoenix.c + This module provides IFD handling functions for Smartmouse/Phoenix reader. +*/ + +#include "../globals.h" + +#ifdef CARDREADER_PHOENIX +#include "../oscam-time.h" +#include "icc_async.h" +#include "ifd_db2com.h" +#include "ifd_phoenix.h" +#include "io_serial.h" + +#define OK 0 +#define ERROR 1 + +#define GPIO_PIN (1 << (reader->detect - 4)) + +static inline int reader_use_gpio(struct s_reader *reader) +{ + return reader->use_gpio && reader->detect > 4; +} + +static void set_gpio(struct s_reader *reader, int32_t level) +{ + int ret = 0; + + ret |= read(reader->gpio_outen, &reader->gpio, sizeof(reader->gpio)); + reader->gpio |= GPIO_PIN; + ret |= write(reader->gpio_outen, &reader->gpio, sizeof(reader->gpio)); + + ret |= read(reader->gpio_out, &reader->gpio, sizeof(reader->gpio)); + if(level > 0) + { reader->gpio |= GPIO_PIN; } + else + { reader->gpio &= ~GPIO_PIN; } + ret |= write(reader->gpio_out, &reader->gpio, sizeof(reader->gpio)); + + rdr_log_dbg(reader, D_IFD, "%s level: %d ret: %d", __func__, level, ret); +} + +static void set_gpio_input(struct s_reader *reader) +{ + int ret = 0; + ret |= read(reader->gpio_outen, &reader->gpio, sizeof(reader->gpio)); + reader->gpio &= ~GPIO_PIN; + ret |= write(reader->gpio_outen, &reader->gpio, sizeof(reader->gpio)); + rdr_log_dbg(reader, D_IFD, "%s ret:%d", __func__, ret); +} + +static int32_t get_gpio(struct s_reader *reader) +{ + int ret = 0; + set_gpio_input(reader); + ret = read(reader->gpio_in, &reader->gpio, sizeof(reader->gpio)); + rdr_log_dbg(reader, D_IFD, "%s ok:%d ret:%d", __func__, reader->gpio & GPIO_PIN, ret); + if(reader->gpio & GPIO_PIN) + { return OK; } + else + { return ERROR; } +} + +int32_t Phoenix_Init(struct s_reader *reader) +{ + // First set card in reset state, to not change any parameters while communication ongoing + IO_Serial_RTS_Set(reader); + + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + if(crdr_ops->flush) { IO_Serial_Flush(reader); } + + // define reader->gpio number used for card detect and reset. ref to globals.h + if(reader_use_gpio(reader)) + { + reader->gpio_outen = open("/dev/gpio/outen", O_RDWR); + reader->gpio_out = open("/dev/gpio/out", O_RDWR); + reader->gpio_in = open("/dev/gpio/in", O_RDWR); + rdr_log_dbg(reader, D_IFD, "init gpio_outen:%d gpio_out:%d gpio_in:%d", + reader->gpio_outen, reader->gpio_out, reader->gpio_in); + set_gpio_input(reader); + } + + rdr_log_dbg(reader, D_IFD, "Initializing reader type=%d", reader->typ); + + /* Default serial port settings */ + if(reader->atr[0] == 0) + { + if(IO_Serial_SetParams(reader, DEFAULT_BAUDRATE, 8, PARITY_EVEN, 2, NULL, NULL)) { return ERROR; } + if(crdr_ops->flush) { IO_Serial_Flush(reader); } + } + return OK; +} + +int32_t Phoenix_GetStatus(struct s_reader *reader, int32_t *status) +{ + // detect card via defined reader->gpio + if(reader_use_gpio(reader)) + { + *status = !get_gpio(reader); + return OK; + } + else + { + return IO_Serial_GetStatus(reader, status); + } +} + +int32_t Phoenix_Reset(struct s_reader *reader, ATR *atr) +{ + rdr_log_dbg(reader, D_IFD, "Resetting card"); + int32_t ret; + int32_t i; + unsigned char buf[ATR_MAX_SIZE]; + int32_t parity[3] = {PARITY_EVEN, PARITY_ODD, PARITY_NONE}; + + call(IO_Serial_SetBaudrate(reader, DEFAULT_BAUDRATE)); + + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + for(i = 0; i < 3; i++) + { + if(crdr_ops->flush) { IO_Serial_Flush(reader); } + if(crdr_ops->set_parity) { IO_Serial_SetParity(reader, parity[i]); } + + ret = ERROR; + + IO_Serial_Ioctl_Lock(reader, 1); + if(reader_use_gpio(reader)) + { set_gpio(reader, 0); } + else + { IO_Serial_RTS_Set(reader); } + + cs_sleepms(50); + + // felix: set card reset hi (inactive) + if(reader_use_gpio(reader)) + { set_gpio_input(reader); } + else + { IO_Serial_RTS_Clr(reader); } + cs_sleepms(50); + IO_Serial_Ioctl_Lock(reader, 0); + + int32_t n = 0; + while(n < ATR_MAX_SIZE && !IO_Serial_Read(reader, 0, ATR_TIMEOUT, 1, buf + n)) + { n++; } + if(n == 0) + { continue; } + if(ATR_InitFromArray(atr, buf, n) != ERROR) + { ret = OK; } + // Successfully retrieve ATR + if(ret == OK) + { break; } + } + + return ret; +} + +int32_t Phoenix_Close(struct s_reader *reader) +{ + rdr_log_dbg(reader, D_IFD, "Closing phoenix device %s", reader->device); + if(reader_use_gpio(reader)) + { + if(reader->gpio_outen > -1) + { close(reader->gpio_outen); } + if(reader->gpio_out > -1) + { close(reader->gpio_out); } + if(reader->gpio_in > -1) + { close(reader->gpio_in); } + } + IO_Serial_Close(reader); + return OK; +} + +/* +int32_t Phoenix_FastReset (struct s_reader * reader, int32_t delay) +{ + IO_Serial_Ioctl_Lock(reader, 1); + if (reader_use_gpio(reader)) + set_gpio(reader, 0); + else + IO_Serial_RTS_Set(reader); + + cs_sleepms(delay); + + // set card reset hi (inactive) + if (reader_use_gpio(reader)) + set_gpio_input(reader); + else + IO_Serial_RTS_Clr(reader); + + IO_Serial_Ioctl_Lock(reader, 0); + + cs_sleepms(50); + + IO_Serial_Flush(reader); + return 0; + +} +*/ +static int32_t mouse_init(struct s_reader *reader) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + if(detect_db2com_reader(reader)) + { + reader->crdr = crdr_ops = &cardreader_db2com; + return crdr_ops->reader_init(reader); + } + + reader->handle = open(reader->device, O_RDWR | O_NOCTTY | O_NONBLOCK); + if(reader->handle < 0) + { + rdr_log(reader, "ERROR: Opening device %s (errno=%d %s)", + reader->device, errno, strerror(errno)); + return ERROR; + } + if(Phoenix_Init(reader)) + { + rdr_log(reader, "ERROR: Phoenix_Init returns error"); + Phoenix_Close(reader); + return ERROR; + } + return OK; +} + +const struct s_cardreader cardreader_mouse = +{ + .desc = "mouse", + .typ = R_MOUSE, + .flush = 1, + .read_written = 1, + .need_inverse = 1, + .reader_init = mouse_init, + .get_status = Phoenix_GetStatus, + .activate = Phoenix_Reset, + .transmit = IO_Serial_Transmit, + .receive = IO_Serial_Receive, + .close = Phoenix_Close, + .set_parity = IO_Serial_SetParity, + .set_baudrate = IO_Serial_SetBaudrate, +}; + +#endif diff --git a/csctapi/ifd_phoenix.h b/csctapi/ifd_phoenix.h new file mode 100644 index 0000000..21c952a --- /dev/null +++ b/csctapi/ifd_phoenix.h @@ -0,0 +1,13 @@ +/* + * Header file for Smartmouse/Phoenix reader. + */ +#ifndef _CSCTAPI_IFD_PHOENIX_H_ +#define _CSCTAPI_IFD_PHOENIX_H_ + +int32_t Phoenix_Init(struct s_reader *reader); +int32_t Phoenix_GetStatus(struct s_reader *reader, int32_t *status); +int32_t Phoenix_Reset(struct s_reader *reader, ATR *atr); +int32_t Phoenix_Close(struct s_reader *reader); +int32_t Phoenix_FastReset(struct s_reader *reader, int32_t delay); + +#endif diff --git a/csctapi/ifd_sc8in1.c b/csctapi/ifd_sc8in1.c new file mode 100644 index 0000000..cc5a1f9 --- /dev/null +++ b/csctapi/ifd_sc8in1.c @@ -0,0 +1,1281 @@ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "../globals.h" + +#ifdef CARDREADER_SC8IN1 +#include "../oscam-lock.h" +#include "../oscam-string.h" +#include "../oscam-time.h" +#include "atr.h" +#include "ifd_phoenix.h" +#include "io_serial.h" + +#define OK 0 +#define ERROR 1 + +struct s_sc8in1_display +{ + char *text; + uint16_t text_length; + uint16_t char_change_time; + uint16_t last_char; + uint8_t blocking; + struct s_sc8in1_display *next; +}; + +struct sc8in1_data +{ + struct termios stored_termio[8]; + uint16_t current_slot; + uint32_t current_baudrate; + struct s_reader *current_reader; + unsigned char cardstatus; + unsigned char mcr_type; + CS_MUTEX_LOCK sc8in1_lock; + struct s_sc8in1_display *display; + CS_MUTEX_LOCK sc8in1_display_lock; + unsigned char display_running; + pthread_t display_thread; +}; + +#ifdef WITH_DEBUG +static int32_t Sc8in1_DebugSignals(struct s_reader *reader, uint16_t slot, const char *extra) +{ + uint32_t msr; + if(ioctl(reader->handle, TIOCMGET, &msr) < 0) + { return ERROR; } + rdr_log_dbg(reader, D_DEVICE, "SC8in1: Signals(%s): Slot: %i, DTR: %u, RTS: %u", + extra, slot, msr & TIOCM_DTR ? 1 : 0, msr & TIOCM_RTS ? 1 : 0); + return OK; +} +#else +#define Sc8in1_DebugSignals(a, b, c) {} +#endif + +static int32_t Sc8in1_NeedBaudrateChange(struct s_reader *reader, uint32_t desiredBaudrate, struct termios *current, struct termios *new, uint8_t cmdMode) +{ + struct sc8in1_data *crdr_data = reader->crdr_data; + // Returns 1 if we need to change the baudrate + if((desiredBaudrate != crdr_data->current_baudrate) || + (reader->mhz != reader->cardmhz) || + (cmdMode == 0 && memcmp(current, new, sizeof(struct termios)))) + { + rdr_log_dbg(reader, D_TRACE, "Sc8in1_NeedBaudrateChange 1"); + return 1; + } + rdr_log_dbg(reader, D_TRACE, "Sc8in1_NeedBaudrateChange 0"); + return 0; +} + +static int32_t Sc8in1_SetBaudrate(struct s_reader *reader, uint32_t baudrate, struct termios *termio, uint8_t cmdMode) +{ + struct sc8in1_data *crdr_data = reader->crdr_data; + /* Get current settings */ + struct termios tio; + if(termio == NULL) + { + call(tcgetattr(reader->handle, &tio) != 0); + } + else + { + tio = *termio; + if(baudrate == 0) + { + baudrate = reader->current_baudrate; + if(baudrate == 0) + { + baudrate = 9600; + } + } + } + rdr_log_dbg(reader, D_IFD, "Sc8in1 Setting baudrate to %u", baudrate); + rdr_log_dbg(reader, D_TRACE, "Sc8in1 Setting baudrate to %u, reader br=%u, currentBaudrate=%u, cmdMode=%u", + baudrate, reader->current_baudrate, crdr_data->current_baudrate, cmdMode); + call(IO_Serial_SetBitrate(reader, baudrate, &tio)); + crdr_data->current_baudrate = baudrate; + call(IO_Serial_SetProperties(reader, tio)); + if(cmdMode == 0) + { + reader->current_baudrate = baudrate; + } + return OK; +} + +static int32_t sc8in1_tcdrain(struct s_reader *reader) +{ + while(1) + { + int32_t tcdrain_ret = tcdrain(reader->handle); + if(tcdrain_ret == -1) + { + if(errno == EINTR) + { + //try again in case of Interrupted system call + continue; + } + else + { rdr_log(reader, "ERROR: %s: (errno=%d %s)", __func__, errno, strerror(errno)); } + return ERROR; + } + break; + } + return OK; +} + +static int32_t sc8in1_command(struct s_reader *reader, unsigned char *buff, + uint16_t lenwrite, uint16_t lenread, uint8_t enableEepromWrite, unsigned char UNUSED(getStatusMode), + uint8_t selectSlotMode) +{ + struct sc8in1_data *crdr_data = reader->crdr_data; + struct termios termio, termiobackup; + uint32_t currentBaudrate = 0; + + if(! reader->handle) + { + rdr_log(reader, "ERROR: SC8in1 Command no valid handle"); + return ERROR; + } + + Sc8in1_DebugSignals(reader, reader->slot, "CMD10"); + + // switch SC8in1 to command mode + IO_Serial_DTR_Set(reader); + tcflush(reader->handle, TCIOFLUSH); + + // backup data + tcgetattr(reader->handle, &termio); + memcpy(&termiobackup, &termio, sizeof(termio)); + + if(selectSlotMode) + { + if(crdr_data->current_slot != 0) + { + memcpy(&crdr_data->stored_termio[crdr_data->current_slot - 1], + &termiobackup, sizeof(termiobackup)); //not if current_slot is undefined + } + } + + // set communication parameters + termio.c_oflag = 0; + termio.c_lflag = 0; + termio.c_cc[VTIME] = 1; // working + termio.c_cflag = B9600 | CS8 | CREAD | CLOCAL; + + // Do we need to set the baudrate? + if(Sc8in1_NeedBaudrateChange(reader, 9600, &termiobackup, &termio, 1)) + { + rdr_log_dbg(reader, D_TRACE, "Sc8in1_NeedBaudrateChange for SC8in1 command"); + // save current baudrate for later restore + currentBaudrate = crdr_data->current_baudrate; + crdr_data->current_baudrate = 9600; + cfsetospeed(&termio, B9600); + cfsetispeed(&termio, B9600); + rdr_log_dbg(reader, D_DEVICE, "standard baudrate: cardmhz=%d mhz=%d -> effective baudrate %u", reader->cardmhz, reader->mhz, 9600); + } + if(tcsetattr(reader->handle, TCSANOW, &termio) < 0) + { + rdr_log(reader, "ERROR: SC8in1 Command error in set RS232 attributes"); + return ERROR; + } + if(reader->sc8in1_dtrrts_patch == 1) + { + IO_Serial_DTR_Set(reader); + } + Sc8in1_DebugSignals(reader, reader->slot, "CMD11"); + + // enable EEPROM write + if(enableEepromWrite) + { + unsigned char eepromBuff[3]; + eepromBuff[0] = 0x70; + eepromBuff[1] = 0xab; + eepromBuff[2] = 0xba; + rdr_log_dump_dbg(reader, D_DEVICE, eepromBuff, 3, "Sending:"); + if(!write(reader->handle, eepromBuff, 3)) + { + rdr_log(reader, "SC8in1 Command write EEPROM error"); + return ERROR; + } + tcflush(reader->handle, TCIOFLUSH); + } + // write cmd + rdr_log_dump_dbg(reader, D_DEVICE, buff, lenwrite, "Sending:"); + int32_t dataWritten = 0, dataToWrite = lenwrite; + while(dataWritten < lenwrite) + { + int32_t written = write(reader->handle, buff, dataToWrite); + if(written == -1) + { + rdr_log(reader, "SC8in1 Command write error"); + return ERROR; + } + if(written == lenwrite) + { + break; + } + else + { + dataWritten += written; + dataToWrite -= written; + } + } + + sc8in1_tcdrain(reader); + + if(IO_Serial_Read(reader, 0, 1000000, lenread, buff) == ERROR) + { + rdr_log(reader, "SC8in1 Command read error"); + return ERROR; + } + + // Workaround for systems where tcdrain doesnt work properly + if(lenread <= 0 && crdr_data->mcr_type) + { + unsigned char buff_echo_hack[2] = { 0x65, 'A' }; + rdr_log_dump_dbg(reader, D_DEVICE, &buff_echo_hack[0], 2, "Sending:"); + if(write(reader->handle, &buff_echo_hack[0], 2) != 2) + { + rdr_log(reader, "SC8in1 Echo command write error"); + return ERROR; + } + sc8in1_tcdrain(reader); + if(IO_Serial_Read(reader, 0, 1000000, 1, &buff_echo_hack[0]) == ERROR) + { + rdr_log(reader, "SC8in1 Echo command read error"); + return ERROR; + } + if(buff_echo_hack[0] != 'A') + { + rdr_log(reader, "SC8in1 Echo command read wrong character"); + } + } + + if(selectSlotMode) + { + memcpy(&termiobackup, &crdr_data->stored_termio[selectSlotMode - 1], + sizeof(termiobackup)); + if(Sc8in1_NeedBaudrateChange(reader, reader->current_baudrate, &termio, &termiobackup, 1)) + { + rdr_log_dbg(reader, D_TRACE, "Sc8in1_SetTermioForSlot for select slot"); + if(Sc8in1_SetBaudrate(reader, reader->current_baudrate, &termiobackup, 0)) + { + rdr_log(reader, "ERROR: SC8in1 Command Sc8in1_SetBaudrate"); + return ERROR; + } + } + else + { + if(tcsetattr(reader->handle, TCSANOW, &termiobackup) < 0) + { + rdr_log(reader, "ERROR: SC8in1 Command error in set RS232 attributes"); + return ERROR; + } + } + } + else + { + // restore baudrate only if changed + if(currentBaudrate) + { + if(Sc8in1_SetBaudrate(reader, currentBaudrate, &termiobackup, 1)) + { + rdr_log(reader, "ERROR: SC8in1 selectslot restore Bitrate attributes"); + return ERROR; + } + } + else + { + // restore data + if(tcsetattr(reader->handle, TCSANOW, &termiobackup) < 0) + { + rdr_log(reader, "ERROR: SC8in1 Command error in restore RS232 attributes"); + return ERROR; + } + } + } + + Sc8in1_DebugSignals(reader, reader->slot, "CMD12"); + if(reader->sc8in1_dtrrts_patch == 1) + { + IO_Serial_DTR_Set(reader); + } + + tcflush(reader->handle, TCIOFLUSH); + + // switch SC8in1 to normal mode + IO_Serial_DTR_Clr(reader); + + Sc8in1_DebugSignals(reader, reader->slot, "CMD13"); + + return OK; +} + +static int32_t mcrReadStatus(struct s_reader *reader, unsigned char *status) +{ + unsigned char buff[2] = ""; + buff[0] = 0x3f; + if(sc8in1_command(reader, buff, 1, 2, 0, 1, 0) < 0) + { return ERROR; } + status[0] = buff[0]; + status[1] = buff[1]; + return OK; +} + +static int32_t sc8in1ReadStatus(struct s_reader *reader, unsigned char *status) +{ + unsigned char buff[9]; // read 1 echo byte + 8 status bytes + buff[0] = 0x47; + if(sc8in1_command(reader, buff, 1, 9, 0, 1, 0) < 0) + { return ERROR; } + memcpy(&status[0], &buff[1], 8); + return OK; +} + + +static int32_t mcrReadType(struct s_reader *reader, unsigned char *type) +{ + unsigned char buff[1]; + buff[0] = 0x74; + if(sc8in1_command(reader, buff, 1, 1, 0, 0, 0) < 0) + { return ERROR; } + type[0] = buff[0]; + return OK; +} + +static int32_t mcrReadVersion(struct s_reader *reader, unsigned char *version) +{ + unsigned char buff[1]; + buff[0] = 0x76; + if(sc8in1_command(reader, buff, 1, 1, 0, 0, 0) < 0) + { return ERROR; } + version[0] = buff[0]; + return OK; +} + +static int32_t mcrReadSerial(struct s_reader *reader, unsigned char *serial) +{ + unsigned char buff[2]; + buff[0] = 0x6e; + if(sc8in1_command(reader, buff, 1, 2, 0, 0, 0) < 0) + { return ERROR; } + serial[0] = buff[1]; + serial[1] = buff[0]; + return OK; +} + +/*static int32_t mcrWriteDisplayRaw(struct s_reader *reader, unsigned char data[7]) { + unsigned char buff[8]; + buff[0] = 0x64; + memcpy(&buff[1], &data[0], 7); + if (sc8in1_command(reader, buff, 8, 0, 0, 0, 0) < 0) + return ERROR; + return OK; +}*/ + +static int32_t mcrWriteDisplayAscii(struct s_reader *reader, unsigned char data, unsigned char timeout) +{ + unsigned char buff[3]; + buff[0] = 0x61; + buff[1] = data; + buff[2] = timeout; + if(sc8in1_command(reader, buff, 3, 0, 0, 0, 0) < 0) + { return ERROR; } + return OK; +} + +static int32_t mcrWriteClock(struct s_reader *reader, unsigned char saveClock, unsigned char clock_val[2]) +{ + unsigned char buff[3]; + buff[0] = 0x63; + buff[1] = clock_val[0]; + buff[2] = clock_val[1]; + if(sc8in1_command(reader, buff, 3, 0, 0, 0, 0) < 0) + { return ERROR; } + if(saveClock) + { + buff[0] = 0x6d; + if(sc8in1_command(reader, buff, 1, 0, 1, 0, 0) < 0) + { return ERROR; } + } + return OK; +} + +static int32_t mcrReadClock(struct s_reader *reader, unsigned char *clock_val) +{ + unsigned char buff[2]; + buff[0] = 0x67; + if(sc8in1_command(reader, buff, 1, 2, 0, 0, 0) < 0) + { return ERROR; } + clock_val[0] = buff[0]; + clock_val[1] = buff[1]; + return OK; +} + +static int32_t mcrWriteTimeout(struct s_reader *reader, unsigned char timeout[2]) +{ + unsigned char buff[3]; + buff[0] = 0x6f; + buff[1] = timeout[0]; + buff[2] = timeout[1]; + if(sc8in1_command(reader, buff, 3, 0, 1, 0, 0) < 0) + { return ERROR; } + return OK; +} + +static int32_t mcrReadTimeout(struct s_reader *reader, unsigned char *timeout) +{ + unsigned char buff[2]; + buff[0] = 0x72; + if(sc8in1_command(reader, buff, 1, 2, 0, 0, 0) < 0) + { return ERROR; } + timeout[0] = buff[1]; + timeout[1] = buff[0]; + return OK; +} + +static int32_t mcrSelectSlot(struct s_reader *reader, unsigned char slot) +{ + // Select slot for MCR device. + // Parameter slot is from 1-8 + unsigned char buff[2]; + buff[0] = 0x73; + buff[1] = slot - 1; + if(sc8in1_command(reader, buff, 2, 0, 0, 0, slot) < 0) + { return ERROR; } + return OK; +} + +static int32_t sc8in1SelectSlot(struct s_reader *reader, unsigned char slot) +{ + // Select slot for SC8in1 device. + // Parameter slot is from 1-8 + unsigned char buff[6]; + buff[0] = 0x53; + buff[1] = slot & 0x0F; + // Read 6 Bytes: 2 Bytes write cmd and 4 unknown Bytes. + if(sc8in1_command(reader, buff, 2, 6, 0, 0, slot) < 0) + { return ERROR; } + return OK; +} + +static int32_t MCR_DisplayText(struct s_reader *reader, char *text, uint16_t text_len, uint16_t ch_time, uint8_t blocking) +{ + struct sc8in1_data *crdr_data = reader->crdr_data; + struct s_sc8in1_display *display; + if(cs_malloc(&display, sizeof(struct s_sc8in1_display))) + { + if(!cs_malloc(&display->text, text_len)) + { + rdr_log(reader, "MCR_DisplayText: Out of memory."); + NULLFREE(display); + return ERROR; + } +if (display) { + memcpy(display->text, text, text_len); + display->text_length = text_len; + display->char_change_time = ch_time; + display->last_char = 0; + display->blocking = blocking; + display->next = NULL; + } + cs_writelock(__func__, &crdr_data->sc8in1_display_lock); + if(crdr_data->display == NULL) + { + crdr_data->display = display; + } + else + { + struct s_sc8in1_display *d = crdr_data->display; + while(d != NULL) + { + if(d->next == NULL) + { + d->next = display; + break; + } + else + { + d = d->next; + } + } + } + cs_writeunlock(__func__, &crdr_data->sc8in1_display_lock); + } + else + { + rdr_log(reader, "MCR_DisplayText: Out of memory."); + return ERROR; + } + return OK; +} + +static int32_t mcrHelloOscam(struct s_reader *reader) +{ + // Display "OSCam" on MCR display + char helloOscam[5] = {'O', 'S', 'C', 'a', 'm'}; + return MCR_DisplayText(reader, &helloOscam[0], 5, 100, 1); +} + +static int32_t mcr_generateStatisticsForDisplay(struct s_reader *reader) +{ + // show number of clients + struct s_client *cl; + uint16_t numClients = 0; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 'c') + { + numClients++; + } + } + char msg[8] = { 0 }; + int msgLen = snprintf(&msg[0], 8, "CN%i", numClients); + if(msgLen > 0 && MCR_DisplayText(reader, msg, msgLen, 300, 0)) + { + return ERROR; + } + return OK; +} + +static void *mcr_update_display_thread(void *param) +{ + const uint16_t DEFAULT_SLEEP_TIME = 100; + const int32_t STATISTICS_UPDATE_SECONDS = 60; + struct s_reader *reader = (struct s_reader *)param; + struct sc8in1_data *crdr_data = reader->crdr_data; + time_t lastStatisticUpdateTime = time((time_t *)0); + + if(reader->typ != R_SC8in1 || ! crdr_data->mcr_type) + { + rdr_log(reader, "Error: mcr_update_display_thread reader no MCR8in1 reader"); + pthread_exit(NULL); + } + + set_thread_name(__func__); + + while(crdr_data->display_running) + { + uint16_t display_sleep = DEFAULT_SLEEP_TIME; + + // Update statistics + time_t currentTime = time((time_t *)0); + if(currentTime - lastStatisticUpdateTime >= STATISTICS_UPDATE_SECONDS) + { + if(mcr_generateStatisticsForDisplay(reader)) + { + rdr_log(reader, "ERROR: mcr_generateStatisticsForDisplay"); + } + lastStatisticUpdateTime = currentTime; + } + + cs_writelock(__func__, &crdr_data->sc8in1_display_lock); + if(crdr_data->display != NULL) // is there something to display? + { + cs_writeunlock(__func__, &crdr_data->sc8in1_display_lock); + + display_sleep = crdr_data->display->char_change_time; + + // display the next character + cs_writelock(__func__, &crdr_data->sc8in1_lock); + if(crdr_data->display->blocking) + { + uint16_t i = 0; + for(i = 0; i < crdr_data->display->text_length; i++) + { + if(mcrWriteDisplayAscii(crdr_data->current_reader, + crdr_data->display->text[++crdr_data->display->last_char - 1], 0xFF)) + { + rdr_log(reader, "SC8in1: Error in mcr_update_display_thread write"); + } + cs_sleepms(display_sleep); + } + } + else + { + if(mcrWriteDisplayAscii(crdr_data->current_reader, + crdr_data->display->text[++crdr_data->display->last_char - 1], 0xFF)) + { + rdr_log(reader, "SC8in1: Error in mcr_update_display_thread write"); + } + } + cs_writeunlock(__func__, &crdr_data->sc8in1_lock); + + // remove the display struct if the text has been shown completely + if(crdr_data->display->last_char == crdr_data->display->text_length) + { + cs_writelock(__func__, &crdr_data->sc8in1_display_lock); + struct s_sc8in1_display *next = crdr_data->display->next; + NULLFREE(crdr_data->display->text); + NULLFREE(crdr_data->display); + crdr_data->display = next; + cs_writeunlock(__func__, &crdr_data->sc8in1_display_lock); + } + } + else + { + cs_writeunlock(__func__, &crdr_data->sc8in1_display_lock); + } + cs_sleepms(display_sleep); + } + pthread_exit(NULL); + return NULL; +} + + +static int32_t readSc8in1Status(struct s_reader *reader) +{ + struct sc8in1_data *crdr_data = reader->crdr_data; + // Reads the card status + // + // the bits in the return bytes: + // bit0=1 means Slot1=Smartcard inside + // bit1=1 means Slot2=Smartcard inside + // bit2=1 means Slot3=Smartcard inside + // bit3=1 means Slot4=Smartcard inside + // bit4=1 means Slot5=Smartcard inside + // bit5=1 means Slot6=Smartcard inside + // bit6=1 means Slot7=Smartcard inside + // bit7=1 means Slot8=Smartcard inside + tcflush(reader->handle, TCIOFLUSH); + if(crdr_data->mcr_type) + { + unsigned char buff[2]; + if(mcrReadStatus(reader, &buff[0])) + { + return (-1); + } + tcflush(reader->handle, TCIOFLUSH); + return buff[0]; + } + else + { + unsigned char buff[8]; + if(sc8in1ReadStatus(reader, &buff[0])) + { + return (-1); + } + if(buff[0] != 0x90) + { + return (-1); + } + tcflush(reader->handle, TCIOFLUSH); + return buff[1]; + } +} + +static int32_t Sc8in1_Selectslot(struct s_reader *reader, uint16_t slot) +{ + // selects the Smartcard Socket "slot" + // + struct sc8in1_data *crdr_data = reader->crdr_data; + if(slot == crdr_data->current_slot) + { return OK; } + rdr_log_dbg(reader, D_TRACE, "SC8in1: select slot %i", slot); + +#ifdef WITH_DEBUG + struct timeb tv_start, tv_end; + cs_ftime(&tv_start); +#endif + + int32_t status = ERROR; + if(crdr_data->mcr_type) + { + status = mcrSelectSlot(reader, slot); + } + else + { + status = sc8in1SelectSlot(reader, slot); + } + + if(status == OK) + { + crdr_data->current_reader = reader; + crdr_data->current_slot = slot; + } +#ifdef WITH_DEBUG + cs_ftime(&tv_end); + rdr_log_dbg(reader, D_DEVICE, "SC8in1 Selectslot in %"PRId64" ms", comp_timeb(&tv_end, &tv_start)); +#endif + return status; +} + +static int32_t Sc8in1_Card_Changed(struct s_reader *reader) +{ + // returns the SC8in1 Status + // 0= no card was changed (inserted or removed) + // -1= one ore more cards were changed (inserted or removed) + int32_t result; + int32_t lineData; + if(reader->handle == 0) + { return 0; } + ioctl(reader->handle, TIOCMGET, &lineData); + result = (lineData & TIOCM_CTS) / TIOCM_CTS; + return result - 1; +} + +static int32_t Sc8in1_GetStatus(struct s_reader *reader, int32_t *in) +{ + // Only same thread my access serial port + struct sc8in1_data *crdr_data = reader->crdr_data; + if((crdr_data->current_slot == reader->slot && Sc8in1_Card_Changed(reader)) || *in == -1) + { + int32_t i = readSc8in1Status(reader); //read cardstatus + if(i < 0) + { + rdr_log(reader, "Sc8in1_GetStatus Error"); + return ERROR; + } + crdr_data->cardstatus = i; + rdr_log_dbg(reader, D_TRACE, "SC8in1: Card status changed; cardstatus=0x%X", crdr_data->cardstatus); + } + *in = (crdr_data->cardstatus & 1 << (reader->slot - 1)); + return OK; +} + +static int32_t Sc8in1_Init(struct s_reader *reader) +{ + struct sc8in1_data *crdr_data = reader->crdr_data; + //additional init, Phoenix_Init is also called for Sc8in1 ! + struct termios termio; + int32_t i, speed, retval; + uint16_t sc8in1_clock = 0; + //unsigned char buff[3]; + + Sc8in1_DebugSignals(reader, reader->slot, "I-1"); + + // Clr DTR, which is set by phoenix_init + IO_Serial_DTR_Clr(reader); + + tcgetattr(reader->handle, &termio); + for(i = 0; i < 8; i++) + { + //init all stored termios to default comm settings after device init, before ATR + memcpy(&crdr_data->stored_termio[i], &termio, + sizeof(termio)); + } + + // Init sc8in1 config + crdr_data->mcr_type = 0; + crdr_data->current_reader = reader; + + // check for a MCR device and how many slots it has. + unsigned char mcrType[1]; + mcrType[0] = 0; + // at least fritzbox7170 needs to issue this twice + mcrReadType(reader, &mcrType[0]); + mcrType[0] = 0; + if(! mcrReadType(reader, &mcrType[0])) + { + if(mcrType[0] == 4 || mcrType[0] == 8) + { + crdr_data->mcr_type = mcrType[0]; + rdr_log(reader, "SC8in1: MCR%u detected for device %s", crdr_data->mcr_type, reader->device); + + unsigned char version[1]; + version[0] = 0; + if(! mcrReadVersion(reader, &version[0])) + { + rdr_log(reader, "SC8in1: Version %u for device %s", (unsigned char)version[0], reader->device); + } + + unsigned char serial[2]; + serial[0] = 0; + serial[1] = 0; + if(! mcrReadSerial(reader, &serial[0])) + { + rdr_log(reader, "SC8in1: Serial %u for device %s", (uint16_t)serial[0], reader->device); + } + + //now work-around the problem that timeout of MCR has to be 0 in case of USB + unsigned char timeout[2]; + timeout[0] = 0; + timeout[1] = 0; + retval = mcrReadTimeout(reader, &timeout[0]); + if(retval) + { + rdr_log(reader, "SC8in1: Error reading timeout."); + } + else + { + rdr_log(reader, "SC8in1: Timeout %u for device %s", (uint16_t)timeout[0], reader->device); + } + if((strstr(reader->device, "USB")) + && (retval == ERROR || timeout[0] != 0 || timeout[1] != 0)) //assuming we are connected thru USB and timeout is undetected or not zero + { + rdr_log(reader, "SC8in1: Detected Sc8in1 device connected with USB, setting timeout to 0 and writing to EEPROM"); + timeout[0] = 0; + timeout[1] = 0; + if(mcrWriteTimeout(reader, timeout)) + { + rdr_log(reader, "SC8in1: Error writing timeout."); + } + } + + // Start display thread + crdr_data->display_running = 1; + + start_thread("mcr_update_display", mcr_update_display_thread, (void *)(reader), &crdr_data->display_thread, 0, 1); + } + } + + if(! crdr_data->mcr_type) + { + tcflush(reader->handle, TCIOFLUSH); // a non MCR reader might give longer answer + } + + Sc8in1_DebugSignals(reader, reader->slot, "I0"); + + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) //also do this for disabled readers, so we configure the clocks right for all readers + if(rdr->crdr_data == crdr_data) //corresponding slot + { + //check slot boundaries + int32_t upper_slot = (crdr_data->mcr_type) ? crdr_data->mcr_type : 8; //set upper limit to 8 for non MCR readers + if(rdr->slot <= 0 || rdr->slot > upper_slot) + { + rdr_log(reader, "ERROR: device %s has invalid slot number %i", rdr->device, rdr->slot); + return ERROR; + } + + // set initial current_baudrate which is needed by sc8in1_command + rdr->current_baudrate = reader->current_baudrate; + + if(crdr_data->mcr_type) + { + //set RTS for every slot to 1 to prevent jitter/glitch detection problems + Sc8in1_DebugSignals(reader, rdr->slot, "I1"); + mcrSelectSlot(reader, rdr->slot); + Sc8in1_DebugSignals(reader, rdr->slot, "I2"); + IO_Serial_RTS_Set(reader); + Sc8in1_DebugSignals(reader, rdr->slot, "I3"); + + //calculate clock-bits + switch(rdr->mhz) + { + case 357: + case 358: + speed = 0; + break; + case 368: + case 369: + speed = 1; + break; + case 600: + speed = 2; + break; + case 800: + speed = 3; + break; + default: + speed = 0; + rdr_log(reader, "ERROR: Sc8in1 cannot set clockspeed to %d", rdr->mhz); + break; + } + sc8in1_clock |= (speed << ((rdr->slot - 1) * 2)); + } + } + + if(crdr_data->mcr_type) + { + sc8in1_clock = ((sc8in1_clock & 0xFF) << 8) | ((sc8in1_clock & 0xFF00) >> 8); + + //set clockspeeds for all slots + unsigned char clockspeed[2]; + memcpy(&clockspeed, &sc8in1_clock, 2); + if(mcrWriteClock(reader, 0, clockspeed)) + { + rdr_log(reader, "ERROR: Sc8in1 cannot set clockspeed to %d", (uint16_t)clockspeed[0]); + } + + // Clear RTS again + itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->crdr_data == crdr_data) + { + Sc8in1_DebugSignals(reader, rdr->slot, "I4"); + mcrSelectSlot(reader, rdr->slot); + Sc8in1_DebugSignals(reader, rdr->slot, "I5"); + IO_Serial_RTS_Clr(reader); + Sc8in1_DebugSignals(reader, rdr->slot, "I6"); + // Discard ATR + unsigned char buff[1]; + while(! IO_Serial_Read(reader, 0, 500000, 1, &buff[0])) + { + cs_sleepms(1); + } + tcflush(reader->handle, TCIOFLUSH); + } + } + + //DEBUG get clockspeeds + if(mcrReadClock(reader, &clockspeed[0])) + { + rdr_log(reader, "ERROR: Sc8in1 cannot read clockspeed"); + } + static char *clock_mhz[] = { "3,57", "3,68", "6,00", "8,00" }; + uint16_t result = clockspeed[0] << 8 | clockspeed[1]; + for(i = 0; i < 8; i++) + { + rdr_log(reader, "Slot %i is clocked with %s mhz", i + 1, clock_mhz[(result >> (i * 2)) & 0X0003]); + } + } + + Sc8in1_Selectslot(reader, reader->slot); + + i = -1; //Flag for GetStatus init + Sc8in1_GetStatus(reader, &i); //Initialize cardstatus + // Gimmick + if(crdr_data->mcr_type) + { + mcrHelloOscam(reader); + } + + return OK; +} + +static int32_t Sc8in1_GetActiveHandle(struct s_reader *reader, uint8_t onlyEnabledReaders) +{ + // Returns a handle to the serial port, if it exists in some other + // slot of the same physical reader. + // Or returns 0 otherwise. + struct sc8in1_data *crdr_data = reader->crdr_data; + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->typ == R_SC8in1) + { + if((reader != rdr) && (crdr_data == rdr->crdr_data) + && rdr->handle != 0 && (onlyEnabledReaders ? rdr->enable != 0 : 1)) + { + return rdr->handle; + } + } + } + return OK; +} + +static int32_t Sc8in1_Close(struct s_reader *reader) +{ + // Check if we are the last active slot for the reader, + // then close the serial port. Otherwise select next acive slot. + struct sc8in1_data *crdr_data = reader->crdr_data; + rdr_log_dbg(reader, D_IFD, "Closing SC8in1 device %s", reader->device); + bool status = ERROR; + + if(Sc8in1_GetActiveHandle(reader, 1)) + { + rdr_log_dbg(reader, D_IFD, "Just deactivating SC8in1 device %s", reader->device); + reader->written = 0; + status = OK; + // select next active reader slot, so getstatus still works + if(crdr_data->current_slot == reader->slot) + { + struct s_reader *rdr; + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + if(rdr->typ == R_SC8in1) + { + if((reader != rdr) && (crdr_data == rdr->crdr_data) + && rdr->handle != 0) + { + status = Sc8in1_Selectslot(rdr, rdr->slot); + break; + } + } + } + } + } + else + { + if(crdr_data->mcr_type) + { + // disable reader threads + crdr_data->display_running = 0; + SAFE_THREAD_JOIN(crdr_data->display_thread, NULL); + } + // disable other slots + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->typ == R_SC8in1) + { + if((reader != rdr) && (crdr_data == rdr->crdr_data)) + { + rdr->handle = 0; + } + } + } + // close serial port + if(reader->handle != 0) + { + status = IO_Serial_Close(reader); + reader->handle = 0; + } + } + + return status; +} + +static int32_t Sc8in1_SetSlotForReader(struct s_reader *reader) +{ + // Sets the slot for the reader if it is not set already + int32_t pos = cs_strlen(reader->device) - 2; //this is where : should be located; is also valid length of physical device name + if(reader->device[pos] != 0x3a) //0x3a = ":" + { rdr_log(reader, "ERROR: '%c' detected instead of slot separator `:` at second to last position of device %s", reader->device[pos], reader->device); } + reader->slot = (uint16_t)reader->device[pos + 1] - 0x30; + return OK; +} + +static int32_t Sc8in1_InitLocks(struct s_reader *reader) +{ + // Create SC8in1_Configs and init locks. + // Method is called once for every reader. + // If there is already a Sc8in1 reader configured with the + // same device (means same reader, different slot) then use + // its sc8in1_config, otherwise create a new sc8in1_config and return. + + Sc8in1_SetSlotForReader(reader); + + // Get device name + int32_t pos = cs_strlen(reader->device) - 2; + if(pos <= 0) + { + return ERROR; + } + if(reader->device[pos] != 0x3a) //0x3a = ":" + { rdr_log(reader, "ERROR: Sc8in1_InitLocks: '%c' detected instead of slot separator `:` at second to last position of device %s", reader->device[pos], reader->device); } + unsigned char savePos = reader->device[pos]; + reader->device[pos] = 0; + + + uint8_t reader_config_exists = 0; + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->typ == R_SC8in1 && rdr != reader) + { + unsigned char save = rdr->device[pos]; + rdr->device[pos] = 0; //set to 0 so we can compare device names + if(!strcmp(reader->device, rdr->device)) //we have a match to another slot with same device name + { + rdr->device[pos] = save; //restore character + Sc8in1_SetSlotForReader(rdr); + if(rdr->crdr_data) + { + reader->crdr_data = rdr->crdr_data; + reader_config_exists = 1; + rdr_log_dbg(reader, D_DEVICE, "Sc8in1_InitLocks: Found config for %s", reader->device); + } + } + else + { + rdr->device[pos] = save; //restore character + } + if(reader_config_exists) + { + break; + } + } + } + + if(!reader_config_exists) + { + rdr_log_dbg(reader, D_DEVICE, "Sc8in1_InitLocks: Creating new config for %s", reader->device); + // Create SC8in1_Config for reader + if(cs_malloc(&reader->crdr_data, sizeof(struct sc8in1_data))) + { + struct sc8in1_data *crdr_data = reader->crdr_data; + char *buff = NULL, *buff2 = NULL; + if(cs_malloc(&buff, 128)) + { snprintf(buff, 128, "sc8in1_lock_%.64s", reader->device); } + if(cs_malloc(&buff2, 128)) + { snprintf(buff2, 128, "display_sc8in1_lock_%.64s", reader->device); } + cs_lock_create(__func__, &crdr_data->sc8in1_lock, ESTR(buff), 40000); + cs_lock_create(__func__, &crdr_data->sc8in1_display_lock, ESTR(buff2), 10000); + } + else + { + reader->device[pos] = savePos; + rdr_log(reader, "sc8in1: Out of memory."); + return ERROR; + } + } + + reader->device[pos] = savePos; + + return OK; +} + +static void sc8in1_lock(struct s_reader *reader) +{ + struct sc8in1_data *crdr_data = reader->crdr_data; + cs_writelock(__func__, &crdr_data->sc8in1_lock); + rdr_log_dbg(reader, D_ATR, "Locked for access of slot %i", reader->slot); + Sc8in1_Selectslot(reader, reader->slot); +} + +static void sc8in1_unlock(struct s_reader *reader) +{ + struct sc8in1_data *crdr_data = reader->crdr_data; + cs_writeunlock(__func__, &crdr_data->sc8in1_lock); + rdr_log_dbg(reader, D_ATR, "Unlocked for access of slot %i", reader->slot); +} + +static void sc8in1_display(struct s_reader *reader, char *message) +{ + struct sc8in1_data *crdr_data = reader->crdr_data; + if(!crdr_data->mcr_type) + { return; } + char msg[4] = " "; + if(cs_strlen(message) >= 3) + { + msg[0] = message[0]; + msg[1] = message[1]; + msg[2] = message[2]; + } + char text[5] = { 'S', (char)reader->slot + 0x30, msg[0], msg[1], msg[2] }; + MCR_DisplayText(reader, text, sizeof(text), 400, 0); +} + +static int32_t sc8in1_init(struct s_reader *reader) +{ + struct sc8in1_data *crdr_data = reader->crdr_data; + cs_writelock(__func__, &crdr_data->sc8in1_lock); + if(reader->handle != 0) //this reader is already initialized + { + rdr_log_dbg(reader, D_DEVICE, "%s Sc8in1 already open", __func__); + cs_writeunlock(__func__, &crdr_data->sc8in1_lock); + return OK; + } + //get physical device name + int32_t pos = cs_strlen(reader->device) - 2; //this is where : should be located; is also valid length of physical device name + if(pos <= 0 || reader->device[pos] != 0x3a) //0x3a = ":" + { rdr_log(reader, "ERROR: '%c' detected instead of slot separator `:` at second to last position of device %s", reader->device[pos], reader->device); } + // Check if serial port is open already + reader->handle = Sc8in1_GetActiveHandle(reader, 0); + if(!reader->handle) + { + rdr_log_dbg(reader, D_DEVICE, "%s opening SC8in1", __func__); + //open physical device + char deviceName[128]; + cs_strncpy(deviceName, reader->device, 128); + deviceName[pos] = 0; + reader->handle = open(deviceName, O_RDWR | O_NOCTTY | O_NONBLOCK); + if(reader->handle < 0) + { + rdr_log(reader, "ERROR: Opening device %s with real device %s (errno=%d %s)", reader->device, deviceName, errno, strerror(errno)); + reader->handle = 0; + cs_writeunlock(__func__, &crdr_data->sc8in1_lock); + return ERROR; + } + } + else + { + // serial port already initialized + rdr_log_dbg(reader, D_DEVICE, "%s another Sc8in1 already open", __func__); + cs_writeunlock(__func__, &crdr_data->sc8in1_lock); + return OK; + } + if(Phoenix_Init(reader)) + { + rdr_log(reader, "ERROR: Phoenix_Init returns error"); + Phoenix_Close(reader); + cs_writeunlock(__func__, &crdr_data->sc8in1_lock); + return ERROR; + } + int32_t ret = Sc8in1_Init(reader); + cs_writeunlock(__func__, &crdr_data->sc8in1_lock); + if(ret) + { + rdr_log(reader, "ERROR: Sc8in1_Init returns error"); + return ERROR; + } + return OK; +} + +static int32_t sc8in1_close(struct s_reader *reader) +{ + struct sc8in1_data *crdr_data = reader->crdr_data; + cs_writelock(__func__, &crdr_data->sc8in1_lock); + int32_t retval = Sc8in1_Close(reader); + cs_writeunlock(__func__, &crdr_data->sc8in1_lock); + if(retval == ERROR) + { return ERROR; } + return OK; +} + +static int32_t sc8in1_get_status(struct s_reader *reader, int32_t *in) +{ + struct sc8in1_data *crdr_data = reader->crdr_data; + cs_writelock(__func__, &crdr_data->sc8in1_lock); + int32_t ret = Sc8in1_GetStatus(reader, in); + cs_writeunlock(__func__, &crdr_data->sc8in1_lock); + return ret; +} + +static int32_t sc8in1_activate(struct s_reader *reader, struct s_ATR *atr) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + crdr_ops->lock(reader); + int32_t retval = Phoenix_Reset(reader, atr); + crdr_ops->unlock(reader); + if(retval == ERROR) + { + rdr_log_dbg(reader, D_TRACE, "ERROR: Phoenix_Reset returns error"); + return ERROR; + } + return OK; +} + +static int32_t sc8in1_set_baudrate(struct s_reader *reader, uint32_t baudrate) +{ + call(Sc8in1_SetBaudrate(reader, baudrate, NULL, 0)); + return OK; +} + +const struct s_cardreader cardreader_sc8in1 = +{ + .desc = "sc8in1", + .typ = R_SC8in1, + .flush = 1, + .read_written = 1, + .need_inverse = 1, + .skip_t1_command_retries = 1, + .lock_init = Sc8in1_InitLocks, + .lock = sc8in1_lock, + .unlock = sc8in1_unlock, + .display_msg = sc8in1_display, + .reader_init = sc8in1_init, + .close = sc8in1_close, + .get_status = sc8in1_get_status, + .activate = sc8in1_activate, + .transmit = IO_Serial_Transmit, + .receive = IO_Serial_Receive, + .set_parity = IO_Serial_SetParity, + .set_baudrate = sc8in1_set_baudrate, +}; + +#endif diff --git a/csctapi/ifd_sci.c b/csctapi/ifd_sci.c new file mode 100644 index 0000000..303cd68 --- /dev/null +++ b/csctapi/ifd_sci.c @@ -0,0 +1,576 @@ +/* + ifd_sci.c + This module provides IFD handling functions for SCI internal reader. +*/ + +#include "../globals.h" + +#ifdef CARDREADER_INTERNAL_SCI + +#include "../oscam-time.h" + +#include "atr.h" +#include "ifd_sci_global.h" +#include "ifd_sci_ioctl.h" +#include "io_serial.h" +#include "../oscam-string.h" + +#define OK 0 +#define ERROR 1 + +struct sr_data +{ + uint8_t old_reset; + unsigned char T; + uint32_t fs; + uint32_t ETU; + uint32_t WWT; + uint32_t CWT; + uint32_t BWT; + uint32_t EGT; + unsigned char P; + unsigned char I; +}; + +static int32_t Sci_GetStatus(struct s_reader *reader, int32_t *status) +{ + call (ioctl(reader->handle, IOCTL_GET_IS_CARD_PRESENT, status)<0); + return OK; +} + +static int32_t Sci_Deactivate(struct s_reader *reader) +{ + int32_t in = 0 ; + rdr_log(reader, "Deactivating card"); + if (ioctl(reader->handle, IOCTL_GET_IS_CARD_PRESENT, &in)<0) + { + rdr_log(reader, "Error:%s ioctl(IOCTL_GET_IS_CARD_PRESENT) failed.(%d:%s)", __FUNCTION__, errno, strerror(errno) ); + return ERROR; + } + if (in != 1) {ioctl(reader->handle, IOCTL_GET_IS_CARD_ACTIVATED, &in);} + + if(in && boxtype_is("dm8000")) + { + if((ioctl(reader->handle, IOCTL_SET_DEACTIVATE)<0)) + { + rdr_log(reader,"ioctl(IOCTL_SET_DEACTIVATE) not supported on %s", boxtype_get()); + return ERROR; + } + } + else {return ERROR;} + + return OK; + +} + +static int32_t Sci_Activate(struct s_reader *reader) +{ + rdr_log_dbg(reader, D_IFD, "Is card present?"); + int32_t in = 0; + if (ioctl(reader->handle, IOCTL_GET_IS_CARD_PRESENT, &in)<0) + { + rdr_log(reader, "Error:%s ioctl(IOCTL_GET_IS_CARD_PRESENT) failed.(%d:%s)", __FUNCTION__, errno, strerror(errno) ); + Sci_Deactivate(reader); + return ERROR; + } + if (in != 1) {ioctl(reader->handle, IOCTL_GET_IS_CARD_ACTIVATED, &in);} + + if (in) + { + cs_sleepms(50); + return OK; + } + else + { + rdr_log(reader, "Error: no card is present in readerslot!"); + Sci_Deactivate(reader); + return ERROR; + } +} + +static int32_t Sci_Read_ATR(struct s_reader *reader, ATR *atr) // reads ATR on the fly: reading and some low levelchecking at the same time +{ + uint32_t timeout = ATR_TIMEOUT; + unsigned char buf[SCI_MAX_ATR_SIZE]; + int32_t n = 0, statusreturn = 0; + + if(IO_Serial_Read(reader, 0, timeout, 1, buf + n)) //read first char of atr + { + rdr_log(reader, "ERROR: no characters found in ATR!"); + return ERROR; + } + if(buf[0] == 0x3F) // 3F: card is using inverse convention, 3B = card is using direct convention + { + rdr_log_dbg(reader, D_IFD, "This card uses inverse convention"); + } + else { rdr_log_dbg(reader, D_IFD, "This card uses direct convention"); } + n++; + if(IO_Serial_Read(reader, 0, timeout, 1, buf + n)) + { + rdr_log_dbg(reader, D_IFD, "ERROR: only 1 character found in ATR"); + return ERROR; + } + int32_t T0 = buf[n]; + int32_t historicalbytes = T0 & 0x0F; // num of historical bytes in lower nibble of T0 byte + rdr_log_dbg(reader, D_ATR, "ATR historicalbytes should be: %d", historicalbytes); + rdr_log_dbg(reader, D_ATR, "Fetching global interface characters for protocol T0"); // protocol T0 always aboard! + n++; + + int32_t protocols = 1, tck = 0, protocol, protocolnumber; // protocols = total protocols on card, tck = checksum byte present, protocol = mandatory protocol + int32_t D = 0; // protocolnumber = TDi uses protocolnumber + int32_t TDi = T0; // place T0 char into TDi for looped parsing. + while(n < SCI_MAX_ATR_SIZE) + { + if(TDi & 0x10) //TA Present: //The value of TA(i) is always interpreted as XI || UI if i > 2 and T = 15 ='F'in TD(i�1) + { + if(IO_Serial_Read(reader, 0, timeout, 1, buf + n)) { break; } //In this case, TA(i) contains the clock stop indicator XI, which indicates the logical + //state the clockline must assume when the clock is stopped, and the class indicator UI, + rdr_log_dbg(reader, D_ATR, "TA%d: %02X", protocols, buf[n]); //which specifies the supply voltage class. + if((protocols > 2) && ((TDi & 0x0F) == 0x0F)) // Protocol T15 does not exists, it means mandatory on all ATRs + { + if((buf[n] & 0xC0) == 0xC0) { rdr_log_dbg(reader, D_ATR, "Clockline low or high on clockstop"); } + if((buf[n] & 0xC0) == 0x00) { rdr_log_dbg(reader, D_ATR, "Clockline not supported on clockstop"); } + if((buf[n] & 0xC0) == 0x40) { rdr_log_dbg(reader, D_ATR, "Clockline should be low on clockstop"); } + if((buf[n] & 0xC0) == 0x80) { rdr_log_dbg(reader, D_ATR, "Clockline should be high on clockstop"); } + if((buf[n] & 0x3F) == 0x01) { rdr_log_dbg(reader, D_ATR, "Voltage class A 4.5~5.5V"); } + if((buf[n] & 0x3F) == 0x02) { rdr_log_dbg(reader, D_ATR, "Voltage class B 2.7~3.3V"); } + if((buf[n] & 0x3F) == 0x03) { rdr_log_dbg(reader, D_ATR, "Voltage class A 4.5~5.5V and class B 2.7~3.3V"); } + if((buf[n] & 0x3F) == 0x04) { rdr_log_dbg(reader, D_ATR, "Voltage RFU"); } + } + if((protocols > 2) && ((TDi & 0x0F) == 0x01)) // Protocol T1 specfic (There is always an obsolete T0 protocol!) + { + int32_t ifsc = buf[n]; + if(ifsc == 0x00) { ifsc = 32; } //default is 32 + rdr_log_dbg(reader, D_ATR, "Maximum information field length this card can receive is %d bytes (IFSC)", ifsc); + } + + if(protocols < 2) + { + int32_t FI = (buf[n] >> 4); // FI is high nibble ***** work ETU = (1/D)*(Frequencydivider/cardfrequency) (in seconds!) + int32_t Fi = atr_f_table[FI]; // lookup the frequency divider + float fmax = atr_fs_table[FI]; // lookup the max frequency ***** initial ETU = 372 / initial frequency during atr (in seconds!) + + int32_t DI = (buf[n] & 0x0F); // DI is low nibble + D = atr_d_table[DI]; // lookup the bitrate adjustment (yeah there are floats in it, but in iso only integers!?) + rdr_log_dbg(reader, D_ATR, "Advertised max cardfrequency is %.2f (Fmax), frequency divider is %d (Fi)", fmax / 1000000L, Fi); // High nibble TA1 contains cardspeed + rdr_log_dbg(reader, D_ATR, "Bitrate adjustment is %d (D)", D); // Low nibble TA1 contains Bitrateadjustment + rdr_log_dbg(reader, D_ATR, "Work ETU = %.2f us assuming card runs at %.2f Mhz", + (double)((1 / (double)D) * ((double)Fi / (double)fmax) * 1000000), fmax / 1000000L); // And display it... + rdr_log_dbg(reader, D_ATR, "Initial ETU = %.2f us", (double)372 / (double)fmax * 1000000); // And display it... since D=1 and frequency during ATR fetch might be different! + } + if(protocols > 1 && protocols < 3) + { + if((buf[n] & 0x80) == 0x80) { rdr_log_dbg(reader, D_ATR, "Switching between negotiable mode and specific mode is not possible"); } + else + { + rdr_log_dbg(reader, D_ATR, "Switching between negotiable mode and specific mode is possible"); + // int32_t PPS = 1; Stupid compiler, will need it later on eventually + } + if((buf[n] & 0x01) == 0x01) { rdr_log_dbg(reader, D_ATR, "Transmission parameters implicitly defined in the interface characters."); } + else { rdr_log_dbg(reader, D_ATR, "Transmission parameters explicitly defined in the interface characters."); } + + protocol = buf[n] & 0x0F; + if(protocol) { rdr_log_dbg(reader, D_ATR, "Protocol T = %d is to be used!", protocol); } + } + n++; // next interface character + } + if(TDi & 0x20) //TB Present + { + if(IO_Serial_Read(reader, 0, timeout, 1, buf + n)) { break; } + rdr_log_dbg(reader, D_ATR, "TB%d: %02X", protocols, buf[n]); + if((protocols > 2) && ((TDi & 0x0F) == 0x01)) // Protocol T1 specfic (There is always an obsolete T0 protocol!) + { + int32_t CWI = (buf[n] & 0x0F); // low nibble contains CWI code for the character waiting time CWT + int32_t BWI = (buf[n] >> 4); // high nibble contains BWI code for the block waiting time BWT + rdr_log_dbg(reader, D_ATR, "Protocol T1: Character waiting time is %d(CWI)", CWI); + rdr_log_dbg(reader, D_ATR, "Protocol T1: Block waiting time is %d (BWI)", BWI); + } + + n++; // next interface character + } + if(TDi & 0x40) //TC Present + { + if(IO_Serial_Read(reader, 0, timeout, 1, buf + n)) { break; } + rdr_log_dbg(reader, D_ATR, "TC%d: %02X", protocols, buf[n]); + if((protocols > 1) && ((TDi & 0x0F) == 0x00)) + { + int32_t WI = buf[n]; + rdr_log_dbg(reader, D_ATR, "Protocol T0: work wait time is %d work etu (WWT)", (int)(960 * D * WI)); + } + if((protocols > 1) && ((TDi & 0x0F) == 0x01)) + { + if(buf[n] & 0x01) { rdr_log_dbg(reader, D_ATR, "Protocol T1: CRC is used to compute the error detection code"); } + else { rdr_log_dbg(reader, D_ATR, "Protocol T1: LRC is used to compute the error detection code"); } + } + if((protocols < 2) && (buf[n] < 0xFF)) { rdr_log_dbg(reader, D_ATR, "Extra guardtime of %d ETU (N)", (int) buf[n]); } + if((protocols < 2) && (buf[n] == 0xFF)) { rdr_log_dbg(reader, D_ATR, "Protocol T1: Standard 2 ETU guardtime is lowered to 1 ETU"); } + + n++; // next interface character + } + if(TDi & 0x80) //TD Present? Get next TDi there will be a next protocol + { + if(IO_Serial_Read(reader, 0, timeout, 1, buf + n)) { break; } + rdr_log_dbg(reader, D_ATR, "TD%d %02X", protocols, buf[n]); + TDi = buf[n]; + protocolnumber = TDi & 0x0F; + if(protocolnumber == 0x00) { tck = 0; } // T0 protocol do not use tck byte (TCK = checksum byte!) + if(protocolnumber == 0x0E) { tck = 1; } // T14 protocol tck byte should be present + if(protocolnumber == 0x01) { tck = 1; } // T1 protocol tck byte is mandatory, BTW: this code doesnt calculate if the TCK is valid jet... + rdr_log_dbg(reader, D_ATR, "Fetching global interface characters for protocol T%d:", (TDi & 0x0F)); // lower nibble contains protocol number + protocols++; // there is always 1 protocol T0 in every ATR as per iso defined, max is 16 (numbered 0..15) + + n++; // next interface character + } + else { break; } + } + int32_t atrlength = 0; + atrlength += n; + atrlength += historicalbytes; + rdr_log_dbg(reader, D_ATR, "Total ATR Length including %d historical bytes should be %d", historicalbytes, atrlength); + if(T0 & 0x80) { protocols--; } // if bit 8 set there was a TD1 and also more protocols, otherwise this is a T0 card: substract 1 from total protocols + rdr_log_dbg(reader, D_ATR, "Total protocols in this ATR is %d", protocols); + + while(n < atrlength + tck) // read all the rest and mandatory tck byte if other protocol than T0 is used. + { + if(IO_Serial_Read(reader, 0, timeout, 1, buf + n)) { break; } + n++; + } + + if(n != atrlength + tck) { rdr_log(reader, "WARNING: Total ATR characters received is: %d instead of expected %d", n, atrlength + tck); } + + if((buf[0] != 0x3B) && (buf[0] != 0x3F) && (n > 9 && !memcmp(buf + 4, "IRDETO", 6))) // irdeto S02 reports FD as first byte on dreambox SCI, not sure about SH4 or phoenix + { buf[0] = 0x3B; } + + statusreturn = ATR_InitFromArray(atr, buf, n); // n should be same as atr length but in case of atr read error it's less, so do not use atr length here! + + if(buf[7] == 0x70 && buf[8] == 0x70 && (buf[9]&0x0F) >= 10) + { + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + int8_t nxtr = 0; + reader->crdr_flush = 0; + while(nxtr < 2) + { + if(IO_Serial_Read(reader, 0, 75000, 1, buf + n + nxtr)) { break; } + nxtr++; + } + } + + if(statusreturn == ATR_MALFORMED) { rdr_log(reader, "WARNING: ATR is malformed, you better inspect it with a -d2 log!"); } + + if(statusreturn == ERROR) + { + rdr_log(reader, "WARNING: ATR is invalid!"); + return ERROR; + } + + return OK; // return OK but atr might be softfailing! +} + +static int32_t Sci_Reset(struct s_reader *reader, ATR *atr) +{ + int32_t ret = ERROR; + + SCI_PARAMETERS params; + + memset(¶ms, 0, sizeof(SCI_PARAMETERS)); + + params.ETU = 372; //initial ETU (in iso this parameter F) + params.EGT = 0; //initial guardtime should be 0 (in iso this is parameter N) + params.fs = 3; //initial cardmhz 3 Mhz for non pll readers (in iso this is parameter D) + params.T = 0; + if(reader->cardmhz > 2000) // PLL based reader + { + params.ETU = 372; + params.EGT = 0; + params.fs = (int32_t)(reader->cardmhz / 100.0 + 0.5); /* calculate divider for 1 MHz */ + params.T = 0; + } + if(reader->cardmhz == 8300) /* PLL based reader DM7025 */ + { + params.ETU = 372; + params.EGT = 0; + params.fs = 16; /* read from table setting for 1 MHz: + params.fs = 6 for cardmhz = 5.188 MHz + params.fs = 7 for cardmhz = 4.611 MHz + params.fs = 8 for cardmhz = 3.953 MHz + params.fs = 9 for cardmhz = 3.609 MHz + params.fs = 10 for cardmhz = 3.192 MHz + params.fs = 11 for cardmhz = 2.965 MHz + params.fs = 12 for cardmhz = 2.677 MHz + params.fs = 13 for cardmhz = 2.441 MHz + params.fs = 14 for cardmhz = 2.306 MHz + params.fs = 15 for cardmhz = 2.128 MHz + params.fs = 16 for cardmhz = 1.977 MHz */ + params.T = 0; + } + + int32_t tries = 0; + int32_t max_tries = 0; + int32_t pll_start_fs = 0; + if (reader->cardmhz > 2000 && reader->cardmhz != 8300) + { + max_tries = (((double)(reader->cardmhz/900)) * 2 ) + 1 ; // the higher the maxpll the higher tries needed, to have 9 Mhz or first avb below. + pll_start_fs = ((double)(reader->cardmhz/300)) + 1.5 ; // first avbl reader Mhz equal or above 3.0 Mhz + } + else + { + max_tries = 5; + } + while(ret == ERROR && tries < max_tries) + { + cs_sleepms(50); +// rdr_log(reader, "Set reader parameters!"); + rdr_log_dbg(reader, D_IFD, "Sent reader setting at cardinit T=%d fs=%d ETU=%d WWT=%d CWT=%d BWT=%d EGT=%d clock=%d check=%d P=%d I=%d U=%d", + (int)params.T, params.fs, (int)params.ETU, (int)params.WWT, + (int)params.CWT, (int)params.BWT, (int)params.EGT, + (int)params.clock_stop_polarity, (int)params.check, + (int)params.P, (int)params.I, (int)params.U); + ioctl(reader->handle, IOCTL_SET_PARAMETERS, ¶ms); + cs_sleepms(150); // give the reader some time to process the params + +// rdr_log(reader, "Reset internal cardreader!"); + if(ioctl(reader->handle, IOCTL_SET_RESET, 1) < 0) + { + ret = ERROR; + rdr_log(reader, "Error:%s ioctl(IOCTL_SET_RESET) failed.(%d:%s)", __FUNCTION__, errno, strerror(errno) ); + Sci_Deactivate(reader); + Sci_Activate(reader); + cs_sleepms(50); + } + else + { + ret = Sci_Read_ATR(reader, atr); + if(ret == ERROR) + { + Sci_Deactivate(reader); + Sci_Activate(reader); + tries++; // increase fs + if (reader->cardmhz > 2000 && reader->cardmhz != 8300) + { + params.fs = (pll_start_fs - tries); // if 1 Mhz init failed retry with min 300 Mhz up to max 9.0 Mhz + rdr_log(reader, "Read ATR fail, attempt %d/%d fs = %d", tries, max_tries, params.fs); + } + else if (reader->cardmhz == 8300) + { + params.fs = (11 - tries); // if 1 Mhz init failed retry with 3.19 Mhz up to 5.188 Mhz + rdr_log(reader, "Read ATR fail, attempt %d/5 fs = %d", tries, params.fs); + } + else + { + params.fs = (2 + tries); // if 1 Mhz init failed retry with 3.0 Mhz up to 7.0 Mhz + rdr_log(reader, "Read ATR fail, attempt %d/5 fs = %d", tries, params.fs); + } + } + else // ATR fetched successfully! + { + if(ioctl(reader->handle, IOCTL_SET_ATR_READY, 1) < 0) + { + ret = ERROR; + rdr_log(reader, "Error:%s ioctl(IOCTL_SET_ATR_READY) failed.(%d:%s)", __FUNCTION__, errno, strerror(errno) ); + } + } + } + } + return ret; +} + +static int32_t Sci_WriteSettings(struct s_reader *reader, unsigned char T, uint32_t fs, uint32_t ETU, uint32_t WWT, uint32_t CWT, uint32_t BWT, uint32_t EGT, unsigned char P, unsigned char I) +{ + cs_sleepms(150); + struct sr_data *crdr_data = reader->crdr_data; + //int32_t n; + SCI_PARAMETERS params; + //memset(¶ms,0,sizeof(SCI_PARAMETERS)); + ioctl(reader->handle, IOCTL_GET_PARAMETERS, ¶ms); + params.T = T; + params.fs = fs; + + //for Irdeto T14 cards, do not set ETU + if(ETU) + { params.ETU = ETU; } + params.EGT = EGT; + params.WWT = WWT; + params.BWT = BWT; + params.CWT = CWT; + if(P) + { params.P = P; } + if(I) + { params.I = I; } + + crdr_data->T = params.T; + crdr_data->fs = params.fs; + crdr_data->ETU = params.ETU; + crdr_data->WWT = params.WWT; + crdr_data->CWT = params.CWT; + crdr_data->BWT = params.BWT; + crdr_data->EGT = params.EGT; + crdr_data->P = params.P; + crdr_data->I = params.I; + + rdr_log_dbg(reader, D_IFD, "Sent reader settings T=%d fs=%d ETU=%d WWT=%d CWT=%d BWT=%d EGT=%d clock=%d check=%d P=%d I=%d U=%d", + (int)params.T, params.fs, (int)params.ETU, (int)params.WWT, + (int)params.CWT, (int)params.BWT, (int)params.EGT, + (int)params.clock_stop_polarity, (int)params.check, + (int)params.P, (int)params.I, (int)params.U); + + ioctl(reader->handle, IOCTL_SET_PARAMETERS, ¶ms); + cs_sleepms(150); // give the reader some time to process the params + return OK; +} + +static int32_t Sci_FastReset(struct s_reader *reader, ATR *atr) +{ + struct sr_data *crdr_data = reader->crdr_data; + int8_t atr_ok = 1; // initiate atr in ERROR + uint32_t timeout = ATR_TIMEOUT; + unsigned char buf[SCI_MAX_ATR_SIZE]; + int8_t atr_len = 0; + + if(reader->seca_nagra_card == 1) + { + atr_len = reader->card_atr_length; // this is a special case the data buffer has only the atr length. + } + else + { + atr_len = reader->card_atr_length + 2; // data buffer has atr length + 2 bytes + } + + Sci_Activate(reader); + cs_sleepms(50); + if(ioctl(reader->handle, IOCTL_SET_RESET, 1) < 0) + { + rdr_log(reader, "Error:%s ioctl(IOCTL_SET_RESET) failed.(%d:%s)", __FUNCTION__, errno, strerror(errno) ); + Sci_Deactivate(reader); + atr_ok = ERROR; + } + else + { + IO_Serial_Read(reader, 0, timeout*10, atr_len, buf); //read atr: give some extra timeout time since on some platforms all chars are read at once +// rdr_log_dump(reader,buf, SCI_MAX_ATR_SIZE * 2, "SCI ATR :"); // just to crosscheck the buffer I left it commented. + if(ioctl(reader->handle, IOCTL_SET_ATR_READY, 1) < 0) + { + rdr_log(reader, "Error:%s ioctl(IOCTL_SET_ATR_READY) failed.(%d:%s)", __FUNCTION__, errno, strerror(errno) ); + Sci_Deactivate(reader); + atr_ok = ERROR; + } + else + { + if(ATR_InitFromArray(atr, buf, atr_len) != ERROR) + { + atr_ok = OK; + } + else + { + rdr_log(reader,"Error reading ATR"); + atr_ok= ERROR; + } + + cs_sleepms(150); + Sci_WriteSettings(reader, crdr_data->T,crdr_data->fs,crdr_data->ETU, crdr_data->WWT,crdr_data->CWT,crdr_data->BWT,crdr_data->EGT,crdr_data->P,crdr_data->I); + cs_sleepms(150); + } + } + return atr_ok; +} + +static int32_t Sci_Init(struct s_reader *reader) +{ + uint8_t i = 0; + while(reader->handle_nr > 0 && i < 5) + { + i++; + rdr_log(reader," Wait On closing before restart %u", i); + cs_sleepms(1000); + } + + int flags = O_RDWR | O_NOCTTY; +#if defined(__SH4__) || defined(STB04SCI) + flags |= O_NONBLOCK; +#endif + reader->handle = open(reader->device, flags); + if(reader->handle < 0) + { + rdr_log(reader, "ERROR: Opening device %s (errno=%d %s)", reader->device, errno, strerror(errno)); + return ERROR; + } + + if(!reader->crdr_data && !cs_malloc(&reader->crdr_data, sizeof(struct sr_data))) + { return ERROR; } + struct sr_data *crdr_data = reader->crdr_data; + crdr_data->old_reset = 1; + reader->handle_nr = reader->handle + 1; + return OK; +} + +static int32_t sci_activate(struct s_reader *reader, ATR *atr) +{ + if(!reader->ins7e11_fast_reset) + { + call(Sci_Activate(reader)); + call(Sci_Reset(reader, atr)); + } + else + { + rdr_log_dbg(reader, D_IFD, "Fast card reset with atr"); + call(Sci_FastReset(reader, atr)); + } + return OK; +} + +static int32_t Sci_Close(struct s_reader *reader) +{ + Sci_Deactivate(reader); + IO_Serial_Close(reader); + NULLFREE(reader->crdr_data); //clearing allocated module mem + NULLFREE(reader->csystem_data); //clearing allocated card system mem + cs_sleepms(150); // some stb's needs small extra time even after close procedure seems to be ok. + reader->handle_nr = 0; + return OK; +} + +static int32_t sci_write_settings(struct s_reader *reader, struct s_cardreader_settings *s) +{ + if(reader->cardmhz > 2000) // only for dreambox internal pll readers clockspeed can be set precise others like vu ignore it and work always at 4.5 Mhz + { + // P fixed at 5V since this is default class A card, and TB is deprecated + if(reader->protocol_type != ATR_PROTOCOL_TYPE_T14) // fix VU+ internal reader slow responses on T0/T1 + { + cs_sleepms(150); + call(Sci_WriteSettings(reader, 0, reader->divider, s->ETU, s->WWT, reader->CWT, reader->BWT, s->EGT, 5, (unsigned char)s->I)); + cs_sleepms(150); + } + else // no fixup for T14 protocol otherwise error + { + cs_sleepms(150); + call(Sci_WriteSettings(reader, reader->protocol_type, reader->divider, s->ETU, s->WWT, reader->CWT, reader->BWT, s->EGT, 5, (unsigned char)s->I)); + cs_sleepms(150); + } + } + else // all other brand boxes than dreamboxes or VU+! + { + // P fixed at 5V since this is default class A card, and TB is deprecated + cs_sleepms(150); + // non pll internal reader needs base frequency like 1,2,3,4,5,6 MHz not clock rate conversion factor (Fi) + call(Sci_WriteSettings(reader, reader->protocol_type, s->F/100, s->ETU, s->WWT, reader->CWT, reader->BWT, s->EGT, 5, (unsigned char)s->I)); + cs_sleepms(150); + } + return OK; +} + +const struct s_cardreader cardreader_internal_sci = +{ + .desc = "internal", + .typ = R_INTERNAL, + .flush = 1, + .max_clock_speed = 1, + .reader_init = Sci_Init, + .get_status = Sci_GetStatus, + .activate = sci_activate, + .transmit = IO_Serial_Transmit, + .receive = IO_Serial_Receive, + .close = Sci_Close, + .write_settings = sci_write_settings, +}; + +#endif diff --git a/csctapi/ifd_sci_global.h b/csctapi/ifd_sci_global.h new file mode 100644 index 0000000..09a5548 --- /dev/null +++ b/csctapi/ifd_sci_global.h @@ -0,0 +1,103 @@ +#ifndef _sci_global_h_ +#define _sci_global_h_ + +#define SCI_CLASS_A 1 /* only 5V Vcc to Smart Card */ +#define SCI_CLASS_B 2 /* only 3V Vcc to Smart Card */ +#define SCI_CLASS_AB 3 /* 5V or 3V Vcc to Smart Card */ +#define SCI_NUMBER_OF_CONTROLLERS 2 /* number of SCI controllers */ + +#define SCI_BUFFER_SIZE 512 + +#define SCI_CLOCK_STOP_DISABLED 0 +#define SCI_CLOCK_STOP_LOW 1 +#define SCI_CLOCK_STOP_HIGH 2 + +#define SCI_MAX_ATR_SIZE 33 + +#define SCI_MAX_F 80000000 +#define SCI_MAX_ETU 0xFFF +#define SCI_MAX_WWT 0xFFFFFFFF +#define SCI_MAX_CWT 0xFFFF +#define SCI_MAX_BWT 0xFFFFFFFF +#define SCI_MAX_EGT 0xFF + +#define SCI_MIN_F 1000000 +#define SCI_MIN_ETU 8 +#define SCI_MIN_WWT 12 +#define SCI_MIN_CWT 12 +#define SCI_MIN_BWT 971 +#define SCI_MIN_EGT 0 + +#define SCI_SYNC 0x00000001 +#define SCI_DATA_ANY 0x00000002 + +/* Reserved for Future Use defined as 0 */ +#define RFU 0 + +/* error codes */ +typedef enum +{ + SCI_ERROR_OK = 0, + SCI_ERROR_DRIVER_NOT_INITIALIZED = -1691, + SCI_ERROR_FAIL, + SCI_ERROR_KERNEL_FAIL, + SCI_ERROR_NO_ATR, + SCI_ERROR_TS_CHARACTER_INVALID, + SCI_ERROR_LRC_FAIL, + SCI_ERROR_CRC_FAIL, + SCI_ERROR_LENGTH_FAIL, + SCI_ERROR_PARITY_FAIL, + SCI_ERROR_RX_OVERFLOW_FAIL, + SCI_ERROR_TX_OVERFLOW_FAIL, + SCI_ERROR_TX_UNDERRUN_FAIL, + SCI_ERROR_CARD_NOT_PRESENT, + SCI_ERROR_CARD_NOT_ACTIVATED, + SCI_ERROR_AWT_TIMEOUT, + SCI_ERROR_WWT_TIMEOUT, + SCI_ERROR_CWT_TIMEOUT, + SCI_ERROR_BWT_TIMEOUT, + SCI_ERROR_PARAMETER_OUT_OF_RANGE, + SCI_ERROR_TRANSACTION_ABORTED, + SCI_ERROR_CLOCK_STOP_DISABLED, + SCI_ERROR_TX_PENDING, + SCI_ERROR_ATR_PENDING +} +SCI_ERROR; + +/* SCI driver modes */ +typedef struct sci_modes +{ + int32_t emv2000; + int32_t dma; + int32_t man_act; + int32_t rw_mode; +} +SCI_MODES; + +/* SCI communication parameters */ +typedef struct sci_parameters +{ + unsigned char T; + uint32_t fs; + uint32_t ETU; + uint32_t WWT; + uint32_t CWT; + uint32_t BWT; + uint32_t EGT; + uint32_t clock_stop_polarity; + unsigned char check; + unsigned char P; + unsigned char I; + unsigned char U; +} +SCI_PARAMETERS; + +/* SCI ATR status */ +typedef enum +{ + SCI_WITHOUT_ATR = 0, + SCI_ATR_READY +} +SCI_ATR_STATUS; + +#endif /* _sci_global_h_ */ diff --git a/csctapi/ifd_sci_ioctl.h b/csctapi/ifd_sci_ioctl.h new file mode 100644 index 0000000..7e645ae --- /dev/null +++ b/csctapi/ifd_sci_ioctl.h @@ -0,0 +1,64 @@ +#ifndef _sci_ioctl_h_ +#define _sci_ioctl_h_ + +/* constants */ +#define DEVICE_NAME "sci_dev" + +#define SCI_IOW_MAGIC 's' + +#ifdef STB04SCI +//-------------------------------------------------------------------------- +// reset sci_parameters are optional, i.e. may be NULL +// (but not: ioctl (fd, STB04SCI_RESET), +// rather ioctl (fd, STB04SCI_RESET, NULL)) +//-------------------------------------------------------------------------- + +#define IOCTL_SET_RESET _IO (0x64, 1) +#define IOCTL_SET_MODES _IOW(0x64, 2, SCI_MODES) +#define IOCTL_GET_MODES _IOR(0x64, 3, SCI_MODES) +#define IOCTL_SET_PARAMETERS _IOW(0x64, 4, SCI_PARAMETERS) +#define IOCTL_GET_PARAMETERS _IOR(0x64, 5, SCI_PARAMETERS) +#define IOCTL_CLOCK_START _IO (0x64, 6) +#define IOCTL_CLOCK_STOP _IO (0x64, 7) +#define IOCTL_GET_IS_CARD_PRESENT _IO (0x64, 8) +#define IOCTL_GET_IS_CARD_ACTIVATED _IO (0x64, 9) +#define IOCTL_SET_DEACTIVATE _IO (0x64, 10) +#define IOCTL_SET_ATR_READY _IO (0x64, 11) +#define IOCTL_GET_ATR_STATUS _IO (0x64, 12) +#define IOCTL_DUMP_REGS _IO (0x64, 20) + +#elif defined(__CYGWIN__) +/* ioctl cmd table */ +#define IOCTL_SET_RESET 1 +#define IOCTL_SET_MODES 2 +#define IOCTL_GET_MODES 3 +#define IOCTL_SET_PARAMETERS 4 +#define IOCTL_GET_PARAMETERS 5 +#define IOCTL_SET_CLOCK_START 6 +#define IOCTL_SET_CLOCK_STOP 7 +#define IOCTL_GET_IS_CARD_PRESENT 8 +#define IOCTL_GET_IS_CARD_ACTIVATED 9 +#define IOCTL_SET_DEACTIVATE 10 +#define IOCTL_SET_ATR_READY 11 +#define IOCTL_GET_ATR_STATUS 12 +#define IOCTL_DUMP_REGS 13 +#else +#define IOCTL_SET_RESET _IOW(SCI_IOW_MAGIC, 1, uint32_t) +#define IOCTL_SET_MODES _IOW(SCI_IOW_MAGIC, 2, SCI_MODES) +#define IOCTL_GET_MODES _IOW(SCI_IOW_MAGIC, 3, SCI_MODES) +#define IOCTL_SET_PARAMETERS _IOW(SCI_IOW_MAGIC, 4, SCI_PARAMETERS) +#define IOCTL_GET_PARAMETERS _IOW(SCI_IOW_MAGIC, 5, SCI_PARAMETERS) +#define IOCTL_SET_CLOCK_START _IOW(SCI_IOW_MAGIC, 6, uint32_t) +#define IOCTL_SET_CLOCK_STOP _IOW(SCI_IOW_MAGIC, 7, uint32_t) +#define IOCTL_GET_IS_CARD_PRESENT _IOW(SCI_IOW_MAGIC, 8, uint32_t) +#define IOCTL_GET_IS_CARD_ACTIVATED _IOW(SCI_IOW_MAGIC, 9, uint32_t) +#define IOCTL_SET_DEACTIVATE _IOW(SCI_IOW_MAGIC, 10, uint32_t) +#define IOCTL_SET_ATR_READY _IOW(SCI_IOW_MAGIC, 11, uint32_t) +#define IOCTL_GET_ATR_STATUS _IOW(SCI_IOW_MAGIC, 12, uint32_t) +#define IOCTL_DUMP_REGS _IOW(SCI_IOW_MAGIC, 20, uint32_t) +#endif + +/* MAJOR NUM OF DEVICE DRVIER */ +#define MAJOR_NUM 169 + +#endif /* _sci_ioctl_h_ */ diff --git a/csctapi/ifd_smargo.c b/csctapi/ifd_smargo.c new file mode 100644 index 0000000..869daa3 --- /dev/null +++ b/csctapi/ifd_smargo.c @@ -0,0 +1,274 @@ +#include "../globals.h" + +#ifdef CARDREADER_SMARGO +#include "../oscam-time.h" +#include "icc_async.h" +#include "io_serial.h" + +#if defined(__CYGWIN__) +#undef OK +#undef ERROR +#undef LOBYTE +#undef HIBYTE +#endif + +#define OK 0 +#define ERROR 1 +#define LOBYTE(w) ((unsigned char)((w) & 0xff)) +#define HIBYTE(w) ((unsigned char)((w) >> 8)) + +#define SMARGO_DELAY 150 + +static void smargo_set_config_mode_on(struct s_reader *reader) +{ + struct termios term; + + tcgetattr(reader->handle, &term); + term.c_cflag &= ~CSIZE; + term.c_cflag |= CS5; + tcsetattr(reader->handle, TCSANOW, &term); + + cs_sleepms(SMARGO_DELAY); +} + +static void smargo_set_config_mode_off(struct s_reader *reader) +{ + struct termios term; + + cs_sleepms(SMARGO_DELAY); + + tcgetattr(reader->handle, &term); + term.c_cflag &= ~CSIZE; + term.c_cflag |= CS8; + tcsetattr(reader->handle, TCSANOW, &term); +} + +static int32_t smargo_set_settings(struct s_reader *reader, int32_t freq, unsigned char T, unsigned char inv, uint16_t Fi, unsigned char Di, unsigned char Ni) +{ + uint16_t freqk = (freq * 10); + uint8_t data[4]; + + smargo_set_config_mode_on(reader); + + rdr_log_dbg(reader, D_DEVICE, "sending F=%04X (%d), D=%02X (%d), Freq=%04X (%d), N=%02X (%d), T=%02X (%d), inv=%02X (%d)", + Fi, Fi, Di, Di, freqk, freqk, Ni, Ni, T, T, inv, inv); + + if(T != 14 || freq == 369) + { + data[0] = 0x01; + data[1] = HIBYTE(Fi); + data[2] = LOBYTE(Fi); + data[3] = Di; + IO_Serial_Write(reader, 0, 1000, 4, data); + } + + data[0] = 0x02; + data[1] = HIBYTE(freqk); + data[2] = LOBYTE(freqk); + IO_Serial_Write(reader, 0, 1000, 3, data); + + data[0] = 0x03; + data[1] = Ni; + IO_Serial_Write(reader, 0, 1000, 2, data); + + data[0] = 0x04; + data[1] = T; + IO_Serial_Write(reader, 0, 1000, 2, data); + + data[0] = 0x05; + data[1] = inv; + IO_Serial_Write(reader, 0, 1000, 2, data); + + smargo_set_config_mode_off(reader); + + return OK; +} + +static int32_t smargo_write_settings(struct s_reader *reader, struct s_cardreader_settings *s) +{ + return smargo_set_settings(reader, reader->mhz, reader->protocol_type == 1 ? 0 : reader->protocol_type , reader->convention, s->Fi, s->D, s->Ni); +} + + +static int32_t smargo_init(struct s_reader *reader) +{ + reader->handle = open(reader->device, O_RDWR); + + if(reader->handle < 0) + { + rdr_log(reader, "ERROR: Opening device %s (errno=%d %s)", reader->device, errno, strerror(errno)); + return ERROR; + } + + if(IO_Serial_SetParams(reader, DEFAULT_BAUDRATE, 8, PARITY_EVEN, 2, NULL, NULL)) + { return ERROR; } + + IO_Serial_RTS_Set(reader); + IO_Serial_DTR_Set(reader); + IO_Serial_Flush(reader); + + return OK; +} + +static int32_t smargo_Serial_Read(struct s_reader *reader, uint32_t timeout, uint32_t size, unsigned char *data, int32_t *read_bytes) +{ + uint32_t count = 0; + uint32_t bytes_read = 0; + + for(count = 0; count < size ; count += bytes_read) + { + if(IO_Serial_WaitToRead(reader, 0, timeout) == OK) + { + if((bytes_read = read(reader->handle, data + count, size - count)) < 1) + { + int saved_errno = errno; + rdr_log_dump_dbg(reader, D_DEVICE, data, count, "Receiving:"); + rdr_log(reader, "ERROR: %s (errno=%d %s)", __func__, saved_errno, strerror(saved_errno)); + return ERROR; + } + } + else + { + rdr_log_dump_dbg(reader, D_DEVICE, data, count, "Receiving:"); + rdr_log_dbg(reader, D_DEVICE, "Timeout in IO_Serial_Read"); + *read_bytes = count; + return ERROR; + } + } + rdr_log_dump_dbg(reader, D_DEVICE, data, count, "Receiving:"); + return OK; +} + +static int32_t smargo_fast_reset_by_atr(struct s_reader *reader, ATR *atr) +{ + int32_t ret = ERROR; + unsigned char buf[ATR_MAX_SIZE]; + int32_t n = 0; + int8_t atr_len = 0; + + if(reader->seca_nagra_card == 1) + { + atr_len = reader->card_atr_length; // this is a special case the data buffer has only the atr length. + } + else + { + atr_len = reader->card_atr_length + 2; // data buffer has atr length + 2 bytes + } + + IO_Serial_Read(reader, 0, 500000, atr_len, buf); + + IO_Serial_RTS_Set(reader); + cs_sleepms(150); + IO_Serial_RTS_Clr(reader); + + smargo_Serial_Read(reader, ATR_TIMEOUT, atr_len + 1, buf, &n); + + if(ATR_InitFromArray(atr, buf, n) != ERROR) + { + rdr_log_dbg(reader, D_DEVICE, "SR: ATR parsing OK"); + ret = OK; + } + + return ret; +} + +static int32_t smargo_reset(struct s_reader *reader, ATR *atr) +{ + rdr_log_dbg(reader, D_IFD, "Resetting card"); + int32_t ret = ERROR; + int32_t i; + unsigned char buf[ATR_MAX_SIZE]; + + int32_t parity[4] = {PARITY_EVEN, PARITY_ODD, PARITY_NONE, PARITY_EVEN}; + + int32_t mhz = 369; + + if(reader->mhz == reader->cardmhz && reader->cardmhz > 369) + { mhz = reader->cardmhz; } + + for(i = 0; i < 4; i++) + { + if(i == 3) // hack for irdeto cards + { smargo_set_settings(reader, 600, 1, 0, 618, 1, 0); } + else + { smargo_set_settings(reader, mhz, 0, 0, 372, 1, 0); } + + call(IO_Serial_SetParity(reader, parity[i])); + + //IO_Serial_Flush(reader); + + IO_Serial_Read(reader, 0, 500000, ATR_MAX_SIZE, buf); + + IO_Serial_RTS_Set(reader); + cs_sleepms(150); + IO_Serial_RTS_Clr(reader); + + int32_t n = 0; + + smargo_Serial_Read(reader, ATR_TIMEOUT, ATR_MAX_SIZE, buf, &n); + + if(n == 0 || buf[0] == 0) + { continue; } + + rdr_log_dump_dbg(reader, D_IFD, buf, n, "ATR: %d bytes", n); + + if((buf[0] != 0x3B && buf[0] != 0x03 && buf[0] != 0x3F) || (buf[1] == 0xFF && buf[2] == 0x00)) + { continue; } // this is not a valid ATR + + if(ATR_InitFromArray(atr, buf, n) != ERROR) + { + ret = OK; + break; + } + } + + int32_t convention; + + ATR_GetConvention(atr, &convention); + // If inverse convention, switch here due to if not PTS will fail + if(convention == ATR_CONVENTION_INVERSE) + { + uint8_t data[4]; + + smargo_set_config_mode_on(reader); + + data[0] = 0x05; + data[1] = 0x01; + IO_Serial_Write(reader, 0, 1000, 2, data); + + smargo_set_config_mode_off(reader); + } + + return ret; +} + +int32_t smargo_activate(struct s_reader *reader, struct s_ATR *atr) +{ + if(!reader->ins7e11_fast_reset) + { + call(smargo_reset(reader, atr)); + } + else + { + rdr_log_dbg(reader, D_DEVICE, "Fast card reset with atr"); + call(smargo_fast_reset_by_atr(reader, atr)); + } + return OK; +} + +const struct s_cardreader cardreader_smargo = +{ + .desc = "smargo", + .typ = R_MOUSE, + .max_clock_speed = 1, + .reader_init = smargo_init, + .activate = smargo_activate, + .write_settings = smargo_write_settings, + .get_status = IO_Serial_GetStatus, + .transmit = IO_Serial_Transmit, + .receive = IO_Serial_Receive, + .close = IO_Serial_Close, + .set_parity = IO_Serial_SetParity, +}; + +#endif diff --git a/csctapi/ifd_smartreader.c b/csctapi/ifd_smartreader.c new file mode 100644 index 0000000..e3e937f --- /dev/null +++ b/csctapi/ifd_smartreader.c @@ -0,0 +1,1831 @@ +/* + ifd_smartreader.c + This module provides IFD handling functions for for Argolis smartreader+. +*/ + +#include "../globals.h" + +#ifdef CARDREADER_SMART +#include +#if defined(__FreeBSD__) +#include +#else +#include +#endif +#include "../oscam-lock.h" +#include "../oscam-string.h" +#include "../oscam-time.h" +#include "icc_async.h" // atr.h included in icc_async.h +#include "ifd_smartreader_types.h" + +#if defined(__CYGWIN__) +#undef OK +#undef ERROR +#undef LOBYTE +#undef HIBYTE +#endif + +#undef OK +#undef ERROR +#define OK 0 +#define ERROR 1 +#define LOBYTE(w) ((unsigned char)((w) & 0xff)) +#define HIBYTE(w) ((unsigned char)((w) >> 8)) + +#define NUM_TXFERS 2 + +static int8_t init_lock = 0; + +static CS_MUTEX_LOCK sr_lock; +// to debug rdrtype and ftdi chip type string value in logs instead off the enumarated value +static const char *const rdrtype_str[6] = { "SR","Infinity", "SRv2", "TripleP1", "TripleP2", "TripleP3" }; +static const char *const type_str[7] = { "TYPE_AM", "TYPE_BM", "TYPE_2232C", "TYPE_R", "TYPE_2232H", "TYPE_4232H", "TYPE_232H" }; + +struct sr_data +{ + int32_t F; + float D; + int8_t closing; + int32_t fs; + int32_t N; + int32_t T; + int32_t inv; + int32_t parity; + int32_t irdeto; + int32_t running; + libusb_device *usb_dev; + libusb_device_handle *usb_dev_handle; + enum smartreader_chip_type type; + enum smartreader_rdrtypename rdrtype; + uint8_t in_ep; + uint8_t out_ep; + int32_t index; + /** usb read timeout */ + int32_t usb_read_timeout; + /** usb write timeout */ + int32_t usb_write_timeout; + uint32_t writebuffer_chunksize; + unsigned char bitbang_enabled; + int baudrate; + int32_t interface; // 0 ,1 or 2 + /** maximum packet size. Needed for filtering modem status bytes every n packets. */ + uint32_t max_packet_size; + unsigned char g_read_buffer[4096]; + uint32_t g_read_buffer_size; + pthread_mutex_t g_read_mutex; + pthread_cond_t g_read_cond; + pthread_mutex_t g_usb_mutex; + pthread_cond_t g_usb_cond; + int32_t poll; + pthread_t rt; + struct libusb_transfer *usbt[NUM_TXFERS]; + unsigned char usb_buffers[NUM_TXFERS][64]; + unsigned char modem_status; + uint16_t tripledelay; + int detectstart ; +}; + +static int32_t init_count; +static int32_t current_count; + +static int32_t smart_read(struct s_reader *reader, unsigned char *buff, uint32_t size, double timeout_ms) +{ + struct sr_data *crdr_data = reader->crdr_data; + int32_t ret = 0; + uint32_t total_read = 0; + int64_t gone = 0; + struct timeb start, now; + + cs_ftime(&start); + now = start; + do { + SAFE_MUTEX_LOCK(&crdr_data->g_read_mutex); + + while(crdr_data->g_read_buffer_size == 0) + { + gone = comp_timeb(&now, &start); + if (gone >= timeout_ms) + break; + struct timespec ts; + add_ms_to_timespec(&ts, timeout_ms - gone); + SAFE_COND_TIMEDWAIT(&crdr_data->g_read_cond, &crdr_data->g_read_mutex, &ts); + cs_ftime(&now); + } + + ret = (crdr_data->g_read_buffer_size > size - total_read ? size - total_read : crdr_data->g_read_buffer_size); + memcpy(buff + total_read, crdr_data->g_read_buffer, ret); + crdr_data->g_read_buffer_size -= ret; + + if(crdr_data->g_read_buffer_size > 0) + { memmove(crdr_data->g_read_buffer, crdr_data->g_read_buffer + ret, crdr_data->g_read_buffer_size); } + + total_read += ret; + SAFE_MUTEX_UNLOCK(&crdr_data->g_read_mutex); + cs_ftime(&now); + if(ret>0) { cs_ftime(&start); now = start;} // reset timeout calculation again since reader is responsive! + } while(total_read < size && comp_timeb(&now, &start) < timeout_ms); + + rdr_log_dump_dbg(reader, D_DEVICE, buff, total_read, "SR: Receive:"); + rdr_log_dbg(reader, D_IFD, " used timeout by smartreader %4.2f ms ", timeout_ms); + return total_read; +} + +static int32_t smart_write(struct s_reader *reader, unsigned char *buff, uint32_t size) +{ + struct sr_data *crdr_data = reader->crdr_data; + int32_t write_size; + uint32_t offset = 0; + int32_t total_written = 0; + int32_t written; + + if(size < crdr_data->writebuffer_chunksize) + { write_size = size; } + else + { write_size = crdr_data->writebuffer_chunksize; } + + while(offset < size) + { + if(offset + write_size > size) + { write_size = size - offset; } + + int32_t ret = libusb_bulk_transfer(crdr_data->usb_dev_handle, + crdr_data->in_ep, + buff + offset, + write_size, + &written, + crdr_data->usb_write_timeout); + if(ret < 0) + { + rdr_log(reader, "usb bulk write failed : ret = %d", ret); + return (ret); + } + rdr_log_dump_dbg(reader, D_DEVICE, buff + offset, written, "SR: Transmit:"); + total_written += written; + offset += write_size; + } + return total_written; +} + +static bool smartreader_check_endpoint(struct s_reader *rdr, libusb_device *usb_dev, uint8_t in_endpoint, uint8_t out_endpoint) +{ + struct libusb_device_descriptor usbdesc; + struct libusb_config_descriptor *configDesc; + int32_t ret; + int32_t j, k, l; + uint8_t tmpEndpointAddress; + int32_t nb_endpoint_ok; + + + nb_endpoint_ok = 0; + ret = libusb_get_device_descriptor(usb_dev, &usbdesc); + if(ret < 0) + { + rdr_log(rdr, "Couldn't read device descriptor, assuming this is not a smartreader"); + return 0; + } + if(usbdesc.bNumConfigurations) + { + ret = libusb_get_active_config_descriptor(usb_dev, &configDesc); + if(ret) + { + rdr_log(rdr, "Couldn't read config descriptor, assuming this is not a smartreader"); + return 0; + } + + for(j = 0; j < configDesc->bNumInterfaces; j++) + for(k = 0; k < configDesc->interface[j].num_altsetting; k++) + for(l = 0; l < configDesc->interface[j].altsetting[k].bNumEndpoints; l++) + { + tmpEndpointAddress = configDesc->interface[j].altsetting[k].endpoint[l].bEndpointAddress; + if((tmpEndpointAddress == in_endpoint) || (tmpEndpointAddress == out_endpoint)) + { nb_endpoint_ok++; } + } + } + if(nb_endpoint_ok != 2) + { + rdr_log(rdr, "Endpoint check failed, assuming this is not a smartreader"); + return 0; + } + return 1; +} + +static struct libusb_device *find_smartreader(struct s_reader *rdr, const char *busname, const char *dev_name, uint8_t in_endpoint, uint8_t out_endpoint) +{ + rdr->smartdev_found = 0; + libusb_device *dev; + libusb_device_handle *usb_dev_handle; + libusb_device **devs; + ssize_t cnt; + int32_t i = 0; + int32_t ret; + struct libusb_device_descriptor usbdesc; + + cnt = libusb_get_device_list(NULL, &devs); + if(cnt < 0) + { return NULL; } + + while((dev = devs[i++]) != NULL) + { + rdr->smartdev_found = 0; + ret = libusb_get_device_descriptor(dev, &usbdesc); + if(ret < 0) + { + rdr_log(rdr, "failed to get device descriptor for device %s on bus %s", dev_name, busname); + return NULL; + } + + if(usbdesc.idVendor == 0x0403 && (usbdesc.idProduct == 0x6001 || usbdesc.idProduct == 0x6011)) + { + ret = libusb_open(dev, &usb_dev_handle); + if(ret) + { + rdr_log(rdr, "coulnd't open device %03d:%03d", libusb_get_bus_number(dev), libusb_get_device_address(dev)); + switch(ret) + { + case LIBUSB_ERROR_NO_MEM: + rdr_log(rdr, "libusb_open error LIBUSB_ERROR_NO_MEM : memory allocation failure"); + break; + case LIBUSB_ERROR_ACCESS: + rdr_log(rdr, "libusb_open error LIBUSB_ERROR_ACCESS : the user has insufficient permissions"); + break; + case LIBUSB_ERROR_NO_DEVICE: + rdr_log(rdr, "libusb_open error LIBUSB_ERROR_NO_DEVICE : the device has been disconnected"); + break; + default: + rdr_log(rdr, "libusb_open unknown error : %d", ret); + break; + } + continue; + } + + // If the device is specified as "Serial:number", check iSerial + if(!strcasecmp(busname, "Serial")) + { + char iserialbuffer[128]; + if(libusb_get_string_descriptor_ascii(usb_dev_handle, usbdesc.iSerialNumber, (unsigned char *)iserialbuffer, sizeof(iserialbuffer)) > 0) + { + if(!strcmp(trim(iserialbuffer), dev_name)) + { + rdr_log_dbg(rdr, D_IFD, "Found reader with serial %s at %03d:%03d", dev_name, libusb_get_bus_number(dev), libusb_get_device_address(dev)); + if(smartreader_check_endpoint(rdr, dev, in_endpoint, out_endpoint)) { + if(out_endpoint == 0x82 && in_endpoint == 0x01 && usbdesc.idProduct == 0x6001) { rdr->smart_type = 0; rdr->smartdev_found = 1;} else + if(out_endpoint == 0x81 && in_endpoint == 0x01) { rdr->smart_type = 1; rdr->smartdev_found = 2;} else + if(out_endpoint == 0x81 && in_endpoint == 0x02 && usbdesc.idProduct == 0x6001) { rdr->smart_type = 2; rdr->smartdev_found = 3;} else + if(out_endpoint == 0x81 && in_endpoint == 0x02 && usbdesc.idProduct == 0x6011) { rdr->smart_type = 3; rdr->smartdev_found = 4; rdr->modemstat = 1;} else + if(out_endpoint == 0x83 && in_endpoint == 0x04 && usbdesc.idProduct == 0x6011) { rdr->smart_type = 4; rdr->smartdev_found = 5; rdr->modemstat = 1;} else + if(out_endpoint == 0x85 && in_endpoint == 0x06 && usbdesc.idProduct == 0x6011) { rdr->smart_type = 5; rdr->smartdev_found = 6; rdr->modemstat = 1;} else + rdr->smartdev_found = 0; + } + } + } + } + else if(libusb_get_bus_number(dev) == atoi(busname) && libusb_get_device_address(dev) == atoi(dev_name)) + { + rdr_log_dbg(rdr, D_DEVICE, "SR: Checking FTDI device: %03d on bus %03d", libusb_get_device_address(dev), libusb_get_bus_number(dev)); + // check for smargo endpoints. + if(smartreader_check_endpoint(rdr, dev, in_endpoint, out_endpoint)) { + if(out_endpoint == 0x82 && in_endpoint == 0x01 && usbdesc.idProduct == 0x6001) { rdr->smart_type = 0; rdr->smartdev_found = 1;} else + if(out_endpoint == 0x81 && in_endpoint == 0x01) { rdr->smart_type = 1; rdr->smartdev_found = 2;} else + if(out_endpoint == 0x81 && in_endpoint == 0x02 && usbdesc.idProduct == 0x6001) { rdr->smart_type = 2; rdr->smartdev_found = 3;} else + if(out_endpoint == 0x81 && in_endpoint == 0x02 && usbdesc.idProduct == 0x6011) { rdr->smart_type = 3; rdr->smartdev_found = 4; rdr->modemstat = 1;} else + if(out_endpoint == 0x83 && in_endpoint == 0x04 && usbdesc.idProduct == 0x6011) { rdr->smart_type = 4; rdr->smartdev_found = 5; rdr->modemstat = 1;} else + if(out_endpoint == 0x85 && in_endpoint == 0x06 && usbdesc.idProduct == 0x6011) { rdr->smart_type = 5; rdr->smartdev_found = 6; rdr->modemstat = 1;} else + rdr->smartdev_found = 0; + } + } + libusb_close(usb_dev_handle); + } + + if(rdr->smartdev_found >= 1) + { break; } + } + + if(!rdr->smartdev_found) + { + rdr_log(rdr, "Smartreader device %s:%s not found", busname, dev_name); + return NULL; + } + else + rdr_log_dbg(rdr, D_IFD, "Found smartreader device %s:%s", busname, dev_name); + + return dev; +} + +void smartreader_init(struct s_reader *reader) +{ + uint32_t i; + struct sr_data *crdr_data = reader->crdr_data; + + crdr_data->usb_dev = NULL; + crdr_data->usb_dev_handle = NULL; + crdr_data->usb_read_timeout = 15000; + crdr_data->usb_write_timeout = 10000; + + crdr_data->type = TYPE_BM; /* chip type */ + crdr_data->baudrate = -1; + crdr_data->bitbang_enabled = 0; /* 0: normal mode 1: any of the bitbang modes enabled */ + + crdr_data->writebuffer_chunksize = 4096; + crdr_data->max_packet_size = 0; + rdr_log_dbg(reader, D_IFD, "initing smartreader type %s", rdrtype_str[crdr_data->rdrtype]); + for(i = 0; i < sizeof(reader_types) / sizeof(struct s_reader_types); ++i) { + if(reader_types[i].rdrtypename == crdr_data->rdrtype) { + crdr_data->in_ep = reader_types[i].in_ep; + crdr_data->out_ep = reader_types[i].out_ep; + crdr_data->index = reader_types[i].index; + crdr_data->interface = reader_types[i].interface; + } + } +} + + +static uint32_t smartreader_determine_max_packet_size(struct s_reader *reader) +{ + struct sr_data *crdr_data = reader->crdr_data; + uint32_t packet_size; + struct libusb_device_descriptor usbdesc; + struct libusb_config_descriptor *configDesc; + struct libusb_interface interface; + struct libusb_interface_descriptor intDesc; + + int32_t ret; + // Determine maximum packet size. Init with default value. + // New hi-speed devices from FTDI use a packet size of 512 bytes + // but could be connected to a normal speed USB hub -> 64 bytes packet size. +// rdr_log(reader,"DE PACKET SIZE DETERMINATION USES READER TYPE %u", crdr_data->type); + if(crdr_data->type == TYPE_2232H || crdr_data->type == TYPE_4232H) + { + packet_size = 512; + } + else + { packet_size = 64; } + + ret = libusb_get_device_descriptor(crdr_data->usb_dev, &usbdesc); + if(ret < 0) + { + rdr_log(reader, "Couldn't read device descriptor, using default packet size"); + return packet_size; + } + if(usbdesc.bNumConfigurations) + { + ret = libusb_get_active_config_descriptor(crdr_data->usb_dev, &configDesc); + if(ret) + { + rdr_log(reader, "Couldn't read config descriptor, using default packet size"); + return packet_size; + } + + if(crdr_data->interface < configDesc->bNumInterfaces) + { + interface = configDesc->interface[crdr_data->interface]; + if(interface.num_altsetting > 0) + { + intDesc = interface.altsetting[0]; + if(intDesc.bNumEndpoints > 0) + { + packet_size = intDesc.endpoint[0].wMaxPacketSize; + } + } + } + } + return packet_size; +} + + +static int32_t smartreader_usb_close_internal(struct s_reader *reader) +{ + struct sr_data *crdr_data = reader->crdr_data; + int32_t ret = 0; + + if(crdr_data->usb_dev_handle) + { + libusb_close(crdr_data->usb_dev_handle); + crdr_data->usb_dev_handle = NULL; + } + + return ret; +} + + +static int32_t smartreader_usb_reset(struct s_reader *reader) +{ + struct sr_data *crdr_data = reader->crdr_data; + if(libusb_control_transfer(crdr_data->usb_dev_handle, + FTDI_DEVICE_OUT_REQTYPE, + SIO_RESET_REQUEST, + SIO_RESET_SIO, + crdr_data->index, + NULL, + 0, + crdr_data->usb_write_timeout) != 0) + { + rdr_log(reader, "Smartreader reset failed"); + return (-1); + } + + return 0; +} + + +static int32_t smartreader_usb_purge_rx_buffer(struct s_reader *reader) +{ + struct sr_data *crdr_data = reader->crdr_data; + if(libusb_control_transfer(crdr_data->usb_dev_handle, + FTDI_DEVICE_OUT_REQTYPE, + SIO_RESET_REQUEST, + SIO_RESET_PURGE_RX, + crdr_data->index, + NULL, + 0, + crdr_data->usb_write_timeout) != 0) + { + rdr_log(reader, "FTDI purge of RX buffer failed"); + return (-1); + } + + + return 0; +} + +static int32_t smartreader_usb_purge_tx_buffer(struct s_reader *reader) +{ + struct sr_data *crdr_data = reader->crdr_data; + if(libusb_control_transfer(crdr_data->usb_dev_handle, + FTDI_DEVICE_OUT_REQTYPE, + SIO_RESET_REQUEST, + SIO_RESET_PURGE_TX, + crdr_data->index, + NULL, + 0, + crdr_data->usb_write_timeout) != 0) + { + rdr_log(reader, "FTDI purge of TX buffer failed"); + return (-1); + } + + return 0; +} + +static int32_t smartreader_usb_purge_buffers(struct s_reader *reader) +{ + int32_t result; + + result = smartreader_usb_purge_rx_buffer(reader); + if(result < 0) + { return -1; } + + result = smartreader_usb_purge_tx_buffer(reader); + if(result < 0) + { return -2; } + + return 0; +} + +static int smartreader_to_clkbits_AM(int baudrate, unsigned long *encoded_divisor) + +{ + static const char frac_code[8] = {0, 3, 2, 4, 1, 5, 6, 7}; + static const char am_adjust_up[8] = {0, 0, 0, 1, 0, 3, 2, 1}; + static const char am_adjust_dn[8] = {0, 0, 0, 1, 0, 1, 2, 3}; + int divisor, best_divisor, best_baud, best_baud_diff; + divisor = 24000000 / baudrate; + int i; + + // Round down to supported fraction (AM only) + divisor -= am_adjust_dn[divisor & 7]; + + // Try this divisor and the one above it (because division rounds down) + best_divisor = 0; + best_baud = 0; + best_baud_diff = 0; + for (i = 0; i < 2; i++) + { + int try_divisor = divisor + i; + int baud_estimate; + int baud_diff; + + // Round up to supported divisor value + if (try_divisor <= 8) + { + // Round up to minimum supported divisor + try_divisor = 8; + } + else if (divisor < 16) + { + // AM doesn't support divisors 9 through 15 inclusive + try_divisor = 16; + } + else + { + // Round up to supported fraction (AM only) + try_divisor += am_adjust_up[try_divisor & 7]; + if (try_divisor > 0x1FFF8) + { + // Round down to maximum supported divisor value (for AM) + try_divisor = 0x1FFF8; + } + } + // Get estimated baud rate (to nearest integer) + baud_estimate = (24000000 + (try_divisor / 2)) / try_divisor; + // Get absolute difference from requested baud rate + if (baud_estimate < baudrate) + { + baud_diff = baudrate - baud_estimate; + } + else + { + baud_diff = baud_estimate - baudrate; + } + if (i == 0 || baud_diff < best_baud_diff) + { + // Closest to requested baud rate so far + best_divisor = try_divisor; + best_baud = baud_estimate; + best_baud_diff = baud_diff; + if (baud_diff == 0) + { + // Spot on! No point trying + break; + } + } + } + // Encode the best divisor value + *encoded_divisor = (best_divisor >> 3) | (frac_code[best_divisor & 7] << 14); + // Deal with special cases for encoded value + if (*encoded_divisor == 1) + { + *encoded_divisor = 0; // 3000000 baud + } + else if (*encoded_divisor == 0x4001) + { + *encoded_divisor = 1; // 2000000 baud (BM only) + } + return best_baud; +} + +/* ftdi_to_clkbits Convert a requested baudrate for a given system clock and predivisor + to encoded divisor and the achievable baudrate + Function is only used internally + \internal + + See AN120 + clk/1 -> 0 + clk/1.5 -> 1 + clk/2 -> 2 + From /2, 0.125 steps may be taken. + The fractional part has frac_code encoding + + value[13:0] of value is the divisor + index[9] mean 12 MHz Base(120 MHz/10) rate versus 3 MHz (48 MHz/16) else + + H Type have all features above with + {index[8],value[15:14]} is the encoded subdivisor + + FT232R, FT2232 and FT232BM have no option for 12 MHz and with + {index[0],value[15:14]} is the encoded subdivisor + + AM Type chips have only four fractional subdivisors at value[15:14] + for subdivisors 0, 0.5, 0.25, 0.125 +*/ +static int smartreader_to_clkbits(int baudrate, int clk, int clk_div, unsigned long *encoded_divisor) +{ + static const char frac_code[8] = {0, 3, 2, 4, 1, 5, 6, 7}; + int best_baud = 0; + int divisor, best_divisor; + if (baudrate >= clk/clk_div) + { + *encoded_divisor = 0; + best_baud = clk/clk_div; + } + else if (baudrate >= clk/(clk_div + clk_div/2)) + { + *encoded_divisor = 1; + best_baud = clk/(clk_div + clk_div/2); + } + else if (baudrate >= clk/(2*clk_div)) + { + *encoded_divisor = 2; + best_baud = clk/(2*clk_div); + } + else + { + /* We divide by 16 to have 3 fractional bits and one bit for rounding */ + divisor = clk*16/clk_div / baudrate; + if (divisor & 1) /* Decide if to round up or down*/ + best_divisor = divisor /2 +1; + else + best_divisor = divisor/2; + if(best_divisor > 0x20000) + best_divisor = 0x1ffff; + best_baud = clk*16/clk_div/best_divisor; + if (best_baud & 1) /* Decide if to round up or down*/ + best_baud = best_baud /2 +1; + else + best_baud = best_baud /2; + *encoded_divisor = (best_divisor >> 3) | (frac_code[best_divisor & 0x7] << 14); + } + return best_baud; +} +/** + ftdi_convert_baudrate returns nearest supported baud rate to that requested. + Function is only used internally + \internal +*/ +static int smartreader_convert_baudrate(int baudrate, struct s_reader *reader, unsigned short *value, unsigned short *idx) +{ + int best_baud; + unsigned long encoded_divisor; + struct sr_data *crdr_data = reader->crdr_data; + + if (baudrate <= 0) + { + // return ERROR + return -1; + } + +#define H_CLK 120000000 +#define C_CLK 48000000 + if ((crdr_data->type == TYPE_2232H) || (crdr_data->type == TYPE_4232H) || (crdr_data->type == TYPE_232H)) + { + if(baudrate*10 > H_CLK /0x3fff) + { + /* On H Devices, use 12 000 000 Baudrate when possible + We have a 14 bit divisor, a 1 bit divisor switch (10 or 16) + three fractional bits and a 120 MHz clock + Assume AN_120 "Sub-integer divisors between 0 and 2 are not allowed" holds for + DIV/10 CLK too, so /1, /1.5 and /2 can be handled the same*/ + best_baud = smartreader_to_clkbits(baudrate, H_CLK, 10, &encoded_divisor); + encoded_divisor |= 0x20000; /* switch on CLK/10*/ + } + else + best_baud = smartreader_to_clkbits(baudrate, C_CLK, 16, &encoded_divisor); + } + else if ((crdr_data->type == TYPE_BM) || (crdr_data->type == TYPE_2232C) || (crdr_data->type == TYPE_R )) + { + best_baud = smartreader_to_clkbits(baudrate, C_CLK, 16, &encoded_divisor); + } + else + { + best_baud = smartreader_to_clkbits_AM(baudrate, &encoded_divisor); + } + // Split into "value" and "index" values + *value = (unsigned short)(encoded_divisor & 0xFFFF); + if (crdr_data->type == TYPE_2232H || + crdr_data->type == TYPE_4232H || crdr_data->type == TYPE_232H) + { + *idx = (unsigned short)(encoded_divisor >> 8); + *idx &= 0xFF00; + *idx |= crdr_data->index; + } + else + *idx = (unsigned short)(encoded_divisor >> 16); + + // Return the nearest baud rate + return best_baud; +} + +/** + Sets the chip baud rate + + \param ftdi pointer to ftdi_context + \param baudrate baud rate to set + + \retval 0: all fine + \retval -1: invalid baudrate + \retval -2: setting baudrate failed + \retval -3: USB device unavailable +*/ +int smartreader_set_baudrate(struct s_reader *reader, int baudrate) +{ + struct sr_data *crdr_data = reader->crdr_data; + unsigned short value, idx; + int actual_baudrate; + + if (crdr_data->usb_dev == NULL){ + rdr_log(reader, "USB device unavailable"); + return ERROR; + } + + if (crdr_data->bitbang_enabled) + { + baudrate = baudrate*4; + } + + actual_baudrate = smartreader_convert_baudrate(baudrate, reader, &value, &idx); + if (actual_baudrate <= 0) { + rdr_log(reader, "Silly baudrate <= 0."); + return (-1); + } + + // Check within tolerance (about 5%) + if ((actual_baudrate * 2 < baudrate /* Catch overflows */ ) + || ((actual_baudrate < baudrate) + ? (actual_baudrate * 21 < baudrate * 20) + : (baudrate * 21 < actual_baudrate * 20))) { + rdr_log(reader, "Unsupported baudrate. Note: bitbang baudrates are automatically multiplied by 4"); + return (-1); + } + if (libusb_control_transfer(crdr_data->usb_dev_handle, + FTDI_DEVICE_OUT_REQTYPE, + SIO_SET_BAUDRATE_REQUEST, + value, + idx, + NULL, + 0, + crdr_data->usb_write_timeout) < 0) { + rdr_log(reader, "Setting new baudrate failed"); + return (-2); + } + crdr_data->baudrate = baudrate; +// rdr_log(reader,"BAUDRATE IS NOW SET ON %u", crdr_data->baudrate); +// rdr_log(reader,"ACTUAL BAUDRATE = %u", actual_baudrate); + return 0; +} + +static int32_t smartreader_setdtr_rts(struct s_reader *reader, int32_t dtr, int32_t rts) +{ + struct sr_data *crdr_data = reader->crdr_data; + uint16_t usb_val; + + + if(dtr) + { usb_val = SIO_SET_DTR_HIGH; } + else + { usb_val = SIO_SET_DTR_LOW; } + + if(rts) + { usb_val |= SIO_SET_RTS_HIGH; } + else + { usb_val |= SIO_SET_RTS_LOW; } + if(libusb_control_transfer(crdr_data->usb_dev_handle, + FTDI_DEVICE_OUT_REQTYPE, + SIO_SET_MODEM_CTRL_REQUEST, + usb_val, + crdr_data->index, + NULL, + 0, + crdr_data->usb_write_timeout) != 0) + { + rdr_log(reader, "set of rts/dtr failed"); + return (-1); + } + return 0; +} + +static int32_t smartreader_setflowctrl(struct s_reader *reader, int32_t flowctrl) +{ + struct sr_data *crdr_data = reader->crdr_data; + if(libusb_control_transfer(crdr_data->usb_dev_handle, + FTDI_DEVICE_OUT_REQTYPE, + SIO_SET_FLOW_CTRL_REQUEST, + 0, + (flowctrl | crdr_data->index), + NULL, + 0, + crdr_data->usb_write_timeout) != 0) + { + rdr_log(reader, "set flow control failed"); + return (-1); + } + return 0; +} + +static int32_t smartreader_set_line_property2(struct s_reader *reader, enum smartreader_bits_type bits, + enum smartreader_stopbits_type sbit, enum smartreader_parity_type parity, + enum smartreader_break_type break_type) +{ + struct sr_data *crdr_data = reader->crdr_data; + uint16_t value = bits; + + switch(parity) + { + case NONE: + value |= (0x00 << 8); + break; + case ODD: + value |= (0x01 << 8); + break; + case EVEN: + value |= (0x02 << 8); + break; + case MARK: + value |= (0x03 << 8); + break; + case SPACE: + value |= (0x04 << 8); + break; + } + + switch(sbit) + { + case STOP_BIT_1: + value |= (0x00 << 11); + break; + case STOP_BIT_15: + value |= (0x01 << 11); + break; + case STOP_BIT_2: + value |= (0x02 << 11); + break; + } + + switch(break_type) + { + case BREAK_OFF: + value |= (0x00 << 14); + break; + case BREAK_ON: + value |= (0x01 << 14); + break; + } + if(libusb_control_transfer(crdr_data->usb_dev_handle, + FTDI_DEVICE_OUT_REQTYPE, + SIO_SET_DATA_REQUEST, + value, + crdr_data->index, + NULL, + 0, + crdr_data->usb_write_timeout) != 0) + { + rdr_log(reader, "Setting new line property failed"); + return (-1); + } + return 0; +} + + +static int32_t smartreader_set_line_property(struct s_reader *reader, enum smartreader_bits_type bits, + enum smartreader_stopbits_type sbit, enum smartreader_parity_type parity) +{ + return smartreader_set_line_property2(reader, bits, sbit, parity, BREAK_OFF); +} + + + +static void smart_flush(struct s_reader *reader) +{ + smartreader_usb_purge_buffers(reader); + + struct sr_data *crdr_data = reader->crdr_data; + SAFE_MUTEX_LOCK(&crdr_data->g_read_mutex); + crdr_data->g_read_buffer_size = 0; + SAFE_MUTEX_UNLOCK(&crdr_data->g_read_mutex); +} + +static int32_t smartreader_set_latency_timer(struct s_reader *reader, uint16_t latency) +{ + struct sr_data *crdr_data = reader->crdr_data; + uint16_t usb_val; + + if(latency < 1) + { + rdr_log(reader, "latency out of range. Only valid for 1-255"); + return (-1); + } + + usb_val = latency; + if(libusb_control_transfer(crdr_data->usb_dev_handle, + FTDI_DEVICE_OUT_REQTYPE, + SIO_SET_LATENCY_TIMER_REQUEST, + usb_val, + crdr_data->index, + NULL, + 0, + crdr_data->usb_write_timeout) != 0) + { + rdr_log(reader, "unable to set latency timer"); + return (-2); + } + return 0; +} + +static void read_callback(struct libusb_transfer *transfer) +{ + struct s_reader *reader = (struct s_reader *)transfer->user_data; + struct sr_data *crdr_data = reader->crdr_data; + int32_t copy_size; + int32_t ret; + + if(transfer->status == LIBUSB_TRANSFER_COMPLETED) + { + if(transfer->actual_length > 2) //FTDI always sends modem status bytes as first 2 chars with the 232BM + { + SAFE_MUTEX_LOCK(&crdr_data->g_read_mutex); + + if(crdr_data->g_read_buffer_size == sizeof(crdr_data->g_read_buffer)) + { + rdr_log(reader, "SR: buffer full"); + //if out read buffer is full then delay + //slightly and go around again + ret = libusb_submit_transfer(transfer); + if(ret != 0) + { rdr_log(reader, "SR: submit async transfer failed with error %d", ret); } + SAFE_COND_SIGNAL(&crdr_data->g_read_cond); + SAFE_MUTEX_UNLOCK(&crdr_data->g_read_mutex); + return; + } + crdr_data->modem_status = transfer->buffer[0]; +// rdr_log(reader, " Transfer Buf 0 = 0x%2x, Buf 1 = 0x%2x, Buf 2 = 0x%2x", transfer->buffer[0], transfer->buffer[1], transfer->buffer[2] ); + + copy_size = sizeof(crdr_data->g_read_buffer) - crdr_data->g_read_buffer_size > (uint32_t)transfer->actual_length - 2 ? (uint32_t)transfer->actual_length - 2 : sizeof(crdr_data->g_read_buffer) - crdr_data->g_read_buffer_size; + memcpy(crdr_data->g_read_buffer + crdr_data->g_read_buffer_size, transfer->buffer + 2, copy_size); + crdr_data->g_read_buffer_size += copy_size; + + SAFE_COND_SIGNAL(&crdr_data->g_read_cond); + SAFE_MUTEX_UNLOCK(&crdr_data->g_read_mutex); + } + else + { + if(transfer->actual_length == 2) + { + SAFE_MUTEX_LOCK(&crdr_data->g_read_mutex); + crdr_data->modem_status = transfer->buffer[0]; + SAFE_MUTEX_UNLOCK(&crdr_data->g_read_mutex); + } + } + + ret = libusb_submit_transfer(transfer); + + if(ret != 0) + { rdr_log(reader, "SR: submit async transfer failed with error %d", ret); } + + } + else + { + if (!crdr_data->closing && init_count) { + rdr_log(reader, "SR: USB bulk read failed with error %d", transfer->status); + } + } +} + +static int32_t smartreader_usb_open_dev(struct s_reader *reader) +{ + struct sr_data *crdr_data = reader->crdr_data; + int32_t detach_errno = 0; + struct libusb_device_descriptor usbdesc; + int32_t ret; + +#ifdef __WIN32__ + int32_t config; + int32_t config_val = 1; +#endif + + ret = libusb_open(crdr_data->usb_dev, &crdr_data->usb_dev_handle); + if(ret) + { + rdr_log(reader, "Coulnd't open smartreader device %03d:%03d", libusb_get_bus_number(crdr_data->usb_dev), libusb_get_device_address(crdr_data->usb_dev)); + switch(ret) + { + case LIBUSB_ERROR_NO_MEM: + rdr_log(reader, "libusb_open error LIBUSB_ERROR_NO_MEM : memory allocation failure"); + break; + case LIBUSB_ERROR_ACCESS: + rdr_log(reader, "libusb_open error LIBUSB_ERROR_ACCESS : the user has insufficient permissions"); + break; + case LIBUSB_ERROR_NO_DEVICE: + rdr_log(reader, "libusb_open error LIBUSB_ERROR_NO_DEVICE : the device has been disconnected"); + break; + default: + rdr_log(reader, "libusb_open unknown error : %d", ret); + break; + } + return (-4); + } + +#if defined(__linux__) + // Try to detach ftdi_sio kernel module. + // Returns ENODATA if driver is not loaded. + // + // The return code is kept in a separate variable and only parsed + // if usb_set_configuration() or usb_claim_interface() fails as the + // detach operation might be denied and everything still works fine. + // Likely scenario is a static smartreader_sio kernel module. + if(libusb_detach_kernel_driver(crdr_data->usb_dev_handle, crdr_data->interface) != 0 && errno != ENODATA) + { + smartreader_usb_close_internal(reader); + rdr_log(reader, "Couldn't detach interface from kernel. Please unload the FTDI drivers"); + return (LIBUSB_ERROR_NOT_SUPPORTED); + } +#endif + ret = libusb_get_device_descriptor(crdr_data->usb_dev, &usbdesc); + if(ret != 0) { + rdr_log_dbg(reader, D_IFD, "libusb_get_device_descriptor failed"); + } else { + rdr_log_dbg(reader, D_IFD, "libusb_get_device_descriptor ok"); + } + +#ifdef __WIN32__ + // set configuration (needed especially for windows) + // tolerate EBUSY: one device with one configuration, but two interfaces + // and libftdi sessions to both interfaces (e.g. FT2232) + + if(usbdesc.bNumConfigurations > 0) + { + ret = libusb_get_configuration(crdr_data->usb_dev_handle, &config); + + // libusb-win32 on Windows 64 can return a null pointer for a valid device + if(libusb_set_configuration(crdr_data->usb_dev_handle, config) && errno != EBUSY) + { + smartreader_usb_close_internal(reader); + if(detach_errno == EPERM) + { + rdr_log(reader, "inappropriate permissions on device!"); + return (-8); + } else { + rdr_log(reader, "unable to set usb configuration. Make sure smartreader_sio is unloaded!"); + return (-3); + } + } else { + rdr_log_dbg(rdr, D_IFD, "libusb_set_configuration failed"); + } + } +#endif + + ret = libusb_claim_interface(crdr_data->usb_dev_handle, crdr_data->interface) ; + + if(ret != 0) + { + smartreader_usb_close_internal(reader); + if(detach_errno == EPERM) + { + rdr_log(reader, "inappropriate permissions on device!"); + return (-8); + } else { + rdr_log(reader, "unable to claim usb device. Make sure smartreader_sio is unloaded!"); + return (-5); + } + } + else { + rdr_log_dbg(reader, D_IFD, "smartreader_usb_close_internal OK"); + } + + if(smartreader_usb_reset(reader) != 0) + { + libusb_release_interface(crdr_data->usb_dev_handle, crdr_data->interface); + smartreader_usb_close_internal(reader); + rdr_log(reader, "smartreader_usb_reset failed"); + return (-6); + } + + // Try to guess chip type + // Bug in the BM type chips: bcdDevice is 0x200 for serial == 0 + if(usbdesc.bcdDevice == 0x400 || (usbdesc.bcdDevice == 0x200 && usbdesc.iSerialNumber == 0)) + { crdr_data->type = TYPE_BM; } + else if(usbdesc.bcdDevice == 0x200) + { crdr_data->type = TYPE_AM; } + else if(usbdesc.bcdDevice == 0x500) + { + if(usbdesc.idProduct == 0x6011) + { + crdr_data->type = TYPE_4232H; + } else { + crdr_data->type = TYPE_2232C; + } + } + else if(usbdesc.bcdDevice == 0x600) + { crdr_data->type = TYPE_R; } + else if(usbdesc.bcdDevice == 0x700) + { crdr_data->type = TYPE_2232H; } + else if(usbdesc.bcdDevice == 0x800) + { crdr_data->type = TYPE_4232H; } + + // Determine maximum packet size + crdr_data->max_packet_size = smartreader_determine_max_packet_size(reader); + rdr_log_dbg(reader, D_IFD, "FTDI CHIP %s", type_str[crdr_data->type]); + rdr_log_dbg(reader, D_IFD, "max packet size is %u", crdr_data->max_packet_size); + + if(smartreader_set_baudrate(reader, 9600) != 0) + { + libusb_release_interface(crdr_data->usb_dev_handle, crdr_data->interface); + smartreader_usb_close_internal(reader); + rdr_log(reader, "set baudrate failed"); + return (-7); + } + + return (0); +} + +static void EnableSmartReader(struct s_reader *reader, uint32_t baud_temp2, int32_t clock_val, uint16_t Fi, unsigned char Di, unsigned char Ni, unsigned char T, unsigned char inv, int32_t parity) +{ + struct sr_data *crdr_data = reader->crdr_data; + unsigned char FiDi[4]; + uint16_t freqk; + unsigned char Freq[3]; + unsigned char N[2]; + unsigned char Prot[2]; + unsigned char Invert[2]; + unsigned char temp_T; + + smartreader_set_baudrate(reader, baud_temp2); + smartreader_setflowctrl(reader, 0); + if (crdr_data->rdrtype >= 2) cs_sleepms(150); // for changing a line setting the V2 and Triple need a delay + smartreader_set_line_property(reader, (enum smartreader_bits_type) 5, STOP_BIT_2, NONE); + + // command 1, set F and D parameter + if(!crdr_data->irdeto) + { + rdr_log_dbg(reader, D_DEVICE, "SR: sending F=%04X (%d) to smartreader", Fi, Fi); + rdr_log_dbg(reader, D_DEVICE, "SR: sending D=%02X (%d) to smartreader", Di, Di); + FiDi[0] = 0x01; + FiDi[1] = HIBYTE(Fi); + FiDi[2] = LOBYTE(Fi); + FiDi[3] = Di; + smart_write(reader, FiDi, sizeof(FiDi)); + } + else + { + rdr_log_dbg(reader, D_IFD, "Not setting F and D as we're in Irdeto mode"); + } + + // command 2, set the frequency in KHz + // direct from the source .. 4MHz is the best init frequency for T=0 card, but looks like it's causing issue with some nagra card, reveting to 3.69MHz + freqk = clock_val * 10; //clock with type int32_t couldnt hold freq in Hz on all platforms, so I reverted to 10khz units (like mhz) - dingo + rdr_log_dbg(reader, D_DEVICE, "SR: sending Freq=%04X (%d) to smartreader", freqk, freqk); + Freq[0] = 0x02; + Freq[1] = HIBYTE(freqk); + Freq[2] = LOBYTE(freqk); + smart_write(reader, Freq, sizeof(Freq)); + + // command 3, set paramter N + rdr_log_dbg(reader, D_DEVICE, "SR: sending N=%02X (%d) to smartreader", Ni, Ni); + N[0] = 0x03; + N[1] = Ni; + smart_write(reader, N, sizeof(N)); + + // command 4 , set parameter T + temp_T = T; + if(T == 2) // special trick to get ATR for Irdeto card, we need T=1 at reset, after that oscam takes care of T1 protocol, so we need T=0 + //if(crdr_data->irdeto) // special trick to get ATR for Irdeto card, we need T=1 at reset, after that oscam takes care of T1 protocol, so we need T=0 + { + T = 1; + crdr_data->T = 1; + temp_T = 1; + } + else if(T == 1) + { T = 0; } // T=1 protocol is handled by oscam + + rdr_log_dbg(reader, D_DEVICE, "SR: sending T=%02X (%d) to smartreader", T, T); + Prot[0] = 0x04; + Prot[1] = T; + smart_write(reader, Prot, sizeof(Prot)); + + // command 5, set invert y/n + rdr_log_dbg(reader, D_DEVICE, "SR: sending inv=%02X to smartreader", inv); + Invert[0] = 0x05; + Invert[1] = inv; + smart_write(reader, Invert, sizeof(Invert)); + + cs_sleepms(250); // this delay needed for Triple and v2 + smartreader_set_line_property2(reader, BITS_8, STOP_BIT_2, parity, BREAK_ON); + // send break for 350ms, also comes from JoePub debugging.break + cs_sleepms(350); + + + if(temp_T == 1) + { smartreader_set_line_property2(reader, BITS_8, STOP_BIT_1, parity, BREAK_OFF); } + else + { smartreader_set_line_property2(reader, BITS_8, STOP_BIT_2, parity, BREAK_OFF); } + + cs_sleepus(800); + smart_flush(reader); + cs_sleepus(800); + crdr_data->detectstart = 1; + +} + + +static void *ReaderThread(void *p) +{ + struct s_reader *reader; + int32_t ret, idx; + + reader = (struct s_reader *)p; + struct sr_data *crdr_data = reader->crdr_data; + crdr_data->running = 1; + + set_thread_name(__func__); + + for(idx = 0; idx < NUM_TXFERS; idx++) + { + + crdr_data->usbt[idx] = libusb_alloc_transfer(0); + libusb_fill_bulk_transfer(crdr_data->usbt[idx], + crdr_data->usb_dev_handle, + crdr_data->out_ep, + crdr_data->usb_buffers[idx], + 64, + (void *)(&read_callback), + reader, + 0); + + ret = libusb_submit_transfer(crdr_data->usbt[idx]); + if(ret != 0) + { + rdr_log_dbg(reader, D_IFD, "libusb_submit_transfer ok"); + } else { + rdr_log_dbg(reader, D_IFD, "libusb_submit_transfer failed"); + } + } + + while(crdr_data->running) + { + ret = libusb_handle_events(NULL); + if(ret != 0) + { rdr_log(reader, "libusb_handle_events returned with %d", ret); } + + SAFE_MUTEX_LOCK(&crdr_data->g_usb_mutex); + if(!crdr_data->poll) + { + struct timespec timeout; + add_ms_to_timespec(&timeout, 2000); + SAFE_COND_TIMEDWAIT(&crdr_data->g_usb_cond, &crdr_data->g_usb_mutex, &timeout); + } + SAFE_MUTEX_UNLOCK(&crdr_data->g_usb_mutex); + } + + pthread_exit(NULL); +} + +static void smart_fastpoll(struct s_reader *reader, int32_t on) +{ + struct sr_data *crdr_data = reader->crdr_data; + SAFE_MUTEX_LOCK(&crdr_data->g_usb_mutex); + //printf("poll stat: %d\n", on); + crdr_data->poll = on; + SAFE_COND_SIGNAL(&crdr_data->g_usb_cond); + SAFE_MUTEX_UNLOCK(&crdr_data->g_usb_mutex); +} + +static int32_t SR_Init(struct s_reader *reader) +{ + uint8_t i = 0; + while(reader->handle_nr > 0 && i < 10) // Restarting the reader while it was not closed does cause segfault. + { + i++; + rdr_log(reader," Wait on close before restart second %u", i); + cs_sleepms(1000); + } + + int32_t ret; + char device[cs_strlen(reader->device) + 1]; + char *rdrtype, *busname, *dev, *search = ":", *saveptr1 = NULL; + memcpy(device, reader->device, cs_strlen(reader->device) + 1); + // split the device name from the reader conf into devname and busname. rdrtype is optional + rdrtype = strtok_r(device, ";", &saveptr1); + busname = strtok_r(NULL, ":", &saveptr1); + dev = strtok_r(NULL, ":", &saveptr1); + if(!busname) + { + rdrtype = "SR"; + memcpy(device, reader->device, cs_strlen(reader->device) + 1); + busname = strtok_r(device, ":", &saveptr1); + dev = strtok_r(NULL, search, &saveptr1); + } + + if(!busname || !dev) + { + rdr_log(reader, "Wrong device format (%s), it should be Device=bus:dev", reader->device); + return ERROR; + } + + if(!reader->crdr_data && !cs_malloc(&reader->crdr_data, sizeof(struct sr_data))) + { return ERROR; } + struct sr_data *crdr_data = reader->crdr_data; + crdr_data->detectstart = 0; + crdr_data->tripledelay = 0; + if (!strcasecmp(rdrtype, "SR")) { + crdr_data->rdrtype = SR; + } + if (!strcasecmp(rdrtype, "Infinity")) { + crdr_data->rdrtype = Infinity; + } + if (!strcasecmp(rdrtype, "SRv2")) { + crdr_data->tripledelay = 0; + crdr_data->rdrtype = SRv2; + } + if (!strcasecmp(rdrtype, "TripleP1")) { + crdr_data->tripledelay = 0; + crdr_data->rdrtype = TripleP1; + } + if (!strcasecmp(rdrtype, "TripleP2")) { + crdr_data->tripledelay = 100; + crdr_data->rdrtype = TripleP2; + } + if (!strcasecmp(rdrtype, "TripleP3")) { + crdr_data->tripledelay = 150; + crdr_data->rdrtype = TripleP3; + } + + rdr_log_dbg(reader, D_DEVICE, "SR: Looking for device %s on bus %s", dev, busname); + cs_writelock(__func__, &sr_lock); + smartreader_init(reader); + + if(!init_count) + { + ret = libusb_init(NULL); + if(ret < 0) + { + cs_writeunlock(__func__, &sr_lock); + rdr_log(reader, "Libusb init error : %d", ret); + return ret; + } + } + init_count++; + current_count++; + + rdr_log_dbg(reader, D_IFD, "Using 0x%02X/0x%02X as endpoint for smartreader hardware detection", crdr_data->in_ep, crdr_data->out_ep); + if (crdr_data->tripledelay > 0) { + cs_writeunlock(__func__, &sr_lock); + cs_sleepms(crdr_data->tripledelay); + cs_writelock(__func__, &sr_lock); + } + crdr_data->usb_dev = find_smartreader(reader, busname, dev, crdr_data->in_ep, crdr_data->out_ep); + if(!crdr_data->usb_dev) + { + --init_count; + --current_count; + if(!init_count) + { libusb_exit(NULL); } + cs_writeunlock(__func__, &sr_lock); + return ERROR; + } + + rdr_log_dbg(reader, D_DEVICE, "SR: Opening smartreader device %s on bus %s endpoint in 0x%02X out 0x%02X", dev, busname, crdr_data->in_ep, crdr_data->out_ep); + + if((ret = smartreader_usb_open_dev(reader))) + { + --init_count; + --current_count; + if(!init_count) + { libusb_exit(NULL); } + cs_writeunlock(__func__, &sr_lock); + rdr_log(reader, "Unable to open smartreader device %s in bus %s endpoint in 0x%02X out 0x%02X (ret=%d)\n", dev, busname, crdr_data->in_ep, crdr_data->out_ep, ret); + return ERROR; + } + + if (crdr_data->rdrtype >= 2) { + //Set the FTDI latency timer to 16 ms is ftdi default latency. + ret = smartreader_set_latency_timer(reader, 16); + rdr_log_dbg(reader, D_DEVICE, "SR: Setting smartreader latency timer to %d ms", ret); + } else { + //Set the FTDI latency timer to 1 ms . + ret = smartreader_set_latency_timer(reader, 1); + rdr_log_dbg(reader, D_DEVICE, "SR: Setting smartreader latency timer to %d ms", ret); + } + + //Set databits to 8o2 + ret = smartreader_set_line_property(reader, BITS_8, STOP_BIT_2, ODD); + if(ret != 0) + { + rdr_log_dbg(reader, D_IFD, "smartreader_set_line_property ok"); + } else { + rdr_log_dbg(reader, D_IFD, "smartreader_set_line_property failed"); + } + + //Set the DTR LOW and RTS LOW + ret = smartreader_setdtr_rts(reader, 0, 0); + if(ret != 0) + { + rdr_log_dbg(reader, D_IFD, "smartreader_setdtr_rts ok"); + } else { + rdr_log_dbg(reader, D_IFD, "smartreader_setdtr_rts failed"); + } + + //Disable flow control + ret = smartreader_setflowctrl(reader, 0); + if(ret != 0) + { + rdr_log_dbg(reader, D_IFD, "smartreader_setflowctrl ok"); + } else { + rdr_log_dbg(reader, D_IFD, "smartreader_setflowctrl failed"); + } + + // start the reading thread + crdr_data->g_read_buffer_size = 0; + crdr_data->modem_status = 0 ; + cs_pthread_cond_init(__func__, &crdr_data->g_read_mutex, &crdr_data->g_read_cond); + cs_pthread_cond_init(__func__, &crdr_data->g_usb_mutex, &crdr_data->g_usb_cond); + + cs_writeunlock(__func__, &sr_lock); + ret = start_thread("smartreader", ReaderThread, (void *)(reader), &crdr_data->rt, 0, 0); + if(ret) + { + --init_count; + --current_count; + return ERROR; + } + + reader->handle_nr = (long)crdr_data->usb_dev_handle + 1; + + return OK; +} + +static int32_t SR_Reset(struct s_reader *reader, ATR *atr) +{ + struct sr_data *crdr_data = reader->crdr_data; + unsigned char data[ATR_MAX_SIZE]; + int32_t ret; + int32_t atr_ok; + uint32_t baud_temp2; + int32_t i; + int32_t parity[4] = {EVEN, ODD, NONE, EVEN}; // the last EVEN is to try with different F, D values for irdeto card. + static const char *const parity_str[5] = {"NONE", "ODD", "EVEN", "MARK", "SPACE"}; + +// seems to be ok after all + if (reader->cardmhz == reader->mhz && reader->cardmhz > 369) + crdr_data->fs = reader->cardmhz * 10000; else + crdr_data->fs = 3690000; + + rdr_log_dbg(reader, D_IFD, " init card at %u mhz", crdr_data->fs / 10000); + + smart_fastpoll(reader, 1); + // set smartreader+ default values + crdr_data->F = 372; + crdr_data->D = 1; + crdr_data->N = 0; + crdr_data->T = 1; + crdr_data->inv = 0; + baud_temp2 = (double)(crdr_data->D * crdr_data->fs / (double)crdr_data->F); +// rdr_log(reader,"CARD INIT BAUDRATE = %u", baud_temp2); + + for(i = 0 ; i < 4 ; i++) + { + crdr_data->irdeto = 0; + atr_ok = ERROR; + memset(data, 0, sizeof(data)); + rdr_log_dbg(reader, D_IFD, "SR: Trying with parity %s", parity_str[parity[i]]); + + // special irdeto case + if(i == 3) + { + rdr_log_dbg(reader, D_DEVICE, "SR: Trying irdeto"); + crdr_data->F = 618; // why 618 needs to be used instead off 558 ? but magic it is + crdr_data->D = 1; + crdr_data->T = 2; // will be set to T=1 in EnableSmartReader + crdr_data->fs = 6000000; + baud_temp2 = (double)(crdr_data->D * crdr_data->fs / (double)crdr_data->F); + } + + smart_flush(reader); + EnableSmartReader(reader, baud_temp2, crdr_data->fs / 10000, crdr_data->F, (unsigned char)crdr_data->D, crdr_data->N, crdr_data->T, crdr_data->inv, parity[i]); + + //Reset smartcard + + //Set the DTR HIGH and RTS HIGH + smartreader_setdtr_rts(reader, 1, 1); + + // A card with an active low reset is reset by maintaining RST in state L for at least 40 000 clock cycles + // so if we have a base freq of 3.5712MHz : 40000/3690000 = .0112007168458781 seconds, aka 11ms + // so if we have a base freq of 6.00MHz : 40000/6000000 = .0066666666666666 seconds, aka 6ms + cs_sleepms(25); + + //Set the DTR HIGH and RTS LOW + + smartreader_setdtr_rts(reader, 1, 0); + + + //Read the ATR + ret = smart_read(reader, data, ATR_MAX_SIZE, (800)); // timeouts are in ms by smartreader + rdr_log_dbg(reader, D_DEVICE, "SR: get ATR ret = %d" , ret); + if(ret) + { rdr_log_dump_dbg(reader, D_DEVICE, data, ATR_MAX_SIZE * 2, "SR:"); } + + // this is to make sure we don't think this 03 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 is a valid ATR. + if((data[0] != 0x3B && data[0] != 0x03 && data[0] != 0x3F) || (data[1] == 0xFF && data[2] == 0x00)) + { + crdr_data->irdeto = 0; + continue; // this is not a valid ATR. + } + + if(data[0] == 0x03) + { + rdr_log_dbg(reader, D_DEVICE, "SR: Inverse convention detected, setting smartreader inv to 1"); + + crdr_data->inv = 1; + EnableSmartReader(reader, baud_temp2, crdr_data->fs / 10000, crdr_data->F, (unsigned char)crdr_data->D, crdr_data->N, crdr_data->T, crdr_data->inv, parity[i]); + } + // parse atr + if(ATR_InitFromArray(atr, data, ret) != ERROR) + { + rdr_log_dbg(reader, D_DEVICE, "SR: ATR parsing OK"); + atr_ok = OK; + if(i == 3) + { + crdr_data->irdeto = 1; + rdr_log_dbg(reader, D_IFD, "SR: Locking F and D for Irdeto mode irdeto = %u", crdr_data->irdeto = 1); + } + } + + if(atr_ok == OK) {break;} + } + smart_fastpoll(reader, 0); + + return atr_ok; +} + +static int32_t SR_Transmit(struct s_reader *reader, unsigned char *buffer, uint32_t size, uint32_t UNUSED(expectedlen), uint32_t delay, uint32_t timeout) // delay and timeout not used (yet)! +{ + (void) delay; // delay not used (yet)! + (void) timeout; // timeout not used (yet)! + uint32_t ret; + + smart_fastpoll(reader, 1); + ret = smart_write(reader, buffer, size); + smart_fastpoll(reader, 0); + if(ret != size) + { return ERROR; } + + return OK; +} + +static int32_t SR_GetStatus(struct s_reader *reader, int32_t *in) +{ + struct sr_data *crdr_data = reader->crdr_data; + if (crdr_data->rdrtype >= 3) + { + char usb_val[2]; + uint32_t state2; + + if (crdr_data->usb_dev == NULL) + { + rdr_log(reader,"usb device unavailable"); + return ERROR; + } + if (crdr_data->detectstart == 0) + { + *in = 1; + return OK; + } + else + { + if (((crdr_data->detectstart == 1) && (reader->card_status != 1)) && ((crdr_data->detectstart == 1) && (reader->card_status != 0))) + { + cs_writelock(__func__, &sr_lock); + if (libusb_control_transfer(crdr_data->usb_dev_handle, + FTDI_DEVICE_IN_REQTYPE, + SIO_POLL_MODEM_STATUS_REQUEST, + 2, crdr_data->index, + (unsigned char *)usb_val, + 2, crdr_data->usb_read_timeout) != 1) + { + rdr_log(reader, "getting modem status failed "); + cs_writeunlock(__func__, &sr_lock); + return ERROR; + } + cs_writeunlock(__func__, &sr_lock); + state2 = (usb_val[0] & 0xFF); + rdr_log_dbg(reader, D_IFD, "the status of card in or out %u ( 64 means card IN)", state2); + if (state2 == 64) + { + *in = 1; //Card is activated + } + else + { + *in = 0; //NOCARD reader will be set to off + } + return OK; + } + else + { + *in = 1; + rdr_log(reader,"CARD STILL IN AKTIVATION PROCESS NO DETECTION"); + return OK; + } + } + } + else + { + int32_t state; + + smart_fastpoll(reader, 1); + SAFE_MUTEX_LOCK(&crdr_data->g_read_mutex); + state = (crdr_data->modem_status & 0x80) == 0x80 ? 0 : 2; + SAFE_MUTEX_UNLOCK(&crdr_data->g_read_mutex); + smart_fastpoll(reader, 0); + rdr_log_dbg(reader, D_IFD, "the status of card in or out old procedure for v1 %u ", state); + //state = 0 no card, 1 = not ready, 2 = ready + if(state) + { *in = 1; } //CARD, even if not ready report card is in, or it will never get activated + else + { *in = 0; } //NOCARD + + return OK; + } + } + +static int32_t SR_Receive(struct s_reader *reader, unsigned char *buffer, uint32_t size, uint32_t delay, uint32_t timeout) +{ + (void) delay; // delay not used (yet)! + uint32_t ret; + double timeout2; + smart_fastpoll(reader, 1); + if(reader->smart_type >= 2) + { + timeout2 = ((double)timeout/1000) * 1.09; +// rdr_log(reader," TEMPO test read timeout adapted for triple to %4.2f", timeout2); + } + else + { + timeout2 = (double)timeout/1000; + } + // Limit the max timeout to 14 seconds to avoid a device read timeout. + timeout2 = MIN(timeout2, 14000); // convert timeout to ms precize + if (timeout2 < (double)timeout/1000) + { + rdr_log_dbg(reader, D_IFD, "the max timeout has been limited to 14000 ms the calculated is %4.2f", (double)timeout/1000); + } + ret = smart_read(reader, buffer, size, (double)timeout2); + smart_fastpoll(reader, 0); + if(ret != size) + { return ERROR; } + + return OK; +} + +int32_t SR_WriteSettings(struct s_reader *reader, uint16_t F, unsigned char D, uint32_t N, unsigned char T, uint16_t convention) +{ + // smartreader supports 3.20, 3.43, 3.69, 4.00, 4.36, 4.80, 5.34, 6.00, 6.86, 8.00, 9.61, 12.0, 16.0 MHz + struct sr_data *crdr_data = reader->crdr_data; + crdr_data->inv = convention;//FIXME this one is set by icc_async and local smartreader reset routine + static const char *const parity_str[5] = {"NONE", "ODD", "EVEN", "MARK", "SPACE"}; + rdr_log_dbg(reader, D_IFD, "autospeed = %u", reader->autospeed); + rdr_log(reader, "Effective reader settings mhz =%u F= %u D= %u N=%u T=%u inv=%u parity=%s", reader->mhz, F, D, N, T, crdr_data->inv, parity_str[crdr_data->parity]); + smart_fastpoll(reader, 1); + uint32_t baud_temp2 = 3000000; //set to max device speed compatible with usb 1.1 card sets the baudrate. + smart_flush(reader); + EnableSmartReader(reader, baud_temp2, reader->mhz, F, D, N, T, crdr_data->inv, crdr_data->parity); + smart_fastpoll(reader, 0); + + return OK; +} + +static int32_t SR_SetParity(struct s_reader *reader, uint8_t parity) +{ + struct sr_data *crdr_data = reader->crdr_data; + int32_t ret; + + static const char *const parity_str[5] = {"NONE", "ODD", "EVEN", "MARK", "SPACE"}; + rdr_log_dbg(reader, D_DEVICE, "SR: Setting parity to %s", parity_str[parity]); + + crdr_data->parity = parity; + smart_fastpoll(reader, 1); + ret = smartreader_set_line_property(reader, (enum smartreader_bits_type) 8, STOP_BIT_2, parity); + smart_fastpoll(reader, 0); + if(ret) + { return ERROR; } + + return OK; +} + +static int32_t SR_Close(struct s_reader *reader) +{ + struct sr_data *crdr_data = reader->crdr_data; + if(!crdr_data) { return OK; } + crdr_data->running = 0; + if(crdr_data->usb_dev_handle) + { + crdr_data->closing = 1; + if (init_count >= 2) + { + init_count--; + smart_fastpoll(reader, 1); + cs_writeunlock(__func__, &sr_lock); + SAFE_THREAD_JOIN(crdr_data->rt, NULL); + smart_fastpoll(reader, 0); + } + else + { + init_count--; + } + reader->seca_nagra_card = 0; + cs_writelock(__func__, &sr_lock); + libusb_release_interface(crdr_data->usb_dev_handle, crdr_data->interface); +#if defined(__linux__) +// libusb_attach_kernel_driver(crdr_data->usb_dev_handle, crdr_data->interface); // attaching kernel drive +#endif + libusb_close(crdr_data->usb_dev_handle); + crdr_data->usb_dev_handle = NULL; + cs_writeunlock(__func__, &sr_lock); + crdr_data->closing = 0; + NULLFREE(reader->crdr_data); //clearing allocated mem + NULLFREE(reader->csystem_data); //clearing allocated mem + current_count--; // this reader may be restarted now + if(!current_count) + { + libusb_exit(NULL); + } + } + + init_lock = 0; + reader->handle_nr = 0; + rdr_log(reader,"SR: smartreader closed"); + + return OK; +} + +/*static int32_t SR_FastReset(struct s_reader *reader, int32_t delay) +{ + unsigned char data[ATR_MAX_SIZE]; + + smart_fastpoll(reader, 1); + //Set the DTR HIGH and RTS HIGH + smartreader_setdtr_rts(reader, 1, 1); + // A card with an active low reset is reset by maintaining RST in state L for at least 40 000 clock cycles + // so if we have a base freq of 3.5712MHz : 40000/3690000 = .0112007168458781 seconds, aka 11ms + // so if we have a base freq of 6.00MHz : 40000/6000000 = .0066666666666666 seconds, aka 6ms + cs_sleepms(delay); + + //Set the DTR HIGH and RTS LOW + smartreader_setdtr_rts(reader, 1, 0); + + //Read the ATR + smart_read(reader,data, ATR_MAX_SIZE,1000); + smart_fastpoll(reader, 0); + return 0; +} */ + +static int32_t SR_FastReset_With_ATR(struct s_reader *reader, ATR *atr) +{ + unsigned char data[ATR_MAX_SIZE]; + int32_t ret = 0; + int32_t atr_ok = ERROR; + int8_t atr_len = 0; + if(reader->seca_nagra_card == 1) + { + atr_len = reader->card_atr_length; // this is a special case the data buffer has only the atr length. + } + else + { + atr_len = reader->card_atr_length + 2; // data buffer has atr length + 2 bytes + } + + smart_fastpoll(reader, 1); + //Set the DTR HIGH and RTS HIGH + smartreader_setdtr_rts(reader, 1, 1); + // A card with an active low reset is reset by maintaining RST in state L for at least 40 000 clock cycles + // so if we have a base freq of 3.5712MHz : 40000/3690000 = .0112007168458781 seconds, aka 11ms + // so if we have a base freq of 6.00MHz : 40000/6000000 = .0066666666666666 seconds, aka 6ms + cs_sleepms(25); + + //Set the DTR HIGH and RTS LOW + smartreader_setdtr_rts(reader, 1, 0); + + //Read the ATR + ret = smart_read(reader, data, atr_len , (800)); // timeouts are in ms by smartreader. + + // parse atr + if (ATR_InitFromArray(atr, data, ret) != ERROR) + { + rdr_log_dbg(reader, D_DEVICE, "SR: ATR parsing OK"); + atr_ok = OK; + } + + smart_fastpoll(reader, 0); + return atr_ok; +} + +int32_t SR_Activate(struct s_reader *reader, struct s_ATR *atr) +{ + if(!reader->ins7e11_fast_reset) + { + call(SR_Reset(reader, atr)); + } + else + { + call(SR_FastReset_With_ATR(reader, atr)); + } + return OK; +} + +int32_t sr_write_settings(struct s_reader *reader, struct s_cardreader_settings *s) +{ + SR_WriteSettings(reader, s->Fi, s->D, s->EGT, (unsigned char)reader->protocol_type, reader->convention); + return OK; +} + +static int32_t sr_init_locks(struct s_reader *UNUSED(reader)) +{ + if (!init_lock) { + init_lock = 1; + cs_lock_create(__func__, &sr_lock, "sr_lock", 5000); + } + + return OK; +} + +const struct s_cardreader cardreader_smartreader = +{ + .desc = "smartreader", + .typ = R_SMART, + .reader_init = SR_Init, + .get_status = SR_GetStatus, + .set_parity = SR_SetParity, + .activate = SR_Activate, + .transmit = SR_Transmit, + .receive = SR_Receive, + .close = SR_Close, + .write_settings = sr_write_settings, + .lock_init = sr_init_locks, +}; + +#endif diff --git a/csctapi/ifd_smartreader_types.h b/csctapi/ifd_smartreader_types.h new file mode 100644 index 0000000..800b70e --- /dev/null +++ b/csctapi/ifd_smartreader_types.h @@ -0,0 +1,107 @@ +// FTDI defines +#ifndef __SMARTREADER_TYPES_H__ +#define __SMARTREADER_TYPES_H__ +/* Definitions for flow control */ +#define SIO_RESET 0 /* Reset the port */ +#define SIO_MODEM_CTRL 1 /* Set the modem control register */ +#define SIO_SET_FLOW_CTRL 2 /* Set flow control register */ +#define SIO_SET_BAUD_RATE 3 /* Set baud rate */ +#define SIO_SET_DATA 4 /* Set the data characteristics of the port */ + +#define FTDI_DEVICE_OUT_REQTYPE (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT) +#define FTDI_DEVICE_IN_REQTYPE (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN) +/* Requests */ +#define SIO_RESET_REQUEST SIO_RESET +#define SIO_SET_BAUDRATE_REQUEST SIO_SET_BAUD_RATE +#define SIO_SET_DATA_REQUEST SIO_SET_DATA +#define SIO_SET_FLOW_CTRL_REQUEST SIO_SET_FLOW_CTRL +#define SIO_SET_MODEM_CTRL_REQUEST SIO_MODEM_CTRL +#define SIO_POLL_MODEM_STATUS_REQUEST 0x05 +#define SIO_SET_EVENT_CHAR_REQUEST 0x06 +#define SIO_SET_ERROR_CHAR_REQUEST 0x07 +#define SIO_SET_LATENCY_TIMER_REQUEST 0x09 +#define SIO_GET_LATENCY_TIMER_REQUEST 0x0A +#define SIO_SET_BITMODE_REQUEST 0x0B +#define SIO_READ_PINS_REQUEST 0x0C +#define SIO_READ_EEPROM_REQUEST 0x90 +#define SIO_WRITE_EEPROM_REQUEST 0x91 +#define SIO_ERASE_EEPROM_REQUEST 0x92 +#define CLK_BITS 0x8e +#define CLK_BYTES 0x8f +#define CLK_WAIT_HIGH 0x94 +#define CLK_WAIT_LOW 0x95 +#define EN_ADAPTIVE 0x96 +#define DIS_ADAPTIVE 0x97 +#define CLK_BYTES_OR_HIGH 0x9c +#define CLK_BYTES_OR_LOW 0x0d + +#define SIO_RESET_SIO 0 +#define SIO_RESET_PURGE_RX 1 +#define SIO_RESET_PURGE_TX 2 + +#define SIO_DISABLE_FLOW_CTRL 0x0 +#define SIO_RTS_CTS_HS (0x1 << 8) +#define SIO_DTR_DSR_HS (0x2 << 8) +#define SIO_XON_XOFF_HS (0x4 << 8) + +#define SIO_SET_DTR_MASK 0x1 +#define SIO_SET_DTR_HIGH ( 1 | ( SIO_SET_DTR_MASK << 8)) +#define SIO_SET_DTR_LOW ( 0 | ( SIO_SET_DTR_MASK << 8)) +#define SIO_SET_RTS_MASK 0x2 +#define SIO_SET_RTS_HIGH ( 2 | ( SIO_SET_RTS_MASK << 8 )) +#define SIO_SET_RTS_LOW ( 0 | ( SIO_SET_RTS_MASK << 8 )) + +#define SIO_RTS_CTS_HS (0x1 << 8) +/** FTDI chip type */ +enum smartreader_chip_type { TYPE_AM = 0, TYPE_BM = 1, TYPE_2232C = 2, TYPE_R = 3, TYPE_2232H = 4, TYPE_4232H = 5, TYPE_232H = 6 }; +/** Parity mode for smartreader_set_line_property() */ +enum smartreader_parity_type { NONE = 0, ODD = 1, EVEN = 2, MARK = 3, SPACE = 4 }; +/** Number of stop bits for smartreader_set_line_property() */ +enum smartreader_stopbits_type { STOP_BIT_1 = 0, STOP_BIT_15 = 1, STOP_BIT_2 = 2 }; +/** Number of bits for smartreader_set_line_property() */ +enum smartreader_bits_type { BITS_7 = 7, BITS_8 = 8 }; +/** Break type for smartreader_set_line_property2() */ +enum smartreader_break_type { BREAK_OFF = 0, BREAK_ON = 1 }; + +/** Port interface for chips with multiple interfaces */ + +enum smartreader_rdrtypename +{ + SR = 0, + Infinity = 1, + SRv2 = 2, + TripleP1 = 3, + TripleP2 = 4, + TripleP3 = 5 +}; + +enum smartreader_interface +{ + INTERFACE_ANY = 0, + INTERFACE_A = 1, + INTERFACE_B = 2, + INTERFACE_C = 3, + INTERFACE_D = 4 +}; + +struct s_reader_types +{ + uint16_t rdrtypename; + uint8_t in_ep; + uint8_t out_ep; + int32_t index; + int32_t interface; +}; + +const struct s_reader_types reader_types[] = +{ + { SR, 0x01, 0x82, INTERFACE_A, 0}, // type 0 + { Infinity, 0x01, 0x81, INTERFACE_A, 0}, // type 1 + { SRv2, 0x02, 0x81, INTERFACE_A, 0}, // type 2 + { TripleP1, 0x02, 0x81, INTERFACE_A, 0}, // type 3 + { TripleP2, 0x04, 0x83, INTERFACE_B, 1}, // type 4 + { TripleP3, 0x06, 0x85, INTERFACE_C, 2} // type 5 +}; + +#endif // __SMARTREADER_TYPES_H__ +// end of FTDI defines diff --git a/csctapi/ifd_stapi.c b/csctapi/ifd_stapi.c new file mode 100644 index 0000000..29f4b61 --- /dev/null +++ b/csctapi/ifd_stapi.c @@ -0,0 +1,104 @@ +#include "../globals.h" + +#if defined(CARDREADER_STAPI) || defined(CARDREADER_STAPI5) +#include "atr.h" +#include "../oscam-string.h" + +/* These functions are implemented in liboscam_stapi.a */ +extern int32_t STReader_Open(char *device, uint32_t *stsmart_handle); +extern int32_t STReader_GetStatus(uint32_t stsmart_handle, int32_t *in); +extern int32_t STReader_Reset(uint32_t stsmart_handle, ATR *atr); +extern int32_t STReader_Transmit(uint32_t stsmart_handle, unsigned char *sent, uint32_t size); +extern int32_t STReader_Receive(uint32_t stsmart_handle, unsigned char *data, uint32_t size); +extern int32_t STReader_Close(uint32_t stsmart_handle); +extern int32_t STReader_SetProtocol(uint32_t stsmart_handle, unsigned char *params, unsigned *length, uint32_t len_request); +extern int32_t STReader_SetClockrate(uint32_t stsmart_handle); + +#ifdef CARDREADER_STAPI5 +/* These functions are implemented in liboscam_stapi5.a */ +extern char *STReader_GetRevision(void); +#endif + +#define OK 0 +#define ERROR 1 + +struct stapi_data +{ + uint32_t stapi_handle; +}; + +static int32_t stapi_init(struct s_reader *reader) +{ + if(!cs_malloc(&reader->crdr_data, sizeof(struct stapi_data))) + { return ERROR; } + struct stapi_data *crdr_data = reader->crdr_data; + +#ifdef CARDREADER_STAPI5 + STReader_GetRevision(); +#endif + + return STReader_Open(reader->device, &crdr_data->stapi_handle); +} + +static int32_t stapi_getstatus(struct s_reader *reader, int32_t *in) +{ + struct stapi_data *crdr_data = reader->crdr_data; + return STReader_GetStatus(crdr_data->stapi_handle, in); +} + +static int32_t stapi_reset(struct s_reader *reader, ATR *atr) +{ + struct stapi_data *crdr_data = reader->crdr_data; + return STReader_Reset(crdr_data->stapi_handle, atr); +} + +static int32_t stapi_transmit(struct s_reader *reader, unsigned char *sent, uint32_t size, uint32_t UNUSED(expectedlen), uint32_t delay, uint32_t timeout) // delay + timeout not in use (yet)! +{ + (void) delay; // delay not in use (yet)! + (void) timeout; // timeout not in use (yet)! + struct stapi_data *crdr_data = reader->crdr_data; + return STReader_Transmit(crdr_data->stapi_handle, sent, size); +} + +static int32_t stapi_receive(struct s_reader *reader, unsigned char *data, uint32_t size, uint32_t delay, uint32_t timeout) // delay + timeout not in use (yet)! +{ + (void) delay; // delay not in use (yet)! + (void) timeout; // timeout not in use (yet)! + struct stapi_data *crdr_data = reader->crdr_data; + return STReader_Receive(crdr_data->stapi_handle, data, size); +} + +static int32_t stapi_close(struct s_reader *reader) +{ + struct stapi_data *crdr_data = reader->crdr_data; + return STReader_Close(crdr_data->stapi_handle); +} + +static int32_t stapi_setprotocol(struct s_reader *reader, unsigned char *params, unsigned *length, uint32_t len_request) +{ + struct stapi_data *crdr_data = reader->crdr_data; + return STReader_SetProtocol(crdr_data->stapi_handle, params, length, len_request); +} + +static int32_t stapi_writesettings(struct s_reader *reader, struct s_cardreader_settings *s) +{ + (void)s; + struct stapi_data *crdr_data = reader->crdr_data; + return STReader_SetClockrate(crdr_data->stapi_handle); +} + +const struct s_cardreader cardreader_stapi = +{ + .desc = "stapi", + .typ = R_INTERNAL, + .reader_init = stapi_init, + .get_status = stapi_getstatus, + .activate = stapi_reset, + .transmit = stapi_transmit, + .receive = stapi_receive, + .close = stapi_close, + .set_protocol = stapi_setprotocol, + .write_settings = stapi_writesettings, +}; + +#endif diff --git a/csctapi/ifd_stinger.c b/csctapi/ifd_stinger.c new file mode 100644 index 0000000..a4883f5 --- /dev/null +++ b/csctapi/ifd_stinger.c @@ -0,0 +1,577 @@ +/* + ifd_stinger.c + This module provides IFD handling functions for Stinger USB. +Usage: + +Stinger Dedicated parameters. +cardmhz = real card clock (see smartcard docs) +mhz = stinger card clock (343, 400, 480, 600, 800, 1200) +detect = Inverted CTS + +Ex. +[reader] +label = stinger_UP +protocol = stinger +detect = !cts +device = /dev/ttyUSB4 +group = 2 +cardmhz = 357 +mhz = 600 +emmcache = 1,3,2 +services = services2 +... + +*/ + +#include "../globals.h" + +#ifdef CARDREADER_STINGER + +#include "../oscam-time.h" +#include "icc_async.h" +#include "ifd_db2com.h" +#include "ifd_phoenix.h" +#include "io_serial.h" + +#define OK 0 +#define ERROR 1 +#define STINGER_DELAY 150 + +static int32_t Stinger_Set_Clock(struct s_reader *reader, unsigned int c); + +static bool Stinger_IO_Serial_WaitToWrite(struct s_reader *reader, uint32_t delay_us, uint32_t timeout_us) +{ + struct pollfd ufds; + struct timeb start, end; + int32_t ret_val; + int32_t out_fd; + int64_t polltimeout = timeout_us / 1000; + +#if !defined(WITH_COOLAPI) && !defined(WITH_COOLAPI2) && !defined(WITH_AZBOX) + + if(reader->typ == R_INTERNAL) { return OK; } // needed for internal readers, otherwise error! +#endif + if(delay_us > 0) + { cs_sleepus(delay_us); } // wait in us + out_fd = reader->handle; + + ufds.fd = out_fd; + ufds.events = POLLOUT; + ufds.revents = 0x0000; + cs_ftime(&start); // register start time + while(1) + { + ret_val = poll(&ufds, 1, polltimeout); + cs_ftime(&end); // register end time + switch(ret_val) + { + case 0: + rdr_log(reader, "ERROR: not ready to write, timeout=%"PRId64" ms", comp_timeb(&end, &start)); + return ERROR; + case -1: + if(errno == EINTR || errno == EAGAIN) + { + cs_sleepus(1); + if(timeout_us > 0) + { + polltimeout = (timeout_us / 1000) - comp_timeb(&end, &start); + if(polltimeout < 0) { polltimeout = 0; } + } + continue; + } + rdr_log(reader, "ERROR: %s: timeout=%"PRId64" ms (errno=%d %s)", __func__, comp_timeb(&end, &start), errno, strerror(errno)); + return ERROR; + default: + if(((ufds.revents) & POLLOUT) == POLLOUT) + { return OK; } + else + { return ERROR; } + } + } +} + + +bool Stinger_IO_Serial_Write(struct s_reader *reader, uint32_t delay, uint32_t timeout, uint32_t size, const unsigned char *data) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + if(timeout == 0) // General fix for readers not communicating timeout and delay + { + if(reader->char_delay != 0) { timeout = reader->char_delay; } + else { timeout = 1000000; } + rdr_log_dbg(reader, D_DEVICE, "Warning: write timeout 0 changed to %d us", timeout); + } + uint32_t count, to_send, i_w; + unsigned char data_w[MAX_ECM_SIZE]; + + to_send = (delay ? 1 : size); // calculate chars to send at one + rdr_log_dbg(reader, D_DEVICE, "Write timeout %d us, write delay %d us, to send %d char(s), chunksize %d char(s)", timeout, delay, size, to_send); + + for(count = 0; count < size; count += to_send) + { + if(count + to_send > size) + { + to_send = size - count; + } + uint16_t errorcount = 0, to_do = to_send; + for(i_w = 0; i_w < to_send; i_w++) + { data_w [i_w] = data [count + i_w]; } + rdr_log_dump_dbg(reader, D_DEVICE, data_w + (to_send - to_do), to_do, "Sending:"); +AGAIN: + if(!Stinger_IO_Serial_WaitToWrite(reader, delay, timeout)) + { + while(to_do != 0) + { + int32_t u = write(reader->handle, data_w + (to_send - to_do), to_do); + if(u < 1) + { + if(errno == EINTR) { continue; } //try again in case of Interrupted system call + if(errno == EAGAIN) { goto AGAIN; } //EAGAIN needs a select procedure again + errorcount++; + int16_t written = count + to_send - to_do; + if(u != 0) + { + rdr_log(reader, "ERROR: %s: Written=%d of %d (errno=%d %s)", + __func__, written , size, errno, strerror(errno)); + } + if(errorcount > 10) //exit if more than 10 errors + { + return ERROR; + } + } + else + { + to_do -= u; + errorcount = 0; + } + } + } + else + { + rdr_log(reader, "Timeout in Stinger_IO_Serial_WaitToWrite, delay=%d us, timeout=%d us", delay, timeout); + if(crdr_ops->read_written && reader->written > 0) // these readers need to read all transmitted chars before they can receive! + { + unsigned char buf[256]; + rdr_log_dbg(reader, D_DEVICE, "Reading %d echoed transmitted chars...", reader->written); + int32_t n = reader->written; + if(IO_Serial_Read(reader, 0, 9990000, n, buf)) // use 9990000 = aprox 10 seconds (since written chars could be hughe!) + { return ERROR; } + reader->written = 0; + rdr_log_dbg(reader, D_DEVICE, "Reading of echoed transmitted chars done!"); + } + return ERROR; + } + } + + return OK; +} + + + +static int32_t Stinger_Get_Info(struct s_reader *reader, unsigned char *sc, unsigned char *parity, unsigned int *baudrate) +{ + unsigned char buf[64]; + memset(buf, 0, 64); + + //DTR down + IO_Serial_DTR_Set(reader); + buf[0] = 0; + + Stinger_IO_Serial_Write(reader, 0, 10, 1, buf); + int32_t n = 0; + while(n < 32 && !IO_Serial_Read(reader, 0, ATR_TIMEOUT, 1, buf + n)) + { n++; } + + //DTR up + IO_Serial_DTR_Clr(reader); + + *sc = 0xFF; + *baudrate = 0xFF; + *parity = 0xFF; + + + if(n == 0) + { + rdr_log(reader, "Stinger_Get_Info: n %d", n); + return ERROR; + } + if(buf[0] == 0x00) + + { + /* Reader */ + if((buf[1] == 0x01) || (buf[1] == 0x02)) + { + *sc = buf[1] - 1; + } + else + { + rdr_log(reader, "Stinger_Get_Info: buf[1] %d", buf[1]); + return ERROR; + } + /* Baudrate */ + if((buf[25] == 0x00) || (buf[25] == 0x01) || (buf[25] == 0x02)) + { + *baudrate = buf[25]; + } + else + { + rdr_log(reader, "Stinger_Get_Info: buf[25] %d", buf[25]); + return ERROR; + } + /* Parity */ + if((buf[26] == 0x00) || (buf[26] == 0x01) || (buf[26] == 0x02)) + { + *parity = buf[26]; + } + else + { + rdr_log(reader, "Stinger_Get_Info: buf[26] %d", buf[26]); + return ERROR; + } + return OK; + + } + else + { + rdr_log(reader, "Stinger_Get_Info: buf[0] %d", buf[0]); + return ERROR; + } + + +} + +int32_t Stinger_Init(struct s_reader *reader) +{ + // First set card in reset state, to not change any parameters while communication ongoing + IO_Serial_RTS_Set(reader); + + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + if(crdr_ops->flush) { IO_Serial_Flush(reader); } + + rdr_log_dbg(reader, D_IFD, "Initializing reader type=%d", reader->typ); + + /* Default serial port settings */ + if(reader->atr[0] == 0) + { + if(IO_Serial_SetParams(reader, DEFAULT_BAUDRATE, 8, PARITY_NONE, 2, NULL, NULL)) { return ERROR; } + if(crdr_ops->flush) { IO_Serial_Flush(reader); } + } + + return OK; +} + + +int32_t Stinger_Reset(struct s_reader *reader, ATR *atr) +{ + rdr_log_dbg(reader, D_IFD, "Resetting card"); + int32_t ret; + int32_t i; + unsigned char buf[ATR_MAX_SIZE]; + + IO_Serial_SetParams(reader, DEFAULT_BAUDRATE, 8, PARITY_NONE, 2, NULL, NULL); + + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + for(i = 0; i < 1; i++) + { + if(crdr_ops->flush) { IO_Serial_Flush(reader); } + + + ret = ERROR; + + IO_Serial_Ioctl_Lock(reader, 1); + + IO_Serial_RTS_Set(reader); + + cs_sleepms(50); + + IO_Serial_RTS_Clr(reader); + + cs_sleepms(50); + + IO_Serial_Ioctl_Lock(reader, 0); + + int32_t n = 0; + + while(n < 1 && !IO_Serial_Read(reader, 0, ATR_TIMEOUT, 1, buf + n)) + { n++; } + + while(n < ATR_MAX_SIZE && !IO_Serial_Read(reader, 0, 900000, 1, buf + n)) + { n++; } + + + if(n == 0) + { continue; } + if(ATR_InitFromArray(atr, buf, n) != ERROR) + { ret = OK; } + // Successfully retrieve ATR + if(ret == OK) + { break; } + } + + + if(ret == OK) + { + IO_Serial_SetParams(reader, 115200, 8, PARITY_NONE, 1/*2*/, NULL, NULL); + return OK; + } + else + { return ret; } +} + +static int32_t stinger_mouse_init(struct s_reader *reader) +{ + + + unsigned int clock_mhz = 0; + + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + if(detect_db2com_reader(reader)) + { + reader->crdr = crdr_ops = &cardreader_db2com; + return crdr_ops->reader_init(reader); + } + + clock_mhz = reader->mhz; + reader->mhz = reader->cardmhz; + + reader->handle = open(reader->device, O_RDWR | O_NOCTTY | O_NONBLOCK); + if(reader->handle < 0) + { + rdr_log(reader, "ERROR: Opening device %s (errno=%d %s)", + reader->device, errno, strerror(errno)); + return ERROR; + } + + + if(Stinger_Init(reader)) + { + rdr_log(reader, "ERROR: Stinger_Init returns error"); + Phoenix_Close(reader); + + return ERROR; + } + + + // SET CLOCK + + cs_sleepms(200); + Stinger_Set_Clock(reader, clock_mhz); + + return OK; +} + + + +int32_t Stinger_IO_Serial_SetBaudrate(struct s_reader *reader, uint32_t baudrate) +{ + unsigned char dat[8]; + unsigned char p = 0xFF; + unsigned int b = 0xFF; + unsigned char s = 0xFF; + memset(dat, 0, 8); + rdr_log_dbg(reader, D_IFD, "Setting baudrate to %u", baudrate); + + if(baudrate == 9600) + { + dat[2] = 0; + } + else if(baudrate == 38400) + { + dat[2] = 1; + } + else if(baudrate == 115200) + { + dat[2] = 2; + } + else + { + rdr_log_dbg(reader, D_IFD, "Baudrate value not supported"); + return ERROR; + } + + + Stinger_Get_Info(reader, &s, &p, &b); + + + if((s == 0xFF) || (p == 0xFF) || (b == 0xFF)) + { + rdr_log_dbg(reader, D_IFD, "Stinger_Get_Info error"); + return ERROR; + } + + //Stinger cmds + dat[0] = 0x03; + dat[1] = s; + dat[3] = p; + + + //DTR down + IO_Serial_DTR_Set(reader); + + Stinger_IO_Serial_Write(reader, 0, 10, 4, dat); + + + int32_t n = 0; + while(n < 2 && !IO_Serial_Read(reader, 0, 300000, 1, dat + n)) + { n++; } + + + //DTR up + IO_Serial_DTR_Clr(reader); + + reader->current_baudrate = baudrate; //so if update fails, reader->current_baudrate is not changed either + + + return OK; + +} + +int32_t Stinger_IO_Serial_SetParity(struct s_reader *reader, unsigned char parity) +{ + + //Stinger cmds + unsigned char dat[4]; + unsigned char p = 0xFF; + unsigned int b = 0xFF; + unsigned char s = 0xFF; + + if(parity == PARITY_NONE) + { + dat[3] = 0; + } + else if(parity == PARITY_ODD) + { + dat[3] = 1; + } + else if(parity == PARITY_EVEN) + { + dat[3] = 2; + } + else + { + rdr_log_dbg(reader, D_IFD, "Parity value not supported"); + return ERROR; + } + + + Stinger_Get_Info(reader, &s, &p, &b); + + if((s == 0xFF) || (p == 0xFF) || (b == 0xFF)) + { + rdr_log_dbg(reader, D_IFD, "Stinger_Get_Info error"); + return ERROR; + } + + //Stinger cmd + dat[0] = 0x03; + dat[1] = s; + dat[2] = b; + + + //DTR down + IO_Serial_DTR_Set(reader); + + Stinger_IO_Serial_Write(reader, 0, 10, 4, dat); + + int32_t n = 0; + while(n < 2 && !IO_Serial_Read(reader, 0, 300000, 1, dat + n)) + { n++; } + + //DTR up + IO_Serial_DTR_Clr(reader); + + return OK; +} + + +static int32_t Stinger_Set_Clock(struct s_reader *reader, unsigned int c) +{ + rdr_log_dbg(reader, D_IFD, "Setting Smartcard clock at: %d", c); + unsigned char p = 0xFF; + unsigned int b = 0xFF; + unsigned char s = 0xFF; + unsigned char dat[3]; + + Stinger_Get_Info(reader, &s, &p, &b); + + if((s == 0xFF) || (p == 0xFF) || (b == 0xFF)) + { + rdr_log_dbg(reader, D_IFD, "Stinger_Get_Info error"); + return ERROR; + } + + //Stinger cmd + dat[0] = 0x02; // Set Clock COMMAND + dat[1] = s; + + if(c == 343) + { + dat[2] = 1; + } + else if(c == 400) + { + dat[2] = 2; + } + else if(c == 480) + { + dat[2] = 3; + } + else if(c == 600) + { + dat[2] = 4; + } + else if(c == 800) + { + dat[2] = 5; + } + else if(c == 1200) + { + dat[2] = 6; + } + else + { + rdr_log_dbg(reader, D_IFD, "Clock speed not recognized. Check configuration"); + return ERROR; + } + + //DTR down + IO_Serial_DTR_Set(reader); + + // SEND Command + Stinger_IO_Serial_Write(reader, 0, 10, 3, dat); + + int32_t n = 0; + while(n < 2 && !IO_Serial_Read(reader, 0, 300000, 1, dat + n)) + { n++; } + + //DTR up + IO_Serial_DTR_Clr(reader); + rdr_log_dbg(reader, D_IFD, "Smartcard clock at %d set", c); + return OK; +} + +const struct s_cardreader cardreader_stinger = +{ + .desc = "stinger", + .typ = R_MOUSE, + .flush = 1, + .read_written = 1, + .need_inverse = 1, + .reader_init = stinger_mouse_init, + .get_status = Phoenix_GetStatus, + .activate = Stinger_Reset, + .transmit = IO_Serial_Transmit, + .receive = IO_Serial_Receive, + .close = Phoenix_Close, + .set_parity = Stinger_IO_Serial_SetParity, + .set_baudrate = Stinger_IO_Serial_SetBaudrate, +}; + +#endif diff --git a/csctapi/io_serial.c b/csctapi/io_serial.c new file mode 100644 index 0000000..a929497 --- /dev/null +++ b/csctapi/io_serial.c @@ -0,0 +1,887 @@ +/* + io_serial.c + Serial port input/output functions + + This file is part of the Unix driver for Towitoko smartcard readers + Copyright (C) 2000 2001 Carlos Prados + + This version is modified by doz21 to work in a special manner ;) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "../globals.h" +#ifdef WITH_CARDREADER + +#if defined(__HPUX__) +#include +#endif + +#include + +#if defined(__linux__) && !defined(__ANDROID__) +#include +#endif + +#if defined(__ANDROID__) +#include "../extapi/linux/serial.h" +#endif + +#include "../oscam-time.h" +#include "icc_async.h" +#include "io_serial.h" + +#if defined(__APPLE__) +#include +#endif + +#define OK 0 +#define ERROR 1 + +#define IO_SERIAL_FILENAME_LENGTH 32 + +/* + * Internal functions declaration + */ + +static int32_t IO_Serial_Bitrate(int32_t bitrate); + +static bool IO_Serial_WaitToWrite(struct s_reader *reader, uint32_t delay_us, uint32_t timeout_us); + +static int32_t oscam_sem; + +void IO_Serial_Ioctl_Lock(struct s_reader *reader, int32_t flag) +{ + if((reader->typ != R_DB2COM1) && (reader->typ != R_DB2COM2)) { return; } + if(!flag) + { oscam_sem = 0; } + else while(oscam_sem != reader->typ) + { + while(oscam_sem) + if(reader->typ == R_DB2COM1) + { cs_sleepms(6); } + else + { cs_sleepms(8); } + oscam_sem = reader->typ; + cs_sleepms(1); + } +} + +bool IO_Serial_DTR_RTS(struct s_reader *reader, int32_t *dtr, int32_t *rts) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + if(crdr_ops->set_DTS_RTS) + { return crdr_ops->set_DTS_RTS(reader, dtr, rts); } + + uint32_t msr; + uint32_t mbit; + + if(dtr) + { + mbit = TIOCM_DTR; +#if defined(TIOCMBIS) && defined(TIOBMBIC) + if(ioctl(reader->handle, *dtr ? TIOCMBIS : TIOCMBIC, &mbit) < 0) + { return ERROR; } +#else + if(ioctl(reader->handle, TIOCMGET, &msr) < 0) + { return ERROR; } + if(*dtr) + { msr |= mbit; } + else + { msr &= ~mbit; } + if(ioctl(reader->handle, TIOCMSET, &msr) < 0) + { return ERROR; } +#endif + rdr_log_dbg(reader, D_DEVICE, "Setting %s=%i", "DTR", *dtr); + } + + if(rts) + { + mbit = TIOCM_RTS; +#if defined(TIOCMBIS) && defined(TIOBMBIC) + if(ioctl(reader->handle, *rts ? TIOCMBIS : TIOCMBIC, &mbit) < 0) + { return ERROR; } +#else + if(ioctl(reader->handle, TIOCMGET, &msr) < 0) + { return ERROR; } + if(*rts) + { msr |= mbit; } + else + { msr &= ~mbit; } + if(ioctl(reader->handle, TIOCMSET, &msr) < 0) + { return ERROR; } +#endif + rdr_log_dbg(reader, D_DEVICE, "Setting %s=%i", "RTS", *rts); + } + + return OK; +} + +/* + * Public functions definition + */ + +bool IO_Serial_SetBitrate(struct s_reader *reader, uint32_t bitrate, struct termios *tio) +{ + /* Set the bitrate */ +#if !defined(__linux__) +#if !defined(__APPLE__) + if(IO_Serial_Bitrate(bitrate) == B0) + { + rdr_log(reader, "Baudrate %u not supported", bitrate); + return ERROR; + } + else + { + //no overclocking + cfsetospeed(tio, IO_Serial_Bitrate(bitrate)); + cfsetispeed(tio, IO_Serial_Bitrate(bitrate)); + rdr_log_dbg(reader, D_DEVICE, "standard baudrate: cardmhz=%d mhz=%d -> effective baudrate %u", + reader->cardmhz, reader->mhz, bitrate); + } +#endif +#endif + + /* Set the bitrate */ +#if defined(__linux__) + //FIXME workaround for Smargo until native mode works + if((reader->mhz == reader->cardmhz) && (reader->smargopatch != 1) && IO_Serial_Bitrate(bitrate) != B0) + { + //no overclocking + cfsetospeed(tio, IO_Serial_Bitrate(bitrate)); + cfsetispeed(tio, IO_Serial_Bitrate(bitrate)); + rdr_log_dbg(reader, D_DEVICE, "standard baudrate: cardmhz=%d mhz=%d -> effective baudrate %u", + reader->cardmhz, reader->mhz, bitrate); + } + else + { + //over or underclocking + /* these structures are only available on linux */ + struct serial_struct nuts; + // This makes valgrind happy, because it doesn't know what TIOCGSERIAL does + // Without this there are lots of misleading errors of type: + // "Conditional jump or move depends on uninitialised value(s)" + nuts.baud_base = 0; + nuts.custom_divisor = 0; + ioctl(reader->handle, TIOCGSERIAL, &nuts); + int32_t custom_baud_asked = bitrate * reader->mhz / reader->cardmhz; + nuts.custom_divisor = (nuts.baud_base + (custom_baud_asked / 2)) / custom_baud_asked; + int32_t custom_baud_delivered = nuts.baud_base / nuts.custom_divisor; + rdr_log_dbg(reader, D_DEVICE, "custom baudrate: cardmhz=%d mhz=%d custom_baud=%d baud_base=%d divisor=%d -> effective baudrate %d", + reader->cardmhz, reader->mhz, custom_baud_asked, nuts.baud_base, nuts.custom_divisor, custom_baud_delivered); + int32_t baud_diff = custom_baud_delivered - custom_baud_asked; + if(baud_diff < 0) + { baud_diff = (-baud_diff); } + if(baud_diff > 0.05 * custom_baud_asked) + { + rdr_log(reader, "WARNING: your card is asking for custom_baudrate = %i, but your configuration can only deliver custom_baudrate = %i", custom_baud_asked, custom_baud_delivered); + rdr_log(reader, "You are over- or underclocking, try OSCam when running your reader at normal clockspeed as required by your card, and setting mhz and cardmhz parameters accordingly."); + if(nuts.baud_base <= 115200) + { rdr_log(reader, "You are probably connecting your reader via a serial port, OSCam has more flexibility switching to custom_baudrates when using an USB->serial converter, preferably based on FTDI chip."); } + } + nuts.flags &= ~ASYNC_SPD_MASK; + nuts.flags |= ASYNC_SPD_CUST; + ioctl(reader->handle, TIOCSSERIAL, &nuts); + cfsetospeed(tio, IO_Serial_Bitrate(38400)); + cfsetispeed(tio, IO_Serial_Bitrate(38400)); + } +#endif + + /* Set the bitrate */ +#if defined(__APPLE__) + if(IO_Serial_Bitrate(bitrate) == B0) + { + int32_t custom_baud_rate = bitrate * reader->mhz / reader->cardmhz; + if(ioctl(reader->handle, IOSSIOSPEED, &custom_baud_rate) < 0) + { + rdr_log(reader, "Baudrate %u not supported", custom_baud_rate); + return ERROR; + } + else + { + rdr_log_dbg(reader, D_DEVICE, "custom baudrate: cardmhz=%d mhz=%d -> effective baudrate %u", + reader->cardmhz, reader->mhz, custom_baud_rate); + } + } + else + { + //no overclocking + cfsetospeed(tio, IO_Serial_Bitrate(bitrate)); + cfsetispeed(tio, IO_Serial_Bitrate(bitrate)); + rdr_log_dbg(reader, D_DEVICE, "standard baudrate: cardmhz=%d mhz=%d -> effective baudrate %u", + reader->cardmhz, reader->mhz, bitrate); + } +#endif + return OK; +} + +bool IO_Serial_SetParams(struct s_reader *reader, uint32_t bitrate, uint32_t bits, int32_t parity, uint32_t stopbits, int32_t *dtr, int32_t *rts) +{ + struct termios newtio; + + if(reader->typ == R_INTERNAL) + { return ERROR; } + + memset(&newtio, 0, sizeof(newtio)); + + if(IO_Serial_SetBitrate(reader, bitrate, & newtio)) + { return ERROR; } + + /* Set the character size */ + switch(bits) + { + case 5: + newtio.c_cflag |= CS5; + break; + + case 6: + newtio.c_cflag |= CS6; + break; + + case 7: + newtio.c_cflag |= CS7; + break; + + case 8: + newtio.c_cflag |= CS8; + break; + } + + /* Set the parity */ + switch(parity) + { + case PARITY_ODD: + newtio.c_cflag |= PARENB; + newtio.c_cflag |= PARODD; + break; + + case PARITY_EVEN: + newtio.c_cflag |= PARENB; + newtio.c_cflag &= ~PARODD; + break; + + case PARITY_NONE: + newtio.c_cflag &= ~PARENB; + break; + } + + /* Set the number of stop bits */ + switch(stopbits) + { + case 1: + newtio.c_cflag &= (~CSTOPB); + break; + case 2: + newtio.c_cflag |= CSTOPB; + break; + } + + /* Selects raw (non-canonical) input and output */ + newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + newtio.c_oflag &= ~OPOST; +#if 1 + newtio.c_iflag |= IGNPAR; + /* Ignore parity errors!!! Windows driver does so why shouldn't I? */ +#endif + /* Enable receiver, hang on close, ignore control line */ + newtio.c_cflag |= CREAD | HUPCL | CLOCAL; + + /* Read 1 byte minimun, no timeout specified */ + newtio.c_cc[VMIN] = 1; + newtio.c_cc[VTIME] = 0; + + if(IO_Serial_SetProperties(reader, newtio)) + { return ERROR; } + + reader->current_baudrate = bitrate; + + IO_Serial_Ioctl_Lock(reader, 1); + IO_Serial_DTR_RTS(reader, dtr, rts); + IO_Serial_Ioctl_Lock(reader, 0); + return OK; +} + +bool IO_Serial_SetProperties(struct s_reader *reader, struct termios newtio) +{ + if(reader->typ == R_INTERNAL) + { return OK; } + + if(tcsetattr(reader->handle, TCSANOW, &newtio) < 0) // set terminal attributes. + { return ERROR; } + + int32_t mctl; + rdr_log_dbg(reader, D_DEVICE, "Getting readerstatus..."); + if(ioctl(reader->handle, TIOCMGET, &mctl) >= 0) // get reader statusbits + { + mctl &= ~TIOCM_RTS; + rdr_log_dbg(reader, D_DEVICE, "Set reader ready to Send"); + ioctl(reader->handle, TIOCMSET, &mctl); // set reader ready to send. + } + else { rdr_log(reader, "WARNING: Cant get readerstatus!"); } + + return OK; +} + +int32_t IO_Serial_SetParity(struct s_reader *reader, unsigned char parity) +{ + struct termios tio; + int32_t current_parity; + // Get current parity + if(tcgetattr(reader->handle, &tio) != 0) + { + rdr_log(reader, "ERROR: Could not get current parity, %s (errno=%d %s)", __func__, errno, strerror(errno)); + current_parity = 5; // set to unknown (5 is not predefined!) + } + else + { + if(((tio.c_cflag) & PARENB) == PARENB) + { + if(((tio.c_cflag) & PARODD) == PARODD) + { current_parity = PARITY_ODD; } + else + { current_parity = PARITY_EVEN; } + } + else + { + current_parity = PARITY_NONE; + } + } + + if(current_parity != parity) + { + rdr_log_dbg(reader, D_IFD, "Setting parity from %s to %s", + current_parity == PARITY_ODD ? "Odd" : + current_parity == PARITY_NONE ? "None" : + current_parity == PARITY_EVEN ? "Even" : "Unknown", + + parity == PARITY_ODD ? "Odd" : + parity == PARITY_NONE ? "None" : + parity == PARITY_EVEN ? "Even" : "Invalid"); + + // Set the parity + switch(parity) + { + case PARITY_ODD: + tio.c_cflag |= PARENB; + tio.c_cflag |= PARODD; + break; + + case PARITY_EVEN: + tio.c_cflag |= PARENB; + tio.c_cflag &= ~PARODD; + break; + + case PARITY_NONE: + tio.c_cflag &= ~PARENB; + break; + } + if(IO_Serial_SetProperties(reader, tio)) + { + rdr_log_dbg(reader, D_IFD, "ERROR: could set parity!"); + return ERROR; + } + } + + return OK; +} + +void IO_Serial_Flush(struct s_reader *reader) +{ + unsigned char b; + uint8_t n = 0; + tcflush(reader->handle, TCIOFLUSH); + struct timeb starttotal, endtotal; + struct timeb start, end; + cs_ftimeus(&starttotal); + endtotal = starttotal; + cs_ftimeus(&start); + end = start; + int64_t gone = 0; + while(!IO_Serial_Read(reader, 0, 75000, 1, &b)) // first appears between 9~75ms + { + n++; + cs_ftimeus(&end); + gone = comp_timebus(&end, &start); + rdr_log_dbg(reader, D_DEVICE, "Flush readed byte Nr %d value %.2x time_us %"PRId64, n, b, gone); + cs_ftimeus(&start); // Reset timer + end = start; + } + cs_ftimeus(&endtotal); + gone = comp_timebus(&endtotal, &starttotal); + rdr_log_dbg(reader, D_DEVICE, "Buffers readed %d bytes total time_us %"PRId64, n, gone); +} + +void IO_Serial_Sendbreak(struct s_reader *reader, int32_t duration) +{ + tcsendbreak(reader->handle, duration / 1000); +} + +bool IO_Serial_Read(struct s_reader *reader, uint32_t delay, uint32_t timeout, uint32_t size, unsigned char *data) +{ + uint32_t count = 0; + + if(timeout == 0) // General fix for readers not communicating timeout and delay + { + if(reader->read_timeout != 0) { timeout = reader->read_timeout; } + else { timeout = 9999000; } // hope 9999000 is long enough! + rdr_log_dbg(reader, D_DEVICE, "Warning: read timeout 0 changed to %d us", timeout); + } + + rdr_log_dbg(reader, D_DEVICE, "Read timeout %d us, read delay %d us, to read %d char(s), chunksize %d char(s)", timeout, delay, size, size); + +#if defined(WITH_STAPI) || defined(WITH_STAPI5) || defined(__SH4__) //internal stapi and sh4 readers need special treatment as they don't respond correctly to poll and some sh4 boxes only can read 1 byte at once + if(reader->typ == R_INTERNAL) + { + int32_t readed; +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + const uint32_t chunksize = INT_MAX; +#elif defined(__SH4__) + const uint32_t chunksize = 1; +#endif + struct timeb start, end; + cs_ftime(&start); + end = start; + int64_t gone = 0; + int32_t timeout_ms = timeout / 1000; + readed = 0; + + while(gone < timeout_ms) + { + readed = read(reader->handle, &data[count], size - count >= chunksize ? chunksize : size - count); + cs_ftime(&end); + if(readed > 0) + { + count += readed; + cs_ftime(&start); // Reset timer + end = start; + } + gone = comp_timeb(&end, &start); + if(count < size) + { + if(readed < (int32_t)chunksize) { cs_sleepus(1); } + continue; + } + else { break; } + } + if(count < size) + { + rdr_log_dump_dbg(reader, D_DEVICE, data, count, "Receiving:"); + return ERROR; + } + } + else +#endif // read all chars at once for all other boxes + { + while(count < size) + { + int32_t readed = -1, errorcount = 0; +AGAIN: + if(IO_Serial_WaitToRead(reader, delay, timeout)) + { + rdr_log_dbg(reader, D_DEVICE, "Timeout in IO_Serial_WaitToRead, timeout=%d us", timeout); + return ERROR; + } + + while(readed < 0 && errorcount < 10) + { + readed = read(reader->handle, &data[count], size - count); + if(readed < 0) + { + if(errno == EINTR) { continue; } // try again in case of interrupt + if(errno == EAGAIN) { goto AGAIN; } //EAGAIN needs select procedure again + rdr_log(reader, "ERROR: %s (errno=%d %s)", __func__, errno, strerror(errno)); + errorcount++; + } + } + + if(readed == 0) + { + rdr_log_dump_dbg(reader, D_DEVICE, data, count, "Receiving:"); + rdr_log_dbg(reader, D_DEVICE, "Received End of transmission"); + return ERROR; + } + count += readed; + } + } + rdr_log_dump_dbg(reader, D_DEVICE, data, count, "Receiving:"); + return OK; +} + +int32_t IO_Serial_Receive(struct s_reader *reader, unsigned char *buffer, uint32_t size, uint32_t delay, uint32_t timeout) +{ + return IO_Serial_Read(reader, delay, timeout, size, buffer); +} + +bool IO_Serial_Write(struct s_reader *reader, uint32_t delay, uint32_t timeout, uint32_t size, const unsigned char *data) +{ + const struct s_cardreader *crdr_ops = reader->crdr; + if (!crdr_ops) return ERROR; + + if(timeout == 0) // General fix for readers not communicating timeout and delay + { + if(reader->char_delay != 0) { timeout = reader->char_delay; } + else { timeout = 1000000; } + rdr_log_dbg(reader, D_DEVICE, "Warning: write timeout 0 changed to %d us", timeout); + } + uint32_t count, to_send, i_w; + unsigned char data_w[MAX_ECM_SIZE]; + + to_send = (delay ? 1 : size); // calculate chars to send at one + rdr_log_dbg(reader, D_DEVICE, "Write timeout %d us, write delay %d us, to send %d char(s), chunksize %d char(s)", timeout, delay, size, to_send); + + for(count = 0; count < size; count += to_send) + { + if(count + to_send > size) + { + to_send = size - count; + } + uint16_t errorcount = 0, to_do = to_send; + for(i_w = 0; i_w < to_send; i_w++) + { data_w [i_w] = data [count + i_w]; } + rdr_log_dump_dbg(reader, D_DEVICE, data_w + (to_send - to_do), to_do, "Sending:"); +AGAIN: + if(!IO_Serial_WaitToWrite(reader, delay, timeout)) + { + while(to_do != 0) + { + int32_t u = write(reader->handle, data_w + (to_send - to_do), to_do); + if(u < 1) + { + if(errno == EINTR) { continue; } //try again in case of Interrupted system call + if(errno == EAGAIN) { goto AGAIN; } //EAGAIN needs a select procedure again + errorcount++; + int16_t written = count + to_send - to_do; + if(u != 0) + { + rdr_log(reader, "ERROR: %s: Written=%d of %d (errno=%d %s)", + __func__, written , size, errno, strerror(errno)); + } + if(errorcount > 10) //exit if more than 10 errors + { + return ERROR; + } + } + else + { + to_do -= u; + errorcount = 0; + if(crdr_ops->read_written) + { reader->written += u; } // these readers echo transmitted chars + } + } + } + else + { + rdr_log(reader, "Timeout in IO_Serial_WaitToWrite, delay=%d us, timeout=%d us", delay, timeout); + if(crdr_ops->read_written && reader->written > 0) // these readers need to read all transmitted chars before they can receive! + { + unsigned char buf[256]; + rdr_log_dbg(reader, D_DEVICE, "Reading %d echoed transmitted chars...", reader->written); + int32_t n = reader->written; + if(IO_Serial_Read(reader, 0, 9990000, n, buf)) // use 9990000 = aprox 10 seconds (since written chars could be hughe!) + { return ERROR; } + reader->written = 0; + rdr_log_dbg(reader, D_DEVICE, "Reading of echoed transmitted chars done!"); + } + return ERROR; + } + } + if(crdr_ops->read_written && reader->written > 0) // these readers need to read all transmitted chars before they can receive! + { + unsigned char buf[256]; + rdr_log_dbg(reader, D_DEVICE, "Reading %d echoed transmitted chars...", reader->written); + int32_t n = reader->written; + if(IO_Serial_Read(reader, 0, 9990000, n, buf)) // use 9990000 = aprox 10 seconds (since written chars could be hughe!) + { return ERROR; } + reader->written = 0; + rdr_log_dbg(reader, D_DEVICE, "Reading of echoed transmitted chars done!"); + } + return OK; +} + +#define MAX_TRANSMIT 255 + +int32_t IO_Serial_Transmit(struct s_reader *reader, unsigned char *buffer, uint32_t size, uint32_t UNUSED(expectedlen), uint32_t delay, uint32_t timeout) +{ + uint32_t sent, to_send; + for(sent = 0; sent < size; sent = sent + to_send) + { + to_send = MIN(size, MAX_TRANSMIT); + if(IO_Serial_Write(reader, delay, timeout , to_send, buffer + sent)) + { return ERROR; } + } + return OK; +} + +int32_t IO_Serial_Close(struct s_reader *reader) +{ + + rdr_log_dbg(reader, D_DEVICE, "Closing serial port %s", reader->device); + cs_sleepms(100); // maybe a dirty fix for the restart problem posted by wonderdoc + if(reader->fdmc >= 0) { close(reader->fdmc); } + if(reader->handle >= 0 && close(reader->handle) != 0) + { return ERROR; } + + reader->written = 0; + + return OK; +} + +/* + * Internal functions definition + */ + +static int32_t IO_Serial_Bitrate(int32_t bitrate) +{ + static const struct BaudRates + { + int32_t real; + speed_t apival; + } BaudRateTab[] = + { +#ifdef B230400 + { 230400, B230400 }, +#endif +#ifdef B115200 + { 115200, B115200 }, +#endif +#ifdef B76800 + { 76800, B76800 }, +#endif +#ifdef B57600 + { 57600, B57600 }, +#endif +#ifdef B38400 + { 38400, B38400 }, +#endif +#ifdef B28800 + { 28800, B28800 }, +#endif +#ifdef B19200 + { 19200, B19200 }, +#endif +#ifdef B14400 + { 14400, B14400 }, +#endif +#ifdef B9600 + { 9600, B9600 }, +#endif +#ifdef B7200 + { 7200, B7200 }, +#endif +#ifdef B4800 + { 4800, B4800 }, +#endif +#ifdef B2400 + { 2400, B2400 }, +#endif +#ifdef B1200 + { 1200, B1200 }, +#endif +#ifdef B600 + { 600, B600 }, +#endif +#ifdef B300 + { 300, B300 }, +#endif +#ifdef B200 + { 200, B200 }, +#endif +#ifdef B150 + { 150, B150 }, +#endif +#ifdef B134 + { 134, B134 }, +#endif +#ifdef B110 + { 110, B110 }, +#endif +#ifdef B75 + { 75, B75 }, +#endif +#ifdef B50 + { 50, B50 }, +#endif + }; + + int32_t i; + + for(i = 0; i < (int)(sizeof(BaudRateTab) / sizeof(struct BaudRates)); i++) + { + int32_t b = BaudRateTab[i].real; + int32_t d = ((b - bitrate) * 10000) / b; + if(abs(d) <= 350) + { + return BaudRateTab[i].apival; + } + } + return B0; +} + +bool IO_Serial_WaitToRead(struct s_reader *reader, uint32_t delay_us, uint32_t timeout_us) +{ + struct pollfd ufds; + struct timeb start, end; + int32_t ret_val; + int32_t in_fd; + int64_t polltimeout = timeout_us / 1000; + + if(delay_us > 0) + { cs_sleepus(delay_us); } // wait in us + in_fd = reader->handle; + + ufds.fd = in_fd; + ufds.events = POLLIN | POLLPRI; + ufds.revents = 0x0000; + cs_ftime(&start); // register start time + while(1) + { + ret_val = poll(&ufds, 1, polltimeout); + cs_ftime(&end); // register end time + switch(ret_val) + { + case -1: + if(errno == EINTR || errno == EAGAIN) + { + cs_sleepus(1); + if(timeout_us > 0) + { + polltimeout = (timeout_us / 1000) - comp_timeb(&end, &start); + if(polltimeout < 0) { polltimeout = 0; } + } + continue; + } + rdr_log(reader, "ERROR: %s: timeout=%"PRId64" ms (errno=%d %s)", __func__, comp_timeb(&end, &start), errno, strerror(errno)); + return ERROR; + default: + if(ufds.revents & (POLLIN | POLLPRI)) + { return OK; } + else + { return ERROR; } + } + } +} + +static bool IO_Serial_WaitToWrite(struct s_reader *reader, uint32_t delay_us, uint32_t timeout_us) +{ + struct pollfd ufds; + struct timeb start, end; + int32_t ret_val; + int32_t out_fd; + int64_t polltimeout = timeout_us / 1000; + +#if !defined(WITH_COOLAPI) && !defined(WITH_COOLAPI2) + if(reader->typ == R_INTERNAL) { return OK; } // needed for internal readers, otherwise error! +#endif + if(delay_us > 0) + { cs_sleepus(delay_us); } // wait in us + out_fd = reader->handle; + + ufds.fd = out_fd; + ufds.events = POLLOUT; + ufds.revents = 0x0000; + cs_ftime(&start); // register start time + while(1) + { + ret_val = poll(&ufds, 1, polltimeout); + cs_ftime(&end); // register end time + switch(ret_val) + { + case 0: + rdr_log(reader, "ERROR: not ready to write, timeout=%"PRId64" ms", comp_timeb(&end, &start)); + return ERROR; + case -1: + if(errno == EINTR || errno == EAGAIN) + { + cs_sleepus(1); + if(timeout_us > 0) + { + polltimeout = (timeout_us / 1000) - comp_timeb(&end, &start); + if(polltimeout < 0) { polltimeout = 0; } + } + continue; + } + rdr_log(reader, "ERROR: %s: timeout=%"PRId64" ms (errno=%d %s)", __func__, comp_timeb(&end, &start), errno, strerror(errno)); + return ERROR; + default: + if(((ufds.revents) & POLLOUT) == POLLOUT) + { return OK; } + else + { return ERROR; } + } + } +} + +bool IO_Serial_InitPnP(struct s_reader *reader) +{ + uint32_t PnP_id_size = 0; + unsigned char PnP_id[IO_SERIAL_PNPID_SIZE]; /* PnP Id of the serial device */ + int32_t dtr = IO_SERIAL_HIGH; + int32_t cts = IO_SERIAL_LOW; + + if(IO_Serial_SetParams(reader, 1200, 7, PARITY_NONE, 1, &dtr, &cts)) + { return ERROR; } + + while((PnP_id_size < IO_SERIAL_PNPID_SIZE) && !IO_Serial_Read(reader, 0, 200000, 1, &(PnP_id[PnP_id_size]))) + { PnP_id_size++; } + + return OK; +} + +int32_t IO_Serial_GetStatus(struct s_reader *reader, int32_t *status) +{ + uint32_t modembits = 0; + if(ioctl(reader->handle, TIOCMGET, &modembits) == -1) + { + rdr_log(reader, "ERROR: %s: ioctl(TIOCMGET): %s", __func__, strerror(errno)); + return ERROR; + } + *status = 0; + switch(reader->detect & 0x7f) + { + case 0: + *status = modembits & TIOCM_CAR; + break; + case 1: + *status = modembits & TIOCM_DSR; + break; + case 2: + *status = modembits & TIOCM_CTS; + break; + case 3: + *status = modembits & TIOCM_RNG; + break; + } + if(!(reader->detect & 0x80)) + { *status = !*status; } + return OK; +} + +int32_t IO_Serial_SetBaudrate(struct s_reader *reader, uint32_t baudrate) +{ + rdr_log_dbg(reader, D_IFD, "Setting baudrate to %u", baudrate); + // Get current settings + struct termios tio; + call(tcgetattr(reader->handle, &tio) != 0); + // Set new baudrate + call(IO_Serial_SetBitrate(reader, baudrate, &tio)); + call(IO_Serial_SetProperties(reader, tio)); + reader->current_baudrate = baudrate; //so if update fails, reader->current_baudrate is not changed either + return OK; +} + +#endif diff --git a/csctapi/io_serial.h b/csctapi/io_serial.h new file mode 100644 index 0000000..9c7b059 --- /dev/null +++ b/csctapi/io_serial.h @@ -0,0 +1,88 @@ +/* + io_serial.h + Serial port input/output definitions + + This file is part of the Unix driver for Towitoko smartcard readers + Copyright (C) 2000 Carlos Prados + + This version is modified by doz21 to work in a special manner ;) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _IO_SERIAL_ +#define _IO_SERIAL_ + +#define IO_Serial_DTR_Set(reader) {int32_t _dtr = 1; IO_Serial_DTR_RTS(reader, &_dtr, NULL);} +#define IO_Serial_DTR_Clr(reader) {int32_t _dtr = 0; IO_Serial_DTR_RTS(reader, &_dtr, NULL);} +#define IO_Serial_RTS_Set(reader) {int32_t _rts = 1; IO_Serial_DTR_RTS(reader, NULL, &_rts);} +#define IO_Serial_RTS_Clr(reader) {int32_t _rts = 0; IO_Serial_DTR_RTS(reader, NULL, &_rts);} + +//Type of parity of the serial device +//Chosen to Smartreader definition +//Since for io_serial it doesnt matter which values we choose +#if defined(__CYGWIN__) +#undef PARITY_NONE +#undef PARITY_ODD +#undef PARITY_EVEN +#undef PARITY_MARK +#undef PARITY_SPACE +#endif + +#define PARITY_NONE 0 +#define PARITY_ODD 1 +#define PARITY_EVEN 2 +#define PARITY_MARK 3 +#define PARITY_SPACE 4 +/* Values for the modem lines */ +#define IO_SERIAL_HIGH 1 +#define IO_SERIAL_LOW 0 + +/* Maximum size of PnP Com ID */ +#define IO_SERIAL_PNPID_SIZE 256 + +/* + * Exported functions declaration + */ + +/* IO_Serial creation and deletion */ +void IO_Serial_Flush(struct s_reader *reader); + +/* Initialization and closing */ +bool IO_Serial_InitPnP(struct s_reader *reader); +int32_t IO_Serial_Close(struct s_reader *reader); + +/* Transmission properties */ +bool IO_Serial_DTR_RTS_dbox2(struct s_reader *reader, int32_t *dtr, int32_t *rts); +bool IO_Serial_DTR_RTS(struct s_reader *reader, int32_t *dtr, int32_t *rts); +void IO_Serial_Ioctl_Lock(struct s_reader *reader, int32_t); + +bool IO_Serial_SetBitrate(struct s_reader *reader, uint32_t bitrate, struct termios *tio); +bool IO_Serial_SetParams(struct s_reader *reader, uint32_t bitrate, uint32_t bits, int32_t parity, uint32_t stopbits, int32_t *dtr, int32_t *rts); +bool IO_Serial_SetProperties(struct s_reader *reader, struct termios newtio); +int32_t IO_Serial_SetParity(struct s_reader *reader, unsigned char parity); + +/* Input and output */ +bool IO_Serial_Read(struct s_reader *reader, uint32_t delay, uint32_t timeout, uint32_t size, unsigned char *data); +bool IO_Serial_Write(struct s_reader *reader, uint32_t delay, uint32_t timeout, uint32_t size, const unsigned char *data); +void IO_Serial_Sendbreak(struct s_reader *reader, int32_t duration); +bool IO_Serial_WaitToRead(struct s_reader *reader, uint32_t delay_us, uint32_t timeout_us); + +int32_t IO_Serial_Receive(struct s_reader *reader, unsigned char *buffer, uint32_t size, uint32_t delay, uint32_t timeout); +int32_t IO_Serial_Transmit(struct s_reader *reader, unsigned char *buffer, uint32_t size, uint32_t expectedlen, uint32_t delay, uint32_t timeout); +int32_t IO_Serial_GetStatus(struct s_reader *reader, int32_t *status); +int32_t IO_Serial_SetBaudrate(struct s_reader *reader, uint32_t baudrate); + +#endif /* IO_SERIAL */ diff --git a/csctapi/protocol_t0.c b/csctapi/protocol_t0.c new file mode 100644 index 0000000..cd691c4 --- /dev/null +++ b/csctapi/protocol_t0.c @@ -0,0 +1,555 @@ +/* + protocol_t0.c + Handling of ISO 7816 T=0 protocol + + This file is part of the Unix driver for Towitoko smartcard readers + Copyright (C) 2000 Carlos Prados + + This version is modified by doz21 to work in a special manner ;) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "../globals.h" + +#ifdef WITH_CARDREADER +#include "../oscam-time.h" +#include "icc_async.h" +#include "protocol_t0.h" +/* + * Not exported constants definition + */ + +#define OK 0 +#define ERROR 1 + +#define PROTOCOL_T0_MAX_NULLS 200 +#define PROTOCOL_T0_DEFAULT_WI 10 +#define PROTOCOL_T0_MAX_SHORT_COMMAND 260 +#define PROTOCOL_T0_MAX_SHORT_RESPONSE 258 + +#define PROTOCOL_T14_MAX_NULLS 200 +#define PROTOCOL_T14_DEFAULT_WI 10 +#define PROTOCOL_T14_MAX_SHORT_COMMAND 260 +#define PROTOCOL_T14_MAX_SHORT_RESPONSE 258 + +/* Types of APDU's */ +#define APDU_CASE_1 0x0001 /* Nor send neither receive data */ +#define APDU_CASE_2S 0x0002 /* Receive data (1..256) */ +#define APDU_CASE_3S 0x0003 /* Send data (1..255) */ +#define APDU_CASE_4S 0x0004 /* Send data (1..255) and receive data (1..256) */ +#define APDU_CASE_2E 0x0102 /* Receive data (1..65536) */ +#define APDU_CASE_3E 0x0103 /* Send data (1..65535) */ +#define APDU_CASE_4E 0x0104 /* Send data (1..65535) and receive data (1..65536) */ +#define APDU_MALFORMED 5 /* Malformed APDU */ + +/* Timings in ATR are not used in T=0 cards */ +/* #undef PROTOCOL_T0_USE_DEFAULT_TIMINGS */ + +/* + * Not exported functions declaration + */ + +static int32_t Protocol_T0_Case2E(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr); +static int32_t Protocol_T0_Case3E(struct s_reader *reader, unsigned char *command, unsigned char *rsp, uint16_t *lr); +static int32_t Protocol_T0_Case4E(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr); +static int32_t Protocol_T0_ExchangeTPDU(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr); + +static int32_t APDU_Cmd_Case(unsigned char *command, uint16_t command_len){ + unsigned char B1; + uint16_t B2B3; + uint32_t L; + int32_t res; + + /* Calculate length of body */ + L = MAX(command_len - 4, 0); + + /* Case 1 */ + if(L == 0) + { res = APDU_CASE_1; } + else + { + /* Get first byte of body */ + B1 = command[4]; + + if((B1 != 0) && (L == (uint32_t)B1 + 1)) + { res = APDU_CASE_2S; } + else if(L == 1) + { res = APDU_CASE_3S; } + else if((B1 != 0) && (L == (uint32_t)B1 + 2)) + { res = APDU_CASE_4S; } + else if((B1 == 0) && (L > 2)) + { + /* Get second and third byte of body */ + B2B3 = (((uint16_t)(command[5]) << 8) | command[6]); + + if((B2B3 != 0) && (L == (uint32_t)B2B3 + 3)) + { res = APDU_CASE_2E; } + else if(L == 3) + { res = APDU_CASE_3E; } + else if((B2B3 != 0) && (L == (uint32_t)B2B3 + 5)) + { res = APDU_CASE_4E; } + else + { res = APDU_MALFORMED; } + } + else + { res = APDU_MALFORMED; } + } + return res; +} + +/* + * Exported funtions definition + */ + +int32_t Protocol_T0_Command(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr) +{ + *lr = 0; //will be returned in case of error + if(command_len < 5) //APDU_CASE_1 or malformed + { return ERROR; } + int32_t cmd_case = APDU_Cmd_Case(command, command_len); + switch(cmd_case) + { + case APDU_CASE_2E: + return Protocol_T0_Case2E(reader, command, command_len, rsp, lr); /* fallthrough */ + case APDU_CASE_3E: + return Protocol_T0_Case3E(reader, command, rsp, lr); /* fallthrough */ + case APDU_CASE_4E: + return Protocol_T0_Case4E(reader, command, command_len, rsp, lr); /* fallthrough */ + case APDU_CASE_4S: + command_len--; /* fallthrough */ //FIXME this should change 4S to 2S/3S command + case APDU_CASE_2S: /* fallthrough */ + case APDU_CASE_3S: + return Protocol_T0_ExchangeTPDU(reader, command, command_len, rsp, lr); /* fallthrough */ + default: + rdr_log_dbg(reader, D_IFD, "Protocol: T=0: Invalid APDU"); + return ERROR; + } +} + +/* + * Not exported functions definition + */ + + +static int32_t Protocol_T0_Case2E(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr) +{ + unsigned char buffer[PROTOCOL_T0_MAX_SHORT_COMMAND]; + unsigned char tpdu_rsp[CTA_RES_LEN]; + uint16_t tpdu_lr = 0; + uint32_t i; + + uint32_t Lc = (((uint32_t)(command[5]) << 8) | command[6]); + if(Lc < 256) + { + /* MAP APDU onto command TPDU */ + memcpy(buffer, command, 4); + buffer[4] = (unsigned char) Lc; + memcpy(buffer + 5, command + 7, buffer[4]); + return Protocol_T0_ExchangeTPDU(reader, buffer, buffer[4] + 5, rsp, lr); + } + + /* Prepare envelope TPDU */ + buffer[0] = command[0]; + buffer[1] = 0xC2; + buffer[2] = 0x00; + buffer[3] = 0x00; + + for(i = 0; i < command_len; i += buffer[4]) + { + /* Create envelope command TPDU */ + buffer[4] = MIN(255, command_len - i); + memcpy(buffer + 5, command + i, buffer[4]); + call(Protocol_T0_ExchangeTPDU(reader, buffer, buffer[4] + 5, tpdu_rsp, &tpdu_lr)); + /* Card does support envelope command */ + if(tpdu_rsp[tpdu_lr - 2] == 0x90) + { + /* This is not the last segment */ + if(buffer[4] + i < command_len) + { tpdu_lr = 0; } + else + { + memcpy(rsp, tpdu_rsp, tpdu_lr); // Map response TPDU onto APDU + *lr = tpdu_lr; + } + } + else /* Card does not support envelope command or error */ + { + memcpy(rsp, tpdu_rsp, tpdu_lr); // Map response TPDU onto APDU + *lr = tpdu_lr; + break; + } + } + return OK; +} + + +static int32_t Protocol_T0_Case3E(struct s_reader *reader, unsigned char *command, unsigned char *rsp, uint16_t *lr) +{ + int32_t ret; + unsigned char buffer[5]; + unsigned char tpdu_rsp[CTA_RES_LEN]; + uint16_t tpdu_lr = 0; + int32_t Lm, Lx; + + uint32_t Le = ((((uint32_t)(command[5]) << 8) | command[6]) == 0 ? 65536 : (((uint32_t)(command[5]) << 8) | command[6])); + memcpy(buffer, command, 4);//Map APDU command onto TPDU + + if(Le <= 256) + { + buffer[4] = (unsigned char)Le; + return Protocol_T0_ExchangeTPDU(reader, buffer, 5, rsp, lr); //this was Case3S !!! + } + + /* Map APDU onto command TPDU */ + buffer[4] = 0x00; + call(Protocol_T0_ExchangeTPDU(reader, buffer, 5 , tpdu_rsp, &tpdu_lr)); + + if(tpdu_rsp[tpdu_lr - 2] == 0x6C) /* Le not accepted, La indicated */ + { + /* Map command APDU onto TPDU */ + memcpy(buffer, command, 4); + buffer[4] = tpdu_rsp[tpdu_lr - 1]; + + /* Delete response TPDU */ + tpdu_lr = 0; + + return Protocol_T0_ExchangeTPDU(reader, buffer, 5, rsp, lr); //Reissue command + } + + memcpy(rsp, tpdu_rsp, tpdu_lr);//Map response TPDU onto APDU without change , also for SW1 = 0x67 + *lr = tpdu_lr; + ret = OK; + if(tpdu_rsp[tpdu_lr - 2] == 0x61) /* Command processed, Lx indicated */ + { + Lx = (tpdu_rsp[tpdu_lr - 1] == 0x00) ? 256 : tpdu_rsp[tpdu_lr - 1]; + Lm = Le - (*lr - 2); + + /* Prepare Get Response TPDU */ + buffer[0] = command[0]; + buffer[1] = 0xC0; + buffer[2] = 0x00; + buffer[3] = 0x00; + + while(Lm > 0) + { + buffer[4] = (unsigned char) MIN(Lm, Lx); + call(Protocol_T0_ExchangeTPDU(reader, buffer, 5, tpdu_rsp, &tpdu_lr)); + + /* Append response TPDU to APDU */ + if((*lr + tpdu_lr) > CTA_RES_LEN) + { + rdr_log(reader, "TPDU Append error, new length %i exceeds max length %i", *lr + tpdu_lr, CTA_RES_LEN); + return ERROR; + } + memcpy(rsp + (*lr - 2), tpdu_rsp, tpdu_lr); + *lr += tpdu_lr; + + /* Delete response TPDU */ + tpdu_lr = 0; + + Lm = Le - (*lr - 2); + }/* Lm == 0 */ + } + return ret; +} + + +static int32_t Protocol_T0_Case4E(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr) +{ + int32_t ret; + unsigned char buffer[PROTOCOL_T0_MAX_SHORT_COMMAND]; + unsigned char tpdu_rsp[CTA_RES_LEN]; + memset(tpdu_rsp, 0, sizeof(tpdu_rsp)); + uint16_t tpdu_lr = 0; + int32_t Le; + + uint32_t Lc = (((uint32_t)(command[5]) << 8) | command[6]); + /* 4E1 */ + if(Lc < 256) + { + /* Map APDU onto command TPDU */ + memcpy(buffer, command, 4); + buffer[4] = (unsigned char) Lc; + memcpy(buffer + 5, command, buffer[4]); + ret = Protocol_T0_ExchangeTPDU(reader, buffer, buffer[4] + 5, tpdu_rsp, &tpdu_lr); + } + else /* 4E2 */ + { ret = Protocol_T0_Case2E(reader, command, command_len, tpdu_rsp, &tpdu_lr); } + + /* 4E1 a) b) and c) */ + if(ret == OK) + { + Le = ((((uint32_t)(command[command_len - 2]) << 8) | command[command_len - 1]) == 0 ? 65536 : (((uint32_t)(command[command_len - 2]) << 8) | command[command_len - 1])); + if(tpdu_lr > 1 && tpdu_rsp[tpdu_lr - 2] == 0x61) + { + /* Lm == (Le - APDU_Rsp_RawLen (tpdu_rsp)) == 0 */ + if(tpdu_rsp[tpdu_lr - 1] != 0x00) + { Le = MIN(tpdu_rsp[tpdu_lr - 1], Le); } + + /* Delete response TPDU */ + tpdu_lr = 0; + + /* Prepare extended Get Response APDU command */ + buffer[0] = command[0]; + buffer[1] = 0xC0; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; /* B1 = 0x00 */ + buffer[5] = (unsigned char)(Le >> 8); /* B2 = BL-1 */ + buffer[6] = (unsigned char)(Le & 0x00FF); /* B3 = BL */ + ret = Protocol_T0_Case3E(reader, buffer, rsp, lr); + } + else if(tpdu_lr > 1 && (tpdu_rsp[tpdu_lr - 2] & 0xF0) == 0x60) + { + /* Map response TPDU onto APDU without change */ + memcpy(rsp, tpdu_rsp, tpdu_lr); + *lr = tpdu_lr; + } + else + { + /* Delete response TPDU */ + tpdu_lr = 0; + + /* Prepare extended Get Response APDU command */ + buffer[0] = command[0]; + buffer[1] = 0xC0; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; /* B1 = 0x00 */ + buffer[5] = (unsigned char) Le >> 8; /* B2 = BL-1 */ + buffer[6] = (unsigned char) Le & 0x00FF; /* B3 = BL */ + ret = Protocol_T0_Case3E(reader, buffer, rsp, lr); + } + } + return ret; +} + + +static int32_t Protocol_T0_ExchangeTPDU(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr) +{ + unsigned char buffer[PROTOCOL_T0_MAX_SHORT_RESPONSE]; + unsigned char *data; + int32_t Lc, Le, sent, recved, expectedlen; + int32_t nulls, cmd_case; + int32_t timeout; + *lr = 0; //in case of error this will be returned + + cmd_case = APDU_Cmd_Case(command, command_len); + switch(cmd_case) + { + case APDU_CASE_2S: + Lc = command[4]; + Le = 0; + expectedlen = 1; + data = command + 5; + break; + case APDU_CASE_3S: + Lc = 0; + Le = command[4]; + if(!Le) + { expectedlen = 2; } + else + { expectedlen = 1 + Le + 2; } + data = NULL; + break; + default: + rdr_log_dbg(reader, D_TRACE, "ERROR: invalid cmd_case = %i in Protocol_T0_ExchangeTPDU", cmd_case); + return ERROR; + } + timeout = ICC_Async_GetTimings(reader, reader->char_delay); // we are going to send: char delay timeout + if(ICC_Async_Transmit(reader, 5, expectedlen, command, 0, timeout) != OK) { return ERROR; } //Send header bytes + + /* Initialise counters */ + nulls = 0; + sent = 0; + recved = 0; + + /* + * Let's be a bit paranoid with buffer sizes within this loop + * so it doesn't overflow reception and transmission buffers + * if card does not strictly respect the protocol + */ + + while(recved < PROTOCOL_T0_MAX_SHORT_RESPONSE) + { + timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout + if(ICC_Async_Receive(reader, 1, buffer + recved, 0, timeout) != OK) { return ERROR; } //Read one procedure byte + + /* NULL byte received */ + if(buffer[recved] == 0x60) + { + nulls++; + if(nulls >= PROTOCOL_T0_MAX_NULLS) //Maximum number of nulls reached + { + rdr_log_dbg(reader, D_TRACE, "ERROR: %s: Maximum number of nulls reached: %d", __func__, nulls); + return ERROR; + } + } + else if((buffer[recved] & 0xF0) == 0x60 || (buffer[recved] & 0xF0) == 0x90) /* SW1 byte received */ + { + rdr_log_dbg(reader, D_TRACE, "SW1: %02X", buffer[recved] & 0xf0); + recved++; + if(recved >= PROTOCOL_T0_MAX_SHORT_RESPONSE) + { + rdr_log_dbg(reader, D_TRACE, "ERROR: %s: Maximum short response exceeded: %d", __func__, recved); + return ERROR; + } + timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout + if(ICC_Async_Receive(reader, 1, buffer + recved, 0, timeout) != OK) { return ERROR; } //Read SW2 byte + rdr_log_dbg(reader, D_TRACE, "SW2: %02X", buffer[recved] & 0xf0); + recved++; + break; + } + else if((buffer[recved] & 0x0E) == (command[1] & 0x0E)) /* ACK byte received */ + { + //printf("ack\n"); + /* Reset null's counter */ + nulls = 0; + + /* Case 2 command: send data */ + if(cmd_case == APDU_CASE_2S) + { + if(sent >= Lc) + { + rdr_log_dbg(reader, D_TRACE, "ERROR: %s: ACK byte: sent=%d exceeds Lc=%d", __func__, sent, Lc); + return ERROR; + } + timeout = ICC_Async_GetTimings(reader, reader->char_delay); // we are going to send: char delay timeout + if(ICC_Async_Transmit(reader, MAX(Lc - sent, 0), 2, data + sent, 0, timeout) != OK) { return ERROR; } /* Send remaining data bytes */ + sent = Lc; + continue; + } + else /* Case 3 command: receive data */ + { + if(recved > PROTOCOL_T0_MAX_SHORT_RESPONSE) + { + rdr_log_dbg(reader, D_TRACE, "ERROR: %s: Case 3 ACK - maximum short response exceeded: %d", __func__, recved); + return ERROR; + } + + /* + * Le <= PROTOCOL_T0_MAX_SHORT_RESPONSE - 2 for short commands + */ + + /* Read remaining data bytes */ + timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout + if(ICC_Async_Receive(reader, MAX(Le - recved, 0), buffer + recved, 0, timeout) != OK) { return ERROR; } + recved = Le; + continue; + } + } + else if((buffer[recved] & 0x0E) == ((~command[1]) & 0x0E)) /* ~ACK byte received */ + { + //printf("~ack\n"); + nulls = 0; //Reset null's counter + + /* Case 2 command: send data */ + if(cmd_case == APDU_CASE_2S) + { + if(sent >= Lc) + { + rdr_log_dbg(reader, D_TRACE, "ERROR: %s: ~ACK byte: sent=%d exceeds Lc=%d", __func__, sent, Lc); + return ERROR; + } + timeout = ICC_Async_GetTimings(reader, reader->char_delay); // we are going to send: char delay timeout + if(ICC_Async_Transmit(reader, 1, 1, data + sent, 0, timeout) != OK) { return ERROR; } //Send next data byte + sent++; + continue; + } + else /* Case 3 command: receive data */ + { + if(recved > PROTOCOL_T0_MAX_SHORT_RESPONSE) + { + rdr_log_dbg(reader, D_TRACE, "ERROR: %s: Case 3 ~ACK - maximum short response exceeded: %d", __func__, recved); + return ERROR; + } + timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout + if(ICC_Async_Receive(reader, 1, buffer + recved, 0, timeout) != OK) { return ERROR; } //Read next data byte + recved++; + continue; + } + } + else /* Anything else received */ + { + rdr_log_dbg(reader, D_TRACE, "ERROR: %s: Received unexpected character: %02X", __func__, buffer[recved]); + return ERROR; + } + }//while + + memcpy(rsp, buffer, recved); + *lr = recved; + return OK; +} + +int32_t Protocol_T14_ExchangeTPDU(struct s_reader *reader, unsigned char *cmd_raw, uint16_t command_len, unsigned char *rsp, uint16_t *lr) +{ + unsigned char buffer[PROTOCOL_T14_MAX_SHORT_RESPONSE]; + int32_t recved; + int32_t cmd_case; + int32_t timeout; + unsigned char ixor = 0x3E; + unsigned char ixor1 = 0x3F; + int32_t i; + int32_t cmd_len = (int32_t) command_len; + *lr = 0; //in case of error this is returned + + /* Parse APDU */ + cmd_case = APDU_Cmd_Case(cmd_raw, cmd_len); + for(i = 0; i < cmd_len; i++) + { ixor ^= cmd_raw[i]; } + + /* Check case of command */ + if((cmd_case != APDU_CASE_2S) && (cmd_case != APDU_CASE_3S)) + { + rdr_log_dbg(reader, D_TRACE, "ERROR: invalid cmd_case = %i in Protocol_T14_ExchangeTPDU", cmd_case); + return ERROR; + } + + buffer[0] = 0x01; //send 0x01 byte + memcpy(buffer + 1, cmd_raw, cmd_len); // apdu + buffer[cmd_len + 1] = ixor; // xor byte + + /* Send apdu */ + timeout = ICC_Async_GetTimings(reader, reader->char_delay); // we are going to send: char delay timeout + if(ICC_Async_Transmit(reader, cmd_len + 2, 0, buffer, 0, timeout) != OK) { return ERROR; } //send apdu + if(cmd_raw[0] == 0x02 && cmd_raw[1] == 0x09) { cs_sleepms(2500); } //FIXME why wait? -> needed for init on overclocked T14 cards + + timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout +// if ((reader->typ == R_SMART) && (reader->smart_type >= 2)) timeout *= 1.1; + if(ICC_Async_Receive(reader, 8, buffer, 0, timeout) != OK) { return ERROR; } //Read one procedure byte + recved = (int32_t)buffer[7]; + if(recved) + { + timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout +// if ((reader->typ == R_SMART) && (reader->smart_type >= 2)) timeout *= 1.1; + if(ICC_Async_Receive(reader, recved, buffer + 8, 0 , timeout) != OK) {return ERROR; } + } + timeout = ICC_Async_GetTimings(reader, reader->read_timeout); // we are going to receive: WWT timeout +// if ((reader->typ == R_SMART) && (reader->smart_type >= 2)) timeout *= 1.1; + if(ICC_Async_Receive(reader, 1, &ixor, 0, timeout) != OK) {return ERROR; } + for(i = 0; i < 8 + recved; i++) + { ixor1 ^= buffer[i]; } + if(ixor1 != ixor) + { + rdr_log_dbg(reader, D_TRACE, "ERROR: invalid checksum = %02X expected %02X", ixor1, ixor); + return ERROR; + } + memcpy(buffer + 8 + recved, buffer + 2, 2); + *lr = recved + 2; + memcpy(rsp, buffer + 8, *lr); + return OK; +} +#endif diff --git a/csctapi/protocol_t0.h b/csctapi/protocol_t0.h new file mode 100644 index 0000000..e6aefa8 --- /dev/null +++ b/csctapi/protocol_t0.h @@ -0,0 +1,33 @@ +/* + protocol_t0.h + ISO 7816 T=0 Transport Protocol definitions + + This file is part of the Unix driver for Towitoko smartcard readers + Copyright (C) 2000 Carlos Prados + + This version is modified by doz21 to work in a special manner ;) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _PROTOCOL_T0_ +#define _PROTOCOL_T0_ + +/* Send a command and return a response */ +int32_t Protocol_T0_Command(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr); +int32_t Protocol_T14_ExchangeTPDU(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr); +int32_t Protocol_T1_Command(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr); + +#endif /* _PROTOCOL_T0_ */ diff --git a/csctapi/protocol_t1.c b/csctapi/protocol_t1.c new file mode 100644 index 0000000..9adf961 --- /dev/null +++ b/csctapi/protocol_t1.c @@ -0,0 +1,330 @@ +#include "../globals.h" +#include "../oscam-time.h" +#ifdef WITH_CARDREADER +#include "icc_async.h" + +#define OK 0 +#define ERROR 1 + +/* Buffer sizes */ +#define T1_BLOCK_MAX_SIZE 259 + +/* Types of block */ +#define T1_BLOCK_I 0x00 +#define T1_BLOCK_R_OK 0x80 +#define T1_BLOCK_R_EDC_ERR 0x81 +#define T1_BLOCK_R_OTHER_ERR 0x82 +#define T1_BLOCK_S_RESYNCH_REQ 0xC0 +#define T1_BLOCK_S_RESYNCH_RES 0xE0 +#define T1_BLOCK_S_IFS_REQ 0xC1 +#define T1_BLOCK_S_IFS_RES 0xE1 +#define T1_BLOCK_S_ABORT_REQ 0xC2 +#define T1_BLOCK_S_ABORT_RES 0xE2 +#define T1_BLOCK_S_WTX_REQ 0xC3 +#define T1_BLOCK_S_WTX_RES 0xE3 +#define T1_BLOCK_S_VPP_ERR 0xE4 + +#define T1_BLOCK_NAD 0x00 + +#define T1_Block_GetNS(a) ((a[1] >> 6) & 0x01) +#define T1_Block_GetMore(a) ((a[1] >> 5) & 0x01) +#define T1_Block_GetNR(a) ((a[1] >> 4) & 0x01) +#define T1_Block_GetLen(a) a[2] + + +static unsigned char T1_Block_LRC(unsigned char *data, uint32_t length) +{ + unsigned char lrc = 0x00; + uint32_t i; + for(i = 0; i < length; i++) + { + lrc ^= data[i]; + } + return lrc; +} + +static int32_t T1_Block_SendIBlock(struct s_reader *reader, uint8_t *block_data, unsigned char len, unsigned char *inf, unsigned char ns, int32_t more, uint32_t timeout) +{ + int length = len + 4; + + block_data[0] = T1_BLOCK_NAD; + block_data[1] = T1_BLOCK_I | ((ns << 6) & 0x40); + if(more) + { + block_data[1] |= 0x20; + } + block_data[2] = len; + if(len != 0x00) + { + memcpy(block_data + 3, inf, len); + } + block_data[len + 3] = T1_Block_LRC(block_data, len + 3); + + return ICC_Async_Transmit(reader, length, 0, block_data, 0, timeout); +} + +static int32_t T1_Block_SendRBlock(struct s_reader *reader, uint8_t *block_data, unsigned char type, unsigned char nr, uint32_t timeout) +{ + int length = 4; + + block_data[0] = T1_BLOCK_NAD; + block_data[1] = type | ((nr << 4) & 0x10); + block_data[2] = 0x00; + block_data[3] = T1_Block_LRC(block_data, 3); + + return ICC_Async_Transmit(reader, length, 0, block_data, 0, timeout); +} + +static int32_t T1_Block_SendSBlock(struct s_reader *reader, uint8_t *block_data, unsigned char type, unsigned char len, unsigned char *inf, uint32_t timeout) +{ + int length = 4 + len; + + block_data[0] = T1_BLOCK_NAD; + block_data[1] = type; + block_data[2] = len; + if(len != 0x00) + { + memcpy(block_data + 3, inf, len); + } + + block_data[len + 3] = T1_Block_LRC(block_data, len + 3); + + return ICC_Async_Transmit(reader, length, 0, block_data, 0, timeout); +} + +static int32_t Protocol_T1_ReceiveBlock(struct s_reader *reader, uint8_t *block_data, uint32_t *block_length, uint8_t *rsp_type, uint32_t timeout) +{ + int32_t ret, length; + + /* Receive four mandatory bytes */ + if(ICC_Async_Receive(reader, 4, block_data, 0, timeout)) + { + ret = ERROR; + } + else + { + length = block_data[2]; + if(length != 0x00) + { + *block_length = (length + 4 > T1_BLOCK_MAX_SIZE) ? T1_BLOCK_MAX_SIZE : length + 4; + + /* Receive remaining bytes */ + if(ICC_Async_Receive(reader, *block_length - 4, block_data + 4, 0, timeout)) + { + ret = ERROR; + } + else + { + ret = OK; + } + } + else + { + ret = OK; + *block_length = 4; + } + } + *rsp_type = ((block_data[1] & 0x80) == T1_BLOCK_I) ? T1_BLOCK_I : (block_data[1] & 0xEF); + + return ret; +} + +int32_t Protocol_T1_Command(struct s_reader *reader, unsigned char *command, uint16_t command_len, unsigned char *rsp, uint16_t *lr) +{ + uint8_t block_data[T1_BLOCK_MAX_SIZE]; + uint8_t rsp_type, bytes, nr, wtx; + uint16_t counter; + int32_t ret, timeout; + bool more; + uint32_t block_length = 0; + if(command[1] == T1_BLOCK_S_IFS_REQ) + { + uint8_t inf = command[3]; + + /* Create an IFS request S-Block */ + timeout = ICC_Async_GetTimings(reader, reader->CWT); // we are going to send: CWT timeout + //cs_sleepus(reader->block_delay); // we were receiving, now sending so wait BGT time + ret = T1_Block_SendSBlock(reader, block_data, T1_BLOCK_S_IFS_REQ, 1, &inf, timeout); + if(ret == ERROR) + { + rdr_log_dbg(reader, D_IFD, "Protocol: Sending block S(IFS request, %d)", inf); + } + + /* Receive a block */ + timeout = ICC_Async_GetTimings(reader, reader->BWT); // we are going to receive so set Block Waiting Timeout! + //cs_sleepus(reader->block_delay); // we were sending, now receiving so wait BGT time + ret = Protocol_T1_ReceiveBlock(reader, block_data, &block_length, &rsp_type, timeout); + + if(ret == OK) + { + /* Positive IFS Response S-Block received */ + if(rsp_type == T1_BLOCK_S_IFS_RES) + { + rdr_log_dbg(reader, D_IFD, "Protocol: Received block S(IFS response, %d)", block_data[3]); + } + } + + return ret; + } + else if(command[1] == T1_BLOCK_S_RESYNCH_REQ) + { + /* Create an Resynch request S-Block */ + timeout = ICC_Async_GetTimings(reader, reader->CWT); // we are going to send: CWT timeout + //cs_sleepus(reader->block_delay); // we were receiving, now sending so wait BGT time + ret = T1_Block_SendSBlock(reader, block_data, T1_BLOCK_S_RESYNCH_REQ, 0, NULL, timeout); + if(ret == ERROR) + { + rdr_log_dbg(reader, D_IFD, "Protocol: Sending block S(RESYNCH request)"); + } + + /* Receive a block */ + timeout = ICC_Async_GetTimings(reader, reader->BWT); // we are going to receive so set Block Waiting Timeout! + //cs_sleepus(reader->block_delay); // we were sending, now receiving so wait BGT time + ret = Protocol_T1_ReceiveBlock(reader, block_data, &block_length, &rsp_type, timeout); + + if(ret == OK) + { + /* Positive IFS Response S-Block received */ + if(rsp_type == T1_BLOCK_S_RESYNCH_RES) + { + rdr_log_dbg(reader, D_IFD, "Protocol: Received block S(RESYNCH response)"); + reader->ns = 0; + } + } + return ret; + } + + /* Calculate the number of bytes to send */ + counter = 0; + bytes = MIN(command_len, reader->ifsc); + + /* See if chaining is needed */ + more = (command_len > reader->ifsc); + + /* Increment ns */ + reader->ns = (reader->ns == 1) ? 0 : 1; //toggle from 0 to 1 and back + + /* Create an I-Block */ + timeout = ICC_Async_GetTimings(reader, reader->CWT); // we are going to send: CWT timeout + //cs_sleepus(reader->block_delay); // we were receiving, now sending so wait BGT time + ret = T1_Block_SendIBlock(reader, block_data, bytes, command, reader->ns, more, timeout); + rdr_log_dbg(reader, D_IFD, "Sending block I(%d,%d)", reader->ns, more); + + while((ret == OK) && more) + { + /* Receive a block */ + + timeout = ICC_Async_GetTimings(reader, reader->BWT); // we are going to receive so set Block Waiting Timeout! + //cs_sleepus(reader->block_delay); // we were sending, now receiving so wait BGT time + ret = Protocol_T1_ReceiveBlock(reader, block_data, &block_length, &rsp_type, timeout); + + if(ret == OK) + { + /* Positive ACK R-Block received */ + if(rsp_type == T1_BLOCK_R_OK) + { + rdr_log_dbg(reader, D_IFD, "Protocol: Received block R(%d)", T1_Block_GetNR(block_data)); + + /* Increment ns */ + reader->ns = (reader->ns == 1) ? 0 : 1; //toggle from 0 to 1 and back + + /* Calculate the number of bytes to send */ + counter += bytes; + bytes = MIN(command_len - counter, reader->ifsc); + + /* See if chaining is needed */ + more = (command_len - counter > reader->ifsc); + + /* Send an I-Block */ + timeout = ICC_Async_GetTimings(reader, reader->CWT); // we are going to send: CWT timeout + //cs_sleepus(reader->block_delay); // we were receiving, now sending so wait BGT time + ret = T1_Block_SendIBlock(reader, block_data, bytes, command + counter, reader->ns, more, timeout); + rdr_log_dbg(reader, D_IFD, "Protocol: Sending block I(%d,%d)", reader->ns, more); + + } + else + { + rdr_log_dbg(reader, D_TRACE, "ERROR: T1 Command %02X not implemented", rsp_type); + return ERROR; + } + } + else + { + rdr_log_dbg(reader, D_TRACE, "ERROR: T1 Command returned error"); + return ERROR; + } + } + + /* Reset counter */ + counter = 0; + more = 1; + wtx = 1; + + while((ret == OK) && more) + { + + /* Receive a block */ + timeout = ICC_Async_GetTimings(reader, wtx * reader->BWT); // we are going to receive so set Block Waiting Timeout! + //cs_sleepus(reader->block_delay); // we were sending, now receiving so wait BGT time + ret = Protocol_T1_ReceiveBlock(reader, block_data, &block_length, &rsp_type, timeout); + wtx = 1; // reset WTX value since its only valid for first received I block + + if(ret == OK) + { + if(rsp_type == T1_BLOCK_I) + { + rdr_log_dbg(reader, D_IFD, "Protocol: Received block I(%d,%d)", T1_Block_GetNS(block_data), T1_Block_GetMore(block_data)); + + bytes = T1_Block_GetLen(block_data); + + /* Calculate nr */ + nr = (T1_Block_GetNS(block_data) + 1) % 2; + + if(counter + bytes > T1_BLOCK_MAX_SIZE) + { + return ERROR; + } + + memcpy(rsp + counter, block_data + 3, bytes); + counter += bytes; + + /* See if chaining is requested */ + more = T1_Block_GetMore(block_data); + + if(more) + { + /* Send R-Block */ + timeout = ICC_Async_GetTimings(reader, reader->CWT); // we are going to send: CWT timeout + //cs_sleepus(reader->block_delay); // we were receiving, now sending so wait BGT time + ret = T1_Block_SendRBlock(reader, block_data, T1_BLOCK_R_OK, nr, timeout); + rdr_log_dbg(reader, D_IFD, "Protocol: Sending block R(%d)", nr); + } + } + else if(rsp_type == T1_BLOCK_S_WTX_REQ) /* WTX Request S-Block received */ + { + /* Get wtx multiplier */ + wtx = block_data[3]; + rdr_log_dbg(reader, D_IFD, "Protocol: Received block S(WTX request, %d)", wtx); + + /* Send an WTX response S-Block */ + timeout = ICC_Async_GetTimings(reader, reader->CWT); // we are going to send: CWT timeout + //cs_sleepus(reader->block_delay); // we were receiving, now sending so wait BGT time + ret = T1_Block_SendSBlock(reader, block_data, T1_BLOCK_S_WTX_RES, 1, &wtx, timeout); + rdr_log_dbg(reader, D_IFD, "Protocol: Sending block S(WTX response, %d)", wtx); + } + else + { + rdr_log_dbg(reader, D_TRACE, "ERROR: T1 Command %02X not implemented in Receive Block", rsp_type); + ret = ERROR; //not implemented + } + } + } + + if(ret == OK) + { + *lr = counter; + } + + return ret; +} +#endif diff --git a/devtools/README b/devtools/README new file mode 100644 index 0000000..e4ca5d6 --- /dev/null +++ b/devtools/README @@ -0,0 +1,16 @@ +The idea of devtools directory is to collect tools that are useful for +OSCam coders during development or tools used in the oscam build. + +These tools are not meant to be used by OSCam users. + +Currently available tools are: + + check_config_tables.sh This script checks config tables + against variable defintions + in order to find wrong types. + + check_cmdline_opts.sh This script checks if all command + line parameters are properly set. + + extract_config.sh This script extracts OSCam build + config from oscam binary. diff --git a/devtools/check_cmdline_opts.sh b/devtools/check_cmdline_opts.sh new file mode 100644 index 0000000..715f640 --- /dev/null +++ b/devtools/check_cmdline_opts.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +if [ ! -f oscam.c ] +then + echo "ERROR: Run this script in the oscam source directory (where oscam.c file is)." + exit 1 +fi + +OPTS=`grep "static const char short_options" oscam.c | sed -e 's|.* \"||;s|\".*||;s|:||g;s|\(.\)|\1 |g'` +SOPTS=$((for i in $OPTS; do echo $i; done) | sort) +LOPTS=$(grep "^ { \"" oscam.c | sed -e 's|.*{ \"||;s|".*||') +FOPTS="" + +#echo opts:$OPTS +#echo sopts:$SOPTS +#echo lopts:$LOPTS +#exit + +echo -en "Short options that are free to use:\n " +for i in $(echo {A..Z}) $(echo {a..z}) $(echo {0..9}) +do + echo $OPTS | grep -q $i 2>/dev/null + if [ $? != 0 ] + then + echo -n $i + FOPTS="$FOPTS $i" + fi +done +echo + +echo -en "Options that are not processed in oscam.c (missing case 'x'):\n " +#AOPTS="$FOPTS $SOPTS" +AOPTS="$SOPTS" +for i in $AOPTS +do + grep -q "case '$i'" oscam.c 2>/dev/null + [ $? != 0 ] && echo -n $i +done +echo + +echo -en "Short options that are missing from 'struct long_options[]'\n " +for i in $AOPTS +do + grep -q "NULL, '$i' }," oscam.c 2>/dev/null + [ $? != 0 ] && echo -n $i +done +echo + +echo -en "No help entry in usage() for short options:\n " +for i in $AOPTS +do + grep -q " printf(\" -$i" oscam.c 2>/dev/null + [ $? != 0 ] && echo -n $i +done +echo + +echo "No help entry in usage() long options:" +for i in $LOPTS +do + grep -q " printf(\" -., --$i" oscam.c 2>/dev/null + [ $? != 0 ] && echo " $i" +done +echo diff --git a/devtools/check_config_tables.sh b/devtools/check_config_tables.sh new file mode 100644 index 0000000..aeec2e4 --- /dev/null +++ b/devtools/check_config_tables.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +FILES="oscam-config-global.c oscam-config-account.c oscam-config-reader.c" + +echo "** Checking config tables stored in: $FILES" + +if [ ! -f globals.h ] +then + echo "ERROR: Run this script in the oscam source directory (where globals.h file is)." + exit 1 +fi + +check_int() { + DEF=$1 + TYPE=$2 + echo "== Checking $DEF -> config parser expects '$TYPE' type but globals.h is THE CORRECT ONE" + for VAR in `cat $FILES | grep $DEF | grep -w OFS | tr -d ' \t' | cut -d\( -f3 | cut -d\) -f1` + do + [ $VAR = "cacheex.maxhop" ] && continue + awk '{print $1 " " $2 }' globals.h | grep -vE "^[#/]" | sed -e 's|;||g' | grep -w $VAR | while read Z + do + if [ "$TYPE $VAR" != "$Z" ] + then + printf "globals.h: %-32s | config: $TYPE $VAR\n" "$Z" + fi + done + done +} + +check_int DEF_OPT_INT8 int8_t +check_int DEF_OPT_UINT8 uint8_t +check_int DEF_OPT_INT32 int32_t +check_int DEF_OPT_UINT32 uint32_t + +echo "== Checking DEF_OPT_STR (strings) -> config parser expects 'char *' type" +for VAR in `cat $FILES | grep DEF_OPT_STR | grep OFS | awk '{print $3}' | sed "s|OFS(||;s|)||;s|,||"` +do + grep -w $VAR globals.h | grep -vwE "(\*$VAR|#include)" | grep -w --color $VAR +done + +echo "== Checking DEF_OPT_SSTR (static strings) -> config parser expects 'char[x]'" +for VAR in `cat $FILES | grep DEF_OPT_SSTR | grep OFS | awk '{print $3}' | sed "s|OFS(||;s|)||;s|,||"` +do + grep -w $VAR globals.h | grep -vE "(\[|#define)" | grep -w --color $VAR +done + +echo "== Checking DEF_OPT_HEX (arrays) -> config parser expects 'uint8_t[x]' type" +for VAR in `cat $FILES | grep DEF_OPT_HEX | grep OFS | awk '{print $3}' | sed "s|OFS(||;s|)||;s|,||"` +do + grep -w $VAR globals.h | grep -vw uint8_t | grep -w --color $VAR +done diff --git a/devtools/extract_config.sh b/devtools/extract_config.sh new file mode 100644 index 0000000..c6872fe --- /dev/null +++ b/devtools/extract_config.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +if [ "$1" = "" ] +then + echo "Extract OSCam config from OSCam binary" + echo + echo " Usage: `basename $0` oscam_binary" + echo + exit 1 +fi + +strings $1 | sed -n 's/^CFG~//p' | openssl enc -d -base64 | gzip -d 2>/dev/null diff --git a/extapi/README b/extapi/README new file mode 100644 index 0000000..f0a426d --- /dev/null +++ b/extapi/README @@ -0,0 +1,5 @@ +This directory holds files that are external to OSCam. + +These files are API definitions or libs that are better to be shipped +with OSCAM than to depend on founding them on the system that OSCam is +being compiled on. diff --git a/extapi/coolapi.h b/extapi/coolapi.h new file mode 100644 index 0000000..2b26ae3 --- /dev/null +++ b/extapi/coolapi.h @@ -0,0 +1,240 @@ +#ifndef _COOLAPI_H_ +#define _COOLAPI_H_ + +/* timeouts in ETU */ +typedef struct +{ + uint16_t CardActTime; + uint16_t CardDeactTime; + uint16_t ATRSTime; + uint16_t ATRDTime; + uint32_t BLKTime; + uint32_t CHTime; + uint8_t CHGuardTime; + uint8_t BKGuardTime; +} CNXT_SMC_TIMEOUT; + +typedef struct +{ + uint8_t TXRetries; + uint8_t RXRetries; +} CNXT_SMC_RETRIES; + +typedef enum +{ + CNXT_SMC_CONV_DIRECT = 0, + CNXT_SMC_CONV_INVERSE, + CNXT_SMC_CONV_LAST = CNXT_SMC_CONV_INVERSE +} CNXT_SMC_CONVENTION; + +typedef struct +{ + CNXT_SMC_CONVENTION convention; + uint8_t protocol; + uint8_t FI; + uint8_t N; + uint8_t DI; + uint8_t PI1; + uint8_t PI2; + uint8_t II; + uint8_t historical[15]; + uint8_t length; + CNXT_SMC_RETRIES retries; + uint8_t filterprotocolbytes; +} CNXT_SMC_COMM; + +typedef struct +{ + uint8_t *pStartAddress; + uint8_t *pEndAddress; + uint8_t *pWriteAddress; + uint8_t *pReadAddress; +} CNXT_CBUF_POINTERS; + +/* These functions are implemented in libnxp and are used in coolstream */ +int32_t cnxt_cbuf_init(void *); +int32_t cnxt_cbuf_get_used(void *buffer, uint32_t * bytes_used); +int32_t cnxt_cbuf_attach(void *handle, int32_t type, void * channel); +int32_t cnxt_cbuf_detach(void *handle, int32_t type, void * channel); +int32_t cnxt_cbuf_close(void * handle); +int32_t cnxt_cbuf_read_data(void * handle, void *buffer, uint32_t size, uint32_t * ret_size); +int32_t cnxt_cbuf_flush(void * handle, int); +int32_t cnxt_cbuf_removed_data(void *handle, uint32_t bytes); +int32_t cnxt_cbuf_get_pointers(void *handle, CNXT_CBUF_POINTERS *buffer_pointers); + +void cnxt_kal_initialize(void); +void cnxt_kal_terminate(void); +void cnxt_drv_init(void); +void cnxt_drv_term(void); + +int32_t cnxt_dmx_init(void *); +int32_t cnxt_dmx_close(void * handle); +int32_t cnxt_dmx_channel_close(void * channel); +int32_t cnxt_dmx_open_filter(void * handle, void *flt); +int32_t cnxt_dmx_close_filter(void * filter); +int32_t cnxt_dmx_channel_attach(void * channel, int32_t param1, int32_t param2, void * buffer); +int32_t cnxt_dmx_channel_detach(void * channel, int32_t param1, int32_t param2, void * buffer); +int32_t cnxt_dmx_channel_attach_filter(void * channel, void * filter); +int32_t cnxt_dmx_channel_detach_filter(void * channel, void * filter); +int32_t cnxt_dmx_set_channel_buffer(void * channel, int32_t param1, void * buffer); +int32_t cnxt_dmx_set_channel_pid(void * channel, uint32_t pid); +int32_t cnxt_dmx_get_channel_from_pid(void * device, uint16_t pid, void * channel); +int32_t cnxt_dmx_set_channel_key(void * channel, int32_t param1, uint32_t parity, unsigned char *cw, uint32_t len); +int32_t cnxt_dmx_channel_ctrl(void * channel, int32_t param1, int32_t param2); + +int32_t cnxt_smc_init(void *); + +int32_t cnxt_smc_open(void *cool_handle, int32_t *, void *, void *); +int32_t cnxt_smc_enable_flow_control(void *cool_handle, int32_t enable); +int32_t cnxt_smc_get_state(void *cool_handle, int32_t *state); +int32_t cnxt_smc_get_clock_freq(void *cool_handle, uint32_t *clk); +int32_t cnxt_smc_reset_card(void *cool_handle, int32_t timeout, void *, void *); +int32_t cnxt_smc_get_atr(void *cool_handle, unsigned char *buf, int32_t *buflen); +int32_t cnxt_smc_get_comm_parameters(void *cool_handle, CNXT_SMC_COMM *comm); +int32_t cnxt_smc_get_config_timeout(void *cool_handle, CNXT_SMC_TIMEOUT *timeout); +int32_t cnxt_smc_set_config_timeout(void *cool_handle, CNXT_SMC_TIMEOUT timeout); +int32_t cnxt_smc_set_convention(void *cool_handle, CNXT_SMC_CONVENTION conv); +int32_t cnxt_smc_start_pps(void *cool_handle, uint8_t *params, uint8_t *response, uint8_t *len, int32_t setfd); +int32_t cnxt_smc_get_F_D_factors(void *cool_handle, uint16_t *F, uint8_t *D); +int32_t cnxt_smc_set_F_D_factors(void *cool_handle, uint16_t F, uint8_t D); +int32_t cnxt_smc_read_write(void *cool_handle, int8_t async, uint8_t *sent, uint32_t size, uint8_t *cardbuffer, uint32_t *cardbuflen, int32_t rw_timeout, void *tag); +int32_t cnxt_smc_set_clock_freq(void *cool_handle, int32_t clk); +int32_t cnxt_smc_set_filter_protocol_bytes(void *cool_handle, int32_t enable); +int32_t cnxt_smc_close(void *cool_handle); + +/* Error checking */ +#define CNXT_STATUS_ERRORS 108 +static const char* const cnxt_status[CNXT_STATUS_ERRORS] = { + "OK", + "ALREADY_INIT", + "NOT_INIT", + "INTERNAL_ERROR", + "BAD_HANDLE", + "BAD_PARAMETER", + "BAD_LENGTH", + "BAD_UNIT", + "RESOURCE_ERROR", + "CLOSED_HANDLE", + "TIMEOUT", + "NOT_ATTACHED", + "NOT_SUPPORTED", + "REOPENED_HANDLE", + "INVALID", + "DESTROYED", + "DISCONNECTED", + "BUSY", + "IN_USE", + "CANCELLED", + "UNDEFINED", + "UNKNOWN", + "NOT_FOUND", + "NOT_AVAILABLE", + "NOT_COMPATIBLE", + "NOT_IMPLEMENTED", + "EMPTY", + "FULL", + "FAILURE", + "ALREADY_ATTACHED", + "ALREADY_DONE", + "ASLEEP", + "BAD_ATTACHMENT", + "BAD_COMMAND", + "BAD_GPIO", + "BAD_INDEX", + "BAD_MODE", + "BAD_PID", + "BAD_PLANE", + "BAD_PTR", + "BAD_RECT", + "BAD_RGN_HANDLE", + "BAD_SIZE", + "INT_HANDLED", + "INT_NOT_HANDLED", + "NOT_SET", + "NOT_HOOKED", + "CC_NOT_ENABLED", + "CLOSED_RGN", + "COMPLETE", + "DEMOD_ERROR", + "INVALID_NODE", + "DUPLICATE_NODE", + "HARDWARE_NOT_FOUND", + "HDCP_AUTH_FAILED", + "HDCP_BAD_BKSV", + "ILLEGAL_OPERATION", + "INCOMPATIBLE_FORMATS", + "INVALID_DEVICE", + "INVALID_EDGE", + "INVALID_NUMBER", + "INVALID_STATE", + "INVALID_TYPE", + "NO_BUFFER", + "NO_DESTINATION_BUF", + "NO_OSD", + "NO_PALETTE", + "NO_ACK", + "RECEIVER_HDMI_INCAPABLE", + "RECEIVER_NOT_ATTACHED", + "ADJUSTED", + "CLIPPED", + "CLIPRECT_ADJUSTED", + "NOT_ALIGNED", + "FIXUP_OK", + "FIXUP_OPTION_ERROR", + "FIXUP_ZERO_RECT", + "UNABLE_TO_FIXUP_AND_PRESERVE", + "UNABLE_TO_FIXUP_X", + "UNABLE_TO_FIXUP_Y", + "OUT_OF_BOUNDS", + "OUTSIDE_CLIP_RECT", + "RECT_CLIPPED", + "RECT_ENCLOSED", + "RECT_FIXED_UP", + "RECT_INCLUDES", + "RECT_NO_OVERLAP", + "RECT_OVERLAP", + "RECT_ZERO_AREA", + "SERVICE_LIST_NOT_READY", + "SERVICE_LIST_READY", + "STOPPED", + "SUSPENDED", + "TERMINATED", + "TOO_MUCH_DATA", + "WIPE_NONE", + "NOT_STOPPED", + "INT_NOT_COMPLETE", + "NOT_ALLOWED", + "DUPLICATE_PID", + "MAX_FILTERS_ATTACHED", + "HW_NOT_READY", + "OUTPUT_BUF_FULL", + "REJECTED", + "INVALID_PID", + "EOF", + "BOF", + "MISSING_DATA" +}; + +#define coolapi_check_error(label, ret) \ +do { \ + if (ret) { \ + cs_log("[%s:%d] %s: API ERROR %d (%s%s)", \ + __func__, \ + __LINE__ , \ + label, \ + ret, \ + ret >= CNXT_STATUS_ERRORS ? "UNKNOWN" : "CNXT_STATUS_", \ + ret >= CNXT_STATUS_ERRORS ? "" : cnxt_status[ret] \ + ); \ + } \ +} while(0) + +#if defined(HAVE_DVBAPI) && (defined(WITH_COOLAPI) || defined(WITH_SU980) || defined(WITH_COOLAPI2)) +extern void coolapi_open_all(void); +extern void coolapi_close_all(void); +#else +static inline void coolapi_open_all(void) { } +static inline void coolapi_close_all(void) { } +#endif + +#endif diff --git a/extapi/cygwin/SCardErr.h b/extapi/cygwin/SCardErr.h new file mode 100644 index 0000000..e3a67f8 --- /dev/null +++ b/extapi/cygwin/SCardErr.h @@ -0,0 +1,655 @@ +/* + scarderr.mc + + Error message codes from the Smart Card Resource Manager + These messages must be reconciled with winerror.w + They exist here to provide error messages on pre-Win2K systems. + +*/ +#ifndef SCARD_S_SUCCESS +// +// ============================= +// Facility SCARD Error Messages +// ============================= +// +#define SCARD_S_SUCCESS NO_ERROR +// +// Values are 32 bit values laid out as follows: +// +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---+-+-+-----------------------+-------------------------------+ +// |Sev|C|R| Facility | Code | +// +---+-+-+-----------------------+-------------------------------+ +// +// where +// +// Sev - is the severity code +// +// 00 - Success +// 01 - Informational +// 10 - Warning +// 11 - Error +// +// C - is the Customer code flag +// +// R - is a reserved bit +// +// Facility - is the facility code +// +// Code - is the facility's status code +// +// +// Define the facility codes +// +#define FACILITY_SYSTEM 0x0 +#define FACILITY_SCARD 0x10 + + +// +// Define the severity codes +// +#define STATUS_SEVERITY_WARNING 0x2 +#define STATUS_SEVERITY_INFORMATIONAL 0x1 +#define STATUS_SEVERITY_ERROR 0x3 + + +// +// MessageId: SCARD_F_INTERNAL_ERROR +// +// MessageText: +// +// An internal consistency check failed. +// +#define SCARD_F_INTERNAL_ERROR ((DWORD)0x80100001L) + +// +// MessageId: SCARD_E_CANCELLED +// +// MessageText: +// +// The action was cancelled by an SCardCancel request. +// +#define SCARD_E_CANCELLED ((DWORD)0x80100002L) + +// +// MessageId: SCARD_E_INVALID_HANDLE +// +// MessageText: +// +// The supplied handle was invalid. +// +#define SCARD_E_INVALID_HANDLE ((DWORD)0x80100003L) + +// +// MessageId: SCARD_E_INVALID_PARAMETER +// +// MessageText: +// +// One or more of the supplied parameters could not be properly interpreted. +// +#define SCARD_E_INVALID_PARAMETER ((DWORD)0x80100004L) + +// +// MessageId: SCARD_E_INVALID_TARGET +// +// MessageText: +// +// Registry startup information is missing or invalid. +// +#define SCARD_E_INVALID_TARGET ((DWORD)0x80100005L) + +// +// MessageId: SCARD_E_NO_MEMORY +// +// MessageText: +// +// Not enough memory available to complete this command. +// +#define SCARD_E_NO_MEMORY ((DWORD)0x80100006L) + +// +// MessageId: SCARD_F_WAITED_TOO_LONG +// +// MessageText: +// +// An internal consistency timer has expired. +// +#define SCARD_F_WAITED_TOO_LONG ((DWORD)0x80100007L) + +// +// MessageId: SCARD_E_INSUFFICIENT_BUFFER +// +// MessageText: +// +// The data buffer to receive returned data is too small for the returned data. +// +#define SCARD_E_INSUFFICIENT_BUFFER ((DWORD)0x80100008L) + +// +// MessageId: SCARD_E_UNKNOWN_READER +// +// MessageText: +// +// The specified reader name is not recognized. +// +#define SCARD_E_UNKNOWN_READER ((DWORD)0x80100009L) + +// +// MessageId: SCARD_E_TIMEOUT +// +// MessageText: +// +// The user-specified timeout value has expired. +// +#define SCARD_E_TIMEOUT ((DWORD)0x8010000AL) + +// +// MessageId: SCARD_E_SHARING_VIOLATION +// +// MessageText: +// +// The smart card cannot be accessed because of other connections outstanding. +// +#define SCARD_E_SHARING_VIOLATION ((DWORD)0x8010000BL) + +// +// MessageId: SCARD_E_NO_SMARTCARD +// +// MessageText: +// +// The operation requires a Smart Card, but no Smart Card is currently in the device. +// +#define SCARD_E_NO_SMARTCARD ((DWORD)0x8010000CL) + +// +// MessageId: SCARD_E_UNKNOWN_CARD +// +// MessageText: +// +// The specified smart card name is not recognized. +// +#define SCARD_E_UNKNOWN_CARD ((DWORD)0x8010000DL) + +// +// MessageId: SCARD_E_CANT_DISPOSE +// +// MessageText: +// +// The system could not dispose of the media in the requested manner. +// +#define SCARD_E_CANT_DISPOSE ((DWORD)0x8010000EL) + +// +// MessageId: SCARD_E_PROTO_MISMATCH +// +// MessageText: +// +// The requested protocols are incompatible with the protocol currently in use with the smart card. +// +#define SCARD_E_PROTO_MISMATCH ((DWORD)0x8010000FL) + +// +// MessageId: SCARD_E_NOT_READY +// +// MessageText: +// +// The reader or smart card is not ready to accept commands. +// +#define SCARD_E_NOT_READY ((DWORD)0x80100010L) + +// +// MessageId: SCARD_E_INVALID_VALUE +// +// MessageText: +// +// One or more of the supplied parameters values could not be properly interpreted. +// +#define SCARD_E_INVALID_VALUE ((DWORD)0x80100011L) + +// +// MessageId: SCARD_E_SYSTEM_CANCELLED +// +// MessageText: +// +// The action was cancelled by the system, presumably to log off or shut down. +// +#define SCARD_E_SYSTEM_CANCELLED ((DWORD)0x80100012L) + +// +// MessageId: SCARD_F_COMM_ERROR +// +// MessageText: +// +// An internal communications error has been detected. +// +#define SCARD_F_COMM_ERROR ((DWORD)0x80100013L) + +// +// MessageId: SCARD_F_UNKNOWN_ERROR +// +// MessageText: +// +// An internal error has been detected, but the source is unknown. +// +#define SCARD_F_UNKNOWN_ERROR ((DWORD)0x80100014L) + +// +// MessageId: SCARD_E_INVALID_ATR +// +// MessageText: +// +// An ATR obtained from the registry is not a valid ATR string. +// +#define SCARD_E_INVALID_ATR ((DWORD)0x80100015L) + +// +// MessageId: SCARD_E_NOT_TRANSACTED +// +// MessageText: +// +// An attempt was made to end a non-existent transaction. +// +#define SCARD_E_NOT_TRANSACTED ((DWORD)0x80100016L) + +// +// MessageId: SCARD_E_READER_UNAVAILABLE +// +// MessageText: +// +// The specified reader is not currently available for use. +// +#define SCARD_E_READER_UNAVAILABLE ((DWORD)0x80100017L) + +// +// MessageId: SCARD_P_SHUTDOWN +// +// MessageText: +// +// The operation has been aborted to allow the server application to exit. +// +#define SCARD_P_SHUTDOWN ((DWORD)0x80100018L) + +// +// MessageId: SCARD_E_PCI_TOO_SMALL +// +// MessageText: +// +// The PCI Receive buffer was too small. +// +#define SCARD_E_PCI_TOO_SMALL ((DWORD)0x80100019L) + +// +// MessageId: SCARD_E_READER_UNSUPPORTED +// +// MessageText: +// +// The reader driver does not meet minimal requirements for support. +// +#define SCARD_E_READER_UNSUPPORTED ((DWORD)0x8010001AL) + +// +// MessageId: SCARD_E_DUPLICATE_READER +// +// MessageText: +// +// The reader driver did not produce a unique reader name. +// +#define SCARD_E_DUPLICATE_READER ((DWORD)0x8010001BL) + +// +// MessageId: SCARD_E_CARD_UNSUPPORTED +// +// MessageText: +// +// The smart card does not meet minimal requirements for support. +// +#define SCARD_E_CARD_UNSUPPORTED ((DWORD)0x8010001CL) + +// +// MessageId: SCARD_E_NO_SERVICE +// +// MessageText: +// +// The Smart card resource manager is not running. +// +#define SCARD_E_NO_SERVICE ((DWORD)0x8010001DL) + +// +// MessageId: SCARD_E_SERVICE_STOPPED +// +// MessageText: +// +// The Smart card resource manager has shut down. +// +#define SCARD_E_SERVICE_STOPPED ((DWORD)0x8010001EL) + +// +// MessageId: SCARD_E_UNEXPECTED +// +// MessageText: +// +// An unexpected card error has occurred. +// +#define SCARD_E_UNEXPECTED ((DWORD)0x8010001FL) + +// +// MessageId: SCARD_E_ICC_INSTALLATION +// +// MessageText: +// +// No Primary Provider can be found for the smart card. +// +#define SCARD_E_ICC_INSTALLATION ((DWORD)0x80100020L) + +// +// MessageId: SCARD_E_ICC_CREATEORDER +// +// MessageText: +// +// The requested order of object creation is not supported. +// +#define SCARD_E_ICC_CREATEORDER ((DWORD)0x80100021L) + +// +// MessageId: SCARD_E_UNSUPPORTED_FEATURE +// +// MessageText: +// +// This smart card does not support the requested feature. +// +#define SCARD_E_UNSUPPORTED_FEATURE ((DWORD)0x80100022L) + +// +// MessageId: SCARD_E_DIR_NOT_FOUND +// +// MessageText: +// +// The identified directory does not exist in the smart card. +// +#define SCARD_E_DIR_NOT_FOUND ((DWORD)0x80100023L) + +// +// MessageId: SCARD_E_FILE_NOT_FOUND +// +// MessageText: +// +// The identified file does not exist in the smart card. +// +#define SCARD_E_FILE_NOT_FOUND ((DWORD)0x80100024L) + +// +// MessageId: SCARD_E_NO_DIR +// +// MessageText: +// +// The supplied path does not represent a smart card directory. +// +#define SCARD_E_NO_DIR ((DWORD)0x80100025L) + +// +// MessageId: SCARD_E_NO_FILE +// +// MessageText: +// +// The supplied path does not represent a smart card file. +// +#define SCARD_E_NO_FILE ((DWORD)0x80100026L) + +// +// MessageId: SCARD_E_NO_ACCESS +// +// MessageText: +// +// Access is denied to this file. +// +#define SCARD_E_NO_ACCESS ((DWORD)0x80100027L) + +// +// MessageId: SCARD_E_WRITE_TOO_MANY +// +// MessageText: +// +// The smartcard does not have enough memory to store the information. +// +#define SCARD_E_WRITE_TOO_MANY ((DWORD)0x80100028L) + +// +// MessageId: SCARD_E_BAD_SEEK +// +// MessageText: +// +// There was an error trying to set the smart card file object pointer. +// +#define SCARD_E_BAD_SEEK ((DWORD)0x80100029L) + +// +// MessageId: SCARD_E_INVALID_CHV +// +// MessageText: +// +// The supplied PIN is incorrect. +// +#define SCARD_E_INVALID_CHV ((DWORD)0x8010002AL) + +// +// MessageId: SCARD_E_UNKNOWN_RES_MNG +// +// MessageText: +// +// An unrecognized error code was returned from a layered component. +// +#define SCARD_E_UNKNOWN_RES_MNG ((DWORD)0x8010002BL) + +// +// MessageId: SCARD_E_NO_SUCH_CERTIFICATE +// +// MessageText: +// +// The requested certificate does not exist. +// +#define SCARD_E_NO_SUCH_CERTIFICATE ((DWORD)0x8010002CL) + +// +// MessageId: SCARD_E_CERTIFICATE_UNAVAILABLE +// +// MessageText: +// +// The requested certificate could not be obtained. +// +#define SCARD_E_CERTIFICATE_UNAVAILABLE ((DWORD)0x8010002DL) + +// +// MessageId: SCARD_E_NO_READERS_AVAILABLE +// +// MessageText: +// +// Cannot find a smart card reader. +// +#define SCARD_E_NO_READERS_AVAILABLE ((DWORD)0x8010002EL) + +// +// MessageId: SCARD_E_COMM_DATA_LOST +// +// MessageText: +// +// A communications error with the smart card has been detected. Retry the operation. +// +#define SCARD_E_COMM_DATA_LOST ((DWORD)0x8010002FL) + +// +// MessageId: SCARD_E_NO_KEY_CONTAINER +// +// MessageText: +// +// The requested key container does not exist on the smart card. +// +#define SCARD_E_NO_KEY_CONTAINER ((DWORD)0x80100030L) + +// +// MessageId: SCARD_E_SERVER_TOO_BUSY +// +// MessageText: +// +// The Smart card resource manager is too busy to complete this operation. +// +#define SCARD_E_SERVER_TOO_BUSY ((DWORD)0x80100031L) + +// +// MessageId: SCARD_E_PIN_CACHE_EXPIRED +// +// MessageText: +// +// The smart card PIN cache has expired. +// +#define SCARD_E_PIN_CACHE_EXPIRED ((DWORD)0x80100032L) + +// +// MessageId: SCARD_E_NO_PIN_CACHE +// +// MessageText: +// +// The smart card PIN cannot be cached. +// +#define SCARD_E_NO_PIN_CACHE ((DWORD)0x80100033L) + +// +// MessageId: SCARD_E_READ_ONLY_CARD +// +// MessageText: +// +// The smart card is read only and cannot be written to. +// +#define SCARD_E_READ_ONLY_CARD ((DWORD)0x80100034L) + +// +// These are warning codes. +// +// +// MessageId: SCARD_W_UNSUPPORTED_CARD +// +// MessageText: +// +// The reader cannot communicate with the smart card, due to ATR configuration conflicts. +// +#define SCARD_W_UNSUPPORTED_CARD ((DWORD)0x80100065L) + +// +// MessageId: SCARD_W_UNRESPONSIVE_CARD +// +// MessageText: +// +// The smart card is not responding to a reset. +// +#define SCARD_W_UNRESPONSIVE_CARD ((DWORD)0x80100066L) + +// +// MessageId: SCARD_W_UNPOWERED_CARD +// +// MessageText: +// +// Power has been removed from the smart card, so that further communication is not possible. +// +#define SCARD_W_UNPOWERED_CARD ((DWORD)0x80100067L) + +// +// MessageId: SCARD_W_RESET_CARD +// +// MessageText: +// +// The smart card has been reset, so any shared state information is invalid. +// +#define SCARD_W_RESET_CARD ((DWORD)0x80100068L) + +// +// MessageId: SCARD_W_REMOVED_CARD +// +// MessageText: +// +// The smart card has been removed, so that further communication is not possible. +// +#define SCARD_W_REMOVED_CARD ((DWORD)0x80100069L) + +// +// MessageId: SCARD_W_SECURITY_VIOLATION +// +// MessageText: +// +// Access was denied because of a security violation. +// +#define SCARD_W_SECURITY_VIOLATION ((DWORD)0x8010006AL) + +// +// MessageId: SCARD_W_WRONG_CHV +// +// MessageText: +// +// The card cannot be accessed because the wrong PIN was presented. +// +#define SCARD_W_WRONG_CHV ((DWORD)0x8010006BL) + +// +// MessageId: SCARD_W_CHV_BLOCKED +// +// MessageText: +// +// The card cannot be accessed because the maximum number of PIN entry attempts has been reached. +// +#define SCARD_W_CHV_BLOCKED ((DWORD)0x8010006CL) + +// +// MessageId: SCARD_W_EOF +// +// MessageText: +// +// The end of the smart card file has been reached. +// +#define SCARD_W_EOF ((DWORD)0x8010006DL) + +// +// MessageId: SCARD_W_CANCELLED_BY_USER +// +// MessageText: +// +// The action was cancelled by the user. +// +#define SCARD_W_CANCELLED_BY_USER ((DWORD)0x8010006EL) + +// +// MessageId: SCARD_W_CARD_NOT_AUTHENTICATED +// +// MessageText: +// +// No PIN was presented to the smart card. +// +#define SCARD_W_CARD_NOT_AUTHENTICATED ((DWORD)0x8010006FL) + +// +// MessageId: SCARD_W_CACHE_ITEM_NOT_FOUND +// +// MessageText: +// +// The requested item could not be found in the cache. +// +#define SCARD_W_CACHE_ITEM_NOT_FOUND ((DWORD)0x80100070L) + +// +// MessageId: SCARD_W_CACHE_ITEM_STALE +// +// MessageText: +// +// The requested cache item is too old and was deleted from the cache. +// +#define SCARD_W_CACHE_ITEM_STALE ((DWORD)0x80100071L) + +// +// MessageId: SCARD_W_CACHE_ITEM_TOO_BIG +// +// MessageText: +// +// The new cache item exceeds the maximum per-item size defined for the cache. +// +#define SCARD_W_CACHE_ITEM_TOO_BIG ((DWORD)0x80100072L) + +#endif // SCARD_S_SUCCESS + diff --git a/extapi/cygwin/WinSCard.h b/extapi/cygwin/WinSCard.h new file mode 100644 index 0000000..f4149d1 --- /dev/null +++ b/extapi/cygwin/WinSCard.h @@ -0,0 +1,1137 @@ +/*++ + +Copyright (c) 1996 Microsoft Corporation + +Module Name: + + WinSCard + +Abstract: + + This header file provides the definitions and symbols necessary for an + Application or Smart Card Service Provider to access the Smartcard + Subsystem. + +Environment: + + Win32 + +Notes: + +--*/ + +#ifndef _WINSCARD_H_ +#define _WINSCARD_H_ + +#if defined (_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include +#include +#include "winsmcrd.h" +#ifndef SCARD_S_SUCCESS +#include "SCardErr.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _LPCBYTE_DEFINED +#define _LPCBYTE_DEFINED + typedef const BYTE *LPCBYTE; +#endif +#ifndef _LPCVOID_DEFINED +#define _LPCVOID_DEFINED + typedef const VOID *LPCVOID; +#endif + +#ifndef WINSCARDAPI +#define WINSCARDAPI +#endif +#ifndef WINSCARDDATA +#define WINSCARDDATA __declspec(dllimport) +#endif + + /* In clr:pure we cannot mark data export with dllimport. + * We should add small functions which returns the value of + * the global. + */ +#if !defined(_M_CEE_PURE) + WINSCARDDATA extern const SCARD_IO_REQUEST + g_rgSCardT0Pci, + g_rgSCardT1Pci, + g_rgSCardRawPci; +#define SCARD_PCI_T0 (&g_rgSCardT0Pci) +#define SCARD_PCI_T1 (&g_rgSCardT1Pci) +#define SCARD_PCI_RAW (&g_rgSCardRawPci) +#endif + +// +//////////////////////////////////////////////////////////////////////////////// +// +// Service Manager Access Services +// +// The following services are used to manage user and terminal contexts for +// Smart Cards. +// + typedef ULONG_PTR SCARDCONTEXT; + typedef SCARDCONTEXT *PSCARDCONTEXT, *LPSCARDCONTEXT; + + typedef ULONG_PTR SCARDHANDLE; + typedef SCARDHANDLE *PSCARDHANDLE, *LPSCARDHANDLE; + +#define SCARD_AUTOALLOCATE (DWORD)(-1) + +#define SCARD_SCOPE_USER 0 // The context is a user context, and any + // database operations are performed within the + // domain of the user. +#define SCARD_SCOPE_TERMINAL 1 // The context is that of the current terminal, + // and any database operations are performed + // within the domain of that terminal. (The + // calling application must have appropriate + // access permissions for any database actions.) +#define SCARD_SCOPE_SYSTEM 2 // The context is the system context, and any + // database operations are performed within the + // domain of the system. (The calling + // application must have appropriate access + // permissions for any database actions.) + + extern WINSCARDAPI LONG WINAPI + SCardEstablishContext( + __in DWORD dwScope, + __reserved LPCVOID pvReserved1, + __reserved LPCVOID pvReserved2, + __out LPSCARDCONTEXT phContext); + + extern WINSCARDAPI LONG WINAPI + SCardReleaseContext( + __in SCARDCONTEXT hContext); + + extern WINSCARDAPI LONG WINAPI + SCardIsValidContext( + __in SCARDCONTEXT hContext); + + +// +//////////////////////////////////////////////////////////////////////////////// +// +// Smart Card Database Management Services +// +// The following services provide for managing the Smart Card Database. +// + +#define SCARD_ALL_READERS TEXT("SCard$AllReaders\000") +#define SCARD_DEFAULT_READERS TEXT("SCard$DefaultReaders\000") +#define SCARD_LOCAL_READERS TEXT("SCard$LocalReaders\000") +#define SCARD_SYSTEM_READERS TEXT("SCard$SystemReaders\000") + +#define SCARD_PROVIDER_PRIMARY 1 // Primary Provider Id +#define SCARD_PROVIDER_CSP 2 // Crypto Service Provider Id +#define SCARD_PROVIDER_KSP 3 // Key Storage Provider Id + + +// +// Database Reader routines +// + + extern WINSCARDAPI LONG WINAPI + SCardListReaderGroupsA( + __in SCARDCONTEXT hContext, + __nullnullterminated __out_ecount_opt(*pcchGroups) LPSTR mszGroups, + __inout LPDWORD pcchGroups); + extern WINSCARDAPI LONG WINAPI + SCardListReaderGroupsW( + __in SCARDCONTEXT hContext, + __nullnullterminated __out_ecount_opt(*pcchGroups) LPWSTR mszGroups, + __inout LPDWORD pcchGroups); +#ifdef UNICODE +#define SCardListReaderGroups SCardListReaderGroupsW +#else +#define SCardListReaderGroups SCardListReaderGroupsA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardListReadersA( + __in SCARDCONTEXT hContext, + __in_opt LPCSTR mszGroups, + __nullnullterminated __out_ecount_opt(*pcchReaders) LPSTR mszReaders, + __inout LPDWORD pcchReaders); + extern WINSCARDAPI LONG WINAPI + SCardListReadersW( + __in SCARDCONTEXT hContext, + __in_opt LPCWSTR mszGroups, + __nullnullterminated __out_ecount_opt(*pcchReaders) LPWSTR mszReaders, + __inout LPDWORD pcchReaders); +#ifdef UNICODE +#define SCardListReaders SCardListReadersW +#else +#define SCardListReaders SCardListReadersA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardListCardsA( + __in SCARDCONTEXT hContext, + __in_opt LPCBYTE pbAtr, + __in_ecount_opt(cguidInterfaceCount) LPCGUID rgquidInterfaces, + __in DWORD cguidInterfaceCount, + __nullnullterminated __out_ecount_opt(*pcchCards) LPSTR mszCards, + __inout LPDWORD pcchCards); + extern WINSCARDAPI LONG WINAPI + SCardListCardsW( + __in SCARDCONTEXT hContext, + __in_opt LPCBYTE pbAtr, + __in_ecount_opt(cguidInterfaceCount) LPCGUID rgquidInterfaces, + __in DWORD cguidInterfaceCount, + __nullnullterminated __out_ecount_opt(*pcchCards) LPWSTR mszCards, + __inout LPDWORD pcchCards); +#ifdef UNICODE +#define SCardListCards SCardListCardsW +#else +#define SCardListCards SCardListCardsA +#endif // !UNICODE +// +// NOTE: The routine SCardListCards name differs from the PC/SC definition. +// It should be: +// +// extern WINSCARDAPI LONG WINAPI +// SCardListCardTypes( +// __in SCARDCONTEXT hContext, +// __in_opt LPCBYTE pbAtr, +// __in_opt LPCGUID rgquidInterfaces, +// __in DWORD cguidInterfaceCount, +// __out_opt LPTSTR mszCards, +// __inout LPDWORD pcchCards); +// +// Here's a work-around MACRO: +#define SCardListCardTypes SCardListCards + + extern WINSCARDAPI LONG WINAPI + SCardListInterfacesA( + __in SCARDCONTEXT hContext, + __in LPCSTR szCard, + __out LPGUID pguidInterfaces, + __inout LPDWORD pcguidInterfaces); + extern WINSCARDAPI LONG WINAPI + SCardListInterfacesW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szCard, + __out LPGUID pguidInterfaces, + __inout LPDWORD pcguidInterfaces); +#ifdef UNICODE +#define SCardListInterfaces SCardListInterfacesW +#else +#define SCardListInterfaces SCardListInterfacesA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardGetProviderIdA( + __in SCARDCONTEXT hContext, + __in LPCSTR szCard, + __out LPGUID pguidProviderId); + extern WINSCARDAPI LONG WINAPI + SCardGetProviderIdW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szCard, + __out LPGUID pguidProviderId); +#ifdef UNICODE +#define SCardGetProviderId SCardGetProviderIdW +#else +#define SCardGetProviderId SCardGetProviderIdA +#endif // !UNICODE +// +// NOTE: The routine SCardGetProviderId in this implementation uses GUIDs. +// The PC/SC definition uses BYTEs. +// + + extern WINSCARDAPI LONG WINAPI + SCardGetCardTypeProviderNameA( + __in SCARDCONTEXT hContext, + __in LPCSTR szCardName, + __in DWORD dwProviderId, + __out_ecount_opt(*pcchProvider) LPSTR szProvider, + __inout LPDWORD pcchProvider); + extern WINSCARDAPI LONG WINAPI + SCardGetCardTypeProviderNameW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szCardName, + __in DWORD dwProviderId, + __out_ecount_opt(*pcchProvider) LPWSTR szProvider, + __inout LPDWORD pcchProvider); +#ifdef UNICODE +#define SCardGetCardTypeProviderName SCardGetCardTypeProviderNameW +#else +#define SCardGetCardTypeProviderName SCardGetCardTypeProviderNameA +#endif // !UNICODE +// +// NOTE: This routine is an extension to the PC/SC definitions. +// + + +// +// Database Writer routines +// + + extern WINSCARDAPI LONG WINAPI + SCardIntroduceReaderGroupA( + __in SCARDCONTEXT hContext, + __in LPCSTR szGroupName); + extern WINSCARDAPI LONG WINAPI + SCardIntroduceReaderGroupW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szGroupName); +#ifdef UNICODE +#define SCardIntroduceReaderGroup SCardIntroduceReaderGroupW +#else +#define SCardIntroduceReaderGroup SCardIntroduceReaderGroupA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardForgetReaderGroupA( + __in SCARDCONTEXT hContext, + __in LPCSTR szGroupName); + extern WINSCARDAPI LONG WINAPI + SCardForgetReaderGroupW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szGroupName); +#ifdef UNICODE +#define SCardForgetReaderGroup SCardForgetReaderGroupW +#else +#define SCardForgetReaderGroup SCardForgetReaderGroupA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardIntroduceReaderA( + __in SCARDCONTEXT hContext, + __in LPCSTR szReaderName, + __in LPCSTR szDeviceName); + extern WINSCARDAPI LONG WINAPI + SCardIntroduceReaderW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szReaderName, + __in LPCWSTR szDeviceName); +#ifdef UNICODE +#define SCardIntroduceReader SCardIntroduceReaderW +#else +#define SCardIntroduceReader SCardIntroduceReaderA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardForgetReaderA( + __in SCARDCONTEXT hContext, + __in LPCSTR szReaderName); + extern WINSCARDAPI LONG WINAPI + SCardForgetReaderW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szReaderName); +#ifdef UNICODE +#define SCardForgetReader SCardForgetReaderW +#else +#define SCardForgetReader SCardForgetReaderA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardAddReaderToGroupA( + __in SCARDCONTEXT hContext, + __in LPCSTR szReaderName, + __in LPCSTR szGroupName); + extern WINSCARDAPI LONG WINAPI + SCardAddReaderToGroupW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szReaderName, + __in LPCWSTR szGroupName); +#ifdef UNICODE +#define SCardAddReaderToGroup SCardAddReaderToGroupW +#else +#define SCardAddReaderToGroup SCardAddReaderToGroupA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardRemoveReaderFromGroupA( + __in SCARDCONTEXT hContext, + __in LPCSTR szReaderName, + __in LPCSTR szGroupName); + extern WINSCARDAPI LONG WINAPI + SCardRemoveReaderFromGroupW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szReaderName, + __in LPCWSTR szGroupName); +#ifdef UNICODE +#define SCardRemoveReaderFromGroup SCardRemoveReaderFromGroupW +#else +#define SCardRemoveReaderFromGroup SCardRemoveReaderFromGroupA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardIntroduceCardTypeA( + __in SCARDCONTEXT hContext, + __in LPCSTR szCardName, + __in_opt LPCGUID pguidPrimaryProvider, + __in_opt LPCGUID rgguidInterfaces, + __in DWORD dwInterfaceCount, + __in LPCBYTE pbAtr, + __in LPCBYTE pbAtrMask, + __in DWORD cbAtrLen); + extern WINSCARDAPI LONG WINAPI + SCardIntroduceCardTypeW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szCardName, + __in_opt LPCGUID pguidPrimaryProvider, + __in_opt LPCGUID rgguidInterfaces, + __in DWORD dwInterfaceCount, + __in LPCBYTE pbAtr, + __in LPCBYTE pbAtrMask, + __in DWORD cbAtrLen); +#ifdef UNICODE +#define SCardIntroduceCardType SCardIntroduceCardTypeW +#else +#define SCardIntroduceCardType SCardIntroduceCardTypeA +#endif // !UNICODE +// +// NOTE: The routine SCardIntroduceCardType's parameters' order differs from +// the PC/SC definition. It should be: +// +// extern WINSCARDAPI LONG WINAPI +// SCardIntroduceCardType( +// __in SCARDCONTEXT hContext, +// __in LPCTSTR szCardName, +// __in LPCBYTE pbAtr, +// __in LPCBYTE pbAtrMask, +// __in DWORD cbAtrLen, +// __in_opt LPCGUID pguidPrimaryProvider, +// __in_opt LPCGUID rgguidInterfaces, +// __in DWORD dwInterfaceCount); +// +// Here's a work-around MACRO: +#define PCSCardIntroduceCardType(hContext, szCardName, pbAtr, pbAtrMask, cbAtrLen, pguidPrimaryProvider, rgguidInterfaces, dwInterfaceCount) \ + SCardIntroduceCardType(hContext, szCardName, pguidPrimaryProvider, rgguidInterfaces, dwInterfaceCount, pbAtr, pbAtrMask, cbAtrLen) + + extern WINSCARDAPI LONG WINAPI + SCardSetCardTypeProviderNameA( + __in SCARDCONTEXT hContext, + __in LPCSTR szCardName, + __in DWORD dwProviderId, + __in LPCSTR szProvider); + extern WINSCARDAPI LONG WINAPI + SCardSetCardTypeProviderNameW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szCardName, + __in DWORD dwProviderId, + __in LPCWSTR szProvider); +#ifdef UNICODE +#define SCardSetCardTypeProviderName SCardSetCardTypeProviderNameW +#else +#define SCardSetCardTypeProviderName SCardSetCardTypeProviderNameA +#endif // !UNICODE +// +// NOTE: This routine is an extention to the PC/SC specifications. +// + + extern WINSCARDAPI LONG WINAPI + SCardForgetCardTypeA( + __in SCARDCONTEXT hContext, + __in LPCSTR szCardName); + extern WINSCARDAPI LONG WINAPI + SCardForgetCardTypeW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szCardName); +#ifdef UNICODE +#define SCardForgetCardType SCardForgetCardTypeW +#else +#define SCardForgetCardType SCardForgetCardTypeA +#endif // !UNICODE + + +// +//////////////////////////////////////////////////////////////////////////////// +// +// Service Manager Support Routines +// +// The following services are supplied to simplify the use of the Service +// Manager API. +// + + extern WINSCARDAPI LONG WINAPI + SCardFreeMemory( + __in SCARDCONTEXT hContext, + __in LPCVOID pvMem); + +#if (NTDDI_VERSION >= NTDDI_WINXP) + extern WINSCARDAPI HANDLE WINAPI + SCardAccessStartedEvent(void); + + extern WINSCARDAPI void WINAPI + SCardReleaseStartedEvent(void); +#endif // (NTDDI_VERSION >= NTDDI_WINXP) + +// +//////////////////////////////////////////////////////////////////////////////// +// +// Reader Services +// +// The following services supply means for tracking cards within readers. +// + + typedef struct { + LPCSTR szReader; // reader name + LPVOID pvUserData; // user defined data + DWORD dwCurrentState; // current state of reader at time of call + DWORD dwEventState; // state of reader after state change + DWORD cbAtr; // Number of bytes in the returned ATR. + BYTE rgbAtr[36]; // Atr of inserted card, (extra alignment bytes) + } SCARD_READERSTATEA, *PSCARD_READERSTATEA, *LPSCARD_READERSTATEA; + typedef struct { + LPCWSTR szReader; // reader name + LPVOID pvUserData; // user defined data + DWORD dwCurrentState; // current state of reader at time of call + DWORD dwEventState; // state of reader after state change + DWORD cbAtr; // Number of bytes in the returned ATR. + BYTE rgbAtr[36]; // Atr of inserted card, (extra alignment bytes) + } SCARD_READERSTATEW, *PSCARD_READERSTATEW, *LPSCARD_READERSTATEW; +#ifdef UNICODE + typedef SCARD_READERSTATEW SCARD_READERSTATE; + typedef PSCARD_READERSTATEW PSCARD_READERSTATE; + typedef LPSCARD_READERSTATEW LPSCARD_READERSTATE; +#else + typedef SCARD_READERSTATEA SCARD_READERSTATE; + typedef PSCARD_READERSTATEA PSCARD_READERSTATE; + typedef LPSCARD_READERSTATEA LPSCARD_READERSTATE; +#endif // UNICODE + +// Backwards compatibility macros +#define SCARD_READERSTATE_A SCARD_READERSTATEA +#define SCARD_READERSTATE_W SCARD_READERSTATEW +#define PSCARD_READERSTATE_A PSCARD_READERSTATEA +#define PSCARD_READERSTATE_W PSCARD_READERSTATEW +#define LPSCARD_READERSTATE_A LPSCARD_READERSTATEA +#define LPSCARD_READERSTATE_W LPSCARD_READERSTATEW + +#define SCARD_STATE_UNAWARE 0x00000000 // The application is unaware of the + // current state, and would like to + // know. The use of this value + // results in an immediate return + // from state transition monitoring + // services. This is represented by + // all bits set to zero. +#define SCARD_STATE_IGNORE 0x00000001 // The application requested that + // this reader be ignored. No other + // bits will be set. +#define SCARD_STATE_CHANGED 0x00000002 // This implies that there is a + // difference between the state + // believed by the application, and + // the state known by the Service + // Manager. When this bit is set, + // the application may assume a + // significant state change has + // occurred on this reader. +#define SCARD_STATE_UNKNOWN 0x00000004 // This implies that the given + // reader name is not recognized by + // the Service Manager. If this bit + // is set, then SCARD_STATE_CHANGED + // and SCARD_STATE_IGNORE will also + // be set. +#define SCARD_STATE_UNAVAILABLE 0x00000008 // This implies that the actual + // state of this reader is not + // available. If this bit is set, + // then all the following bits are + // clear. +#define SCARD_STATE_EMPTY 0x00000010 // This implies that there is not + // card in the reader. If this bit + // is set, all the following bits + // will be clear. +#define SCARD_STATE_PRESENT 0x00000020 // This implies that there is a card + // in the reader. +#define SCARD_STATE_ATRMATCH 0x00000040 // This implies that there is a card + // in the reader with an ATR + // matching one of the target cards. + // If this bit is set, + // SCARD_STATE_PRESENT will also be + // set. This bit is only returned + // on the SCardLocateCard() service. +#define SCARD_STATE_EXCLUSIVE 0x00000080 // This implies that the card in the + // reader is allocated for exclusive + // use by another application. If + // this bit is set, + // SCARD_STATE_PRESENT will also be + // set. +#define SCARD_STATE_INUSE 0x00000100 // This implies that the card in the + // reader is in use by one or more + // other applications, but may be + // connected to in shared mode. If + // this bit is set, + // SCARD_STATE_PRESENT will also be + // set. +#define SCARD_STATE_MUTE 0x00000200 // This implies that the card in the + // reader is unresponsive or not + // supported by the reader or + // software. +#define SCARD_STATE_UNPOWERED 0x00000400 // This implies that the card in the + // reader has not been powered up. + + extern WINSCARDAPI LONG WINAPI + SCardLocateCardsA( + __in SCARDCONTEXT hContext, + __in LPCSTR mszCards, + __inout LPSCARD_READERSTATEA rgReaderStates, + __in DWORD cReaders); + extern WINSCARDAPI LONG WINAPI + SCardLocateCardsW( + __in SCARDCONTEXT hContext, + __in LPCWSTR mszCards, + __inout LPSCARD_READERSTATEW rgReaderStates, + __in DWORD cReaders); +#ifdef UNICODE +#define SCardLocateCards SCardLocateCardsW +#else +#define SCardLocateCards SCardLocateCardsA +#endif // !UNICODE + +#if (NTDDI_VERSION >= NTDDI_WINXP) + typedef struct _SCARD_ATRMASK { + DWORD cbAtr; // Number of bytes in the ATR and the mask. + BYTE rgbAtr[36]; // Atr of card (extra alignment bytes) + BYTE rgbMask[36]; // Mask for the Atr (extra alignment bytes) + } SCARD_ATRMASK, *PSCARD_ATRMASK, *LPSCARD_ATRMASK; + + + extern WINSCARDAPI LONG WINAPI + SCardLocateCardsByATRA( + __in SCARDCONTEXT hContext, + __in LPSCARD_ATRMASK rgAtrMasks, + __in DWORD cAtrs, + __inout LPSCARD_READERSTATEA rgReaderStates, + __in DWORD cReaders); + extern WINSCARDAPI LONG WINAPI + SCardLocateCardsByATRW( + __in SCARDCONTEXT hContext, + __in LPSCARD_ATRMASK rgAtrMasks, + __in DWORD cAtrs, + __inout LPSCARD_READERSTATEW rgReaderStates, + __in DWORD cReaders); +#ifdef UNICODE +#define SCardLocateCardsByATR SCardLocateCardsByATRW +#else +#define SCardLocateCardsByATR SCardLocateCardsByATRA +#endif // !UNICODE +#endif // (NTDDI_VERSION >= NTDDI_WINXP) + + extern WINSCARDAPI LONG WINAPI + SCardGetStatusChangeA( + __in SCARDCONTEXT hContext, + __in DWORD dwTimeout, + __inout LPSCARD_READERSTATEA rgReaderStates, + __in DWORD cReaders); + extern WINSCARDAPI LONG WINAPI + SCardGetStatusChangeW( + __in SCARDCONTEXT hContext, + __in DWORD dwTimeout, + __inout LPSCARD_READERSTATEW rgReaderStates, + __in DWORD cReaders); +#ifdef UNICODE +#define SCardGetStatusChange SCardGetStatusChangeW +#else +#define SCardGetStatusChange SCardGetStatusChangeA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardCancel( + __in SCARDCONTEXT hContext); + + +// +//////////////////////////////////////////////////////////////////////////////// +// +// Card/Reader Communication Services +// +// The following services provide means for communication with the card. +// + +#define SCARD_SHARE_EXCLUSIVE 1 // This application is not willing to share this + // card with other applications. +#define SCARD_SHARE_SHARED 2 // This application is willing to share this + // card with other applications. +#define SCARD_SHARE_DIRECT 3 // This application demands direct control of + // the reader, so it is not available to other + // applications. + +#define SCARD_LEAVE_CARD 0 // Don't do anything special on close +#define SCARD_RESET_CARD 1 // Reset the card on close +#define SCARD_UNPOWER_CARD 2 // Power down the card on close +#define SCARD_EJECT_CARD 3 // Eject the card on close + + extern WINSCARDAPI LONG WINAPI + SCardConnectA( + __in SCARDCONTEXT hContext, + __in LPCSTR szReader, + __in DWORD dwShareMode, + __in DWORD dwPreferredProtocols, + __out LPSCARDHANDLE phCard, + __out LPDWORD pdwActiveProtocol); + extern WINSCARDAPI LONG WINAPI + SCardConnectW( + __in SCARDCONTEXT hContext, + __in LPCWSTR szReader, + __in DWORD dwShareMode, + __in DWORD dwPreferredProtocols, + __out LPSCARDHANDLE phCard, + __out LPDWORD pdwActiveProtocol); +#ifdef UNICODE +#define SCardConnect SCardConnectW +#else +#define SCardConnect SCardConnectA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardReconnect( + __in SCARDHANDLE hCard, + __in DWORD dwShareMode, + __in DWORD dwPreferredProtocols, + __in DWORD dwInitialization, + __out_opt LPDWORD pdwActiveProtocol); + + extern WINSCARDAPI LONG WINAPI + SCardDisconnect( + __in SCARDHANDLE hCard, + __in DWORD dwDisposition); + + extern WINSCARDAPI LONG WINAPI + SCardBeginTransaction( + __in SCARDHANDLE hCard); + + extern WINSCARDAPI LONG WINAPI + SCardEndTransaction( + __in SCARDHANDLE hCard, + __in DWORD dwDisposition); + + extern WINSCARDAPI LONG WINAPI + SCardCancelTransaction( + __in SCARDHANDLE hCard); +// +// NOTE: This call corresponds to the PC/SC SCARDCOMM::Cancel routine, +// terminating a blocked SCardBeginTransaction service. +// + + + extern WINSCARDAPI LONG WINAPI + SCardState( + __in SCARDHANDLE hCard, + __out LPDWORD pdwState, + __out LPDWORD pdwProtocol, + __out_bcount(*pcbAtrLen) LPBYTE pbAtr, + __inout LPDWORD pcbAtrLen); +// +// NOTE: SCardState is an obsolete routine. PC/SC has replaced it with +// SCardStatus. +// + + extern WINSCARDAPI LONG WINAPI + SCardStatusA( + __in SCARDHANDLE hCard, + __nullnullterminated __out_ecount_opt(*pcchReaderLen) LPSTR mszReaderNames, + __inout_opt LPDWORD pcchReaderLen, + __out_opt LPDWORD pdwState, + __out_opt LPDWORD pdwProtocol, + __out_ecount_opt(*pcbAtrLen) LPBYTE pbAtr, + __inout_opt LPDWORD pcbAtrLen); + extern WINSCARDAPI LONG WINAPI + SCardStatusW( + __in SCARDHANDLE hCard, + __nullnullterminated __out_ecount_opt(*pcchReaderLen) LPWSTR mszReaderNames, + __inout_opt LPDWORD pcchReaderLen, + __out_opt LPDWORD pdwState, + __out_opt LPDWORD pdwProtocol, + __out_ecount_opt(*pcbAtrLen) LPBYTE pbAtr, + __inout_opt LPDWORD pcbAtrLen); +#ifdef UNICODE +#define SCardStatus SCardStatusW +#else +#define SCardStatus SCardStatusA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardTransmit( + __in SCARDHANDLE hCard, + __in LPCSCARD_IO_REQUEST pioSendPci, + __in_bcount(cbSendLength) LPCBYTE pbSendBuffer, + __in DWORD cbSendLength, + __inout_opt LPSCARD_IO_REQUEST pioRecvPci, + __out_bcount(*pcbRecvLength) LPBYTE pbRecvBuffer, + __inout LPDWORD pcbRecvLength); + +#if (NTDDI_VERSION >= NTDDI_VISTA) + extern WINSCARDAPI LONG WINAPI + SCardGetTransmitCount( + __in SCARDHANDLE hCard, + __out LPDWORD pcTransmitCount); +#endif // (NTDDI_VERSION >= NTDDI_VISTA) + +// +//////////////////////////////////////////////////////////////////////////////// +// +// Reader Control Routines +// +// The following services provide for direct, low-level manipulation of the +// reader by the calling application allowing it control over the +// attributes of the communications with the card. +// + + extern WINSCARDAPI LONG WINAPI + SCardControl( + __in SCARDHANDLE hCard, + __in DWORD dwControlCode, + __in_bcount(cbInBufferSize) LPCVOID lpInBuffer, + __in DWORD cbInBufferSize, + __out_bcount(cbOutBufferSize) LPVOID lpOutBuffer, + __in DWORD cbOutBufferSize, + __out LPDWORD lpBytesReturned); + + extern WINSCARDAPI LONG WINAPI + SCardGetAttrib( + __in SCARDHANDLE hCard, + __in DWORD dwAttrId, + __out_bcount_opt(*pcbAttrLen) LPBYTE pbAttr, + __inout LPDWORD pcbAttrLen); +// +// NOTE: The routine SCardGetAttrib's name differs from the PC/SC definition. +// It should be: +// +// extern WINSCARDAPI LONG WINAPI +// SCardGetReaderCapabilities( +// __in SCARDHANDLE hCard, +// __in DWORD dwTag, +// __out LPBYTE pbAttr, +// __inout LPDWORD pcbAttrLen); +// +// Here's a work-around MACRO: +#define SCardGetReaderCapabilities SCardGetAttrib + + extern WINSCARDAPI LONG WINAPI + SCardSetAttrib( + __in SCARDHANDLE hCard, + __in DWORD dwAttrId, + __in_bcount(cbAttrLen) LPCBYTE pbAttr, + __in DWORD cbAttrLen); +// +// NOTE: The routine SCardSetAttrib's name differs from the PC/SC definition. +// It should be: +// +// extern WINSCARDAPI LONG WINAPI +// SCardSetReaderCapabilities( +// __in SCARDHANDLE hCard, +// __in DWORD dwTag, +// __in LPCBYTE pbAttr, +// __in DWORD cbAttrLen); +// +// Here's a work-around MACRO: +#define SCardSetReaderCapabilities SCardSetAttrib + + +// +//////////////////////////////////////////////////////////////////////////////// +// +// Smart Card Dialog definitions +// +// The following section contains structures and exported function +// declarations for the Smart Card Common Dialog dialog. +// + +// Defined constants +// Flags +#define SC_DLG_MINIMAL_UI 0x01 +#define SC_DLG_NO_UI 0x02 +#define SC_DLG_FORCE_UI 0x04 + +#define SCERR_NOCARDNAME 0x4000 +#define SCERR_NOGUIDS 0x8000 + typedef SCARDHANDLE(WINAPI *LPOCNCONNPROCA)(__in SCARDCONTEXT, __in LPSTR, __in LPSTR, __in PVOID); + typedef SCARDHANDLE(WINAPI *LPOCNCONNPROCW)(__in SCARDCONTEXT, __in LPWSTR, __in LPWSTR, __in PVOID); + +#ifdef UNICODE +#define LPOCNCONNPROC LPOCNCONNPROCW +#else +#define LPOCNCONNPROC LPOCNCONNPROCA +#endif // !UNICODE + typedef BOOL (WINAPI *LPOCNCHKPROC)(__in SCARDCONTEXT, __in SCARDHANDLE, __in PVOID); + typedef void (WINAPI *LPOCNDSCPROC)(__in SCARDCONTEXT, __in SCARDHANDLE, __in PVOID); + + +// +// OPENCARD_SEARCH_CRITERIA: In order to specify a user-extended search, +// lpfnCheck must not be NULL. Moreover, the connection to be made to the +// card before performing the callback must be indicated by either providing +// lpfnConnect and lpfnDisconnect OR by setting dwShareMode. +// If both the connection callbacks and dwShareMode are non-NULL, the callbacks +// will be used. +// + + typedef struct { + DWORD dwStructSize; + LPSTR lpstrGroupNames; // OPTIONAL reader groups to include in + DWORD nMaxGroupNames; // search. NULL defaults to + // SCard$DefaultReaders + LPCGUID rgguidInterfaces; // OPTIONAL requested interfaces + DWORD cguidInterfaces; // supported by card's SSP + LPSTR lpstrCardNames; // OPTIONAL requested card names; all cards w/ + DWORD nMaxCardNames; // matching ATRs will be accepted + LPOCNCHKPROC lpfnCheck; // OPTIONAL if NULL no user check will be performed. + LPOCNCONNPROCA lpfnConnect; // OPTIONAL if lpfnConnect is provided, + LPOCNDSCPROC lpfnDisconnect; // lpfnDisconnect must also be set. + LPVOID pvUserData; // OPTIONAL parameter to callbacks + DWORD dwShareMode; // OPTIONAL must be set if lpfnCheck is not null + DWORD dwPreferredProtocols; // OPTIONAL + } OPENCARD_SEARCH_CRITERIAA, *POPENCARD_SEARCH_CRITERIAA, *LPOPENCARD_SEARCH_CRITERIAA; + typedef struct { + DWORD dwStructSize; + LPWSTR lpstrGroupNames; // OPTIONAL reader groups to include in + DWORD nMaxGroupNames; // search. NULL defaults to + // SCard$DefaultReaders + LPCGUID rgguidInterfaces; // OPTIONAL requested interfaces + DWORD cguidInterfaces; // supported by card's SSP + LPWSTR lpstrCardNames; // OPTIONAL requested card names; all cards w/ + DWORD nMaxCardNames; // matching ATRs will be accepted + LPOCNCHKPROC lpfnCheck; // OPTIONAL if NULL no user check will be performed. + LPOCNCONNPROCW lpfnConnect; // OPTIONAL if lpfnConnect is provided, + LPOCNDSCPROC lpfnDisconnect; // lpfnDisconnect must also be set. + LPVOID pvUserData; // OPTIONAL parameter to callbacks + DWORD dwShareMode; // OPTIONAL must be set if lpfnCheck is not null + DWORD dwPreferredProtocols; // OPTIONAL + } OPENCARD_SEARCH_CRITERIAW, *POPENCARD_SEARCH_CRITERIAW, *LPOPENCARD_SEARCH_CRITERIAW; +#ifdef UNICODE + typedef OPENCARD_SEARCH_CRITERIAW OPENCARD_SEARCH_CRITERIA; + typedef POPENCARD_SEARCH_CRITERIAW POPENCARD_SEARCH_CRITERIA; + typedef LPOPENCARD_SEARCH_CRITERIAW LPOPENCARD_SEARCH_CRITERIA; +#else + typedef OPENCARD_SEARCH_CRITERIAA OPENCARD_SEARCH_CRITERIA; + typedef POPENCARD_SEARCH_CRITERIAA POPENCARD_SEARCH_CRITERIA; + typedef LPOPENCARD_SEARCH_CRITERIAA LPOPENCARD_SEARCH_CRITERIA; +#endif // UNICODE + + +// +// OPENCARDNAME_EX: used by SCardUIDlgSelectCard; replaces obsolete OPENCARDNAME +// + + typedef struct { + DWORD dwStructSize; // REQUIRED + SCARDCONTEXT hSCardContext; // REQUIRED + HWND hwndOwner; // OPTIONAL + DWORD dwFlags; // OPTIONAL -- default is SC_DLG_MINIMAL_UI + LPCSTR lpstrTitle; // OPTIONAL + LPCSTR lpstrSearchDesc; // OPTIONAL (eg. "Please insert your smart card.") + HICON hIcon; // OPTIONAL 32x32 icon for your brand insignia + POPENCARD_SEARCH_CRITERIAA pOpenCardSearchCriteria; // OPTIONAL + LPOCNCONNPROCA lpfnConnect; // OPTIONAL - performed on successful selection + LPVOID pvUserData; // OPTIONAL parameter to lpfnConnect + DWORD dwShareMode; // OPTIONAL - if lpfnConnect is NULL, dwShareMode and + DWORD dwPreferredProtocols; // OPTIONAL dwPreferredProtocols will be used to + // connect to the selected card + LPSTR lpstrRdr; // REQUIRED [IN|OUT] Name of selected reader + DWORD nMaxRdr; // REQUIRED [IN|OUT] + LPSTR lpstrCard; // REQUIRED [IN|OUT] Name of selected card + DWORD nMaxCard; // REQUIRED [IN|OUT] + DWORD dwActiveProtocol; // [OUT] set only if dwShareMode not NULL + SCARDHANDLE hCardHandle; // [OUT] set if a card connection was indicated + } OPENCARDNAME_EXA, *POPENCARDNAME_EXA, *LPOPENCARDNAME_EXA; + typedef struct { + DWORD dwStructSize; // REQUIRED + SCARDCONTEXT hSCardContext; // REQUIRED + HWND hwndOwner; // OPTIONAL + DWORD dwFlags; // OPTIONAL -- default is SC_DLG_MINIMAL_UI + LPCWSTR lpstrTitle; // OPTIONAL + LPCWSTR lpstrSearchDesc; // OPTIONAL (eg. "Please insert your smart card.") + HICON hIcon; // OPTIONAL 32x32 icon for your brand insignia + POPENCARD_SEARCH_CRITERIAW pOpenCardSearchCriteria; // OPTIONAL + LPOCNCONNPROCW lpfnConnect; // OPTIONAL - performed on successful selection + LPVOID pvUserData; // OPTIONAL parameter to lpfnConnect + DWORD dwShareMode; // OPTIONAL - if lpfnConnect is NULL, dwShareMode and + DWORD dwPreferredProtocols; // OPTIONAL dwPreferredProtocols will be used to + // connect to the selected card + LPWSTR lpstrRdr; // REQUIRED [IN|OUT] Name of selected reader + DWORD nMaxRdr; // REQUIRED [IN|OUT] + LPWSTR lpstrCard; // REQUIRED [IN|OUT] Name of selected card + DWORD nMaxCard; // REQUIRED [IN|OUT] + DWORD dwActiveProtocol; // [OUT] set only if dwShareMode not NULL + SCARDHANDLE hCardHandle; // [OUT] set if a card connection was indicated + } OPENCARDNAME_EXW, *POPENCARDNAME_EXW, *LPOPENCARDNAME_EXW; +#ifdef UNICODE + typedef OPENCARDNAME_EXW OPENCARDNAME_EX; + typedef POPENCARDNAME_EXW POPENCARDNAME_EX; + typedef LPOPENCARDNAME_EXW LPOPENCARDNAME_EX; +#else + typedef OPENCARDNAME_EXA OPENCARDNAME_EX; + typedef POPENCARDNAME_EXA POPENCARDNAME_EX; + typedef LPOPENCARDNAME_EXA LPOPENCARDNAME_EX; +#endif // UNICODE + +#define OPENCARDNAMEA_EX OPENCARDNAME_EXA +#define OPENCARDNAMEW_EX OPENCARDNAME_EXW +#define POPENCARDNAMEA_EX POPENCARDNAME_EXA +#define POPENCARDNAMEW_EX POPENCARDNAME_EXW +#define LPOPENCARDNAMEA_EX LPOPENCARDNAME_EXA +#define LPOPENCARDNAMEW_EX LPOPENCARDNAME_EXW + + +// +// SCardUIDlgSelectCard replaces GetOpenCardName +// + + extern WINSCARDAPI LONG WINAPI + SCardUIDlgSelectCardA( + LPOPENCARDNAMEA_EX); + extern WINSCARDAPI LONG WINAPI + SCardUIDlgSelectCardW( + LPOPENCARDNAMEW_EX); +#ifdef UNICODE +#define SCardUIDlgSelectCard SCardUIDlgSelectCardW +#else +#define SCardUIDlgSelectCard SCardUIDlgSelectCardA +#endif // !UNICODE + + +// +// "Smart Card Common Dialog" definitions for backwards compatibility +// with the Smart Card Base Services SDK version 1.0 +// + + typedef struct { + DWORD dwStructSize; + HWND hwndOwner; + SCARDCONTEXT hSCardContext; + LPSTR lpstrGroupNames; + DWORD nMaxGroupNames; + LPSTR lpstrCardNames; + DWORD nMaxCardNames; + LPCGUID rgguidInterfaces; + DWORD cguidInterfaces; + LPSTR lpstrRdr; + DWORD nMaxRdr; + LPSTR lpstrCard; + DWORD nMaxCard; + LPCSTR lpstrTitle; + DWORD dwFlags; + LPVOID pvUserData; + DWORD dwShareMode; + DWORD dwPreferredProtocols; + DWORD dwActiveProtocol; + LPOCNCONNPROCA lpfnConnect; + LPOCNCHKPROC lpfnCheck; + LPOCNDSCPROC lpfnDisconnect; + SCARDHANDLE hCardHandle; + } OPENCARDNAMEA, *POPENCARDNAMEA, *LPOPENCARDNAMEA; + typedef struct { + DWORD dwStructSize; + HWND hwndOwner; + SCARDCONTEXT hSCardContext; + LPWSTR lpstrGroupNames; + DWORD nMaxGroupNames; + LPWSTR lpstrCardNames; + DWORD nMaxCardNames; + LPCGUID rgguidInterfaces; + DWORD cguidInterfaces; + LPWSTR lpstrRdr; + DWORD nMaxRdr; + LPWSTR lpstrCard; + DWORD nMaxCard; + LPCWSTR lpstrTitle; + DWORD dwFlags; + LPVOID pvUserData; + DWORD dwShareMode; + DWORD dwPreferredProtocols; + DWORD dwActiveProtocol; + LPOCNCONNPROCW lpfnConnect; + LPOCNCHKPROC lpfnCheck; + LPOCNDSCPROC lpfnDisconnect; + SCARDHANDLE hCardHandle; + } OPENCARDNAMEW, *POPENCARDNAMEW, *LPOPENCARDNAMEW; +#ifdef UNICODE + typedef OPENCARDNAMEW OPENCARDNAME; + typedef POPENCARDNAMEW POPENCARDNAME; + typedef LPOPENCARDNAMEW LPOPENCARDNAME; +#else + typedef OPENCARDNAMEA OPENCARDNAME; + typedef POPENCARDNAMEA POPENCARDNAME; + typedef LPOPENCARDNAMEA LPOPENCARDNAME; +#endif // UNICODE + +// Backwards compatibility macros +#define OPENCARDNAME_A OPENCARDNAMEA +#define OPENCARDNAME_W OPENCARDNAMEW +#define POPENCARDNAME_A POPENCARDNAMEA +#define POPENCARDNAME_W POPENCARDNAMEW +#define LPOPENCARDNAME_A LPOPENCARDNAMEA +#define LPOPENCARDNAME_W LPOPENCARDNAMEW + + extern WINSCARDAPI LONG WINAPI + GetOpenCardNameA( + LPOPENCARDNAMEA); + extern WINSCARDAPI LONG WINAPI + GetOpenCardNameW( + LPOPENCARDNAMEW); +#ifdef UNICODE +#define GetOpenCardName GetOpenCardNameW +#else +#define GetOpenCardName GetOpenCardNameA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardDlgExtendedError(void); + +#if (NTDDI_VERSION >= NTDDI_VISTA) + +// +// Smartcard Caching API +// + + extern WINSCARDAPI LONG WINAPI + SCardReadCacheA( + __in SCARDCONTEXT hContext, + __in UUID *CardIdentifier, + __in DWORD FreshnessCounter, + __in LPSTR LookupName, + __out_bcount(*DataLen) PBYTE Data, + __out DWORD *DataLen); + extern WINSCARDAPI LONG WINAPI + SCardReadCacheW( + __in SCARDCONTEXT hContext, + __in UUID *CardIdentifier, + __in DWORD FreshnessCounter, + __in LPWSTR LookupName, + __out_bcount(*DataLen) PBYTE Data, + __out DWORD *DataLen); +#ifdef UNICODE +#define SCardReadCache SCardReadCacheW +#else +#define SCardReadCache SCardReadCacheA +#endif // !UNICODE + + extern WINSCARDAPI LONG WINAPI + SCardWriteCacheA( + __in SCARDCONTEXT hContext, + __in UUID *CardIdentifier, + __in DWORD FreshnessCounter, + __in LPSTR LookupName, + __in_bcount(DataLen) PBYTE Data, + __in DWORD DataLen); + extern WINSCARDAPI LONG WINAPI + SCardWriteCacheW( + __in SCARDCONTEXT hContext, + __in UUID *CardIdentifier, + __in DWORD FreshnessCounter, + __in LPWSTR LookupName, + __in_bcount(DataLen) PBYTE Data, + __in DWORD DataLen); +#ifdef UNICODE +#define SCardWriteCache SCardWriteCacheW +#else +#define SCardWriteCache SCardWriteCacheA +#endif // !UNICODE + +#endif // (NTDDI_VERSION >= NTDDI_VISTA) + +#ifdef __cplusplus +} +#endif +#endif // _WINSCARD_H_ + + diff --git a/extapi/cygwin/WinSmCrd.h b/extapi/cygwin/WinSmCrd.h new file mode 100644 index 0000000..c35b0e1 --- /dev/null +++ b/extapi/cygwin/WinSmCrd.h @@ -0,0 +1,333 @@ +/*++ + +Copyright (c) 1996 Microsoft Corporation + +Module Name: + + winsmcrd.h + +Abstract: + Smart Card class/port IOCTL codes. This file is required for all code + user mode and kernel mode, using Smart Card IOCTL's, defines, + data structures + +Revision History: + +--*/ + + +#ifndef _NTDDSCRD_H2_ +#define _NTDDSCRD_H2_ + +#if defined (_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WINSCARD_H_ + typedef DWORD ULONG; + typedef WORD UWORD; + typedef BYTE UCHAR; +#else + typedef ULONG DWORD; +// typedef UWORD WORD; + typedef UCHAR BYTE; +#endif + +#ifndef DEVICE_TYPE_SMARTCARD +#ifndef FILE_DEVICE_SMARTCARD +#define FILE_DEVICE_SMARTCARD 0x00000031 +#endif +#else +#if 0x00000031 != FILE_DEVICE_SMARTCARD +#error "Incorrect Smart Card Device Definition" +#endif +#endif + + +// +// Various constants +// + +#define SCARD_ATR_LENGTH 33 // ISO 7816-3 spec. + +// +/////////////////////////////////////////////////////////////////////////////// +// +// Protocol Flag definitions +// + +#define SCARD_PROTOCOL_UNDEFINED 0x00000000 // There is no active protocol. +#define SCARD_PROTOCOL_T0 0x00000001 // T=0 is the active protocol. +#define SCARD_PROTOCOL_T1 0x00000002 // T=1 is the active protocol. +#define SCARD_PROTOCOL_RAW 0x00010000 // Raw is the active protocol. +// +// This is the mask of ISO defined transmission protocols +// +#define SCARD_PROTOCOL_Tx (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1) +// +// Use the default transmission parameters / card clock freq. +// +#define SCARD_PROTOCOL_DEFAULT 0x80000000 +// +// Use optimal transmission parameters / card clock freq. +// Since using the optimal parameters is the default case no bit is defined to be 1 +// +#define SCARD_PROTOCOL_OPTIMAL 0x00000000 + + +// +// Ioctl parameters 1 for IOCTL_SMARTCARD_POWER +// +#define SCARD_POWER_DOWN 0 // Power down the card. +#define SCARD_COLD_RESET 1 // Cycle power and reset the card. +#define SCARD_WARM_RESET 2 // Force a reset on the card. + +// +/////////////////////////////////////////////////////////////////////////////// +// +// Reader Action IOCTLs +// + +#define SCARD_CTL_CODE(code) CTL_CODE(FILE_DEVICE_SMARTCARD, \ + (code), \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_SMARTCARD_POWER SCARD_CTL_CODE( 1) +#define IOCTL_SMARTCARD_GET_ATTRIBUTE SCARD_CTL_CODE( 2) +#define IOCTL_SMARTCARD_SET_ATTRIBUTE SCARD_CTL_CODE( 3) +#define IOCTL_SMARTCARD_CONFISCATE SCARD_CTL_CODE( 4) +#define IOCTL_SMARTCARD_TRANSMIT SCARD_CTL_CODE( 5) +#define IOCTL_SMARTCARD_EJECT SCARD_CTL_CODE( 6) +#define IOCTL_SMARTCARD_SWALLOW SCARD_CTL_CODE( 7) +// #define IOCTL_SMARTCARD_READ SCARD_CTL_CODE( 8) obsolete +// #define IOCTL_SMARTCARD_WRITE SCARD_CTL_CODE( 9) obsolete +#define IOCTL_SMARTCARD_IS_PRESENT SCARD_CTL_CODE(10) +#define IOCTL_SMARTCARD_IS_ABSENT SCARD_CTL_CODE(11) +#define IOCTL_SMARTCARD_SET_PROTOCOL SCARD_CTL_CODE(12) +#define IOCTL_SMARTCARD_GET_STATE SCARD_CTL_CODE(14) +#define IOCTL_SMARTCARD_GET_LAST_ERROR SCARD_CTL_CODE(15) +#define IOCTL_SMARTCARD_GET_PERF_CNTR SCARD_CTL_CODE(16) + + +// +/////////////////////////////////////////////////////////////////////////////// +// +// Tags for requesting card and reader attributes +// + +#define MAXIMUM_ATTR_STRING_LENGTH 32 // Nothing bigger than this from getAttr +#define MAXIMUM_SMARTCARD_READERS 10 // Limit the readers on the system + +#define SCARD_ATTR_VALUE(Class, Tag) ((((ULONG)(Class)) << 16) | ((ULONG)(Tag))) + +#define SCARD_CLASS_VENDOR_INFO 1 // Vendor information definitions +#define SCARD_CLASS_COMMUNICATIONS 2 // Communication definitions +#define SCARD_CLASS_PROTOCOL 3 // Protocol definitions +#define SCARD_CLASS_POWER_MGMT 4 // Power Management definitions +#define SCARD_CLASS_SECURITY 5 // Security Assurance definitions +#define SCARD_CLASS_MECHANICAL 6 // Mechanical characteristic definitions +#define SCARD_CLASS_VENDOR_DEFINED 7 // Vendor specific definitions +#define SCARD_CLASS_IFD_PROTOCOL 8 // Interface Device Protocol options +#define SCARD_CLASS_ICC_STATE 9 // ICC State specific definitions +#define SCARD_CLASS_PERF 0x7ffe // performace counters +#define SCARD_CLASS_SYSTEM 0x7fff // System-specific definitions + +#define SCARD_ATTR_VENDOR_NAME SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0100) +#define SCARD_ATTR_VENDOR_IFD_TYPE SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0101) +#define SCARD_ATTR_VENDOR_IFD_VERSION SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0102) +#define SCARD_ATTR_VENDOR_IFD_SERIAL_NO SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0103) +#define SCARD_ATTR_CHANNEL_ID SCARD_ATTR_VALUE(SCARD_CLASS_COMMUNICATIONS, 0x0110) +#define SCARD_ATTR_PROTOCOL_TYPES SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0120) +// #define SCARD_ATTR_ASYNC_PROTOCOL_TYPES SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0120) +#define SCARD_ATTR_DEFAULT_CLK SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0121) +#define SCARD_ATTR_MAX_CLK SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0122) +#define SCARD_ATTR_DEFAULT_DATA_RATE SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0123) +#define SCARD_ATTR_MAX_DATA_RATE SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0124) +#define SCARD_ATTR_MAX_IFSD SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0125) +// #define SCARD_ATTR_SYNC_PROTOCOL_TYPES SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0126) +#define SCARD_ATTR_POWER_MGMT_SUPPORT SCARD_ATTR_VALUE(SCARD_CLASS_POWER_MGMT, 0x0131) +#define SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE SCARD_ATTR_VALUE(SCARD_CLASS_SECURITY, 0x0140) +#define SCARD_ATTR_USER_AUTH_INPUT_DEVICE SCARD_ATTR_VALUE(SCARD_CLASS_SECURITY, 0x0142) +#define SCARD_ATTR_CHARACTERISTICS SCARD_ATTR_VALUE(SCARD_CLASS_MECHANICAL, 0x0150) + +#define SCARD_ATTR_CURRENT_PROTOCOL_TYPE SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0201) +#define SCARD_ATTR_CURRENT_CLK SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0202) +#define SCARD_ATTR_CURRENT_F SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0203) +#define SCARD_ATTR_CURRENT_D SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0204) +#define SCARD_ATTR_CURRENT_N SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0205) +#define SCARD_ATTR_CURRENT_W SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0206) +#define SCARD_ATTR_CURRENT_IFSC SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0207) +#define SCARD_ATTR_CURRENT_IFSD SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0208) +#define SCARD_ATTR_CURRENT_BWT SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0209) +#define SCARD_ATTR_CURRENT_CWT SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x020a) +#define SCARD_ATTR_CURRENT_EBC_ENCODING SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x020b) +#define SCARD_ATTR_EXTENDED_BWT SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x020c) + +#define SCARD_ATTR_ICC_PRESENCE SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0300) +#define SCARD_ATTR_ICC_INTERFACE_STATUS SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0301) +#define SCARD_ATTR_CURRENT_IO_STATE SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0302) +#define SCARD_ATTR_ATR_STRING SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0303) +#define SCARD_ATTR_ICC_TYPE_PER_ATR SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0304) + +#define SCARD_ATTR_ESC_RESET SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA000) +#define SCARD_ATTR_ESC_CANCEL SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA003) +#define SCARD_ATTR_ESC_AUTHREQUEST SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA005) +#define SCARD_ATTR_MAXINPUT SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA007) + +#define SCARD_ATTR_DEVICE_UNIT SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0001) +#define SCARD_ATTR_DEVICE_IN_USE SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0002) +#define SCARD_ATTR_DEVICE_FRIENDLY_NAME_A SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0003) +#define SCARD_ATTR_DEVICE_SYSTEM_NAME_A SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0004) +#define SCARD_ATTR_DEVICE_FRIENDLY_NAME_W SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0005) +#define SCARD_ATTR_DEVICE_SYSTEM_NAME_W SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0006) +#define SCARD_ATTR_SUPRESS_T1_IFS_REQUEST SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0007) + +#define SCARD_PERF_NUM_TRANSMISSIONS SCARD_ATTR_VALUE(SCARD_CLASS_PERF, 0x0001) +#define SCARD_PERF_BYTES_TRANSMITTED SCARD_ATTR_VALUE(SCARD_CLASS_PERF, 0x0002) +#define SCARD_PERF_TRANSMISSION_TIME SCARD_ATTR_VALUE(SCARD_CLASS_PERF, 0x0003) + +#ifdef UNICODE +#define SCARD_ATTR_DEVICE_FRIENDLY_NAME SCARD_ATTR_DEVICE_FRIENDLY_NAME_W +#define SCARD_ATTR_DEVICE_SYSTEM_NAME SCARD_ATTR_DEVICE_SYSTEM_NAME_W +#else +#define SCARD_ATTR_DEVICE_FRIENDLY_NAME SCARD_ATTR_DEVICE_FRIENDLY_NAME_A +#define SCARD_ATTR_DEVICE_SYSTEM_NAME SCARD_ATTR_DEVICE_SYSTEM_NAME_A +#endif + + +// +// T=0 Protocol Defines +// + +#define SCARD_T0_HEADER_LENGTH 7 +#define SCARD_T0_CMD_LENGTH 5 + + +// +// T=1 Protocol Defines +// + +#define SCARD_T1_PROLOGUE_LENGTH 3 +#define SCARD_T1_EPILOGUE_LENGTH 2 +#define SCARD_T1_MAX_IFS 254 + + +// +/////////////////////////////////////////////////////////////////////////////// +// +// Reader states +// + +#define SCARD_UNKNOWN 0 // This value implies the driver is unaware + // of the current state of the reader. +#define SCARD_ABSENT 1 // This value implies there is no card in + // the reader. +#define SCARD_PRESENT 2 // This value implies there is a card is + // present in the reader, but that it has + // not been moved into position for use. +#define SCARD_SWALLOWED 3 // This value implies there is a card in the + // reader in position for use. The card is + // not powered. +#define SCARD_POWERED 4 // This value implies there is power is + // being provided to the card, but the + // Reader Driver is unaware of the mode of + // the card. +#define SCARD_NEGOTIABLE 5 // This value implies the card has been + // reset and is awaiting PTS negotiation. +#define SCARD_SPECIFIC 6 // This value implies the card has been + // reset and specific communication + // protocols have been established. + +//////////////////////////////////////////////////////////////////////////////// +// +// I/O Services +// +// The following services provide access to the I/O capabilities of the +// reader drivers. Services of the Smart Card are requested by placing the +// following structure into the protocol buffer: +// + + + typedef struct _SCARD_IO_REQUEST { + DWORD dwProtocol; // Protocol identifier + DWORD cbPciLength; // Protocol Control Information Length + } SCARD_IO_REQUEST, *PSCARD_IO_REQUEST, *LPSCARD_IO_REQUEST; + typedef const SCARD_IO_REQUEST *LPCSCARD_IO_REQUEST; + + +// +// T=0 protocol services. +// + + typedef struct { + BYTE + bCla, // The instruction class + bIns, // The instruction code within the instruction class + bP1, + bP2, // Parameters to the instruction + bP3; // Size of I/O Transfer + } SCARD_T0_COMMAND, *LPSCARD_T0_COMMAND; + + typedef struct { + SCARD_IO_REQUEST ioRequest; + BYTE + bSw1, + bSw2; // Return codes from the instruction + union + { + SCARD_T0_COMMAND CmdBytes; + BYTE rgbHeader[5]; + } DUMMYUNIONNAME; + } SCARD_T0_REQUEST; + + typedef SCARD_T0_REQUEST *PSCARD_T0_REQUEST, *LPSCARD_T0_REQUEST; + + +// +// T=1 Protocol Services +// + + typedef struct { + SCARD_IO_REQUEST ioRequest; + } SCARD_T1_REQUEST; + typedef SCARD_T1_REQUEST *PSCARD_T1_REQUEST, *LPSCARD_T1_REQUEST; + + +// +//////////////////////////////////////////////////////////////////////////////// +// +// Driver attribute flags +// + +#define SCARD_READER_SWALLOWS 0x00000001 // Reader has a card swallowing + // mechanism. +#define SCARD_READER_EJECTS 0x00000002 // Reader has a card ejection + // mechanism. +#define SCARD_READER_CONFISCATES 0x00000004 // Reader has a card capture + // mechanism. + +// +/////////////////////////////////////////////////////////////////////////////// +// +// Type of reader +// +#define SCARD_READER_TYPE_SERIAL 0x01 +#define SCARD_READER_TYPE_PARALELL 0x02 +#define SCARD_READER_TYPE_KEYBOARD 0x04 +#define SCARD_READER_TYPE_SCSI 0x08 +#define SCARD_READER_TYPE_IDE 0x10 +#define SCARD_READER_TYPE_USB 0x20 +#define SCARD_READER_TYPE_PCMCIA 0x40 +#define SCARD_READER_TYPE_VENDOR 0xF0 + +#ifdef __cplusplus +} +#endif +#endif + + diff --git a/extapi/linux/README b/extapi/linux/README new file mode 100644 index 0000000..901e846 --- /dev/null +++ b/extapi/linux/README @@ -0,0 +1,2 @@ +The files in this directory are the official Linux include files that +for some reason are not shipped with Android NDK. diff --git a/extapi/linux/serial.h b/extapi/linux/serial.h new file mode 100644 index 0000000..ef1abc4 --- /dev/null +++ b/extapi/linux/serial.h @@ -0,0 +1,126 @@ +/* + * include/linux/serial.h + * + * Copyright (C) 1992 by Theodore Ts'o. + * + * Redistribution of this file is permitted under the terms of the GNU + * Public License (GPL) + */ + +#ifndef _LINUX_SERIAL_H +#define _LINUX_SERIAL_H + +#include + +#include "tty_flags.h" + + +struct serial_struct { + int type; + int line; + unsigned int port; + int irq; + int flags; + int xmit_fifo_size; + int custom_divisor; + int baud_base; + unsigned short close_delay; + char io_type; + char reserved_char[1]; + int hub6; + unsigned short closing_wait; /* time to wait before closing */ + unsigned short closing_wait2; /* no longer used... */ + unsigned char *iomem_base; + unsigned short iomem_reg_shift; + unsigned int port_high; + unsigned long iomap_base; /* cookie passed into ioremap */ +}; + +/* + * For the close wait times, 0 means wait forever for serial port to + * flush its output. 65535 means don't wait at all. + */ +#define ASYNC_CLOSING_WAIT_INF 0 +#define ASYNC_CLOSING_WAIT_NONE 65535 + +/* + * These are the supported serial types. + */ +#define PORT_UNKNOWN 0 +#define PORT_8250 1 +#define PORT_16450 2 +#define PORT_16550 3 +#define PORT_16550A 4 +#define PORT_CIRRUS 5 /* usurped by cyclades.c */ +#define PORT_16650 6 +#define PORT_16650V2 7 +#define PORT_16750 8 +#define PORT_STARTECH 9 /* usurped by cyclades.c */ +#define PORT_16C950 10 /* Oxford Semiconductor */ +#define PORT_16654 11 +#define PORT_16850 12 +#define PORT_RSA 13 /* RSA-DV II/S card */ +#define PORT_MAX 13 + +#define SERIAL_IO_PORT 0 +#define SERIAL_IO_HUB6 1 +#define SERIAL_IO_MEM 2 + +#define UART_CLEAR_FIFO 0x01 +#define UART_USE_FIFO 0x02 +#define UART_STARTECH 0x04 +#define UART_NATSEMI 0x08 + + +/* + * Multiport serial configuration structure --- external structure + */ +struct serial_multiport_struct { + int irq; + int port1; + unsigned char mask1, match1; + int port2; + unsigned char mask2, match2; + int port3; + unsigned char mask3, match3; + int port4; + unsigned char mask4, match4; + int port_monitor; + int reserved[32]; +}; + +/* + * Serial input interrupt line counters -- external structure + * Four lines can interrupt: CTS, DSR, RI, DCD + */ +struct serial_icounter_struct { + int cts, dsr, rng, dcd; + int rx, tx; + int frame, overrun, parity, brk; + int buf_overrun; + int reserved[9]; +}; + +/* + * Serial interface for controlling RS485 settings on chips with suitable + * support. Set with TIOCSRS485 and get with TIOCGRS485 if supported by your + * platform. The set function returns the new state, with any unsupported bits + * reverted appropriately. + */ + +struct serial_rs485 { + __u32 flags; /* RS485 feature flags */ +#define SER_RS485_ENABLED (1 << 0) /* If enabled */ +#define SER_RS485_RTS_ON_SEND (1 << 1) /* Logical level for + RTS pin when + sending */ +#define SER_RS485_RTS_AFTER_SEND (1 << 2) /* Logical level for + RTS pin after sent*/ +#define SER_RS485_RX_DURING_TX (1 << 4) + __u32 delay_rts_before_send; /* Delay before send (milliseconds) */ + __u32 delay_rts_after_send; /* Delay after send (milliseconds) */ + __u32 padding[5]; /* Memory is cheap, new structs + are a royal PITA .. */ +}; + +#endif /* _LINUX_SERIAL_H */ diff --git a/extapi/linux/tty_flags.h b/extapi/linux/tty_flags.h new file mode 100644 index 0000000..af505d5 --- /dev/null +++ b/extapi/linux/tty_flags.h @@ -0,0 +1,78 @@ +#ifndef _LINUX_TTY_FLAGS_H +#define _LINUX_TTY_FLAGS_H + +/* + * Definitions for async_struct (and serial_struct) flags field also + * shared by the tty_port flags structures. + * + * Define ASYNCB_* for convenient use with {test,set,clear}_bit. + */ +#define ASYNCB_HUP_NOTIFY 0 /* Notify getty on hangups and closes +* on the callout port */ +#define ASYNCB_FOURPORT 1 /* Set OU1, OUT2 per AST Fourport settings */ +#define ASYNCB_SAK 2 /* Secure Attention Key (Orange book) */ +#define ASYNCB_SPLIT_TERMIOS 3 /* Separate termios for dialin/callout */ +#define ASYNCB_SPD_HI 4 /* Use 56000 instead of 38400 bps */ +#define ASYNCB_SPD_VHI 5 /* Use 115200 instead of 38400 bps */ +#define ASYNCB_SKIP_TEST 6 /* Skip UART test during autoconfiguration */ +#define ASYNCB_AUTO_IRQ 7 /* Do automatic IRQ during +* autoconfiguration */ +#define ASYNCB_SESSION_LOCKOUT 8 /* Lock out cua opens based on session */ +#define ASYNCB_PGRP_LOCKOUT 9 /* Lock out cua opens based on pgrp */ +#define ASYNCB_CALLOUT_NOHUP 10 /* Don't do hangups for cua device */ +#define ASYNCB_HARDPPS_CD 11 /* Call hardpps when CD goes high */ +#define ASYNCB_SPD_SHI 12 /* Use 230400 instead of 38400 bps */ +#define ASYNCB_LOW_LATENCY 13 /* Request low latency behaviour */ +#define ASYNCB_BUGGY_UART 14 /* This is a buggy UART, skip some safety +* checks. Note: can be dangerous! */ +#define ASYNCB_AUTOPROBE 15 /* Port was autoprobed by PCI or PNP code */ +#define ASYNCB_LAST_USER 15 + +/* Internal flags used only by kernel */ +#define ASYNCB_INITIALIZED 31 /* Serial port was initialized */ +#define ASYNCB_SUSPENDED 30 /* Serial port is suspended */ +#define ASYNCB_NORMAL_ACTIVE 29 /* Normal device is active */ +#define ASYNCB_BOOT_AUTOCONF 28 /* Autoconfigure port on bootup */ +#define ASYNCB_CLOSING 27 /* Serial port is closing */ +#define ASYNCB_CTS_FLOW 26 /* Do CTS flow control */ +#define ASYNCB_CHECK_CD 25 /* i.e., CLOCAL */ +#define ASYNCB_SHARE_IRQ 24 /* for multifunction cards, no longer used */ +#define ASYNCB_CONS_FLOW 23 /* flow control for console */ +#define ASYNCB_FIRST_KERNEL 22 + +#define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY) +#define ASYNC_SUSPENDED (1U << ASYNCB_SUSPENDED) +#define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT) +#define ASYNC_SAK (1U << ASYNCB_SAK) +#define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS) +#define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI) +#define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI) +#define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST) +#define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ) +#define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT) +#define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT) +#define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP) +#define ASYNC_HARDPPS_CD (1U << ASYNCB_HARDPPS_CD) +#define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI) +#define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY) +#define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART) +#define ASYNC_AUTOPROBE (1U << ASYNCB_AUTOPROBE) + +#define ASYNC_FLAGS ((1U << (ASYNCB_LAST_USER + 1)) - 1) +#define ASYNC_USR_MASK (ASYNC_SPD_MASK|ASYNC_CALLOUT_NOHUP| \ + ASYNC_LOW_LATENCY) +#define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI) +#define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI) +#define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI) + +#define ASYNC_INITIALIZED (1U << ASYNCB_INITIALIZED) +#define ASYNC_NORMAL_ACTIVE (1U << ASYNCB_NORMAL_ACTIVE) +#define ASYNC_BOOT_AUTOCONF (1U << ASYNCB_BOOT_AUTOCONF) +#define ASYNC_CLOSING (1U << ASYNCB_CLOSING) +#define ASYNC_CTS_FLOW (1U << ASYNCB_CTS_FLOW) +#define ASYNC_CHECK_CD (1U << ASYNCB_CHECK_CD) +#define ASYNC_SHARE_IRQ (1U << ASYNCB_SHARE_IRQ) +#define ASYNC_CONS_FLOW (1U << ASYNCB_CONS_FLOW) +#define ASYNC_INTERNAL_FLAGS (~((1U << ASYNCB_FIRST_KERNEL) - 1)) + +#endif diff --git a/extapi/openxcas/libOpenXCASAPI.a b/extapi/openxcas/libOpenXCASAPI.a new file mode 100644 index 0000000000000000000000000000000000000000..0cc819bda5b39ebd8662244a8239baf929dfc7c1 GIT binary patch literal 87544 zcmdqK4R}@8dFQ+Ife=Wxk8RmeQl;Jp1eP5YBq3wjCCM=e8z-`@*f=E(8GW4tQY|4x zI@of<+#WkhOjCjsmnJx+_o|Lu(_GbHN12DFWU6UtN^j{rOfGkZ&f`8+Thh=w%q_j8 zcjydL?(e@p&e><5eRRaZ$@Jd+ApZNm)_UKy*1NvfUVESDrk>Hh{*SC&Z)IQ2EgK$e zX=!b1X|gQ-arytZtmX$dIu2I%N@L6;#w<_f{@r!fm>=ceqt(WAj4$o)%NLFL(f$pO z#S;TWZ0fu^>OLPs4^mRR# zc+r#TAL{BGP4pxaJ_mmU`1d4}J$?N?|9B$cFq#+|e%`NPaJcU|pTOQdK8K;P&t^v{ z2Vaz;SqkMame><_6*~TXiGg&CPJq$p6QfRu!@1DNoz;K8w4No92(o5NsH@Yn=i}5i!EHA=bNk0^U2jnP3pC4 zjAx&*{^1v>qq~N?dMNtDxZ0QJrt!nNTXbv@pHI^88DpnjA!IjrO9$vU#^#%`K;jnR0TZJ z`O0z+Bv^iCeQg~f zu(5fgr|%cGc05{AQo@kyTqUN`WSBAS6^z0WGtz2%H<;TDLuA%fx5=0z_eK|9D_gk! z-Fs`z?;eYolgG^3a#O~iF)vLT6RkI6?8f`PB!8oIqRa#<})ticnjX8d)n7Mwvw0^ZIy*p~<${Mrf!0o2A zeue2JkB**blj6?K*H4hWD7uW9AT1O1im$>{U?-*Vu^c;i;evYZL66cCZIHbx(6byJ z*>Odul;*PfGE;VkQgVmca(6VMvsj^9>NL6lHg88U5({C!y517~AG?CK!IxtEZ)S5T$EwOEvop0s2+H9#QG3D4K zW9May*cp8$8c7)^Uq8nD)E3Uqy^&J$A^v_`X)B=~|BtaX)xo{~$t-`zyAQ$C)$7-< zd+ruDALNfV;aQ_T;noSsT{1u4^%7-Pnsm$(~*? zIqG1)cgKWaT{1DAH0$C$$sV(=cWlh88%^xiTt+v8>qg?EX5ElIiCNdvJFuYz=V1kw zw&r!M9yROwhKGijHY+1byi_h4TNqO8E5`rb<<7nSkGQw=jb?>=FPN_~rj1_d7Vaf4 zkxKs7xZEBrxS#gvf~NI-hFq?a+mzW4qsBbD!S ziPBtRv@Maj%HV4=l)Dwit7ne$3e&`mWSwh;d4QXGj}rc2?i+plqujUqxb8cBJblYe znV}GTS6RAMIgVwf$~^A#zk?jaeOzTateVyaFJ~Otbt6gg`)7ACpvrlf>{`gZ* zc6F_5GW8u>?Eji|*P{P6=)YF|_kjM}=+l$4@zS+>goV=Bh#5=96QiSM7pt5xzkt1U zv5S?+p5!jChwthl=$_#&wmo|6J%n8^j1I8JGJ^B)s6zE6S!QRpdr;3F$`W>RYzr;B z%V}E`poPnda;KhEfT~|BsJcyH1;}LGte`)QiG$n3?{?~06%}6PEqx>M2M^01Jx9dB zS#iS839F4!8CHO&z^eBfvmpEc|4|%ObmR>}cnn-Y$JY*4fSuy#*&+WrWBSGE7zX7J zj>w(C{E+Y`jX5cQ!k-e-a8H93;5qpd?*+LdcTw)(CAlN7`lS5Sfyz%SsQhR=QGT|G zEC0gVjM*-Ca3?6e2ZWX}N99kYpOrs&LL9ky`ICOtOT}|Z{$M%vQt?!RilbUlVXGAvrAiBHn+62wb_fy?v;hsmgUrsrLHZt z-m_Tm<*xONxzD5`tY^5g=l{{B7<0i`ZPZeKxx79asi$d4DB|Gj%0_h@8`&9|ie|I+4O`0~hBn@3ZntQ#ll?~i=Z{NQ@5M)6($h0;ye zuWxRxkCc{@{w1!HLe{iX(-h?QT7!AK*E~}0tRu7Y?yW=bW*w@vWt6pOv>{d+ZBkOR z!`_|Nky#`C^df5{x((SI<2d2#7743xOIago@1TzIO|Roa(s^e(o!RMk*7QNQ&rcc? zKag4L$8z$mv=L`|-5T6u@b70tnYe}7F5muyUp8p(rO4b&d{rv%5c@49-s&5a&ASgX z#XijbX*!WNa_VeVx*UURe(GnNMZL-{%ca(#ylL4_TGW05dh@4)wi2=rveb0s)a4ZM z=czw-8t8wQvj0(H%IL$o&0=*nte>>#*|aa#8mXvfzbnTMnf9M;_ocNf+g3R`*SUQJ zbK*#eiPS4@Q@Nb}Ham`0>AH~ZuKufvuA|KKMyegGQOcR3uwSIRV0i881>J&uKzLdi zGVT6M=~NjP>Zi}rS2fUA)wAztvF}*Re&;=|jU#6ISeTs&Gx^bwyvjXjXUDB@+7(Xr zRhms@X??`Jo;PkQZ`@lBu$O7u*|oQAXa38v^UbDJ>BMf6*liNK*?WaefxW^eYP3b+uH$q zT*}l5rN7b4+)G=`>SN>{wL;wX$4rTNnmYT-lM&LucyiDiPmK8@<@P<=Q!J}4I=ZaV z`4aYzef3f9*RMzF?=+nei*gFn6%4Qaw@ULm`^+)w`&VJ-{JXupXsb|@|d08rPOmTZDDq8cgvP^*lAef zrXimV>PUx^|MxZ>VP(Nm!=8g`o|OtI`v%IvGH>1+ZAz6+o1;9-_{#(3=C2M^nhOW2 z%@63ies8wi{QhjEIW=2t{$Ms@epvtiT)$Wop^o{(`opQbXe*Pnl}XylByDAqwlYau znWU}Q?dxoyeT9up)#>^3L{1-CZpx|i8T;v(jN)Bv42sc)Ptu0N+O68F#$vC%_Iq|? ze7x!Qno@iH1pVOKw6EMcUu(D5+;;vL+g|idCQ2U=R(5)ha0|~7oH`w@*KZ@W^%gH* zdFq3wGhh2uT7x<>bu2ucnK~AGPwNxsB#EcAZK&bxN^m>{9%dA@P=*`h)8K!{*0YM^)nQx4Y{5 zQsxBjMcU|feM25P(ovbYB{n3p)02&MYieU&3@&=A<#VzWgZ4bs}Id)v>U0UA*iJ z{cq-3s%p?bm3;IaVfa;+_3V}5JMe7v#-6=eIr1h;|35$fLisD?pZ)APH-Clfo9(ad z9_qC3+4o1Y>~}WDzfk7UP$FH}C+>C3Gjh5QTYFO>ctoqV!CVU6{3C^K#i@t9`x zMQdQp>KWvaX8c9VIcgSfw0id@t%0P~#~I4uq}7|Sb`K|48kahuvbC(l#Aa8|z@U{J zwm2AI<;2slX6!yf*=GsH?`yQ!mftnFTgL@#SsfM_=hiv8x+ngy`*hWCsmY!^HI zc33YQ2?#N(Kb$L`KcKD8GpK8Ezxp*}D@}M9pK1l-3QL>H)h%v+7@sipA`ocJ*u$2N z^JeF*Cy`9VZ5=zdJo3zz?N4_&aXxH$$=6|R>+#MlGMxfSkLQwH?+d0^?(rp$5c%rm2$lhvf#eaIPhz}e@QK4=_rl38a> zmv+MW|LbRCM($KCa4vTtWrAm8ws8pIDiaSLsj~KUo&!U3&c+-^m#0G-y?gc#S@Y=m zm~+kh;*^UQu8I2!-`N<2v$w4sF~h_<^lZ#|>GPCxt8ls=;*WVs`iiYLW*(+|5RV;B z>DtVn^5tFU?cFJlW&ApEQaIM#_N8!o_Uc`VSN>ftmbxAbJTskk?*(3dcXRKLH^npT zJAC0hKb@oSMPpti+(<|`h4EH{Q72B@$B8_;KfU@tjq8I zUYw2bE7w_=TU`g4UuR)7*HHOmKASg!x*R)~*=M9!m)Z4I;kR+GGSZ`G@n^t6{-i_v zUw{+*dH&p{FP*g+NHg86FnjoaQ`&8X8RLGZkB@Vo^6`Jk{b3(}nfqxUe}(&bA3wnT z1t0$k_mycQuP|SQH~RRm!gZFxxmK88=O*RjZ^DoI_;10_`1rTrSA6`p;oeyoG$z}2cPiq{{Yt+2IpE~{*0S5KK@s5onde;f6G*Tq@wz+ zu(L*g9oNGR=HnJ?8H>aLg!#(>d{09U69Ar*kY8MX#_@)5g0$*_5r0?l~|EB}I z51w@MtMJKy|IfpZ`}}_?;Qtl)1)u-x0sr5GTW-6R{u6MW@z5pyNASZw{vQMKe*xE- z4KMs|6cOG@ONOlO}9A`l5d9Z z^YIRNwJIK${QKd@eLMxf;CATpcPAhAL6^SZk+G~3WX;Bz2l1AP=Kfs+af0jZFiw}X zWNtTPZd)_A_5rg($Im!}_Mi@;wducy^k1_hVjo#+w$H3J+lSVg?Ne*bw$|p2c1T;Y z*%ocKWm|077U#HIi*Uc#QU?-=wr(Nz6=vsTsUH#1X;KzLE5+m3oac5cQAmeMI{u(ya4@ z{$wd)%YRzi=f;&=wzyX${Cld*12a3;W|sGvvTU=E5MZa zdd}D>Jj$lU>2zk~56-&&bFTjasCab7Q1NIYr+73zE1pIn6HkrL(%UYiuh|AxfSvN+ z;EXrYGcI@VklY_|#u>TKx$dXs{-86y*!gqa%jvVKm^f=(kvk9VZ9Os9UE_<~yIuEv zuDiw=xoaM&c$m^o5t?587u4Nw3Bq$u)qI>jbNi>jxz_A|!lL{$NV} z$Z4FC+;O>s=j0wkKPb5?!aIpa{-CB8lGFGlxkgZOI^SNknRtZQc|zEN-9YI%B4oFF z8ms_k<&U0oLgX$9k*j0e6E+EvkAafkCcKNcy+NI|+#!GDHBKrWQ*sCA<=$@0c_Df( z3DI*6l%8_NO`XZC1S`NAP~H~; zzDq(DxR=2S@QOHku8M;!18jXwLiB7CqGzWNJ%gb1u+3&x)X~ns3UHq|@+om}Kd5va z5t6Qx!fohdz9M^ffUdGgHJ_3Hao7Kf+$oRhYLN12}G^l(n z$lano$sN2XcgjNxYw4*JMkz0_0^}iyt%pMvww_v0dfJ4Il$-oXM~D2u9pdQe7OpZT zE_bkBoOBF|gCn5y?3X)o2jmVOk~@pyS-GQUPPm%<$sar^j{LkhcnXxBcjb=!g51H2 za(~#F-^zXw#u(YP9#psnad4A3@w5x;$&cK@4srB%ii6uh#nUg`NcqYi`F-*SC*4f`c7S$1gw5nb{>q2^!TsXsnG&v{Jmd}@7DxVwI5-VT&k4CB zcT(=)DY;XAS~n=)7v&CK0~OBP>4d8R6;GWIds}h`Is9eE(;!YfjiBO*39H8 zt#0@RevZyM^XAurX>!gNY_2CCljOs9c2fGOgQnyi=PvC3>}O?71YuaQpB1hdR zO&j78`?0@E`rf=ldRb|AFSQx5x2@ygaJGtb7{ni%EwN!_@Yr_bXa`rY4){cpNw`+9``!`=;LTIsu1 zm?+FEv+NR6e#nIH%H2QBewS4HQFQ0&OEy+Iyi0a^%p;&U)2{R-?(TQkoo}Df=Y6`^ zj^HydS#Ln5zb2xGYW6pP(tAO>Y|1B(_%P7e9`}7lCyHelM|ss4COfQ9S&D;=VAXAm z$wCs+4X&W!#K8)1Q0`>tfZPc`C3kSz)2;O1kthAd=zl7$Kid{9Dua~L<>cwE zJbC(1QDx#kr&hRJij9Nu!?j(Et(JEC+b>~fr)gVn9Wv!>qYY|rnt^89!YYxUMYo*o zuJeb5-qR?io^L8pPik}O_{mAOLpWQiG-m9ndg8ccr@I*acNxna)>a+lOmZa3dF`M* zrcCL<<+dN^hC}?L%n5@s#n`V&-$hEL(seUmdwaTo9a}@}xF0(@=l`OyGW?!{4zJ(*{@@sz%$zPM1G&Aeaf zyz{oy`Z`dczHSexuPh^TY>;n!C`Lca<1GF8%5Z3j^);9Wwo`M)=X_;YY#h$`UwW`o zZGiIQU7eivM&q{2cCl|p?f={F-kXg4E^SeJS93NFM!scZe?;%SX|GG~)G(im9JkS8 z9UYxsU~lU<^91@WdtT$T%dmcn@RKhUs0YbFJ&?>!Udmibzg4XM_Kf}mt$}u}K1_M` z_hE|Xm2{n0y=Wg6mY;@9e3Rb1Ev#PLY&^HR@#vkPM)St8Ci5G|TFslswz7HlH{vT$qUm@+hFXhgS6rYoY2`=7C# zHJh`y{?4M`CkR{jE_ELKrv>WxY)Bn{CB%OD=E2429}ehO9SB>u{aLyWEWU0F%fn9) zhqG=jyl%Uhc)WGnA=Yid@?Ywj?JwMXF1BV1%j1o#*}OOkt=Yoj@z!kYN&HCHY=2vz zuAT|0tL#DNwDo*-wHW;zmCDkeZ_W1iORTHGJlx2dt=Kr!7imAKl(~x5Y*Vby{yD78 zDoUI^=zQbVr9gRW&U+(kvtsmnYqKfy@{-qPRc=14lEv3%Vfnd{wb{+Y^IomZO3j@G z*JeKvu*Xu?W?{Cuk+s=-6{oj0Q$DpeJG9u^Y-+K!*`4;Lq5=SJ3MHxti|uFamji9FrN+U#cHS?bzM{qv2l&2;wZo_Cz`5Bop+I|EGw z@yNhd`)n1<&w&1WvVLP=9Ajk1pZ%SM8&BVlI)09lct5h^yl9^LBTebowcX!^ z!Uk!XuwQoy@z4J0s9gEeZI}1qR-q%O8eMMEJfMV7Wi^V>P&z+|x6Z z(ANW=e72**`R>Cax&ogF@O14O9wkjHN$?`F@?Ol}Q%0f32h&MHY7w2vf9AslGN_lo zrO4SIC~$tIO`l6B7|+0-=X(YR;(Ur@mo+x<^ZMZp>mwhwyzgZcl=rNsI>4ts?4R^< zzqV0ORtKks`2+~x@(2+xoc6K3eSO3jCU5nicJ#$9{03LjQg)VNJ2&}W%~9bN%tL7Y zw+zKU>{zg%e82_`8PURVlYd|KJ<+hN|MaBGY-&dgsOPpX%d{k`q?F&4M%78>&E6RH zT4C-7Tlv!*Mw~r4vzygp!`@$|UP^3L+-h411141bD8`Ub&E{C&XX ztY_`-E_fW8zVuy+m(xr)D-HYQww)d4mG(K)OgXJIU%^B1JJ(A4I}VE9n$JKVq(|K|Fj=KXq_??C|0sqW5F&y~~0sn{L zUjCGxXW;LuBj6JMEPTqXl;TqX`CozSeN->}I|2XG0siOkIdzm=(swoBe?7qUodoZa zJ=VZ4yNAIg|B-?{s%!BZrd`q_9q11(+?J;XVHPxMEc};IzZ0vuBK0^dmkQf zkEIXsbD+b0Ch*$5w`vKlPSX0_33c`gAnofd0J6PzjV63Quo||w$$V(QeZ5~AeV-r5 ze(B#!q^l{t+8{5PZwL53v1JEH{}%DyvB5-w*ZKom((96(wAemr@wA+mS2?5?OBvzd z4$1JPk^ILGH+37Dv94enj#S z;hnt828YW=k$eLvdHdT1$alycxlT~>JH(OGH-{wO z4N88$kjdpy7w3hIP9LxGx&&8wRWgPto?1}xG>H>WoA55|0qPz6c5(D?69@Gj7sV5o zJM!b=$nzsUw)|lsa`v|kz!`Dmj)Ib(1tou0xQ_A?GM6r=BI(-?8cUSUMo{VO1Qk!W z{E25=oOGsy^y3qt-tj*mj{Yfe@DQkYIFN74&x<2}UL5%aA#xW$$zKvj?y@+jZz4#( zdYL0%2TFdEIP&d6>f<(0^-|vsll*pZ(Ee5e^7>YS(mw%8enuR5eH%gY$3e-T5J&E$ zI5-a~{smzR^+f*YsjP7H@Z+?$o<<>u^7SnReXF2N9QjS+U<{NVeM>>|gW{w^-&9aK z4hfMv3`%}l9Jv{B@F*yG%?VU43vvg|az|eTl)eUW^fd{wXDcYX>YEGF7ZV5DLFwBm z_qEtZ{9fv($BMo z1H=zX&t-A+)Kxip>V+&m8bE#HqDdV2R&lTml%7txBOe!+9mHh^A#z7R$ev42Ty{M*PLDLtFaFu} zya7}_ZxTnYRUEXxMS*;$+>wuilAjPqUf-5bJDCC{e^?y3BjVsRsQBmQj{I41g}2#F6h7A{PfGKPb-Mh&X>q{v>}$_#x~h ze297qs(dbhc0WenC;j^NgY-9vqrXjvT{nStKPHZh{mlo^{^kSv<8nuS9F)BMjR)ip z%N=?9+YaE2D}NMJ`eqd#`BQR7{+u}SSHzLGzwN-i{cQ)Zk~x{;uLdO_0VS_*I!Jz# zIPyEik&g?J>j%}p>zl5UPl|)%plu)FPm><`qvxbJdi4EN=~)mmUS0q-Ze9{c{<1iD z1(cp@=4z6+K*{Umx#YJAk?Q~@zg--;9pd0lQ1V)Pt6UDr9h?KD@09$}_pUhlE(o#b zMNs8(SzPH52d{$C7hz5&`Fc?Do5YcC7b3R}lzgW+a@)nh9iZe#hb3q)u2uh!Rz)bQrpyV6Hk#7|erVW&QOdPp(ac~uJ>)KXh|3;?we2C~kC&Eg`C4)0>cl|{lzd$H z0P)KoJqN_mb3&YS%nKPOPk|aQ&xj*`RvbJBO3x*^Bd?7@+YX>@2O)oL{7F70j$FGq zxDAy2i15SISK))?6O_JF;-u%IIO(}8WV7)KsBa=&6Gx9W7^SZql)gGp@|(nw?-obC zUx?fwDEXu~a^vFQK2Y*U#tDESGwBY#93`BUP^pAlNvAFKe+izD~0IJf{x{;J%OuVGA; z{3cNH?Ly?Xfs*eOM{c_~xC4~D_OR4WrsNLJgVJ|a{^+|Xj=sx6>h~2;_57MRdbFV| zedVC^)q#?45=VZUIP#rBAJQ6qNkD+>t*gj{FsIl(}ua^D?!Oui-QqR@=Zd<+wH=S5}r$MtXjgKqFo7_ zv72xm{j0E*^a$ICU-&TfL%5#**e~#ZODMCIGcusB`!u&~c(A3VwXKEkFf=zc`TyV8 z%2$0JXl{OB;}XBO(D3uw-&?rdetJS0(pTo&KK|driDDoBxhs8(r^IY-%=a5L-fuQ> zCij@m=^ZhfvC!rN>o}VmF`4fk@LdH)vuxcu$NMR>_?g+@HyeJ@?q0LM*)S3E&QdYD zN7K49=gy=@ztwZZMC(6d$K(C>(Pp~s@I3YU@^r-gjVJxi(_-&x2lGjj&UyDW>^(lE z@4bl6$!Dhgs_k<8{3O+!S#9sE_kK&ha@_69Gd;)wQ{ej*44pao%XiNGMs+JM`u)*L zzRuYY_>Hwi>Z@Bno%bv9<#SJgeAWccc&`e{=LhrD*BjN%GTF7^1od-*b~l0D!|N`+ z*6d<7;r#qDi}&lW-LX>ha$^&9dnw;h_?&Bxnnm8tYY5nct}&{jvpUkw8jj6WF5T-&MN9e3CcW$Q%4EacKdg^MmYFEOMV?BB+vO0Nr4VQU;RXP@XA0ax~ z_a*G`u{DL`RQ}0F+s8;>0h?i~XyiFUZ$7ZhJkC2CKZnh>u-*HYe1_IZqwIHoF$V|Szgu1*bP?sflg!&eIhm!syr#|0I9G`dNSPk-95AJW1|03miU;g@I z*X`w$>3+(zvT&K!wo^_6ZaFeH8U0G3Hr#H9SKs4Yo7wK)LK)06ZFK7Y-`Z`X zf_hzApZg8NOj`(kV?=GBCD5OTwE>Nrf4GD;uszTQB=>XJL+@gFWvsRy)&_1Sj!(F8 z(9b&g@ztq(`8iW$em34feu|BQrH5;?SExfjqz>gDqn2EMY9FHvdZ{~G(cPV{JI=VI zI>ii_v+L_y3F&H!Jntg z;BD3j;ooK$38_=RfPI$Iuc}TJ8^t-kvQe}%X7?QpX7laywYQtqwV!2X;0&d@*OhvI89nAo>dD7^^No)=e%>6Ywio^~ zbyjOV`hJ`1?&oIpf3+E%5muTpckexJN8WKJKPzb>`E`^R*7=Kr?DL`dCa zlw5Lo78^&W8;955#cG#f)%YJa7MZ zyEA8Z>Y1GV>h15lnq7H}RQ5eO=gh2sCf>Y+R!;bN@6vdrjrN zx4PJx=WtG)$zAj08y}0)pV<>px=D0Sx~tvyoECd8|7Ox%oc>I@!{&vm)3;>3JJ(mg z*gi^b`cF=>EgOkivWL@lG&kWJ4X#^u+1_kiPCVH<*@@l6uJXOnh1ck8GkYw0e_g4n zrtU7jrpX?My?khlicY1_kR9I#)8(x1j)jl6b2;_%dp0-N8wSvl7--RkQrT71%)_LxFVw1@p!`S1PoNu0>&8NSQYOg!|dZJb= z$4z^9)$HNv_qIPrp7eck!jaePd|khOd;J#k3D#H$w@e_fcysqyDhMCN zcnV+XhVPRtnSVL9l%MlmseEm-C996d5BTzj>?@OD<@LDYu;b0t>n(1b3g)XA{mlXW z`O?0zh_v&_#x4VYUCX!sSB!otfK$fUN9C}T@6t^D(|7M3EcxAI{Uz@lib7 z4^ta++Q7}$U8kJ#+418`Xt$?w+U?TEmE!cPe=gkS6oxc{LhyZGE@dr|f|Ik{f-*r#7@@xDCmq}VukjONDS*&(}MvFzVSaQZC$M!|gZ z_pnu1m~1yE-Pvyc-21YD0{ua59X>gEBYjyh`j^_54Hc308|ll6(I4!~=%Xg-qbBL2 zCh4Q>b^J)lcX`5@nP=qd%QEfM8OPr3%^qK)`!!a*Iyv3^^x9P9cKW@i;pGP_n4!xj zSZ8r~i(ighTWMC^e}!?|mO)39Z~vAU{l-Uj-IaT3h`V%@qT~Oz$9{_}zf8WIXNgtm zwwiwo&Wyd5y$)dClDOS@UIUHOO!IB{>0`>jH;zYHCI4Ha{n6|+`orYbQF0au?X4{=HAyRA~p>*&Ul8@IWFO_D;qczb|>QC6(sM32bv_C~w z7az-x6=+8na>lWflW$T?!Tp|mbAn>@V)omS>BUp-ROMlKiEdvnWJXA{f=vs8orn7 z=u{YQo^D9XE0ba6exEPhYP0&w_l)#Mot6qWE$YX#cA9SP<2Q%4aKHJ$jegtPYcHu< z%v_+o{6T^GR37qum^C4G%U7R@(SM6izf+%j$wxWg@lVw{d;Qtvk7vQ}WV?2tuTnX0 zNtK#zd#^(J!q&hi*pF^!KRV_ar+H)Y`t|SejIWem4UW}Zzy3pd z^u_0p3+XzVf4{FG#Ma;O*}|)%`Sx#$jpNsSad_jG`XlVhwRnH~g97y@EKgtX#pmTI zU;QaY|B$QSGQaWWD)Z)>_b^7*GX~$ziE4dIw4Q#V%zU2N!sq!p&zJX?o0kta^31Qv zcK-(M-@yHK++RQ3ihCRG5#*SNMD$JWDtm`|aXpoUuXe-pCUT@K9dEXNFJ6V0Zqhb+ zn>{AKes2U`hr83SWyXA-sq+m~Q*tIShgeU1+RrFdX3I+#Kl>C`X3D$M_A+CLy+>ou zKC;_mG5V#O_BKg-v*(rD0(QG|86sLd^`VJwC#6zw)i&B7BlBG^gJ;-wZJzm<|e&*bp86e2Qq1Bg%TFN@#9&}jQXe~{jnzob1C2ZxoWtYv(#G^R!p4oU@JM)Fk zYpmil@8@|2%KR{;`C-{XwX^l^d8TSp`qf7B34Rv0a&4Vi&TrtX9xmuorkLCJei)x-XD3#{Jqx2$Z_WMcAW=d-oqR! z^?C_^<>o7|m)>!^S!P~-o%#GhzUcAI3iI-JZZWTZbESFpJGYt%z6saOSQTRqo^8WQ z?9G^W@NceT_f8#AcFvm26Q69LJkfq9X}^=S-$~jp&t%`G-8`M zpXw}u*B0{ahkm9&e>xaw+v<-_*mblF+hp2yzW%fr{nM`gCjDNE{kva_j|sg+;L zq$#~a&sennoM@QeMG`f}6qR;5|}R<&96)~%6J^sahK zV{Qr0$-O#~ul{|ph_bWiXLg=4_0OK4*>2hPEk?iWwbbVsi=O34v5pF!<=MZGZj)i- zlj_8)zP>$QzdjMtE=SUJF550d$dzrEV&ic3Kb$(}?eWD}BRKUiv!BR0es@ik9k0SQ zapunXorA^O^=k#{MwktB-pfmix8BNkroR~d%hLM2=a8#rz4*Mn)LYf&n9oHRm$$LT z$yw7dMnv1Xv&IRPyV{lN-BQowEVfS2n$|g2vAB)jSwdYq5~yom!v;#P*S2NnSIKi& zJu61P&ZJ4d>Ii*P4SNrq?c^yjV|V7vc)J~4kjLzEEbJ-qPDl(r(S|MNHgw#me0NnJ z_0*B~@|mv>`)=BX`R8Fl8*;il$A*vOv0*X#*SY%DPxJHPGdq_}&uTt){rd9ydFC9e zIqUA7PuID3T^YXB=H=$G8QoO4c)BDutG+R>yv131R{yKn%k#=5eU{%nb8DH{Yzbw< zxwM9VVE&`!@E*U%tI1FecOb3at}0R<$y33|${GPIZ{yoa{8;WWVpsKX+46GQmif;Yh1v3+8?a@uaa5$^DElhq*7~r!9&0n7b;kgw zo`jt>QyKmjH*c)xzN&L(i>+V7^7z;3_K|6WTED8V^3QYLi@3af47$BYbVoP(Z~u** z%St(4%e-ClL@So=^D_OX)~f#gwA1Dl?t31W((ifn@jg!6=-jyaLA8N0(|Z?x_w8I( zbx^4HI_4u)$S-cw$}(r4vxYTc8T0PN-s=dn=~r&RrZ*ec^KM+LBOmvTIqV8@Wo+o! z>~zT%J{{6)zu#g9C}4-%e0GQwVTY$~zz#PXSF0PB>QkBdc+ET?`6)5IYv6CKZu?JW zzHPakM(mZyVv$qv*$gRRGdt;s*jMWX_FsTM+r(DsdAMkh9l$rWp}JAleyxVEwS=ux*oHSv zwBb14u6v!ipD~tsefD%o>6dwTxgPv2=B_1-FFE@c!L#J~#_wp`v~LWbCkCg^hmAKH zi`S&fI{&)T;5&QiG0ndxvy^qu{cgVUt^LCCbSb3X=G&7g zM*lfi|0m4v-PnBm{Y+OV#~JEKG}6oW-!>n(U45s!uQTnsYY(D(S;+IBrUkxbn0~`H3v*G_1uwnNS zZTLtLHq<$@;CwFMnb~6WHwX0R>mRnJ`-lAV(Xh6}r&n^)PaSjJvghQ*#v%I@ny-iU zlPTXiX9>>%pIm|su7>oH@Avgz-abmcKC;+280^w^zR^DNnF94PEKe6g$~9jfS&aTO zuKp*A+_RW5&N&$Mk@mi@y@wQjrtXgd?fJKedntY5XNs^}*mJL`Jbhv@`u``_ZXHGJ z)5O`Q(b=J;*zAu3Hd|^RxT^@8-AEr;jQ*wefj{S_eX;kE!rID>^nu03v6MdWIC~b` z*t2Li-#!-O`SvEBZ@00=X=QJ`@N=HY$yDhJLs-GL#v^v;*_o?I>UvX1&$ZdSdz%&dOZe4xy%;r&R9y$vUx%o$BR zTMODidy0zFOOxtTG^Bt1ai_~k)2dfX+&r;9{HpR5GoR1e=lO{|Hk@?p17{HJeIC}L zX=XX(T+l{J%bg+hs$+Y z&Uv;b`y6rY&9h@)J&c>h-sKFl^KZL(_1DL+XK3$boJZU^SC~~_((~{N)5KB?N8VLz zvG>4K<(xK~umAZ*k^N8b%#GS#*j!inEjEt0n?HTOM9)U%YwzUS0AuNI14Oyk<$Wta zbDC(xsUsHY(eu3d+Kdgt*1SsJ(R7;!{i2DvYd5=8f+v|JA z$vbw@rFYAtwWEw#_q|$X*1l?5DsXL}@3{9>^YC)Mvge#xxP`j1(yaQrp07G}-fe?C zC#=QIGA|y8S}C5HOqZ@{`Xi(F;nuWW^9s)2yuoMn< zlasHodZ#%5b%A;pmM5=Xcs5hJ3#)g<>c7R!PsF^*oH6Ve)DT_^G%+iE&c5Lzh6TA=m@DF zExxjo9rN{J%3rZ@cxwWkHP>8-^V`h7-+c3a^Nlwf&CHwY%o}eunWJyEnBRP})qL|! z_J{3r-5giSbFRC)6@~0u zY#g__ap?ShA=~}(4chI!e#c{Zx%a#&|6G3}#E##&0Xr5O$FKY1@b_a>j&%LV*N;`Z z`N}sh4a?IPeDQg8BVRvOjQ$~4|Nr0L@i-Ky)9cB%`U7u#$X90TiYPPtd9J;RamF>T zzjWUF$gK4he=+(Cz2h;(zQ!Q?8vU#tJ&hgSnSIt6(dlXSG!{&XIf>WD zUBCYQCeI#W^I$#m{z$rQekTsZKO755JdX_n}>Dk0-HyNAUOdO+b9MOgtlfCTk zXGyPjwsa%sVExa^@|`8v>dR|qf#+ zueTFs8)4WxJvPA{r_&yjopHA@bllY$^;GFppsgu?yaQgVvy4%TFqDtNwV5~Xl^v?9 zGv|%Ga?)8v>hL6Wc#=9iNgd{W!M7)=!_NEif%$XhtZeqXDPG-kS7h6)l{K7yE}ie)jzM1@7dsmhmQT$C z{pYULhUqs$>+{XTqyAIz@a0_25xh=)4w>W8Q+*|*o~=mNGebSI?0#VW)@)m4pM}l5 z3lTg^keLtWdH2cfN7HpIRk)6QF>U|+{pXpGI(FVJ|IFUvV*O{aar~|uM~j(v_ki-R zH&vH^smQ#%aRYfNHVzgkS#xUbBRb#4$R2B^e0k7bE8jX~f2+*hXN{XW`oDbhwXkxs zh<9E;h~uu+RZhh1y8US5R{j*XfB!@Gx>8@!J5hWOB=U*$`PtRn+kW2ONMtkZ#d#-6 z?$Mb2UeuShr@g^FhgWV&)Ap%0<-ejm+eY(w-jiCsrjB2|;P;E`Z()pC$rwXFYk#*o zTDy$*rG9|M_NiO#_oe=Td*^+ra`ft5DZY7GzXIDux9NLouQyb<{fejS{>ba*|Kcp0 zbVZDH`>m#Q?Fv)zr3zE=b;dQ)wA|jmQGemdXz!-My#LG2n3J-Mwxnp`Ks+8YM(7UEl`X~PwueqM~9-Zd{ z{9c7Ab@yGvzy0x$TYn!k)*a0E_!dhkXU4S`Mu4X>)69NE%Bc74@wlX;i+jA#3ut*&lPp_2B!;3)q?8uHe1#lcc@0o_FMO z#vA>{MZVv+$n@d=iaF7@d9F8yO+vnndz3kGYT5+z6+Irq?JJXY)R)b--(|O*=+r0p z>5*@=s7%Ta_u_wu_b?8vjot2iLt1;Vx}xmI>Y7i9&%dQJohi;@stp?Vth!}-8~+=1 zQ10{<2iHV;3B36&Lh|<#dZUbkO^kzc$4bl$?*UqRXI*#hc_TAQ{zAI?&UGAUe{iJ!$^ex!wtn15+ z!Kzc$v~StNdtW(91#uyZlD>RmzFw$rEk zpSOK+@?z=t>Z_LJrk94I_jI_N@2NrU_eFy_@T z_s^H#H6i&`UZaueJnsqU-H_|oWexB8r|ZA)j&~n|sjI&576CrU-#z?cS6x~DGPCEe z-`ZWw-;~R{Bz4WF-!DJ1O#Zo-r~g!i{Ica~j_LUITcAOIzy9iK`QMh6el=xJ|NO1; z&(`mkmoI1Av*i8pJN~)60|S{FIeGfBkU$3&|Jq|7ceI=|pL^{j%+w zTmQ2CV+G_3`RA5jcKM9t=+Dkyu6?rQ{qZMzP!?!E{`lYRm49~sV&UM~r>;-lzmQcv zF`xfjbR=dVAJg#7dC@38%|^XHdezx9b{*FVLN&+pjsh-UVmeyVfple+JEr1SBI z*NlC}S~K?Wnz*%kwKd+?GuG8NyeBz2JZKG!Sv`ZJiJth2*5GhYJP}{HsH_vla-|0o zqt@f?)`zX8@ik3tI}lwg-d#NdgI03bjmM{>fSfNfQoCn3nMvuQ@jbKs>8+1E(e-$H z!+cyO(ZkkI&z={pk%73iYhW;$7;UgVVxhK>u1D>_#2inI zH;g4Q^iUT%J}lX+BrQ^2u8zm|>>6HF#bUO9;;Hs$JGNR+KJvuYhp7oGmk{=oTc3XB z@u!}2f}7@b53EytTO^%SG%HJJk+4dFS)UvlSwGg-Ga6sFW^CPH-*b!W_v(*+UJr{> z2zz=4t-k)AJ$n*^1?o_itWU(3hOWWkzULB3it2|iZFa&I%}axwiR`u{T~v?c@ZP?D zE2xd%%Cy~`xJHMppV~#KC|>72H~b>;D{l>U4O0qKS9=F`t7aCG85`;uP0IZKlJt>I)pt$S!VzIQNzvi0%A^G;b6)Jqa9yNuHM8`g{!NY9pG3b8LaFigohZ9FlS z?CBjG80)vYq{Z^`?4IZL48O3)+PdWl>*HHLWhGx6Nfc_AP9wH@_wEX7QcphH(XqJw zHg!JU{^;W!+qXXb5i7e{H`<+q(JjMrhCrB&|bIalzkeMfDf;UHKS`3RM8^ok?9iS17k_Mwm8-( z6t_?2i6`7l*fLZ&v-OE57PI@NHDkFIWGwO7y@@@2340j0vHJ4tp3%f^lAjp$mAyq} zY|s$gI&K?sk^H+upjQ;mxU>ZOyYv)d;#n&^AJYtP=H zz#OR28XNd|wLsfQN`!l9g4gArA>zJwPp<1&7tOfKF=5weVodqTa@$4bx)MVp$rnT1 zdtY=)Eg;hqx9yRoXEZTHbur4bovg9`;TI6zHQd#s2*wAJ89xOgz3H&J>3U%x+255O zij>W?|L#OGdS$D$M>-$a)pJa{F}#kFLc5Lnb&Ms4M>5VCTew9X zn*Ol_MKLlu@O)1);mb%mu0H0y`1B2oFsGo*{PD#Z=!ZvN^n0e|T-!<_ zPJ5P2Ano#drajR&949qT(?GV4JCn&L5@Wkn0RHQ-En6P88XkM{+54;w>st99tywqr z;t+EaFgfaAzjsH4S(i+VC(SzbDrQ~p*qB+T9-I2Ao56J>@lmsGD4yuuyL(+v@4$u@ zoQD-y+M3t3I-zU_1!qp$Gw1~|>-vU=h7x;dYD>PfH^w%NvA3td7=Ooi@t=D|T;zVtr>XSGU(0`?m*3>`nKa>S$1CjO&u`~5f9Tjr>62Q|&RPsepJ$g8 z`s_ymxasoj_c`tr&by}QiSFf{*4OUy*zy$_t5WNZvHN7?^j>t@n3vX!cSp}J#&>&!#q34hqD1j^;q`~ zFE{sduVi`r5$?6s>Tzz$6Q}0{7SPk9a?2mqUvg2vUv%bM?rTl+V-D- zAK}l7?;!Vjh^{|{e+_i^rtR>*1#jT*PS^hgXbS_1?~g%?Cxj_7T7t*FhkJX|^8W|8 z;#ZrIe*QDg-n1iM3*U~P>?HryaA$AY@&5>X1V7am`9BVK_NE>GpM@(=UiuSoXK&i^ ze-VBd|2kKmcc9E1_u9UeJ|3`|`DW^h&CAVe@KfAtA5H!X%b8oTt3!@Fr;9)AfwhrE~G zFT>BkJ$t_fSDrlmZ@`rYFF$X?^_cPfZv5ZnzL7uAUVp@W2Y(-M{r?Z{`}y{xx{FkN+B6<>7_@2KEpi)f7i!fgYz@8>GXMb!ZMRKwC!AG(uQ>TE#`kgUYFo@{y*+=xv9_oZ68;=oe%Ku z2l$@`_+JG0KL+?+B=M@-A>2X#((FsJoHxP;HTZJL{~7pUAMXi-9|-W50{njt@RRUL zb?&bIKMnXVz|W~OcK!c0;QtTs4(8>af3?rVO8@-*jjFWY#%w=#!z%6lU?yanadtxDy)R}QA2M48hsP4^=c!cKN3$O$ zb?x%S(A<;>-rVfFZ}Hu4@ZGnD?>VK@)ck;XemMT9z4@cf#znS6GBO)|5j>cwS6Q1; zi9Q|@XtN_RS4Rl=kD#|`uwN^ z`8JcYca-goI&Vv}Yk1G_E@Jegbz{c7ba<}bW~bA+WXl!558vCVPp3-}kNI*cV*eczek@A?HM@Ng#PVyU+@C^^XZU*o$O4aL$~15 zVqTq0Z@{O0a<|{pUQ5}1&jijqHYl|9?mV?JBTg|BiyVeWlFsw6F`l{fC5;TdYjl{` zP@QYp%g&mi0@Qv%)#sgkF1g3xa@RCW>qAYyD!_j6msl@>RTG@J75{>>Keb|;voBWx z&d8lP;8CywoK^TQvi6WW^5?{nza)ANV7zRN=NT#-9?O&mRJq}ckjPa%CZpyX@i4%Umm`IAEA=0VA8KST1`SCaf$Q1X{RmG@=2gWBg%Ix9h? zvsN7ae45tQ9}`FaHsQyyH&_8~7f0_7ad0On{UdTmenOn|9T7)p|JPS(xqTKnrBzOKI%N(wK7IEz_%kD{W zaKiQ9@A_YH`Bj(KRygu?pyaiGBKgze;1&6k-ty&6dM!}tZ4i>qMo{T(6@Lu7h=ZFz zrFT0h`F`;U(j$)i0U>fzpyUsWKSnuaxa1q;j{GKZ zYx`cuLV$`w@p z4v3?7N*p`{O8>0fk)Ibw{#|k8F9R9fs$X4dnxIaI~cnaUQRkd z*-OuFWUo`=;6?cp-&IiY)!YUrzB(cKv_R#zL7aFR#la>}@okg4>@AM`xH$4DA#xL- zZEtbpro_QRpyW@;9r@GZ$X^gg{*v%X>4hce5Vk( z?V#j$iX+!84#q({ez})pPq~BVTz=l=+CP-Nu8D(nY+Ty$fp&c2ici={d|(B*Q(WR0kGsNg`c23fE8ek zIC8b(U>zv=Cb=WuE{=S+IP(2MC&iH)7YFx&lIH_Tw)_!sbw#ld$$>ANC#(o@O4tMpl*^wopX*C>v@R^c=BYjOu;;^=7?2e*OJ zw^R5f@&T&cM&u8UgDU5J;-L0<<$uKWKPi9GaRyX6E{YTHWg(NzD_{k9O`Q1jU`z3q zgNnBflzfvo@*U#HZx?Q-y@QhP7Dp~F4)%kRAD4S6>61G+u)W4obdS9Jz=%SOZGFQSQiZ z5=VZ!IPyD%KZ9Mt3b0=sxj}Jo1eE-Kxg&o>9QiqMV_<)rUE8;yED>o&*)&S-B&BK^%EK zgq3`y5V>kl@-^bf)ry04plvU?ms7rS2V2E=kX~^x2CBYvf-63SJ>?GW5GOx7#ldlL zPZn?ioeweAUdibnxm7v;*1xmi&b#E0Xe;x9lpq-18zd_+AsgI!Q`M5aw+9wXC zK;`eS+>xIZNB)#J@@Is|odqR-UL3i1#lZzo@>k_9`|?~#@)juh2I0?OXHfOLRUElC zac~nT`R#H?zF!=9ojsBKej##v$iS9AB#zu+aqtKzc|FfkJ2@|R@G7{1wa_(ju$%?G z>Q^PG`c)@hPCCWGHn8dt@e6;2_+6X^6>bJpxH*@faQQq~^;PWSx}S61-*x$d%dfip zn#-#>j8gSA(gE7(0Hwb{?uSW_>%Pf#Z+H1Nm+t_pUL(D(`=INdbosc;_lc9=lsI@; z{4>~19Gn(c{=~tfpz7;MA@%i){Qm{{lRtRQb-y5Y^j(xYsONjKe=R8c$3T^5C#dr5 z7pFW&gd9*yf)(ICamq0z4o-k7&m(e2{A9??!=gOaZnM=l}`)_{_4lsoBak~`QgciOq0A1j?BatFu36|C*{iGzBcEdOb7@I0t= zTm+Sl^3`zCQ7vS>5dpQ{s1+yvI&shfm5w&KBi|v8e7`vIBSPeopyc<7BbO2fCqT&` zkvr+1mOFS{9DVchN8fpI^eqVcXa`^gcu5>Rm&L&=p!8MOJMtDN`8ILnV?yNGLCJTB zBiAVoZU^oBtiipU{u@+!Izjbkak-POr1%8=w)hvQcfvU34_1KF;^>{N&E|>TZr6MQ1WK2BVP_mz7mwYp8KoZn&b}d1f_2fl)nAq=$jH^??a&MencES z)8a}OD19g8j{I41rAJ)p5h8aLl)SmukuL}B^njALeGP6aTnyH~k%00Zxb`w_hAQ07`yF?#Q1INB*oh^5=!fy$eeIf;e&)#lcIUId|DZTH;`%3D{{yc7l{@Ut-}elzjw}cpE-w%*q5<~@8Ij&JZI2-3OeW9*V(5m`R9`Z=s%|%>7GxA zRp>qq=k#rzzOQ>uNjHS{W8HJ=o)fOm>D+TVhmWx@Vo$v@dOZz&JqKU;6X$|n??dO% zzwhliE_|JHTow8Y|3-&X=s9N2>6bcv`dv|o)1r3_j#Ru3)(zjs=N!6TU-!OA-scdmpD&$tu5~zpu6OU8excK+-W~NBNuP!G zIh}4(hx`jV*L_jG$^G;7XS@$zhyJ}k&v~Z9TVH3N8FZhO&N;*PK-Nz}_etxlpV46! zdd`A#`aPY#qSGHsx)W$$)9LCuY(V>|^N@2mhpC;&(@@{gS$_+<{=UxoWy$?GfZmrQ zopmcZtU}jsIHzyx^aGuKDCtJf{#vJ-=@PqfWn+bWhMecz@J~&^`(651i9ib^0@%zAaDK7bf7PPS@385898N)8Fd!OPzit z>HNkV`y{ka=`aoL{d=iCuW9J>T7<4whOT#_vtCV>c)p;|TT^G9mJZLL>vf&e4|V!m zo&HYJ-9!5aoo=qfM`#~@fO*Jyq0eVlXT3ahy`s)~`;tFrl%Rj!IM7+=P=`m*^=i)P z&vg2(PT!Yw186_g=|(ynL;K_hnRAX5^gOxPH)G$4eK+Lt{X)_$q5Vpy3yM)6Li-Z*+-2vm8vAkVjo6#9x1s0m zIcMLY&c0KfeP7CNaUWp<&UE&D(BT}qZ}6e0&p`Vvoqk)=?LhmYPPeDSeP~~C{sz}g zr$5)}FC@Q?bfCYUbalF(4*Ssl+W8xtSEpa-^iPs*1?|HRM|~37r=Y!mkK3OQJI-Mh zx?T;sUR!6qj^sJMgg(zb-E-)009|k5oc>OyU+VNLNf&%1>XXnur9=L;IO{&|Jmfk$ zhr2rKm3*D`sygeP$XB^;(C4|Xvra>Y{Hxs8>vf#d4|Mv8PCu2e^1MO&JDu)chcoE= z+1g{yISSD8lwvQ(UV%PGHRtTp(py|7oqc+e=e7@hey?=aAL?)f-RIUh{e#}(dg=7x zN25Lo?bAA4Mu%BwUvVCCeVoH)?5)@@py%v6XP=SIKDYV{-oNBIy@x)p4?645b@&L~ zC;V8{XQ6#Tf5G!3>2{(0o=&%~!xFTwI%ofyPT$t)JCf)068gOMbh^F{2he`voc>Oy zU+VNLNf+?7_DN`;()p3*hrQp2;Pbxg9JZkAUHCfdUFobhl02_t=<`0&`EjH3W6BTL zdw}*&I(=q8>Ng}^4%+8+zGh2@1!&)J9&*2&!|T{5vES;fKZCBn(m7xFap?PmzTdRY zx)~j2q3ai%)9>l@6`lTAzQ)fZXkXLm>N;#d`%CBa1D*ax|2FR*Cg8n(%K3B{egbFx zl>83of(e+>J(mtQ;QD;dJ)iFRbk?mx*RScUTi4;G&iXycx_#$xptJ6k4u{b7XUB#zRvZ5{&{Ifr`y$G5!xR*r$5o@yE^;#CEWnp4|Tec4#&{G%)Pg-K>Kr@ z{z872=N=|tSEuXgun+C8ozuV6>4Q&2eNxh;pnXQC%j$3guJ?ED{yKd_XaAP`9=}h+ z1Z?Yc7dq@f`zz=46PxJcLy&QDCyyO~hL4Uq(>#Vb* z!(F(3KhEiobCYuy8`^htx=S5)q5a5t$n)VGPIcD1_jT4=>a4et{Js=?hOdK3 z=z1v~`Z30?mv>ITtJ5Fo^hc7e0_~4=x)U9qLi-EnoTK9$_MP+l#0~ViO`XHcXZbqz z*@W&>)Y)fW@_tLu_kEzV{-F+!p!?LE)1T?|J)M3a>8_ytNT(a?@EY3tQR<#E13hOR zdQQJj%yX8U!y0s-manrkjpP|nBBi;R=`@D2cKiBEQgQ!nQx-_)U>U0}A z%t8B-bIwG%sEF6dY)~#zQ4{{_fY3tr#kD_WtCsgp#OhJOJ}_^ z9iBti?K!6(>GZcc{hg${hxQLT-CT!{(BAJe^uA@F_w5k6-Z6B&md<+TlIz`uUiXg9 zI+r@^Lf0EPr=RNdbDe%6>6UPP-*vj+i%}m!`yBLtf7x^n_jJ}f@O9R!>#Wz5-{*WV z0nc^TYwPd=y57J!{k2a2pwmA}x&^d<(&<*Z=lW9ApF!{2IrP4Do&TEW#X0AjI){FL zrq?kEy^h<^efOaIR&@3~mRyGu=yj;+>{r)e1A6WY=k$G@exlP)CEYEwztidNbvT3e zE9acc4;%4(S?GFs=z9A)>y_mXxV|s}k95|l=&%Z1ui>1&tOq9rMt0Zb8rK_qBQ*k964ab*}?lUkBamAiWN7eI0c6yVv0iy6=;7 z`qWpVJ`e2+l71W7@9K0#9qvK6beF zO49jZKK4mypVDC(+UK3q@96X=I(<#nd2V0=Hg&p|4$q){*E#)Ar=RQe3rV+x_A8w( zs6>4T?GK^%?Z`Rwb2+>}b?E)+!B_sp{f5tf#C>rNZ=L^}_wiLYq?iApgU^4=^8^#H z>71_{O1{p|ZE^h>bbUV*$^Sp!&+~AegU%0~UvfR5=R0){&tq@L{t){-_OMF#Uw*xV zc3EhbgRWPE&i9~mKR3jE8#)|7pVuoL&fqKm<9&WD`nnAC>$ad@SJ0uK_u@L``1P8v zzhGVY6Rv}E*wVd!I&4GlL*MyZ+21+5)>(I=!c{0;X*{?|4J_h^zv`)FaJ&-!hg`qD|-3QVEuhK>-}~1>c+f| SIg1&*Eqa}mf@g0Jg8u_Ty8)8` literal 0 HcmV?d00001 diff --git a/extapi/openxcas/openxcas.conf b/extapi/openxcas/openxcas.conf new file mode 100644 index 0000000..5a968e7 --- /dev/null +++ b/extapi/openxcas/openxcas.conf @@ -0,0 +1,5 @@ +[module] +name = oscamCAS +daemon = oscam_module +#daemon = oscam_module -255 +version = 1 diff --git a/extapi/openxcas/openxcas_api.h b/extapi/openxcas/openxcas_api.h new file mode 100644 index 0000000..740bb47 --- /dev/null +++ b/extapi/openxcas/openxcas_api.h @@ -0,0 +1,228 @@ +#ifndef __OPENXCAS_API_H__ +#define __OPENXCAS_API_H__ + +#include "openxcas_message.h" +#include "openxcas_smartcard.h" + +/* + * Be careful! This API is not safe in multi-process + * + */ + +enum eOPENXCAS_FILTER_TYPE { + OPENXCAS_FILTER_UNKNOWN = 0, + OPENXCAS_FILTER_ECM, + OPENXCAS_FILTER_EMM, +}; + +#ifdef __cplusplus +extern "C" { +#endif + + /* This function will be used for checking compatibility with API + * After printing information, it is terminated automatically + * Be careful! Module name & version info should set exactly + */ + void openxcas_show_info_and_exit(char * module_name, char * version_info); + + /* RETURN VALUE: status + * -1 : error + * 0 : success + */ + int openxcas_open(char * module_name); + + /* RETURN VALUE: status + * -1 : error + * 0 : success + */ + int openxcas_open_with_smartcard(char * module_name); + + /* RETURN VALUE: status + * -1 : error + * 0 : success + */ + int openxcas_close(void); + + /* + * This function should be called after opening openxcas + */ + void openxcas_debug_message_onoff(int bVerbose); + + /* RETURN VALUE: device fd + * -1 : error + * >0 : success + */ + int openxcas_get_smartcard_device(unsigned int idx); + + /* RETURN VALUE: status + * -1 : error + * 0 : success + */ + int openxcas_release_smartcard_device(unsigned int idx); + + + /* RETURN VALUE: status + * path of working directory + * + */ + char * openxcas_get_working_directory(void); + + /* RETURN VALUE: status + * -1 : error + * 0 : timeout + * 1 : success + */ + int openxcas_get_message(openxcas_msg_t * message, int wait_time); + + /* RETURN VALUE: status + * -1 : error + * 0 : timeout + * 1 : success + */ + int openxcas_put_message(int streamd_id, unsigned int sequence, + int msg_type, unsigned char *msg_buf, unsigned int msg_size); + + /* + * DVB-CSA Key API + * + */ + + /* RETURN VALUE: status + * -1 : error + * 0 : timeout + * 1 : success + */ + int openxcas_set_key(int stream_id, unsigned int sequence, + unsigned short ca_system_id, unsigned short cipher_index, + unsigned char * even, unsigned char * odd); + + /* RETURN VALUE: status + * -1 : error + * 0 : timeout + * 1 : success + * + * Be careful! + * If you call this function, + * all filter information is reset(same to call openxcas_remove_filter) + */ + int openxcas_key_not_found(int stream_id, unsigned int sequence); + + + /* for ADAPTOR */ + + /* + * Filter API + * Use for ECM & EMM + * + */ + + /* RETURN VALUE: filter_index + * -1 : error + * >= 0 : success + */ + int openxcas_add_filter(int stream_id, + int type, unsigned short ca_system_id, + unsigned short target_pid, unsigned short pid, + unsigned char * mask, unsigned char * comp, + ecmemm_callback callback_func); + + /* RETURN VALUE: status + * -1 : error + * 0 : timeout + * 1 : success + */ + int openxcas_start_filter(int stream_id, unsigned int sequence, int type); + + /* RETURN VALUE: status + * -1 : error + * 0 : timeout + * 1 : success + */ + int openxcas_stop_filter(int stream_id, int type); + + /* RETURN VALUE: status + * -1 : error + * 0 : success + */ + int openxcas_remove_filter(int stream_id, int type); + + /* RETURN VALUE: status + * -1 : error + * 0 : success + */ + int openxcas_filter_callback(int stream_id, unsigned int sequence, int type, + struct stOpenXCAS_Data * openxcas_data); + + + int openxcas_send_private_message(int stream_id, unsigned int sequence, int msg_type, + unsigned char *msg_buf, unsigned int msg_size); + + + const char * openxcas_get_time(void); + + + + + + + + + + + + + + /* RETURN VALUE: filter_index + * -1 : error + * >= 0 : success + */ + int openxcas_start_filter_ex(int stream_id, unsigned int sequence, + unsigned short pid, unsigned char * mask, unsigned char * comp, + filter_callback callback_func); + + /* RETURN VALUE: status + * -1 : error + * 0 : success + */ + int openxcas_stop_filter_ex(int stream_id, unsigned int sequence, + int filter_index); + + + int openxcas_filter_callback_ex(int stream_id, unsigned int sequence, + struct stOpenXCAS_Data * openxcas_data); + + /* RETURN VALUE: status + * -1 : error + * 0 : timeout + * 1 : success + */ + int openxcas_create_cipher_ex(int stream_id, unsigned int sequence, + unsigned short ca_system_id, + unsigned short ecm_pid, + unsigned short video_pid, unsigned short video_ecm_pid, + unsigned short audio_pid, unsigned short audio_ecm_pid, + unsigned short data_pid, unsigned short data_ecm_pid); + + /* RETURN VALUE: status + * -1 : error + * 0 : timeout + * 1 : success + */ + int openxcas_destory_cipher_ex(int stream_id, unsigned int sequence); + + /* RETURN VALUE: status + * -1 : error + * 0 : timeout + * 1 : success + */ + int openxcas_set_key_ex(int stream_id, unsigned int sequence, + unsigned short ca_system_id, + unsigned short ecm_pid, + unsigned char * even, unsigned char * odd); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extapi/openxcas/openxcas_message.h b/extapi/openxcas/openxcas_message.h new file mode 100644 index 0000000..1a156bc --- /dev/null +++ b/extapi/openxcas/openxcas_message.h @@ -0,0 +1,138 @@ +#ifndef __OPENXCAS_MESSAGE_H__ +#define __OPENXCAS_MESSAGE_H__ + +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define OPENXCAS_VERSION "1.5.0" + +#define OPENXCAS_MODULE_MAX 8 +#define OPENXCAS_MODULE_NAME_LEN 32 +#define OPENXCAS_DAEMON_NAME_LEN 64 +#define OPENXCAS_PATH_LEN 128 +#define OPENXCAS_VERSION_LEN 32 + +#define OPENXCAS_SECTION_LEN 4096 + +enum eOPENXCAS_STREAM_ID { + OPENXCAS_STREAM_1 = 0, + OPENXCAS_STREAM_2, + OPENXCAS_STREAM_MAX, +}; + +enum eECMKEYTYPE { + ECM_KEY_UNKNOWN = 0, + ECM_KEY_EVEN_ODD, + ECM_KEY_EVEN, + ECM_KEY_ODD, +}; + +enum eOPENXCAS_COMMAND { + OPENXCAS_UKNOWN_MSG = 0, + + /* OpenXCAS manager --> OpenXCAS module */ + OPENXCAS_SELECT_CHANNEL = 100, + OPENXCAS_START_PMT_ECM, + OPENXCAS_STOP_PMT_ECM, + OPENXCAS_START_CAT_EMM, + OPENXCAS_STOP_CAT_EMM, + OPENXCAS_ECM_CALLBACK, + OPENXCAS_EMM_CALLBACK, + OPENXCAS_QUIT, + OPENXCAS_BIG_MSG_FROM_MANAGER, + OPENXCAS_SMALL_MSG_FROM_MANAGER, + + /* OpenXCAS module --> OpenXCAS manager */ + OPENXCAS_START_ECM_FILTER = 200, + OPENXCAS_STOP_ECM_FILTER, + OPENXCAS_START_EMM_FILTER, + OPENXCAS_STOP_EMM_FILTER, + OPENXCAS_SET_KEY, + OPENXCAS_KEY_NOT_FOUND, + OPENXCAS_TERMINATED, + OPENXCAS_BIG_MSG_FROM_MODULE, + OPENXCAS_SMALL_MSG_FROM_MODULE, + + /* API v2.0 */ + /* OpenXCAS manager --> OpenXCAS module */ + OPENXCAS_PID_FILTER_CALLBACK = 300, + + /* OpenXCAS module --> OpenXCAS manager */ + OPENXCAS_START_PID_FILTER = 400, + OPENXCAS_STOP_PID_FILTER, + OPENXCAS_CREATE_CIPHER, + OPENXCAS_DESTROY_CIPHER, + OPENXCAS_SET_KEY_V2, + + /* + * COMMAND 1XXX : reserved for sending internal message in module + * example: use for smartcard + * + */ + OPENXCAS_PRIVIATE_CMD_START = 1000, +}; + +/* section buf + header info */ +#define OPENXCAS_MSG_MAX_LEN (OPENXCAS_SECTION_LEN + 36) + + +typedef void (*filter_callback)(int stream_id, unsigned int sequence, int filter_index, unsigned short pid, unsigned char *pBuf, int size); + +typedef void (*ecmemm_callback)(int stream_id, unsigned int sequence, int cipher_index, unsigned int ca_system_id, unsigned char *pEcm, int Len, unsigned short pid); + +typedef struct stOpenCASMessage { + long mtype; /* do not touch, used by message queue */ + + int stream_id; + unsigned int sequence; + + int cmd; + + int buf_len; + unsigned char buf[OPENXCAS_MSG_MAX_LEN]; +} openxcas_msg_t; + +#pragma pack(1) + +struct stOpenXCAS_Data { + unsigned short ca_system_id; + unsigned short cipher_index; + unsigned short pid; + + int filter_index; + + int len; + unsigned char buf[OPENXCAS_SECTION_LEN]; +}; + +struct stOpenXCASChannel { + /* If current av is transtered from satellite, + * latitude & polarisation is meaningful. + * If polarisation is (-1), channel source is unknown + * If polarisation is (-2), channel source is DVB-T + * If polarisation is (-3), channel source is DVB-C + * If polarisation is (-4), channel source is ATSC + * If polarisation is (-5), channel source is ISDB-T + * If polarisation is 0, then DVB-S(horizontal) + * If polarisation is 1, then DVB-S(vertical) + */ + int polarisation; + short latitude; + + unsigned long frequency; + + unsigned short service_id; + + unsigned short v_pid; + unsigned short a_pid; + unsigned short d_pid; +}; + +#pragma pack() + +#endif diff --git a/extapi/openxcas/openxcas_smartcard.h b/extapi/openxcas/openxcas_smartcard.h new file mode 100644 index 0000000..60cc994 --- /dev/null +++ b/extapi/openxcas/openxcas_smartcard.h @@ -0,0 +1,17 @@ +#ifndef __OPENXCARD_SMARTCARD_H__ +#define __OPENXCARD_SMARTCARD_H__ + +/* from LINUX_BSP/include/asm/tango2/scard.h */ +/* ioctl commands for user level applications */ + +#define SCARD_IOC_MAGIC 'S' +#define SCARD_IOC_WARMRESET _IO(SCARD_IOC_MAGIC, 0) +#define SCARD_IOC_CLOCKSTOP _IO(SCARD_IOC_MAGIC, 1) +#define SCARD_IOC_CLOCKSTART _IO(SCARD_IOC_MAGIC, 2) +#define SCARD_IOC_CHECKCARD _IO(SCARD_IOC_MAGIC, 3) + +#define SMARTCARD_DEV "/dev/scard" + +#define AZBOX_MODES 16 + +#endif diff --git a/gitupdate.sh b/gitupdate.sh new file mode 100644 index 0000000..a291ef3 --- /dev/null +++ b/gitupdate.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +echo "===== GIT STATUS =====" +git status + +echo "" +read -p "Podaj opis commita: " msg + +if [ -z "$msg" ]; then + echo "❌ Commit message nie może być puste." + exit 1 +fi + +git add -A +git commit -m "$msg" + +if [ $? -ne 0 ]; then + echo "❌ Brak zmian do commitowania." + exit 1 +fi + +git push origin main + +echo "✅ Commit zapisany i wypchnięty bez nadpisywania historii." + diff --git a/globals.h b/globals.h new file mode 100644 index 0000000..d1edc02 --- /dev/null +++ b/globals.h @@ -0,0 +1,2813 @@ +#ifndef GLOBALS_H_ +#define GLOBALS_H_ + +#define _GNU_SOURCE // needed for PTHREAD_MUTEX_RECURSIVE on some plattforms and maybe other things; do not remove +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) +#include +#endif + +/* + * The following hack is taken from Linux: include/linux/kconfig.h + * Original comment follows: + * Getting something that works in C and CPP for an arg that may or may + * not be defined is tricky. Here, if we have "#define CONFIG_BOOGER 1" + * we match on the placeholder define, insert the "0," for arg1 and generate + * the triplet (0, 1, 0). Then the last step cherry picks the 2nd arg (a one). + * When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when + * the last step cherry picks the 2nd arg, we get a zero. + */ +#define __ARG_PLACEHOLDER_1 0, +#define config_enabled(cfg) _config_enabled(cfg) +#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value) +#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0) +#define ___config_enabled(__ignored, val, ...) val + +#include "config.h" + +#if defined(WITH_SSL) && !defined(WITH_LIBCRYPTO) +# define WITH_LIBCRYPTO 1 +#endif + +/* For deprecated but still needed cryptography functions: + * 10002 corresponds to OpenSSL version 1.0.2*/ + +#define OPENSSL_API_COMPAT 10002 + +#if defined(__CYGWIN__) || defined(__arm__) || defined(__SH4__) || defined(__MIPS__) || defined(__MIPSEL__) || defined(__powerpc__) +# define CS_LOGFILE "/dev/tty" +#endif + +#if defined(__AIX__) || defined(__SGI__) || defined(__OSF__) || defined(__HPUX__) || defined(__SOLARIS__) || defined(__APPLE__) +# define NEED_DAEMON +#endif + +#if defined(__AIX__) || defined(__SGI__) || defined(__OSF__) || defined(__HPUX__) || defined(__SOLARIS__) || defined(__CYGWIN__) +# define NO_ENDIAN_H +#endif + +#if defined(__AIX__) || defined(__SGI__) +# define socklen_t unsigned long +#endif + +#if defined(__SOLARIS__) || defined(__FreeBSD__) || defined(__OpenBSD__) +# define BSD_COMP +#endif + +#if defined(__HPUX__) +# define _XOPEN_SOURCE_EXTENDED +#endif + +#if (defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)) && !defined(s6_addr32) +#define s6_addr32 __u6_addr.__u6_addr32 +#endif + +#ifdef __ANDROID__ +#ifndef in_port_t +#define in_port_t uint16_t +#endif +#define tcdrain(fd) ioctl(fd, TCSBRK, 1) +#endif + +#ifdef __uClinux__ +#define fork() 0 +#endif + +// Prevent warnings about openssl functions. Apple may consider 'openssl' +// deprecated but changing perfectly working portable code just because they +// introduced some proprietary API is not going to happen. +#if defined(__APPLE__) +#define __AVAILABILITY_MACROS_USES_AVAILABILITY 0 +#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6 +#endif + +#include "cscrypt/aes.h" + +#ifdef IPV6SUPPORT +#define IN_ADDR_T struct in6_addr +#define SOCKADDR sockaddr_storage +#define ADDR_ANY in6addr_any +#define DEFAULT_AF AF_INET6 +#else +#define IN_ADDR_T in_addr_t +#define SOCKADDR sockaddr_in +#define ADDR_ANY INADDR_ANY +#define DEFAULT_AF AF_INET +#endif + +#ifndef NO_ENDIAN_H +#if defined(__APPLE__) +#include +#define __BYTE_ORDER __DARWIN_BYTE_ORDER +#define __BIG_ENDIAN __DARWIN_BIG_ENDIAN +#define __LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +#elif defined(__FreeBSD__) || defined(__OpenBSD__) +#include +#define __BYTE_ORDER _BYTE_ORDER +#define __BIG_ENDIAN _BIG_ENDIAN +#define __LITTLE_ENDIAN _LITTLE_ENDIAN +#else +#include +#include +#endif +#endif + +/* =========================== + * macros + * =========================== */ +// Prevent use of unsafe functions (doesn't work for MacOSX) +#if !defined(__APPLE__) +#define strcpy(a,b) UNSAFE_STRCPY_USE_CS_STRNCPY_INSTEAD() +#define sprintf(a,...) UNSAFE_SPRINTF_USE_SNPRINTF_INSTEAD() +#define strtok(a,b,c) UNSAFE_STRTOK_USE_STRTOK_R_INSTEAD() +#define gmtime(a) UNSAFE_GMTIME_NOT_THREADSAFE_USE_CS_GMTIME_R() +#define localtime(a) UNSAFE_LOCALTIME_NOT_THREADSAFE_USE_LOCALTIME_R() +#define asctime(a) UNSAFE_ASCTIME_NOT_THREADSAFE_USE_ASCTIME_R() +#define ctime(a) UNSAFE_CTIME_NOT_THREADSAFE_USE_CS_CTIME_R() +#define gethostbyaddr(a,b,c) UNSAFE_GETHOSTBYADDR_NOT_THREADSAFE_USE_GETADDRINFO() +#define gethostent(a) UNSAFE_GETHOSTENT_NOT_THREADSAFE() +#define getprotobyname(a) UNSAFE_GETPROTOBYNAME_NOT_THREADSAFE_USE_GETPROTOBYNAME_R() +#define getservbyname(a,b) UNSAFE_GETSERVBYNAME_NOT_THREADSAFE_USE_GETSERVBYNAME_R() +#define getservbyport(a,b) UNSAFE_GETSERVBYPORT_NOT_THREADSAFE_USE_GETSERVBYPORT_R() +#define getservent() UNSAFE_GETSERVENT_NOT_THREADSAFE_USE_GETSERVENT_R() +#define getnetbyname(a) UNSAFE_GETNETBYNAME_NOT_THREADSAFE_USE_GETNETBYNAME_R +#define getnetbyaddr(a,b) UNSAFE_GETNETBYADDR_NOT_THREADSAFE_USE_GETNETBYADDR_R +#define getnetent() UNSAFE_GETNETENT_NOT_THREADSAFE_USE_GETNETENT_R +#define getrpcbyname(a) UNSAFE_GETRPCBYNAME_NOT_THREADSAFE_USE_GETRPCBYNAME_R +#define getrpcbynumber(a) UNSAFE_GETRPCBYNUMBER_NOT_THREADSAFE_USE_GETRPCBYNUMBER_R +#define getrpcent() UNSAFE_GETRPCENT_NOT_THREADSAFE_USE_GETRPCENT_R +#define ctermid(a) UNSAFE_CTERMID_NOT_THREADSAFE_USE_CTERMID_R +#define tmpnam(a) UNSAFE_TMPNAM_NOT_THREADSAFE +#define tempnam(a,b) UNSAFE_TEMPNAM_NOT_THREADSAFE +#define getlogin() UNSAFE_GETLOGIN_NOT_THREADSAFE_USE_GETLOGIN_R +#define getpwnam(a) UNSAFE_GETPWNAM_NOT_THREADSAFE_USE_GETPWNAM_R +#define getpwent() UNSAFE_GETPWENT_NOT_THREADSAFE_USE_GETPWENT_R +#define fgetpwent(a) UNSAFE_FGETPWENT_NOT_THREADSAFE_USE_FGETPWENT_R +#ifndef __ANDROID__ +#define getpwuid(a) UNSAFE_GETPWUID_NOT_THREADSAFE_USE_GETPWUID_R +#endif +#define getspent() UNSAFE_GETSPENT_NOT_THREADSAFE_USE_GETSPENT_R +#define getspnam(a) UNSAFE_GETSPNAM_NOT_THREADSAFE_USE_GETSPNAM_R +#define fgetspent(a) UNSAFE_FGETSPENT_NOT_THREADSAFE_USE_FGETSPENT_R +#define getgrnam(a) UNSAFE_GETGRNAM_NOT_THREADSAFE_USE_GETGRNAM_R +#define getgrent() UNSAFE_GETGRENT_NOT_THREADSAFE_USE_GETGRENT_R +#define getgrgid(a) UNSAFE_GETGRGID_NOT_THREADSAFE_USE_GETGRGID_R +#define fgetgrent() UNSAFE_FGETGRENT_NOT_THREADSAFE_USE_FGETGRGID_R +#define fcvt(a,b,c,d) UNSAFE_FCVT_NOT_THREADSAFE_AND_DEPRECATED +#define ecvt(a,b,c,d) UNSAFE_ECVT_NOT_THREADSAFE_AND_DEPRECATED +#define gcvt(a,b,c) UNSAFE_GCVT_NOT_THREADSAFE_AND_DEPRECATED +#define strptime(a,b,c) STRPTIME_NOT_EXISTS_ON_SOME_DM500_DB2() +#define ftime(a) FTIME_DEPRECATED() +#define timegm(a) TIMEGM_GNU_SPECIFIC_USE_CS_TIMEGM +#endif + +#ifdef UNUSED +#elif __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) +#elif defined(__LCLINT__) +# define UNUSED(x) /*@unused@*/ x +#else +# define UNUSED(x) x +#endif + +#if __GNUC__ >= 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# define MUST_CHECK_RESULT __attribute__((warn_unused_result)) +#endif + +#ifdef OK +#undef OK +#endif + +#ifdef ERROR +#undef ERROR +#endif + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +#ifdef WITH_DEBUG +# define call(arg) \ + if(arg) \ + { \ + cs_log_dbg(D_TRACE, "ERROR, function call %s returns error.",#arg); \ + return ERROR; \ + } +#else +# define call(arg) \ + if(arg) \ + { \ + return ERROR; \ + } +#endif + +// checking if (X) free(X) unneccessary since freeing a null pointer doesnt do anything +#define NULLFREE(X) {void *tmpX=X; X=NULL; free(tmpX); } + +#ifdef __CYGWIN__ +#define cs_recv(a,b,c,d) cygwin_recv(a,b,c,d) +#else +#define cs_recv(a,b,c,d) recv(a,b,c,d) +#endif + +// safe wrappers to pthread functions +#define fprintf_stderr(fmt, params...) fprintf(stderr, fmt, ##params) + +#define SAFE_PTHREAD_1ARG(a, b, c) { \ + int32_t pter = a(b); \ + if(pter != 0) \ + { \ + c("FATAL ERROR: %s() failed in %s with error %d %s\n", #a, __func__, pter, strerror(pter)); \ + } } + +#define SAFE_MUTEX_LOCK(a) SAFE_PTHREAD_1ARG(pthread_mutex_lock, a, cs_log) +#define SAFE_MUTEX_UNLOCK(a) SAFE_PTHREAD_1ARG(pthread_mutex_unlock, a, cs_log) +#define SAFE_COND_SIGNAL(a) SAFE_PTHREAD_1ARG(pthread_cond_signal, a, cs_log) +#define SAFE_COND_BROADCAST(a) SAFE_PTHREAD_1ARG(pthread_cond_broadcast, a, cs_log) +#define SAFE_RWLOCK_RDLOCK(a) SAFE_PTHREAD_1ARG(pthread_rwlock_rdlock, a, cs_log) +#define SAFE_RWLOCK_WRLOCK(a) SAFE_PTHREAD_1ARG(pthread_rwlock_wrlock, a, cs_log) +#define SAFE_RWLOCK_UNLOCK(a) SAFE_PTHREAD_1ARG(pthread_rwlock_unlock, a, cs_log) +#define SAFE_ATTR_INIT(a) SAFE_PTHREAD_1ARG(pthread_attr_init, a, cs_log) +#define SAFE_MUTEXATTR_INIT(a) SAFE_PTHREAD_1ARG(pthread_mutexattr_init, a, cs_log) +#define SAFE_CONDATTR_INIT(a) SAFE_PTHREAD_1ARG(pthread_condattr_init, a, cs_log) + +#define SAFE_MUTEX_LOCK_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_mutex_lock, a, fprintf_stderr) +#define SAFE_MUTEX_UNLOCK_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_mutex_unlock, a, fprintf_stderr) +#define SAFE_COND_SIGNAL_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_cond_signal, a, fprintf_stderr) +#define SAFE_MUTEX_UNLOCK_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_mutex_unlock, a, fprintf_stderr) +#define SAFE_ATTR_INIT_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_attr_init, a, fprintf_stderr) +#define SAFE_CONDATTR_INIT_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_condattr_init, a, fprintf_stderr) + +#define SAFE_PTHREAD_2ARG(a, b, c, d) { \ + int32_t pter = a(b, c); \ + if(pter != 0) \ + { \ + d("FATAL ERROR: %s() failed in %s with error %d %s\n", #a, __func__, pter, strerror(pter)); \ + } } + +#define SAFE_COND_WAIT(a,b) SAFE_PTHREAD_2ARG(pthread_cond_wait, a, b, cs_log) +#define SAFE_THREAD_JOIN(a,b) SAFE_PTHREAD_2ARG(pthread_join, a, b, cs_log) +#define SAFE_SETSPECIFIC(a,b) SAFE_PTHREAD_2ARG(pthread_setspecific, a, b, cs_log) +#define SAFE_MUTEXATTR_SETTYPE(a,b) SAFE_PTHREAD_2ARG(pthread_mutexattr_settype, a, b, cs_log) +#define SAFE_MUTEX_INIT(a,b) SAFE_PTHREAD_2ARG(pthread_mutex_init, a, b, cs_log) +#define SAFE_COND_INIT(a,b) SAFE_PTHREAD_2ARG(pthread_cond_init, a, b, cs_log) +#define SAFE_CONDATTR_SETCLOCK(a,b) SAFE_PTHREAD_2ARG(pthread_condattr_setclock, a, b, cs_log) + +#define SAFE_MUTEX_INIT_NOLOG(a,b) SAFE_PTHREAD_2ARG(pthread_mutex_init, a, b, fprintf_stderr) +#define SAFE_COND_INIT_NOLOG(a,b) SAFE_PTHREAD_2ARG(pthread_cond_init, a, b, fprintf_stderr) +#define SAFE_THREAD_JOIN_NOLOG(a,b) SAFE_PTHREAD_2ARG(pthread_join, a, b, fprintf_stderr) +#define SAFE_CONDATTR_SETCLOCK_NOLOG(a,b) SAFE_PTHREAD_2ARG(pthread_condattr_setclock, a, b, fprintf_stderr) + +#define SAFE_PTHREAD_1ARG_R(a, b, c, d) { \ + int32_t pter = a(b); \ + if(pter != 0) \ + { \ + c("FATAL ERROR: %s() failed in %s (called from %s) with error %d %s\n", #a, __func__, d, pter, strerror(pter)); \ + } } + +#define SAFE_MUTEX_LOCK_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_mutex_lock, a, cs_log, b) +#define SAFE_MUTEX_UNLOCK_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_mutex_unlock, a, cs_log, b) +#define SAFE_COND_SIGNAL_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_cond_signal, a, cs_log, b) +#define SAFE_COND_BROADCAST_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_cond_broadcast, a, cs_log, b) +#define SAFE_CONDATTR_INIT_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_condattr_init, a, cs_log, b) + +#define SAFE_MUTEX_LOCK_NOLOG_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_mutex_lock, a, fprintf_stderr, b) +#define SAFE_MUTEX_UNLOCK_NOLOG_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_mutex_unlock, a, fprintf_stderr, b) +#define SAFE_CONDATTR_INIT_NOLOG_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_condattr_init, a, fprintf_stderr, b) + +#define SAFE_PTHREAD_2ARG_R(a, b, c, d, e) { \ + int32_t pter = a(b, c); \ + if(pter != 0) \ + { \ + d("FATAL ERROR: %s() failed in %s (called from %s) with error %d %s\n", #a, __func__, e, pter, strerror(pter)); \ + } } + +#define SAFE_MUTEX_INIT_R(a,b,c) SAFE_PTHREAD_2ARG_R(pthread_mutex_init, a, b, cs_log, c) +#define SAFE_COND_INIT_R(a,b,c) SAFE_PTHREAD_2ARG_R(pthread_cond_init, a, b, cs_log, c) +#define SAFE_CONDATTR_SETCLOCK_R(a,b,c) SAFE_PTHREAD_2ARG(pthread_condattr_setclock, a, b, cs_log, c) + +#define SAFE_MUTEX_INIT_NOLOG_R(a,b,c) SAFE_PTHREAD_2ARG_R(pthread_mutex_init, a, b, fprintf_stderr, c) +#define SAFE_COND_INIT_NOLOG_R(a,b,c) SAFE_PTHREAD_2ARG_R(pthread_cond_init, a, b, fprintf_stderr, c) +#define SAFE_CONDATTR_SETCLOCK_NOLOG_R(a,b,c) SAFE_PTHREAD_2ARG(pthread_condattr_setclock, a, b, fprintf_stderr, c) + +#define SAFE_COND_TIMEDWAIT(a, b, c) { \ + int32_t pter; \ + if((c)->tv_nsec < 0) (c)->tv_nsec = 0; \ + if((c)->tv_nsec > 999999999) (c)->tv_nsec = 999999999; \ + pter = pthread_cond_timedwait(a, b, c); \ + if(pter != 0 && pter != ETIMEDOUT) \ + { \ + cs_log("FATAL ERROR: pthread_cond_timedwait failed in %s with error %d %s\n", __func__, pter, strerror(pter)); \ + } } + +#define SAFE_COND_TIMEDWAIT_R(a, b, c, d) { \ + int32_t pter; \ + if((c)->tv_nsec < 0) (c)->tv_nsec = 0; \ + if((c)->tv_nsec > 999999999) (c)->tv_nsec = 999999999; \ + pter = pthread_cond_timedwait(a, b, c); \ + if(pter != 0 && pter != ETIMEDOUT) \ + { \ + cs_log("FATAL ERROR: pthread_cond_timedwait failed in %s (called from %s) with error %d %s\n", __func__, d, pter, strerror(pter)); \ + } } + +#define SAFE_ATTR_SETSTACKSIZE(a,b) { \ + int32_t pter = pthread_attr_setstacksize(a, b); \ + if(pter != 0) \ + { \ + cs_log("WARNING: pthread_attr_setstacksize() failed in %s with error %d %s\n", __func__, pter, strerror(pter)); \ + } } + +#define SAFE_ATTR_SETSTACKSIZE_NOLOG(a,b) { \ + int32_t pter = pthread_attr_setstacksize(a, b); \ + if(pter != 0) \ + { \ + fprintf_stderr("WARNING: pthread_attr_setstacksize() failed in %s with error %d %s\n", __func__, pter, strerror(pter)); \ + } } + +#ifdef NO_PTHREAD_STACKSIZE +#undef SAFE_ATTR_SETSTACKSIZE +#undef SAFE_ATTR_SETSTACKSIZE_NOLOG +#define SAFE_ATTR_SETSTACKSIZE(a,b) +#define SAFE_ATTR_SETSTACKSIZE_NOLOG(a,b) +#endif + +#define CHECK_BIT(var,pos) (((var) & (1<<(pos)))? 1 : 0) + +// CW VOTING +#define MAX_VOTE_CANDIDATES 16 // Zwiększono dla obsługi większej liczby głosów +#define MIN_VOTES_REQUIRED 2 +#define CW_COMPARE_LEN 14 + +/* =========================== + * constants + * =========================== */ +#define SCM_URL "https://github.com/oscam-mirror/oscam-emu" +#define WIKI_URL "https://git.streamboard.tv/common/oscam/-/wikis" +#define BOARD_URL "https://github.com/oscam-mirror/oscam-emu/discussions" +#ifndef CS_VERSION +#define CS_VERSION "2.26.01-11942" +#endif +#ifndef CS_GIT_COMMIT +#define CS_GIT_COMMIT "a2b4c6d8" +#endif +#ifdef CS_CACHEEX +#ifdef CS_CACHEEX_AIO +#define CS_AIO_VERSION CS_VERSION +#define CS_AIO_VERSION_LEN (sizeof(CS_AIO_VERSION)) +#endif +#endif +#ifndef CS_TARGET +# define CS_TARGET "unknown" +#endif +#ifndef CS_CONFDIR +#define CS_CONFDIR "/usr/local/etc" +#endif +#ifndef CS_LOGFILE +#define CS_LOGFILE "/var/log/oscam.log" +#endif +#define CS_QLEN 128 // size of request queue +#define CS_MAXPROV 32 +#define CS_MAXPORTS 32 // max server ports +#define CS_CLIENT_HASHBUCKETS 32 +#define CS_SERVICENAME_SIZE 32 + +#define CS_ECMSTORESIZE 16 // use MD5() +#define CS_EMMSTORESIZE 16 // use MD5() +#define CS_CLIENT_TIMEOUT 5000 +#define CS_CLIENT_MAXIDLE 120 +#define CS_BIND_TIMEOUT 120 +#define CS_DELAY 0 +#define CS_ECM_RINGBUFFER_MAX 0x10 // max size for ECM last responsetimes ringbuffer. Keep this set to power of 2 values! + +// Support for multiple CWs per channel and other encryption algos +#define WITH_EXTENDED_CW 1 + +#define MAX_ECM_SIZE 1024 +#define MAX_EMM_SIZE 1024 + +#define MAX_CMD_SIZE 0xff + 5 // maximum value from length byte + command header + +#ifdef WITH_EMU +#define CS_EMMCACHESIZE 1024 // nr of EMMs that EMU reader will cache +#else +#define CS_EMMCACHESIZE 512 // nr of EMMs that each reader will cache +#endif +#define MSGLOGSIZE 64 // size of string buffer for a ecm to return messages + +#define D_TRACE 0x0001 // Generate very detailed error/trace messages per routine +#define D_ATR 0x0002 // Debug ATR parsing, dump of ecm, cw +#define D_READER 0x0004 // Debug Reader/Proxy Process +#define D_CLIENT 0x0008 // Debug Client Process +#define D_IFD 0x0010 // Debug IFD+protocol +#define D_DEVICE 0x0020 // Debug Reader I/O +#define D_EMM 0x0040 // Dumps EMM +#define D_DVBAPI 0x0080 // Debug DVBAPI +#define D_LB 0x0100 // Debug Loadbalancer/ECM handler +#define D_CACHEEX 0x0200 // Debug CACHEEX +#define D_CLIENTECM 0x0400 // Debug Client ECMs +#define D_CSP 0x0800 // Debug CSP +#define D_CWC 0x1000 // Debug CWC +#ifdef CS_CACHEEX_AIO +#define D_CW_CACHE 0x2000 // Debug CW Cache +#endif +#define D_ALL_DUMP 0xFFFF // dumps all + +#ifdef CS_CACHEEX_AIO +#define MAX_DEBUG_LEVELS 14 +#else +#define MAX_DEBUG_LEVELS 13 +#endif + +/////// phoenix readers which need baudrate setting and timings need to be guarded by OSCam: BEFORE R_MOUSE +#define R_DB2COM1 0x1 // Reader Dbox2 @ com1 +#define R_DB2COM2 0x2 // Reader Dbox2 @ com1 +#define R_SC8in1 0x3 // Reader Sc8in1 or MCR +#define R_MP35 0x4 // AD-Teknik Multiprogrammer 3.5 and 3.6 (only usb tested) +#define R_MOUSE 0x5 // Reader smartcard mouse +/////// internal readers (Dreambox, Coolstream, IPBox) are all R_INTERNAL, they are determined compile-time +#define R_INTERNAL 0x6 // Reader smartcard intern +#define R_SMART 0x7 // Reader smartcard (generic) +/////// readers that do not reed baudrate setting and timings are guarded by reader itself (large buffer built in): AFTER R_SMART +#define R_PCSC 0x8 // PCSC +#define R_DRECAS 0x9 // Reader DRECAS +#define R_EMU 0x17 // Reader EMU +/////// proxy readers after R_CS378X +#define R_CAMD35 0x20 // Reader cascading camd 3.5x +#define R_CAMD33 0x21 // Reader cascading camd 3.3x +#define R_NEWCAMD 0x22 // Reader cascading newcamd +#define R_RADEGAST 0x23 // Reader cascading radegast +#define R_CS378X 0x24 // Reader cascading camd 3.5x TCP +#define R_CONSTCW 0x25 // Reader for Constant CW +#define R_CSP 0x26 // Cache CSP +#define R_GHTTP 0x27 // Reader ghttp +#define R_SCAM 0x28 // Reader cascading scam +/////// peer to peer proxy readers after R_CCCAM +#define R_GBOX 0x30 // Reader cascading gbox +#define R_CCCAM 0x35 // Reader cascading cccam +#define R_PANDORA 0x36 // Reader cascading pandora +#define R_SERIAL 0x80 // Reader serial +#define R_IS_NETWORK 0x60 +#define R_IS_CASCADING 0xE0 + +#define is_network_reader(__X) (__X->typ & R_IS_NETWORK) +#define is_cascading_reader(__X) (__X->typ & R_IS_CASCADING) +#define is_smargo_reader(__X) (__X->crdr && strcmp(__X->crdr->desc, "smargo") == 0) + +// ECM rc codes: +/////// all found +#define E_FOUND 0 +#define E_CACHE1 1 +#define E_CACHE2 2 +#define E_CACHEEX 3 +/////// all notfound, some error or problem +#define E_NOTFOUND 4 // for selection of found, use < E_NOTFOUND +#define E_TIMEOUT 5 +#define E_SLEEPING 6 +#define E_FAKE 7 +#define E_INVALID 8 +#define E_CORRUPT 9 +#define E_NOCARD 10 +#define E_EXPDATE 11 +#define E_DISABLED 12 +#define E_STOPPED 13 // for selection of error, use <= E_STOPPED and exclude selection of found + +#define E_ALREADY_SENT 101 +#define E_WAITING 102 +#define E_99 99 // this code is undocumented +#define E_UNHANDLED 100 // for selection of unhandled, use >= E_UNHANDLED + +#define CS_MAX_MOD 20 +#define MOD_CONN_TCP 1 +#define MOD_CONN_UDP 2 +#define MOD_CONN_NET 3 +#define MOD_CONN_SERIAL 4 +#define MOD_NO_CONN 8 + +#define EMM_UNIQUE 1 +#define EMM_SHARED 2 +#define EMM_GLOBAL 4 +#define EMM_UNKNOWN 8 + +// Listener Types +#define LIS_CAMD33TCP 1 +#define LIS_CAMD35UDP 2 +#define LIS_CAMD35TCP 4 +#define LIS_NEWCAMD 8 +#define LIS_CCCAM 16 +#define LIS_GBOX 32 +#define LIS_RADEGAST 64 +#define LIS_DVBAPI 128 +#define LIS_CONSTCW 256 +#define LIS_SERIAL 1024 +#define LIS_CSPUDP 2048 +#define LIS_SCAM 4096 + +// EMM types: +#define UNKNOWN 0 +#define UNIQUE 1 +#define SHARED 2 +#define GLOBAL 3 + +#define NCD_AUTO 0 +#define NCD_524 1 +#define NCD_525 2 + +// moved from reader-common.h +#define UNKNOWN 0 +#define CARD_NEED_INIT 1 +#define CARD_INSERTED 2 +#define CARD_FAILURE 3 +#define NO_CARD 4 +#define READER_DEVICE_ERROR 5 + +// moved from stats +#define DEFAULT_REOPEN_SECONDS 30 +#define DEFAULT_MIN_ECM_COUNT 5 +#define DEFAULT_MAX_ECM_COUNT 500 +#define DEFAULT_NBEST 1 +#define DEFAULT_NFB 1 +#define DEFAULT_RETRYLIMIT 0 +#define DEFAULT_LB_MODE 0 +#define DEFAULT_LB_STAT_CLEANUP 336 +#define DEFAULT_UPDATEINTERVAL 240 +#define DEFAULT_LB_AUTO_BETATUNNEL 1 +#define DEFAULT_LB_AUTO_BETATUNNEL_MODE 0 +#define DEFAULT_LB_AUTO_BETATUNNEL_PREFER_BETA 50 + +#define DEFAULT_MAX_CACHE_TIME 15 +#define DEFAULT_MAX_HITCACHE_TIME 15 + +#define DEFAULT_LB_AUTO_TIMEOUT 0 +#define DEFAULT_LB_AUTO_TIMEOUT_P 30 +#define DEFAULT_LB_AUTO_TIMEOUT_T 300 + +enum {E1_GLOBAL = 0, E1_USER, E1_READER, E1_SERVER, E1_LSERVER}; + +// LB blocking events: +enum {E2_GLOBAL = 0, E2_GROUP, E2_CAID, E2_IDENT, E2_CLASS, E2_CHID, E2_QUEUE, E2_OFFLINE, + E2_SID, E2_CCCAM_NOCARD, + // From here only LB nonblocking events: + E2_CCCAM_NOK1, E2_CCCAM_NOK2, E2_CCCAM_LOOP, E2_WRONG_CHKSUM, E2_RATELIMIT + }; + +#define LB_NONBLOCK_E2_FIRST E2_CCCAM_NOK1 + +#define CTA_RES_LEN 512 + +#define MAX_ATR_LEN 33 // max. ATR length +#define MAX_HIST 15 // max. number of historical characters + +#define MAX_SIDBITS 64 // max services +#define SIDTABBITS uint64_t // 64bit type for services, if a system does not support this type, +// please use a define and define it as uint32_t / MAX_SIDBITS 32 + +#define BAN_UNKNOWN 1 // Failban mask for anonymous/ unknown contact +#define BAN_DISABLED 2 // Failban mask for Disabled user +#define BAN_SLEEPING 4 // Failban mask for sleeping user +#define BAN_DUPLICATE 8 // Failban mask for duplicate user + +#define MAX_HTTP_DYNDNS 3 // maximum allowed Dyndns addresses for webif access + +#define CHECK_WAKEUP 1 +#define CHECK_ANTICASCADER 2 +#define CHECK_ECMCACHE 3 + +#define AVAIL_CHECK_CONNECTED 0 +#define AVAIL_CHECK_LOADBALANCE 1 + +#define ECM_FMT_LEN 109 // 64 +#define CXM_FMT_LEN 209 // 160 + +#define LB_MAX_STAT_TIME 10 + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#define OSCAM_SIGNAL_WAKEUP SIGCONT +#else +#define OSCAM_SIGNAL_WAKEUP SIGRTMAX-2 +#endif + +#define READER_ACTIVE 0x01 +#define READER_FALLBACK 0x02 +#define READER_LOCAL 0x04 +#define READER_CACHEEX 0x08 + +#define REQUEST_SENT 0x10 +#define REQUEST_ANSWERED 0x20 + +#define CW_MODE_ONE_CW 0 +#define CW_MODE_MULTIPLE_CW 1 +#define CW_TYPE_VIDEO 0 +#define CW_TYPE_AUDIO 1 +#define CW_TYPE_DATA 2 +#define CW_ALGO_CSA 0 +#define CW_ALGO_DES 1 +#define CW_ALGO_AES128 2 +#define CW_ALGO_CSA_ALT 3 +#define CW_ALGO_MODE_ECB 0 +#define CW_ALGO_MODE_CBC 1 + +#define SIZE_SHORTDAY 8 +#define MAXALLOWEDTF 1001 // 10 allowed time frame slots for everyday + all [(3 + 1 + 10*(12) + 1)*8] +extern const char *shortDay[SIZE_SHORTDAY]; +extern const char *weekdstr; + +/* =========================== + * Default Values + * =========================== */ +#define DEFAULT_INACTIVITYTIMEOUT 0 +#define DEFAULT_TCP_RECONNECT_TIMEOUT 30 +#define DEFAULT_NCD_KEEPALIVE 0 + +#define DEFAULT_CC_MAXHOPS 10 +#define DEFAULT_CC_RESHARE -1 // Use global cfg +#define DEFAULT_CC_IGNRSHR -1 // Use global cfg +#define DEFAULT_CC_STEALTH -1 // Use global cfg +#define DEFAULT_CC_KEEPALIVE 0 +#define DEFAULT_CC_RECONNECT 12000 +#define DEFAULT_CC_RECV_TIMEOUT 2000 + +#define DEFAULT_AC_USERS -1 // Use global cfg +#define DEFAULT_AC_PENALTY -1 // Use global cfg + +// Return MPEG section length +#define SCT_LEN(sct) (3+((sct[1]&0x0f)<<8)+sct[2]) +// Used by readers +#define MAX_LEN 256 + +#define NO_CAID_VALUE 0xfffe +#define NO_PROVID_VALUE 0xfffffe +#define NO_SRVID_VALUE 0xfffe + +// If NULL return empty string +#define ESTR(x) ((x) ? (x) : "") + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + See: http://stackoverflow.com/questions/10269685/kernels-container-of-any-way-to-make-it-iso-conforming + http://www.kroah.com/log/linux/container_of.html +*/ +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) - offsetof(type, member) + \ + (&((type *) 0)->member == (ptr)) * 0)) + +/* =========================== + * global structures + * =========================== */ +struct timeb +{ + time_t time; + int64_t millitm; +}; + +typedef struct cs_mutexlock +{ + int32_t timeout; + pthread_mutex_t lock; + pthread_cond_t writecond, readcond; + const char *name; + int8_t flag; + int16_t writelock, readlock; +} CS_MUTEX_LOCK; + +#include "oscam-llist.h" + +typedef struct s_caidvaluetab_data +{ + uint16_t caid; + uint16_t value; +} CAIDVALUETAB_DATA; + +typedef struct s_caidvaluetab +{ + int32_t cvnum; + CAIDVALUETAB_DATA *cvdata; +} CAIDVALUETAB; + +typedef struct s_classtab +{ + uint8_t an; + uint8_t bn; + uint8_t *aclass; + uint8_t *bclass; +} CLASSTAB; + +typedef struct s_caidtab_data +{ + uint16_t caid; + uint16_t mask; + uint16_t cmap; +} CAIDTAB_DATA; + +typedef struct s_caidtab +{ + int32_t ctnum; + CAIDTAB_DATA *ctdata; +} CAIDTAB; + +typedef struct s_tuntab_data +{ + uint16_t bt_caidfrom; + uint16_t bt_caidto; + uint16_t bt_srvid; +} TUNTAB_DATA; + +typedef struct s_tuntab +{ + int32_t ttnum; + TUNTAB_DATA *ttdata; +} TUNTAB; + +typedef struct s_sidtab +{ + char label[64]; +#ifdef CS_CACHEEX_AIO + uint8_t disablecrccws_only_for_exception; + uint8_t no_wait_time; + uint8_t lg_only_exception; +#endif + uint16_t num_caid; + uint16_t num_provid; + uint16_t num_srvid; + uint16_t *caid; + uint32_t *provid; + uint16_t *srvid; + struct s_sidtab *next; +} SIDTAB; + +typedef struct s_filter +{ + uint16_t caid; + uint8_t nprids; + uint32_t prids[CS_MAXPROV]; +} FILTER; + +typedef struct s_ftab +{ + int32_t nfilts; + FILTER *filts; +} FTAB; + +typedef struct s_ncd_ftab +{ + int32_t nfilts; + FILTER filts[16]; +} NCD_FTAB; + +struct ncd_port +{ + bool ncd_key_is_set; + uint8_t ncd_key[14]; + NCD_FTAB ncd_ftab; +}; + +typedef struct s_port +{ + int32_t fd; + int32_t s_port; + struct ncd_port *ncd; // newcamd specific settings +} PORT; + +typedef struct s_ptab +{ + int32_t nports; + PORT ports[CS_MAXPORTS]; +} PTAB; + +typedef struct aes_entry +{ + uint16_t keyid; + uint16_t caid; + uint32_t ident; + uint8_t plainkey[16]; + AES_KEY key; + struct aes_entry *next; +} AES_ENTRY; + +struct aes_keys +{ + AES_KEY aeskey_encrypt; // encryption key needed by monitor and used by camd33, camd35 + AES_KEY aeskey_decrypt; // decryption key needed by monitor and used by camd33, camd35 +}; + +struct s_ecm +{ + uint8_t ecmd5[CS_ECMSTORESIZE]; + uint8_t cw[16]; + uint16_t caid; + uint64_t grp; + struct s_reader *reader; + int32_t rc; + time_t time; +}; + +struct s_emmstat +{ + uint8_t emmd5[CS_EMMSTORESIZE]; + uint8_t type; + int32_t count; + struct timeb firstwritten; + struct timeb lastwritten; +}; + +struct s_emmcache +{ + uint8_t emmd5[CS_EMMSTORESIZE]; + uint8_t type; + uint16_t len; + uint8_t emm[MAX_EMM_SIZE]; + struct timeb firstseen; + struct timeb lastseen; +}; + +struct s_csystem_emm_filter +{ + uint8_t type; + uint8_t enabled; + uint8_t filter[16]; + uint8_t mask[16]; +}; + +typedef struct v_ban // Failban listmember +{ + int32_t v_count; + IN_ADDR_T v_ip; + int32_t v_port; + struct timeb v_time; + bool acosc_entry; + int32_t acosc_penalty_dur; + char *info; +} V_BAN; + +typedef struct s_cacheex_stat_entry // Cacheex stats listmember +{ + int32_t cache_count; + time_t cache_last; + uint16_t cache_caid; + uint16_t cache_srvid; + uint32_t cache_prid; + int8_t cache_direction; // 0 = push / 1 = got +#ifdef CS_CACHEEX_AIO + int32_t cache_count_lg; +#endif +} S_CACHEEX_STAT_ENTRY; + +typedef struct s_entitlement // contains entitlement Info +{ + uint64_t id; // the element ID + uint32_t type; // enumerator for tier,chid whatever + // 0="", 1="Package", 2="PPV-Event", 3="chid", 4="tier", 5 = "class", 6 = "PBM". 7 = "seca-admin" + uint16_t caid; // the caid of element + uint32_t provid; // the provid of element + uint32_t class; // the class needed for some systems + time_t start; // startdate + time_t end; // enddate +#ifdef WITH_EMU + bool isKey; + bool isData; + char name[8]; + uint8_t *key; + uint32_t keyLength; +#endif +} S_ENTITLEMENT; + +struct s_client; +struct ecm_request_t; +struct emm_packet_t; +struct cmd_packet_t; +struct s_ecm_answer; +struct demux_s; + +#define DEFAULT_MODULE_BUFsize 1024 + +struct s_module +{ + const char *desc; + int8_t type; + int8_t large_ecm_support; + int16_t listenertype; + //int32_t s_port; + IN_ADDR_T s_ip; + uint16_t bufsize; + void *(*s_handler)(struct s_client *, uint8_t *, int32_t); + void (*s_init)(struct s_client *); + int32_t (*recv)(struct s_client *, uint8_t *, int32_t); + void (*send_dcw)(struct s_client *, struct ecm_request_t *); + void (*cleanup)(struct s_client *); + int32_t (*c_recv_chk)(struct s_client *, uint8_t *, int32_t *, uint8_t *, int32_t); + int32_t (*c_init)(struct s_client *); + int32_t (*c_send_ecm)(struct s_client *, struct ecm_request_t *); + int32_t (*c_send_emm)(struct emm_packet_t *); + int32_t (*c_available)(struct s_reader *, int32_t, struct ecm_request_t *); // Schlocke: available check for load-balancing, + // params: + // rdr (reader to check) + // int32_t checktype (0=return connected, 1=return loadbalance-avail) return int + void (*c_idle)(void); // Schlocke: called when reader is idle + void (*s_idle)(struct s_client *); + void (*s_peer_idle)(struct s_client *); + void (*c_card_info)(void); // Schlocke: request card infos + + int32_t (*c_capmt)(struct s_client *, struct demux_s *); + +#ifdef CS_CACHEEX + int32_t (*c_cache_push)(struct s_client *, struct ecm_request_t *); // Cache push + int32_t (*c_cache_push_chk)(struct s_client *, struct ecm_request_t *); // Cache push Node Check, 0=no push +#endif + int32_t c_port; + PTAB ptab; + int32_t num; +}; + +struct s_ATR; + +struct s_cardreader_settings +{ + uint32_t ETU; + uint32_t EGT; + uint8_t P; + uint32_t I; + uint32_t F; + uint32_t Fi; + uint8_t Di; + uint8_t Ni; + uint32_t WWT; + uint32_t BGT; + uint8_t D; +}; + +struct s_cardreader +{ + const char *desc; + int32_t (*reader_init)(struct s_reader *); + int32_t (*get_status)(struct s_reader *, int *); + int32_t (*activate)(struct s_reader *, struct s_ATR *); + int32_t (*transmit)(struct s_reader *, uint8_t *sent, uint32_t size, uint32_t expectedlen, uint32_t delay, uint32_t timeout); + int32_t (*receive)(struct s_reader *, uint8_t *data, uint32_t size, uint32_t delay, uint32_t timeout); + int32_t (*lock_init)(struct s_reader *); + void (*lock)(struct s_reader *); + void (*unlock)(struct s_reader *); + int32_t (*close)(struct s_reader *); + int32_t (*set_parity)(struct s_reader *, uint8_t parity); + int32_t (*write_settings)(struct s_reader *, struct s_cardreader_settings *s); + int32_t (*set_protocol)(struct s_reader *, uint8_t *params, uint32_t *length, uint32_t len_request); + int32_t (*set_baudrate)(struct s_reader *, uint32_t baud); // set only for readers which need baudrate setting and timings need to be guarded by OSCam + int32_t (*card_write)(struct s_reader *pcsc_reader, const uint8_t *buf, uint8_t *cta_res, uint16_t *cta_lr, int32_t l); + void (*display_msg)(struct s_reader *, char *msg); + + int32_t (*do_reset)(struct s_reader *, struct s_ATR *, int32_t (*rdr_activate_card)(struct s_reader *, struct s_ATR *, uint16_t deprecated), int32_t (*rdr_get_cardsystem)(struct s_reader *, struct s_ATR *)); + + bool (*set_DTS_RTS)(struct s_reader *, int32_t *dtr, int32_t *rts); + + int32_t typ; // fixme: workaround, remove when all old code is converted + + int8_t max_clock_speed; // 1 for reader->typ > R_MOUSE + int8_t need_inverse; // 0 = reader does inversing; 1 = inversing done by oscam + //io_serial config + int8_t flush; + int8_t read_written; // 1 = written bytes has to read from device + bool skip_extra_atr_parsing; + bool skip_t1_command_retries; + bool skip_setting_ifsc; +}; + +struct s_cardsystem +{ + const char *desc; + const uint16_t *caids; + int32_t (*card_init)(struct s_reader *reader, struct s_ATR *); + void (*card_done)(struct s_reader *reader); + int32_t (*card_info)(struct s_reader *); + void (*poll_status)(struct s_reader *); + int32_t (*do_ecm)(struct s_reader *, const struct ecm_request_t *, struct s_ecm_answer *); + int32_t (*do_emm_reassembly)(struct s_reader *, struct s_client *, struct emm_packet_t *); // Returns 1/true if the EMM is ready to be written in the card + int32_t (*do_emm)(struct s_reader *, struct emm_packet_t *); + int32_t (*do_rawcmd)(struct s_reader *, struct cmd_packet_t *); + void (*post_process)(struct s_reader *); + int32_t (*get_emm_type)(struct emm_packet_t *, struct s_reader *); + int32_t (*get_emm_filter)(struct s_reader *, struct s_csystem_emm_filter **, uint32_t *); + int32_t (*get_emm_filter_adv)(struct s_reader *, struct s_csystem_emm_filter **, uint32_t *, uint16_t, uint32_t, uint16_t, uint16_t, uint16_t, uint32_t); + int32_t (*get_tunemm_filter)(struct s_reader *, struct s_csystem_emm_filter **, uint32_t *); +}; + +typedef struct cw_extendted_t +{ +#ifdef WITH_EXTENDED_CW + uint8_t mode; + uint8_t audio[4][16]; // 4 x odd/even pairs of 8 byte CWs + uint8_t data[16]; // odd/even pair of 8 byte CW or 16 byte IV + uint8_t session_word[32]; // odd/even pair of 16 byte CW + uint8_t algo; // CSA, DES or AES128 + uint8_t algo_mode; // ECB or CBC +#else + uint8_t disabled; +#endif +} EXTENDED_CW; + +typedef struct s_cw_vote { + uint8_t cw[16]; + uint8_t votes; + uint8_t local_votes; + struct s_reader *voters[MAX_VOTE_CANDIDATES]; +} s_cw_vote; + +// CW Vote settings +typedef struct s_cw_vote_caid_data +{ + uint16_t caid; +} CW_VOTE_CAID_DATA; + +typedef struct s_cw_vote_caid_tab +{ + int32_t cvcnum; + CW_VOTE_CAID_DATA *cvcdata; +} CW_VOTE_CAID_TAB; + +typedef struct ecm_request_t +{ + uint8_t ecm[MAX_ECM_SIZE]; + uint8_t cw[16]; + EXTENDED_CW cw_ex; + uint8_t ecmd5[CS_ECMSTORESIZE]; + int16_t ecmlen; + uint16_t caid; + uint16_t ocaid; // original caid, used for betatunneling + uint16_t srvid; + uint16_t onid; + uint16_t tsid; + uint16_t pmtpid; + uint32_t ens; // enigma namespace + uint32_t vpid; // videopid + uint16_t chid; + uint16_t pid; + uint16_t idx; + uint32_t prid; + struct s_reader *selected_reader; + struct s_ecm_answer *matching_rdr; // list of matching readers + const struct s_reader *fallback; // fallback is the first fallback reader in the list matching_rdr + struct s_client *client; // contains pointer to 'c' client while running in 'r' client + uint64_t grp; + int32_t msgid; // client pending table index + uint8_t stage; // processing stage in server module + int8_t rc; + uint8_t rcEx; + struct timeb tps; // incoming time stamp + int8_t btun; // mark er as betatunneled + uint16_t reader_avail; // count of available readers for ecm + uint16_t readers; // count of available used readers for ecm + uint16_t reader_requested; // count of real requested readers + uint16_t localreader_count; // count of selected local readers + uint16_t cacheex_reader_count; // count of selected cacheex mode-1 readers + uint16_t fallback_reader_count; // count of selected fb readers + uint16_t reader_count; // count of selected not fb readers + int8_t preferlocalcards; + int8_t checked; // for doublecheck + uint8_t cw_checked[16]; // for doublecheck + int8_t readers_timeout_check; // set to 1 after ctimeout occurs and readers not answered are checked + uint32_t client_timeout; // clienttimeout per CAID + struct s_reader *origin_reader; + +#if defined MODULE_CCCAM + void *origin_card; // CCcam preferred card! +#endif + +#if defined MODULE_GBOX + uint32_t gbox_crc; // rcrc for gbox, used to identify ECM task in peer responses + uint16_t gbox_cw_src_peer; + uint16_t gbox_ecm_src_peer; + uint8_t gbox_ecm_dist; + uint8_t gbox_ecm_status; + LLIST *gbox_cards_pending; // type gbox_card_pending +#endif + + void *src_data; + uint32_t csp_hash; // csp has its own hash + + struct s_client *cacheex_src; // Cacheex origin +#ifdef CS_CACHEEX + int8_t cacheex_pushed; // to avoid duplicate pushs + uint8_t csp_answered; // =1 if er get answer by csp + LLIST *csp_lastnodes; // last 10 Cacheex nodes atm cc-proto-only + uint32_t cacheex_wait_time; // cacheex wait time in ms + uint8_t cacheex_wait_time_expired; // =1 if cacheex wait_time expires + uint16_t cacheex_mode1_delay; // cacheex mode 1 delay + uint8_t cacheex_hitcache; // =1 if wait_time due hitcache + void *cw_cache; // pointer to cw stored in cache +#endif +#ifdef CS_CACHEEX_AIO + int32_t ecm_time; // ecm-time in ms + uint8_t localgenerated; // flag for local generated CW +#endif + uint32_t cw_count; + uint8_t from_csp; // =1 if er from csp cache + uint8_t from_cacheex; // =1 if er from cacheex client pushing cache + uint8_t from_cacheex1_client; // =1 if er from cacheex-1 client + char msglog[MSGLOGSIZE]; + uint8_t cwc_cycletime; + uint8_t cwc_next_cw_cycle; +#ifdef CW_CYCLE_CHECK + char cwc_msg_log[MSGLOGSIZE]; +#endif +#ifdef WITH_STAPI5 + char dev_name[20]; +#endif + struct ecm_request_t *parent; + struct ecm_request_t *next; +#ifdef HAVE_DVBAPI + uint8_t adapter_index; +#endif + uint32_t vote_pool_session; // sesja dla vote_pool - zapobiega wielokrotnemu czyszczeniu + struct s_cw_vote vote_pool[MAX_VOTE_CANDIDATES]; +} ECM_REQUEST; + +struct s_ecm_answer +{ + uint8_t status; + struct s_reader *reader; + ECM_REQUEST *er; + int8_t rc; + uint8_t rcEx; + uint8_t cw[16]; + EXTENDED_CW cw_ex; + char msglog[MSGLOGSIZE]; + struct timeb time_request_sent; // using for evaluate ecm_time + int32_t ecm_time; + uint16_t tier; // only filled by local videoguard reader atm +#ifdef WITH_LB + int32_t value; + int32_t time; +#endif + struct s_ecm_answer *next; + CS_MUTEX_LOCK ecmanswer_lock; + struct s_ecm_answer *pending; + struct s_ecm_answer *pending_next; + bool is_pending; +}; + +struct s_acasc_shm +{ + uint16_t ac_count : 15; + uint16_t ac_deny : 1; +}; + +struct s_acasc +{ + uint16_t stat[10]; + uint8_t idx; // current active index in stat[] +}; + +struct s_cwresponse +{ + int32_t duration; + time_t timestamp; + int32_t rc; +}; + +struct s_cascadeuser +{ + uint16_t caid; + uint32_t prid; + uint16_t srvid; + time_t time; + int8_t cwrate; +}; + +typedef struct sidtabs +{ + SIDTABBITS ok; // positive services + SIDTABBITS no; // negative services +} SIDTABS; + +struct s_zap_list +{ + uint16_t caid; + uint32_t provid; + uint16_t chid; + uint16_t sid; + int8_t request_stage; + time_t lasttime; +}; + +// EMM reassemply +struct emm_rass +{ + int16_t emmlen; + int32_t provid; + uint8_t emm[MAX_EMM_SIZE]; +}; + +struct s_client +{ + uint32_t tid; + int8_t init_done; + pthread_mutex_t thread_lock; + int8_t thread_active; + int8_t kill; + int8_t kill_started; + LLIST *joblist; + IN_ADDR_T ip; + in_port_t port; + time_t login; // connection + time_t logout; // disconnection + time_t last; + time_t lastswitch; + time_t lastemm; + time_t lastecm; + time_t expirationdate; + uint32_t allowedtimeframe[SIZE_SHORTDAY][24][2]; // day[0-sun to 6-sat, 7-ALL],hours,minutes use as binary flags to reduce mem usage + uint8_t allowedtimeframe_set; // flag for internal use to mention if allowed time frame is used + int8_t c35_suppresscmd08; + uint8_t c35_sleepsend; + int8_t ncd_keepalive; + int8_t disabled; + uint64_t grp; + int8_t crypted; + int8_t dup; + LLIST *aureader_list; + int8_t autoau; + LLIST *ra_buf; // EMM reassembly buffer for viaccess + struct emm_rass *cw_rass; // EMM reassembly buffer for cryptoworks + int8_t monlvl; + CAIDTAB ctab; + TUNTAB ttab; + SIDTABS sidtabs; + SIDTABS lb_sidtabs; + int8_t typ; // first s_client is type s=starting (master) thread; type r = physical reader, type p = proxy reader both always have 1 s_reader struct allocated; type c = client (user logging in into oscam) type m = monitor type h = http server a = anticascader + uint8_t module_idx; + uint16_t last_srvid; + uint32_t last_provid; + uint16_t last_caid; + struct s_provid *last_providptr; + struct s_srvid *last_srvidptr; + uint32_t last_srvidptr_search_provid; + int32_t tosleep; + struct s_auth *account; + int32_t udp_fd; + struct SOCKADDR udp_sa; + socklen_t udp_sa_len; + int8_t tcp_nodelay; + int8_t log; + int32_t logcounter; + int32_t cwfound; // count found ECMs per client + int32_t cwcache; // count ECMs from cache1/2 per client + int32_t cwnot; // count not found ECMs per client + int32_t cwtun; // count betatunneled ECMs per client + int32_t cwignored; // count ignored ECMs per client + int32_t cwtout; // count timeouted ECMs per client + int32_t cwlastresptime; // last Responsetime (ms) +#ifdef CW_CYCLE_CHECK + int32_t cwcycledchecked; // count checked cwcycles per client + int32_t cwcycledok; // count pos checked cwcycles per client + int32_t cwcyclednok; // count neg checked cwcycles per client + int32_t cwcycledign; // count ign cwcycles per client +#endif + int32_t emmok; // count EMM ok + int32_t emmnok; // count EMM nok + int8_t pending; // number of ECMs pending +#ifdef CS_CACHEEX + int32_t cwcacheexpush; // count pushed ecms/cws + int32_t cwcacheexgot; // count got ecms/cws + int32_t cwcacheexhit; // count hit ecms/cws + LLIST *ll_cacheex_stats; // list for Cacheex statistics + int8_t cacheex_maxhop; + int32_t cwcacheexerr; // cw=00 or chksum wrong + int32_t cwcacheexerrcw; // same Hex, different CW + int16_t cwcacheexping; // peer ping in ms, only used by csp + int32_t cwc_info; // count of in/out comming cacheex ecms with CWCinfo + uint8_t cxnodeid_last[9]; // save previous nodeid + uint8_t cxnodeid_changer_detected; // save node-changer detection status +#ifdef CS_CACHEEX_AIO + int32_t cwcacheexgotlg; // count got localgenerated-flagged CWs + int32_t cwcacheexpushlg; // count pushed localgenerated-flagged CWs +#endif + uint8_t cacheex_needfilter; // flag for cachex mode 3 used with camd35 +#ifdef CS_CACHEEX_AIO + uint8_t cacheex_aio_checked; // flag for cacheex aio detection done +#endif +#endif +#ifdef CS_ANTICASC + struct s_zap_list client_zap_list[15]; // 15 last zappings from client used for ACoSC +#endif +#ifdef WEBIF + struct s_cwresponse cwlastresptimes[CS_ECM_RINGBUFFER_MAX]; // ringbuffer for last 20 times + int32_t cwlastresptimes_last; // ringbuffer pointer + int8_t wihidden; // hidden in webinterface status + char lastreader[64]; // last cw got from this reader +#endif + + uint8_t ucrc[4]; // needed by monitor and used by camd35 + uint32_t pcrc; // password crc + struct aes_keys *aes_keys; // used by camd33 and camd35 + uint16_t ncd_msgid; + uint16_t ncd_client_id; + uint8_t ncd_skey[16]; // Also used for camd35 Cacheex to store remote node id + +#ifdef MODULE_CCCAM + void *cc; +#endif + +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP) + uint8_t c35_extmode; +#endif +#endif + +#ifdef MODULE_GBOX + void *gbox; + uint16_t gbox_peer_id; +#endif + +#ifdef MODULE_GHTTP + void *ghttp; +#endif + + int32_t port_idx; // index in server ptab + int32_t ncd_server; // newcamd server + +#ifdef CS_ANTICASC + int32_t ac_fakedelay; // When this is -1, the global ac_fakedelay is used + uint16_t ac_limit; + int8_t ac_penalty; + struct s_acasc_shm acasc; +#endif + + FTAB fchid; + FTAB ftab; // user [caid] and ident filter + CLASSTAB cltab; + + int32_t pfd; // Primary FD, must be closed on exit + struct s_reader *reader; // points to s_reader when cl->typ='r' + + ECM_REQUEST *ecmtask; + + pthread_t thread; + +#ifdef MODULE_SERIAL + struct s_serial_client *serialdata; +#endif + // reader common + int32_t last_idx; + uint16_t idx; + + int8_t ncd_proto; + uint8_t ncd_header[12]; + + // camd35 + uint8_t upwd[64]; + int8_t is_udp; + int8_t stopped; + uint16_t lastcaid; + uint16_t lastsrvid; + int32_t lastpid; + int8_t disable_counter; + uint8_t lastserial[8]; + + // Failban value set bitwise - compared with BAN_ + int32_t failban; + + LLIST *cascadeusers; // s_cascadeuser + + int32_t n_request[2]; // count for number of request per minute by client + + void *work_mbuf; // Points to local data allocated in work_thread when the thread is running + void *work_job_data; // Points to current job_data when work_thread is running + +#ifdef MODULE_PANDORA + int32_t pand_autodelay; + uint8_t pand_send_ecm; + uint8_t pand_ignore_ecm; + uint8_t pand_md5_key[16]; +#endif + +#ifdef MODULE_SCAM + void *scam; +#endif + void *module_data; // private module data + + struct s_client *next; // make client a linked list + struct s_client *nexthashed; + + int8_t start_hidecards; +}; + +typedef struct s_ecm_whitelist_data +{ + uint16_t len; + uint16_t caid; + uint32_t ident; +} ECM_WHITELIST_DATA; + +typedef struct s_ecm_whitelist +{ + int32_t ewnum; + ECM_WHITELIST_DATA *ewdata; +} ECM_WHITELIST; + +typedef struct s_ecm_hdr_whitelist_data +{ + uint16_t len; + uint16_t caid; + uint32_t provid; + uint8_t header[20]; +} ECM_HDR_WHITELIST_DATA; + +typedef struct s_ecm_hdr_whitelist +{ + int32_t ehnum; + ECM_HDR_WHITELIST_DATA *ehdata; +} ECM_HDR_WHITELIST; + +// ratelimit +struct ecmrl +{ + struct timeb last; + uint8_t kindecm; + bool once; + uint8_t ecmd5[CS_ECMSTORESIZE]; + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint16_t chid; + int32_t ratelimitecm; + int32_t ratelimittime; + int32_t srvidholdtime; +}; +#define MAXECMRATELIMIT 20 + +// maxparallel service tracking +struct s_parallel_slot +{ + uint16_t srvid; // service ID + struct timeb last_ecm; // time of last ECM for this service + int32_t ecm_interval; // measured interval between ECMs in ms + struct s_client *client; // client using this slot +}; + +// maxparallel blocked client tracking +struct s_blocked_client +{ + struct s_client *client; // client pointer + uint16_t srvid; // blocked service ID +}; + +#ifdef MODULE_SERIAL +struct ecmtw +{ + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint16_t deg; + uint16_t freq; +}; +#endif + +typedef struct ce_csp_tab_data +{ + int32_t caid; + int32_t cmask; + int32_t prid; + int32_t srvid; + int16_t awtime; + int16_t dwtime; +} CECSPVALUETAB_DATA; + +typedef struct ce_csp_tab +{ + int32_t cevnum; + CECSPVALUETAB_DATA *cevdata; +} CECSPVALUETAB; + +typedef struct cacheex_check_cw_tab_data +{ + int32_t caid; + int32_t cmask; + int32_t prid; + int32_t srvid; + int8_t mode; + uint32_t counter; +} CWCHECKTAB_DATA; + +typedef struct cacheex_check_cw_tab +{ + int32_t cwchecknum; + CWCHECKTAB_DATA *cwcheckdata; + +} CWCHECKTAB; + +typedef struct cacheex_check_cw +{ + int8_t mode; + uint32_t counter; +} CWCHECK; + +typedef struct ce_csp_t +{ + int8_t mode; + int8_t maxhop; +#ifdef CS_CACHEEX_AIO + int8_t maxhop_lg; +#endif + CECSPVALUETAB filter_caidtab; + CAIDVALUETAB cacheex_nopushafter_tab; + uint8_t allow_request; + uint8_t allow_reforward; + uint8_t drop_csp; + uint8_t allow_filter; +#ifdef CS_CACHEEX_AIO + uint8_t allow_maxhop; +#endif + uint8_t block_fakecws; +#ifdef CS_CACHEEX_AIO + uint8_t cw_check_for_push; + uint8_t localgenerated_only; + CAIDTAB localgenerated_only_caidtab; + FTAB lg_only_tab; + uint8_t localgenerated_only_in; + CAIDTAB localgenerated_only_in_caidtab; + FTAB lg_only_in_tab; + uint8_t lg_only_in_aio_only; + uint8_t lg_only_remote_settings; + int32_t feature_bitfield; + char aio_version[CS_AIO_VERSION_LEN]; +#endif +} CECSP; + +struct s_emmlen_range +{ + int16_t min; + int16_t max; +}; + +typedef struct emm_packet_t +{ + uint8_t emm[MAX_EMM_SIZE]; + int16_t emmlen; + uint8_t caid[2]; + uint8_t provid[4]; + uint8_t hexserial[8]; // contains hexserial or SA of EMM + uint8_t type; + uint8_t skip_filter_check; + struct s_client *client; +} EMM_PACKET; + +typedef struct cmd_packet_t +{ + uint8_t cmd[MAX_CMD_SIZE]; + int16_t cmdlen; + struct s_client *client; +} CMD_PACKET; + +struct s_reader // contains device info, reader info and card info +{ + uint8_t keepalive; + uint8_t changes_since_shareupdate; + int32_t resetcycle; // ECM until reset + int32_t resetcounter; // actual count + uint32_t auprovid; // AU only for this provid + int8_t audisabled; // exclude reader from auto AU + struct timeb emm_last; // time of last successfully written emm + struct s_client *client; // pointer to 'r'client this reader is running in + LLIST *ll_entitlements; // entitlements + int8_t enable; + int8_t active; + int8_t dropbadcws; // Schlocke: 1=drops cw if checksum is wrong. 0=fix checksum (default) + int8_t disablecrccws; // 1=disable cw checksum test. 0=enable checksum check + uint64_t grp; + int8_t fallback; + FTAB fallback_percaid; + FTAB localcards; + FTAB disablecrccws_only_for; // ignore checksum for selected caid provid +#ifdef READER_CRYPTOWORKS + int8_t needsglobalfirst; // 0:Write one Global EMM for SHARED EMM disabled 1:Write one Global EMM for SHARED EMM enabled +#endif +#if defined(READER_NAGRA) + uint8_t cak63nuid[4]; + uint8_t cak63nuid_length; + uint8_t cak63cwekey[16]; + uint8_t cak63cwekey_length; +#endif +#ifdef READER_NAGRA_MERLIN + uint8_t mod1[112]; + uint8_t mod1_length; + uint8_t cmd0eprov[2]; + uint8_t cmd0eprov_length; + uint8_t mod2[112]; + uint8_t mod2_length; + uint8_t tmprsa[112]; + uint8_t data50[80]; + uint8_t data50_length; + uint8_t mod50[80]; + uint8_t mod50_length; + uint8_t key3588[136]; + uint8_t key3588_length; + uint8_t key60[96]; + uint8_t exp60[96]; + uint8_t key68[104]; + uint8_t exp68[104]; + uint8_t key3des[16]; + uint8_t klucz68[24]; + uint8_t pairtype; + uint8_t hasunique; + uint8_t key3460[96]; + uint8_t key3460_length; + uint8_t key3310[16]; + uint8_t key3310_length; + uint8_t cwekey[17][16]; + uint8_t cwekey_length[17]; + uint8_t idird[4]; + uint8_t idird_length; + uint8_t kdt05_00[216]; + uint8_t kdt05_10[208]; + uint8_t edata[255]; + uint8_t dt5num; + uint8_t out[255]; + uint8_t ideakey1[16]; + uint8_t block3[8]; + uint8_t v[8]; + uint8_t iout[8]; + uint32_t dword_83DBC; + uint8_t data2[4]; + uint8_t ecmheader[4]; + uint8_t timestmp1[4]; + uint8_t timestmp2[4]; + uint8_t cak7expo[0x11]; + uint8_t data[0x80]; + uint8_t step1[0x60]; + uint8_t step2[0x68]; + uint8_t step3[0x6c]; + uint8_t encrypted[0x68]; + uint8_t result[104]; + uint8_t stillencrypted[0x50]; + uint8_t resultrsa[0x50]; + uint32_t cak7_seq; + uint32_t needrestart; + uint8_t otpcsc[2]; + uint8_t otpcsc_length; + uint8_t otacsc[2]; + uint8_t otacsc_length; + uint8_t forcepair[1]; + uint8_t forcepair_length; + uint8_t cak7_camstate; + uint8_t cak7_aes_key[32]; + uint8_t cak7_aes_iv[16]; + int8_t forcecwswap; + int8_t evensa; + int8_t forceemmg; + int8_t cwpkota; + int8_t headermode; + struct timeb last_refresh; +#endif +#if defined(READER_DGCRYPT) || defined(READER_NAGRA_MERLIN) + uint8_t cardid[8]; +#endif +#ifdef CS_CACHEEX + CECSP cacheex; // CacheEx Settings + uint8_t cxnodeid_last[9]; // save previous nodeid + uint8_t cxnodeid_changer_detected; // save node-changer detection status +#endif + int32_t typ; + char label[64]; +#ifdef WEBIF + char *description; +#endif + char device[128]; +#ifdef WITH_CARDREADER + uint16_t slot; // in case of multiple slots like sc8in1; first slot = 1 + int32_t handle; // device handle + int64_t handle_nr; // device handle_nr for mutiple readers same driver + int32_t fdmc; // device handle for multicam + int32_t detect; + int32_t mhz; // actual clock rate of reader in 10khz steps + int32_t cardmhz; // standard clock speed your card should have in 10khz steps; normally 357 but for Irdeto cards 600 + int32_t divider; // PLL divider for internal readers +#endif + int32_t r_port; + char r_usr[64]; + char r_pwd[64]; // Max length 63 + null terminator + int32_t l_port; + CAIDTAB ctab; + uint32_t boxid; +#ifdef READER_TONGFANG + uint32_t tongfang_version; + uint8_t tongfang3_commkey[8]; + uint32_t tongfang3_calibsn; + uint32_t tongfang_boxid; + uint8_t tongfang3_deskey[8]; + uint8_t tongfang3_deskey_length; + uint8_t stbid[8]; + uint8_t stbid_length; +#endif +#ifdef READER_NAGRA_MERLIN + int8_t cak7_mode; + uint8_t cak7type; + uint8_t cwpkcaid[2]; + uint8_t cwpkcaid_length; + uint8_t nuid[4]; + uint8_t nuid_length; +#endif +#ifdef READER_NAGRA + int8_t nagra_read; // read nagra ncmed records: 0 Disabled (default), 1 read all records, 2 read valid records only + int8_t detect_seca_nagra_tunneled_card; +#endif +#ifdef READER_IRDETO + int8_t force_irdeto; +#endif +#ifdef WITH_CARDREADER + uint8_t boxkey[16]; // n3 boxkey 8 bytes, seca sessionkey 16 bytes, viaccess camid 4 bytes + uint8_t boxkey_length; + uint8_t rsa_mod[120]; // rsa modulus for nagra cards. + uint8_t rsa_mod_length; + uint8_t cwpk_mod[16]; // cwpk modulus for conax cards. + uint8_t cwpk_mod_length; + uint8_t des_key[128]; // 3des key for Viaccess 16 bytes, des key for Dre 128 bytes + uint8_t des_key_length; +#endif + uint8_t card_atr[64]; // ATR readed from card + int8_t card_atr_length; // length of ATR +#ifdef WITH_CARDREADER + uint8_t atr[64]; + int32_t atrlen; + int8_t seca_nagra_card; // seca nagra card +#endif + SIDTABS sidtabs; + SIDTABS lb_sidtabs; + uint8_t hexserial[8]; + int32_t nprov; + uint8_t prid[CS_MAXPROV][8]; + uint8_t sa[CS_MAXPROV][4]; // viaccess & seca +#if defined(READER_NAGRA) || defined(READER_NAGRA_MERLIN) + int32_t nsa; + int32_t nemm82u; + int32_t nemm84; + int32_t nemm84s; + int32_t nemm83u; + int32_t nemm83s; + int32_t nemm87; + uint8_t emm82u[CS_MAXPROV][7]; + uint8_t emm84[CS_MAXPROV][3]; + uint8_t emm84s[CS_MAXPROV][6]; + uint8_t emm83; + uint8_t emm83u[CS_MAXPROV][6]; + uint8_t emm83s[CS_MAXPROV][6]; + uint8_t emm87[CS_MAXPROV][6]; + uint8_t emm82; +#endif + uint8_t read_old_classes; // viaccess + uint8_t maturity; // viaccess & seca maturity level + uint16_t caid; + uint16_t cak7_emm_caid; + uint16_t b_nano; + uint16_t s_nano; + int8_t ecmcommand; // used for filtering nagra bad ecm commands + uint8_t ecmcommandcache[5]; // cachebuff for ecm commands + int32_t blockemm; + int32_t saveemm; + LLIST *blockemmbylen; + char *emmfile; + char pincode[5]; + int8_t logemm; + int8_t cachemm; + int16_t rewritemm; + int16_t deviceemm; // catch device specific emms (so far only used for viaccess) + int8_t card_status; +#ifdef WITH_CARDREADER + int8_t deprecated; // if 0 ATR obeyed, if 1 default speed (9600) is chosen; for devices that cannot switch baudrate +#endif + int8_t resetalways; // send reset after each commands (for pscs) + struct s_module ph; + const struct s_cardreader *crdr; + void *crdr_data; // Private card reader data + bool crdr_flush; // sci readers may disable flush per reader + const struct s_cardsystem *csystem; + void *csystem_data; // Private card system data + bool csystem_active; + uint8_t ncd_key[14]; + uint8_t ncd_skey[16]; + int8_t ncd_connect_on_init; + int8_t ncd_disable_server_filt; + int8_t ncd_proto; + int8_t currenthops; // number of hops (cccam & gbox) +#ifdef MODULE_CCCAM + char cc_version[7]; // cccam version + char cc_build[7]; // cccam build number + int8_t cc_maxhops; // cccam max distance + int8_t cc_mindown; // cccam min downhops + int8_t cc_want_emu; // Schlocke: Client want to have EMUs, 0 - NO; 1 - YES + uint32_t cc_id; + int8_t cc_keepalive; + int8_t cc_hop; // For non-cccam reader: hop for virtual cards + int8_t cc_reshare; + int32_t cc_reconnect; // reconnect on ecm-request timeout +#endif + int8_t tcp_connected; + int32_t tcp_ito; // inactivity timeout + int32_t tcp_rto; // reconnect timeout + int32_t tcp_reconnect_delay; // max tcp connection block delay + uint8_t ipv4force; + uint8_t ipv6_connect_failed; + struct timeb tcp_block_connect_till; // time tcp connect ist blocked + int32_t tcp_block_delay; // incrementing block time + time_t last_g; // get (if last_s-last_g>tcp_rto - reconnect ) + time_t last_s; // send + time_t last_check; // last checked + time_t last_poll; // last poll + FTAB fchid; + FTAB ftab; + CLASSTAB cltab; + ECM_WHITELIST ecm_whitelist; + ECM_HDR_WHITELIST ecm_hdr_whitelist; + int32_t brk_pos; + int32_t msg_idx; + int32_t secatype; // 0=not determined, 2=seca2, 3=nagra(~seca3) this is only valid for localreaders! + uint32_t maxreadtimeout; // in us + uint32_t minreadtimeout; // in us + uint32_t maxwritetimeout; // in us + uint32_t minwritetimeout; // in us +#if defined(WEBIF) || defined(LCDSUPPORT) + int32_t emmwritten[4]; // count written EMM + int32_t emmskipped[4]; // count skipped EMM + int32_t emmerror[4]; // count error EMM + int32_t emmblocked[4]; // count blocked EMM + int32_t webif_emmwritten[4]; // count written EMM for webif reader info + int32_t webif_emmskipped[4]; // count skipped EMM for webif reader info + int32_t webif_emmerror[4]; // count error EMM for reader webif info + int32_t webif_emmblocked[4]; // count blocked EMM for reader webif info + int32_t lbvalue; // loadbalance Value +#endif +#ifdef WITH_AZBOX + int32_t azbox_mode; +#endif +#ifdef WITH_CARDREADER + int32_t use_gpio; // Should this reader use GPIO functions + int gpio_outen; // fd of opened /dev/gpio/outen + int gpio_out; // fd of opened /dev/gpio/out + int gpio_in; // fd of opened /dev/gpio/in + uint32_t gpio; // gpio addr + // variables from icc_async.h start + int32_t convention; // Convention of this ICC + uint8_t protocol_type; // Type of protocol + uint32_t current_baudrate; // (for overclocking uncorrected) baudrate to prevent unnecessary conversions from/to termios structure + double worketu; // in us for internal and external readers calculated (1/D)*(F/cardclock)*1000000 + uint32_t read_timeout; // Max timeout (ETU) to receive characters + uint32_t char_delay; // Delay (ETU) after transmiting each successive char + uint32_t block_delay; // Delay (ms) after starting to transmit + uint32_t BWT, CWT; // (for overclocking uncorrected) block waiting time, character waiting time, in ETU + // variables from io_serial.h + int32_t written; // keep score of how much bytes are written to serial port, since they are echoed back they have to be read + // variables from protocol_t1.h + uint16_t ifsc; // Information field size for the ICC + uint8_t ns; // Send sequence number + int16_t smartdev_found; + int16_t smart_type; + uint16_t statuscnt; + uint16_t modemstat; +#endif +#ifdef READER_CRYPTOWORKS + EMM_PACKET *last_g_emm; // last global EMM + bool last_g_emm_valid; // status of last global EMM +#endif + uint8_t rom[15]; + uint8_t irdId[4]; +#ifdef READER_VIDEOGUARD + uint8_t payload4C[15]; + uint16_t VgCredit; + uint16_t VgPin; + uint8_t VgFuse; + uint8_t VgCountryC[3]; + uint8_t VgRegionC[8]; + uint8_t VgLastPayload[6]; + int32_t card_startdate_basemonth; + int32_t card_startdate_baseyear; + int32_t card_expiredate_basemonth; + int32_t card_expiredate_baseyear; +#endif +#ifdef WITH_LB + int32_t lb_weight; // loadbalance weight factor, if unset, weight=100. The higher the value, the higher the usage-possibility + int8_t lb_force_fallback; // force this reader as fallback if fallback or fallback_percaid paramters set + int32_t lb_usagelevel; // usagelevel for loadbalancer + int32_t lb_usagelevel_ecmcount; + struct timeb lb_usagelevel_time; // time for counting ecms, this creates usagelevel + struct timeb lb_last; // time for oldest reader + LLIST *lb_stat; // loadbalancer reader statistics + CS_MUTEX_LOCK lb_stat_lock; + int32_t lb_stat_busy; // do not add while saving +#endif + time_t card_valid_to; + // ratelimit + int32_t ratelimitecm; + int32_t ratelimittime; // ratelimit time in ms (everything below 60 ms is converted to ms by applying *1000) + int8_t ecmunique; // check for matching ecm hash in ratelimitslot + int32_t srvidholdtime; // time in ms to keep srvid in ratelimitslot (during this time not checked for ecmunique!) + // (everything below 60 ms is converted to ms by applying *1000) + struct timeb lastdvbapirateoverride; + uint32_t ecmsok; +#ifdef CS_CACHEEX_AIO + uint32_t ecmsoklg; +#endif + uint32_t webif_ecmsok; + uint32_t ecmsnok; + uint32_t webif_ecmsnok; + uint32_t ecmstout; + uint32_t webif_ecmstout; + uint32_t ecmnotfoundlimit; // config setting. restart reader if ecmsnok >= ecmnotfoundlimit + int32_t ecmsfilteredhead; // count filtered ECM's by ECM Headerwhitelist + int32_t ecmsfilteredlen; // count filtered ECM's by ECM Whitelist + int32_t webif_ecmsfilteredhead; // count filtered ECM's by ECM Headerwhitelist to readers ecminfo + int32_t webif_ecmsfilteredlen; // count filtered ECM's by ECM Whitelist to readers ecm info + float ecmshealthok; +#ifdef CS_CACHEEX_AIO + float ecmshealthoklg; +#endif + float ecmshealthnok; + float ecmshealthtout; + int32_t cooldown[2]; + int32_t maxparallel; // max parallel active services, 0 = unlimited (default) + float parallelfactor; // zapping tolerance multiplier (default: 2.0) + int32_t paralleltimeout; // timeout buffer in ms after expected ECM (default: 1000) + struct s_parallel_slot *parallel_slots; // regular service tracking (size: maxparallel) + struct s_parallel_slot *parallel_slots_prov; // pending service tracking (size: maxparallel * parallelfactor, rounded) + LLIST *blocked_services; // list of s_blocked_client for dropped services + CS_MUTEX_LOCK parallel_lock; // lock for parallel_slots access + int8_t parallel_full; // flag: 1 if reader is at capacity limit + int8_t cooldownstate; + struct timeb cooldowntime; + struct ecmrl rlecmh[MAXECMRATELIMIT]; +#ifdef READER_VIDEOGUARD + int8_t ndsversion; // 0 auto (default), 1 NDS1, 12 NDS1+, 2 NDS2 + int8_t fix_07; + int8_t fix_9993; + int8_t readtiers; // method to get videoguard tiers + uint8_t ins7E[0x1A + 1]; + uint8_t ins7E11[0x01 + 1]; + uint8_t ins42[0x25 + 1]; + uint8_t ins2e06[0x04 + 1]; + uint8_t k1_generic[0x10 + 1]; // k1 for generic pairing mode + uint8_t k1_unique[0x10 + 1]; // k1 for unique pairing mode +#endif +#ifdef WITH_CARDREADER + int8_t smargopatch; + int8_t autospeed; // 1 clockspeed set according to atr f max + int8_t ins7e11_fast_reset; + uint8_t sc8in1_dtrrts_patch; // fix for kernel commit 6a1a82df91fa0eb1cc76069a9efe5714d087eccd +#endif + +#ifdef READER_VIACCESS + AES_ENTRY *aes_list; // multi AES linked list + uint8_t initCA28; // To set when CA28 succeed + uint32_t key_schedule1[32]; + uint32_t key_schedule2[32]; +#endif + +#if defined(READER_DRE) || defined(READER_DRECAS) + char *userscript; + uint32_t force_ua; +#endif + +#ifdef READER_DRECAS + char *stmkeys; +#endif + +#ifdef MODULE_GBOX + uint8_t gbox_maxdist; + uint8_t gbox_maxecmsend; + uint8_t gbox_reshare; + int8_t gbox_cccam_reshare; + char last_gsms[128]; + uint16_t gbox_remm_peer; + uint16_t gbox_gsms_peer; + uint8_t gbox_force_remm; + uint16_t gbox_cw_src_peer; + uint8_t gbox_crd_slot_lev; + FTAB ccc_gbx_reshare_ident; + uint8_t send_offline_cmd; + uint16_t nb_send_crds; +#endif + +#ifdef MODULE_PANDORA + uint8_t pand_send_ecm; +#endif +#ifdef MODULE_GHTTP + uint8_t ghttp_use_ssl; +#endif +#ifdef WITH_EMU + FTAB emu_auproviders; // AU providers for Emu reader + int8_t emu_datecodedenabled; // date-coded keys for BISS + LLIST *ll_biss2_rsa_keys; // BISS2 RSA keys - Read from external PEM files +#endif +#ifdef READER_CONAX + uint8_t cnxlastecm; // == 0 - last ecm has not been paired ecm, > 0 last ecm has been paired ecm +#endif + LLIST *emmstat; // emm stats + CS_MUTEX_LOCK emmstat_lock; + struct s_reader *next; +}; + +struct s_cpmap +{ + uint16_t caid; + uint32_t provid; + uint16_t sid; + uint16_t chid; + uint16_t dwtime; + struct s_cpmap *next; +}; + +struct s_auth +{ + char usr[64]; + char *pwd; +#ifdef WEBIF + char *description; +#endif + int8_t uniq; +#ifdef CS_CACHEEX + CECSP cacheex; // CacheEx Settings + uint8_t no_wait_time; + uint8_t disablecrccacheex; + FTAB disablecrccacheex_only_for; +#endif + int16_t allowedprotocols; + LLIST *aureader_list; + int8_t autoau; + uint8_t emm_reassembly; // 0 = OFF; 1 = OFF / DVBAPI = ON; 2 = ON (default) + int8_t monlvl; + uint64_t grp; + int32_t tosleep; + int32_t umaxidle; + CAIDTAB ctab; + SIDTABS sidtabs; + FTAB fchid; + FTAB ftab; // user [caid] and ident filter + CLASSTAB cltab; + TUNTAB ttab; + int8_t preferlocalcards; + uint32_t max_connections; +#ifdef CS_ANTICASC + int32_t ac_fakedelay; // When this is -1, the global ac_fakedelay is used + int32_t ac_users; // 0 - unlimited + int8_t ac_penalty; // 0 - log, >0 - fake dw + struct s_acasc ac_stat; + int8_t acosc_max_ecms_per_minute; // user value 0 - unlimited + int8_t acosc_max_active_sids; // user value 0 - unlimited + int8_t acosc_zap_limit; // user value 0 - unlimited + int8_t acosc_penalty; // user value penalty + int32_t acosc_penalty_duration; // user value how long is penalty activ in sek. + time_t acosc_penalty_until; + int8_t acosc_penalty_active; // 0-deaktiv 1-max_active_sids 2-zap_limit 3-max_ecms_per_minute 4-penaly_duration + int32_t acosc_delay; // user value + int8_t acosc_user_zap_count; + time_t acosc_user_zap_count_start_time; +#endif +#ifdef WITH_LB + int32_t lb_nbest_readers; // When this is -1, the global lb_nbest_readers is used + int32_t lb_nfb_readers; // When this is -1, the global lb_nfb_readers is used + CAIDVALUETAB lb_nbest_readers_tab; // like nbest_readers, but for special caids +#endif + IN_ADDR_T dynip; + char *dyndns; + time_t expirationdate; + time_t firstlogin; + uint32_t allowedtimeframe[SIZE_SHORTDAY][24][2]; // day[0-sun to 6-sat, 7-ALL],hours,minutes use as binary flags to reduce mem usage + uint8_t allowedtimeframe_set; // flag for internal use to mention if allowed time frame is used + int8_t c35_suppresscmd08; + uint8_t c35_sleepsend; + int8_t ncd_keepalive; +#ifdef MODULE_CCCAM + int32_t cccmaxhops; + int8_t cccreshare; + int8_t cccignorereshare; + int8_t cccstealth; +#endif + int8_t disabled; + int32_t failban; + + int32_t cwfound; + int32_t cwcache; + int32_t cwnot; + int32_t cwtun; + int32_t cwignored; + int32_t cwtout; +#ifdef CW_CYCLE_CHECK + int32_t cwcycledchecked; // count checked cwcycles per client + int32_t cwcycledok; // count pos checked cwcycles per client + int32_t cwcyclednok; // count neg checked cwcycles per client + int32_t cwcycledign; // count ign cwcycles per client + int8_t cwc_disable; // disable cwc checking for this Client +#endif + int32_t emmok; + int32_t emmnok; +#ifdef CS_CACHEEX + int32_t cwcacheexpush; // count pushed ecms/cws + int32_t cwcacheexgot; // count got ecms/cws + int32_t cwcacheexhit; // count hit ecms/cws + int32_t cwcacheexerr; // cw=00 or chksum wrong + int32_t cwcacheexerrcw; // Same Hex, different CW + int32_t cwc_info; // count of in/out comming cacheex ecms with CWCinfo + uint8_t cxnodeid_last[9]; // save previous nodeid + uint8_t cxnodeid_changer_detected; // save node-changer detection status +#ifdef CS_CACHEEX_AIO + int32_t cwcacheexgotlg; // count got localgenerated-flagged CWs + int32_t cwcacheexpushlg; // count pushed localgenerated-flagged CWs +#endif +#endif + struct s_auth *next; +}; + + +struct s_srvid_caid +{ + uint16_t caid; + uint16_t nprovid; + uint32_t *provid; +}; + +struct s_srvid +{ + uint16_t srvid; + int8_t ncaid; + struct s_srvid_caid *caid; + char *data; + const char *prov; + const char *name; + const char *type; + const char *desc; + struct s_srvid *next; +}; + +struct s_rlimit +{ + struct ecmrl rl; + struct s_rlimit *next; +}; + +struct s_cw +{ + uint8_t cw[16]; +}; + +struct s_fakecws +{ + uint32_t count; + struct s_cw *data; +}; + +#ifdef MODULE_SERIAL +struct s_twin +{ + struct ecmtw tw; + struct s_twin *next; +}; +#endif + +struct s_tierid +{ + uint16_t tierid; + int8_t ncaid; + uint16_t caid[10]; + char name[33]; + struct s_tierid *next; +}; + +struct s_provid +{ + uint16_t caid; + uint16_t nprovid; + uint32_t *provid; + char prov[33]; + char sat[33]; + char lang[33]; + struct s_provid *next; +}; + +struct s_ip +{ + IN_ADDR_T ip[2]; + struct s_ip *next; +}; + +struct s_global_whitelist +{ + uint32_t line; // linenr of oscam.whitelist file, starting with 1 + char type; // w or i or l + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint16_t chid; + uint16_t pid; + uint16_t ecmlen; + uint16_t mapcaid; + uint32_t mapprovid; + struct s_global_whitelist *next; +}; + +struct s_cacheex_matcher +{ + uint32_t line; // linenr of oscam.Cacheex file, starting with 1 + char type; // m + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint16_t chid; + uint16_t pid; + uint16_t ecmlen; + + uint16_t to_caid; + uint32_t to_provid; + uint16_t to_srvid; + uint16_t to_chid; + uint16_t to_pid; + uint16_t to_ecmlen; + + int32_t valid_from; + int32_t valid_to; + + struct s_cacheex_matcher *next; +}; + +struct s_config +{ + int32_t nice; + uint32_t netprio; + uint32_t ctimeout; + uint32_t ftimeout; + CAIDVALUETAB ftimeouttab; + CAIDVALUETAB ctimeouttab; // ← clienttimeout_percaid + uint32_t cmaxidle; + int32_t ulparent; + uint32_t delay; + int32_t bindwait; + int32_t tosleep; + IN_ADDR_T srvip; + char *usrfile; + char *cwlogdir; + char *emmlogdir; + char *logfile; + char *mailfile; + int8_t disablecrccws; // 1=disable cw checksum test. 0=enable checksum check + FTAB disablecrccws_only_for; // ignore checksum for selected caid provid +#ifdef CS_CACHEEX_AIO + uint8_t cacheex_srcname_webif; // show cacheex-srcname not cache3 in log +#endif + uint8_t logtostdout; + uint8_t logtosyslog; + int8_t logduplicatelines; + int32_t initial_debuglevel; + char *sysloghost; + int32_t syslogport; +#if defined(WEBIF) || defined(MODULE_MONITOR) + uint32_t loghistorylines; +#endif + int8_t disablelog; + int8_t disablemail; + int8_t disableuserfile; + int8_t usrfileflag; + struct s_auth *account; + struct s_srvid *srvid[16]; + struct s_tierid *tierid; + struct s_provid *provid; + struct s_sidtab *sidtab; +#ifdef MODULE_MONITOR + int32_t mon_port; + IN_ADDR_T mon_srvip; + struct s_ip *mon_allowed; + uint8_t mon_level; +#endif + int32_t aulow; + int32_t hideclient_to; +#ifdef WEBIF + int32_t http_port; + IN_ADDR_T http_srvip; + char *http_user; + char *http_pwd; + char *http_css; + int8_t http_prepend_embedded_css; + char *http_jscript; + char *http_tpl; + char *http_piconpath; + char *http_script; +#ifndef WEBIF_JQUERY + char *http_extern_jquery; +#endif + int32_t http_refresh; + int32_t poll_refresh; + int8_t http_hide_idle_clients; + char *http_hide_type; + int8_t http_showpicons; + int8_t http_picon_size; + int8_t http_status_log; + int8_t http_showmeminfo; + int8_t http_showecminfo; + int8_t http_showloadinfo; + int8_t http_showuserinfo; + int8_t http_showreaderinfo; + int8_t http_showcacheexinfo; + struct s_ip *http_allowed; + int8_t http_readonly; + IN_ADDR_T http_dynip[MAX_HTTP_DYNDNS]; + uint8_t http_dyndns[MAX_HTTP_DYNDNS][64]; + int8_t http_use_ssl; + int8_t https_force_secure_mode; + int8_t https_auto_create_cert; + char *http_cert; + char *http_help_lang; + char *http_locale; + char *http_oscam_label; + int32_t http_emmu_clean; + int32_t http_emms_clean; + int32_t http_emmg_clean; +#endif + int8_t http_full_cfg; + int8_t http_overwrite_bak_file; + int32_t failbantime; + int32_t failbancount; + LLIST *v_list; // Failban list +#ifdef MODULE_CAMD33 + int32_t c33_port; + IN_ADDR_T c33_srvip; + uint8_t c33_key[16]; + int32_t c33_crypted; + int32_t c33_passive; + struct s_ip *c33_plain; +#endif +#if defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP) + int32_t c35_port; + IN_ADDR_T c35_srvip; + int8_t c35_tcp_suppresscmd08; + int8_t c35_udp_suppresscmd08; + PTAB c35_tcp_ptab; + IN_ADDR_T c35_tcp_srvip; +#endif + int8_t c35_suppresscmd08; // used in cccam module + int8_t getblockemmauprovid; + int32_t umaxidle; //User max Idle +#ifdef MODULE_NEWCAMD + PTAB ncd_ptab; + IN_ADDR_T ncd_srvip; + uint8_t ncd_key[14]; + int8_t ncd_keepalive; + int8_t ncd_mgclient; + struct s_ip *ncd_allowed; +#endif +#ifdef MODULE_RADEGAST + int32_t rad_port; + IN_ADDR_T rad_srvip; + struct s_ip *rad_allowed; + char *rad_usr; +#endif +#ifdef MODULE_CCCAM + uint16_t cc_port[CS_MAXPORTS]; + int8_t cc_reshare; + int8_t cc_ignore_reshare; + int32_t cc_update_interval; + IN_ADDR_T cc_srvip; + char cc_version[7]; + int8_t cc_minimize_cards; + int8_t cc_keep_connected; + int8_t cc_stealth; + int8_t cc_reshare_services; + int8_t cc_forward_origin_card; + uint8_t cc_fixed_nodeid[8]; + uint32_t cc_recv_timeout; // The poll() timeout parameter in ms. Default: DEFAULT_CC_RECV_TIMEOUT (2000 ms). +#endif +#ifdef MODULE_GBOX + #define GBOX_MY_VERS_DEF 0x2A + #define GBOX_MY_CPU_API_DEF 0x61 + #define GBOX_MAX_PROXY_CARDS 32 + #define GBOX_MAX_IGNORED_PEERS 16 + #define GBOX_MAX_BLOCKED_ECM 16 + #define GBOX_MAX_REMM_PEERS 8 + #define GBOX_MAX_DEST_PEERS 16 + #define GBOX_MAX_MSG_TXT 127 + uint16_t gbox_port[CS_MAXPORTS]; + char *gbox_hostname; + uint32_t gbox_reconnect; + uint32_t gbox_password; + unsigned long gbox_proxy_card[GBOX_MAX_PROXY_CARDS]; + int8_t gbox_proxy_cards_num; + uint32_t gbox_my_vers; + uint8_t gbox_my_cpu_api; + uint8_t gsms_dis; + uint8_t log_hello; + uint8_t dis_attack_txt; + char *gbox_tmp_dir; + uint8_t cc_gbx_reshare_en; + uint16_t gbox_ignored_peer[GBOX_MAX_IGNORED_PEERS]; + uint8_t gbox_ignored_peer_num; + uint16_t accept_remm_peer[GBOX_MAX_REMM_PEERS]; + uint8_t accept_remm_peer_num; + uint16_t gbox_block_ecm[GBOX_MAX_BLOCKED_ECM]; + uint8_t gbox_block_ecm_num; + uint8_t gbox_save_gsms; + uint8_t gbox_msg_type; + uint16_t gbox_dest_peers[GBOX_MAX_DEST_PEERS]; + uint8_t gbox_dest_peers_num; + char gbox_msg_txt[GBOX_MAX_MSG_TXT+1]; +#endif +#ifdef MODULE_SERIAL + char *ser_device; +#endif + int32_t max_log_size; + int8_t waitforcards; + int32_t waitforcards_extra_delay; + int8_t preferlocalcards; + int32_t reader_restart_seconds; // schlocke: reader restart auf x seconds, disable = 0 + int8_t dropdups; // drop duplicate logins + + + // Loadbalancer-Config: + int32_t lb_mode; // schlocke: reader loadbalancing mode + int32_t lb_auto_betatunnel; // automatic selection of betatunnel convertion based on learned data + int32_t lb_auto_betatunnel_mode; // automatic selection of betatunnel direction +#ifdef WITH_LB + int32_t lb_save; // schlocke: load/save statistics to file, save every x ecms + int32_t lb_nbest_readers; // count of best readers + int32_t lb_nfb_readers; // count of fallback readers + int32_t lb_min_ecmcount; // minimal ecm count to evaluate lbvalues + int32_t lb_max_ecmcount; // maximum ecm count before reseting lbvalues + int32_t lb_reopen_seconds; // time between retrying failed readers/caids/prov/srv + int8_t lb_reopen_invalid; // default=1; if 0, rc=E_INVALID will be blocked until stats cleaned + int8_t lb_force_reopen_always; // force reopening immediately all failing readers if no matching reader found + int32_t lb_retrylimit; // reopen only happens if reader response time > retrylimit + CAIDVALUETAB lb_retrylimittab; + CAIDVALUETAB lb_nbest_readers_tab; // like nbest_readers, but for special caids + CAIDTAB lb_noproviderforcaid; // do not store loadbalancer stats with providers for this caid + char *lb_savepath; // path where the stat file is save. Empty=default=/tmp/.oscam/stat + int32_t lb_stat_cleanup; // duration in hours for cleaning old statistics + int32_t lb_max_readers; // limit the amount of readers during learning + int32_t lb_auto_betatunnel_prefer_beta; // prefer-beta-over-nagra factor + int32_t lb_auto_timeout; // Automatic timeout by loadbalancer statistics + int32_t lb_auto_timeout_p; // percent added to avg time as timeout time + int32_t lb_auto_timeout_t; // minimal time added to avg time as timeout time +#endif + int32_t resolve_gethostbyname; + int8_t double_check; // schlocke: Double checks each ecm+dcw from two (or more) readers + FTAB double_check_caid; // do not store loadbalancer stats with providers for this caid + + // CW Vote settings + int8_t cwvote_enabled; + int8_t cwvote_log_enabled; + int32_t cwvote_timeout; // w milisekundach, 0 = wyłączony + int32_t cwvote_min_votes; + float cwvote_local_weight; // waga local readera (np. 2.0) + int32_t cwvote_max_candidates; + int32_t cwvote_compare_len; // 8 lub 16 + int32_t cwvote_fallback; // 0=wait, 1=best anyway, 2=first CW + CW_VOTE_CAID_TAB cwvote_caids; + +#ifdef HAVE_DVBAPI + int8_t dvbapi_enabled; + int8_t dvbapi_au; + char *dvbapi_usr; + int8_t dvbapi_boxtype; + int8_t dvbapi_pmtmode; + int8_t dvbapi_requestmode; + int32_t dvbapi_listenport; // TCP port to listen instead of camd.socket (network mode, default=0 -> disabled) + IN_ADDR_T dvbapi_srvip; + SIDTABS dvbapi_sidtabs; + int32_t dvbapi_delayer; // delayer ms, minimum time to write cw + int8_t dvbapi_ecminfo_file; // Enable or disable ecm.info file creation + int8_t dvbapi_ecminfo_type; + int8_t dvbapi_read_sdt; + int8_t dvbapi_write_sdt_prov; + int8_t dvbapi_extended_cw_api; +#ifdef MODULE_STREAMRELAY + int8_t dvbapi_demuxer_fix; +#endif +#endif + +#ifdef CS_ANTICASC + int8_t ac_enabled; + int32_t ac_users; // num of users for account (0 - default) + int32_t ac_stime; // time to collect AC statistics (3 min - default) + int32_t ac_samples; // qty of samples + int8_t ac_penalty; // 0 - write to log + int32_t ac_fakedelay; // 100-1000 ms + int32_t ac_denysamples; + char *ac_logfile; + struct s_cpmap *cpmap; + int8_t acosc_enabled; + int8_t acosc_max_ecms_per_minute; // global value 0 - unlimited + int8_t acosc_max_active_sids; // global value 0 - unlimited + int8_t acosc_zap_limit; // global value 0 - unlimited + int32_t acosc_penalty_duration; // global value how long is penalty activ in sek. + int8_t acosc_penalty; // global value + int32_t acosc_delay; // global value +#endif + +#ifdef LEDSUPPORT + int8_t enableled; // 0=disabled led, 1=enable led for routers, 2=enable qboxhd led +#endif + +#ifdef LCDSUPPORT + int8_t enablelcd; + char *lcd_output_path; + int32_t lcd_hide_idle; + int32_t lcd_write_intervall; +#endif + +#ifdef MODULE_PANDORA + int8_t pand_skip_send_dw; + struct s_ip *pand_allowed; + char *pand_usr; + char *pand_pass; + int8_t pand_ecm; + int32_t pand_port; + IN_ADDR_T pand_srvip; +#endif + +#ifdef MODULE_SCAM + int32_t scam_port; + IN_ADDR_T scam_srvip; + struct s_ip *scam_allowed; +#endif + +#ifdef MODULE_STREAMRELAY + int8_t stream_relay_enabled; + int32_t stream_relay_port; + char *stream_relay_user; + CAIDTAB stream_relay_ctab; // use the stream server for these caids + char *stream_source_host; + int8_t stream_client_source_host; + int32_t stream_source_port; + char *stream_source_auth_user; + char *stream_source_auth_password; + uint32_t stream_relay_buffer_time; + int8_t stream_relay_reconnect_count; + int8_t stream_display_client; + int8_t stream_reuse_client; +#ifdef WEBIF + int8_t stream_hide_client; +#endif +#ifdef WITH_NEUTRINO +#define DEFAULT_STREAM_SOURCE_PORT 31339 //Neutrino +#else +#define DEFAULT_STREAM_SOURCE_PORT 8001 //Enigma2 +#endif +#endif +#ifdef WITH_EMU + uint32_t emu_stream_ecm_delay; + int8_t emu_stream_emm_enabled; +#endif + + int32_t max_cache_time; // seconds ecms are stored in ecmcwcache + int32_t max_hitcache_time; // seconds hits are stored in cspec_hitcache (to detect dyn wait_time) + + int8_t reload_useraccounts; + int8_t reload_readers; + int8_t reload_provid; + int8_t reload_services_ids; + int8_t reload_tier_ids; + int8_t reload_fakecws; + int8_t reload_ac_stat; + int8_t reload_log; + + int8_t block_same_ip; // 0=allow all, 1=block client requests to reader with same ip (default=1) + int8_t block_same_name; // 0=allow all, 1=block client requests to reader with same name (default=1) + +#ifdef CS_CACHEEX +#ifdef CS_CACHEEX_AIO + uint32_t cw_cache_size; + uint32_t cw_cache_memory; + CWCHECKTAB cw_cache_settings; + + uint32_t ecm_cache_size; + uint32_t ecm_cache_memory; + int32_t ecm_cache_droptime; +#endif + uint8_t wait_until_ctimeout; + CWCHECKTAB cacheex_cwcheck_tab; + IN_ADDR_T csp_srvip; + int32_t csp_port; + CECSPVALUETAB cacheex_wait_timetab; + CAIDVALUETAB cacheex_mode1_delay_tab; +#ifdef CS_CACHEEX_AIO + uint8_t waittime_block_start; + uint16_t waittime_block_time; +#endif + CECSP csp; // CSP Settings + uint8_t cacheex_enable_stats; // enable stats + struct s_cacheex_matcher *cacheex_matcher; +#ifdef CS_CACHEEX_AIO + uint8_t cacheex_dropdiffs; + uint8_t cacheex_lg_only_remote_settings; + uint8_t cacheex_localgenerated_only; + CAIDTAB cacheex_localgenerated_only_caidtab; + FTAB lg_only_tab; + uint8_t localgenerated_only_in; + CAIDTAB localgenerated_only_in_caidtab; + FTAB lg_only_in_tab; + uint8_t lg_only_in_aio_only; + uint8_t lg_only_remote_settings; + int32_t feature_bitfield; + char aio_version[CS_AIO_VERSION_LEN]; +#endif + CAIDVALUETAB cacheex_nopushafter_tab; + uint8_t cacheex_localgenerated_only_in; + CAIDTAB cacheex_localgenerated_only_in_caidtab; + FTAB cacheex_lg_only_in_tab; + uint8_t cacheex_lg_only_in_aio_only; + CECSPVALUETAB cacheex_filter_caidtab; + CECSPVALUETAB cacheex_filter_caidtab_aio; + uint32_t cacheex_push_lg_groups; + FTAB cacheex_lg_only_tab; +#endif + +#ifdef CW_CYCLE_CHECK + int8_t cwcycle_check_enable; // on or off + CAIDTAB cwcycle_check_caidtab; // Caid for CW Cycle Check + int32_t keepcycletime; // how long stay the learned Cycletime in Memory + int32_t maxcyclelist; // max size of cwcyclelist + int8_t onbadcycle; // what to do on bad cwcycle + int8_t cwcycle_dropold; // what to do on old ecmd5/cw + int8_t cwcycle_sensitive; + int8_t cwcycle_allowbadfromffb; // allow Bad cycles from Fixed Fallbackreader + int8_t cwcycle_usecwcfromce; // Use CWC Info from Cacheex Sources for CWC Checking +#endif + + // Global whitelist: + struct s_global_whitelist *global_whitelist; + int8_t global_whitelist_use_l; + int8_t global_whitelist_use_m; + + char *ecmfmt; + char *pidfile; + + int32_t max_pending; + + // Ratelimit list + struct s_rlimit *ratelimit_list; + + // fake cws + struct s_fakecws fakecws[0x100]; + +#ifdef MODULE_SERIAL + struct s_twin *twin_list; +#endif +}; + +struct s_clientinit +{ + void *(*handler)(struct s_client *); + struct s_client *client; +}; + +struct s_clientmsg +{ + uint8_t msg[1024]; + int32_t len; + int32_t cmd; +}; + +typedef struct reader_stat_t +{ + int32_t rc; + uint16_t caid; + uint32_t prid; + uint16_t srvid; + uint32_t chid; + int16_t ecmlen; + + struct timeb last_received; + + int32_t ecm_count; + int32_t time_avg; + int32_t time_stat[LB_MAX_STAT_TIME]; + int32_t time_idx; + + int32_t fail_factor; +} READER_STAT; + +typedef struct cs_stat_query +{ + uint16_t caid; + uint32_t prid; + uint16_t srvid; + uint32_t chid; + int16_t ecmlen; +} STAT_QUERY; + +struct s_write_from_cache +{ + ECM_REQUEST *er_new; + ECM_REQUEST *er_cache; +}; + +/* =========================== + * global variables + * =========================== */ +extern pthread_key_t getclient; +extern struct s_client *first_client; +extern CS_MUTEX_LOCK config_lock; +extern CS_MUTEX_LOCK clientlist_lock; +extern CS_MUTEX_LOCK readerlist_lock; +extern struct s_reader *first_active_reader; // points to list of _active_ readers (enable = 1, deleted = 0) +extern LLIST *configured_readers; + +// These are used pretty much everywhere +extern struct s_config cfg; +extern uint16_t cs_dblevel; + +#include "oscam-log.h" +#include "oscam-log-reader.h" + +// Add here *only* funcs that are implemented in oscam.c and are called in other places +void cs_exit(int32_t sig); +void cs_exit_oscam(void); +void cs_restart_oscam(void); +int32_t cs_get_restartmode(void); + +void set_thread_name(const char *thread_name); +int32_t start_thread(char *nameroutine, void *startroutine, void *arg, pthread_t *pthread, int8_t detach, int8_t modify_stacksize); +int32_t start_thread_nolog(char *nameroutine, void *startroutine, void *arg, pthread_t *pthread, int8_t detach, int8_t modify_stacksize); +void kill_thread(struct s_client *cl); + +struct s_module *get_module(struct s_client *cl); +void module_reader_set(struct s_reader *rdr); + +// Until we find a better place for these (they are implemented in oscam-simples.h) +char *get_servicename(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +char *get_servicename_or_null(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +char *get_picon_servicename_or_null(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +int32_t picon_servicename_remve_hd(char *buf, uint32_t buflen); +char *get_tiername(uint16_t tierid, uint16_t caid, char *buf); +char *get_tiername_defaultid(uint16_t tierid, uint16_t caid, char *buf); +char *get_provider(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +char *get_providername(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +char *get_providername_or_null(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +void add_provider(uint16_t caid, uint32_t provid, const char *name, const char *sat, const char *lang); +const char *get_cl_lastprovidername(struct s_client *cl); +bool boxtype_is(const char *boxtype); +bool boxname_is(const char *boxname); +const char *boxtype_get(void); +const char *boxname_get(void); +static inline bool caid_is_fake(uint16_t caid) { return caid == 0xffff; } +static inline bool caid_is_biss(uint16_t caid) { return caid >> 8 == 0x26; } +static inline bool caid_is_biss_fixed(uint16_t caid) { return caid == 0x2600 || caid == 0x2602; } // fixed cw, fake ecm +static inline bool caid_is_biss_dynamic(uint16_t caid) { return caid == 0x2610; } // dynamic cw, real ecm and emm +static inline bool caid_is_seca(uint16_t caid) { return caid >> 8 == 0x01; } +static inline bool caid_is_viaccess(uint16_t caid) { return caid >> 8 == 0x05; } +static inline bool caid_is_irdeto(uint16_t caid) { return caid >> 8 == 0x06; } +static inline bool caid_is_videoguard(uint16_t caid) { return caid >> 8 == 0x09; } +static inline bool caid_is_conax(uint16_t caid) { return caid >> 8 == 0x0B; } +static inline bool caid_is_cryptoworks(uint16_t caid) { return caid >> 8 == 0x0D; } +static inline bool caid_is_powervu(uint16_t caid) { return caid >> 8 == 0x0E; } +static inline bool caid_is_director(uint16_t caid) { return caid >> 8 == 0x10; } +static inline bool caid_is_betacrypt(uint16_t caid) { return caid >> 8 == 0x17; } +static inline bool caid_is_nagra(uint16_t caid) { return caid >> 8 == 0x18; } +static inline bool caid_is_bulcrypt(uint16_t caid) { return caid == 0x5581 || caid == 0x4AEE; } +static inline bool caid_is_dre(uint16_t caid) { return caid == 0x4AE0 || caid == 0x4AE1 || caid == 0x2710;} +static inline bool caid_is_tongfang(uint16_t caid) { return caid == 0x4A02; } +#if defined(WITH_EXTENDED_CW) || defined(MODULE_STREAMRELAY) +static inline bool select_csa_alt(const ECM_REQUEST *er) { + return (caid_is_videoguard(er->caid) && er->ecm[4] != 0 && (er->ecm[2] - er->ecm[4]) == 4); +} +#endif +#ifdef MODULE_STREAMRELAY +static inline uint8_t get_ecm_mode(const ECM_REQUEST *er) { + return (caid_is_videoguard(er->caid) && er->ecmlen >= 4) ? (er->ecm[er->ecmlen - 1] & 0x0F) : 0; +} +#endif +const char *get_cardsystem_desc_by_caid(uint16_t caid); + +#ifdef WITH_EMU +FILTER *get_emu_prids_for_caid(struct s_reader *rdr, uint16_t caid); +#endif + +#endif diff --git a/globals.h.orig b/globals.h.orig new file mode 100644 index 0000000..3790a6f --- /dev/null +++ b/globals.h.orig @@ -0,0 +1,2811 @@ +#ifndef GLOBALS_H_ +#define GLOBALS_H_ + +#define _GNU_SOURCE // needed for PTHREAD_MUTEX_RECURSIVE on some plattforms and maybe other things; do not remove +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) +#include +#endif + +/* + * The following hack is taken from Linux: include/linux/kconfig.h + * Original comment follows: + * Getting something that works in C and CPP for an arg that may or may + * not be defined is tricky. Here, if we have "#define CONFIG_BOOGER 1" + * we match on the placeholder define, insert the "0," for arg1 and generate + * the triplet (0, 1, 0). Then the last step cherry picks the 2nd arg (a one). + * When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when + * the last step cherry picks the 2nd arg, we get a zero. + */ +#define __ARG_PLACEHOLDER_1 0, +#define config_enabled(cfg) _config_enabled(cfg) +#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value) +#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0) +#define ___config_enabled(__ignored, val, ...) val + +#include "config.h" + +#if defined(WITH_SSL) && !defined(WITH_LIBCRYPTO) +# define WITH_LIBCRYPTO 1 +#endif + +/* For deprecated but still needed cryptography functions: + * 10002 corresponds to OpenSSL version 1.0.2*/ + +#define OPENSSL_API_COMPAT 10002 + +#if defined(__CYGWIN__) || defined(__arm__) || defined(__SH4__) || defined(__MIPS__) || defined(__MIPSEL__) || defined(__powerpc__) +# define CS_LOGFILE "/dev/tty" +#endif + +#if defined(__AIX__) || defined(__SGI__) || defined(__OSF__) || defined(__HPUX__) || defined(__SOLARIS__) || defined(__APPLE__) +# define NEED_DAEMON +#endif + +#if defined(__AIX__) || defined(__SGI__) || defined(__OSF__) || defined(__HPUX__) || defined(__SOLARIS__) || defined(__CYGWIN__) +# define NO_ENDIAN_H +#endif + +#if defined(__AIX__) || defined(__SGI__) +# define socklen_t unsigned long +#endif + +#if defined(__SOLARIS__) || defined(__FreeBSD__) || defined(__OpenBSD__) +# define BSD_COMP +#endif + +#if defined(__HPUX__) +# define _XOPEN_SOURCE_EXTENDED +#endif + +#if (defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)) && !defined(s6_addr32) +#define s6_addr32 __u6_addr.__u6_addr32 +#endif + +#ifdef __ANDROID__ +#ifndef in_port_t +#define in_port_t uint16_t +#endif +#define tcdrain(fd) ioctl(fd, TCSBRK, 1) +#endif + +#ifdef __uClinux__ +#define fork() 0 +#endif + +// Prevent warnings about openssl functions. Apple may consider 'openssl' +// deprecated but changing perfectly working portable code just because they +// introduced some proprietary API is not going to happen. +#if defined(__APPLE__) +#define __AVAILABILITY_MACROS_USES_AVAILABILITY 0 +#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6 +#endif + +#include "cscrypt/aes.h" + +#ifdef IPV6SUPPORT +#define IN_ADDR_T struct in6_addr +#define SOCKADDR sockaddr_storage +#define ADDR_ANY in6addr_any +#define DEFAULT_AF AF_INET6 +#else +#define IN_ADDR_T in_addr_t +#define SOCKADDR sockaddr_in +#define ADDR_ANY INADDR_ANY +#define DEFAULT_AF AF_INET +#endif + +#ifndef NO_ENDIAN_H +#if defined(__APPLE__) +#include +#define __BYTE_ORDER __DARWIN_BYTE_ORDER +#define __BIG_ENDIAN __DARWIN_BIG_ENDIAN +#define __LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +#elif defined(__FreeBSD__) || defined(__OpenBSD__) +#include +#define __BYTE_ORDER _BYTE_ORDER +#define __BIG_ENDIAN _BIG_ENDIAN +#define __LITTLE_ENDIAN _LITTLE_ENDIAN +#else +#include +#include +#endif +#endif + +/* =========================== + * macros + * =========================== */ +// Prevent use of unsafe functions (doesn't work for MacOSX) +#if !defined(__APPLE__) +#define strcpy(a,b) UNSAFE_STRCPY_USE_CS_STRNCPY_INSTEAD() +#define sprintf(a,...) UNSAFE_SPRINTF_USE_SNPRINTF_INSTEAD() +#define strtok(a,b,c) UNSAFE_STRTOK_USE_STRTOK_R_INSTEAD() +#define gmtime(a) UNSAFE_GMTIME_NOT_THREADSAFE_USE_CS_GMTIME_R() +#define localtime(a) UNSAFE_LOCALTIME_NOT_THREADSAFE_USE_LOCALTIME_R() +#define asctime(a) UNSAFE_ASCTIME_NOT_THREADSAFE_USE_ASCTIME_R() +#define ctime(a) UNSAFE_CTIME_NOT_THREADSAFE_USE_CS_CTIME_R() +#define gethostbyaddr(a,b,c) UNSAFE_GETHOSTBYADDR_NOT_THREADSAFE_USE_GETADDRINFO() +#define gethostent(a) UNSAFE_GETHOSTENT_NOT_THREADSAFE() +#define getprotobyname(a) UNSAFE_GETPROTOBYNAME_NOT_THREADSAFE_USE_GETPROTOBYNAME_R() +#define getservbyname(a,b) UNSAFE_GETSERVBYNAME_NOT_THREADSAFE_USE_GETSERVBYNAME_R() +#define getservbyport(a,b) UNSAFE_GETSERVBYPORT_NOT_THREADSAFE_USE_GETSERVBYPORT_R() +#define getservent() UNSAFE_GETSERVENT_NOT_THREADSAFE_USE_GETSERVENT_R() +#define getnetbyname(a) UNSAFE_GETNETBYNAME_NOT_THREADSAFE_USE_GETNETBYNAME_R +#define getnetbyaddr(a,b) UNSAFE_GETNETBYADDR_NOT_THREADSAFE_USE_GETNETBYADDR_R +#define getnetent() UNSAFE_GETNETENT_NOT_THREADSAFE_USE_GETNETENT_R +#define getrpcbyname(a) UNSAFE_GETRPCBYNAME_NOT_THREADSAFE_USE_GETRPCBYNAME_R +#define getrpcbynumber(a) UNSAFE_GETRPCBYNUMBER_NOT_THREADSAFE_USE_GETRPCBYNUMBER_R +#define getrpcent() UNSAFE_GETRPCENT_NOT_THREADSAFE_USE_GETRPCENT_R +#define ctermid(a) UNSAFE_CTERMID_NOT_THREADSAFE_USE_CTERMID_R +#define tmpnam(a) UNSAFE_TMPNAM_NOT_THREADSAFE +#define tempnam(a,b) UNSAFE_TEMPNAM_NOT_THREADSAFE +#define getlogin() UNSAFE_GETLOGIN_NOT_THREADSAFE_USE_GETLOGIN_R +#define getpwnam(a) UNSAFE_GETPWNAM_NOT_THREADSAFE_USE_GETPWNAM_R +#define getpwent() UNSAFE_GETPWENT_NOT_THREADSAFE_USE_GETPWENT_R +#define fgetpwent(a) UNSAFE_FGETPWENT_NOT_THREADSAFE_USE_FGETPWENT_R +#ifndef __ANDROID__ +#define getpwuid(a) UNSAFE_GETPWUID_NOT_THREADSAFE_USE_GETPWUID_R +#endif +#define getspent() UNSAFE_GETSPENT_NOT_THREADSAFE_USE_GETSPENT_R +#define getspnam(a) UNSAFE_GETSPNAM_NOT_THREADSAFE_USE_GETSPNAM_R +#define fgetspent(a) UNSAFE_FGETSPENT_NOT_THREADSAFE_USE_FGETSPENT_R +#define getgrnam(a) UNSAFE_GETGRNAM_NOT_THREADSAFE_USE_GETGRNAM_R +#define getgrent() UNSAFE_GETGRENT_NOT_THREADSAFE_USE_GETGRENT_R +#define getgrgid(a) UNSAFE_GETGRGID_NOT_THREADSAFE_USE_GETGRGID_R +#define fgetgrent() UNSAFE_FGETGRENT_NOT_THREADSAFE_USE_FGETGRGID_R +#define fcvt(a,b,c,d) UNSAFE_FCVT_NOT_THREADSAFE_AND_DEPRECATED +#define ecvt(a,b,c,d) UNSAFE_ECVT_NOT_THREADSAFE_AND_DEPRECATED +#define gcvt(a,b,c) UNSAFE_GCVT_NOT_THREADSAFE_AND_DEPRECATED +#define strptime(a,b,c) STRPTIME_NOT_EXISTS_ON_SOME_DM500_DB2() +#define ftime(a) FTIME_DEPRECATED() +#define timegm(a) TIMEGM_GNU_SPECIFIC_USE_CS_TIMEGM +#endif + +#ifdef UNUSED +#elif __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) +#elif defined(__LCLINT__) +# define UNUSED(x) /*@unused@*/ x +#else +# define UNUSED(x) x +#endif + +#if __GNUC__ >= 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# define MUST_CHECK_RESULT __attribute__((warn_unused_result)) +#endif + +#ifdef OK +#undef OK +#endif + +#ifdef ERROR +#undef ERROR +#endif + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +#ifdef WITH_DEBUG +# define call(arg) \ + if(arg) \ + { \ + cs_log_dbg(D_TRACE, "ERROR, function call %s returns error.",#arg); \ + return ERROR; \ + } +#else +# define call(arg) \ + if(arg) \ + { \ + return ERROR; \ + } +#endif + +// checking if (X) free(X) unneccessary since freeing a null pointer doesnt do anything +#define NULLFREE(X) {void *tmpX=X; X=NULL; free(tmpX); } + +#ifdef __CYGWIN__ +#define cs_recv(a,b,c,d) cygwin_recv(a,b,c,d) +#else +#define cs_recv(a,b,c,d) recv(a,b,c,d) +#endif + +// safe wrappers to pthread functions +#define fprintf_stderr(fmt, params...) fprintf(stderr, fmt, ##params) + +#define SAFE_PTHREAD_1ARG(a, b, c) { \ + int32_t pter = a(b); \ + if(pter != 0) \ + { \ + c("FATAL ERROR: %s() failed in %s with error %d %s\n", #a, __func__, pter, strerror(pter)); \ + } } + +#define SAFE_MUTEX_LOCK(a) SAFE_PTHREAD_1ARG(pthread_mutex_lock, a, cs_log) +#define SAFE_MUTEX_UNLOCK(a) SAFE_PTHREAD_1ARG(pthread_mutex_unlock, a, cs_log) +#define SAFE_COND_SIGNAL(a) SAFE_PTHREAD_1ARG(pthread_cond_signal, a, cs_log) +#define SAFE_COND_BROADCAST(a) SAFE_PTHREAD_1ARG(pthread_cond_broadcast, a, cs_log) +#define SAFE_RWLOCK_RDLOCK(a) SAFE_PTHREAD_1ARG(pthread_rwlock_rdlock, a, cs_log) +#define SAFE_RWLOCK_WRLOCK(a) SAFE_PTHREAD_1ARG(pthread_rwlock_wrlock, a, cs_log) +#define SAFE_RWLOCK_UNLOCK(a) SAFE_PTHREAD_1ARG(pthread_rwlock_unlock, a, cs_log) +#define SAFE_ATTR_INIT(a) SAFE_PTHREAD_1ARG(pthread_attr_init, a, cs_log) +#define SAFE_MUTEXATTR_INIT(a) SAFE_PTHREAD_1ARG(pthread_mutexattr_init, a, cs_log) +#define SAFE_CONDATTR_INIT(a) SAFE_PTHREAD_1ARG(pthread_condattr_init, a, cs_log) + +#define SAFE_MUTEX_LOCK_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_mutex_lock, a, fprintf_stderr) +#define SAFE_MUTEX_UNLOCK_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_mutex_unlock, a, fprintf_stderr) +#define SAFE_COND_SIGNAL_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_cond_signal, a, fprintf_stderr) +#define SAFE_MUTEX_UNLOCK_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_mutex_unlock, a, fprintf_stderr) +#define SAFE_ATTR_INIT_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_attr_init, a, fprintf_stderr) +#define SAFE_CONDATTR_INIT_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_condattr_init, a, fprintf_stderr) + +#define SAFE_PTHREAD_2ARG(a, b, c, d) { \ + int32_t pter = a(b, c); \ + if(pter != 0) \ + { \ + d("FATAL ERROR: %s() failed in %s with error %d %s\n", #a, __func__, pter, strerror(pter)); \ + } } + +#define SAFE_COND_WAIT(a,b) SAFE_PTHREAD_2ARG(pthread_cond_wait, a, b, cs_log) +#define SAFE_THREAD_JOIN(a,b) SAFE_PTHREAD_2ARG(pthread_join, a, b, cs_log) +#define SAFE_SETSPECIFIC(a,b) SAFE_PTHREAD_2ARG(pthread_setspecific, a, b, cs_log) +#define SAFE_MUTEXATTR_SETTYPE(a,b) SAFE_PTHREAD_2ARG(pthread_mutexattr_settype, a, b, cs_log) +#define SAFE_MUTEX_INIT(a,b) SAFE_PTHREAD_2ARG(pthread_mutex_init, a, b, cs_log) +#define SAFE_COND_INIT(a,b) SAFE_PTHREAD_2ARG(pthread_cond_init, a, b, cs_log) +#define SAFE_CONDATTR_SETCLOCK(a,b) SAFE_PTHREAD_2ARG(pthread_condattr_setclock, a, b, cs_log) + +#define SAFE_MUTEX_INIT_NOLOG(a,b) SAFE_PTHREAD_2ARG(pthread_mutex_init, a, b, fprintf_stderr) +#define SAFE_COND_INIT_NOLOG(a,b) SAFE_PTHREAD_2ARG(pthread_cond_init, a, b, fprintf_stderr) +#define SAFE_THREAD_JOIN_NOLOG(a,b) SAFE_PTHREAD_2ARG(pthread_join, a, b, fprintf_stderr) +#define SAFE_CONDATTR_SETCLOCK_NOLOG(a,b) SAFE_PTHREAD_2ARG(pthread_condattr_setclock, a, b, fprintf_stderr) + +#define SAFE_PTHREAD_1ARG_R(a, b, c, d) { \ + int32_t pter = a(b); \ + if(pter != 0) \ + { \ + c("FATAL ERROR: %s() failed in %s (called from %s) with error %d %s\n", #a, __func__, d, pter, strerror(pter)); \ + } } + +#define SAFE_MUTEX_LOCK_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_mutex_lock, a, cs_log, b) +#define SAFE_MUTEX_UNLOCK_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_mutex_unlock, a, cs_log, b) +#define SAFE_COND_SIGNAL_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_cond_signal, a, cs_log, b) +#define SAFE_COND_BROADCAST_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_cond_broadcast, a, cs_log, b) +#define SAFE_CONDATTR_INIT_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_condattr_init, a, cs_log, b) + +#define SAFE_MUTEX_LOCK_NOLOG_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_mutex_lock, a, fprintf_stderr, b) +#define SAFE_MUTEX_UNLOCK_NOLOG_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_mutex_unlock, a, fprintf_stderr, b) +#define SAFE_CONDATTR_INIT_NOLOG_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_condattr_init, a, fprintf_stderr, b) + +#define SAFE_PTHREAD_2ARG_R(a, b, c, d, e) { \ + int32_t pter = a(b, c); \ + if(pter != 0) \ + { \ + d("FATAL ERROR: %s() failed in %s (called from %s) with error %d %s\n", #a, __func__, e, pter, strerror(pter)); \ + } } + +#define SAFE_MUTEX_INIT_R(a,b,c) SAFE_PTHREAD_2ARG_R(pthread_mutex_init, a, b, cs_log, c) +#define SAFE_COND_INIT_R(a,b,c) SAFE_PTHREAD_2ARG_R(pthread_cond_init, a, b, cs_log, c) +#define SAFE_CONDATTR_SETCLOCK_R(a,b,c) SAFE_PTHREAD_2ARG(pthread_condattr_setclock, a, b, cs_log, c) + +#define SAFE_MUTEX_INIT_NOLOG_R(a,b,c) SAFE_PTHREAD_2ARG_R(pthread_mutex_init, a, b, fprintf_stderr, c) +#define SAFE_COND_INIT_NOLOG_R(a,b,c) SAFE_PTHREAD_2ARG_R(pthread_cond_init, a, b, fprintf_stderr, c) +#define SAFE_CONDATTR_SETCLOCK_NOLOG_R(a,b,c) SAFE_PTHREAD_2ARG(pthread_condattr_setclock, a, b, fprintf_stderr, c) + +#define SAFE_COND_TIMEDWAIT(a, b, c) { \ + int32_t pter; \ + if((c)->tv_nsec < 0) (c)->tv_nsec = 0; \ + if((c)->tv_nsec > 999999999) (c)->tv_nsec = 999999999; \ + pter = pthread_cond_timedwait(a, b, c); \ + if(pter != 0 && pter != ETIMEDOUT) \ + { \ + cs_log("FATAL ERROR: pthread_cond_timedwait failed in %s with error %d %s\n", __func__, pter, strerror(pter)); \ + } } + +#define SAFE_COND_TIMEDWAIT_R(a, b, c, d) { \ + int32_t pter; \ + if((c)->tv_nsec < 0) (c)->tv_nsec = 0; \ + if((c)->tv_nsec > 999999999) (c)->tv_nsec = 999999999; \ + pter = pthread_cond_timedwait(a, b, c); \ + if(pter != 0 && pter != ETIMEDOUT) \ + { \ + cs_log("FATAL ERROR: pthread_cond_timedwait failed in %s (called from %s) with error %d %s\n", __func__, d, pter, strerror(pter)); \ + } } + +#define SAFE_ATTR_SETSTACKSIZE(a,b) { \ + int32_t pter = pthread_attr_setstacksize(a, b); \ + if(pter != 0) \ + { \ + cs_log("WARNING: pthread_attr_setstacksize() failed in %s with error %d %s\n", __func__, pter, strerror(pter)); \ + } } + +#define SAFE_ATTR_SETSTACKSIZE_NOLOG(a,b) { \ + int32_t pter = pthread_attr_setstacksize(a, b); \ + if(pter != 0) \ + { \ + fprintf_stderr("WARNING: pthread_attr_setstacksize() failed in %s with error %d %s\n", __func__, pter, strerror(pter)); \ + } } + +#ifdef NO_PTHREAD_STACKSIZE +#undef SAFE_ATTR_SETSTACKSIZE +#undef SAFE_ATTR_SETSTACKSIZE_NOLOG +#define SAFE_ATTR_SETSTACKSIZE(a,b) +#define SAFE_ATTR_SETSTACKSIZE_NOLOG(a,b) +#endif + +#define CHECK_BIT(var,pos) (((var) & (1<<(pos)))? 1 : 0) + +// CW VOTING +#define MAX_VOTE_CANDIDATES 16 // Zwiększono dla obsługi większej liczby głosów +#define MIN_VOTES_REQUIRED 2 +#define CW_COMPARE_LEN 14 + +/* =========================== + * constants + * =========================== */ +#define SCM_URL "https://github.com/oscam-mirror/oscam-emu" +#define WIKI_URL "https://git.streamboard.tv/common/oscam/-/wikis" +#define BOARD_URL "https://github.com/oscam-mirror/oscam-emu/discussions" +#ifndef CS_VERSION +#define CS_VERSION "2.26.01-11942" +#endif +#ifndef CS_GIT_COMMIT +#define CS_GIT_COMMIT "a2b4c6d8" +#endif +#ifdef CS_CACHEEX +#ifdef CS_CACHEEX_AIO +#define CS_AIO_VERSION CS_VERSION +#define CS_AIO_VERSION_LEN (sizeof(CS_AIO_VERSION)) +#endif +#endif +#ifndef CS_TARGET +# define CS_TARGET "unknown" +#endif +#ifndef CS_CONFDIR +#define CS_CONFDIR "/usr/local/etc" +#endif +#ifndef CS_LOGFILE +#define CS_LOGFILE "/var/log/oscam.log" +#endif +#define CS_QLEN 128 // size of request queue +#define CS_MAXPROV 32 +#define CS_MAXPORTS 32 // max server ports +#define CS_CLIENT_HASHBUCKETS 32 +#define CS_SERVICENAME_SIZE 32 + +#define CS_ECMSTORESIZE 16 // use MD5() +#define CS_EMMSTORESIZE 16 // use MD5() +#define CS_CLIENT_TIMEOUT 5000 +#define CS_CLIENT_MAXIDLE 120 +#define CS_BIND_TIMEOUT 120 +#define CS_DELAY 0 +#define CS_ECM_RINGBUFFER_MAX 0x10 // max size for ECM last responsetimes ringbuffer. Keep this set to power of 2 values! + +// Support for multiple CWs per channel and other encryption algos +#define WITH_EXTENDED_CW 1 + +#define MAX_ECM_SIZE 1024 +#define MAX_EMM_SIZE 1024 + +#define MAX_CMD_SIZE 0xff + 5 // maximum value from length byte + command header + +#ifdef WITH_EMU +#define CS_EMMCACHESIZE 1024 // nr of EMMs that EMU reader will cache +#else +#define CS_EMMCACHESIZE 512 // nr of EMMs that each reader will cache +#endif +#define MSGLOGSIZE 64 // size of string buffer for a ecm to return messages + +#define D_TRACE 0x0001 // Generate very detailed error/trace messages per routine +#define D_ATR 0x0002 // Debug ATR parsing, dump of ecm, cw +#define D_READER 0x0004 // Debug Reader/Proxy Process +#define D_CLIENT 0x0008 // Debug Client Process +#define D_IFD 0x0010 // Debug IFD+protocol +#define D_DEVICE 0x0020 // Debug Reader I/O +#define D_EMM 0x0040 // Dumps EMM +#define D_DVBAPI 0x0080 // Debug DVBAPI +#define D_LB 0x0100 // Debug Loadbalancer/ECM handler +#define D_CACHEEX 0x0200 // Debug CACHEEX +#define D_CLIENTECM 0x0400 // Debug Client ECMs +#define D_CSP 0x0800 // Debug CSP +#define D_CWC 0x1000 // Debug CWC +#ifdef CS_CACHEEX_AIO +#define D_CW_CACHE 0x2000 // Debug CW Cache +#endif +#define D_ALL_DUMP 0xFFFF // dumps all + +#ifdef CS_CACHEEX_AIO +#define MAX_DEBUG_LEVELS 14 +#else +#define MAX_DEBUG_LEVELS 13 +#endif + +/////// phoenix readers which need baudrate setting and timings need to be guarded by OSCam: BEFORE R_MOUSE +#define R_DB2COM1 0x1 // Reader Dbox2 @ com1 +#define R_DB2COM2 0x2 // Reader Dbox2 @ com1 +#define R_SC8in1 0x3 // Reader Sc8in1 or MCR +#define R_MP35 0x4 // AD-Teknik Multiprogrammer 3.5 and 3.6 (only usb tested) +#define R_MOUSE 0x5 // Reader smartcard mouse +/////// internal readers (Dreambox, Coolstream, IPBox) are all R_INTERNAL, they are determined compile-time +#define R_INTERNAL 0x6 // Reader smartcard intern +#define R_SMART 0x7 // Reader smartcard (generic) +/////// readers that do not reed baudrate setting and timings are guarded by reader itself (large buffer built in): AFTER R_SMART +#define R_PCSC 0x8 // PCSC +#define R_DRECAS 0x9 // Reader DRECAS +#define R_EMU 0x17 // Reader EMU +/////// proxy readers after R_CS378X +#define R_CAMD35 0x20 // Reader cascading camd 3.5x +#define R_CAMD33 0x21 // Reader cascading camd 3.3x +#define R_NEWCAMD 0x22 // Reader cascading newcamd +#define R_RADEGAST 0x23 // Reader cascading radegast +#define R_CS378X 0x24 // Reader cascading camd 3.5x TCP +#define R_CONSTCW 0x25 // Reader for Constant CW +#define R_CSP 0x26 // Cache CSP +#define R_GHTTP 0x27 // Reader ghttp +#define R_SCAM 0x28 // Reader cascading scam +/////// peer to peer proxy readers after R_CCCAM +#define R_GBOX 0x30 // Reader cascading gbox +#define R_CCCAM 0x35 // Reader cascading cccam +#define R_PANDORA 0x36 // Reader cascading pandora +#define R_SERIAL 0x80 // Reader serial +#define R_IS_NETWORK 0x60 +#define R_IS_CASCADING 0xE0 + +#define is_network_reader(__X) (__X->typ & R_IS_NETWORK) +#define is_cascading_reader(__X) (__X->typ & R_IS_CASCADING) +#define is_smargo_reader(__X) (__X->crdr && strcmp(__X->crdr->desc, "smargo") == 0) + +// ECM rc codes: +/////// all found +#define E_FOUND 0 +#define E_CACHE1 1 +#define E_CACHE2 2 +#define E_CACHEEX 3 +/////// all notfound, some error or problem +#define E_NOTFOUND 4 // for selection of found, use < E_NOTFOUND +#define E_TIMEOUT 5 +#define E_SLEEPING 6 +#define E_FAKE 7 +#define E_INVALID 8 +#define E_CORRUPT 9 +#define E_NOCARD 10 +#define E_EXPDATE 11 +#define E_DISABLED 12 +#define E_STOPPED 13 // for selection of error, use <= E_STOPPED and exclude selection of found + +#define E_ALREADY_SENT 101 +#define E_WAITING 102 +#define E_99 99 // this code is undocumented +#define E_UNHANDLED 100 // for selection of unhandled, use >= E_UNHANDLED + +#define CS_MAX_MOD 20 +#define MOD_CONN_TCP 1 +#define MOD_CONN_UDP 2 +#define MOD_CONN_NET 3 +#define MOD_CONN_SERIAL 4 +#define MOD_NO_CONN 8 + +#define EMM_UNIQUE 1 +#define EMM_SHARED 2 +#define EMM_GLOBAL 4 +#define EMM_UNKNOWN 8 + +// Listener Types +#define LIS_CAMD33TCP 1 +#define LIS_CAMD35UDP 2 +#define LIS_CAMD35TCP 4 +#define LIS_NEWCAMD 8 +#define LIS_CCCAM 16 +#define LIS_GBOX 32 +#define LIS_RADEGAST 64 +#define LIS_DVBAPI 128 +#define LIS_CONSTCW 256 +#define LIS_SERIAL 1024 +#define LIS_CSPUDP 2048 +#define LIS_SCAM 4096 + +// EMM types: +#define UNKNOWN 0 +#define UNIQUE 1 +#define SHARED 2 +#define GLOBAL 3 + +#define NCD_AUTO 0 +#define NCD_524 1 +#define NCD_525 2 + +// moved from reader-common.h +#define UNKNOWN 0 +#define CARD_NEED_INIT 1 +#define CARD_INSERTED 2 +#define CARD_FAILURE 3 +#define NO_CARD 4 +#define READER_DEVICE_ERROR 5 + +// moved from stats +#define DEFAULT_REOPEN_SECONDS 30 +#define DEFAULT_MIN_ECM_COUNT 5 +#define DEFAULT_MAX_ECM_COUNT 500 +#define DEFAULT_NBEST 1 +#define DEFAULT_NFB 1 +#define DEFAULT_RETRYLIMIT 0 +#define DEFAULT_LB_MODE 0 +#define DEFAULT_LB_STAT_CLEANUP 336 +#define DEFAULT_UPDATEINTERVAL 240 +#define DEFAULT_LB_AUTO_BETATUNNEL 1 +#define DEFAULT_LB_AUTO_BETATUNNEL_MODE 0 +#define DEFAULT_LB_AUTO_BETATUNNEL_PREFER_BETA 50 + +#define DEFAULT_MAX_CACHE_TIME 15 +#define DEFAULT_MAX_HITCACHE_TIME 15 + +#define DEFAULT_LB_AUTO_TIMEOUT 0 +#define DEFAULT_LB_AUTO_TIMEOUT_P 30 +#define DEFAULT_LB_AUTO_TIMEOUT_T 300 + +enum {E1_GLOBAL = 0, E1_USER, E1_READER, E1_SERVER, E1_LSERVER}; + +// LB blocking events: +enum {E2_GLOBAL = 0, E2_GROUP, E2_CAID, E2_IDENT, E2_CLASS, E2_CHID, E2_QUEUE, E2_OFFLINE, + E2_SID, E2_CCCAM_NOCARD, + // From here only LB nonblocking events: + E2_CCCAM_NOK1, E2_CCCAM_NOK2, E2_CCCAM_LOOP, E2_WRONG_CHKSUM, E2_RATELIMIT + }; + +#define LB_NONBLOCK_E2_FIRST E2_CCCAM_NOK1 + +#define CTA_RES_LEN 512 + +#define MAX_ATR_LEN 33 // max. ATR length +#define MAX_HIST 15 // max. number of historical characters + +#define MAX_SIDBITS 64 // max services +#define SIDTABBITS uint64_t // 64bit type for services, if a system does not support this type, +// please use a define and define it as uint32_t / MAX_SIDBITS 32 + +#define BAN_UNKNOWN 1 // Failban mask for anonymous/ unknown contact +#define BAN_DISABLED 2 // Failban mask for Disabled user +#define BAN_SLEEPING 4 // Failban mask for sleeping user +#define BAN_DUPLICATE 8 // Failban mask for duplicate user + +#define MAX_HTTP_DYNDNS 3 // maximum allowed Dyndns addresses for webif access + +#define CHECK_WAKEUP 1 +#define CHECK_ANTICASCADER 2 +#define CHECK_ECMCACHE 3 + +#define AVAIL_CHECK_CONNECTED 0 +#define AVAIL_CHECK_LOADBALANCE 1 + +#define ECM_FMT_LEN 109 // 64 +#define CXM_FMT_LEN 209 // 160 + +#define LB_MAX_STAT_TIME 10 + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#define OSCAM_SIGNAL_WAKEUP SIGCONT +#else +#define OSCAM_SIGNAL_WAKEUP SIGRTMAX-2 +#endif + +#define READER_ACTIVE 0x01 +#define READER_FALLBACK 0x02 +#define READER_LOCAL 0x04 +#define READER_CACHEEX 0x08 + +#define REQUEST_SENT 0x10 +#define REQUEST_ANSWERED 0x20 + +#define CW_MODE_ONE_CW 0 +#define CW_MODE_MULTIPLE_CW 1 +#define CW_TYPE_VIDEO 0 +#define CW_TYPE_AUDIO 1 +#define CW_TYPE_DATA 2 +#define CW_ALGO_CSA 0 +#define CW_ALGO_DES 1 +#define CW_ALGO_AES128 2 +#define CW_ALGO_CSA_ALT 3 +#define CW_ALGO_MODE_ECB 0 +#define CW_ALGO_MODE_CBC 1 + +#define SIZE_SHORTDAY 8 +#define MAXALLOWEDTF 1001 // 10 allowed time frame slots for everyday + all [(3 + 1 + 10*(12) + 1)*8] +extern const char *shortDay[SIZE_SHORTDAY]; +extern const char *weekdstr; + +/* =========================== + * Default Values + * =========================== */ +#define DEFAULT_INACTIVITYTIMEOUT 0 +#define DEFAULT_TCP_RECONNECT_TIMEOUT 30 +#define DEFAULT_NCD_KEEPALIVE 0 + +#define DEFAULT_CC_MAXHOPS 10 +#define DEFAULT_CC_RESHARE -1 // Use global cfg +#define DEFAULT_CC_IGNRSHR -1 // Use global cfg +#define DEFAULT_CC_STEALTH -1 // Use global cfg +#define DEFAULT_CC_KEEPALIVE 0 +#define DEFAULT_CC_RECONNECT 12000 +#define DEFAULT_CC_RECV_TIMEOUT 2000 + +#define DEFAULT_AC_USERS -1 // Use global cfg +#define DEFAULT_AC_PENALTY -1 // Use global cfg + +// Return MPEG section length +#define SCT_LEN(sct) (3+((sct[1]&0x0f)<<8)+sct[2]) +// Used by readers +#define MAX_LEN 256 + +#define NO_CAID_VALUE 0xfffe +#define NO_PROVID_VALUE 0xfffffe +#define NO_SRVID_VALUE 0xfffe + +// If NULL return empty string +#define ESTR(x) ((x) ? (x) : "") + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + See: http://stackoverflow.com/questions/10269685/kernels-container-of-any-way-to-make-it-iso-conforming + http://www.kroah.com/log/linux/container_of.html +*/ +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) - offsetof(type, member) + \ + (&((type *) 0)->member == (ptr)) * 0)) + +/* =========================== + * global structures + * =========================== */ +struct timeb +{ + time_t time; + int64_t millitm; +}; + +typedef struct cs_mutexlock +{ + int32_t timeout; + pthread_mutex_t lock; + pthread_cond_t writecond, readcond; + const char *name; + int8_t flag; + int16_t writelock, readlock; +} CS_MUTEX_LOCK; + +#include "oscam-llist.h" + +typedef struct s_caidvaluetab_data +{ + uint16_t caid; + uint16_t value; +} CAIDVALUETAB_DATA; + +typedef struct s_caidvaluetab +{ + int32_t cvnum; + CAIDVALUETAB_DATA *cvdata; +} CAIDVALUETAB; + +typedef struct s_classtab +{ + uint8_t an; + uint8_t bn; + uint8_t *aclass; + uint8_t *bclass; +} CLASSTAB; + +typedef struct s_caidtab_data +{ + uint16_t caid; + uint16_t mask; + uint16_t cmap; +} CAIDTAB_DATA; + +typedef struct s_caidtab +{ + int32_t ctnum; + CAIDTAB_DATA *ctdata; +} CAIDTAB; + +typedef struct s_tuntab_data +{ + uint16_t bt_caidfrom; + uint16_t bt_caidto; + uint16_t bt_srvid; +} TUNTAB_DATA; + +typedef struct s_tuntab +{ + int32_t ttnum; + TUNTAB_DATA *ttdata; +} TUNTAB; + +typedef struct s_sidtab +{ + char label[64]; +#ifdef CS_CACHEEX_AIO + uint8_t disablecrccws_only_for_exception; + uint8_t no_wait_time; + uint8_t lg_only_exception; +#endif + uint16_t num_caid; + uint16_t num_provid; + uint16_t num_srvid; + uint16_t *caid; + uint32_t *provid; + uint16_t *srvid; + struct s_sidtab *next; +} SIDTAB; + +typedef struct s_filter +{ + uint16_t caid; + uint8_t nprids; + uint32_t prids[CS_MAXPROV]; +} FILTER; + +typedef struct s_ftab +{ + int32_t nfilts; + FILTER *filts; +} FTAB; + +typedef struct s_ncd_ftab +{ + int32_t nfilts; + FILTER filts[16]; +} NCD_FTAB; + +struct ncd_port +{ + bool ncd_key_is_set; + uint8_t ncd_key[14]; + NCD_FTAB ncd_ftab; +}; + +typedef struct s_port +{ + int32_t fd; + int32_t s_port; + struct ncd_port *ncd; // newcamd specific settings +} PORT; + +typedef struct s_ptab +{ + int32_t nports; + PORT ports[CS_MAXPORTS]; +} PTAB; + +typedef struct aes_entry +{ + uint16_t keyid; + uint16_t caid; + uint32_t ident; + uint8_t plainkey[16]; + AES_KEY key; + struct aes_entry *next; +} AES_ENTRY; + +struct aes_keys +{ + AES_KEY aeskey_encrypt; // encryption key needed by monitor and used by camd33, camd35 + AES_KEY aeskey_decrypt; // decryption key needed by monitor and used by camd33, camd35 +}; + +struct s_ecm +{ + uint8_t ecmd5[CS_ECMSTORESIZE]; + uint8_t cw[16]; + uint16_t caid; + uint64_t grp; + struct s_reader *reader; + int32_t rc; + time_t time; +}; + +struct s_emmstat +{ + uint8_t emmd5[CS_EMMSTORESIZE]; + uint8_t type; + int32_t count; + struct timeb firstwritten; + struct timeb lastwritten; +}; + +struct s_emmcache +{ + uint8_t emmd5[CS_EMMSTORESIZE]; + uint8_t type; + uint16_t len; + uint8_t emm[MAX_EMM_SIZE]; + struct timeb firstseen; + struct timeb lastseen; +}; + +struct s_csystem_emm_filter +{ + uint8_t type; + uint8_t enabled; + uint8_t filter[16]; + uint8_t mask[16]; +}; + +typedef struct v_ban // Failban listmember +{ + int32_t v_count; + IN_ADDR_T v_ip; + int32_t v_port; + struct timeb v_time; + bool acosc_entry; + int32_t acosc_penalty_dur; + char *info; +} V_BAN; + +typedef struct s_cacheex_stat_entry // Cacheex stats listmember +{ + int32_t cache_count; + time_t cache_last; + uint16_t cache_caid; + uint16_t cache_srvid; + uint32_t cache_prid; + int8_t cache_direction; // 0 = push / 1 = got +#ifdef CS_CACHEEX_AIO + int32_t cache_count_lg; +#endif +} S_CACHEEX_STAT_ENTRY; + +typedef struct s_entitlement // contains entitlement Info +{ + uint64_t id; // the element ID + uint32_t type; // enumerator for tier,chid whatever + // 0="", 1="Package", 2="PPV-Event", 3="chid", 4="tier", 5 = "class", 6 = "PBM". 7 = "seca-admin" + uint16_t caid; // the caid of element + uint32_t provid; // the provid of element + uint32_t class; // the class needed for some systems + time_t start; // startdate + time_t end; // enddate +#ifdef WITH_EMU + bool isKey; + bool isData; + char name[8]; + uint8_t *key; + uint32_t keyLength; +#endif +} S_ENTITLEMENT; + +struct s_client; +struct ecm_request_t; +struct emm_packet_t; +struct cmd_packet_t; +struct s_ecm_answer; +struct demux_s; + +#define DEFAULT_MODULE_BUFsize 1024 + +struct s_module +{ + const char *desc; + int8_t type; + int8_t large_ecm_support; + int16_t listenertype; + //int32_t s_port; + IN_ADDR_T s_ip; + uint16_t bufsize; + void *(*s_handler)(struct s_client *, uint8_t *, int32_t); + void (*s_init)(struct s_client *); + int32_t (*recv)(struct s_client *, uint8_t *, int32_t); + void (*send_dcw)(struct s_client *, struct ecm_request_t *); + void (*cleanup)(struct s_client *); + int32_t (*c_recv_chk)(struct s_client *, uint8_t *, int32_t *, uint8_t *, int32_t); + int32_t (*c_init)(struct s_client *); + int32_t (*c_send_ecm)(struct s_client *, struct ecm_request_t *); + int32_t (*c_send_emm)(struct emm_packet_t *); + int32_t (*c_available)(struct s_reader *, int32_t, struct ecm_request_t *); // Schlocke: available check for load-balancing, + // params: + // rdr (reader to check) + // int32_t checktype (0=return connected, 1=return loadbalance-avail) return int + void (*c_idle)(void); // Schlocke: called when reader is idle + void (*s_idle)(struct s_client *); + void (*s_peer_idle)(struct s_client *); + void (*c_card_info)(void); // Schlocke: request card infos + + int32_t (*c_capmt)(struct s_client *, struct demux_s *); + +#ifdef CS_CACHEEX + int32_t (*c_cache_push)(struct s_client *, struct ecm_request_t *); // Cache push + int32_t (*c_cache_push_chk)(struct s_client *, struct ecm_request_t *); // Cache push Node Check, 0=no push +#endif + int32_t c_port; + PTAB ptab; + int32_t num; +}; + +struct s_ATR; + +struct s_cardreader_settings +{ + uint32_t ETU; + uint32_t EGT; + uint8_t P; + uint32_t I; + uint32_t F; + uint32_t Fi; + uint8_t Di; + uint8_t Ni; + uint32_t WWT; + uint32_t BGT; + uint8_t D; +}; + +struct s_cardreader +{ + const char *desc; + int32_t (*reader_init)(struct s_reader *); + int32_t (*get_status)(struct s_reader *, int *); + int32_t (*activate)(struct s_reader *, struct s_ATR *); + int32_t (*transmit)(struct s_reader *, uint8_t *sent, uint32_t size, uint32_t expectedlen, uint32_t delay, uint32_t timeout); + int32_t (*receive)(struct s_reader *, uint8_t *data, uint32_t size, uint32_t delay, uint32_t timeout); + int32_t (*lock_init)(struct s_reader *); + void (*lock)(struct s_reader *); + void (*unlock)(struct s_reader *); + int32_t (*close)(struct s_reader *); + int32_t (*set_parity)(struct s_reader *, uint8_t parity); + int32_t (*write_settings)(struct s_reader *, struct s_cardreader_settings *s); + int32_t (*set_protocol)(struct s_reader *, uint8_t *params, uint32_t *length, uint32_t len_request); + int32_t (*set_baudrate)(struct s_reader *, uint32_t baud); // set only for readers which need baudrate setting and timings need to be guarded by OSCam + int32_t (*card_write)(struct s_reader *pcsc_reader, const uint8_t *buf, uint8_t *cta_res, uint16_t *cta_lr, int32_t l); + void (*display_msg)(struct s_reader *, char *msg); + + int32_t (*do_reset)(struct s_reader *, struct s_ATR *, int32_t (*rdr_activate_card)(struct s_reader *, struct s_ATR *, uint16_t deprecated), int32_t (*rdr_get_cardsystem)(struct s_reader *, struct s_ATR *)); + + bool (*set_DTS_RTS)(struct s_reader *, int32_t *dtr, int32_t *rts); + + int32_t typ; // fixme: workaround, remove when all old code is converted + + int8_t max_clock_speed; // 1 for reader->typ > R_MOUSE + int8_t need_inverse; // 0 = reader does inversing; 1 = inversing done by oscam + //io_serial config + int8_t flush; + int8_t read_written; // 1 = written bytes has to read from device + bool skip_extra_atr_parsing; + bool skip_t1_command_retries; + bool skip_setting_ifsc; +}; + +struct s_cardsystem +{ + const char *desc; + const uint16_t *caids; + int32_t (*card_init)(struct s_reader *reader, struct s_ATR *); + void (*card_done)(struct s_reader *reader); + int32_t (*card_info)(struct s_reader *); + void (*poll_status)(struct s_reader *); + int32_t (*do_ecm)(struct s_reader *, const struct ecm_request_t *, struct s_ecm_answer *); + int32_t (*do_emm_reassembly)(struct s_reader *, struct s_client *, struct emm_packet_t *); // Returns 1/true if the EMM is ready to be written in the card + int32_t (*do_emm)(struct s_reader *, struct emm_packet_t *); + int32_t (*do_rawcmd)(struct s_reader *, struct cmd_packet_t *); + void (*post_process)(struct s_reader *); + int32_t (*get_emm_type)(struct emm_packet_t *, struct s_reader *); + int32_t (*get_emm_filter)(struct s_reader *, struct s_csystem_emm_filter **, uint32_t *); + int32_t (*get_emm_filter_adv)(struct s_reader *, struct s_csystem_emm_filter **, uint32_t *, uint16_t, uint32_t, uint16_t, uint16_t, uint16_t, uint32_t); + int32_t (*get_tunemm_filter)(struct s_reader *, struct s_csystem_emm_filter **, uint32_t *); +}; + +typedef struct cw_extendted_t +{ +#ifdef WITH_EXTENDED_CW + uint8_t mode; + uint8_t audio[4][16]; // 4 x odd/even pairs of 8 byte CWs + uint8_t data[16]; // odd/even pair of 8 byte CW or 16 byte IV + uint8_t session_word[32]; // odd/even pair of 16 byte CW + uint8_t algo; // CSA, DES or AES128 + uint8_t algo_mode; // ECB or CBC +#else + uint8_t disabled; +#endif +} EXTENDED_CW; + +typedef struct s_cw_vote { + uint8_t cw[16]; + uint8_t votes; + uint8_t local_votes; + struct s_reader *voters[MAX_VOTE_CANDIDATES]; +} s_cw_vote; + +// CW Vote settings +typedef struct s_cw_vote_caid_data +{ + uint16_t caid; +} CW_VOTE_CAID_DATA; + +typedef struct s_cw_vote_caid_tab +{ + int32_t cvcnum; + CW_VOTE_CAID_DATA *cvcdata; +} CW_VOTE_CAID_TAB; + +typedef struct ecm_request_t +{ + uint8_t ecm[MAX_ECM_SIZE]; + uint8_t cw[16]; + EXTENDED_CW cw_ex; + uint8_t ecmd5[CS_ECMSTORESIZE]; + int16_t ecmlen; + uint16_t caid; + uint16_t ocaid; // original caid, used for betatunneling + uint16_t srvid; + uint16_t onid; + uint16_t tsid; + uint16_t pmtpid; + uint32_t ens; // enigma namespace + uint32_t vpid; // videopid + uint16_t chid; + uint16_t pid; + uint16_t idx; + uint32_t prid; + struct s_reader *selected_reader; + struct s_ecm_answer *matching_rdr; // list of matching readers + const struct s_reader *fallback; // fallback is the first fallback reader in the list matching_rdr + struct s_client *client; // contains pointer to 'c' client while running in 'r' client + uint64_t grp; + int32_t msgid; // client pending table index + uint8_t stage; // processing stage in server module + int8_t rc; + uint8_t rcEx; + struct timeb tps; // incoming time stamp + int8_t btun; // mark er as betatunneled + uint16_t reader_avail; // count of available readers for ecm + uint16_t readers; // count of available used readers for ecm + uint16_t reader_requested; // count of real requested readers + uint16_t localreader_count; // count of selected local readers + uint16_t cacheex_reader_count; // count of selected cacheex mode-1 readers + uint16_t fallback_reader_count; // count of selected fb readers + uint16_t reader_count; // count of selected not fb readers + int8_t preferlocalcards; + int8_t checked; // for doublecheck + uint8_t cw_checked[16]; // for doublecheck + int8_t readers_timeout_check; // set to 1 after ctimeout occurs and readers not answered are checked + uint32_t client_timeout; // clienttimeout per CAID + struct s_reader *origin_reader; + +#if defined MODULE_CCCAM + void *origin_card; // CCcam preferred card! +#endif + +#if defined MODULE_GBOX + uint32_t gbox_crc; // rcrc for gbox, used to identify ECM task in peer responses + uint16_t gbox_cw_src_peer; + uint16_t gbox_ecm_src_peer; + uint8_t gbox_ecm_dist; + uint8_t gbox_ecm_status; + LLIST *gbox_cards_pending; // type gbox_card_pending +#endif + + void *src_data; + uint32_t csp_hash; // csp has its own hash + + struct s_client *cacheex_src; // Cacheex origin +#ifdef CS_CACHEEX + int8_t cacheex_pushed; // to avoid duplicate pushs + uint8_t csp_answered; // =1 if er get answer by csp + LLIST *csp_lastnodes; // last 10 Cacheex nodes atm cc-proto-only + uint32_t cacheex_wait_time; // cacheex wait time in ms + uint8_t cacheex_wait_time_expired; // =1 if cacheex wait_time expires + uint16_t cacheex_mode1_delay; // cacheex mode 1 delay + uint8_t cacheex_hitcache; // =1 if wait_time due hitcache + void *cw_cache; // pointer to cw stored in cache +#endif +#ifdef CS_CACHEEX_AIO + int32_t ecm_time; // ecm-time in ms + uint8_t localgenerated; // flag for local generated CW +#endif + uint32_t cw_count; + uint8_t from_csp; // =1 if er from csp cache + uint8_t from_cacheex; // =1 if er from cacheex client pushing cache + uint8_t from_cacheex1_client; // =1 if er from cacheex-1 client + char msglog[MSGLOGSIZE]; + uint8_t cwc_cycletime; + uint8_t cwc_next_cw_cycle; +#ifdef CW_CYCLE_CHECK + char cwc_msg_log[MSGLOGSIZE]; +#endif +#ifdef WITH_STAPI5 + char dev_name[20]; +#endif + struct ecm_request_t *parent; + struct ecm_request_t *next; +#ifdef HAVE_DVBAPI + uint8_t adapter_index; +#endif + uint32_t vote_pool_session; // sesja dla vote_pool - zapobiega wielokrotnemu czyszczeniu + struct s_cw_vote vote_pool[MAX_VOTE_CANDIDATES]; +} ECM_REQUEST; + +struct s_ecm_answer +{ + uint8_t status; + struct s_reader *reader; + ECM_REQUEST *er; + int8_t rc; + uint8_t rcEx; + uint8_t cw[16]; + EXTENDED_CW cw_ex; + char msglog[MSGLOGSIZE]; + struct timeb time_request_sent; // using for evaluate ecm_time + int32_t ecm_time; + uint16_t tier; // only filled by local videoguard reader atm +#ifdef WITH_LB + int32_t value; + int32_t time; +#endif + struct s_ecm_answer *next; + CS_MUTEX_LOCK ecmanswer_lock; + struct s_ecm_answer *pending; + struct s_ecm_answer *pending_next; + bool is_pending; +}; + +struct s_acasc_shm +{ + uint16_t ac_count : 15; + uint16_t ac_deny : 1; +}; + +struct s_acasc +{ + uint16_t stat[10]; + uint8_t idx; // current active index in stat[] +}; + +struct s_cwresponse +{ + int32_t duration; + time_t timestamp; + int32_t rc; +}; + +struct s_cascadeuser +{ + uint16_t caid; + uint32_t prid; + uint16_t srvid; + time_t time; + int8_t cwrate; +}; + +typedef struct sidtabs +{ + SIDTABBITS ok; // positive services + SIDTABBITS no; // negative services +} SIDTABS; + +struct s_zap_list +{ + uint16_t caid; + uint32_t provid; + uint16_t chid; + uint16_t sid; + int8_t request_stage; + time_t lasttime; +}; + +// EMM reassemply +struct emm_rass +{ + int16_t emmlen; + int32_t provid; + uint8_t emm[MAX_EMM_SIZE]; +}; + +struct s_client +{ + uint32_t tid; + int8_t init_done; + pthread_mutex_t thread_lock; + int8_t thread_active; + int8_t kill; + int8_t kill_started; + LLIST *joblist; + IN_ADDR_T ip; + in_port_t port; + time_t login; // connection + time_t logout; // disconnection + time_t last; + time_t lastswitch; + time_t lastemm; + time_t lastecm; + time_t expirationdate; + uint32_t allowedtimeframe[SIZE_SHORTDAY][24][2]; // day[0-sun to 6-sat, 7-ALL],hours,minutes use as binary flags to reduce mem usage + uint8_t allowedtimeframe_set; // flag for internal use to mention if allowed time frame is used + int8_t c35_suppresscmd08; + uint8_t c35_sleepsend; + int8_t ncd_keepalive; + int8_t disabled; + uint64_t grp; + int8_t crypted; + int8_t dup; + LLIST *aureader_list; + int8_t autoau; + LLIST *ra_buf; // EMM reassembly buffer for viaccess + struct emm_rass *cw_rass; // EMM reassembly buffer for cryptoworks + int8_t monlvl; + CAIDTAB ctab; + TUNTAB ttab; + SIDTABS sidtabs; + SIDTABS lb_sidtabs; + int8_t typ; // first s_client is type s=starting (master) thread; type r = physical reader, type p = proxy reader both always have 1 s_reader struct allocated; type c = client (user logging in into oscam) type m = monitor type h = http server a = anticascader + uint8_t module_idx; + uint16_t last_srvid; + uint32_t last_provid; + uint16_t last_caid; + struct s_provid *last_providptr; + struct s_srvid *last_srvidptr; + uint32_t last_srvidptr_search_provid; + int32_t tosleep; + struct s_auth *account; + int32_t udp_fd; + struct SOCKADDR udp_sa; + socklen_t udp_sa_len; + int8_t tcp_nodelay; + int8_t log; + int32_t logcounter; + int32_t cwfound; // count found ECMs per client + int32_t cwcache; // count ECMs from cache1/2 per client + int32_t cwnot; // count not found ECMs per client + int32_t cwtun; // count betatunneled ECMs per client + int32_t cwignored; // count ignored ECMs per client + int32_t cwtout; // count timeouted ECMs per client + int32_t cwlastresptime; // last Responsetime (ms) +#ifdef CW_CYCLE_CHECK + int32_t cwcycledchecked; // count checked cwcycles per client + int32_t cwcycledok; // count pos checked cwcycles per client + int32_t cwcyclednok; // count neg checked cwcycles per client + int32_t cwcycledign; // count ign cwcycles per client +#endif + int32_t emmok; // count EMM ok + int32_t emmnok; // count EMM nok + int8_t pending; // number of ECMs pending +#ifdef CS_CACHEEX + int32_t cwcacheexpush; // count pushed ecms/cws + int32_t cwcacheexgot; // count got ecms/cws + int32_t cwcacheexhit; // count hit ecms/cws + LLIST *ll_cacheex_stats; // list for Cacheex statistics + int8_t cacheex_maxhop; + int32_t cwcacheexerr; // cw=00 or chksum wrong + int32_t cwcacheexerrcw; // same Hex, different CW + int16_t cwcacheexping; // peer ping in ms, only used by csp + int32_t cwc_info; // count of in/out comming cacheex ecms with CWCinfo + uint8_t cxnodeid_last[9]; // save previous nodeid + uint8_t cxnodeid_changer_detected; // save node-changer detection status +#ifdef CS_CACHEEX_AIO + int32_t cwcacheexgotlg; // count got localgenerated-flagged CWs + int32_t cwcacheexpushlg; // count pushed localgenerated-flagged CWs +#endif + uint8_t cacheex_needfilter; // flag for cachex mode 3 used with camd35 +#ifdef CS_CACHEEX_AIO + uint8_t cacheex_aio_checked; // flag for cacheex aio detection done +#endif +#endif +#ifdef CS_ANTICASC + struct s_zap_list client_zap_list[15]; // 15 last zappings from client used for ACoSC +#endif +#ifdef WEBIF + struct s_cwresponse cwlastresptimes[CS_ECM_RINGBUFFER_MAX]; // ringbuffer for last 20 times + int32_t cwlastresptimes_last; // ringbuffer pointer + int8_t wihidden; // hidden in webinterface status + char lastreader[64]; // last cw got from this reader +#endif + + uint8_t ucrc[4]; // needed by monitor and used by camd35 + uint32_t pcrc; // password crc + struct aes_keys *aes_keys; // used by camd33 and camd35 + uint16_t ncd_msgid; + uint16_t ncd_client_id; + uint8_t ncd_skey[16]; // Also used for camd35 Cacheex to store remote node id + +#ifdef MODULE_CCCAM + void *cc; +#endif + +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP) + uint8_t c35_extmode; +#endif +#endif + +#ifdef MODULE_GBOX + void *gbox; + uint16_t gbox_peer_id; +#endif + +#ifdef MODULE_GHTTP + void *ghttp; +#endif + + int32_t port_idx; // index in server ptab + int32_t ncd_server; // newcamd server + +#ifdef CS_ANTICASC + int32_t ac_fakedelay; // When this is -1, the global ac_fakedelay is used + uint16_t ac_limit; + int8_t ac_penalty; + struct s_acasc_shm acasc; +#endif + + FTAB fchid; + FTAB ftab; // user [caid] and ident filter + CLASSTAB cltab; + + int32_t pfd; // Primary FD, must be closed on exit + struct s_reader *reader; // points to s_reader when cl->typ='r' + + ECM_REQUEST *ecmtask; + + pthread_t thread; + +#ifdef MODULE_SERIAL + struct s_serial_client *serialdata; +#endif + // reader common + int32_t last_idx; + uint16_t idx; + + int8_t ncd_proto; + uint8_t ncd_header[12]; + + // camd35 + uint8_t upwd[64]; + int8_t is_udp; + int8_t stopped; + uint16_t lastcaid; + uint16_t lastsrvid; + int32_t lastpid; + int8_t disable_counter; + uint8_t lastserial[8]; + + // Failban value set bitwise - compared with BAN_ + int32_t failban; + + LLIST *cascadeusers; // s_cascadeuser + + int32_t n_request[2]; // count for number of request per minute by client + + void *work_mbuf; // Points to local data allocated in work_thread when the thread is running + void *work_job_data; // Points to current job_data when work_thread is running + +#ifdef MODULE_PANDORA + int32_t pand_autodelay; + uint8_t pand_send_ecm; + uint8_t pand_ignore_ecm; + uint8_t pand_md5_key[16]; +#endif + +#ifdef MODULE_SCAM + void *scam; +#endif + void *module_data; // private module data + + struct s_client *next; // make client a linked list + struct s_client *nexthashed; + + int8_t start_hidecards; +}; + +typedef struct s_ecm_whitelist_data +{ + uint16_t len; + uint16_t caid; + uint32_t ident; +} ECM_WHITELIST_DATA; + +typedef struct s_ecm_whitelist +{ + int32_t ewnum; + ECM_WHITELIST_DATA *ewdata; +} ECM_WHITELIST; + +typedef struct s_ecm_hdr_whitelist_data +{ + uint16_t len; + uint16_t caid; + uint32_t provid; + uint8_t header[20]; +} ECM_HDR_WHITELIST_DATA; + +typedef struct s_ecm_hdr_whitelist +{ + int32_t ehnum; + ECM_HDR_WHITELIST_DATA *ehdata; +} ECM_HDR_WHITELIST; + +// ratelimit +struct ecmrl +{ + struct timeb last; + uint8_t kindecm; + bool once; + uint8_t ecmd5[CS_ECMSTORESIZE]; + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint16_t chid; + int32_t ratelimitecm; + int32_t ratelimittime; + int32_t srvidholdtime; +}; +#define MAXECMRATELIMIT 20 + +// maxparallel service tracking +struct s_parallel_slot +{ + uint16_t srvid; // service ID + struct timeb last_ecm; // time of last ECM for this service + int32_t ecm_interval; // measured interval between ECMs in ms + struct s_client *client; // client using this slot +}; + +// maxparallel blocked client tracking +struct s_blocked_client +{ + struct s_client *client; // client pointer + uint16_t srvid; // blocked service ID +}; + +#ifdef MODULE_SERIAL +struct ecmtw +{ + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint16_t deg; + uint16_t freq; +}; +#endif + +typedef struct ce_csp_tab_data +{ + int32_t caid; + int32_t cmask; + int32_t prid; + int32_t srvid; + int16_t awtime; + int16_t dwtime; +} CECSPVALUETAB_DATA; + +typedef struct ce_csp_tab +{ + int32_t cevnum; + CECSPVALUETAB_DATA *cevdata; +} CECSPVALUETAB; + +typedef struct cacheex_check_cw_tab_data +{ + int32_t caid; + int32_t cmask; + int32_t prid; + int32_t srvid; + int8_t mode; + uint32_t counter; +} CWCHECKTAB_DATA; + +typedef struct cacheex_check_cw_tab +{ + int32_t cwchecknum; + CWCHECKTAB_DATA *cwcheckdata; + +} CWCHECKTAB; + +typedef struct cacheex_check_cw +{ + int8_t mode; + uint32_t counter; +} CWCHECK; + +typedef struct ce_csp_t +{ + int8_t mode; + int8_t maxhop; +#ifdef CS_CACHEEX_AIO + int8_t maxhop_lg; +#endif + CECSPVALUETAB filter_caidtab; + CAIDVALUETAB cacheex_nopushafter_tab; + uint8_t allow_request; + uint8_t allow_reforward; + uint8_t drop_csp; + uint8_t allow_filter; +#ifdef CS_CACHEEX_AIO + uint8_t allow_maxhop; +#endif + uint8_t block_fakecws; +#ifdef CS_CACHEEX_AIO + uint8_t cw_check_for_push; + uint8_t localgenerated_only; + CAIDTAB localgenerated_only_caidtab; + FTAB lg_only_tab; + uint8_t localgenerated_only_in; + CAIDTAB localgenerated_only_in_caidtab; + FTAB lg_only_in_tab; + uint8_t lg_only_in_aio_only; + uint8_t lg_only_remote_settings; + int32_t feature_bitfield; + char aio_version[CS_AIO_VERSION_LEN]; +#endif +} CECSP; + +struct s_emmlen_range +{ + int16_t min; + int16_t max; +}; + +typedef struct emm_packet_t +{ + uint8_t emm[MAX_EMM_SIZE]; + int16_t emmlen; + uint8_t caid[2]; + uint8_t provid[4]; + uint8_t hexserial[8]; // contains hexserial or SA of EMM + uint8_t type; + uint8_t skip_filter_check; + struct s_client *client; +} EMM_PACKET; + +typedef struct cmd_packet_t +{ + uint8_t cmd[MAX_CMD_SIZE]; + int16_t cmdlen; + struct s_client *client; +} CMD_PACKET; + +struct s_reader // contains device info, reader info and card info +{ + uint8_t keepalive; + uint8_t changes_since_shareupdate; + int32_t resetcycle; // ECM until reset + int32_t resetcounter; // actual count + uint32_t auprovid; // AU only for this provid + int8_t audisabled; // exclude reader from auto AU + struct timeb emm_last; // time of last successfully written emm + struct s_client *client; // pointer to 'r'client this reader is running in + LLIST *ll_entitlements; // entitlements + int8_t enable; + int8_t active; + int8_t dropbadcws; // Schlocke: 1=drops cw if checksum is wrong. 0=fix checksum (default) + int8_t disablecrccws; // 1=disable cw checksum test. 0=enable checksum check + uint64_t grp; + int8_t fallback; + FTAB fallback_percaid; + FTAB localcards; + FTAB disablecrccws_only_for; // ignore checksum for selected caid provid +#ifdef READER_CRYPTOWORKS + int8_t needsglobalfirst; // 0:Write one Global EMM for SHARED EMM disabled 1:Write one Global EMM for SHARED EMM enabled +#endif +#if defined(READER_NAGRA) + uint8_t cak63nuid[4]; + uint8_t cak63nuid_length; + uint8_t cak63cwekey[16]; + uint8_t cak63cwekey_length; +#endif +#ifdef READER_NAGRA_MERLIN + uint8_t mod1[112]; + uint8_t mod1_length; + uint8_t cmd0eprov[2]; + uint8_t cmd0eprov_length; + uint8_t mod2[112]; + uint8_t mod2_length; + uint8_t tmprsa[112]; + uint8_t data50[80]; + uint8_t data50_length; + uint8_t mod50[80]; + uint8_t mod50_length; + uint8_t key3588[136]; + uint8_t key3588_length; + uint8_t key60[96]; + uint8_t exp60[96]; + uint8_t key68[104]; + uint8_t exp68[104]; + uint8_t key3des[16]; + uint8_t klucz68[24]; + uint8_t pairtype; + uint8_t hasunique; + uint8_t key3460[96]; + uint8_t key3460_length; + uint8_t key3310[16]; + uint8_t key3310_length; + uint8_t cwekey[17][16]; + uint8_t cwekey_length[17]; + uint8_t idird[4]; + uint8_t idird_length; + uint8_t kdt05_00[216]; + uint8_t kdt05_10[208]; + uint8_t edata[255]; + uint8_t dt5num; + uint8_t out[255]; + uint8_t ideakey1[16]; + uint8_t block3[8]; + uint8_t v[8]; + uint8_t iout[8]; + uint32_t dword_83DBC; + uint8_t data2[4]; + uint8_t ecmheader[4]; + uint8_t timestmp1[4]; + uint8_t timestmp2[4]; + uint8_t cak7expo[0x11]; + uint8_t data[0x80]; + uint8_t step1[0x60]; + uint8_t step2[0x68]; + uint8_t step3[0x6c]; + uint8_t encrypted[0x68]; + uint8_t result[104]; + uint8_t stillencrypted[0x50]; + uint8_t resultrsa[0x50]; + uint32_t cak7_seq; + uint32_t needrestart; + uint8_t otpcsc[2]; + uint8_t otpcsc_length; + uint8_t otacsc[2]; + uint8_t otacsc_length; + uint8_t forcepair[1]; + uint8_t forcepair_length; + uint8_t cak7_camstate; + uint8_t cak7_aes_key[32]; + uint8_t cak7_aes_iv[16]; + int8_t forcecwswap; + int8_t evensa; + int8_t forceemmg; + int8_t cwpkota; + int8_t headermode; + struct timeb last_refresh; +#endif +#if defined(READER_DGCRYPT) || defined(READER_NAGRA_MERLIN) + uint8_t cardid[8]; +#endif +#ifdef CS_CACHEEX + CECSP cacheex; // CacheEx Settings + uint8_t cxnodeid_last[9]; // save previous nodeid + uint8_t cxnodeid_changer_detected; // save node-changer detection status +#endif + int32_t typ; + char label[64]; +#ifdef WEBIF + char *description; +#endif + char device[128]; +#ifdef WITH_CARDREADER + uint16_t slot; // in case of multiple slots like sc8in1; first slot = 1 + int32_t handle; // device handle + int64_t handle_nr; // device handle_nr for mutiple readers same driver + int32_t fdmc; // device handle for multicam + int32_t detect; + int32_t mhz; // actual clock rate of reader in 10khz steps + int32_t cardmhz; // standard clock speed your card should have in 10khz steps; normally 357 but for Irdeto cards 600 + int32_t divider; // PLL divider for internal readers +#endif + int32_t r_port; + char r_usr[64]; + char r_pwd[64]; // Max length 63 + null terminator + int32_t l_port; + CAIDTAB ctab; + uint32_t boxid; +#ifdef READER_TONGFANG + uint32_t tongfang_version; + uint8_t tongfang3_commkey[8]; + uint32_t tongfang3_calibsn; + uint32_t tongfang_boxid; + uint8_t tongfang3_deskey[8]; + uint8_t tongfang3_deskey_length; + uint8_t stbid[8]; + uint8_t stbid_length; +#endif +#ifdef READER_NAGRA_MERLIN + int8_t cak7_mode; + uint8_t cak7type; + uint8_t cwpkcaid[2]; + uint8_t cwpkcaid_length; + uint8_t nuid[4]; + uint8_t nuid_length; +#endif +#ifdef READER_NAGRA + int8_t nagra_read; // read nagra ncmed records: 0 Disabled (default), 1 read all records, 2 read valid records only + int8_t detect_seca_nagra_tunneled_card; +#endif +#ifdef READER_IRDETO + int8_t force_irdeto; +#endif +#ifdef WITH_CARDREADER + uint8_t boxkey[16]; // n3 boxkey 8 bytes, seca sessionkey 16 bytes, viaccess camid 4 bytes + uint8_t boxkey_length; + uint8_t rsa_mod[120]; // rsa modulus for nagra cards. + uint8_t rsa_mod_length; + uint8_t cwpk_mod[16]; // cwpk modulus for conax cards. + uint8_t cwpk_mod_length; + uint8_t des_key[128]; // 3des key for Viaccess 16 bytes, des key for Dre 128 bytes + uint8_t des_key_length; +#endif + uint8_t card_atr[64]; // ATR readed from card + int8_t card_atr_length; // length of ATR +#ifdef WITH_CARDREADER + uint8_t atr[64]; + int32_t atrlen; + int8_t seca_nagra_card; // seca nagra card +#endif + SIDTABS sidtabs; + SIDTABS lb_sidtabs; + uint8_t hexserial[8]; + int32_t nprov; + uint8_t prid[CS_MAXPROV][8]; + uint8_t sa[CS_MAXPROV][4]; // viaccess & seca +#if defined(READER_NAGRA) || defined(READER_NAGRA_MERLIN) + int32_t nsa; + int32_t nemm82u; + int32_t nemm84; + int32_t nemm84s; + int32_t nemm83u; + int32_t nemm83s; + int32_t nemm87; + uint8_t emm82u[CS_MAXPROV][7]; + uint8_t emm84[CS_MAXPROV][3]; + uint8_t emm84s[CS_MAXPROV][6]; + uint8_t emm83; + uint8_t emm83u[CS_MAXPROV][6]; + uint8_t emm83s[CS_MAXPROV][6]; + uint8_t emm87[CS_MAXPROV][6]; + uint8_t emm82; +#endif + uint8_t read_old_classes; // viaccess + uint8_t maturity; // viaccess & seca maturity level + uint16_t caid; + uint16_t cak7_emm_caid; + uint16_t b_nano; + uint16_t s_nano; + int8_t ecmcommand; // used for filtering nagra bad ecm commands + uint8_t ecmcommandcache[5]; // cachebuff for ecm commands + int32_t blockemm; + int32_t saveemm; + LLIST *blockemmbylen; + char *emmfile; + char pincode[5]; + int8_t logemm; + int8_t cachemm; + int16_t rewritemm; + int16_t deviceemm; // catch device specific emms (so far only used for viaccess) + int8_t card_status; +#ifdef WITH_CARDREADER + int8_t deprecated; // if 0 ATR obeyed, if 1 default speed (9600) is chosen; for devices that cannot switch baudrate +#endif + int8_t resetalways; // send reset after each commands (for pscs) + struct s_module ph; + const struct s_cardreader *crdr; + void *crdr_data; // Private card reader data + bool crdr_flush; // sci readers may disable flush per reader + const struct s_cardsystem *csystem; + void *csystem_data; // Private card system data + bool csystem_active; + uint8_t ncd_key[14]; + uint8_t ncd_skey[16]; + int8_t ncd_connect_on_init; + int8_t ncd_disable_server_filt; + int8_t ncd_proto; + int8_t currenthops; // number of hops (cccam & gbox) +#ifdef MODULE_CCCAM + char cc_version[7]; // cccam version + char cc_build[7]; // cccam build number + int8_t cc_maxhops; // cccam max distance + int8_t cc_mindown; // cccam min downhops + int8_t cc_want_emu; // Schlocke: Client want to have EMUs, 0 - NO; 1 - YES + uint32_t cc_id; + int8_t cc_keepalive; + int8_t cc_hop; // For non-cccam reader: hop for virtual cards + int8_t cc_reshare; + int32_t cc_reconnect; // reconnect on ecm-request timeout +#endif + int8_t tcp_connected; + int32_t tcp_ito; // inactivity timeout + int32_t tcp_rto; // reconnect timeout + int32_t tcp_reconnect_delay; // max tcp connection block delay + uint8_t ipv4force; + uint8_t ipv6_connect_failed; + struct timeb tcp_block_connect_till; // time tcp connect ist blocked + int32_t tcp_block_delay; // incrementing block time + time_t last_g; // get (if last_s-last_g>tcp_rto - reconnect ) + time_t last_s; // send + time_t last_check; // last checked + time_t last_poll; // last poll + FTAB fchid; + FTAB ftab; + CLASSTAB cltab; + ECM_WHITELIST ecm_whitelist; + ECM_HDR_WHITELIST ecm_hdr_whitelist; + int32_t brk_pos; + int32_t msg_idx; + int32_t secatype; // 0=not determined, 2=seca2, 3=nagra(~seca3) this is only valid for localreaders! + uint32_t maxreadtimeout; // in us + uint32_t minreadtimeout; // in us + uint32_t maxwritetimeout; // in us + uint32_t minwritetimeout; // in us +#if defined(WEBIF) || defined(LCDSUPPORT) + int32_t emmwritten[4]; // count written EMM + int32_t emmskipped[4]; // count skipped EMM + int32_t emmerror[4]; // count error EMM + int32_t emmblocked[4]; // count blocked EMM + int32_t webif_emmwritten[4]; // count written EMM for webif reader info + int32_t webif_emmskipped[4]; // count skipped EMM for webif reader info + int32_t webif_emmerror[4]; // count error EMM for reader webif info + int32_t webif_emmblocked[4]; // count blocked EMM for reader webif info + int32_t lbvalue; // loadbalance Value +#endif +#ifdef WITH_AZBOX + int32_t azbox_mode; +#endif +#ifdef WITH_CARDREADER + int32_t use_gpio; // Should this reader use GPIO functions + int gpio_outen; // fd of opened /dev/gpio/outen + int gpio_out; // fd of opened /dev/gpio/out + int gpio_in; // fd of opened /dev/gpio/in + uint32_t gpio; // gpio addr + // variables from icc_async.h start + int32_t convention; // Convention of this ICC + uint8_t protocol_type; // Type of protocol + uint32_t current_baudrate; // (for overclocking uncorrected) baudrate to prevent unnecessary conversions from/to termios structure + double worketu; // in us for internal and external readers calculated (1/D)*(F/cardclock)*1000000 + uint32_t read_timeout; // Max timeout (ETU) to receive characters + uint32_t char_delay; // Delay (ETU) after transmiting each successive char + uint32_t block_delay; // Delay (ms) after starting to transmit + uint32_t BWT, CWT; // (for overclocking uncorrected) block waiting time, character waiting time, in ETU + // variables from io_serial.h + int32_t written; // keep score of how much bytes are written to serial port, since they are echoed back they have to be read + // variables from protocol_t1.h + uint16_t ifsc; // Information field size for the ICC + uint8_t ns; // Send sequence number + int16_t smartdev_found; + int16_t smart_type; + uint16_t statuscnt; + uint16_t modemstat; +#endif +#ifdef READER_CRYPTOWORKS + EMM_PACKET *last_g_emm; // last global EMM + bool last_g_emm_valid; // status of last global EMM +#endif + uint8_t rom[15]; + uint8_t irdId[4]; +#ifdef READER_VIDEOGUARD + uint8_t payload4C[15]; + uint16_t VgCredit; + uint16_t VgPin; + uint8_t VgFuse; + uint8_t VgCountryC[3]; + uint8_t VgRegionC[8]; + uint8_t VgLastPayload[6]; + int32_t card_startdate_basemonth; + int32_t card_startdate_baseyear; + int32_t card_expiredate_basemonth; + int32_t card_expiredate_baseyear; +#endif +#ifdef WITH_LB + int32_t lb_weight; // loadbalance weight factor, if unset, weight=100. The higher the value, the higher the usage-possibility + int8_t lb_force_fallback; // force this reader as fallback if fallback or fallback_percaid paramters set + int32_t lb_usagelevel; // usagelevel for loadbalancer + int32_t lb_usagelevel_ecmcount; + struct timeb lb_usagelevel_time; // time for counting ecms, this creates usagelevel + struct timeb lb_last; // time for oldest reader + LLIST *lb_stat; // loadbalancer reader statistics + CS_MUTEX_LOCK lb_stat_lock; + int32_t lb_stat_busy; // do not add while saving +#endif + time_t card_valid_to; + // ratelimit + int32_t ratelimitecm; + int32_t ratelimittime; // ratelimit time in ms (everything below 60 ms is converted to ms by applying *1000) + int8_t ecmunique; // check for matching ecm hash in ratelimitslot + int32_t srvidholdtime; // time in ms to keep srvid in ratelimitslot (during this time not checked for ecmunique!) + // (everything below 60 ms is converted to ms by applying *1000) + struct timeb lastdvbapirateoverride; + uint32_t ecmsok; +#ifdef CS_CACHEEX_AIO + uint32_t ecmsoklg; +#endif + uint32_t webif_ecmsok; + uint32_t ecmsnok; + uint32_t webif_ecmsnok; + uint32_t ecmstout; + uint32_t webif_ecmstout; + uint32_t ecmnotfoundlimit; // config setting. restart reader if ecmsnok >= ecmnotfoundlimit + int32_t ecmsfilteredhead; // count filtered ECM's by ECM Headerwhitelist + int32_t ecmsfilteredlen; // count filtered ECM's by ECM Whitelist + int32_t webif_ecmsfilteredhead; // count filtered ECM's by ECM Headerwhitelist to readers ecminfo + int32_t webif_ecmsfilteredlen; // count filtered ECM's by ECM Whitelist to readers ecm info + float ecmshealthok; +#ifdef CS_CACHEEX_AIO + float ecmshealthoklg; +#endif + float ecmshealthnok; + float ecmshealthtout; + int32_t cooldown[2]; + int32_t maxparallel; // max parallel active services, 0 = unlimited (default) + float parallelfactor; // zapping tolerance multiplier (default: 2.0) + int32_t paralleltimeout; // timeout buffer in ms after expected ECM (default: 1000) + struct s_parallel_slot *parallel_slots; // regular service tracking (size: maxparallel) + struct s_parallel_slot *parallel_slots_prov; // pending service tracking (size: maxparallel * parallelfactor, rounded) + LLIST *blocked_services; // list of s_blocked_client for dropped services + CS_MUTEX_LOCK parallel_lock; // lock for parallel_slots access + int8_t parallel_full; // flag: 1 if reader is at capacity limit + int8_t cooldownstate; + struct timeb cooldowntime; + struct ecmrl rlecmh[MAXECMRATELIMIT]; +#ifdef READER_VIDEOGUARD + int8_t ndsversion; // 0 auto (default), 1 NDS1, 12 NDS1+, 2 NDS2 + int8_t fix_07; + int8_t fix_9993; + int8_t readtiers; // method to get videoguard tiers + uint8_t ins7E[0x1A + 1]; + uint8_t ins7E11[0x01 + 1]; + uint8_t ins42[0x25 + 1]; + uint8_t ins2e06[0x04 + 1]; + uint8_t k1_generic[0x10 + 1]; // k1 for generic pairing mode + uint8_t k1_unique[0x10 + 1]; // k1 for unique pairing mode +#endif +#ifdef WITH_CARDREADER + int8_t smargopatch; + int8_t autospeed; // 1 clockspeed set according to atr f max + int8_t ins7e11_fast_reset; + uint8_t sc8in1_dtrrts_patch; // fix for kernel commit 6a1a82df91fa0eb1cc76069a9efe5714d087eccd +#endif + +#ifdef READER_VIACCESS + AES_ENTRY *aes_list; // multi AES linked list + uint8_t initCA28; // To set when CA28 succeed + uint32_t key_schedule1[32]; + uint32_t key_schedule2[32]; +#endif + +#if defined(READER_DRE) || defined(READER_DRECAS) + char *userscript; + uint32_t force_ua; +#endif + +#ifdef READER_DRECAS + char *stmkeys; +#endif + +#ifdef MODULE_GBOX + uint8_t gbox_maxdist; + uint8_t gbox_maxecmsend; + uint8_t gbox_reshare; + int8_t gbox_cccam_reshare; + char last_gsms[128]; + uint16_t gbox_remm_peer; + uint16_t gbox_gsms_peer; + uint8_t gbox_force_remm; + uint16_t gbox_cw_src_peer; + uint8_t gbox_crd_slot_lev; + FTAB ccc_gbx_reshare_ident; + uint8_t send_offline_cmd; + uint16_t nb_send_crds; +#endif + +#ifdef MODULE_PANDORA + uint8_t pand_send_ecm; +#endif +#ifdef MODULE_GHTTP + uint8_t ghttp_use_ssl; +#endif +#ifdef WITH_EMU + FTAB emu_auproviders; // AU providers for Emu reader + int8_t emu_datecodedenabled; // date-coded keys for BISS + LLIST *ll_biss2_rsa_keys; // BISS2 RSA keys - Read from external PEM files +#endif +#ifdef READER_CONAX + uint8_t cnxlastecm; // == 0 - last ecm has not been paired ecm, > 0 last ecm has been paired ecm +#endif + LLIST *emmstat; // emm stats + CS_MUTEX_LOCK emmstat_lock; + struct s_reader *next; +}; + +struct s_cpmap +{ + uint16_t caid; + uint32_t provid; + uint16_t sid; + uint16_t chid; + uint16_t dwtime; + struct s_cpmap *next; +}; + +struct s_auth +{ + char usr[64]; + char *pwd; +#ifdef WEBIF + char *description; +#endif + int8_t uniq; +#ifdef CS_CACHEEX + CECSP cacheex; // CacheEx Settings + uint8_t no_wait_time; + uint8_t disablecrccacheex; + FTAB disablecrccacheex_only_for; +#endif + int16_t allowedprotocols; + LLIST *aureader_list; + int8_t autoau; + uint8_t emm_reassembly; // 0 = OFF; 1 = OFF / DVBAPI = ON; 2 = ON (default) + int8_t monlvl; + uint64_t grp; + int32_t tosleep; + int32_t umaxidle; + CAIDTAB ctab; + SIDTABS sidtabs; + FTAB fchid; + FTAB ftab; // user [caid] and ident filter + CLASSTAB cltab; + TUNTAB ttab; + int8_t preferlocalcards; + uint32_t max_connections; +#ifdef CS_ANTICASC + int32_t ac_fakedelay; // When this is -1, the global ac_fakedelay is used + int32_t ac_users; // 0 - unlimited + int8_t ac_penalty; // 0 - log, >0 - fake dw + struct s_acasc ac_stat; + int8_t acosc_max_ecms_per_minute; // user value 0 - unlimited + int8_t acosc_max_active_sids; // user value 0 - unlimited + int8_t acosc_zap_limit; // user value 0 - unlimited + int8_t acosc_penalty; // user value penalty + int32_t acosc_penalty_duration; // user value how long is penalty activ in sek. + time_t acosc_penalty_until; + int8_t acosc_penalty_active; // 0-deaktiv 1-max_active_sids 2-zap_limit 3-max_ecms_per_minute 4-penaly_duration + int32_t acosc_delay; // user value + int8_t acosc_user_zap_count; + time_t acosc_user_zap_count_start_time; +#endif +#ifdef WITH_LB + int32_t lb_nbest_readers; // When this is -1, the global lb_nbest_readers is used + int32_t lb_nfb_readers; // When this is -1, the global lb_nfb_readers is used + CAIDVALUETAB lb_nbest_readers_tab; // like nbest_readers, but for special caids +#endif + IN_ADDR_T dynip; + char *dyndns; + time_t expirationdate; + time_t firstlogin; + uint32_t allowedtimeframe[SIZE_SHORTDAY][24][2]; // day[0-sun to 6-sat, 7-ALL],hours,minutes use as binary flags to reduce mem usage + uint8_t allowedtimeframe_set; // flag for internal use to mention if allowed time frame is used + int8_t c35_suppresscmd08; + uint8_t c35_sleepsend; + int8_t ncd_keepalive; +#ifdef MODULE_CCCAM + int32_t cccmaxhops; + int8_t cccreshare; + int8_t cccignorereshare; + int8_t cccstealth; +#endif + int8_t disabled; + int32_t failban; + + int32_t cwfound; + int32_t cwcache; + int32_t cwnot; + int32_t cwtun; + int32_t cwignored; + int32_t cwtout; +#ifdef CW_CYCLE_CHECK + int32_t cwcycledchecked; // count checked cwcycles per client + int32_t cwcycledok; // count pos checked cwcycles per client + int32_t cwcyclednok; // count neg checked cwcycles per client + int32_t cwcycledign; // count ign cwcycles per client + int8_t cwc_disable; // disable cwc checking for this Client +#endif + int32_t emmok; + int32_t emmnok; +#ifdef CS_CACHEEX + int32_t cwcacheexpush; // count pushed ecms/cws + int32_t cwcacheexgot; // count got ecms/cws + int32_t cwcacheexhit; // count hit ecms/cws + int32_t cwcacheexerr; // cw=00 or chksum wrong + int32_t cwcacheexerrcw; // Same Hex, different CW + int32_t cwc_info; // count of in/out comming cacheex ecms with CWCinfo +#ifdef CS_CACHEEX_AIO + int32_t cwcacheexgotlg; // count got localgenerated-flagged CWs + int32_t cwcacheexpushlg; // count pushed localgenerated-flagged CWs +#endif +#endif + struct s_auth *next; +}; + + +struct s_srvid_caid +{ + uint16_t caid; + uint16_t nprovid; + uint32_t *provid; +}; + +struct s_srvid +{ + uint16_t srvid; + int8_t ncaid; + struct s_srvid_caid *caid; + char *data; + const char *prov; + const char *name; + const char *type; + const char *desc; + struct s_srvid *next; +}; + +struct s_rlimit +{ + struct ecmrl rl; + struct s_rlimit *next; +}; + +struct s_cw +{ + uint8_t cw[16]; +}; + +struct s_fakecws +{ + uint32_t count; + struct s_cw *data; +}; + +#ifdef MODULE_SERIAL +struct s_twin +{ + struct ecmtw tw; + struct s_twin *next; +}; +#endif + +struct s_tierid +{ + uint16_t tierid; + int8_t ncaid; + uint16_t caid[10]; + char name[33]; + struct s_tierid *next; +}; + +struct s_provid +{ + uint16_t caid; + uint16_t nprovid; + uint32_t *provid; + char prov[33]; + char sat[33]; + char lang[33]; + struct s_provid *next; +}; + +struct s_ip +{ + IN_ADDR_T ip[2]; + struct s_ip *next; +}; + +struct s_global_whitelist +{ + uint32_t line; // linenr of oscam.whitelist file, starting with 1 + char type; // w or i or l + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint16_t chid; + uint16_t pid; + uint16_t ecmlen; + uint16_t mapcaid; + uint32_t mapprovid; + struct s_global_whitelist *next; +}; + +struct s_cacheex_matcher +{ + uint32_t line; // linenr of oscam.Cacheex file, starting with 1 + char type; // m + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint16_t chid; + uint16_t pid; + uint16_t ecmlen; + + uint16_t to_caid; + uint32_t to_provid; + uint16_t to_srvid; + uint16_t to_chid; + uint16_t to_pid; + uint16_t to_ecmlen; + + int32_t valid_from; + int32_t valid_to; + + struct s_cacheex_matcher *next; +}; + +struct s_config +{ + int32_t nice; + uint32_t netprio; + uint32_t ctimeout; + uint32_t ftimeout; + CAIDVALUETAB ftimeouttab; + CAIDVALUETAB ctimeouttab; // ← clienttimeout_percaid + uint32_t cmaxidle; + int32_t ulparent; + uint32_t delay; + int32_t bindwait; + int32_t tosleep; + IN_ADDR_T srvip; + char *usrfile; + char *cwlogdir; + char *emmlogdir; + char *logfile; + char *mailfile; + int8_t disablecrccws; // 1=disable cw checksum test. 0=enable checksum check + FTAB disablecrccws_only_for; // ignore checksum for selected caid provid +#ifdef CS_CACHEEX_AIO + uint8_t cacheex_srcname_webif; // show cacheex-srcname not cache3 in log +#endif + uint8_t logtostdout; + uint8_t logtosyslog; + int8_t logduplicatelines; + int32_t initial_debuglevel; + char *sysloghost; + int32_t syslogport; +#if defined(WEBIF) || defined(MODULE_MONITOR) + uint32_t loghistorylines; +#endif + int8_t disablelog; + int8_t disablemail; + int8_t disableuserfile; + int8_t usrfileflag; + struct s_auth *account; + struct s_srvid *srvid[16]; + struct s_tierid *tierid; + struct s_provid *provid; + struct s_sidtab *sidtab; +#ifdef MODULE_MONITOR + int32_t mon_port; + IN_ADDR_T mon_srvip; + struct s_ip *mon_allowed; + uint8_t mon_level; +#endif + int32_t aulow; + int32_t hideclient_to; +#ifdef WEBIF + int32_t http_port; + IN_ADDR_T http_srvip; + char *http_user; + char *http_pwd; + char *http_css; + int8_t http_prepend_embedded_css; + char *http_jscript; + char *http_tpl; + char *http_piconpath; + char *http_script; +#ifndef WEBIF_JQUERY + char *http_extern_jquery; +#endif + int32_t http_refresh; + int32_t poll_refresh; + int8_t http_hide_idle_clients; + char *http_hide_type; + int8_t http_showpicons; + int8_t http_picon_size; + int8_t http_status_log; + int8_t http_showmeminfo; + int8_t http_showecminfo; + int8_t http_showloadinfo; + int8_t http_showuserinfo; + int8_t http_showreaderinfo; + int8_t http_showcacheexinfo; + struct s_ip *http_allowed; + int8_t http_readonly; + IN_ADDR_T http_dynip[MAX_HTTP_DYNDNS]; + uint8_t http_dyndns[MAX_HTTP_DYNDNS][64]; + int8_t http_use_ssl; + int8_t https_force_secure_mode; + int8_t https_auto_create_cert; + char *http_cert; + char *http_help_lang; + char *http_locale; + char *http_oscam_label; + int32_t http_emmu_clean; + int32_t http_emms_clean; + int32_t http_emmg_clean; +#endif + int8_t http_full_cfg; + int8_t http_overwrite_bak_file; + int32_t failbantime; + int32_t failbancount; + LLIST *v_list; // Failban list +#ifdef MODULE_CAMD33 + int32_t c33_port; + IN_ADDR_T c33_srvip; + uint8_t c33_key[16]; + int32_t c33_crypted; + int32_t c33_passive; + struct s_ip *c33_plain; +#endif +#if defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP) + int32_t c35_port; + IN_ADDR_T c35_srvip; + int8_t c35_tcp_suppresscmd08; + int8_t c35_udp_suppresscmd08; + PTAB c35_tcp_ptab; + IN_ADDR_T c35_tcp_srvip; +#endif + int8_t c35_suppresscmd08; // used in cccam module + int8_t getblockemmauprovid; + int32_t umaxidle; //User max Idle +#ifdef MODULE_NEWCAMD + PTAB ncd_ptab; + IN_ADDR_T ncd_srvip; + uint8_t ncd_key[14]; + int8_t ncd_keepalive; + int8_t ncd_mgclient; + struct s_ip *ncd_allowed; +#endif +#ifdef MODULE_RADEGAST + int32_t rad_port; + IN_ADDR_T rad_srvip; + struct s_ip *rad_allowed; + char *rad_usr; +#endif +#ifdef MODULE_CCCAM + uint16_t cc_port[CS_MAXPORTS]; + int8_t cc_reshare; + int8_t cc_ignore_reshare; + int32_t cc_update_interval; + IN_ADDR_T cc_srvip; + char cc_version[7]; + int8_t cc_minimize_cards; + int8_t cc_keep_connected; + int8_t cc_stealth; + int8_t cc_reshare_services; + int8_t cc_forward_origin_card; + uint8_t cc_fixed_nodeid[8]; + uint32_t cc_recv_timeout; // The poll() timeout parameter in ms. Default: DEFAULT_CC_RECV_TIMEOUT (2000 ms). +#endif +#ifdef MODULE_GBOX + #define GBOX_MY_VERS_DEF 0x2A + #define GBOX_MY_CPU_API_DEF 0x61 + #define GBOX_MAX_PROXY_CARDS 32 + #define GBOX_MAX_IGNORED_PEERS 16 + #define GBOX_MAX_BLOCKED_ECM 16 + #define GBOX_MAX_REMM_PEERS 8 + #define GBOX_MAX_DEST_PEERS 16 + #define GBOX_MAX_MSG_TXT 127 + uint16_t gbox_port[CS_MAXPORTS]; + char *gbox_hostname; + uint32_t gbox_reconnect; + uint32_t gbox_password; + unsigned long gbox_proxy_card[GBOX_MAX_PROXY_CARDS]; + int8_t gbox_proxy_cards_num; + uint32_t gbox_my_vers; + uint8_t gbox_my_cpu_api; + uint8_t gsms_dis; + uint8_t log_hello; + uint8_t dis_attack_txt; + char *gbox_tmp_dir; + uint8_t cc_gbx_reshare_en; + uint16_t gbox_ignored_peer[GBOX_MAX_IGNORED_PEERS]; + uint8_t gbox_ignored_peer_num; + uint16_t accept_remm_peer[GBOX_MAX_REMM_PEERS]; + uint8_t accept_remm_peer_num; + uint16_t gbox_block_ecm[GBOX_MAX_BLOCKED_ECM]; + uint8_t gbox_block_ecm_num; + uint8_t gbox_save_gsms; + uint8_t gbox_msg_type; + uint16_t gbox_dest_peers[GBOX_MAX_DEST_PEERS]; + uint8_t gbox_dest_peers_num; + char gbox_msg_txt[GBOX_MAX_MSG_TXT+1]; +#endif +#ifdef MODULE_SERIAL + char *ser_device; +#endif + int32_t max_log_size; + int8_t waitforcards; + int32_t waitforcards_extra_delay; + int8_t preferlocalcards; + int32_t reader_restart_seconds; // schlocke: reader restart auf x seconds, disable = 0 + int8_t dropdups; // drop duplicate logins + + + // Loadbalancer-Config: + int32_t lb_mode; // schlocke: reader loadbalancing mode + int32_t lb_auto_betatunnel; // automatic selection of betatunnel convertion based on learned data + int32_t lb_auto_betatunnel_mode; // automatic selection of betatunnel direction +#ifdef WITH_LB + int32_t lb_save; // schlocke: load/save statistics to file, save every x ecms + int32_t lb_nbest_readers; // count of best readers + int32_t lb_nfb_readers; // count of fallback readers + int32_t lb_min_ecmcount; // minimal ecm count to evaluate lbvalues + int32_t lb_max_ecmcount; // maximum ecm count before reseting lbvalues + int32_t lb_reopen_seconds; // time between retrying failed readers/caids/prov/srv + int8_t lb_reopen_invalid; // default=1; if 0, rc=E_INVALID will be blocked until stats cleaned + int8_t lb_force_reopen_always; // force reopening immediately all failing readers if no matching reader found + int32_t lb_retrylimit; // reopen only happens if reader response time > retrylimit + CAIDVALUETAB lb_retrylimittab; + CAIDVALUETAB lb_nbest_readers_tab; // like nbest_readers, but for special caids + CAIDTAB lb_noproviderforcaid; // do not store loadbalancer stats with providers for this caid + char *lb_savepath; // path where the stat file is save. Empty=default=/tmp/.oscam/stat + int32_t lb_stat_cleanup; // duration in hours for cleaning old statistics + int32_t lb_max_readers; // limit the amount of readers during learning + int32_t lb_auto_betatunnel_prefer_beta; // prefer-beta-over-nagra factor + int32_t lb_auto_timeout; // Automatic timeout by loadbalancer statistics + int32_t lb_auto_timeout_p; // percent added to avg time as timeout time + int32_t lb_auto_timeout_t; // minimal time added to avg time as timeout time +#endif + int32_t resolve_gethostbyname; + int8_t double_check; // schlocke: Double checks each ecm+dcw from two (or more) readers + FTAB double_check_caid; // do not store loadbalancer stats with providers for this caid + + // CW Vote settings + int8_t cwvote_enabled; + int8_t cwvote_log_enabled; + int32_t cwvote_timeout; // w milisekundach, 0 = wyłączony + int32_t cwvote_min_votes; + float cwvote_local_weight; // waga local readera (np. 2.0) + int32_t cwvote_max_candidates; + int32_t cwvote_compare_len; // 8 lub 16 + int32_t cwvote_fallback; // 0=wait, 1=best anyway, 2=first CW + CW_VOTE_CAID_TAB cwvote_caids; + +#ifdef HAVE_DVBAPI + int8_t dvbapi_enabled; + int8_t dvbapi_au; + char *dvbapi_usr; + int8_t dvbapi_boxtype; + int8_t dvbapi_pmtmode; + int8_t dvbapi_requestmode; + int32_t dvbapi_listenport; // TCP port to listen instead of camd.socket (network mode, default=0 -> disabled) + IN_ADDR_T dvbapi_srvip; + SIDTABS dvbapi_sidtabs; + int32_t dvbapi_delayer; // delayer ms, minimum time to write cw + int8_t dvbapi_ecminfo_file; // Enable or disable ecm.info file creation + int8_t dvbapi_ecminfo_type; + int8_t dvbapi_read_sdt; + int8_t dvbapi_write_sdt_prov; + int8_t dvbapi_extended_cw_api; +#ifdef MODULE_STREAMRELAY + int8_t dvbapi_demuxer_fix; +#endif +#endif + +#ifdef CS_ANTICASC + int8_t ac_enabled; + int32_t ac_users; // num of users for account (0 - default) + int32_t ac_stime; // time to collect AC statistics (3 min - default) + int32_t ac_samples; // qty of samples + int8_t ac_penalty; // 0 - write to log + int32_t ac_fakedelay; // 100-1000 ms + int32_t ac_denysamples; + char *ac_logfile; + struct s_cpmap *cpmap; + int8_t acosc_enabled; + int8_t acosc_max_ecms_per_minute; // global value 0 - unlimited + int8_t acosc_max_active_sids; // global value 0 - unlimited + int8_t acosc_zap_limit; // global value 0 - unlimited + int32_t acosc_penalty_duration; // global value how long is penalty activ in sek. + int8_t acosc_penalty; // global value + int32_t acosc_delay; // global value +#endif + +#ifdef LEDSUPPORT + int8_t enableled; // 0=disabled led, 1=enable led for routers, 2=enable qboxhd led +#endif + +#ifdef LCDSUPPORT + int8_t enablelcd; + char *lcd_output_path; + int32_t lcd_hide_idle; + int32_t lcd_write_intervall; +#endif + +#ifdef MODULE_PANDORA + int8_t pand_skip_send_dw; + struct s_ip *pand_allowed; + char *pand_usr; + char *pand_pass; + int8_t pand_ecm; + int32_t pand_port; + IN_ADDR_T pand_srvip; +#endif + +#ifdef MODULE_SCAM + int32_t scam_port; + IN_ADDR_T scam_srvip; + struct s_ip *scam_allowed; +#endif + +#ifdef MODULE_STREAMRELAY + int8_t stream_relay_enabled; + int32_t stream_relay_port; + char *stream_relay_user; + CAIDTAB stream_relay_ctab; // use the stream server for these caids + char *stream_source_host; + int8_t stream_client_source_host; + int32_t stream_source_port; + char *stream_source_auth_user; + char *stream_source_auth_password; + uint32_t stream_relay_buffer_time; + int8_t stream_relay_reconnect_count; + int8_t stream_display_client; + int8_t stream_reuse_client; +#ifdef WEBIF + int8_t stream_hide_client; +#endif +#ifdef WITH_NEUTRINO +#define DEFAULT_STREAM_SOURCE_PORT 31339 //Neutrino +#else +#define DEFAULT_STREAM_SOURCE_PORT 8001 //Enigma2 +#endif +#endif +#ifdef WITH_EMU + uint32_t emu_stream_ecm_delay; + int8_t emu_stream_emm_enabled; +#endif + + int32_t max_cache_time; // seconds ecms are stored in ecmcwcache + int32_t max_hitcache_time; // seconds hits are stored in cspec_hitcache (to detect dyn wait_time) + + int8_t reload_useraccounts; + int8_t reload_readers; + int8_t reload_provid; + int8_t reload_services_ids; + int8_t reload_tier_ids; + int8_t reload_fakecws; + int8_t reload_ac_stat; + int8_t reload_log; + + int8_t block_same_ip; // 0=allow all, 1=block client requests to reader with same ip (default=1) + int8_t block_same_name; // 0=allow all, 1=block client requests to reader with same name (default=1) + +#ifdef CS_CACHEEX +#ifdef CS_CACHEEX_AIO + uint32_t cw_cache_size; + uint32_t cw_cache_memory; + CWCHECKTAB cw_cache_settings; + + uint32_t ecm_cache_size; + uint32_t ecm_cache_memory; + int32_t ecm_cache_droptime; +#endif + uint8_t wait_until_ctimeout; + CWCHECKTAB cacheex_cwcheck_tab; + IN_ADDR_T csp_srvip; + int32_t csp_port; + CECSPVALUETAB cacheex_wait_timetab; + CAIDVALUETAB cacheex_mode1_delay_tab; +#ifdef CS_CACHEEX_AIO + uint8_t waittime_block_start; + uint16_t waittime_block_time; +#endif + CECSP csp; // CSP Settings + uint8_t cacheex_enable_stats; // enable stats + struct s_cacheex_matcher *cacheex_matcher; +#ifdef CS_CACHEEX_AIO + uint8_t cacheex_dropdiffs; + uint8_t cacheex_lg_only_remote_settings; + uint8_t cacheex_localgenerated_only; + CAIDTAB cacheex_localgenerated_only_caidtab; + FTAB lg_only_tab; + uint8_t localgenerated_only_in; + CAIDTAB localgenerated_only_in_caidtab; + FTAB lg_only_in_tab; + uint8_t lg_only_in_aio_only; + uint8_t lg_only_remote_settings; + int32_t feature_bitfield; + char aio_version[CS_AIO_VERSION_LEN]; +#endif + CAIDVALUETAB cacheex_nopushafter_tab; + uint8_t cacheex_localgenerated_only_in; + CAIDTAB cacheex_localgenerated_only_in_caidtab; + FTAB cacheex_lg_only_in_tab; + uint8_t cacheex_lg_only_in_aio_only; + CECSPVALUETAB cacheex_filter_caidtab; + CECSPVALUETAB cacheex_filter_caidtab_aio; + uint32_t cacheex_push_lg_groups; + FTAB cacheex_lg_only_tab; +#endif + +#ifdef CW_CYCLE_CHECK + int8_t cwcycle_check_enable; // on or off + CAIDTAB cwcycle_check_caidtab; // Caid for CW Cycle Check + int32_t keepcycletime; // how long stay the learned Cycletime in Memory + int32_t maxcyclelist; // max size of cwcyclelist + int8_t onbadcycle; // what to do on bad cwcycle + int8_t cwcycle_dropold; // what to do on old ecmd5/cw + int8_t cwcycle_sensitive; + int8_t cwcycle_allowbadfromffb; // allow Bad cycles from Fixed Fallbackreader + int8_t cwcycle_usecwcfromce; // Use CWC Info from Cacheex Sources for CWC Checking +#endif + + // Global whitelist: + struct s_global_whitelist *global_whitelist; + int8_t global_whitelist_use_l; + int8_t global_whitelist_use_m; + + char *ecmfmt; + char *pidfile; + + int32_t max_pending; + + // Ratelimit list + struct s_rlimit *ratelimit_list; + + // fake cws + struct s_fakecws fakecws[0x100]; + +#ifdef MODULE_SERIAL + struct s_twin *twin_list; +#endif +}; + +struct s_clientinit +{ + void *(*handler)(struct s_client *); + struct s_client *client; +}; + +struct s_clientmsg +{ + uint8_t msg[1024]; + int32_t len; + int32_t cmd; +}; + +typedef struct reader_stat_t +{ + int32_t rc; + uint16_t caid; + uint32_t prid; + uint16_t srvid; + uint32_t chid; + int16_t ecmlen; + + struct timeb last_received; + + int32_t ecm_count; + int32_t time_avg; + int32_t time_stat[LB_MAX_STAT_TIME]; + int32_t time_idx; + + int32_t fail_factor; +} READER_STAT; + +typedef struct cs_stat_query +{ + uint16_t caid; + uint32_t prid; + uint16_t srvid; + uint32_t chid; + int16_t ecmlen; +} STAT_QUERY; + +struct s_write_from_cache +{ + ECM_REQUEST *er_new; + ECM_REQUEST *er_cache; +}; + +/* =========================== + * global variables + * =========================== */ +extern pthread_key_t getclient; +extern struct s_client *first_client; +extern CS_MUTEX_LOCK config_lock; +extern CS_MUTEX_LOCK clientlist_lock; +extern CS_MUTEX_LOCK readerlist_lock; +extern struct s_reader *first_active_reader; // points to list of _active_ readers (enable = 1, deleted = 0) +extern LLIST *configured_readers; + +// These are used pretty much everywhere +extern struct s_config cfg; +extern uint16_t cs_dblevel; + +#include "oscam-log.h" +#include "oscam-log-reader.h" + +// Add here *only* funcs that are implemented in oscam.c and are called in other places +void cs_exit(int32_t sig); +void cs_exit_oscam(void); +void cs_restart_oscam(void); +int32_t cs_get_restartmode(void); + +void set_thread_name(const char *thread_name); +int32_t start_thread(char *nameroutine, void *startroutine, void *arg, pthread_t *pthread, int8_t detach, int8_t modify_stacksize); +int32_t start_thread_nolog(char *nameroutine, void *startroutine, void *arg, pthread_t *pthread, int8_t detach, int8_t modify_stacksize); +void kill_thread(struct s_client *cl); + +struct s_module *get_module(struct s_client *cl); +void module_reader_set(struct s_reader *rdr); + +// Until we find a better place for these (they are implemented in oscam-simples.h) +char *get_servicename(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +char *get_servicename_or_null(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +char *get_picon_servicename_or_null(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +int32_t picon_servicename_remve_hd(char *buf, uint32_t buflen); +char *get_tiername(uint16_t tierid, uint16_t caid, char *buf); +char *get_tiername_defaultid(uint16_t tierid, uint16_t caid, char *buf); +char *get_provider(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +char *get_providername(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +char *get_providername_or_null(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +void add_provider(uint16_t caid, uint32_t provid, const char *name, const char *sat, const char *lang); +const char *get_cl_lastprovidername(struct s_client *cl); +bool boxtype_is(const char *boxtype); +bool boxname_is(const char *boxname); +const char *boxtype_get(void); +const char *boxname_get(void); +static inline bool caid_is_fake(uint16_t caid) { return caid == 0xffff; } +static inline bool caid_is_biss(uint16_t caid) { return caid >> 8 == 0x26; } +static inline bool caid_is_biss_fixed(uint16_t caid) { return caid == 0x2600 || caid == 0x2602; } // fixed cw, fake ecm +static inline bool caid_is_biss_dynamic(uint16_t caid) { return caid == 0x2610; } // dynamic cw, real ecm and emm +static inline bool caid_is_seca(uint16_t caid) { return caid >> 8 == 0x01; } +static inline bool caid_is_viaccess(uint16_t caid) { return caid >> 8 == 0x05; } +static inline bool caid_is_irdeto(uint16_t caid) { return caid >> 8 == 0x06; } +static inline bool caid_is_videoguard(uint16_t caid) { return caid >> 8 == 0x09; } +static inline bool caid_is_conax(uint16_t caid) { return caid >> 8 == 0x0B; } +static inline bool caid_is_cryptoworks(uint16_t caid) { return caid >> 8 == 0x0D; } +static inline bool caid_is_powervu(uint16_t caid) { return caid >> 8 == 0x0E; } +static inline bool caid_is_director(uint16_t caid) { return caid >> 8 == 0x10; } +static inline bool caid_is_betacrypt(uint16_t caid) { return caid >> 8 == 0x17; } +static inline bool caid_is_nagra(uint16_t caid) { return caid >> 8 == 0x18; } +static inline bool caid_is_bulcrypt(uint16_t caid) { return caid == 0x5581 || caid == 0x4AEE; } +static inline bool caid_is_dre(uint16_t caid) { return caid == 0x4AE0 || caid == 0x4AE1 || caid == 0x2710;} +static inline bool caid_is_tongfang(uint16_t caid) { return caid == 0x4A02; } +#if defined(WITH_EXTENDED_CW) || defined(MODULE_STREAMRELAY) +static inline bool select_csa_alt(const ECM_REQUEST *er) { + return (caid_is_videoguard(er->caid) && er->ecm[4] != 0 && (er->ecm[2] - er->ecm[4]) == 4); +} +#endif +#ifdef MODULE_STREAMRELAY +static inline uint8_t get_ecm_mode(const ECM_REQUEST *er) { + return (caid_is_videoguard(er->caid) && er->ecmlen >= 4) ? (er->ecm[er->ecmlen - 1] & 0x0F) : 0; +} +#endif +const char *get_cardsystem_desc_by_caid(uint16_t caid); + +#ifdef WITH_EMU +FILTER *get_emu_prids_for_caid(struct s_reader *rdr, uint16_t caid); +#endif + +#endif diff --git a/images/image1.jpg b/images/image1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0d27c94f904c5478a898c4847c1d4d415d6b9546 GIT binary patch literal 73977 zcmeFa2Ut^Gmp>Yc(vjYzNbe$D0f~wWm+8O{N zA|ik{;UD019H0guAtwIuNBEEu{?3q}IYUZ%hLVizEIBnLH8m9_6%`FF104-5JuMX# z9Sa>jBNH<-Gxa%EHWnr}1}0{vA2%T)A^e8)48@r<6ihT!G)({L&*^6XJvmW4X)6g4 zAAp#ih=iW#v=zVu01%xa^!7)G|LsFWOz7iTGI9z^Dnf%wIsh>d2?;SN$&X$WTKg0J zA3#cfhJjc5`dLQ9dt`i%nPi^DWRUaUC~aak>PHL6-hUEALCM0(#?B!qBrGB-CMU0; zc=?Kw+D&y0O)c$P#&=9i&CD$iY1mtD}F+Ya(FC+Ws1{U;R8riP{`)yoE05u5_Ve&}m z0YJcsab~y(accP^7dhXRVUr{gfk(yf8JOO4n#IGKKi8iE(!`~{=;JRosI|hjrJU3D z)}-)(kY*8*yi-8%GoMp{swwDA8qS6X{ZR_I^rd?{?i3JFaz7lfatfHaYYXd*Ph1c_ z1+Z%3B&d21x65$Zr-0eDx0!)TFswOi+RlQ*q4p`Dt^X82MZAOeD?*3@v9*gL>9lEh zmNQGZLyG_-7{>h%U8xEG`Skz2l+S78wf$)Du;``Xv+b_6w5{Vo*zftsx{Zlw@D%WS z){Oo=BaKR{1KdvmzvsrR-*J-1SHo*uz~A%Y@*nxwjmdya{}0Rv{NMC=vv&dgJj~Dl zdYqDrjz&fGkc#dH+wm18`N%#Y<%q6%%J3!&Z39K=CNe7RI+#s+d@q5Re<|niI0=|D zRSoa~g({!(>y~WDk;nAF9z@5NhZLN!5O z#Cj*UtmLTX?62vlDG=(Ie#%L9p?DG<5(@KyUz}8)tC~!y`Zc| z(Nxkg!_#)cGshC_^#NREnH%k8=RV?fq*?>nw^|boB$X(&-AT$`*mb0VU^mlv`TM>;s*MW4cL>G*9 za5I#lvjh0+hvXt{1SGyD8fe^>wW`8N$izP(3Ax-(!S-MWK@%{^EikOg<7Yg&RdH1Y zxuhL*{uDr|&S@0$!LrvwutG}g@SWWv-xB!7lSgNOy0G5rLd%>6_*igd`5?4I=Ln>8 zu%~+XcYR8RIRV3`WUz+l=?Prv557W-Y~v{){ofZVS-pb9un%SprOxAx@=5&aF0Vz z(xSI>A?)t;Hm*}Z$tfT{<=A5^9=f(knBq16bMQat)jzJ!i#%-jv4At^wvq^iWV=}& z4j6bv%~~33r>m&%X!qVfbD9Mt>Gp|poAUj$_9tW;k$ICikAZq#hjeE%Zu(mEDPVuj z`@N-N?0pYyhB<-Agb&HX^LO==Zn)TFmseEJm~(3pSm`+{O)VZ359)QP9up|0uEwcF4JCDW?v-Z8(^KfIr+O}f4e9c zvHx6jerFB-QRDnva(@~ezZw7kr_C*YdUYlPqQ5xKdc8aa%-aqF*G51mfYBFb4UpGY zzeRjdW1|sB*yanm)+KgreizPr3V0!GPT1@?5oS_I3NIFTX}P!;d&498a6kGKKzyID zY5DN8%}@IL-^v?<(~w4oC20s{f;qe z)5`D`E;~&u@N5`*u9Y=zT+g+l8@j%{NosUfx?8ZI=-I7JcI)>HO44xZ3TV&*?st3n6rQ7-MlZbw4eDc-&b^HMw$E(zVJJvh}Jd=iD z?!cNEi(tJKFf1eMDS%nqX=?>*ae|bp>zQmYL6jgCPXXD*7|mCwfcqPcmRO4dEOGlO zK=TuNGk#WJ)KAngzsIfgo&!v&dATFh{JGY#t)kDdI%#l*OA3rn6 zZ!%Q!Vz6n(XKS$nndR&^8mF~Tb485q$yr)i&`pA6{`+od{;`j<6HwPG#Fy?}Q0grB z))c7t6wq@DXyIWR^E-<@nEBp#0Sj$8>S#IUq3}jn4oNK_YK|WwQ;HNdAxs8^^d8Y{ z#xZq3g)8Sbjuo+MGiMQbYoIfGFj|So45X7~YVs`B@%ZZJDpE~%IN3LWjxT1xO~P@n z{Gos5oIeWeogDkv6=;o6;xzK`h-q>hO6V3#GR_FbKYxy}=#`&q<#`DzPe}`r2Xd+v z;WVp;N3BE6>m5UoBM&^KsBgct-|n}6&FdhX-YjBbnR@jkqQs+@0mq)KKJ?I~0!CqJ z2}aH<(yk(dD=SKMIWonV@7r5YOqgwv^!UgBeb-FXx4VU>T)+EcPMulc-s5 zsSO~yyfuwgmD_wJfa%k~Mr9$_{a%yc%ETDS=+_JAcKHYMbAe<5>W?Ikm2o$*O|w)z zc5}3)GcUPOHyZopl7VDxkM0M<`K5c@`1RCSse&mlUwtCoJNieu`~!>M2rswU!j_m} zoj|g+{>4~jcq>+F=G;1ep|=966}^gt1-Y*!7ZY!3d>oMzkF>O^u}@`r+5IT4cm6}u zgbCetfK8po6P(pCt@m-eEf-qu=qu>l-Uh6tRtf7-aeN$UBzd)%?@D!w{j;n$-(+T< z5z($c`wZ`k{txVj{K-`ug6b5ot-mD*&cwr^+n~po_dMmSt0anHB7p^ThnF0`p^>Mdwj3o-V}>@1J!F`1}@uEdb6?i~yIAKpaKH zDIh%4cK>Ajc*|q?Fby^T%ojnMB-K0;P#Dj10n51WgUz~FJq-bIVblj$d2e%Ofr)0n~6fywaNnZG#breGZe_tU20c8P6mL@#zX?W7%V z(%na-{=}9VTRNlv((?#6b$&R}OqX)s257*7)@c--V|OqSqYiw&U3S(K&*1#64s-4X zx+A1W{={~1PImzQpt3s3Dp|BYRws<%vZ?tOXs8sQ=nnCQ@Z$K5k&!iM2NaR_XdHu6 zJg!X}10zk@PETi{NrIL0(_ z8b;%6iLdt9m*m%bR^nQsQWe)7sbNu-E)fvOc+I1Q)pW&+6T_^mWmd4Y4ok-ozyQ5u6Gf+j|7rr(MyE`BeZ|NH? zI{%$LC_0|VuAAudRVNJel{<_CY`AKjL|J@g}uRJV3XDNDbsgBy*&>V1*o#pqkjzwF6sLk=~ z!V4Y%w?Smxqqj<*#%kS7^R3sLXT)X~XSAIwRo`VcS`Pr7;9jd<#C_)rI%*Lo{(3TTBijN(2J9U3fepc?|89zTDD{n{k=+D)R-tL%Dg+-=w4PMP_~Uz|bC zsONFG`4a|hhK8{lhR48@0$gT;&A2LWni=L4&<;I|h1-UY8$NGKqxDk;h6I5zwfYx7 z&C}fSwT&{j(&Ty5666>_>j<@wEAI1+QRdvu z+JR@(S2HI|?T)9@*_8zSkJXirCwh;dNv8nuFDvf-*36kjRfC(RxQ9h3t7^Mx5cdk6 zb+q1NcFo_a9Y|X*olzW;>PK-gFI_c535>G8&Y48|8gz~2`lGTL7sH$>zE$d5szE3D z0t!6kMvZy!hB!4?`uUWn=2=zVFNf9-Cf>x~($Juq87i2gCMPwkH`K|r1)qF71^BM+ z){RyMXUF-o9>Nk*5($@tv1=W+48s95ex_vu1aw`iQ3TX8ePY$!pJvpzXiq%c6>rpz z6r}*z#hQ`MJCZ)eiwvkpp&@2qcQ{q%3P*K|tVhXr@M!ExmQf7Ug>ggCi|)CQp{o#B zm^aN%yY5jZ{F6wA)A&~2jQlB}nh-PKdh+c++tUkom*7np_rnu5U6MDK>A)xsiHe9c za)mJ1^aVVEbVwt>oxH@0l4P?j%+Pfh`!6sewC6wOy6 zGw{9p?$6=_&y$d=aGiT*Y=z~7CVa&`(P!&y6meL(!|0>f2p!i)ZEv%7?OguO`sGj- zytuwcvK)+*uLHe zNfFsBN)<*MI_y>X)N6guKX*;L#e7>hriJa95*AaQ6NjAqk0Qtw{##5*H$0=0-Y zL;_upCDhTED9EQ71J6Fi*jCv@z$s1`GX~KVv*+c_Cl1^MJ%jwEPe=9{o#d4ux%f)J~ zZg@|zV|LjrkJ6pdy1L;ARkH5EWN1q<`&U-O|AgyYgdzN;*+Xw53b6$ySyHAep^*x`)u3EJtH^AxbttbaT`BxmsR-v3=R zVOgwv+X0~K)Vgu*i}zuq`meK+`KxFaZ~&5jog&R&r!ylnQ0Uh=>i_k3ADq%D8vkwn z<$jq=XATi!=x@`{^DC!>g#Wzvzly|9nh=EkC!hTPT6UCHMKS{^|Ki{!1d>J*mX`_W zf^~xjYZ_(+cpQl<>{M0oyB;^=)w}^~HLXIq4owHD8SF6JQqkbXO?X zXd%KAu~%wn-NT-*ftcLUBBb?Wi%bmZul< zKTN3R6?>aD8y58NYpZyr-h9&b#5(2cnoRaPVS!@fgFC6X`Z|xgSWg1n>b~t1UzAzZ zaIhP_(BruHZk;2cI?t)+L2d9!afGd2RaNt%!>e9dVpF*ZjU`@f$}5*u-)vZ!miD=i zr)U~CR1a1h+spKNd5N_#X(^Q}(=nYhg3R_Z`Q62^wnWR zI(zl;3X3XPMOn1=kUZ$+)2P?(%QUgKrAqj$zypW3Gi?TJ>hkGinAu{IzXP4FY?cI& zuDIFuKpy)DmCkgeTCqrybb46v^{}hF5f*T)5|wmuEfHT3&cpExX|KkuodQU3yC-sp z3PW49WA!z&ebXtD{+Z#66_PxyszOx*9xi(Ap4Ca>Ug3`Bu@1Z$vbvm?wv5<(J{c8# z(TwgXK*3vg&=ua@AZ`G+RQTsr*tjC`iS^18TP~?%9ayTHQWmUe=SFoR0!%5Sy0i`C3?HeNY^G2xh9V5G%R$)YLV3T?X> zy%7yHAvI$13$LEHJpq>GW?XIMVUjrCniL(j?jFry3$9YMOxc;zrlxOtNJ^|J_)3W&77kLXQ@|J zkCfams{)mSx3wZ4mhi<3$Xv3Iw+Xm^NA!VEXj?R=b3gXIH@t<=Qy0m5n2o3x$oBF% zp~|3H!+@$$78XktqV!b3n~9Q-O1ocmQYU*Eh1O3al2C37)%f@mu$PplOd{lDl@Oaj z2oo>yPtJin^{ivsUa8)%eep^77)hC>Vs04N z+VBup@Oou!@wKeof-UQVgMfpDh1U9YvKSBxHUNlKtG_Y}!os%tXBvI*PxDp{WkU(Py_tXH5!+d0cn(v+|wE z@^#}b7h`5MeWWdWXxM4hWfbn$(^GdpF!M0$Y<1#NXD;6Ksn4PFm5XMA{mQWUZ@?sc zvEAm9Md3&I_%eM5%29-lN@mX*zd$!fu2m4e==LnPSwu@uJLQ=7f) z6XGKXdl$Y*m}6S-QBseGz*6&w!gocZ!!yKZY34V#Tjo#Db*0bV1H@u$p?NrVsc%59 z)E5mrQXninEbjQ&sn;npT$EZQGaT^G97(@gl!c#F1p z-=Jb94Q#Y}Bs(g}M zSB>)61D(E#%c^YN(j7SdT1dzcbdY^$Geq$Ue?gGyi)T^HkTulVQ$S}%#wlR*70;0* z#BY%5R-}kYj(*&`I_L4KgogbGV^JLOVvq9mIO}`%e8CV@eyKW>**EV0EIj#tQ+uM( zzrj|CXnw)rW<6ZUZjTNO%U@L*G}N(rxw$d2dFG9p5@&Y)C$5sb*DW#MM_qhQ0o-u} z3<33}<|&|OR7E-qo2u-zmm<_*8Z~n7F)_$Wpjp(jZ0p+R4VRJ@{`b;cJ(h#+Fm2q`5iFQBj-8DT7Gh@ z@|m3?<0Yb;IH&XPgAL5*-_v>*Ns`P+J;RA+IoE20P4}K#KGG{FstpbfNhGDSJ!@1X z6iFu$n15lDEJ)qM-Ed+1koF|Nk=-9{HnaHQNZb~Cu0_!$Q{(lDhClb@x73%{)t`$V z=NJ(YT_WN%yFLq|TAq^acXfx+fmxI`W85nA^Hxo}9z!pVP}gNWGWqzKK4ypnS+Sh4 zmc`IN$*B>EmYS&!mFqFn8wlX>Hq#m0b9%uMnUfIVLTy(1YDP%?nt)+k3-J~C=s83X zi)BZkgbMGBGKdNbyOnz>Bzi$@1DpdhG}s{=T%<`a4&xl7`ew&Mfog|`HQCsz0ARc77Eyzv`GWo6Pr zXvL6Ci9Ka)KVXgyq(Dg*8Vu)1xgX`ZkD>Eft!X z!UjhJaw&QC-3YHwHI$v*i(1BRGnubm#XaXpn_*iQ$r(A(9Ow`Sqtlu=VByBCBaJlZ zlj<k(LO{qM??phZ)Ho0UD zbb=sGi}%}d%*QB01t$_n1nQn9tuE)dNN~Bjadb4J&U%NpXp|27@n<-DT8bLyTG@hM zcXNdq5Fb*0x#>-r?$Qq6j)!TYp`o~|lJ&Kwp}8xuRu;KwBx6WT`tV`Bg=e>(RbA5N zCHq3LV#Q1o5SYM!MrHnJ^E?jR0-{4X%q zuZ^Rt2xi<>f=$xc^Di(Lq3Ke6+QC@X20^j)`T)1e{{`kMgB@-OpX@sw+Y?}}DgER1 ze}TDv-uu6YCM=7!Z#9V+wJ2`rfA#qRbNw~WRGKg~ecUBP@x55YzzXo5_7RPE zp|$5ohw-me`Hffatm9eyRE8uk)K;QHLoT{Fswarf3`$N4Xt0}_QU#G;2vQnCp+BgQ zodRy?hr-atgcJHZ!xi><{)USGu1pY8aGrmL?FwKC3(1*FFbor{IUP;#0H_i&Oqy=< z;M95Jexc~^gai2JXK3QY&Jmpg`r>9#I3_~kP%8l>{zp=s`*SP9e|S96f3cec1p5~z z`i)!$gebs2j)v#Y9VbBC|0w%Ehxc>#0Iffn^Iw*ne=|PO0OAa{e*2fwaPlKXMVYZMq9|aK-H;KnV-@C}ucbWSMzQ2D|y+3?^|Ly|t z!}s^^XwDzLzkgJWKYV{b@BQCn6#cC!{9B+K{0inEQVtc$O)=_cT%QS1Q zl$ebBXP?HsBAR$D-VX=P4Zsf0ADX@OQ^)$|;dJ|4c3n(oH@U7zZ9sEFhGW!wV`RR5 z?5eSnF6Y}MA$|5LQ1^D6a~JZ&*9TWECMoO*KRWd5O(bAZQ9)K|EX!sHdP3Sb*J^D? z2x8{aGl{*{27FJQNb`P^rKqAh=fiu_nem^&3LE3La6(`Yg=Q;oOsWNm2!UU8-(stJ zo(D-FMsB+;*=TZ_5l?CQP{(SD8Z-KoEW~%+??~DT;P(?8P!*hVVof|ytYSq<40gGA z-0d%V6*t~R+E?R#*Umjo06;r`gM8d7fC8P87l=Eb;SJG`^(k`qWs^Jhxo9p|%xU<- zXdXaCmvtH7__5$={s8N#@&sfovC2ye&x*&Qj`jvmBKhew+0qGnd7;RHm8TokVJe2RAXeMe zB!N_JU1PJ<@eKt9{qAh;+w>hZZ~5h7`Rm>ex?M65owVH_G=tt*Z`6+m6+{=@A3$z6 zZ0kaH4$|TvT^+rC_l~u&gW4G3mlcvMQ&;s3&~HrpY&%@0{UL_Bu9)JiPqCLDRLE%F z&9PTE7EO6}|JH^9XiC19#@x>UU6SM>4F}VA#!Qh*$ouy2tqO@A2>M#s1t`)Pl(KWl z5S858+V|J?uxhx~-7$5sAL^lFF6mL@-t{oU@Htqs9o zg%vv*_B&6L7vZs?(=vkAzUMh$E?;gF0`UynaYizhc-MOFP!Dv*k>t(bh5 zVBQrAiLYglczgW~@wQEdXH2Dx9xWK+dkT;}F6P-zMES1|CVHO&q~^9x0q+CWTzs)D z_#wPREWWo{@L{ovnaksXlDr}?o_^S)TO)h`J+5)TwA@XM{Rvw|oJ;gQp8AW@1KwtH zUak7!X0@83a*|TIXveV3{xq}UO%>y;sd{f#W6S1`mU0O@d|&&tMZo4Vnje|&kJ&aZ z?L!Cd53`#0;dEwssIbA%J*xg9Fw}Wa##T!(bu8A`htJ!qKqll>*&EVJ-4ELH(8L4t zE38Rguj8k3}BW=~L?(Nk_FRHkm+T%IwcZh|DuQd)YJ>W8Ja7fZ^`|F$D-C*J?+~^Kv5#yh+qY&a0J|r|^gPEYwl= zms^*GOrCeBa)|q4OYM->_uwQ+McE0@1*2W#*m^#$F(}yY<#a$VYdfaR*DYXNfw|T8 zZm2pbSa4!fEy&Fb&+SLt-^k`{Ni!8%1fk%Ln>mKjp0^4O(%OR?$>>;nVeT zx9VdgLl%wiVcOlZ$QxxHN~xSS!{cG^Yu zVGqo6bx+hf+{74|JDp|^JrnrztFgw}FT0Fms#PEOF7d37 z=<39emsd{$PwHZhz?JRb^9E^Gg2g1 z*iAB9th#IIyx~Tcree6-IXg9S(_pz&n=@)XaE?}b1?gEmO9PzadF96p{_u=zBn3Qs zRc!4}h)(Cm^RMX1@jyWh(YG;uuIH&YUKJEjdV$LDi5@d+KpGwn?;1buB$w29pP3df z7r_)9%Q8Rljr|P)+xsFGXX;wd#|3H&+v#Gx@}1Kd{A^~&3nSpP))SV5ysA41k)*LX z@%53i-IXI+Lp=f|by@W|ztRl{fp(*M-tfOc2MRgk7}rXEZ3JVv8LoHnK}&%UUx~sk7nwWs_~u+fvIO zQEO(fY?3IZ_2spuoz?H1B-XtlDl&eK=uq1}jpBSK5Yh}eoH114V=CVL=-@!imet{I z6wzguJhGThW=69JQ$*sE%ngApsBD+Rb1iJv@patgKGpLp`)I7y^zJB$DTXFiq_Q%m zm}64Tim+2ph^~xTG*PyrZ)e-$)eM^$2k9SQUbP%<$S2gt&?YRvyNwcw?PYW-nw;7N z?3ObRcS=k79v*9dvha&ZE4L>Fn&U)HQiQR}Im=2HLou>#(KC_kHy=4MjyZ)v_^4w` z`oUeI6WR~%UGNl$ZH?9Q!pR7Sm=4&SQPH0D#&hd@P{_s;>m)zUu6TuqXx4Qe8@%X# zN6iP55{Rs${W1oY7uekLZN&A*Blonk({x?O<3i#hA}n1TkOQV5EaRldw$==u$BDx% z+C3VX38h!#&O}DPlyXnbtu+>sNFPi;R=KCodq-O~ZRJ;d$I}J^sK66u%RQoE zD3=tK?}(0QK{@zl>a!Z@x#m!MPx4kjccZOi;vU}QmOXj93uIXf5NN2xNQKo)Im~89 zs%U3Bc^Pzoi%73?+wbuD@IH7+p0bh3;y@zsntT9bwtkYert~ek*xT1?q>%@bWcyyu zi2&Hk*H$EXFG$*l&$4IOn;yP7^zB=M0KU>197v#?;JCAl5imu8W8}%h6n&JgPxZk! zhUzYLc-=w=zU{V406R8I{l%PCUp*KOw!CvI*!2A6t?cc&Yp`HNnP=ZcNOJs%tVdg- zLkSsK@G~ootbM!*k}_>p5Pi`Eu)UX*K$-HBZRPSh>D8~g?v+`~a9;uVRF^pJfY%k} ziOL!GJbcJxsIGneFEZu-Due#Na-A&27S?4s3R|bhCHMf3kLLEIqNcd}Qt>XjxuN>H zCKXm>W!0%iONMn(3ckVNNb;#0rR$HZ$hhyk2sWXf3|_*;7klA9CqePi2sjV)S?vx) zR5h+ZE!~-$2g99Fw49W)@wTz5=DDFZ$13|>i!A?ciGi8wiwdn8(>BgFd)3h4fJA(J z^1u`TiR65 zLI!dIR~!NcRivaWy;AYH=aKIG+9`l#=>CA}I5=n3EHN?Bh4O2&vR2W!^clP3R%!j7DHnUn%a%IX+pG z{peVNNn7u6o7LKzFULq0H&nrAwJOIWWeo?C*7QFG%VCS~9d53P^b~Vn#V=9c2)T2H zO-g?9=oGM`ErmqTU(-m!i!-^$yzynl-F^JzfaC5X2UlRV?r7 z+fXcpct;4Fp07PQvTao!*WGN~G=t%i_3Nyxy~2S`qAJQ5(OsrK{!i$=sI1)OqmoL= z!*DC#?iqYksny}lTko7mO5-UHTPVkMy=>*NS<}5CnP0JWZAjK&^+n7>kA_?4fk0Qq zhdbcD9{c+q?N8M$j5sp0z?+OAR(u(`3hPdnGbWInR$ zOGfzcQ`bd$smfzLa=dXCk$PJ;l`M5%Vc%0PAO@;k+r3U=z3Y2xr4g5x*t)(JZbKSS zlMY9y1<0yZwPJiC3cdpE3vGq5s0hwL&sus{9ax4yX{WB%dsZJkxw>jyv6)Z)@>+@g z)Yo(uvMbD?iW{ucB7RbKaG0}g57^h^vBR01#zu~rDtFcM>`avzz?`*?t z=Pt#RQp}7!T6BR;Q=5T^aX|E(#z>mJ4fzVxF&|{*|DoK%T21DiomsT!4lTK?-*+?r}keArP@q)(= z`B)oBa}y7Jhl@`s@gjt;S3mFCIUiv$69{M19Fo*1~sFo^`x9~+cp79{~?59wHbZ==yg7!0XetQsk!bu zcbi^{eSXZXdJ^lR^7vwghiZPV&7^kWcjx^o8@s8o)rj&)tGw(?tzPnB5jg+#`L z)9Fp!tv`v~Zs@^sq*>q>t2Q$+$;(9MBVio;TidZ(bAB7~Eb z;qt{^crSVZMW|3R*yZkj$K%#X<+j%e>(o)!un@Lq8tb~bp;t6uja;3(9xGBT?0NjS zij|L5;7aQpV`b<&Ga{6S>#aa59Nbk(4m7>OhZ=Cw1uee#vBF{3#t+TN#0xe?Q-YEW)L!Wwj`MDwpfV@&eLE;JcM4HuVj);Ud|W{wBNV&nZ*?WGYhksIf&?@x$2oR<`@ z6QbJ*_$7jnTo9H{?v!8s`wLm_JUid?kFh{(5+Ecz`L}OBfA+tfBm^(A2^2p=@Kq(% zL^8~^;^`{W^ad*;`R|k~CYn8-m~<{1*YZ1@f1e-el1&1BUE;+VO!jKpBTi1$nmy|F z!^24CZ7Uc z|ENp!hYN+Ezv9rJ5B^DN0=NBSuHW?ePjULGKmf0Qs?6W4pZ`~j#L{lJi_uQ&)2O;a zE2R0v1k%bvGAF*eB;V1JJP}};VoOi(%7u#OJHQrUJw2q#F1;Rv^wQt$xNv7>mZr3dBqEyoR_A%J&y;D zClgVFh-;SPJ)k{8b%u?hJxcMk7-z%$+?n7L~=YFlh%2O z1SA*BZ_*AZh_nN!FAXLRF&%&ZaY&+QBlmFxbDJS{gPV*d7Z1uRZ(MrPqt$}{)JM|^|t$d@+W!n=UDtMqaS3r zP20GcV-hN}FT&k2mifN=DBz@!Ub>#8*?KsQNU0>0V%N6nAR-aR^QEV;+va)BDFD8f zxw&hZdZ_Nbi>jCk^&X*`LC|0m+l_RyBVAXySVnl1nuj_u56ttDguyDExoXtn0vVZi zjRn=7M!hT4$+w>59h8#Tn5C~!G~<)kdELqO=GvkiR+UQHf*0&TwI?5YZb9v(_AY-i+bg-mH07`FdI;GQ|$1|rK zoSd^Iq?jR;2>uOvihv-Y{~t=Ji+vZ2RIL}11zr)=S+J&Un^vNZiNS~? zReWCQ=(@b5j?u@;v~RV87W{Q48sm($WOpPZbE=r{r_YlaU89yE%22n*8n;=3B5_yl z!JQI8E(7qno#IWE>gcDA?8xa$D-)D?S=dv+%~701G1g5#b#duV|1C+pgd+>OE>o-Y zBt1PWdyG$7{mJJ`0@VE@lWJd=KV;U3OGR)q4XNm*a#at%Na~)l{w@iR5fOrT&n0+t zS^MQnI9_R*4Akz-@^V-^@n2RLJ;GNgAw>$}oL@H;SbmqWX5ZwGz2jaLlR-ooa!$nP zr5+~u!I_Wa*OA`=)#}X6-aB)l&sEu1V^;8d!@}k7KUW*8D0%dJRd^)(#-b!ATtUYv z;w-WDJErx30Mn-73p>iS;y@kDnUHd~4_0Fn6pn+7ECiOh)zXkX3ofO-NWWVFS zfC;=6ou(XeW#9MLqA3I5tK6v3nF(-ZfvO)yc)dyv#Nec~6zD;Pb-h zA;N8xd_4_fe702Eqa2l#0qi* zCwaqK6chU8B6FPfHwVT#_!_BdyLhKa?u}e-pNYEYjhaqt1-(+>zEF?3lgiPz7lMx8 z3YDw7%C|qeiL}_X6czlGYH=Yl%V-2$(EefJ`V)&TfIpYIa@`cc=lB_nF!O>b`mX~C zaSP&7>JCsr7dR}@@A|RK>eIX^6}4I4v``)@KP&&)m&J~?FqX4*{tVlfS;*+5%Ot&A zZ$AF?D%tK6@M~R@z9qH}{pA$U0iu;d(1NA%#(EtKz^rfWog_!ZbvlJT)D7AKwBCyC ze(TfX9L+YoF)*?`c$_jiNeIo*MTiHmIDW+opS)GkE=oJ!!41#t27BeNY-68LAWe?X zB&|MPRqW#Zj*|V#WpL5#p+R&a*?C)bEYU0%>ugVSxN!X-kN)#E%c)QO9TFkNXBOT+ zx|z=Qd5WFl-g}}~4Bqg7%s2jR4oDPC9OfGit*cwT;yVg0M11QBxIL{(fddW&ocA`n zGbn$h*fEu0$$Q*{Xg)Ax?pZHyr(H1qBoWSL2Y4Q&Dc_95_l!zn;5MUD9#j^7WKXIs zBSRwD?p>`a_97m)>5V0)ysshtJZ02)-i~=o?#`#YtPN;8ihY-PF z+Hl@RAAmM%isrK737O)-aE$~tvSObu>Tqf5rh=tBJflW*bf8LPVMgQhx4$0SC%Qe0 z4El6cYF~R~cMW=WYK@x$>wzIL=vPrqWlg({k#A`zZz2ue*t+m;-9h@6*B5RnkykRa z#^sAgo8eUxIbRQ+A`}Y{#mOMm6}g0nc$dsWVfO|pIF2f7G5RH}JhyI^hsN9M)E7@$C@Gb`-T$b9ShISvy#r(?7WXvGP|x$F(y}Ahlefg3>=h|RU)C8)eCq~ zO0g#)YJoTSZ?=Yi&K6_T#}}YZS_o#7yqhZ9u!sM-8QO*qP8|H$MC+=?k?hg|hFN7d zpG%0EF`i^z^1v#h``+v-yCuI%)=NY~UlpmaW>2hG7FU%dhUJ4M=pk9QHX25duH08) zd||H_1k?hbP+XV}BJFY$g5S$~kP#}*ft(?wF;2n zlk(WY3Cr`~r7acxC3Gw6V55Ys7GOi9L<-Nc6iP@=iz2Yei^VKLSvnDd@mNFHr5w%? zUhHy(;1s}w$E4&i|Hrq!#`Uw+zY_T;C4OPIpOpAtLJ9WK-&>%KP8im}N`3lCwLDv_ zBcAbC|D5-z31ZYwY+(FsW6<`M?7__&6P+Z0`$b<4`NrCbCnJrl0DrhJYvG5c(hby!+4AE<3glAixqKVSrBd;-jUy_M4ldk%u{ zmiTcM;=5C?4*4mf7kiERJd#Uuy=^5GrUo^Kb_0<@xz!RI$`iINvd!$=Qo9is2G5mm z7jN5DW2)CriV2%}iX(GBl|lIk9!Bp0s^j+X4xLZ+NnP%pLlbRWgrErm0CiF?^Opdw>}wIGsdPfKvpS5bBAhTkt&u#n5lU9T1XfYg+aRKxa zRqk>Mh-?(~MzosQGFJ~Z4$tb-_U29X$XT_gakOz~GfKTNeWj%Jp8d|5mcTPlR4<(* z)jyVM(ek^R>HY9#LXua+x{JhgLv`4Ns4bHq2>IN}GHMGl+B1ee*$s)%C+ zBDpDp$EB9DqD;X?Z4(NU*`MH;DSamI@J$|y>(gAY)&R1`s>vdoWiLx-ZO@#K4@*6K zNHkuNZE=b-yfV2J*WjNT?lq#g0!@jJCqPv#H%!C3k9PL()k8ZVinBw6ck`i)%I9<{ z2P%5_?iW!ecOnkhH=PRB71XFkxJUqG#D2z0&lLops-hlPC!s1}T+`pW-A;wh+ zFAT|`vL$SCeqd*@%lI)2*$KJj0iKiqD<)p!V*Q0@G=Dj<-rDbd30%HGu-1fspmMdq zDnCNJ#tjyNHTLWWA}82Kramu>!)YEWVhw*eF;hM%pFMX(SGk3wAV7W>qc_s%V12=G z*tA{>;dOZKMEyP#rvM;5UKh{w>lrhl^zK9KbNmZQUxI2O;F>yF$BwN$ySxw-mXUxX z+5p^be;LcqLH#m8KWFME`~8%upNit2SLx=pdbLNv8^e)P$zEBj3YZSrv8lK3aQRd( zUd!pN1od}H*FF0F1R$oD1JM0Nq5rDc?+`Mbr~W|;|EM7Iv+4gi3Xs|SO==6N;x9!< z6H798@K*`;!(&9ecl5@Yx$dn{6y+YI=X^Jm0afu-FN`VjnNY@)+4BJ%yf!VV=e|U) z7z@po#G}+FcR^Rf!|TdD@zn}`>=?V|G`fww!+OQSizP|S>$L&tlGBNP#_cj+3huBI zkuBI$7vltmQ@mygZz+hTF^3#A^t31E5Qxol!(*S%a0tUofw zoG>ACzVj{5`#isr?Z@vhK-HcR6>r|fOQjb0?*ulwZLSC*n7O}f_RKYeUc3F2UVp`M z`rbLosN^=}`F<+ehR(b5*yed6cvuP>Ly=58-q?tnz9!$-~to(OK zlJ@K{1)ncfR~*o{j2GsfuSmY4yJz=aevA)6yOgO$Nw?x|0DtQ$rF2d ziTqcZzTKje#tM_yvZsRrJM0uNLk$k3uNW zvjX3ckS$T+O)J2$!r$6f0*x;9uUigr=2i{@B4YDJ0l|HSwr1U(N>M*J*_b&zFt4}X zg7vqC6q9}Tl!v^7R$9yOwj&ET^~JfR_@_BoH_9B@zA2l_+myv1pVFaCws%8)hKthNptbf`9FU|xs3mS z*r}w>|7`KykRzYS9jMxI#5BG{KL{3-IL?v<7OCS$cJQ-;B9XlMyX;S)2*#4p_=}JA zV;>GbigXw~tti%Z?YY1leW>R7H=pu(`v|0%>@Lgl`+YEhTG8!0-t%0sA{B;B)?9O#F!JXI1=`!7|$Gpxn_mD@?O#nQMyDi$~ z7af1b-3p;I-!8uwRXyZiA@+hmqrU!=&gc#IP0VKcsQ%{cZ>%1O!Xb^;u3#RViNQ@J zk9-{Nof|(GXu)0tbY(qCwexKc-`fi9RgKMUWNKuD>Uw5h5XM?@%z0j2PH?4(0E3J8 zL-Ad|J9b1gWo6&z^TvdVDf7z|tp}IG7#=?sYaMBuyhJh&;man6bjar6;{_J8dAz@@ z_z>iT>vb`S% zhbU?qsUxNe6`pVM-f+R$h@z2Yp`y-fn%*kCjv*g&wnV>J${4wt=pgw z`Q3WAS)2!Mf31-`pT-{BApVY$0$qcZN`XsN^&9n)B&75BZWo{Acgf?D%}HZ=_2zM` zWcnO7y?Ec&RtsHOy<=f9yz*jgqI!T_Nd>8^+gfg|A9d4G8RQ~Sx?1*+1%I_zY0(*R z9&F9c9P6&}9lN+OBV3t0AhNBzvAZ(F_WTze^G~82;Uuy-`xw}GV8}w7&ZV^U*vnhP z+Du8Pr>D4HA#D%*99tZggrF|jK8HaEWs3#*E0Ws~%r@o8fzo$C>rm=nz3czgKNs@> zq)(+Fw!i2;NCS+F-1TLtYc7&Re|ei@%ogsFW#}| z76Z3%(^BF+H1^I*6Ph3eDAJlbl8MFSU~halEN?LNnv#tT2xMiOd`uF{BwB{#W!a!B zCyileSZ->^&&IojN%9{Je0(oUgp7)D$GNd88ryRN+KR_F;Tf$Ldf$!ve+-Y473PaQ zvU&Y|v0q%n~$68laEz@%#>M@&g^67fnz5LydG$hlol0`V;I;m~Goa}G|#SHv| z9%4kbuyFlC?vSIcgz*4Jyw*ABy$I0)?Nis5HeoNXIL2Kt3tv@5`FjyTtP7^)ZdlPi zsXpZiYjCN=?F`eG6V{(kU|2P76}Uw@CA7EZ`a5`ju>N}Yc6b#Mzir~5-p=O3 zt2|5yq+#dNwitP5P|OI|G6DttnG87wtAb~LPFVJhZxV%^N?tw1E8-;b zjQOHgr!99#Nad!zmj^1!=|XyX-3DOGS_pK>w1Nk0U3S`wA=7DbYUC#BhXc?B2V#3{{vnw#zox-iVCo@Pq*dxTUvJoF2~kb` zu;!5ZP{iy#eYY_eT^H9aBk5<`AWLXs0Qe5{0o_h2{*d7}WMHT?P%G_NI68Q?ToI_8a&^#x ze}mCACj$_=nJrZF35Q>F+KQa0X`|mNsQYvPrWHu(Pcu~fqI-jmz>)HR;_RU~DcBNV zgc-NmQ_S2zROBwn$bU+wl+fBXN%SYY=1eaW;!QHz6e*FtSptv%kkd@;RoCEz9g zR;erNP@bQWP{HfOI+4o^W}c>}5`()GkpD%isgRuDhTe8jj)KwbALTM)j+vdW7H&cc1(^}d zU~G#fg<}&&L9EjsBYJpS-ozQD?93W|p+_@GU1K0(&gMb^FZNd=PPxB{rI!=i$R@w& zo>4Cc5*^dkpO@OW>}=RYI&`EGABFBJ41NBl-xrtiwiKa63XfDO-0HD!u9Gf$*8#^z zSxqi}?>RYf`Eu2?pdlAu;t#AU*NRsXBsV8C$H<$hY+gS5#j3mpxCkE8sob6$2xZN3 zl&v}LpwN7!>Gm~wiApXb$+)NQj}7tMnHBq=T}yvkXa9UXwYX_M{OAP3=~Hpz7FYkS zcUTtn_q>tk)?fOF|0fD*Gkz+2$JwOYPk%dkQw-@~GhaZKZP}OFs1I0V$@>${Id%{TmYjO)54+rzvzyA2f_t! zR5O8uK4ily(pcXD;IHNH$+iQJhv)jxx2vIcvOe|6rrbrfhE{3jcg6e|i~w{BZYUai2;&b%&2DjEp-2no`Y8w#Bb7bxp9SK8~0*DDU zKu{Ipf}+%c{_yBNOTd@{GE0Bm+h6VWSAYBe_*me~g_rfV`j&d-Epxb~ewkNE|GMaO zt%?3#ilj4xNMIE1?@L~ZZ10||_4?s+CBmUN%KEQW@Bc{qh(8Z@{$3dC(vn47tEO0y zW?Hpm>m%1B`b#FgYht)|xn?-8zo9o)f2rMJ&b#FIw?bO426(DbyhBCBH(=r|wz{ej zGx#X{TaxnTRDu!r1%2V>x?<>)qVt#7&Od&gl3e|ZjvZvzrkA%i)EkdI27j*@E>s>t zRlG2Q93TQU41mNrOJ)x*-=fTpOh@9XbRquqayfeN+2+|J+|KJKcVtnF6I6%8IO;h^ z_nl$8Gw7Cdr=tAv&oB{A>!k~AJUbg_&b)}#EySHad&KBA9kJ2A-JXpKA|E|aJn(Ir zR7ra}Vjh>=#jGcxBPr*n|JBfCF7d|eBGxZc=IAi0W&y>Z1@9D|H+Gn8JRtw5vMMAH zgywPMqyL;z4|ETr_9?8P%A2K zlX|UR2`0R-!`TU)vS*9uv2)#RdNn;>h?#}zq+9{)>0+`jlQ6*u z`zc#6k?k!p(AK_bcYkCjv@PWYzwajA*gdH=Sk@Y*OlBgm(=fQv7Eu ze+5}DHDh}0tX#t#e@$AY+k#`%zUxc5c3n?>QT>z8_>((iuB4l;{G4JH*B{=h>RW?B zt25OP&a7^mSa0lmJwAE_Dl+hOLyu#;Rr1Lj?t~-rncIz3Lt{M!dn!qR)x6%!#*k`s zX0m-&*_e!sL%W8-3=l{NVPX&t)9Xw!G0Nu}1ZqEjwqTX5j$!*b&eS@@w6*e7?fORg4unrj+ z3IoiZ0!-h5$HBIGP~occN#L(Z&HBxGjPUO>Z`+ScoJmRkh=$`nP3+raOp@19h9}ag zTvYY#iBfNXL$wo3ewR;Xp8gr?eHAu;;2c`MwVciEuh6%L>4T{3a7spLZF#Cq-h4ws zyc)}5^~P~)U52;llV8+jhiRA+ZDGchWXHI~Kpru%ifYnao|#>Tt5k8%Q^twzDfEPV zNn8|#r6MM3cU5P~@A|pTx-7Bw{304Se^Fd&tSRavfaW)oj7$a`gEo$nM3;@C#-+^Tt)mz zNZZ5tE{$1tme<#7OMY?`*jJQt9%T?irf6m{R}J1 zHwU*W+`71R3SM#^@0&WfvDf`ftM7?EOPPVe%pqR~(R5WXm!jB~TH%J}z>r$^Th&CD z_EH043HaQ|ESLB$)uhmqxncKRLuNdSM29m-2A>0Tt-0_nt0HyM`5wiiw!&j!pNv0I zd$kN+eR+)T-n94p(p{RE;9I7I@F5Mf;5es0*!q5Rl^K}3JZf(_Y3ktbGIygQY@W%; zVLY!G>hZ~lNh0cH$=e_(E-O}MqYhFZ6`+DIa{CG89_s>+^{pJ7Q+|JgW=yOXKZ!U_ zT5HUwsJHFfu^%|Sb!lm-JNBxs0UvlnG{)LJD`HY-{CM8OPhzLKB=%5co=%<{~u`1#Dd&f>O*9(hLIUx-6?-)ntX;@*2~tefv89P;2QJD%Nb z;aY&?j>~b|D-_97h7{Thr106P)8$k`wcxcqoism&y~~)ncP`?U;VlFfH%@}^8R3XR<{zVEVw zo*YaG%~(v;16WY&Tf3vy7mMfr@0u`Ws=C~RPL{wuys@s2Libo9l_V@~%C z-xYoJqllJo-tqhp)UoXevo+$F~Ig=TZ{aFJu5BEBWGgL4L`I^-Q~Ls-ISj+ zn)h{~X;&NVop_YtSL5!R(r;~T9k4mJ28GP7Wj!~lLzFHJnBWYA4;M8x)hk=q5Psyl zW+T-EX1HF1b0IEd>riWQa`Q$F>A;}S*4PCtyxumOP!Y4P%j{P1KrXyn?wU!tFv5ru zKea9ZhS*)MAE7c&>w}r1pc00a!`{ZQ@!W~FqL-}x!v5GN#u`wOlNzaV$On@JMaUj3 z{Sfx6`;%&4_!A40BulDZVTaV}`&3!hvC<|;mGC&@>o0V^q5>ff=7(8`qot6a=CyrJ zaTY2)$|+^}KCpuEjHfwAHD7v`Hnh>75_=47KDF?M^(q>4c^%b83#tJ_17`%P8O6R5 z)D<@Z);9zE)NEsh&w2fHvrx;6$C4MZ+fRF5|sZITX-q2i&on6f9^LcK( zoP1XjIq%b*yxJQ@)xeGrc1~4NVu|*w6bnm|?#MdPp2NjSJ%70RL@3#{T58bDo6|-v zO7zhNcs9~uQ~#QMT?tlT(HF#9Z5qKjZYSz|r(eFx^?|c#Wyysuxx%mYeoXS0>5Vy` zU0{;jTRcqCxWKY+gFHnR(K?!apEinaa@EA`=!^?8bYFU)dPe%SfBTUu(H;^`{pL}~ zrKrI27Q#y$&q%JZvr{CXj2lM)c^Dw$cK1{l4(rY_^yNkGS{@VEG0ll$D2HAL1RVaK zWXgWami?!nKkEK&M)$_|E$${A-wK=vD&MZpWY_8UwwzH#o4UmBbY>Q$UP<93ruaYH z@UITw+j7U)jvVz@sJKJGmt~C40WW zwxeT9g(}cNfQWhBq2dM(%x>&YAXOedc?x7aFwQR;WT#LJZeptEAf=GH>k$5g(H^K< zs_&|-zU0PQ8JCs6#o;);E6SH3BU|q|wv#ZtqKu1D%PODlWC}sZ6n)MI%dt8z^sm)K z`PL{8Tk`nCnZ2~G9Ox*rOHDf3;mw#!mK+%dq*^MyUw$uBP#UA;X5am2 zbhAigLm*mKR--ocC{Jz=;>wpc0HA^F+2@bgX3o^tgjo0c7-{HuiXM`=4Qua8yi*uT)8Z27PCj_45zYpk#=#+{<&&hJ5?m#x6_@~wQ|*w zazs*3LDVa`3#kY4t^L<4VvIx1H<%=_bw4PsfruL(zNH!kk?}1}>uG$J@tQ)|9p09tgv&*YyUrGGN^Bf}0hXzT|7Q#^8)cBRj6@ zrd+qy-Fsg8J-Ngq$tC<$Ml-0qNuI5vV;HH38LYPSQHPozm~XTuym}m1dnU~Cl|bYM znc;~_!sq9E5m(-o_IB@7Rc+4O0QmP=b$nMH07e3G;Ty2TFYu8Wzo;t3IA5&b2cZ+B z4co;UuMQ89SdCIoaJN$f_i?D?wMQ4njD?LnlIN1M(O60%IbdXZxFFxvgA!;Ztxl#wl&>z_edj}j;`yFL9sF;Wy(;e$!{AwU3Uk) znf)K~35CwRBUJV(EW~{6n$t;%yQjZ#*AvrIf0t;{ENdSzf_Km5kMJR!ST^Ud8SQva z34rI+stml?d@F64cUz#1``CT9QlXbVKQ9Xx=Iz!~Q?NE;+tsV3Nf>8ES;gUBbn1xm zO_)Thc$GeY1dOs1C28g6yZMe}oqCWSwDoZ`=SI79GEEadWxFbTl7B;m=@g&HOca^0 z>hI`G0*DE1H16-~>poXCPJ^PAmZ8_rre5SSOnmg@rq;5dXo|4Z{hFj7OogCI{%|yr zpDN|CoK7_py)|MLq}uy@b+$fu&^j;4@O#>9_o*U_mt|J3=sJ8J{Xlkz@m7*(6*(qE zQ*MH*#Xe(i*n^td+8X8pOOsOx9ml$S<^&L>(wlJtADEv*%d=@oKsQWQ{zd0r`s4?( zKM8h#+qY;07!x!_<2CD=!*{2jyRT-qq_5%10Z+UMu)!NwKHolD9p1(&?pxnB?EDoZ zR5^=0&ZN5Xnq7`X$*=RbgIOjp-HD8q0V{c3ouYV32%b zL3t)J6kR?VZv^b$wB*PtiRH=@J!6nzGd%M<{tG&+tzf%}x1*;pq2DCN%bpdVC6>sn zv`rm=A!Fck%CgL|ue{ZLs>6KFF$COe2H(HF(9=nG^-*hFsHw8sB=A7>sQSr6`Qcrh zj%0dl);=u&v5Jz9wP!_``)dip3)=SQ>h^?qcQ7J}u8W(RjjOdWuhG zEsjZ(*o((B+p+hRR||KE-fV+7=S?Q`KFp|EJ1I~UE zbz)geG}EL#qgZ+IlQz%oHE==VoQ-Y-Zeu8n$gFDN^r##0v*j1v$HVw=rBbs=c2P<* z^g8WZbJJ2~uhT#q1v07ZspTLIuFjl&m_dMNYlxn^5B2wyAv2}zX!?(iwzM)$%9;(Iw z0!$P?4pB{>Fz}j=9)+#+^WRP~lB%ldmK@JZ{OBaXAlf97m|%kyRjofLFPMg9eiwltu?;LmKb2QejvzM8#>2A8 z@@1OThrx1S$uX-KOz(Tpm?m$A=kpGmypSUmr=l1Cs5qzQQgKKw4Lbl>X@L0gbFMu= zUL9Y42uy*J0rzO9hnz^(ZaZ2cb+jMp6Oz%ogYIX74fCEnlt~oQz4oc=KoE%#h3pRqgx?PuPF3m*q^U4Z9 z^){d~P_cA6dL};$s0ST;7)syY4!$!e-JAT|6=Huo`0LXDH(TJy-w{ULKU`_jq%AMJ zOL}`U+w0xS6Ow-W_b}YDCbB)J6wnugRloe$)90s~4K}q<`XUjs%lZ24+Bl+PGG}vu z7BoX0qq-brUd;jo$=<93W~h3A{DO4fPOlj1yI%{AawZ#C`yhV3rhB{bn=o?j-wxie zDAvF?X(3oqpzy;3AWt(4#xwvppf-UGD5*uEXeOluP0-ItP!Fy>{%;4*E?`GzdiU9W zvr%QZ3xq#V@h9Y;hfD}{K-KOXpy8l`L2LnBNBA!~m0xs18oxWHYd&y}2Q8=r&Z(TH z0Bf)+sInt_OO&)uJ6-@V5%Cj$f0(}?{Pk4-_e=ZhUH#Pq{~A|+%@+UZ%q}l`W02pn zacDbm3^H+J={WSn$7+a+`Pp=H)5N@lwF!+h^$+^r=|0QYz5DxQ`}YHSq4Ti6&G-Kp zcBgaxy7Yf@3#dh%&H0zAa;F;rz_8ITx^dW?O`{M0-%6d~WImk3v?=>iz;KZoez6W( zg+brN7!qTR>&4$N41T?Fu5%?UIoZhF<&M27RgCg!#wol_1*uj0tsv-)Ow88VoZz!! z_^UWu$g zi!;1t{T=ht3p4R7O@($TrV}Pz#?~|oD3a~ct&uU~V4I$^%RvoWrerhoZOwO4dj~}H zswgyf04BV7I`^-+EK1qV=1%vS)OP~x+fY#$F+qxJJ|s)PIIz*6mA}q6GtVeU_!Xb=Xl~rq z{A>54&U~?Xa~|wIUUZe&yj;}Bczyysxf2M|w)Gh>rY6-Nr2fqBv%3#eCPffxL-NW& z21L0@1yYImrcqf3QC8hhs`_4JrgNo=F8~MUXfaTr7JaRi4WFAje7Bt;FJUm^vrb=@ zJpGG~%hHwr4}e#rw1{_kOVA!|{`^TfqjSMB9&-&#`8O(ac0Yi< zy`f+ti5c79z&2t6tg;UD5Sux)J}ijLm;Yw52E@Fzj)6yC6O-yk_7Q^m{Qu%PhE6}P##SP6Y_bI zlkd^yy}DsO;k3!M;S;4vXK&hBO099Jx;n*jaE!fjKbaM0beKuSm1XX;2KGpez%+5a zbA$UQqsXY>?5fmkvO^nCAQeNM9CzDEJy#@&Wh1LWhG9og@+os1t$jC2EY5ZYi{7Yw z7`!!kgltz_O1(wN%$uC>$0K8d99UzttQC`ztn_4Ko-sR)#&_Sk{Jeu_1(22jBOjTl zj}=g_F5I)|$>u~s%h}-UZP+s(F2ilNdRcAq-@UJkk9zxYXci%R^GVgFu<&!C+Jk;w z+mxZB5BLUZ4-0$>m2J&dC}+lK96hpa9`IZ17HXph57Vu3B4y%6Rt~H$47AmIzi*Kb zh30z3D@$%VmstdF~_5O3C)Jffv)fZpicv^&fr(H!ntQ(|?y8{mySmCr|zMyrlV(kd&b>8Gu}Kft7H)OaLT?Ms1{*Jbt(ssMS+ z=b`^bMDKT%6)!OX5-oQIsx@;T#j}LJ1zU%j_czOer1+TA9++wT5Zy4p&>@F>La~u8#c36RWy~7NArF@RCqplx1Y*u-53GDou`BLj4M{rlST)0<&%8oKf$0;cV4_^k>iD8082RLq z&()?Z$1m@_=gjt$vX=`2)t{{NyCb3lt9R9rzFT#s-hQBeR*H`Q=O(o~nvXz|v61BU zHlgy^tub*nint-g=OIzv;HIr7ID7yjoF?TRy;At`o89FTjOe*o%ZqXjqB|i6`tT&? zUvx)pC?idr@VhNqs+mxwz=NR2lM`d(hGE7ouu@vDP*Z=-hr{to#;3gT>hD*!u)pXI zt8&oA9=^7|<8h9~i@2^7Bb;tJZZd%B5Bl4=e{5ZgEUMsPF4cfs{luq-`Lo*n-e7m1 zt6Pg}r3h1zIxM;pDYn!0MmK_5ks9ePrpNX6$N0?q!+b4?rEC@?H^F{^UV><=zc!%T z&$9f9Yh!I%o|uTH1PiLBtZ|Djs^^td`(iQzgLd`C%(G3XrML+PhdyIG6wY_1f6s$m^}Uljq4{DWrp@+mx?kpNUGt3hvd} zAB>b+nk%CrXIr`UHX<@lYzpy=FTL&UHm}OA0!3+nOP2#sE!jXSBSAHM1-jk=u8;eC1)Oq8kv?ckEoXQLdmqK3qMR%oL zWv<=s`GLdoKIXZ1w&)~6CEA$euzUyZcQ4(PLk_)ziS?D6h070*=j|-2GfwumNznDo z(ee98j@??a_>Q*Y*p}F`la_`SCisFCNvXUP`XLkj_-MV_1Kmx z=u=fTBnzkr{&SZ*3jntNS>-)t9uHczm5Hx{g%trQ*qRC=N_f!=0{AA^yxkGDRq3ZM zQbMbq;OU`N_bZ*?UYAd zTLu4i48L~!MOXcl@-$1Sf|5M#^#)+2*7;z|S>v7~@Of(0B^%LjY|rHucSLU$C$nV4 zE4ddJ&A-h0pv0)R2XSup!9uE0TjG@KRhg!yBT}_h-vLb2cK!^r;nxn2gICAXxlV}7 z77|`2Wd7jtbX5eIa0gOL_6urNQUQkoLhj97`+uJN{`k7f?05Xg?)jbw$dsn%{>=Sh z4dxP2#e*BGm>z#eIU^5e$ryiEhD#++I#ONIBBf%z#Z)MpQ@C&tr-sy2Ipv!1Uh99B;6HNjb#jOK?GD}H9_dD*Ix6FY7TVr7^bcLns?N>MWl~{C zNjUb_Ps^~$_WphK;=xEKRyp+X&VxKXJi|$YuGf{16Q3z*n2<4_Xp!EDUIwG zsWJRvKt9JmZ&c*Ts^s${8w1B%ydFFjzpe-VJpk9xsqx<|;r|JM;(zruqCS6eQ~&U7 zDL+H`Od6+JlfV=^sP+@3uKIfC!NgMPdNtnQQl~s-(l+OI2D9i7bJ)xIM&I92ZAybW z0-wOcBT$cf8Q)=YmbDoBw#35&G85`B^%?qorz2r2Yx0wVrxG7s{K?YyrNju|x3w1= z;M@|=1P~H%6#waLW?|gwk(|X;JPhAZuJ~;u$8vY2b1d^y`>o4^BL;fPIgmyi^VlW| zo>i(f0@Rs|*EA%Wvp?!Te?u`KvWKm22TqvYA{z@s9z}fU%yV6^T`fHYxl4Z z(0g)5yHRIH310~z_CXo-6WgNudjv9Wp=wl2S^P6jcmYqVET;H%;O(LThv3o-JEoS{ zr|&k->K3jF;z35=~mnVTJ^J}hKtCuhDQ<2Uutr6$R*#N0pn!Fod zy4`c8U+|HdLAZCM5*&s%OqYiAyBD~=l&!G*-u^s}SX%A7c>&p--m{jNyI{Q=hc*Ya zi^EnFRuGyD)&_|loT( zZ)Eu1eWOqHUEoQY-#qGdhO}&w!o>j?6`e|WVFo1K5xvEndLth)zYDT!$+zst1J2J@KC7F!g`j*zT{0qK9I zZj8BaFjGZsWfdmuT*BqrM^ZIL*W8&V)GLx#x9<3@`>*d3=E~}Pga*)EuofzM+yl!9 zv|emgS66g4Kc%^)m7zJ|vuhrp7hSWUEtUUtvsc0pE@D_ncaS#!DX=`0rqM?`1+Q$i z<07NOy~j*BjiDOD)+1rgxFngViPL+IakL}B93nh3nvmhbylp?mcO3~jB)>MAI9VF! zHEl0uwIHM#Q7$o9t#0sLZ>ZbCkyAmcGVSLmM}mIAlfGNL7%^@9sX>slDF*J)S_h&^ zssrkVj!e-&W~9Qob7Ku!aG*uFJl{K7v;L)>x;n!&TKFzo*2MdxUF17-XB)VG>_l-f z$UZY2N2DFBA)qoLRHIT#eG84HZYdLCI3Vp`b@%!Q8_3`>!f_=S8Ei6-Dyl~kj5mu?~_lOs zARC2dC-#c*F8Sh~8pZqlvcKq{;EDCZxpgDVA;O-TyfJ3H1Z+`S;=|J8Tj1rTrOL(N zdQF{Hp)3qM*>y!y)<-F+T750E}3)U8>CG_(sTGN!ft+0FQx z(&&S+{Q3{6y`4T;^HziGD&C0DgmYixUx_?^kicRuK)p=PiY9l`WZ+_=Og6Q+?+)LU zS@~P}`xlprczTuYws`S2e*Wg@8aibV{;X3& zVAI&#s;H$U(~Crmxl}{dw2rELa4UT)t0Kh;P1rg^iKYSZE1-F4K$YuS-{(Z=0p@H~ zfaGM3*3!pKd+ELOy++iO3)gNY!JFplS+MI0m?GW%yn;P~aHydKk=dtf8%dsUhj#04 z<(@w4H4~a^X%A5z5?x%@!X6_1d!8x+JwnBX@TX}ASS;@3mbe(LPn?;`KOJN(n^N(? zR<*rOv#RT|UJhP^Kh@PQr0&dSiA%-@TW7Q&-AF%(RH=wt&;tx}X#b_2`Gvs{-6L&fgPM%5@L_i5Y*g#ro{=rqoEAKyCV#y zp0F_cri$W?EkRCbcVoG!H-z&3`a1p#WHihzsHPUdgQo~Y(-VB_?b zA8_$G_%FRsi8X7&aG8Bb^7eAz=`KPeAbpi&N;2*rG56C@wLeU!I?UiACI!fB#na{C zNIvqhM4~`71Q&tjN}nfK4VDrobB@)hOckb|Ni_Uu+Z~nvL459LKhCX}!40WIF_7FB zOg?G19)g<2RKD7Pg`vu5>j=P7)v49c+4iD5MrTYEu z+)z_mYrJ1VGq;vil($@a{W^chY9CTS+#a88V4wax1B;C$C+iQEz^KAy6zSfZl^e6L zs>{(uWvaYmRSKQ=f>u9=(B}%4^Ynurx@xHB8clVH(^I8u`?(IYMmqLWqgf{#S(At3XXO={J(&KewrmYhiWQM;z_t z+#Y7nN5&$s(H6&$yQes~*#cxpR9j4isn-Uv!tu<6MXYkI$qiGAAxuHuv0t&R?pl9N z8$+>=AZPBL-kVd!3e*d-nQsxIlu{D9m3DpzA@QE#)m}-d!}r?)R)!l`v=p|8@gumJ zXaIqpznS#H;sSEn)rP6XG3f3e+YQJq{_~A5DP&Ou!t64>sO=j3Bh73ekWX}$2^(_kHmr{>RWN$$xsg&0y`u3Ym8^?o^JLL)6*(D2 z)#pbR{cm>n`6sttz4zeyTx0HZ@8=9apBC|eToBQ^BmdT|jx9RRQincbUgt69 z{2bA6SjkMCE?&gWm5c=ik z2}7Chksa8Jx9F`^=%Hpgg=U-b z?&~gUm@v1u>qi7RWnO~9?o#5(7yG6w$-`A>Ya7qg=1?{Kq&nC?Lh~J`QrA+_>2=%i z4#nqK?!&iOF#=qtZ3pD`q4sc=pOXW_clVjEDf7&%Pp{nzuH&Z?od1L7Qj@GPQv3wo zVq0${Os?I)BgcI>@hO#|DQfaUcl(u>=O4RE z{cv|^KJTmgdz5h5f16r+2JAMi0DGo#8&|ZNhAN**>P7rygULmgyjow>gnC)O%x$eF zbKp}fTw(5gdOZJ9Bj?Wlp5GlA`I9d`!aJ{#pAjc?TZ$GhhKedO652_vHT@x6tMcx+<+XmnSx;c8h`k^5sY-HmIOUoaO# zYTLoW`bUp^yL3%0=mVU-}S02vhwqnYIX;rtEjUtjoFlLGVEU!CUn!{lELi%<1KWcJW%TP}I9 z*?=+_YPMRE@}z1}UW|{!ieImZu zZ+fr;qP!1_qRaTlnac04Ds=hl&nWNzYLdTt$^ZDk`48dda%&a2>L2dkNoplA`utsq zp_K+&KuVIGn^*6)Ao-%~S4Dkt`U@JOEN39XMz5uYniTYJTz`C?hi?Kyx0B`to*)#I z@A(CtjLq1sx%yigwaN$AW}@0QVwzJE?Cp|#r;^`7S`RlM9XmO^D=T*AF{jbXlXJcz ze5EK}1dzme0h@};CZA_jkasW+1k3Ah%dI_TW_|hYvU02Av_*oRa8-u&m9Fz5FElJ( zEoWB}q7f&EK}XF=y%9dWB39UFpJ`1U(;kV8`A1KbdlwauJi7ET_P1pdByAvS0u#)3aZ8fJn@G+_eN=$=DjN@o^VO`bVL2vRnt3+?P+B*jCyPYZ4Ep2 zz#yxzuZ+#|!AP15u2MA)VN5#cs8ftmkS7kUdOs%ly;xT$L(J62~+&XB!nAYs%-GMhK2R4Pd{8llxwJ%^#LW+i!{)5cX=O~b`0acQ!gtU zcy*n%#7whi(0s}Ie%$rAmr40f(!r|O2Jlbn3+MO+)^;B%G_J#IA`_5Q8AWgTG^H2tdKDyned z_N1F1-n}A!k$?YIfwNDZEk0u`;%%Hk)I4v3!l+--X!Q<cui?xk=~^kQvaF zX;#~w>q)HF;v4WjkgbotmwB^ULJ6sJ^Nee-KjD;|zhDnM2;GDdwXuQsTWZLZeJ4seLun-k)R&X+xBiZ&Z&M%neHA zftSm-5AC-!)p~bY`yY`NBJ-E_8CPFhmku{NGcWc54#BL_8Som4F(lA8$@HSJ;Zu+= z$Iqu-Y@2rubG5{OxTb4Tt$W(b*-BmZL-VZpQvBcFf+mFeX9>IHB`KM!3ufLs#&}DB z(@qgUbP+hP0+h|`WC0Q{A)-{i3}K@HtvbBk=5D6GYAzgY5o4m^(w3x=VC!49Lj9p% z{EjL|$)BE((d%FL?&To3sDl`tDxkX_b>u z<(_|U{&nL^Yf5Q(87{L?@$v2__pe?n8JPTi&@#?g?B{!ApVpE7EfaQgp|)>Dj-{5O zW~~e9_viB^@O1fQLb5J-@EPGjexKVXKA$36Sv#*PP6uW>x9;qENm?T_3V*l)Qs^N@ zDCv(Q<{q~C(CIK0-#tf}6Sz5j&+8%aXDO#i%S5K8Al2sd#*XL8?A&&$AyCBCpuENO z(1=eTK2_Cb>7p(LM$Et+!NHzoHas>3-M)3=As`O#@>nftN*i<wIqb>VO74D zg+DM&HmwPx2?%~Ye-!xCSE@5D7k4kUT+sg*3VoMl9GfRP%}0M@?$#x3$pSE zA6_?%H~z9lbf&ddd}zP~FUC!8cFA`q^W3TRpW@tj?Z~%QhV9?$m@u_Vb>Cg>sV;jT zsEz`PB2A6+IL6(L^+2(i97G^VF#=(R6|Wf1G9H!85)O=%bAR-)K9yy!Z)~FB(TNEa z>}-_&!@17H4V9~bAbfeV+HPz73BUlrb^B~}U!crR$o1IN1^5OK=O0OBkYOoQ+kLN-ck359yhVy3L=ABo@ zB5tpe0j7+?J_dIJK#>3%a}@t6sLp5DbP%WoD>k?VK$N&dulHM88+mtBMnNoRUVeS( z5OzpxpE-N-siCkNH^jVVu_t$=%l4{WzLi(Irr0{B5#qmCIvm~zhs>-4;yQksH7O{R z$9b#H!X|P3#t1jQ&e#Df{uj)xd&%>dOGg`zf7|X2boE5nd>Gf4w4B#V)a|QsKO!ic z_{>r3%`=1Rvh#!x0_(Oo`%;$ppt5}!Ky@BQTrIdMbYMkZsmre%-_yEdt2Sx8csueK zNyw+>M}FX}V!wH^W>3kbPKMNvhEb~wZN}|}o#y9n%z9su^!Th?k^}}4+by_$!hrrA z=+Y2w8ch{nykvbg=go(lsIzqc=0gvx+W!J{=QjI`ZmAT2FoX8F?}J{THe`B!(LIa> z|DwC4s&u4{HUtt=Mm;02_}DtPWN{2O*p}yq4o%eQxwn{B8a}$Dv5RJSFX?tn_hCb) z>DQ2A#rJUVJ+uv}J+bgxVe+I^i^rMvICc&l*lzP1$a<_GAo-0=10{hu$sCFg++&b#d6 z!rjYc5~JU%xI5q?uXR0UmyL0dDGg8!)nG2A_}SGBDzisqNs;yPzAUAim%`pjH86ZQ zoiJwAa_;<*J9YX3GN)d1w$Mh?j};NKN8>x{%wyk8EbV9ZyoLZpMOKaeM2*LxdW^58 zxSLjuR?_n1dz>p=k!J}A`TA&6N-{B;oy%=M#@=P1ioifoG=o%yUP-l*pU&FRkGb5A zbsD@cNpJLkQN0&`X2gbE@VOQt?(?t&ZqdOn^xA^||7!2Mqng^*eS=a21ZmQnf{Ii{ ziUNX#B1VXy^riw*L#Wb1qDU_yumJ@Dl_oV(BOwH(3P|q>1Oke55=4k3#JBbx@0@eb z-gobP?;Ed-ao_nPW2}*t%rVzo-<9S_pLLHm&Vhn&Z85+Mk1pON`HaB--2mCt&mTT?^a=_`>MQBtbr zveVo;{QCPjItCP{Hc1fqeq%w%=6gJxs7i?+-QX?uA}Dw)WW4%THGS>GlbgLFzceWl za)TQdj4xbQDyqyUoe+!YXXL8ih<0@${sdKsS2auKHACv9_e&U}4k6bU+IJW-Ub*b* zdTUj~uyRF@(KEN}P?tH;>0wg6H;G zg+isnZ^58t%j3g@Uas~NSA3ob=Us~$W-+VHzrUY@J>@^k$B?EH-6+McxGSM0%Y&h%pHKI+GOPRds0A5E@!T5;bY(l{&d#T<*p{UKRGEoOPy)$JP&4{ILLZy3E*pAXA1 zaq5}C%+{Ewc9zp_FB7@a`;)Qm z%ncxvR8GeT{q%l?uACR9C6aE3e6@m;9C}?d9;m`i^z=|9=hW=9tx+1M^&f7$H<01( zv0>V}C^MJx1R>!FSEgiEFqFxC(U_2Em^Ar*KD2z63SZHjUzy>mwVPq*@BL&7omWf} z%FRi37MwxNBJfQrR<9w&J01nb<;H|w2F2*kN8$R>V8_ztEaIUbOezD$A)KzN(X46&PTlLYHa}PhWf<%WDx=#@`w@g}>zYlNW z5=~2s<6tJ**NHyE^_ak9^h8m%+hq1^(sm0s706X~WJjgjx{qsxuVfv$*wmruD|_Ei ziT~lshs!;04`%b#hhJ-EWFo zySUoptRE`5=-;!^Yw}>E)0BE=m8;a{Xkkmg zvs>Q0EG#Xl@KHyC(&a=JYK3aHV)EON!bG^~khk96yL?vXE~xr${jcI!XMP5PL2n+z-pmkr>fA)Er{L^Na6aKTMLNB zxfb5#GfNL*+cYF)hwr6cYBUH=Q~2oSr%+I5t_bf0G418C&mr9)_%2IodI|i>DBR<2 zvHQ5KK{ho@U&E|VY}#G*2Sa~2UZnP<(u-n`U0NvH z%lUT$oQP4|i!E*IM>=um8&j?fW8F%15WLlOBR*9yW#|pp^-TSE7q>i@5Wpdtp8isB zUD>wDxW&4|22Maeh^-Ha-N~bdlLGiWMPsVaC*ON!2|i$mjaDgpD?yA=$DYg-*4~Qw z@XmVcXbSlSl!{(JMq-8Tl4woqF2o}|k@$xZ96fYzrRcV5 znxP@Whr$*Oze?_1KzCH$`w6nA<`jAmm9i(yN0fEgN#zbcVqX%5&vR7}wrS`{{U9jL z*@$29VdNpWCJEKn=eFZVF=|EfuHb|yVpK=M2P=elQ_aY{*8WaxsM5Tn+;i`jmKnka z1*ym6l0^@0J^g;h^SaV9vdvgKtoRpL#LQY%D%7UFxkch0YWe<`rONQ)ftMRd_s!S` z489>9L!7i%uY^x<{YYgUjWeg5Ryv~VecDw3>g$r?6&n9#BgG5G`Pf1Q=?RGu4USU8 zngq>1f)zQetiL?<+}U5*dGwM$^hCoJed+!_0kNTP**}z2^LQc6epNKMQ;&u2kVQgN zzNDVLWjEqhH=;Iy8O>TZw=?E0^}EZg8-`tEwYr2lejvuA<-DW$H_~Q;&Mk`RNLOme zRjca3pv^>E!*UOgfV;=-yIj2r`@AQ1d7v}iC^_cP5908g$TSu$uiTD{%42ktT)CdM zrgF0Pc+zv670%GFCxl#;xV+P4C zsx!8nFs(xmFsX`hZ$jI|#RG|_sI)m4heo3K9u{BOU+=NAuR8)jY{#nu{Jb6_hTl$g zDE!butDMe!NAC$l z_p^h(qXen0|p;d0=52|4un%zbDx%0EROGlA@vD$Ji5vft~vwO@=7UABcp)t{oM zI^aVZPP-01$#wG!Jz4NY&Vx^mlsx(IT#ABN?}1m~1I#;mbyjUnKn4Ook!%z%Q%BxO z^gEGPbn=zg#iEYf9-d+%*u(6;O}r~7ugQjkM!AnYeU+R~I*BF?#I-Q18<8jCl`37v z=r)za?yAsp1MaZntf!R`eXzqCC`}?yv>4|v-O`V80Y=dn+_^>ytV4z8GAY^0Y-C#{ zAxtH4VZ?GUUOikaoHOtRp3UC$qv00|y>nOVP1g<kdv~%)A7Wt&>~O4EKdLUco~d!!qqS5 z;dRazuioKx=!(>H9{|CxPE-U&2w7pm`wZEA>c(-yFvXXn^C^-pnE?yC>=dd z20Jd%cFc{!^(0=RZ!tYAusr?5XW!{Up;zWak5tjC4Hv8*i}4l8FshXh#G@BETo(k z-jGD=+MzmaeLlz=#~U)y0#BI~BaA^|i!TBEEv;=tc;-^J z8U0??4PLg)VY6*iyLKE(1)tb-}IP-stu*e znyHEx#`$tzmKEo@w0b}KurGpfHtJ$d{xjO`ziXF+02-9&Mxg%o#{vpTy6^^&Q%KMw z`Gg=DA*-`gQ$S|vUe$kS8xV%z*Vvz+uTKCbvZ%#^pP;RDWKX(g|O!((^%JY6F1^oZt)3uYk;^~}oU1R;p z#jE2A)-wSwe?jvng-f52P zG3rkPg&flWOrB1eXd%Apa$3Z7(lYR=20y}G^69o|F#Qj^1F2}#W1_8J{ z&H%tpzjZkRtpu0>LSF*N-?V1G#m_*<&~xZ=f4?}8Pgfc^@)I;55e`UX^cnpGB>{i^ zZA(W3K0dj!ti27s@mnX@mp0k+DD3MM7_@=tb=9R2`*~2$y%KJgx1K{6H5VOVq)fpn*6{iF7 zSOBz&x{K^q+-Q2{;k{YS!yDd1$7vFuY`E0ig*UU$)*Jg6cQVPmD#iZMi;GFf`P-QO zV{i|-|LYI`WZwT8rT;krU;cMDjTM0A4)jN#n12+^eIIp1b`|~vZD?*)IX>i2NF%#- zq;SBmq9+!~#0m_?-Ku7J?;$$UfdUckl`p;N!Yq~XR25B5w+xz%10=_F)bFBJb~QzP zGQ!m>vHsIjrb%h`*HT++I&Z-*gz`e333=#g-~}7db_nf=eL#>Ph14=Lx-3@#;|b|9 zA~)~EyOt6o@-}U;G}inB?OQtELSe^l3W$SJn*`5Wa2Oviq&OwiwH%~+nNNIgLREW+ z266=i`F*L&v-ec(t7mfF24x?tOtzIX7e1gR>6uqw>A?^i_5oB27zFT$;=D%{40h7` zRgAibTkyRDZzPP*7vit42{sAIRj~o^B?%pgg=Bv&DF}8Hs;>P^ba9wqI|K_vW8?iA z*BN7n5g|8wth<`rT~C3MR+eW4uuA3dD`Rjjj|v0Lgt{h|rlyg|^X{*qg%TYP(qoe{ zm^1i}NweCIW{7^G&Ew=yfG!(Xt>SqgqboUy~z>&2R1)HZ5v z&I427vl;?1!j6W7&pp8`3Q@A+_v&@HD0TorqH;Z_iRAo_lHq$9d!WWQE2!8}KP5L! zSh?T{M5*B|w~&YYd+|~xr68yAEPC!f>-Oy4_-r=1^v|Hde}-dbQv1h=kd?YrJZcl~ zHVi!Ws)k?RiFerg%>MH;3lE-LVy-QqS5-C2Gs$23D4m!2cXi{ktX4N`x`^+_2aBKx z`^;0IBkN*$?gUF#tDS*3y`?L_o_y-x%36R_?`5FyMG$bs9%=nrM>27doV8O--t9ys zI-Vk@M6Xr(Y)1&0L^4$PK2}OJ6uM+v=d8VY8v3^U%6roai^=o$(TAtoTk5}0hXpcJ z0f$b5pZxj=AF@@AA+6QO82-lczE1Oa+Zyf0fzJRVh|{9C7`)W6d zrf}ATu>tG7HT=8I4DHv>AKBxn-=wquT0rzJUmP*A*AQP$H-d3d#}Ev;+Uazc@d+4($Hw(zx*cCbiAK46-Cs8GO${5J50VyM zqD}xc)ii>Gloh2J_1L+I3NBdL^3Wv8Xq6iwm~Vyh&vdxlk>45WYXpmo?R2j3%&`ue z`R3OG)zoC@kDBA7ulgI1Uy$A2!7T(`AoPF53Ajk_bAf}&v?azAg>Ckvnl}Ik#~-U9BpVqf5HS z>Gq>=y@h7Z_j!DSN?7oMgn562*~`%&wvn|;#n-Ne*&f5}lY*&3yn-6CnX+$0RO^ka zDpA>1NdMWdQz`WH9~W_q-0cm(yZ5F(W<)6gj@W@fpd77$=GHE?eavf?%3p%qa`0;a zO441zVx!e=*OIZ76#iQjW+DK3jxwG9cF)e!SK;D7PGEI|(y**YRIiAV*fHJ>Xh9wI z7UlkC=ib)@xXQc)w+a_<*r3{~vQ6_^2#3n@mtSmd=YKZuA>&)cy)=}u5G3>PT0LQJ zYI)W$FISxnNZ*ZD!s|`KmNfslFH*l9$JN|@U@7*@=SK9~>ff6mUI18f-|1)1ULI)^ zVQI`eZ@U!DyrQu@tGjLCgyqCcISqKPx^13>ot9bj>poA2d{4?VQI)eeW$K)(0)PP0A6snv&(R(o=fvOnemD%qmo!6kh!(O+YJ+6 z^_5ONx?sq6Ag9KL1Kr!?zN)JTw= zB)*`H>X@tX=w-_$4|WcrZp(jSCz#tmG2X@?N*(rlolLIiZkYtt<9vEI!@_di{^}i$ zABo0khs5i+ks=OuJj|ac6`J@bP$9eu94H`c$i{XlYKHbD4nWdKTT6%U z-s(NxarkWHrRgm#xo(h8xAXz8fr*{R6Lgh|l|8l;9WiGh9l&EiBB1y@0BSJk8=I9g z1g5VazFw?T694F^h|273xbz{1`H;VvNpr?e5KHi`k}Z)~^rm6h0cn}GJvO=g6ZG31 z={K7Iw(|%+t4j;^Az5sGHVz@~u7O~}R@aUx+g+#F?`G##7IkIC3J#R$r>U|F)Ud~b z))jq^IM25{!5vTU%sWM%ZJTAWqxNsMX5O4xg(Axvo-wBzy2gx8>`R)dtqV_IeU=*B z$@%38A^S5F40dE#68E$6CPw^^|48#&@%f8|EkIIp9|Z)_z;3#y*B*!eW|8PP0@Rx6 z4^~RMjjBu?^T<2P$Ext944wV;bJA(WFt<_q!TEfuDV&`&z!#CV=0p&M0u&;!bDy^0 zlbI$tZ#A7CoO|>lP~eBd=a$UwJs=OC&dBCO1^oo6H*vGB9#L_w<@&rtnSpH;HU1)VT>H>up=^vVyRca{aAuW^*++G7 z0>UxOQiGySBq=YQ9EdmDojN`>tajEDShQf1I!Qk)@&uuHRL5 z+f};mIgqjG!Z4YN*xVbP_~)I;=r{A@E0CY5gmdRJ)u|--Bi*_593JMLGl-cg9^LeL z=nFmeNYy1P&_}iLra<+bhi|J3;o)OCCgfA^89IYl-iQh>UFFEWifsjtsn-qR(KDFE zC4K#yhN_OvB1<6iPi4^J7g<|%`Q=kcr?QiKr)D&_>}s9=VhdQ#=hRhR(N(3yCc?$$ zow(abErL`JsIv0ix1-29#S(O*&0*EH3kgCx9mi|1*DgD19^f)qm`&Vk$fAh00dwH7 zj<&}7d& z;E+LYy2P6$%;7vao4Q<+?_lj_Et!VkCx+-p^hp9!!P)JQo?0L2t0V|s$mt#BNTd?h zdCnbssvzWxy}QsLP*&@j)2+!%n7wi*$*zZ7{!}J3%U{)}f4FtGfq{0)_M90*^TUBz zVS8##`caB#h7CDqSwcgi8;XqD;kupb5q1;&*`O{QBT!Omm`Nho^A4-^ashC z7P&YgyKN_sYwUotwnW>T_*3SFzhZXy?fw5Gyg!aH=%|CEMvzgK*dc5KMTurqxRiQr zlFw5}`51c5|LF9EIr%qYAN**@*ZP)X6!jG~xzfLIGJY5=5j^KuTOW!t@y}s+pZUh8Y zJLOvm--ErWF%eE(KS3f`vH9jO1*qW>LHGso8bg%5q;pvZv+z{?^m^EZyHIh&EpFpR zD0ZB!eV?C%XdyX?UnZxu8H~c&h1v+cx&BJr!e2(x_AcBBXFmx#DpZ#Epd)27@}bL8 z#_qbn=4OE2GR}*RQYacR@U={+AwU}KC^fYaJ~Jtg;Nc}yCr8ReV&!+3FkOq>8Pgyd zN|wdsJ=}|0E{RBG_IKyI909UnY3+@!H_Qr2d~;TA&uD=WTkfgHQfG7HVa7lt zw7xsrUhjl=A_>-p7CSEQ<>f5IjvRY8mTxwsuQ{~D(*-@2wz%jpSwEfg8k8#YoFiIV zcb0wR9-!I9CG??11ZzafWrI8ldLA^u=zWz3&64qZx;rMc-i&o1+UedaZwjlz(`|}F@{P!u&q=v%4r3L84w8h0 z`?!%6(deGbFrL$8AX}qjxA`u8{(igxr@o5drxH+}S`ClpX@O)!DoKIH70_v`=4Spj zz64W~rr>e-6u-3f36BCxv3pN9apewjJA!$AncJPa|8KZ8Ghn!P|z0r_U-;{H~=g(Vsu183t#* zmogtRunT5j)An^&m7`AFd7U6{C}3s)`mcOU{?6a3f-$fLmz z4G^?&QVt|{C%H!$@zhNJ;@D9<<{J+6l5*}+yUTO zs2}D}kz*4JvDfOjM2WC!2`5vq3r!&Ye!g;p?OplMV0M3|TY~M(r7P;QEOd8@W-R>d zf*vo`xz*~eJC5~$68Em{yAdgqvHNuj;r@e0&Z=q&Q5~YOX2PnEcDHnUUe{#kdYa}3 zX3dBg?ri+1`aW~g-mTXRQj5UUts{EI9Zf1zSoWV{Z)0F<-Q@ed?PyVr_%|zBEXh^r zp4s`&c9$CBR&E=MyYx7luk*2SKK}Mr1eLcz2bU~6RqrP29)ze}ogeGlDJRiX*gu_( zcranL)y*<`^Tj#BGgq6W)n{WC@!qeF?n<_h6!iE!^Q3TqV4FsK*u)OGo#dQ$>+sXZ z(w)}wpFSUc)Wr6JgHXAuZtMR`W?J(^I@*!8PZKe37VbPt)Cm4&wM_6qf71=wukr5B zrD@sNo?~-Y-{jV}K70^uQ75u5FBONA8D{Vy#`mO>;SNX`;PM>ulH|_6Y!Ytf$CV^B zL{w^RQ(NECA2#g26?(H%1C-toA!ZYkr%JCW7aY&7p!TbgXKPCP$1;#1dA-7tFMV~<_8A_vh6)O6D5#O=lAbdp;V z{9+|Mfck;ruLQdhOTsLNnI^Kue|dLpo7M>JYn#57#nPbTKEd0yc(CrRE1SI6a&`wL zKUzsAh-6~dHnBB*^=>Jlxv1Cno$+DQGb>^MQ*efCK&0>G52d!UEg7;nGqVX?)_VDt z8ti$~_h2NIKQmt&o|QWwzK@yITdQ0+X)Xe`<}td;$c5QYtjk0c7|<@kbyMawDuDsX zYO}6O`~k%pDnH3UTPq3MJs6X5$s7rKmk}vj&E8N`{ZzSU?-7q-LfM(JGoZE8qw8f6 zImj?H3G=P`G|n<_lfFq^qvz;pR>76Lf(ob#4j6Dw{zHZsH6AJIS=~Cm8vfeBI!ZGb zCi?1Ky{3|)zkP30e|+NgVTE((L1eABqbP8$x=G=7ibCB5I1R&TDiBK(=8~P$is$_6S7(k%pb0Anw?Qbf%z` zg_3n~Af*U5E=g;jW7<#m=nEH@JT{EABC(Bao0tw*4_B?{fy38z1M}Fkn0<_{Sj1fZ zEQ;4jx!yr`imCwA5TV*v-lm1FuPqS1&YTY|_6O^vodF3wP;7t5YCXr);d(w`cn3LS zM$16$BTg7>_2vK~9A7)eHHG|;AGB~>`hz~?`mP;)gj|y#FHuZMrf^iGGBu-NJEoE9yq%I}Ws)lYzUM!fa&%Ze)gNyJt8he?iiLOn&m0Fnnz@0gE zZPCrT`$}2N6|rr}1iZihi(*Wp9!{qrVt6KWMJ~Kdp$5E>rM~Yt@bL%4k(xmoz>6(N zePE>`OYNw+aP^?zc_r^j(kwV`(uCR9-X-OD=!s`?F(xP)T9zxOQ4b&^;n$I(K+MT( z@z{f*Z@`q!E{B?!-Va*QZ398YDleRILKw5qroXmnqx@KYpH->iiBJe^-zw@drNZL|f5!_7?N&wY8}hM)D%Qr!>wHf2rkvC%!cqE~Gc1;w7A z$O9S47VgId0|iuP6Ux|Px!z8@qkifYwHQpAqT)xUqz?`1%%le{5o{!6{GQ6%cS^v5 zc#D8K7{WO)ud=p}G)Y_f;kT0Kn0r|04Bjoi*!Ujd^D9*;RaK$G?0Y>4#DbkCFdwhk zZf)q)j6$ z!%`!wIaE!#*^Vxc#m^(zZK!D430ToPo@IQLR$3cpv~f}5$+M?K`NxCcmh)@^?~UA%B~T;54z6iV%)Z0UsW)cpk6k)0SlrYZDdloqu)cy0>m z;dHc(lGTnRM}~O7&v_k*Q9n9LxGb7*?iy@los9LjSG^Fs&s!*Wu>8jj(-j(PLML%k zB?k$=jRgM$WiGxT?rNqv1-@vplRQE@-(8`%R_=o7&)mLVnFInQ7mgy1_9gG8AjtM)vg z9w`gcSOccnKy4TMBKvp($zpl?rbWd zW=U3fhUICTkE=>OV8(>$J_5RZy~SYzu1%RI&hC^^kVzW|Vd7Ls%Z*`#@T!)!Cq^gH z52{-bLdFXE^%oX&*BtotlAd?Zr<3BKQBRR-o2@$)j{vzla*ir}k8rv=agV~K9PaYqBUUV(WpXEMuQutO1O768_iI;aaQvI|sLUp|c{rntu1h=lf<(K3{ zlF*rThy-Z&?*1obNPjCqI#H4M?-^8WxBu**?7w>N&&hhYzS6@Wj0L3pm!&@zoB(*) z|4@wLkM5oPhXcI0p~G7O@l!0-t#n;%x;A9(rJ4D5!#VS=xOv%uPhdV!FQ2LL$ppCr zdeqB`$fXnWD)iF;Tt?4`E}f~j%|{Vp2>gnDk^)Ya^+?PBWzfs4xE69DfUwz@B1c;P z1Py}Qov7vj{0adSr2$I}Ul$|N@i^dJr~eA1*+#aqI|E`@D!fRN@voSzJ-L?$89ZRA zJ@$}9V3L^_0dg8cfcJ7DWuF-f0P3Sp769eaz<3<_SAYIBKL4_x|A+HyaEPww_F5!w zdb&@Ipr0j#_Mf@Y(}>5pRP@ye-V-{{^6XLOh2cMeocs|K< + http://www.oberhumer.com/opensource/lzo/ + Version : 2.10 + Date : 01 Mar 2017 + + I've created miniLZO for projects where it is inconvenient to + include (or require) the full LZO source code just because you + want to add a little bit of data compression to your application. + + miniLZO implements the LZO1X-1 compressor and both the standard and + safe LZO1X decompressor. Apart from fast compression it also useful + for situations where you want to use pre-compressed data files (which + must have been compressed with LZO1X-999). + + miniLZO consists of one C source file and three header files: + minilzo.c + minilzo.h, lzoconf.h, lzodefs.h + + To use miniLZO just copy these files into your source directory, add + minilzo.c to your Makefile and #include minilzo.h from your program. + Note: you also must distribute this file ('README.LZO') with your project. + + minilzo.o compiles to about 6 KiB (using gcc or Visual C on an i386), and + the sources are about 30 KiB when packed with zip - so there's no more + excuse that your application doesn't support data compression :-) + + For more information, documentation, example programs and other support + files (like Makefiles and build scripts) please download the full LZO + package from + http://www.oberhumer.com/opensource/lzo/ + + Have fun, + Markus + + + P.S. minilzo.c is generated automatically from the LZO sources and + therefore functionality is completely identical + + + Appendix A: building miniLZO + ---------------------------- + miniLZO is written such a way that it should compile and run + out-of-the-box on most machines. + + If you are running on a very unusual architecture and lzo_init() fails then + you should first recompile with '-DLZO_DEBUG' to see what causes the failure. + The most probable case is something like 'sizeof(void *) != sizeof(size_t)'. + After identifying the problem you can compile by adding some defines + like '-DSIZEOF_VOID_P=8' to your Makefile. + + The best solution is (of course) using Autoconf - if your project uses + Autoconf anyway just add '-DMINILZO_HAVE_CONFIG_H' to your compiler + flags when compiling minilzo.c. See the LZO distribution for an example + how to set up configure.ac. + + + Appendix B: list of public functions available in miniLZO + --------------------------------------------------------- + Library initialization + lzo_init() + + Compression + lzo1x_1_compress() + + Decompression + lzo1x_decompress() + lzo1x_decompress_safe() + + Checksum functions + lzo_adler32() + + Version functions + lzo_version() + lzo_version_string() + lzo_version_date() + + Portable (but slow) string functions + lzo_memcmp() + lzo_memcpy() + lzo_memmove() + lzo_memset() + + + Appendix C: suggested macros for 'configure.ac' when using Autoconf + ------------------------------------------------------------------- + Checks for typedefs and structures + AC_CHECK_TYPE(ptrdiff_t,long) + AC_TYPE_SIZE_T + AC_CHECK_SIZEOF(short) + AC_CHECK_SIZEOF(int) + AC_CHECK_SIZEOF(long) + AC_CHECK_SIZEOF(long long) + AC_CHECK_SIZEOF(__int64) + AC_CHECK_SIZEOF(void *) + AC_CHECK_SIZEOF(size_t) + AC_CHECK_SIZEOF(ptrdiff_t) + + Checks for compiler characteristics + AC_C_CONST + + Checks for library functions + AC_CHECK_FUNCS(memcmp memcpy memmove memset) + + + Appendix D: Copyright + --------------------- + LZO and miniLZO are Copyright (C) 1996-2017 Markus Franz Xaver Oberhumer + All Rights Reserved. + + LZO and miniLZO are distributed under the terms of the GNU General + Public License (GPL). See the file COPYING. + + Special licenses for commercial and other applications which + are not willing to accept the GNU General Public License + are available by contacting the author. + + diff --git a/minilzo/lzoconf.h b/minilzo/lzoconf.h new file mode 100644 index 0000000..a06b08e --- /dev/null +++ b/minilzo/lzoconf.h @@ -0,0 +1,453 @@ +/* lzoconf.h -- configuration of the LZO data compression library + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996-2017 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Markus F.X.J. Oberhumer + + http://www.oberhumer.com/opensource/lzo/ + */ + + +#ifndef __LZOCONF_H_INCLUDED +#define __LZOCONF_H_INCLUDED 1 + +#define LZO_VERSION 0x20a0 /* 2.10 */ +#define LZO_VERSION_STRING "2.10" +#define LZO_VERSION_DATE "Mar 01 2017" + +/* internal Autoconf configuration file - only used when building LZO */ +#if defined(LZO_HAVE_CONFIG_H) +# include +#endif +#include +#include +#define LZO_CFG_NO_CONFIG_CHECK 1 + +/*********************************************************************** +// LZO requires a conforming +************************************************************************/ + +#if !defined(CHAR_BIT) || (CHAR_BIT != 8) +# error "invalid CHAR_BIT" +#endif +#if !defined(UCHAR_MAX) || !defined(USHRT_MAX) || !defined(UINT_MAX) || !defined(ULONG_MAX) +# error "check your compiler installation" +#endif +#if (USHRT_MAX < 1) || (UINT_MAX < 1) || (ULONG_MAX < 1) +# error "your limits.h macros are broken" +#endif + +/* get OS and architecture defines */ +#ifndef __LZODEFS_H_INCLUDED +#include +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +/*********************************************************************** +// some core defines +************************************************************************/ + +/* memory checkers */ +#if !defined(__LZO_CHECKER) +# if defined(__BOUNDS_CHECKING_ON) +# define __LZO_CHECKER 1 +# elif defined(__CHECKER__) +# define __LZO_CHECKER 1 +# elif defined(__INSURE__) +# define __LZO_CHECKER 1 +# elif defined(__PURIFY__) +# define __LZO_CHECKER 1 +# endif +#endif + + +/*********************************************************************** +// integral and pointer types +************************************************************************/ + +/* lzo_uint must match size_t */ +#if !defined(LZO_UINT_MAX) +# if (LZO_ABI_LLP64) +# if (LZO_OS_WIN64) + typedef unsigned __int64 lzo_uint; + typedef __int64 lzo_int; +# define LZO_TYPEOF_LZO_INT LZO_TYPEOF___INT64 +# else + typedef lzo_ullong_t lzo_uint; + typedef lzo_llong_t lzo_int; +# define LZO_TYPEOF_LZO_INT LZO_TYPEOF_LONG_LONG +# endif +# define LZO_SIZEOF_LZO_INT 8 +# define LZO_UINT_MAX 0xffffffffffffffffull +# define LZO_INT_MAX 9223372036854775807LL +# define LZO_INT_MIN (-1LL - LZO_INT_MAX) +# elif (LZO_ABI_IP32L64) /* MIPS R5900 */ + typedef unsigned int lzo_uint; + typedef int lzo_int; +# define LZO_SIZEOF_LZO_INT LZO_SIZEOF_INT +# define LZO_TYPEOF_LZO_INT LZO_TYPEOF_INT +# define LZO_UINT_MAX UINT_MAX +# define LZO_INT_MAX INT_MAX +# define LZO_INT_MIN INT_MIN +# elif (ULONG_MAX >= LZO_0xffffffffL) + typedef unsigned long lzo_uint; + typedef long lzo_int; +# define LZO_SIZEOF_LZO_INT LZO_SIZEOF_LONG +# define LZO_TYPEOF_LZO_INT LZO_TYPEOF_LONG +# define LZO_UINT_MAX ULONG_MAX +# define LZO_INT_MAX LONG_MAX +# define LZO_INT_MIN LONG_MIN +# else +# error "lzo_uint" +# endif +#endif + +/* The larger type of lzo_uint and lzo_uint32_t. */ +#if (LZO_SIZEOF_LZO_INT >= 4) +# define lzo_xint lzo_uint +#else +# define lzo_xint lzo_uint32_t +#endif + +typedef int lzo_bool; + +/* sanity checks */ +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int) == LZO_SIZEOF_LZO_INT) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint) == LZO_SIZEOF_LZO_INT) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_xint) >= sizeof(lzo_uint)) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_xint) >= sizeof(lzo_uint32_t)) + +#ifndef __LZO_MMODEL +#define __LZO_MMODEL /*empty*/ +#endif + +/* no typedef here because of const-pointer issues */ +#define lzo_bytep unsigned char __LZO_MMODEL * +#define lzo_charp char __LZO_MMODEL * +#define lzo_voidp void __LZO_MMODEL * +#define lzo_shortp short __LZO_MMODEL * +#define lzo_ushortp unsigned short __LZO_MMODEL * +#define lzo_intp lzo_int __LZO_MMODEL * +#define lzo_uintp lzo_uint __LZO_MMODEL * +#define lzo_xintp lzo_xint __LZO_MMODEL * +#define lzo_voidpp lzo_voidp __LZO_MMODEL * +#define lzo_bytepp lzo_bytep __LZO_MMODEL * + +#define lzo_int8_tp lzo_int8_t __LZO_MMODEL * +#define lzo_uint8_tp lzo_uint8_t __LZO_MMODEL * +#define lzo_int16_tp lzo_int16_t __LZO_MMODEL * +#define lzo_uint16_tp lzo_uint16_t __LZO_MMODEL * +#define lzo_int32_tp lzo_int32_t __LZO_MMODEL * +#define lzo_uint32_tp lzo_uint32_t __LZO_MMODEL * +#if defined(lzo_int64_t) +#define lzo_int64_tp lzo_int64_t __LZO_MMODEL * +#define lzo_uint64_tp lzo_uint64_t __LZO_MMODEL * +#endif + +/* Older LZO versions used to support ancient systems and memory models + * such as 16-bit MSDOS with __huge pointers or Cray PVP, but these + * obsolete configurations are not supported any longer. + */ +#if defined(__LZO_MMODEL_HUGE) +#error "__LZO_MMODEL_HUGE memory model is unsupported" +#endif +#if (LZO_MM_PVP) +#error "LZO_MM_PVP memory model is unsupported" +#endif +#if (LZO_SIZEOF_INT < 4) +#error "LZO_SIZEOF_INT < 4 is unsupported" +#endif +#if (__LZO_UINTPTR_T_IS_POINTER) +#error "__LZO_UINTPTR_T_IS_POINTER is unsupported" +#endif +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(int) >= 4) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint) >= 4) +/* Strange configurations where sizeof(lzo_uint) != sizeof(size_t) should + * work but have not received much testing lately, so be strict here. + */ +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint) == sizeof(size_t)) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint) == sizeof(ptrdiff_t)) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint) == sizeof(lzo_uintptr_t)) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(void *) == sizeof(lzo_uintptr_t)) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(char *) == sizeof(lzo_uintptr_t)) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long *) == sizeof(lzo_uintptr_t)) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(void *) == sizeof(lzo_voidp)) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(char *) == sizeof(lzo_bytep)) + + +/*********************************************************************** +// function types +************************************************************************/ + +/* name mangling */ +#if !defined(__LZO_EXTERN_C) +# ifdef __cplusplus +# define __LZO_EXTERN_C extern "C" +# else +# define __LZO_EXTERN_C extern +# endif +#endif + +/* calling convention */ +#if !defined(__LZO_CDECL) +# define __LZO_CDECL __lzo_cdecl +#endif + +/* DLL export information */ +#if !defined(__LZO_EXPORT1) +# define __LZO_EXPORT1 /*empty*/ +#endif +#if !defined(__LZO_EXPORT2) +# define __LZO_EXPORT2 /*empty*/ +#endif + +/* __cdecl calling convention for public C and assembly functions */ +#if !defined(LZO_PUBLIC) +# define LZO_PUBLIC(r) __LZO_EXPORT1 r __LZO_EXPORT2 __LZO_CDECL +#endif +#if !defined(LZO_EXTERN) +# define LZO_EXTERN(r) __LZO_EXTERN_C LZO_PUBLIC(r) +#endif +#if !defined(LZO_PRIVATE) +# define LZO_PRIVATE(r) static r __LZO_CDECL +#endif + +/* function types */ +typedef int +(__LZO_CDECL *lzo_compress_t) ( const lzo_bytep src, lzo_uint src_len, + lzo_bytep dst, lzo_uintp dst_len, + lzo_voidp wrkmem ); + +typedef int +(__LZO_CDECL *lzo_decompress_t) ( const lzo_bytep src, lzo_uint src_len, + lzo_bytep dst, lzo_uintp dst_len, + lzo_voidp wrkmem ); + +typedef int +(__LZO_CDECL *lzo_optimize_t) ( lzo_bytep src, lzo_uint src_len, + lzo_bytep dst, lzo_uintp dst_len, + lzo_voidp wrkmem ); + +typedef int +(__LZO_CDECL *lzo_compress_dict_t)(const lzo_bytep src, lzo_uint src_len, + lzo_bytep dst, lzo_uintp dst_len, + lzo_voidp wrkmem, + const lzo_bytep dict, lzo_uint dict_len ); + +typedef int +(__LZO_CDECL *lzo_decompress_dict_t)(const lzo_bytep src, lzo_uint src_len, + lzo_bytep dst, lzo_uintp dst_len, + lzo_voidp wrkmem, + const lzo_bytep dict, lzo_uint dict_len ); + + +/* Callback interface. Currently only the progress indicator ("nprogress") + * is used, but this may change in a future release. */ + +struct lzo_callback_t; +typedef struct lzo_callback_t lzo_callback_t; +#define lzo_callback_p lzo_callback_t __LZO_MMODEL * + +/* malloc & free function types */ +typedef lzo_voidp (__LZO_CDECL *lzo_alloc_func_t) + (lzo_callback_p self, lzo_uint items, lzo_uint size); +typedef void (__LZO_CDECL *lzo_free_func_t) + (lzo_callback_p self, lzo_voidp ptr); + +/* a progress indicator callback function */ +typedef void (__LZO_CDECL *lzo_progress_func_t) + (lzo_callback_p, lzo_uint, lzo_uint, int); + +struct lzo_callback_t +{ + /* custom allocators (set to 0 to disable) */ + lzo_alloc_func_t nalloc; /* [not used right now] */ + lzo_free_func_t nfree; /* [not used right now] */ + + /* a progress indicator callback function (set to 0 to disable) */ + lzo_progress_func_t nprogress; + + /* INFO: the first parameter "self" of the nalloc/nfree/nprogress + * callbacks points back to this struct, so you are free to store + * some extra info in the following variables. */ + lzo_voidp user1; + lzo_xint user2; + lzo_xint user3; +}; + + +/*********************************************************************** +// error codes and prototypes +************************************************************************/ + +/* Error codes for the compression/decompression functions. Negative + * values are errors, positive values will be used for special but + * normal events. + */ +#define LZO_E_OK 0 +#define LZO_E_ERROR (-1) +#define LZO_E_OUT_OF_MEMORY (-2) /* [lzo_alloc_func_t failure] */ +#define LZO_E_NOT_COMPRESSIBLE (-3) /* [not used right now] */ +#define LZO_E_INPUT_OVERRUN (-4) +#define LZO_E_OUTPUT_OVERRUN (-5) +#define LZO_E_LOOKBEHIND_OVERRUN (-6) +#define LZO_E_EOF_NOT_FOUND (-7) +#define LZO_E_INPUT_NOT_CONSUMED (-8) +#define LZO_E_NOT_YET_IMPLEMENTED (-9) /* [not used right now] */ +#define LZO_E_INVALID_ARGUMENT (-10) +#define LZO_E_INVALID_ALIGNMENT (-11) /* pointer argument is not properly aligned */ +#define LZO_E_OUTPUT_NOT_CONSUMED (-12) +#define LZO_E_INTERNAL_ERROR (-99) + + +#ifndef lzo_sizeof_dict_t +# define lzo_sizeof_dict_t ((unsigned)sizeof(lzo_bytep)) +#endif + +/* lzo_init() should be the first function you call. + * Check the return code ! + * + * lzo_init() is a macro to allow checking that the library and the + * compiler's view of various types are consistent. + */ +#define lzo_init() __lzo_init_v2(LZO_VERSION,(int)sizeof(short),(int)sizeof(int),\ + (int)sizeof(long),(int)sizeof(lzo_uint32_t),(int)sizeof(lzo_uint),\ + (int)lzo_sizeof_dict_t,(int)sizeof(char *),(int)sizeof(lzo_voidp),\ + (int)sizeof(lzo_callback_t)) +LZO_EXTERN(int) __lzo_init_v2(unsigned,int,int,int,int,int,int,int,int,int); + +/* version functions (useful for shared libraries) */ +LZO_EXTERN(unsigned) lzo_version(void); +LZO_EXTERN(const char *) lzo_version_string(void); +LZO_EXTERN(const char *) lzo_version_date(void); +LZO_EXTERN(const lzo_charp) _lzo_version_string(void); +LZO_EXTERN(const lzo_charp) _lzo_version_date(void); + +/* string functions */ +LZO_EXTERN(int) + lzo_memcmp(const lzo_voidp a, const lzo_voidp b, lzo_uint len); +LZO_EXTERN(lzo_voidp) + lzo_memcpy(lzo_voidp dst, const lzo_voidp src, lzo_uint len); +LZO_EXTERN(lzo_voidp) + lzo_memmove(lzo_voidp dst, const lzo_voidp src, lzo_uint len); +LZO_EXTERN(lzo_voidp) + lzo_memset(lzo_voidp buf, int c, lzo_uint len); + +/* checksum functions */ +LZO_EXTERN(lzo_uint32_t) + lzo_adler32(lzo_uint32_t c, const lzo_bytep buf, lzo_uint len); +LZO_EXTERN(lzo_uint32_t) + lzo_crc32(lzo_uint32_t c, const lzo_bytep buf, lzo_uint len); +LZO_EXTERN(const lzo_uint32_tp) + lzo_get_crc32_table(void); + +/* misc. */ +LZO_EXTERN(int) _lzo_config_check(void); +typedef union { + lzo_voidp a00; lzo_bytep a01; lzo_uint a02; lzo_xint a03; lzo_uintptr_t a04; + void *a05; unsigned char *a06; unsigned long a07; size_t a08; ptrdiff_t a09; +#if defined(lzo_int64_t) + lzo_uint64_t a10; +#endif +} lzo_align_t; + +/* align a char pointer on a boundary that is a multiple of 'size' */ +LZO_EXTERN(unsigned) __lzo_align_gap(const lzo_voidp p, lzo_uint size); +#define LZO_PTR_ALIGN_UP(p,size) \ + ((p) + (lzo_uint) __lzo_align_gap((const lzo_voidp)(p),(lzo_uint)(size))) + + +/*********************************************************************** +// deprecated macros - only for backward compatibility +************************************************************************/ + +/* deprecated - use 'lzo_bytep' instead of 'lzo_byte *' */ +#define lzo_byte unsigned char +/* deprecated type names */ +#define lzo_int32 lzo_int32_t +#define lzo_uint32 lzo_uint32_t +#define lzo_int32p lzo_int32_t __LZO_MMODEL * +#define lzo_uint32p lzo_uint32_t __LZO_MMODEL * +#define LZO_INT32_MAX LZO_INT32_C(2147483647) +#define LZO_UINT32_MAX LZO_UINT32_C(4294967295) +#if defined(lzo_int64_t) +#define lzo_int64 lzo_int64_t +#define lzo_uint64 lzo_uint64_t +#define lzo_int64p lzo_int64_t __LZO_MMODEL * +#define lzo_uint64p lzo_uint64_t __LZO_MMODEL * +#define LZO_INT64_MAX LZO_INT64_C(9223372036854775807) +#define LZO_UINT64_MAX LZO_UINT64_C(18446744073709551615) +#endif +/* deprecated types */ +typedef union { lzo_bytep a; lzo_uint b; } __lzo_pu_u; +typedef union { lzo_bytep a; lzo_uint32_t b; } __lzo_pu32_u; +/* deprecated defines */ +#if !defined(LZO_SIZEOF_LZO_UINT) +# define LZO_SIZEOF_LZO_UINT LZO_SIZEOF_LZO_INT +#endif + +#if defined(LZO_CFG_COMPAT) + +#define __LZOCONF_H 1 + +#if defined(LZO_ARCH_I086) +# define __LZO_i386 1 +#elif defined(LZO_ARCH_I386) +# define __LZO_i386 1 +#endif + +#if defined(LZO_OS_DOS16) +# define __LZO_DOS 1 +# define __LZO_DOS16 1 +#elif defined(LZO_OS_DOS32) +# define __LZO_DOS 1 +#elif defined(LZO_OS_WIN16) +# define __LZO_WIN 1 +# define __LZO_WIN16 1 +#elif defined(LZO_OS_WIN32) +# define __LZO_WIN 1 +#endif + +#define __LZO_CMODEL /*empty*/ +#define __LZO_DMODEL /*empty*/ +#define __LZO_ENTRY __LZO_CDECL +#define LZO_EXTERN_CDECL LZO_EXTERN +#define LZO_ALIGN LZO_PTR_ALIGN_UP + +#define lzo_compress_asm_t lzo_compress_t +#define lzo_decompress_asm_t lzo_decompress_t + +#endif /* LZO_CFG_COMPAT */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* already included */ + + +/* vim:set ts=4 sw=4 et: */ diff --git a/minilzo/lzodefs.h b/minilzo/lzodefs.h new file mode 100644 index 0000000..c3e2bcf --- /dev/null +++ b/minilzo/lzodefs.h @@ -0,0 +1,3268 @@ +/* lzodefs.h -- architecture, OS and compiler specific defines + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996-2017 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Markus F.X.J. Oberhumer + + http://www.oberhumer.com/opensource/lzo/ + */ + + +#ifndef __LZODEFS_H_INCLUDED +#define __LZODEFS_H_INCLUDED 1 + +#if defined(__CYGWIN32__) && !defined(__CYGWIN__) +# define __CYGWIN__ __CYGWIN32__ +#endif +#if 1 && defined(__INTERIX) && defined(__GNUC__) && !defined(_ALL_SOURCE) +# define _ALL_SOURCE 1 +#endif +#if defined(__mips__) && defined(__R5900__) +# if !defined(__LONG_MAX__) +# define __LONG_MAX__ 9223372036854775807L +# endif +#endif +#if 0 +#elif !defined(__LZO_LANG_OVERRIDE) +#if (defined(__clang__) || defined(__GNUC__)) && defined(__ASSEMBLER__) +# if (__ASSEMBLER__+0) <= 0 +# error "__ASSEMBLER__" +# else +# define LZO_LANG_ASSEMBLER 1 +# endif +#elif defined(__cplusplus) +# if (__cplusplus+0) <= 0 +# error "__cplusplus" +# elif (__cplusplus < 199711L) +# define LZO_LANG_CXX 1 +# elif defined(_MSC_VER) && defined(_MSVC_LANG) && (_MSVC_LANG+0 >= 201402L) && 1 +# define LZO_LANG_CXX _MSVC_LANG +# else +# define LZO_LANG_CXX __cplusplus +# endif +# define LZO_LANG_CPLUSPLUS LZO_LANG_CXX +#else +# if defined(__STDC_VERSION__) && (__STDC_VERSION__+0 >= 199409L) +# define LZO_LANG_C __STDC_VERSION__ +# else +# define LZO_LANG_C 1 +# endif +#endif +#endif +#if !defined(LZO_CFG_NO_DISABLE_WUNDEF) +#if defined(__ARMCC_VERSION) +# pragma diag_suppress 193 +#elif defined(__clang__) && defined(__clang_minor__) +# pragma clang diagnostic ignored "-Wundef" +#elif defined(__INTEL_COMPILER) +# pragma warning(disable: 193) +#elif defined(__KEIL__) && defined(__C166__) +# pragma warning disable = 322 +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && !defined(__PATHSCALE__) +# if ((__GNUC__-0) >= 5 || ((__GNUC__-0) == 4 && (__GNUC_MINOR__-0) >= 2)) +# pragma GCC diagnostic ignored "-Wundef" +# endif +#elif defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__MWERKS__) +# if ((_MSC_VER-0) >= 1300) +# pragma warning(disable: 4668) +# endif +#endif +#endif +#if 0 && defined(__POCC__) && defined(_WIN32) +# if (__POCC__ >= 400) +# pragma warn(disable: 2216) +# endif +#endif +#if 0 && defined(__WATCOMC__) +# if (__WATCOMC__ >= 1050) && (__WATCOMC__ < 1060) +# pragma warning 203 9 +# endif +#endif +#if defined(__BORLANDC__) && defined(__MSDOS__) && !defined(__FLAT__) +# pragma option -h +#endif +#if !(LZO_CFG_NO_DISABLE_WCRTNONSTDC) +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#endif +#ifndef _CRT_NONSTDC_NO_WARNINGS +#define _CRT_NONSTDC_NO_WARNINGS 1 +#endif +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS 1 +#endif +#endif +#if 0 +#define LZO_0xffffUL 0xfffful +#define LZO_0xffffffffUL 0xfffffffful +#else +#define LZO_0xffffUL 65535ul +#define LZO_0xffffffffUL 4294967295ul +#endif +#define LZO_0xffffL LZO_0xffffUL +#define LZO_0xffffffffL LZO_0xffffffffUL +#if (LZO_0xffffL == LZO_0xffffffffL) +# error "your preprocessor is broken 1" +#endif +#if (16ul * 16384ul != 262144ul) +# error "your preprocessor is broken 2" +#endif +#if 0 +#if (32767 >= 4294967295ul) +# error "your preprocessor is broken 3" +#endif +#if (65535u >= 4294967295ul) +# error "your preprocessor is broken 4" +#endif +#endif +#if defined(__COUNTER__) +# ifndef LZO_CFG_USE_COUNTER +# define LZO_CFG_USE_COUNTER 1 +# endif +#else +# undef LZO_CFG_USE_COUNTER +#endif +#if (UINT_MAX == LZO_0xffffL) +#if defined(__ZTC__) && defined(__I86__) && !defined(__OS2__) +# if !defined(MSDOS) +# define MSDOS 1 +# endif +# if !defined(_MSDOS) +# define _MSDOS 1 +# endif +#elif 0 && defined(__VERSION) && defined(MB_LEN_MAX) +# if (__VERSION == 520) && (MB_LEN_MAX == 1) +# if !defined(__AZTEC_C__) +# define __AZTEC_C__ __VERSION +# endif +# if !defined(__DOS__) +# define __DOS__ 1 +# endif +# endif +#endif +#endif +#if (UINT_MAX == LZO_0xffffL) +#if defined(_MSC_VER) && defined(M_I86HM) +# define ptrdiff_t long +# define _PTRDIFF_T_DEFINED 1 +#endif +#endif +#if (UINT_MAX == LZO_0xffffL) +# undef __LZO_RENAME_A +# undef __LZO_RENAME_B +# if defined(__AZTEC_C__) && defined(__DOS__) +# define __LZO_RENAME_A 1 +# elif defined(_MSC_VER) && defined(MSDOS) +# if (_MSC_VER < 600) +# define __LZO_RENAME_A 1 +# elif (_MSC_VER < 700) +# define __LZO_RENAME_B 1 +# endif +# elif defined(__TSC__) && defined(__OS2__) +# define __LZO_RENAME_A 1 +# elif defined(__MSDOS__) && defined(__TURBOC__) && (__TURBOC__ < 0x0410) +# define __LZO_RENAME_A 1 +# elif defined(__PACIFIC__) && defined(DOS) +# if !defined(__far) +# define __far far +# endif +# if !defined(__near) +# define __near near +# endif +# endif +# if defined(__LZO_RENAME_A) +# if !defined(__cdecl) +# define __cdecl cdecl +# endif +# if !defined(__far) +# define __far far +# endif +# if !defined(__huge) +# define __huge huge +# endif +# if !defined(__near) +# define __near near +# endif +# if !defined(__pascal) +# define __pascal pascal +# endif +# if !defined(__huge) +# define __huge huge +# endif +# elif defined(__LZO_RENAME_B) +# if !defined(__cdecl) +# define __cdecl _cdecl +# endif +# if !defined(__far) +# define __far _far +# endif +# if !defined(__huge) +# define __huge _huge +# endif +# if !defined(__near) +# define __near _near +# endif +# if !defined(__pascal) +# define __pascal _pascal +# endif +# elif (defined(__PUREC__) || defined(__TURBOC__)) && defined(__TOS__) +# if !defined(__cdecl) +# define __cdecl cdecl +# endif +# if !defined(__pascal) +# define __pascal pascal +# endif +# endif +# undef __LZO_RENAME_A +# undef __LZO_RENAME_B +#endif +#if (UINT_MAX == LZO_0xffffL) +#if defined(__AZTEC_C__) && defined(__DOS__) +# define LZO_BROKEN_CDECL_ALT_SYNTAX 1 +#elif defined(_MSC_VER) && defined(MSDOS) +# if (_MSC_VER < 600) +# define LZO_BROKEN_INTEGRAL_CONSTANTS 1 +# endif +# if (_MSC_VER < 700) +# define LZO_BROKEN_INTEGRAL_PROMOTION 1 +# define LZO_BROKEN_SIZEOF 1 +# endif +#elif defined(__PACIFIC__) && defined(DOS) +# define LZO_BROKEN_INTEGRAL_CONSTANTS 1 +#elif defined(__TURBOC__) && defined(__MSDOS__) +# if (__TURBOC__ < 0x0150) +# define LZO_BROKEN_CDECL_ALT_SYNTAX 1 +# define LZO_BROKEN_INTEGRAL_CONSTANTS 1 +# define LZO_BROKEN_INTEGRAL_PROMOTION 1 +# endif +# if (__TURBOC__ < 0x0200) +# define LZO_BROKEN_SIZEOF 1 +# endif +# if (__TURBOC__ < 0x0400) && defined(__cplusplus) +# define LZO_BROKEN_CDECL_ALT_SYNTAX 1 +# endif +#elif (defined(__PUREC__) || defined(__TURBOC__)) && defined(__TOS__) +# define LZO_BROKEN_CDECL_ALT_SYNTAX 1 +# define LZO_BROKEN_SIZEOF 1 +#endif +#endif +#if defined(__WATCOMC__) && (__WATCOMC__ < 900) +# define LZO_BROKEN_INTEGRAL_CONSTANTS 1 +#endif +#if defined(_CRAY) && defined(_CRAY1) +# define LZO_BROKEN_SIGNED_RIGHT_SHIFT 1 +#endif +#define LZO_PP_STRINGIZE(x) #x +#define LZO_PP_MACRO_EXPAND(x) LZO_PP_STRINGIZE(x) +#define LZO_PP_CONCAT0() /*empty*/ +#define LZO_PP_CONCAT1(a) a +#define LZO_PP_CONCAT2(a,b) a ## b +#define LZO_PP_CONCAT3(a,b,c) a ## b ## c +#define LZO_PP_CONCAT4(a,b,c,d) a ## b ## c ## d +#define LZO_PP_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e +#define LZO_PP_CONCAT6(a,b,c,d,e,f) a ## b ## c ## d ## e ## f +#define LZO_PP_CONCAT7(a,b,c,d,e,f,g) a ## b ## c ## d ## e ## f ## g +#define LZO_PP_ECONCAT0() LZO_PP_CONCAT0() +#define LZO_PP_ECONCAT1(a) LZO_PP_CONCAT1(a) +#define LZO_PP_ECONCAT2(a,b) LZO_PP_CONCAT2(a,b) +#define LZO_PP_ECONCAT3(a,b,c) LZO_PP_CONCAT3(a,b,c) +#define LZO_PP_ECONCAT4(a,b,c,d) LZO_PP_CONCAT4(a,b,c,d) +#define LZO_PP_ECONCAT5(a,b,c,d,e) LZO_PP_CONCAT5(a,b,c,d,e) +#define LZO_PP_ECONCAT6(a,b,c,d,e,f) LZO_PP_CONCAT6(a,b,c,d,e,f) +#define LZO_PP_ECONCAT7(a,b,c,d,e,f,g) LZO_PP_CONCAT7(a,b,c,d,e,f,g) +#define LZO_PP_EMPTY /*empty*/ +#define LZO_PP_EMPTY0() /*empty*/ +#define LZO_PP_EMPTY1(a) /*empty*/ +#define LZO_PP_EMPTY2(a,b) /*empty*/ +#define LZO_PP_EMPTY3(a,b,c) /*empty*/ +#define LZO_PP_EMPTY4(a,b,c,d) /*empty*/ +#define LZO_PP_EMPTY5(a,b,c,d,e) /*empty*/ +#define LZO_PP_EMPTY6(a,b,c,d,e,f) /*empty*/ +#define LZO_PP_EMPTY7(a,b,c,d,e,f,g) /*empty*/ +#if 1 +#define LZO_CPP_STRINGIZE(x) #x +#define LZO_CPP_MACRO_EXPAND(x) LZO_CPP_STRINGIZE(x) +#define LZO_CPP_CONCAT2(a,b) a ## b +#define LZO_CPP_CONCAT3(a,b,c) a ## b ## c +#define LZO_CPP_CONCAT4(a,b,c,d) a ## b ## c ## d +#define LZO_CPP_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e +#define LZO_CPP_CONCAT6(a,b,c,d,e,f) a ## b ## c ## d ## e ## f +#define LZO_CPP_CONCAT7(a,b,c,d,e,f,g) a ## b ## c ## d ## e ## f ## g +#define LZO_CPP_ECONCAT2(a,b) LZO_CPP_CONCAT2(a,b) +#define LZO_CPP_ECONCAT3(a,b,c) LZO_CPP_CONCAT3(a,b,c) +#define LZO_CPP_ECONCAT4(a,b,c,d) LZO_CPP_CONCAT4(a,b,c,d) +#define LZO_CPP_ECONCAT5(a,b,c,d,e) LZO_CPP_CONCAT5(a,b,c,d,e) +#define LZO_CPP_ECONCAT6(a,b,c,d,e,f) LZO_CPP_CONCAT6(a,b,c,d,e,f) +#define LZO_CPP_ECONCAT7(a,b,c,d,e,f,g) LZO_CPP_CONCAT7(a,b,c,d,e,f,g) +#endif +#define __LZO_MASK_GEN(o,b) (((((o) << ((b)-((b)!=0))) - (o)) << 1) + (o)*((b)!=0)) +#if 1 && defined(__cplusplus) +# if !defined(__STDC_CONSTANT_MACROS) +# define __STDC_CONSTANT_MACROS 1 +# endif +# if !defined(__STDC_LIMIT_MACROS) +# define __STDC_LIMIT_MACROS 1 +# endif +#endif +#if defined(__cplusplus) +# define LZO_EXTERN_C extern "C" +# define LZO_EXTERN_C_BEGIN extern "C" { +# define LZO_EXTERN_C_END } +#else +# define LZO_EXTERN_C extern +# define LZO_EXTERN_C_BEGIN /*empty*/ +# define LZO_EXTERN_C_END /*empty*/ +#endif +#if !defined(__LZO_OS_OVERRIDE) +#if (LZO_OS_FREESTANDING) +# define LZO_INFO_OS "freestanding" +#elif (LZO_OS_EMBEDDED) +# define LZO_INFO_OS "embedded" +#elif 1 && defined(__IAR_SYSTEMS_ICC__) +# define LZO_OS_EMBEDDED 1 +# define LZO_INFO_OS "embedded" +#elif defined(__CYGWIN__) && defined(__GNUC__) +# define LZO_OS_CYGWIN 1 +# define LZO_INFO_OS "cygwin" +#elif defined(__EMX__) && defined(__GNUC__) +# define LZO_OS_EMX 1 +# define LZO_INFO_OS "emx" +#elif defined(__BEOS__) +# define LZO_OS_BEOS 1 +# define LZO_INFO_OS "beos" +#elif defined(__Lynx__) +# define LZO_OS_LYNXOS 1 +# define LZO_INFO_OS "lynxos" +#elif defined(__OS400__) +# define LZO_OS_OS400 1 +# define LZO_INFO_OS "os400" +#elif defined(__QNX__) +# define LZO_OS_QNX 1 +# define LZO_INFO_OS "qnx" +#elif defined(__BORLANDC__) && defined(__DPMI32__) && (__BORLANDC__ >= 0x0460) +# define LZO_OS_DOS32 1 +# define LZO_INFO_OS "dos32" +#elif defined(__BORLANDC__) && defined(__DPMI16__) +# define LZO_OS_DOS16 1 +# define LZO_INFO_OS "dos16" +#elif defined(__ZTC__) && defined(DOS386) +# define LZO_OS_DOS32 1 +# define LZO_INFO_OS "dos32" +#elif defined(__OS2__) || defined(__OS2V2__) +# if (UINT_MAX == LZO_0xffffL) +# define LZO_OS_OS216 1 +# define LZO_INFO_OS "os216" +# elif (UINT_MAX == LZO_0xffffffffL) +# define LZO_OS_OS2 1 +# define LZO_INFO_OS "os2" +# else +# error "check your limits.h header" +# endif +#elif defined(__WIN64__) || defined(_WIN64) || defined(WIN64) +# define LZO_OS_WIN64 1 +# define LZO_INFO_OS "win64" +#elif defined(__WIN32__) || defined(_WIN32) || defined(WIN32) || defined(__WINDOWS_386__) +# define LZO_OS_WIN32 1 +# define LZO_INFO_OS "win32" +#elif defined(__MWERKS__) && defined(__INTEL__) +# define LZO_OS_WIN32 1 +# define LZO_INFO_OS "win32" +#elif defined(__WINDOWS__) || defined(_WINDOWS) || defined(_Windows) +# if (UINT_MAX == LZO_0xffffL) +# define LZO_OS_WIN16 1 +# define LZO_INFO_OS "win16" +# elif (UINT_MAX == LZO_0xffffffffL) +# define LZO_OS_WIN32 1 +# define LZO_INFO_OS "win32" +# else +# error "check your limits.h header" +# endif +#elif defined(__DOS__) || defined(__MSDOS__) || defined(_MSDOS) || defined(MSDOS) || (defined(__PACIFIC__) && defined(DOS)) +# if (UINT_MAX == LZO_0xffffL) +# define LZO_OS_DOS16 1 +# define LZO_INFO_OS "dos16" +# elif (UINT_MAX == LZO_0xffffffffL) +# define LZO_OS_DOS32 1 +# define LZO_INFO_OS "dos32" +# else +# error "check your limits.h header" +# endif +#elif defined(__WATCOMC__) +# if defined(__NT__) && (UINT_MAX == LZO_0xffffL) +# define LZO_OS_DOS16 1 +# define LZO_INFO_OS "dos16" +# elif defined(__NT__) && (__WATCOMC__ < 1100) +# define LZO_OS_WIN32 1 +# define LZO_INFO_OS "win32" +# elif defined(__linux__) || defined(__LINUX__) +# define LZO_OS_POSIX 1 +# define LZO_INFO_OS "posix" +# else +# error "please specify a target using the -bt compiler option" +# endif +#elif defined(__palmos__) +# define LZO_OS_PALMOS 1 +# define LZO_INFO_OS "palmos" +#elif defined(__TOS__) || defined(__atarist__) +# define LZO_OS_TOS 1 +# define LZO_INFO_OS "tos" +#elif defined(macintosh) && !defined(__arm__) && !defined(__i386__) && !defined(__ppc__) && !defined(__x64_64__) +# define LZO_OS_MACCLASSIC 1 +# define LZO_INFO_OS "macclassic" +#elif defined(__VMS) +# define LZO_OS_VMS 1 +# define LZO_INFO_OS "vms" +#elif (defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__) +# define LZO_OS_CONSOLE 1 +# define LZO_OS_CONSOLE_PS2 1 +# define LZO_INFO_OS "console" +# define LZO_INFO_OS_CONSOLE "ps2" +#elif defined(__mips__) && defined(__psp__) +# define LZO_OS_CONSOLE 1 +# define LZO_OS_CONSOLE_PSP 1 +# define LZO_INFO_OS "console" +# define LZO_INFO_OS_CONSOLE "psp" +#else +# define LZO_OS_POSIX 1 +# define LZO_INFO_OS "posix" +#endif +#if (LZO_OS_POSIX) +# if defined(_AIX) || defined(__AIX__) || defined(__aix__) +# define LZO_OS_POSIX_AIX 1 +# define LZO_INFO_OS_POSIX "aix" +# elif defined(__FreeBSD__) +# define LZO_OS_POSIX_FREEBSD 1 +# define LZO_INFO_OS_POSIX "freebsd" +# elif defined(__hpux__) || defined(__hpux) +# define LZO_OS_POSIX_HPUX 1 +# define LZO_INFO_OS_POSIX "hpux" +# elif defined(__INTERIX) +# define LZO_OS_POSIX_INTERIX 1 +# define LZO_INFO_OS_POSIX "interix" +# elif defined(__IRIX__) || defined(__irix__) +# define LZO_OS_POSIX_IRIX 1 +# define LZO_INFO_OS_POSIX "irix" +# elif defined(__linux__) || defined(__linux) || defined(__LINUX__) +# define LZO_OS_POSIX_LINUX 1 +# define LZO_INFO_OS_POSIX "linux" +# elif defined(__APPLE__) && defined(__MACH__) +# if ((__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__-0) >= 20000) +# define LZO_OS_POSIX_DARWIN 1040 +# define LZO_INFO_OS_POSIX "darwin_iphone" +# elif ((__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__-0) >= 1040) +# define LZO_OS_POSIX_DARWIN __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +# define LZO_INFO_OS_POSIX "darwin" +# else +# define LZO_OS_POSIX_DARWIN 1 +# define LZO_INFO_OS_POSIX "darwin" +# endif +# define LZO_OS_POSIX_MACOSX LZO_OS_POSIX_DARWIN +# elif defined(__minix__) || defined(__minix) +# define LZO_OS_POSIX_MINIX 1 +# define LZO_INFO_OS_POSIX "minix" +# elif defined(__NetBSD__) +# define LZO_OS_POSIX_NETBSD 1 +# define LZO_INFO_OS_POSIX "netbsd" +# elif defined(__OpenBSD__) +# define LZO_OS_POSIX_OPENBSD 1 +# define LZO_INFO_OS_POSIX "openbsd" +# elif defined(__osf__) +# define LZO_OS_POSIX_OSF 1 +# define LZO_INFO_OS_POSIX "osf" +# elif defined(__solaris__) || defined(__sun) +# if defined(__SVR4) || defined(__svr4__) +# define LZO_OS_POSIX_SOLARIS 1 +# define LZO_INFO_OS_POSIX "solaris" +# else +# define LZO_OS_POSIX_SUNOS 1 +# define LZO_INFO_OS_POSIX "sunos" +# endif +# elif defined(__ultrix__) || defined(__ultrix) +# define LZO_OS_POSIX_ULTRIX 1 +# define LZO_INFO_OS_POSIX "ultrix" +# elif defined(_UNICOS) +# define LZO_OS_POSIX_UNICOS 1 +# define LZO_INFO_OS_POSIX "unicos" +# else +# define LZO_OS_POSIX_UNKNOWN 1 +# define LZO_INFO_OS_POSIX "unknown" +# endif +#endif +#endif +#if (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) +# if (UINT_MAX != LZO_0xffffL) +# error "unexpected configuration - check your compiler defines" +# endif +# if (ULONG_MAX != LZO_0xffffffffL) +# error "unexpected configuration - check your compiler defines" +# endif +#endif +#if (LZO_OS_DOS32 || LZO_OS_OS2 || LZO_OS_WIN32 || LZO_OS_WIN64) +# if (UINT_MAX != LZO_0xffffffffL) +# error "unexpected configuration - check your compiler defines" +# endif +# if (ULONG_MAX != LZO_0xffffffffL) +# error "unexpected configuration - check your compiler defines" +# endif +#endif +#if defined(CIL) && defined(_GNUCC) && defined(__GNUC__) +# define LZO_CC_CILLY 1 +# define LZO_INFO_CC "Cilly" +# if defined(__CILLY__) +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__CILLY__) +# else +# define LZO_INFO_CCVER "unknown" +# endif +#elif 0 && defined(SDCC) && defined(__VERSION__) && !defined(__GNUC__) +# define LZO_CC_SDCC 1 +# define LZO_INFO_CC "sdcc" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(SDCC) +#elif defined(__PATHSCALE__) && defined(__PATHCC_PATCHLEVEL__) +# define LZO_CC_PATHSCALE (__PATHCC__ * 0x10000L + (__PATHCC_MINOR__-0) * 0x100 + (__PATHCC_PATCHLEVEL__-0)) +# define LZO_INFO_CC "Pathscale C" +# define LZO_INFO_CCVER __PATHSCALE__ +# if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) +# define LZO_CC_PATHSCALE_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# endif +#elif defined(__INTEL_COMPILER) && ((__INTEL_COMPILER-0) > 0) +# define LZO_CC_INTELC __INTEL_COMPILER +# define LZO_INFO_CC "Intel C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__INTEL_COMPILER) +# if defined(_MSC_VER) && ((_MSC_VER-0) > 0) +# define LZO_CC_INTELC_MSC _MSC_VER +# elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) +# define LZO_CC_INTELC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# endif +#elif defined(__POCC__) && defined(_WIN32) +# define LZO_CC_PELLESC 1 +# define LZO_INFO_CC "Pelles C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__POCC__) +#elif defined(__ARMCC_VERSION) && defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) +# if defined(__GNUC_PATCHLEVEL__) +# define LZO_CC_ARMCC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# else +# define LZO_CC_ARMCC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100) +# endif +# define LZO_CC_ARMCC __ARMCC_VERSION +# define LZO_INFO_CC "ARM C Compiler" +# define LZO_INFO_CCVER __VERSION__ +#elif defined(__clang__) && defined(__c2__) && defined(__c2_version__) && defined(_MSC_VER) +# define LZO_CC_CLANG (__clang_major__ * 0x10000L + (__clang_minor__-0) * 0x100 + (__clang_patchlevel__-0)) +# define LZO_CC_CLANG_C2 _MSC_VER +# define LZO_CC_CLANG_VENDOR_MICROSOFT 1 +# define LZO_INFO_CC "clang/c2" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__c2_version__) +#elif defined(__clang__) && defined(__llvm__) && defined(__VERSION__) +# if defined(__clang_major__) && defined(__clang_minor__) && defined(__clang_patchlevel__) +# define LZO_CC_CLANG (__clang_major__ * 0x10000L + (__clang_minor__-0) * 0x100 + (__clang_patchlevel__-0)) +# else +# define LZO_CC_CLANG 0x010000L +# endif +# if defined(_MSC_VER) && ((_MSC_VER-0) > 0) +# define LZO_CC_CLANG_MSC _MSC_VER +# elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) +# define LZO_CC_CLANG_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# endif +# if defined(__APPLE_CC__) +# define LZO_CC_CLANG_VENDOR_APPLE 1 +# define LZO_INFO_CC "clang/apple" +# else +# define LZO_CC_CLANG_VENDOR_LLVM 1 +# define LZO_INFO_CC "clang" +# endif +# if defined(__clang_version__) +# define LZO_INFO_CCVER __clang_version__ +# else +# define LZO_INFO_CCVER __VERSION__ +# endif +#elif defined(__llvm__) && defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) +# if defined(__GNUC_PATCHLEVEL__) +# define LZO_CC_LLVM_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# else +# define LZO_CC_LLVM_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100) +# endif +# define LZO_CC_LLVM LZO_CC_LLVM_GNUC +# define LZO_INFO_CC "llvm-gcc" +# define LZO_INFO_CCVER __VERSION__ +#elif defined(__ACK__) && defined(_ACK) +# define LZO_CC_ACK 1 +# define LZO_INFO_CC "Amsterdam Compiler Kit C" +# define LZO_INFO_CCVER "unknown" +#elif defined(__ARMCC_VERSION) && !defined(__GNUC__) +# define LZO_CC_ARMCC __ARMCC_VERSION +# define LZO_CC_ARMCC_ARMCC __ARMCC_VERSION +# define LZO_INFO_CC "ARM C Compiler" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__ARMCC_VERSION) +#elif defined(__AZTEC_C__) +# define LZO_CC_AZTECC 1 +# define LZO_INFO_CC "Aztec C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__AZTEC_C__) +#elif defined(__CODEGEARC__) +# define LZO_CC_CODEGEARC 1 +# define LZO_INFO_CC "CodeGear C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__CODEGEARC__) +#elif defined(__BORLANDC__) +# define LZO_CC_BORLANDC 1 +# define LZO_INFO_CC "Borland C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__BORLANDC__) +#elif defined(_CRAYC) && defined(_RELEASE) +# define LZO_CC_CRAYC 1 +# define LZO_INFO_CC "Cray C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(_RELEASE) +#elif defined(__DMC__) && defined(__SC__) +# define LZO_CC_DMC 1 +# define LZO_INFO_CC "Digital Mars C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__DMC__) +#elif defined(__DECC) +# define LZO_CC_DECC 1 +# define LZO_INFO_CC "DEC C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__DECC) +#elif (defined(__ghs) || defined(__ghs__)) && defined(__GHS_VERSION_NUMBER) && ((__GHS_VERSION_NUMBER-0) > 0) +# define LZO_CC_GHS 1 +# define LZO_INFO_CC "Green Hills C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__GHS_VERSION_NUMBER) +# if defined(_MSC_VER) && ((_MSC_VER-0) > 0) +# define LZO_CC_GHS_MSC _MSC_VER +# elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) +# define LZO_CC_GHS_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# endif +#elif defined(__HIGHC__) +# define LZO_CC_HIGHC 1 +# define LZO_INFO_CC "MetaWare High C" +# define LZO_INFO_CCVER "unknown" +#elif defined(__HP_aCC) && ((__HP_aCC-0) > 0) +# define LZO_CC_HPACC __HP_aCC +# define LZO_INFO_CC "HP aCC" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__HP_aCC) +#elif defined(__IAR_SYSTEMS_ICC__) +# define LZO_CC_IARC 1 +# define LZO_INFO_CC "IAR C" +# if defined(__VER__) +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__VER__) +# else +# define LZO_INFO_CCVER "unknown" +# endif +#elif defined(__IBMC__) && ((__IBMC__-0) > 0) +# define LZO_CC_IBMC __IBMC__ +# define LZO_INFO_CC "IBM C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__IBMC__) +#elif defined(__IBMCPP__) && ((__IBMCPP__-0) > 0) +# define LZO_CC_IBMC __IBMCPP__ +# define LZO_INFO_CC "IBM C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__IBMCPP__) +#elif defined(__KEIL__) && defined(__C166__) +# define LZO_CC_KEILC 1 +# define LZO_INFO_CC "Keil C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__C166__) +#elif defined(__LCC__) && defined(_WIN32) && defined(__LCCOPTIMLEVEL) +# define LZO_CC_LCCWIN32 1 +# define LZO_INFO_CC "lcc-win32" +# define LZO_INFO_CCVER "unknown" +#elif defined(__LCC__) +# define LZO_CC_LCC 1 +# define LZO_INFO_CC "lcc" +# if defined(__LCC_VERSION__) +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__LCC_VERSION__) +# else +# define LZO_INFO_CCVER "unknown" +# endif +#elif defined(__MWERKS__) && ((__MWERKS__-0) > 0) +# define LZO_CC_MWERKS __MWERKS__ +# define LZO_INFO_CC "Metrowerks C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__MWERKS__) +#elif (defined(__NDPC__) || defined(__NDPX__)) && defined(__i386) +# define LZO_CC_NDPC 1 +# define LZO_INFO_CC "Microway NDP C" +# define LZO_INFO_CCVER "unknown" +#elif defined(__PACIFIC__) +# define LZO_CC_PACIFICC 1 +# define LZO_INFO_CC "Pacific C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PACIFIC__) +#elif defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) +# if defined(__PGIC_PATCHLEVEL__) +# define LZO_CC_PGI (__PGIC__ * 0x10000L + (__PGIC_MINOR__-0) * 0x100 + (__PGIC_PATCHLEVEL__-0)) +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PGIC__) "." LZO_PP_MACRO_EXPAND(__PGIC_MINOR__) "." LZO_PP_MACRO_EXPAND(__PGIC_PATCHLEVEL__) +# else +# define LZO_CC_PGI (__PGIC__ * 0x10000L + (__PGIC_MINOR__-0) * 0x100) +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PGIC__) "." LZO_PP_MACRO_EXPAND(__PGIC_MINOR__) ".0" +# endif +# define LZO_INFO_CC "Portland Group PGI C" +#elif defined(__PGI) && (defined(__linux__) || defined(__WIN32__)) +# define LZO_CC_PGI 1 +# define LZO_INFO_CC "Portland Group PGI C" +# define LZO_INFO_CCVER "unknown" +#elif defined(__PUREC__) && defined(__TOS__) +# define LZO_CC_PUREC 1 +# define LZO_INFO_CC "Pure C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PUREC__) +#elif defined(__SC__) && defined(__ZTC__) +# define LZO_CC_SYMANTECC 1 +# define LZO_INFO_CC "Symantec C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__SC__) +#elif defined(__SUNPRO_C) +# define LZO_INFO_CC "SunPro C" +# if ((__SUNPRO_C-0) > 0) +# define LZO_CC_SUNPROC __SUNPRO_C +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__SUNPRO_C) +# else +# define LZO_CC_SUNPROC 1 +# define LZO_INFO_CCVER "unknown" +# endif +#elif defined(__SUNPRO_CC) +# define LZO_INFO_CC "SunPro C" +# if ((__SUNPRO_CC-0) > 0) +# define LZO_CC_SUNPROC __SUNPRO_CC +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__SUNPRO_CC) +# else +# define LZO_CC_SUNPROC 1 +# define LZO_INFO_CCVER "unknown" +# endif +#elif defined(__TINYC__) +# define LZO_CC_TINYC 1 +# define LZO_INFO_CC "Tiny C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__TINYC__) +#elif defined(__TSC__) +# define LZO_CC_TOPSPEEDC 1 +# define LZO_INFO_CC "TopSpeed C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__TSC__) +#elif defined(__WATCOMC__) +# define LZO_CC_WATCOMC 1 +# define LZO_INFO_CC "Watcom C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__WATCOMC__) +#elif defined(__TURBOC__) +# define LZO_CC_TURBOC 1 +# define LZO_INFO_CC "Turbo C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__TURBOC__) +#elif defined(__ZTC__) +# define LZO_CC_ZORTECHC 1 +# define LZO_INFO_CC "Zortech C" +# if ((__ZTC__-0) == 0x310) +# define LZO_INFO_CCVER "0x310" +# else +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__ZTC__) +# endif +#elif defined(__GNUC__) && defined(__VERSION__) +# if defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +# define LZO_CC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# elif defined(__GNUC_MINOR__) +# define LZO_CC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100) +# else +# define LZO_CC_GNUC (__GNUC__ * 0x10000L) +# endif +# define LZO_INFO_CC "gcc" +# define LZO_INFO_CCVER __VERSION__ +#elif defined(_MSC_VER) && ((_MSC_VER-0) > 0) +# define LZO_CC_MSC _MSC_VER +# define LZO_INFO_CC "Microsoft C" +# if defined(_MSC_FULL_VER) +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(_MSC_VER) "." LZO_PP_MACRO_EXPAND(_MSC_FULL_VER) +# else +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(_MSC_VER) +# endif +#else +# define LZO_CC_UNKNOWN 1 +# define LZO_INFO_CC "unknown" +# define LZO_INFO_CCVER "unknown" +#endif +#if (LZO_CC_GNUC) && defined(__OPEN64__) +# if defined(__OPENCC__) && defined(__OPENCC_MINOR__) && defined(__OPENCC_PATCHLEVEL__) +# define LZO_CC_OPEN64 (__OPENCC__ * 0x10000L + (__OPENCC_MINOR__-0) * 0x100 + (__OPENCC_PATCHLEVEL__-0)) +# define LZO_CC_OPEN64_GNUC LZO_CC_GNUC +# endif +#endif +#if (LZO_CC_GNUC) && defined(__PCC__) +# if defined(__PCC__) && defined(__PCC_MINOR__) && defined(__PCC_MINORMINOR__) +# define LZO_CC_PCC (__PCC__ * 0x10000L + (__PCC_MINOR__-0) * 0x100 + (__PCC_MINORMINOR__-0)) +# define LZO_CC_PCC_GNUC LZO_CC_GNUC +# endif +#endif +#if 0 && (LZO_CC_MSC && (_MSC_VER >= 1200)) && !defined(_MSC_FULL_VER) +# error "LZO_CC_MSC: _MSC_FULL_VER is not defined" +#endif +#if !defined(__LZO_ARCH_OVERRIDE) && !(LZO_ARCH_GENERIC) && defined(_CRAY) +# if (UINT_MAX > LZO_0xffffffffL) && defined(_CRAY) +# if defined(_CRAYMPP) || defined(_CRAYT3D) || defined(_CRAYT3E) +# define LZO_ARCH_CRAY_MPP 1 +# elif defined(_CRAY1) +# define LZO_ARCH_CRAY_PVP 1 +# endif +# endif +#endif +#if !defined(__LZO_ARCH_OVERRIDE) +#if (LZO_ARCH_GENERIC) +# define LZO_INFO_ARCH "generic" +#elif (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) +# define LZO_ARCH_I086 1 +# define LZO_INFO_ARCH "i086" +#elif defined(__aarch64__) || defined(_M_ARM64) +# define LZO_ARCH_ARM64 1 +# define LZO_INFO_ARCH "arm64" +#elif defined(__alpha__) || defined(__alpha) || defined(_M_ALPHA) +# define LZO_ARCH_ALPHA 1 +# define LZO_INFO_ARCH "alpha" +#elif (LZO_ARCH_CRAY_MPP) && (defined(_CRAYT3D) || defined(_CRAYT3E)) +# define LZO_ARCH_ALPHA 1 +# define LZO_INFO_ARCH "alpha" +#elif defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64) +# define LZO_ARCH_AMD64 1 +# define LZO_INFO_ARCH "amd64" +#elif defined(__arm__) || defined(_M_ARM) +# define LZO_ARCH_ARM 1 +# define LZO_INFO_ARCH "arm" +#elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICCARM__) +# define LZO_ARCH_ARM 1 +# define LZO_INFO_ARCH "arm" +#elif (UINT_MAX <= LZO_0xffffL) && defined(__AVR__) +# define LZO_ARCH_AVR 1 +# define LZO_INFO_ARCH "avr" +#elif defined(__avr32__) || defined(__AVR32__) +# define LZO_ARCH_AVR32 1 +# define LZO_INFO_ARCH "avr32" +#elif defined(__bfin__) +# define LZO_ARCH_BLACKFIN 1 +# define LZO_INFO_ARCH "blackfin" +#elif (UINT_MAX == LZO_0xffffL) && defined(__C166__) +# define LZO_ARCH_C166 1 +# define LZO_INFO_ARCH "c166" +#elif defined(__cris__) +# define LZO_ARCH_CRIS 1 +# define LZO_INFO_ARCH "cris" +#elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICCEZ80__) +# define LZO_ARCH_EZ80 1 +# define LZO_INFO_ARCH "ez80" +#elif defined(__H8300__) || defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) +# define LZO_ARCH_H8300 1 +# define LZO_INFO_ARCH "h8300" +#elif defined(__hppa__) || defined(__hppa) +# define LZO_ARCH_HPPA 1 +# define LZO_INFO_ARCH "hppa" +#elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386) +# define LZO_ARCH_I386 1 +# define LZO_ARCH_IA32 1 +# define LZO_INFO_ARCH "i386" +#elif (LZO_CC_ZORTECHC && defined(__I86__)) +# define LZO_ARCH_I386 1 +# define LZO_ARCH_IA32 1 +# define LZO_INFO_ARCH "i386" +#elif (LZO_OS_DOS32 && LZO_CC_HIGHC) && defined(_I386) +# define LZO_ARCH_I386 1 +# define LZO_ARCH_IA32 1 +# define LZO_INFO_ARCH "i386" +#elif defined(__ia64__) || defined(__ia64) || defined(_M_IA64) +# define LZO_ARCH_IA64 1 +# define LZO_INFO_ARCH "ia64" +#elif (UINT_MAX == LZO_0xffffL) && defined(__m32c__) +# define LZO_ARCH_M16C 1 +# define LZO_INFO_ARCH "m16c" +#elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICCM16C__) +# define LZO_ARCH_M16C 1 +# define LZO_INFO_ARCH "m16c" +#elif defined(__m32r__) +# define LZO_ARCH_M32R 1 +# define LZO_INFO_ARCH "m32r" +#elif (LZO_OS_TOS) || defined(__m68k__) || defined(__m68000__) || defined(__mc68000__) || defined(__mc68020__) || defined(_M_M68K) +# define LZO_ARCH_M68K 1 +# define LZO_INFO_ARCH "m68k" +#elif (UINT_MAX == LZO_0xffffL) && defined(__C251__) +# define LZO_ARCH_MCS251 1 +# define LZO_INFO_ARCH "mcs251" +#elif (UINT_MAX == LZO_0xffffL) && defined(__C51__) +# define LZO_ARCH_MCS51 1 +# define LZO_INFO_ARCH "mcs51" +#elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICC8051__) +# define LZO_ARCH_MCS51 1 +# define LZO_INFO_ARCH "mcs51" +#elif defined(__mips__) || defined(__mips) || defined(_MIPS_ARCH) || defined(_M_MRX000) +# define LZO_ARCH_MIPS 1 +# define LZO_INFO_ARCH "mips" +#elif (UINT_MAX == LZO_0xffffL) && defined(__MSP430__) +# define LZO_ARCH_MSP430 1 +# define LZO_INFO_ARCH "msp430" +#elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICC430__) +# define LZO_ARCH_MSP430 1 +# define LZO_INFO_ARCH "msp430" +#elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR) +# define LZO_ARCH_POWERPC 1 +# define LZO_INFO_ARCH "powerpc" +#elif defined(__powerpc64__) || defined(__powerpc64) || defined(__ppc64__) || defined(__PPC64__) +# define LZO_ARCH_POWERPC 1 +# define LZO_INFO_ARCH "powerpc" +#elif defined(__powerpc64le__) || defined(__powerpc64le) || defined(__ppc64le__) || defined(__PPC64LE__) +# define LZO_ARCH_POWERPC 1 +# define LZO_INFO_ARCH "powerpc" +#elif defined(__riscv) +# define LZO_ARCH_RISCV 1 +# define LZO_INFO_ARCH "riscv" +#elif defined(__s390__) || defined(__s390) || defined(__s390x__) || defined(__s390x) +# define LZO_ARCH_S390 1 +# define LZO_INFO_ARCH "s390" +#elif defined(__sh__) || defined(_M_SH) +# define LZO_ARCH_SH 1 +# define LZO_INFO_ARCH "sh" +#elif defined(__sparc__) || defined(__sparc) || defined(__sparcv8) +# define LZO_ARCH_SPARC 1 +# define LZO_INFO_ARCH "sparc" +#elif defined(__SPU__) +# define LZO_ARCH_SPU 1 +# define LZO_INFO_ARCH "spu" +#elif (UINT_MAX == LZO_0xffffL) && defined(__z80) +# define LZO_ARCH_Z80 1 +# define LZO_INFO_ARCH "z80" +#elif (LZO_ARCH_CRAY_PVP) +# if defined(_CRAYSV1) +# define LZO_ARCH_CRAY_SV1 1 +# define LZO_INFO_ARCH "cray_sv1" +# elif (_ADDR64) +# define LZO_ARCH_CRAY_T90 1 +# define LZO_INFO_ARCH "cray_t90" +# elif (_ADDR32) +# define LZO_ARCH_CRAY_YMP 1 +# define LZO_INFO_ARCH "cray_ymp" +# else +# define LZO_ARCH_CRAY_XMP 1 +# define LZO_INFO_ARCH "cray_xmp" +# endif +#else +# define LZO_ARCH_UNKNOWN 1 +# define LZO_INFO_ARCH "unknown" +#endif +#endif +#if !defined(LZO_ARCH_ARM_THUMB2) +#if (LZO_ARCH_ARM) +# if defined(__thumb__) || defined(__thumb) || defined(_M_THUMB) +# if defined(__thumb2__) +# define LZO_ARCH_ARM_THUMB2 1 +# elif 1 && defined(__TARGET_ARCH_THUMB) && ((__TARGET_ARCH_THUMB)+0 >= 4) +# define LZO_ARCH_ARM_THUMB2 1 +# elif 1 && defined(_MSC_VER) && defined(_M_THUMB) && ((_M_THUMB)+0 >= 7) +# define LZO_ARCH_ARM_THUMB2 1 +# endif +# endif +#endif +#endif +#if (LZO_ARCH_ARM_THUMB2) +# undef LZO_INFO_ARCH +# define LZO_INFO_ARCH "arm_thumb2" +#endif +#if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_DOS32 || LZO_OS_OS2) +# error "FIXME - missing define for CPU architecture" +#endif +#if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_WIN32) +# error "FIXME - missing LZO_OS_WIN32 define for CPU architecture" +#endif +#if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_WIN64) +# error "FIXME - missing LZO_OS_WIN64 define for CPU architecture" +#endif +#if (LZO_OS_OS216 || LZO_OS_WIN16) +# define LZO_ARCH_I086PM 1 +#elif 1 && (LZO_OS_DOS16 && defined(BLX286)) +# define LZO_ARCH_I086PM 1 +#elif 1 && (LZO_OS_DOS16 && defined(DOSX286)) +# define LZO_ARCH_I086PM 1 +#elif 1 && (LZO_OS_DOS16 && LZO_CC_BORLANDC && defined(__DPMI16__)) +# define LZO_ARCH_I086PM 1 +#endif +#if (LZO_ARCH_AMD64 && !LZO_ARCH_X64) +# define LZO_ARCH_X64 1 +#elif (!LZO_ARCH_AMD64 && LZO_ARCH_X64) && defined(__LZO_ARCH_OVERRIDE) +# define LZO_ARCH_AMD64 1 +#endif +#if (LZO_ARCH_ARM64 && !LZO_ARCH_AARCH64) +# define LZO_ARCH_AARCH64 1 +#elif (!LZO_ARCH_ARM64 && LZO_ARCH_AARCH64) && defined(__LZO_ARCH_OVERRIDE) +# define LZO_ARCH_ARM64 1 +#endif +#if (LZO_ARCH_I386 && !LZO_ARCH_X86) +# define LZO_ARCH_X86 1 +#elif (!LZO_ARCH_I386 && LZO_ARCH_X86) && defined(__LZO_ARCH_OVERRIDE) +# define LZO_ARCH_I386 1 +#endif +#if (LZO_ARCH_AMD64 && !LZO_ARCH_X64) || (!LZO_ARCH_AMD64 && LZO_ARCH_X64) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_ARM64 && !LZO_ARCH_AARCH64) || (!LZO_ARCH_ARM64 && LZO_ARCH_AARCH64) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_I386 && !LZO_ARCH_X86) || (!LZO_ARCH_I386 && LZO_ARCH_X86) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_ARM_THUMB1 && !LZO_ARCH_ARM) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_ARM_THUMB2 && !LZO_ARCH_ARM) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_ARM_THUMB1 && LZO_ARCH_ARM_THUMB2) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_I086PM && !LZO_ARCH_I086) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_I086) +# if (UINT_MAX != LZO_0xffffL) +# error "unexpected configuration - check your compiler defines" +# endif +# if (ULONG_MAX != LZO_0xffffffffL) +# error "unexpected configuration - check your compiler defines" +# endif +#endif +#if (LZO_ARCH_I386) +# if (UINT_MAX != LZO_0xffffL) && defined(__i386_int16__) +# error "unexpected configuration - check your compiler defines" +# endif +# if (UINT_MAX != LZO_0xffffffffL) && !defined(__i386_int16__) +# error "unexpected configuration - check your compiler defines" +# endif +# if (ULONG_MAX != LZO_0xffffffffL) +# error "unexpected configuration - check your compiler defines" +# endif +#endif +#if (LZO_ARCH_AMD64 || LZO_ARCH_I386) +# if !defined(LZO_TARGET_FEATURE_SSE2) +# if defined(__SSE2__) +# define LZO_TARGET_FEATURE_SSE2 1 +# elif defined(_MSC_VER) && (defined(_M_IX86_FP) && ((_M_IX86_FP)+0 >= 2)) +# define LZO_TARGET_FEATURE_SSE2 1 +# elif (LZO_CC_INTELC_MSC || LZO_CC_MSC) && defined(_M_AMD64) +# define LZO_TARGET_FEATURE_SSE2 1 +# endif +# endif +# if !defined(LZO_TARGET_FEATURE_SSSE3) +# if (LZO_TARGET_FEATURE_SSE2) +# if defined(__SSSE3__) +# define LZO_TARGET_FEATURE_SSSE3 1 +# elif defined(_MSC_VER) && defined(__AVX__) +# define LZO_TARGET_FEATURE_SSSE3 1 +# endif +# endif +# endif +# if !defined(LZO_TARGET_FEATURE_SSE4_2) +# if (LZO_TARGET_FEATURE_SSSE3) +# if defined(__SSE4_2__) +# define LZO_TARGET_FEATURE_SSE4_2 1 +# endif +# endif +# endif +# if !defined(LZO_TARGET_FEATURE_AVX) +# if (LZO_TARGET_FEATURE_SSSE3) +# if defined(__AVX__) +# define LZO_TARGET_FEATURE_AVX 1 +# endif +# endif +# endif +# if !defined(LZO_TARGET_FEATURE_AVX2) +# if (LZO_TARGET_FEATURE_AVX) +# if defined(__AVX2__) +# define LZO_TARGET_FEATURE_AVX2 1 +# endif +# endif +# endif +#endif +#if (LZO_TARGET_FEATURE_SSSE3 && !(LZO_TARGET_FEATURE_SSE2)) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_TARGET_FEATURE_SSE4_2 && !(LZO_TARGET_FEATURE_SSSE3)) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_TARGET_FEATURE_AVX && !(LZO_TARGET_FEATURE_SSSE3)) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_TARGET_FEATURE_AVX2 && !(LZO_TARGET_FEATURE_AVX)) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_ARM) +# if !defined(LZO_TARGET_FEATURE_NEON) +# if defined(__ARM_NEON) && ((__ARM_NEON)+0) +# define LZO_TARGET_FEATURE_NEON 1 +# elif 1 && defined(__ARM_NEON__) && ((__ARM_NEON__)+0) +# define LZO_TARGET_FEATURE_NEON 1 +# elif 1 && defined(__TARGET_FEATURE_NEON) && ((__TARGET_FEATURE_NEON)+0) +# define LZO_TARGET_FEATURE_NEON 1 +# endif +# endif +#elif (LZO_ARCH_ARM64) +# if !defined(LZO_TARGET_FEATURE_NEON) +# if 1 +# define LZO_TARGET_FEATURE_NEON 1 +# endif +# endif +#endif +#if 0 +#elif !defined(__LZO_MM_OVERRIDE) +#if (LZO_ARCH_I086) +#if (UINT_MAX != LZO_0xffffL) +# error "unexpected configuration - check your compiler defines" +#endif +#if defined(__TINY__) || defined(M_I86TM) || defined(_M_I86TM) +# define LZO_MM_TINY 1 +#elif defined(__HUGE__) || defined(_HUGE_) || defined(M_I86HM) || defined(_M_I86HM) +# define LZO_MM_HUGE 1 +#elif defined(__SMALL__) || defined(M_I86SM) || defined(_M_I86SM) || defined(SMALL_MODEL) +# define LZO_MM_SMALL 1 +#elif defined(__MEDIUM__) || defined(M_I86MM) || defined(_M_I86MM) +# define LZO_MM_MEDIUM 1 +#elif defined(__COMPACT__) || defined(M_I86CM) || defined(_M_I86CM) +# define LZO_MM_COMPACT 1 +#elif defined(__LARGE__) || defined(M_I86LM) || defined(_M_I86LM) || defined(LARGE_MODEL) +# define LZO_MM_LARGE 1 +#elif (LZO_CC_AZTECC) +# if defined(_LARGE_CODE) && defined(_LARGE_DATA) +# define LZO_MM_LARGE 1 +# elif defined(_LARGE_CODE) +# define LZO_MM_MEDIUM 1 +# elif defined(_LARGE_DATA) +# define LZO_MM_COMPACT 1 +# else +# define LZO_MM_SMALL 1 +# endif +#elif (LZO_CC_ZORTECHC && defined(__VCM__)) +# define LZO_MM_LARGE 1 +#else +# error "unknown LZO_ARCH_I086 memory model" +#endif +#if (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) +#define LZO_HAVE_MM_HUGE_PTR 1 +#define LZO_HAVE_MM_HUGE_ARRAY 1 +#if (LZO_MM_TINY) +# undef LZO_HAVE_MM_HUGE_ARRAY +#endif +#if (LZO_CC_AZTECC || LZO_CC_PACIFICC || LZO_CC_ZORTECHC) +# undef LZO_HAVE_MM_HUGE_PTR +# undef LZO_HAVE_MM_HUGE_ARRAY +#elif (LZO_CC_DMC || LZO_CC_SYMANTECC) +# undef LZO_HAVE_MM_HUGE_ARRAY +#elif (LZO_CC_MSC && defined(_QC)) +# undef LZO_HAVE_MM_HUGE_ARRAY +# if (_MSC_VER < 600) +# undef LZO_HAVE_MM_HUGE_PTR +# endif +#elif (LZO_CC_TURBOC && (__TURBOC__ < 0x0295)) +# undef LZO_HAVE_MM_HUGE_ARRAY +#endif +#if (LZO_ARCH_I086PM) && !(LZO_HAVE_MM_HUGE_PTR) +# if (LZO_OS_DOS16) +# error "unexpected configuration - check your compiler defines" +# elif (LZO_CC_ZORTECHC) +# else +# error "unexpected configuration - check your compiler defines" +# endif +#endif +#if defined(__cplusplus) +extern "C" { +#endif +#if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0200)) + extern void __near __cdecl _AHSHIFT(void); +# define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) +#elif (LZO_CC_DMC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) + extern void __near __cdecl _AHSHIFT(void); +# define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) +#elif (LZO_CC_MSC || LZO_CC_TOPSPEEDC) + extern void __near __cdecl _AHSHIFT(void); +# define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) +#elif (LZO_CC_TURBOC && (__TURBOC__ >= 0x0295)) + extern void __near __cdecl _AHSHIFT(void); +# define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) +#elif ((LZO_CC_AZTECC || LZO_CC_PACIFICC || LZO_CC_TURBOC) && LZO_OS_DOS16) +# define LZO_MM_AHSHIFT 12 +#elif (LZO_CC_WATCOMC) + extern unsigned char _HShift; +# define LZO_MM_AHSHIFT ((unsigned) _HShift) +#else +# error "FIXME - implement LZO_MM_AHSHIFT" +#endif +#if defined(__cplusplus) +} +#endif +#endif +#elif (LZO_ARCH_C166) +#if !defined(__MODEL__) +# error "FIXME - LZO_ARCH_C166 __MODEL__" +#elif ((__MODEL__) == 0) +# define LZO_MM_SMALL 1 +#elif ((__MODEL__) == 1) +# define LZO_MM_SMALL 1 +#elif ((__MODEL__) == 2) +# define LZO_MM_LARGE 1 +#elif ((__MODEL__) == 3) +# define LZO_MM_TINY 1 +#elif ((__MODEL__) == 4) +# define LZO_MM_XTINY 1 +#elif ((__MODEL__) == 5) +# define LZO_MM_XSMALL 1 +#else +# error "FIXME - LZO_ARCH_C166 __MODEL__" +#endif +#elif (LZO_ARCH_MCS251) +#if !defined(__MODEL__) +# error "FIXME - LZO_ARCH_MCS251 __MODEL__" +#elif ((__MODEL__) == 0) +# define LZO_MM_SMALL 1 +#elif ((__MODEL__) == 2) +# define LZO_MM_LARGE 1 +#elif ((__MODEL__) == 3) +# define LZO_MM_TINY 1 +#elif ((__MODEL__) == 4) +# define LZO_MM_XTINY 1 +#elif ((__MODEL__) == 5) +# define LZO_MM_XSMALL 1 +#else +# error "FIXME - LZO_ARCH_MCS251 __MODEL__" +#endif +#elif (LZO_ARCH_MCS51) +#if !defined(__MODEL__) +# error "FIXME - LZO_ARCH_MCS51 __MODEL__" +#elif ((__MODEL__) == 1) +# define LZO_MM_SMALL 1 +#elif ((__MODEL__) == 2) +# define LZO_MM_LARGE 1 +#elif ((__MODEL__) == 3) +# define LZO_MM_TINY 1 +#elif ((__MODEL__) == 4) +# define LZO_MM_XTINY 1 +#elif ((__MODEL__) == 5) +# define LZO_MM_XSMALL 1 +#else +# error "FIXME - LZO_ARCH_MCS51 __MODEL__" +#endif +#elif (LZO_ARCH_CRAY_PVP) +# define LZO_MM_PVP 1 +#else +# define LZO_MM_FLAT 1 +#endif +#if (LZO_MM_COMPACT) +# define LZO_INFO_MM "compact" +#elif (LZO_MM_FLAT) +# define LZO_INFO_MM "flat" +#elif (LZO_MM_HUGE) +# define LZO_INFO_MM "huge" +#elif (LZO_MM_LARGE) +# define LZO_INFO_MM "large" +#elif (LZO_MM_MEDIUM) +# define LZO_INFO_MM "medium" +#elif (LZO_MM_PVP) +# define LZO_INFO_MM "pvp" +#elif (LZO_MM_SMALL) +# define LZO_INFO_MM "small" +#elif (LZO_MM_TINY) +# define LZO_INFO_MM "tiny" +#else +# error "unknown memory model" +#endif +#endif +#if !defined(__lzo_gnuc_extension__) +#if (LZO_CC_GNUC >= 0x020800ul) +# define __lzo_gnuc_extension__ __extension__ +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_gnuc_extension__ __extension__ +#elif (LZO_CC_IBMC >= 600) +# define __lzo_gnuc_extension__ __extension__ +#endif +#endif +#if !defined(__lzo_gnuc_extension__) +# define __lzo_gnuc_extension__ /*empty*/ +#endif +#if !defined(lzo_has_builtin) +#if (LZO_CC_CLANG) && defined(__has_builtin) +# define lzo_has_builtin __has_builtin +#endif +#endif +#if !defined(lzo_has_builtin) +# define lzo_has_builtin(x) 0 +#endif +#if !defined(lzo_has_attribute) +#if (LZO_CC_CLANG) && defined(__has_attribute) +# define lzo_has_attribute __has_attribute +#endif +#endif +#if !defined(lzo_has_attribute) +# define lzo_has_attribute(x) 0 +#endif +#if !defined(lzo_has_declspec_attribute) +#if (LZO_CC_CLANG) && defined(__has_declspec_attribute) +# define lzo_has_declspec_attribute __has_declspec_attribute +#endif +#endif +#if !defined(lzo_has_declspec_attribute) +# define lzo_has_declspec_attribute(x) 0 +#endif +#if !defined(lzo_has_feature) +#if (LZO_CC_CLANG) && defined(__has_feature) +# define lzo_has_feature __has_feature +#endif +#endif +#if !defined(lzo_has_feature) +# define lzo_has_feature(x) 0 +#endif +#if !defined(lzo_has_extension) +#if (LZO_CC_CLANG) && defined(__has_extension) +# define lzo_has_extension __has_extension +#elif (LZO_CC_CLANG) && defined(__has_feature) +# define lzo_has_extension __has_feature +#endif +#endif +#if !defined(lzo_has_extension) +# define lzo_has_extension(x) 0 +#endif +#if !defined(LZO_CFG_USE_NEW_STYLE_CASTS) && defined(__cplusplus) && 0 +# if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020800ul)) +# define LZO_CFG_USE_NEW_STYLE_CASTS 0 +# elif (LZO_CC_INTELC && (__INTEL_COMPILER < 1200)) +# define LZO_CFG_USE_NEW_STYLE_CASTS 0 +# else +# define LZO_CFG_USE_NEW_STYLE_CASTS 1 +# endif +#endif +#if !defined(LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_CFG_USE_NEW_STYLE_CASTS 0 +#endif +#if !defined(__cplusplus) +# if defined(LZO_CFG_USE_NEW_STYLE_CASTS) +# undef LZO_CFG_USE_NEW_STYLE_CASTS +# endif +# define LZO_CFG_USE_NEW_STYLE_CASTS 0 +#endif +#if !defined(LZO_REINTERPRET_CAST) +# if (LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_REINTERPRET_CAST(t,e) (reinterpret_cast (e)) +# endif +#endif +#if !defined(LZO_REINTERPRET_CAST) +# define LZO_REINTERPRET_CAST(t,e) ((t) (e)) +#endif +#if !defined(LZO_STATIC_CAST) +# if (LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_STATIC_CAST(t,e) (static_cast (e)) +# endif +#endif +#if !defined(LZO_STATIC_CAST) +# define LZO_STATIC_CAST(t,e) ((t) (e)) +#endif +#if !defined(LZO_STATIC_CAST2) +# define LZO_STATIC_CAST2(t1,t2,e) LZO_STATIC_CAST(t1, LZO_STATIC_CAST(t2, e)) +#endif +#if !defined(LZO_UNCONST_CAST) +# if (LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_UNCONST_CAST(t,e) (const_cast (e)) +# elif (LZO_HAVE_MM_HUGE_PTR) +# define LZO_UNCONST_CAST(t,e) ((t) (e)) +# elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define LZO_UNCONST_CAST(t,e) ((t) ((void *) ((lzo_uintptr_t) ((const void *) (e))))) +# endif +#endif +#if !defined(LZO_UNCONST_CAST) +# define LZO_UNCONST_CAST(t,e) ((t) ((void *) ((const void *) (e)))) +#endif +#if !defined(LZO_UNCONST_VOLATILE_CAST) +# if (LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_UNCONST_VOLATILE_CAST(t,e) (const_cast (e)) +# elif (LZO_HAVE_MM_HUGE_PTR) +# define LZO_UNCONST_VOLATILE_CAST(t,e) ((t) (e)) +# elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define LZO_UNCONST_VOLATILE_CAST(t,e) ((t) ((volatile void *) ((lzo_uintptr_t) ((volatile const void *) (e))))) +# endif +#endif +#if !defined(LZO_UNCONST_VOLATILE_CAST) +# define LZO_UNCONST_VOLATILE_CAST(t,e) ((t) ((volatile void *) ((volatile const void *) (e)))) +#endif +#if !defined(LZO_UNVOLATILE_CAST) +# if (LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_UNVOLATILE_CAST(t,e) (const_cast (e)) +# elif (LZO_HAVE_MM_HUGE_PTR) +# define LZO_UNVOLATILE_CAST(t,e) ((t) (e)) +# elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define LZO_UNVOLATILE_CAST(t,e) ((t) ((void *) ((lzo_uintptr_t) ((volatile void *) (e))))) +# endif +#endif +#if !defined(LZO_UNVOLATILE_CAST) +# define LZO_UNVOLATILE_CAST(t,e) ((t) ((void *) ((volatile void *) (e)))) +#endif +#if !defined(LZO_UNVOLATILE_CONST_CAST) +# if (LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_UNVOLATILE_CONST_CAST(t,e) (const_cast (e)) +# elif (LZO_HAVE_MM_HUGE_PTR) +# define LZO_UNVOLATILE_CONST_CAST(t,e) ((t) (e)) +# elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define LZO_UNVOLATILE_CONST_CAST(t,e) ((t) ((const void *) ((lzo_uintptr_t) ((volatile const void *) (e))))) +# endif +#endif +#if !defined(LZO_UNVOLATILE_CONST_CAST) +# define LZO_UNVOLATILE_CONST_CAST(t,e) ((t) ((const void *) ((volatile const void *) (e)))) +#endif +#if !defined(LZO_PCAST) +# if (LZO_HAVE_MM_HUGE_PTR) +# define LZO_PCAST(t,e) ((t) (e)) +# endif +#endif +#if !defined(LZO_PCAST) +# define LZO_PCAST(t,e) LZO_STATIC_CAST(t, LZO_STATIC_CAST(void *, e)) +#endif +#if !defined(LZO_CCAST) +# if (LZO_HAVE_MM_HUGE_PTR) +# define LZO_CCAST(t,e) ((t) (e)) +# endif +#endif +#if !defined(LZO_CCAST) +# define LZO_CCAST(t,e) LZO_STATIC_CAST(t, LZO_STATIC_CAST(const void *, e)) +#endif +#if !defined(LZO_ICONV) +# define LZO_ICONV(t,e) LZO_STATIC_CAST(t, e) +#endif +#if !defined(LZO_ICAST) +# define LZO_ICAST(t,e) LZO_STATIC_CAST(t, e) +#endif +#if !defined(LZO_ITRUNC) +# define LZO_ITRUNC(t,e) LZO_STATIC_CAST(t, e) +#endif +#if !defined(__lzo_cte) +# if (LZO_CC_MSC || LZO_CC_WATCOMC) +# define __lzo_cte(e) ((void)0,(e)) +# elif 1 +# define __lzo_cte(e) ((void)0,(e)) +# endif +#endif +#if !defined(__lzo_cte) +# define __lzo_cte(e) (e) +#endif +#if !defined(LZO_BLOCK_BEGIN) +# define LZO_BLOCK_BEGIN do { +# define LZO_BLOCK_END } while __lzo_cte(0) +#endif +#if !defined(LZO_UNUSED) +# if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0600)) +# define LZO_UNUSED(var) ((void) &var) +# elif (LZO_CC_BORLANDC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PELLESC || LZO_CC_TURBOC) +# define LZO_UNUSED(var) if (&var) ; else +# elif (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x030200ul)) +# define LZO_UNUSED(var) ((void) &var) +# elif (LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define LZO_UNUSED(var) ((void) var) +# elif (LZO_CC_MSC && (_MSC_VER < 900)) +# define LZO_UNUSED(var) if (&var) ; else +# elif (LZO_CC_KEILC) +# define LZO_UNUSED(var) {extern int lzo_unused__[1-2*!(sizeof(var)>0)]; (void)lzo_unused__;} +# elif (LZO_CC_PACIFICC) +# define LZO_UNUSED(var) ((void) sizeof(var)) +# elif (LZO_CC_WATCOMC) && defined(__cplusplus) +# define LZO_UNUSED(var) ((void) var) +# else +# define LZO_UNUSED(var) ((void) &var) +# endif +#endif +#if !defined(LZO_UNUSED_RESULT) +# define LZO_UNUSED_RESULT(var) LZO_UNUSED(var) +#endif +#if !defined(LZO_UNUSED_FUNC) +# if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0600)) +# define LZO_UNUSED_FUNC(func) ((void) func) +# elif (LZO_CC_BORLANDC || LZO_CC_NDPC || LZO_CC_TURBOC) +# define LZO_UNUSED_FUNC(func) if (func) ; else +# elif (LZO_CC_CLANG || LZO_CC_LLVM) +# define LZO_UNUSED_FUNC(func) ((void) &func) +# elif (LZO_CC_MSC && (_MSC_VER < 900)) +# define LZO_UNUSED_FUNC(func) if (func) ; else +# elif (LZO_CC_MSC) +# define LZO_UNUSED_FUNC(func) ((void) &func) +# elif (LZO_CC_KEILC || LZO_CC_PELLESC) +# define LZO_UNUSED_FUNC(func) {extern int lzo_unused__[1-2*!(sizeof((int)func)>0)]; (void)lzo_unused__;} +# else +# define LZO_UNUSED_FUNC(func) ((void) func) +# endif +#endif +#if !defined(LZO_UNUSED_LABEL) +# if (LZO_CC_CLANG >= 0x020800ul) +# define LZO_UNUSED_LABEL(l) (__lzo_gnuc_extension__ ((void) ((const void *) &&l))) +# elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_INTELC || LZO_CC_WATCOMC) +# define LZO_UNUSED_LABEL(l) if __lzo_cte(0) goto l +# else +# define LZO_UNUSED_LABEL(l) switch (0) case 1:goto l +# endif +#endif +#if !defined(LZO_DEFINE_UNINITIALIZED_VAR) +# if 0 +# define LZO_DEFINE_UNINITIALIZED_VAR(type,var,init) type var +# elif 0 && (LZO_CC_GNUC) +# define LZO_DEFINE_UNINITIALIZED_VAR(type,var,init) type var = var +# else +# define LZO_DEFINE_UNINITIALIZED_VAR(type,var,init) type var = init +# endif +#endif +#if !defined(__lzo_inline) +#if (LZO_CC_TURBOC && (__TURBOC__ <= 0x0295)) +#elif defined(__cplusplus) +# define __lzo_inline inline +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__-0 >= 199901L) +# define __lzo_inline inline +#elif (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0550)) +# define __lzo_inline __inline +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CILLY || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) +# define __lzo_inline __inline__ +#elif (LZO_CC_DMC) +# define __lzo_inline __inline +#elif (LZO_CC_GHS) +# define __lzo_inline __inline__ +#elif (LZO_CC_IBMC >= 600) +# define __lzo_inline __inline__ +#elif (LZO_CC_INTELC) +# define __lzo_inline __inline +#elif (LZO_CC_MWERKS && (__MWERKS__ >= 0x2405)) +# define __lzo_inline __inline +#elif (LZO_CC_MSC && (_MSC_VER >= 900)) +# define __lzo_inline __inline +#elif (LZO_CC_SUNPROC >= 0x5100) +# define __lzo_inline __inline__ +#endif +#endif +#if defined(__lzo_inline) +# ifndef __lzo_HAVE_inline +# define __lzo_HAVE_inline 1 +# endif +#else +# define __lzo_inline /*empty*/ +#endif +#if !defined(__lzo_forceinline) +#if (LZO_CC_GNUC >= 0x030200ul) +# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) +#elif (LZO_CC_IBMC >= 700) +# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) +#elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 450)) +# define __lzo_forceinline __forceinline +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) +# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) +#elif (LZO_CC_MSC && (_MSC_VER >= 1200)) +# define __lzo_forceinline __forceinline +#elif (LZO_CC_PGI >= 0x0d0a00ul) +# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) +#elif (LZO_CC_SUNPROC >= 0x5100) +# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) +#endif +#endif +#if defined(__lzo_forceinline) +# ifndef __lzo_HAVE_forceinline +# define __lzo_HAVE_forceinline 1 +# endif +#else +# define __lzo_forceinline __lzo_inline +#endif +#if !defined(__lzo_noinline) +#if 1 && (LZO_ARCH_I386) && (LZO_CC_GNUC >= 0x040000ul) && (LZO_CC_GNUC < 0x040003ul) +# define __lzo_noinline __attribute__((__noinline__,__used__)) +#elif (LZO_CC_GNUC >= 0x030200ul) +# define __lzo_noinline __attribute__((__noinline__)) +#elif (LZO_CC_IBMC >= 700) +# define __lzo_noinline __attribute__((__noinline__)) +#elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 600)) +# define __lzo_noinline __declspec(noinline) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) +# define __lzo_noinline __attribute__((__noinline__)) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_noinline __attribute__((__noinline__)) +#elif (LZO_CC_MSC && (_MSC_VER >= 1300)) +# define __lzo_noinline __declspec(noinline) +#elif (LZO_CC_MWERKS && (__MWERKS__ >= 0x3200) && (LZO_OS_WIN32 || LZO_OS_WIN64)) +# if defined(__cplusplus) +# else +# define __lzo_noinline __declspec(noinline) +# endif +#elif (LZO_CC_PGI >= 0x0d0a00ul) +# define __lzo_noinline __attribute__((__noinline__)) +#elif (LZO_CC_SUNPROC >= 0x5100) +# define __lzo_noinline __attribute__((__noinline__)) +#endif +#endif +#if defined(__lzo_noinline) +# ifndef __lzo_HAVE_noinline +# define __lzo_HAVE_noinline 1 +# endif +#else +# define __lzo_noinline /*empty*/ +#endif +#if (__lzo_HAVE_forceinline || __lzo_HAVE_noinline) && !(__lzo_HAVE_inline) +# error "unexpected configuration - check your compiler defines" +#endif +#if !defined(__lzo_static_inline) +#if (LZO_CC_IBMC) +# define __lzo_static_inline __lzo_gnuc_extension__ static __lzo_inline +#endif +#endif +#if !defined(__lzo_static_inline) +# define __lzo_static_inline static __lzo_inline +#endif +#if !defined(__lzo_static_forceinline) +#if (LZO_CC_IBMC) +# define __lzo_static_forceinline __lzo_gnuc_extension__ static __lzo_forceinline +#endif +#endif +#if !defined(__lzo_static_forceinline) +# define __lzo_static_forceinline static __lzo_forceinline +#endif +#if !defined(__lzo_static_noinline) +#if (LZO_CC_IBMC) +# define __lzo_static_noinline __lzo_gnuc_extension__ static __lzo_noinline +#endif +#endif +#if !defined(__lzo_static_noinline) +# define __lzo_static_noinline static __lzo_noinline +#endif +#if !defined(__lzo_c99_extern_inline) +#if defined(__GNUC_GNU_INLINE__) +# define __lzo_c99_extern_inline __lzo_inline +#elif defined(__GNUC_STDC_INLINE__) +# define __lzo_c99_extern_inline extern __lzo_inline +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__-0 >= 199901L) +# define __lzo_c99_extern_inline extern __lzo_inline +#endif +#if !defined(__lzo_c99_extern_inline) && (__lzo_HAVE_inline) +# define __lzo_c99_extern_inline __lzo_inline +#endif +#endif +#if defined(__lzo_c99_extern_inline) +# ifndef __lzo_HAVE_c99_extern_inline +# define __lzo_HAVE_c99_extern_inline 1 +# endif +#else +# define __lzo_c99_extern_inline /*empty*/ +#endif +#if !defined(__lzo_may_alias) +#if (LZO_CC_GNUC >= 0x030400ul) +# define __lzo_may_alias __attribute__((__may_alias__)) +#elif (LZO_CC_CLANG >= 0x020900ul) +# define __lzo_may_alias __attribute__((__may_alias__)) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 1210)) && 0 +# define __lzo_may_alias __attribute__((__may_alias__)) +#elif (LZO_CC_PGI >= 0x0d0a00ul) && 0 +# define __lzo_may_alias __attribute__((__may_alias__)) +#endif +#endif +#if defined(__lzo_may_alias) +# ifndef __lzo_HAVE_may_alias +# define __lzo_HAVE_may_alias 1 +# endif +#else +# define __lzo_may_alias /*empty*/ +#endif +#if !defined(__lzo_noreturn) +#if (LZO_CC_GNUC >= 0x020700ul) +# define __lzo_noreturn __attribute__((__noreturn__)) +#elif (LZO_CC_IBMC >= 700) +# define __lzo_noreturn __attribute__((__noreturn__)) +#elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 450)) +# define __lzo_noreturn __declspec(noreturn) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 600)) +# define __lzo_noreturn __attribute__((__noreturn__)) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_noreturn __attribute__((__noreturn__)) +#elif (LZO_CC_MSC && (_MSC_VER >= 1200)) +# define __lzo_noreturn __declspec(noreturn) +#elif (LZO_CC_PGI >= 0x0d0a00ul) +# define __lzo_noreturn __attribute__((__noreturn__)) +#endif +#endif +#if defined(__lzo_noreturn) +# ifndef __lzo_HAVE_noreturn +# define __lzo_HAVE_noreturn 1 +# endif +#else +# define __lzo_noreturn /*empty*/ +#endif +#if !defined(__lzo_nothrow) +#if (LZO_CC_GNUC >= 0x030300ul) +# define __lzo_nothrow __attribute__((__nothrow__)) +#elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 450)) && defined(__cplusplus) +# define __lzo_nothrow __declspec(nothrow) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 900)) +# define __lzo_nothrow __attribute__((__nothrow__)) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_nothrow __attribute__((__nothrow__)) +#elif (LZO_CC_MSC && (_MSC_VER >= 1200)) && defined(__cplusplus) +# define __lzo_nothrow __declspec(nothrow) +#endif +#endif +#if defined(__lzo_nothrow) +# ifndef __lzo_HAVE_nothrow +# define __lzo_HAVE_nothrow 1 +# endif +#else +# define __lzo_nothrow /*empty*/ +#endif +#if !defined(__lzo_restrict) +#if (LZO_CC_GNUC >= 0x030400ul) +# define __lzo_restrict __restrict__ +#elif (LZO_CC_IBMC >= 800) && !defined(__cplusplus) +# define __lzo_restrict __restrict__ +#elif (LZO_CC_IBMC >= 1210) +# define __lzo_restrict __restrict__ +#elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 600)) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 600)) +# define __lzo_restrict __restrict__ +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM) +# define __lzo_restrict __restrict__ +#elif (LZO_CC_MSC && (_MSC_VER >= 1400)) +# define __lzo_restrict __restrict +#elif (LZO_CC_PGI >= 0x0d0a00ul) +# define __lzo_restrict __restrict__ +#endif +#endif +#if defined(__lzo_restrict) +# ifndef __lzo_HAVE_restrict +# define __lzo_HAVE_restrict 1 +# endif +#else +# define __lzo_restrict /*empty*/ +#endif +#if !defined(__lzo_alignof) +#if (LZO_CC_ARMCC || LZO_CC_CILLY || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) +# define __lzo_alignof(e) __alignof__(e) +#elif (LZO_CC_GHS) && !defined(__cplusplus) +# define __lzo_alignof(e) __alignof__(e) +#elif (LZO_CC_IBMC >= 600) +# define __lzo_alignof(e) (__lzo_gnuc_extension__ __alignof__(e)) +#elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 700)) +# define __lzo_alignof(e) __alignof__(e) +#elif (LZO_CC_MSC && (_MSC_VER >= 1300)) +# define __lzo_alignof(e) __alignof(e) +#elif (LZO_CC_SUNPROC >= 0x5100) +# define __lzo_alignof(e) __alignof__(e) +#endif +#endif +#if defined(__lzo_alignof) +# ifndef __lzo_HAVE_alignof +# define __lzo_HAVE_alignof 1 +# endif +#endif +#if !defined(__lzo_struct_packed) +#if (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020800ul)) && defined(__cplusplus) +#elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020700ul)) +#elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020800ul)) && defined(__cplusplus) +#elif (LZO_CC_PCC && (LZO_CC_PCC < 0x010100ul)) +#elif (LZO_CC_SUNPROC && (LZO_CC_SUNPROC < 0x5110)) && !defined(__cplusplus) +#elif (LZO_CC_GNUC >= 0x030400ul) && !(LZO_CC_PCC_GNUC) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) +# define __lzo_struct_packed(s) struct s { +# define __lzo_struct_packed_end() } __attribute__((__gcc_struct__,__packed__)); +# define __lzo_struct_packed_ma_end() } __lzo_may_alias __attribute__((__gcc_struct__,__packed__)); +#elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || (LZO_CC_PGI >= 0x0d0a00ul) || (LZO_CC_SUNPROC >= 0x5100)) +# define __lzo_struct_packed(s) struct s { +# define __lzo_struct_packed_end() } __attribute__((__packed__)); +# define __lzo_struct_packed_ma_end() } __lzo_may_alias __attribute__((__packed__)); +#elif (LZO_CC_IBMC >= 700) +# define __lzo_struct_packed(s) __lzo_gnuc_extension__ struct s { +# define __lzo_struct_packed_end() } __attribute__((__packed__)); +# define __lzo_struct_packed_ma_end() } __lzo_may_alias __attribute__((__packed__)); +#elif (LZO_CC_INTELC_MSC) || (LZO_CC_MSC && (_MSC_VER >= 1300)) +# define __lzo_struct_packed(s) __pragma(pack(push,1)) struct s { +# define __lzo_struct_packed_end() } __pragma(pack(pop)); +#elif (LZO_CC_WATCOMC && (__WATCOMC__ >= 900)) +# define __lzo_struct_packed(s) _Packed struct s { +# define __lzo_struct_packed_end() }; +#endif +#endif +#if defined(__lzo_struct_packed) && !defined(__lzo_struct_packed_ma) +# define __lzo_struct_packed_ma(s) __lzo_struct_packed(s) +#endif +#if defined(__lzo_struct_packed_end) && !defined(__lzo_struct_packed_ma_end) +# define __lzo_struct_packed_ma_end() __lzo_struct_packed_end() +#endif +#if !defined(__lzo_byte_struct) +#if defined(__lzo_struct_packed) +# define __lzo_byte_struct(s,n) __lzo_struct_packed(s) unsigned char a[n]; __lzo_struct_packed_end() +# define __lzo_byte_struct_ma(s,n) __lzo_struct_packed_ma(s) unsigned char a[n]; __lzo_struct_packed_ma_end() +#elif (LZO_CC_CILLY || LZO_CC_CLANG || LZO_CC_PGI || (LZO_CC_SUNPROC >= 0x5100)) +# define __lzo_byte_struct(s,n) struct s { unsigned char a[n]; } __attribute__((__packed__)); +# define __lzo_byte_struct_ma(s,n) struct s { unsigned char a[n]; } __lzo_may_alias __attribute__((__packed__)); +#endif +#endif +#if defined(__lzo_byte_struct) && !defined(__lzo_byte_struct_ma) +# define __lzo_byte_struct_ma(s,n) __lzo_byte_struct(s,n) +#endif +#if !defined(__lzo_struct_align16) && (__lzo_HAVE_alignof) +#if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x030000ul)) +#elif (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020800ul)) && defined(__cplusplus) +#elif (LZO_CC_CILLY || LZO_CC_PCC) +#elif (LZO_CC_INTELC_MSC) || (LZO_CC_MSC && (_MSC_VER >= 1300)) +# define __lzo_struct_align16(s) struct __declspec(align(16)) s { +# define __lzo_struct_align16_end() }; +# define __lzo_struct_align32(s) struct __declspec(align(32)) s { +# define __lzo_struct_align32_end() }; +# define __lzo_struct_align64(s) struct __declspec(align(64)) s { +# define __lzo_struct_align64_end() }; +#elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_GNUC || (LZO_CC_IBMC >= 700) || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_struct_align16(s) struct s { +# define __lzo_struct_align16_end() } __attribute__((__aligned__(16))); +# define __lzo_struct_align32(s) struct s { +# define __lzo_struct_align32_end() } __attribute__((__aligned__(32))); +# define __lzo_struct_align64(s) struct s { +# define __lzo_struct_align64_end() } __attribute__((__aligned__(64))); +#endif +#endif +#if !defined(__lzo_union_um) +#if (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020800ul)) && defined(__cplusplus) +#elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020700ul)) +#elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020800ul)) && defined(__cplusplus) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER < 810)) +#elif (LZO_CC_PCC && (LZO_CC_PCC < 0x010100ul)) +#elif (LZO_CC_SUNPROC && (LZO_CC_SUNPROC < 0x5110)) && !defined(__cplusplus) +#elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || (LZO_CC_PGI >= 0x0d0a00ul) || (LZO_CC_SUNPROC >= 0x5100)) +# define __lzo_union_am(s) union s { +# define __lzo_union_am_end() } __lzo_may_alias; +# define __lzo_union_um(s) union s { +# define __lzo_union_um_end() } __lzo_may_alias __attribute__((__packed__)); +#elif (LZO_CC_IBMC >= 700) +# define __lzo_union_am(s) __lzo_gnuc_extension__ union s { +# define __lzo_union_am_end() } __lzo_may_alias; +# define __lzo_union_um(s) __lzo_gnuc_extension__ union s { +# define __lzo_union_um_end() } __lzo_may_alias __attribute__((__packed__)); +#elif (LZO_CC_INTELC_MSC) || (LZO_CC_MSC && (_MSC_VER >= 1300)) +# define __lzo_union_um(s) __pragma(pack(push,1)) union s { +# define __lzo_union_um_end() } __pragma(pack(pop)); +#elif (LZO_CC_WATCOMC && (__WATCOMC__ >= 900)) +# define __lzo_union_um(s) _Packed union s { +# define __lzo_union_um_end() }; +#endif +#endif +#if !defined(__lzo_union_am) +# define __lzo_union_am(s) union s { +# define __lzo_union_am_end() }; +#endif +#if !defined(__lzo_constructor) +#if (LZO_CC_GNUC >= 0x030400ul) +# define __lzo_constructor __attribute__((__constructor__,__used__)) +#elif (LZO_CC_GNUC >= 0x020700ul) +# define __lzo_constructor __attribute__((__constructor__)) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) +# define __lzo_constructor __attribute__((__constructor__,__used__)) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_constructor __attribute__((__constructor__)) +#endif +#endif +#if defined(__lzo_constructor) +# ifndef __lzo_HAVE_constructor +# define __lzo_HAVE_constructor 1 +# endif +#endif +#if !defined(__lzo_destructor) +#if (LZO_CC_GNUC >= 0x030400ul) +# define __lzo_destructor __attribute__((__destructor__,__used__)) +#elif (LZO_CC_GNUC >= 0x020700ul) +# define __lzo_destructor __attribute__((__destructor__)) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) +# define __lzo_destructor __attribute__((__destructor__,__used__)) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_destructor __attribute__((__destructor__)) +#endif +#endif +#if defined(__lzo_destructor) +# ifndef __lzo_HAVE_destructor +# define __lzo_HAVE_destructor 1 +# endif +#endif +#if (__lzo_HAVE_destructor) && !(__lzo_HAVE_constructor) +# error "unexpected configuration - check your compiler defines" +#endif +#if !defined(__lzo_likely) && !defined(__lzo_unlikely) +#if (LZO_CC_GNUC >= 0x030200ul) +# define __lzo_likely(e) (__builtin_expect(!!(e),1)) +# define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) +#elif (LZO_CC_IBMC >= 1010) +# define __lzo_likely(e) (__builtin_expect(!!(e),1)) +# define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) +#elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 800)) +# define __lzo_likely(e) (__builtin_expect(!!(e),1)) +# define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) +#elif (LZO_CC_CLANG && LZO_CC_CLANG_C2) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_likely(e) (__builtin_expect(!!(e),1)) +# define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) +#endif +#endif +#if defined(__lzo_likely) +# ifndef __lzo_HAVE_likely +# define __lzo_HAVE_likely 1 +# endif +#else +# define __lzo_likely(e) (e) +#endif +#if defined(__lzo_very_likely) +# ifndef __lzo_HAVE_very_likely +# define __lzo_HAVE_very_likely 1 +# endif +#else +# define __lzo_very_likely(e) __lzo_likely(e) +#endif +#if defined(__lzo_unlikely) +# ifndef __lzo_HAVE_unlikely +# define __lzo_HAVE_unlikely 1 +# endif +#else +# define __lzo_unlikely(e) (e) +#endif +#if defined(__lzo_very_unlikely) +# ifndef __lzo_HAVE_very_unlikely +# define __lzo_HAVE_very_unlikely 1 +# endif +#else +# define __lzo_very_unlikely(e) __lzo_unlikely(e) +#endif +#if !defined(__lzo_loop_forever) +# if (LZO_CC_IBMC) +# define __lzo_loop_forever() LZO_BLOCK_BEGIN for (;;) { ; } LZO_BLOCK_END +# else +# define __lzo_loop_forever() do { ; } while __lzo_cte(1) +# endif +#endif +#if !defined(__lzo_unreachable) +#if (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x020800ul)) && lzo_has_builtin(__builtin_unreachable) +# define __lzo_unreachable() __builtin_unreachable(); +#elif (LZO_CC_GNUC >= 0x040500ul) +# define __lzo_unreachable() __builtin_unreachable(); +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 1300)) && 1 +# define __lzo_unreachable() __builtin_unreachable(); +#endif +#endif +#if defined(__lzo_unreachable) +# ifndef __lzo_HAVE_unreachable +# define __lzo_HAVE_unreachable 1 +# endif +#else +# if 0 +# define __lzo_unreachable() ((void)0); +# else +# define __lzo_unreachable() __lzo_loop_forever(); +# endif +#endif +#if !defined(lzo_unused_funcs_impl) +# if 1 && (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || (LZO_CC_GNUC >= 0x020700ul) || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) +# define lzo_unused_funcs_impl(r,f) static r __attribute__((__unused__)) f +# elif 1 && (LZO_CC_BORLANDC || LZO_CC_GNUC) +# define lzo_unused_funcs_impl(r,f) static r f +# else +# define lzo_unused_funcs_impl(r,f) __lzo_static_forceinline r f +# endif +#endif +#ifndef __LZO_CTA_NAME +#if (LZO_CFG_USE_COUNTER) +# define __LZO_CTA_NAME(a) LZO_PP_ECONCAT2(a,__COUNTER__) +#else +# define __LZO_CTA_NAME(a) LZO_PP_ECONCAT2(a,__LINE__) +#endif +#endif +#if !defined(LZO_COMPILE_TIME_ASSERT_HEADER) +# if (LZO_CC_AZTECC || LZO_CC_ZORTECHC) +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1-!(e)]; LZO_EXTERN_C_END +# elif (LZO_CC_DMC || LZO_CC_SYMANTECC) +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1u-2*!(e)]; LZO_EXTERN_C_END +# elif (LZO_CC_TURBOC && (__TURBOC__ == 0x0295)) +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1-!(e)]; LZO_EXTERN_C_END +# elif (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020900ul)) && defined(__cplusplus) +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN int __LZO_CTA_NAME(lzo_cta_f__)(int [1-2*!(e)]); LZO_EXTERN_C_END +# elif (LZO_CC_GNUC) && defined(__CHECKER__) && defined(__SPARSE_CHECKER__) +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN enum {__LZO_CTA_NAME(lzo_cta_e__)=1/!!(e)} __attribute__((__unused__)); LZO_EXTERN_C_END +# else +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1-2*!(e)]; LZO_EXTERN_C_END +# endif +#endif +#if !defined(LZO_COMPILE_TIME_ASSERT) +# if (LZO_CC_AZTECC) +# define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-!(e)];} +# elif (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x030000ul)) +# define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-2*!(e)] __attribute__((__unused__));} +# elif (LZO_CC_DMC || LZO_CC_PACIFICC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) +# define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; +# elif (LZO_CC_GNUC) && defined(__CHECKER__) && defined(__SPARSE_CHECKER__) +# define LZO_COMPILE_TIME_ASSERT(e) {(void) (0/!!(e));} +# elif (LZO_CC_GNUC >= 0x040700ul) && (LZO_CFG_USE_COUNTER) && defined(__cplusplus) +# define LZO_COMPILE_TIME_ASSERT(e) {enum {__LZO_CTA_NAME(lzo_cta_e__)=1/!!(e)} __attribute__((__unused__));} +# elif (LZO_CC_GNUC >= 0x040700ul) +# define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-2*!(e)] __attribute__((__unused__));} +# elif (LZO_CC_MSC && (_MSC_VER < 900)) +# define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; +# elif (LZO_CC_TURBOC && (__TURBOC__ == 0x0295)) +# define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; +# else +# define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-2*!(e)];} +# endif +#endif +#if (LZO_LANG_ASSEMBLER) +# undef LZO_COMPILE_TIME_ASSERT_HEADER +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) /*empty*/ +#else +LZO_COMPILE_TIME_ASSERT_HEADER(1 == 1) +#if defined(__cplusplus) +extern "C" { LZO_COMPILE_TIME_ASSERT_HEADER(2 == 2) } +#endif +LZO_COMPILE_TIME_ASSERT_HEADER(3 == 3) +#endif +#if (LZO_ARCH_I086 || LZO_ARCH_I386) && (LZO_OS_DOS16 || LZO_OS_DOS32 || LZO_OS_OS2 || LZO_OS_OS216 || LZO_OS_WIN16 || LZO_OS_WIN32 || LZO_OS_WIN64) +# if (LZO_CC_GNUC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PACIFICC) +# elif (LZO_CC_DMC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) +# define __lzo_cdecl __cdecl +# define __lzo_cdecl_atexit /*empty*/ +# define __lzo_cdecl_main __cdecl +# if (LZO_OS_OS2 && (LZO_CC_DMC || LZO_CC_SYMANTECC)) +# define __lzo_cdecl_qsort __pascal +# elif (LZO_OS_OS2 && (LZO_CC_ZORTECHC)) +# define __lzo_cdecl_qsort _stdcall +# else +# define __lzo_cdecl_qsort __cdecl +# endif +# elif (LZO_CC_WATCOMC) +# define __lzo_cdecl __cdecl +# else +# define __lzo_cdecl __cdecl +# define __lzo_cdecl_atexit __cdecl +# define __lzo_cdecl_main __cdecl +# define __lzo_cdecl_qsort __cdecl +# endif +# if (LZO_CC_GNUC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PACIFICC || LZO_CC_WATCOMC) +# elif (LZO_OS_OS2 && (LZO_CC_DMC || LZO_CC_SYMANTECC)) +# define __lzo_cdecl_sighandler __pascal +# elif (LZO_OS_OS2 && (LZO_CC_ZORTECHC)) +# define __lzo_cdecl_sighandler _stdcall +# elif (LZO_CC_MSC && (_MSC_VER >= 1400)) && defined(_M_CEE_PURE) +# define __lzo_cdecl_sighandler __clrcall +# elif (LZO_CC_MSC && (_MSC_VER >= 600 && _MSC_VER < 700)) +# if defined(_DLL) +# define __lzo_cdecl_sighandler _far _cdecl _loadds +# elif defined(_MT) +# define __lzo_cdecl_sighandler _far _cdecl +# else +# define __lzo_cdecl_sighandler _cdecl +# endif +# else +# define __lzo_cdecl_sighandler __cdecl +# endif +#elif (LZO_ARCH_I386) && (LZO_CC_WATCOMC) +# define __lzo_cdecl __cdecl +#elif (LZO_ARCH_M68K && LZO_OS_TOS && (LZO_CC_PUREC || LZO_CC_TURBOC)) +# define __lzo_cdecl cdecl +#endif +#if !defined(__lzo_cdecl) +# define __lzo_cdecl /*empty*/ +#endif +#if !defined(__lzo_cdecl_atexit) +# define __lzo_cdecl_atexit /*empty*/ +#endif +#if !defined(__lzo_cdecl_main) +# define __lzo_cdecl_main /*empty*/ +#endif +#if !defined(__lzo_cdecl_qsort) +# define __lzo_cdecl_qsort /*empty*/ +#endif +#if !defined(__lzo_cdecl_sighandler) +# define __lzo_cdecl_sighandler /*empty*/ +#endif +#if !defined(__lzo_cdecl_va) +# define __lzo_cdecl_va __lzo_cdecl +#endif +#if !(LZO_CFG_NO_WINDOWS_H) +#if !defined(LZO_HAVE_WINDOWS_H) +#if (LZO_OS_CYGWIN || (LZO_OS_EMX && defined(__RSXNT__)) || LZO_OS_WIN32 || LZO_OS_WIN64) +# if (LZO_CC_WATCOMC && (__WATCOMC__ < 1000)) +# elif ((LZO_OS_WIN32 && defined(__PW32__)) && (LZO_CC_GNUC && (LZO_CC_GNUC < 0x030000ul))) +# elif ((LZO_OS_CYGWIN || defined(__MINGW32__)) && (LZO_CC_GNUC && (LZO_CC_GNUC < 0x025f00ul))) +# else +# define LZO_HAVE_WINDOWS_H 1 +# endif +#endif +#endif +#endif +#define LZO_SIZEOF_CHAR 1 +#ifndef LZO_SIZEOF_SHORT +#if defined(SIZEOF_SHORT) +# define LZO_SIZEOF_SHORT (SIZEOF_SHORT) +#elif defined(__SIZEOF_SHORT__) +# define LZO_SIZEOF_SHORT (__SIZEOF_SHORT__) +#endif +#endif +#ifndef LZO_SIZEOF_INT +#if defined(SIZEOF_INT) +# define LZO_SIZEOF_INT (SIZEOF_INT) +#elif defined(__SIZEOF_INT__) +# define LZO_SIZEOF_INT (__SIZEOF_INT__) +#endif +#endif +#ifndef LZO_SIZEOF_LONG +#if defined(SIZEOF_LONG) +# define LZO_SIZEOF_LONG (SIZEOF_LONG) +#elif defined(__SIZEOF_LONG__) +# define LZO_SIZEOF_LONG (__SIZEOF_LONG__) +#endif +#endif +#ifndef LZO_SIZEOF_LONG_LONG +#if defined(SIZEOF_LONG_LONG) +# define LZO_SIZEOF_LONG_LONG (SIZEOF_LONG_LONG) +#elif defined(__SIZEOF_LONG_LONG__) +# define LZO_SIZEOF_LONG_LONG (__SIZEOF_LONG_LONG__) +#endif +#endif +#ifndef LZO_SIZEOF___INT16 +#if defined(SIZEOF___INT16) +# define LZO_SIZEOF___INT16 (SIZEOF___INT16) +#endif +#endif +#ifndef LZO_SIZEOF___INT32 +#if defined(SIZEOF___INT32) +# define LZO_SIZEOF___INT32 (SIZEOF___INT32) +#endif +#endif +#ifndef LZO_SIZEOF___INT64 +#if defined(SIZEOF___INT64) +# define LZO_SIZEOF___INT64 (SIZEOF___INT64) +#endif +#endif +#ifndef LZO_SIZEOF_VOID_P +#if defined(SIZEOF_VOID_P) +# define LZO_SIZEOF_VOID_P (SIZEOF_VOID_P) +#elif defined(__SIZEOF_POINTER__) +# define LZO_SIZEOF_VOID_P (__SIZEOF_POINTER__) +#endif +#endif +#ifndef LZO_SIZEOF_SIZE_T +#if defined(SIZEOF_SIZE_T) +# define LZO_SIZEOF_SIZE_T (SIZEOF_SIZE_T) +#elif defined(__SIZEOF_SIZE_T__) +# define LZO_SIZEOF_SIZE_T (__SIZEOF_SIZE_T__) +#endif +#endif +#ifndef LZO_SIZEOF_PTRDIFF_T +#if defined(SIZEOF_PTRDIFF_T) +# define LZO_SIZEOF_PTRDIFF_T (SIZEOF_PTRDIFF_T) +#elif defined(__SIZEOF_PTRDIFF_T__) +# define LZO_SIZEOF_PTRDIFF_T (__SIZEOF_PTRDIFF_T__) +#endif +#endif +#define __LZO_LSR(x,b) (((x)+0ul) >> (b)) +#if !defined(LZO_SIZEOF_SHORT) +# if (LZO_ARCH_CRAY_PVP) +# define LZO_SIZEOF_SHORT 8 +# elif (USHRT_MAX == LZO_0xffffL) +# define LZO_SIZEOF_SHORT 2 +# elif (__LZO_LSR(USHRT_MAX,7) == 1) +# define LZO_SIZEOF_SHORT 1 +# elif (__LZO_LSR(USHRT_MAX,15) == 1) +# define LZO_SIZEOF_SHORT 2 +# elif (__LZO_LSR(USHRT_MAX,31) == 1) +# define LZO_SIZEOF_SHORT 4 +# elif (__LZO_LSR(USHRT_MAX,63) == 1) +# define LZO_SIZEOF_SHORT 8 +# elif (__LZO_LSR(USHRT_MAX,127) == 1) +# define LZO_SIZEOF_SHORT 16 +# else +# error "LZO_SIZEOF_SHORT" +# endif +#endif +LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_SHORT == sizeof(short)) +#if !defined(LZO_SIZEOF_INT) +# if (LZO_ARCH_CRAY_PVP) +# define LZO_SIZEOF_INT 8 +# elif (UINT_MAX == LZO_0xffffL) +# define LZO_SIZEOF_INT 2 +# elif (UINT_MAX == LZO_0xffffffffL) +# define LZO_SIZEOF_INT 4 +# elif (__LZO_LSR(UINT_MAX,7) == 1) +# define LZO_SIZEOF_INT 1 +# elif (__LZO_LSR(UINT_MAX,15) == 1) +# define LZO_SIZEOF_INT 2 +# elif (__LZO_LSR(UINT_MAX,31) == 1) +# define LZO_SIZEOF_INT 4 +# elif (__LZO_LSR(UINT_MAX,63) == 1) +# define LZO_SIZEOF_INT 8 +# elif (__LZO_LSR(UINT_MAX,127) == 1) +# define LZO_SIZEOF_INT 16 +# else +# error "LZO_SIZEOF_INT" +# endif +#endif +LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_INT == sizeof(int)) +#if !defined(LZO_SIZEOF_LONG) +# if (ULONG_MAX == LZO_0xffffffffL) +# define LZO_SIZEOF_LONG 4 +# elif (__LZO_LSR(ULONG_MAX,7) == 1) +# define LZO_SIZEOF_LONG 1 +# elif (__LZO_LSR(ULONG_MAX,15) == 1) +# define LZO_SIZEOF_LONG 2 +# elif (__LZO_LSR(ULONG_MAX,31) == 1) +# define LZO_SIZEOF_LONG 4 +# elif (__LZO_LSR(ULONG_MAX,39) == 1) +# define LZO_SIZEOF_LONG 5 +# elif (__LZO_LSR(ULONG_MAX,63) == 1) +# define LZO_SIZEOF_LONG 8 +# elif (__LZO_LSR(ULONG_MAX,127) == 1) +# define LZO_SIZEOF_LONG 16 +# else +# error "LZO_SIZEOF_LONG" +# endif +#endif +LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_LONG == sizeof(long)) +#if !defined(LZO_SIZEOF_LONG_LONG) && !defined(LZO_SIZEOF___INT64) +#if (LZO_SIZEOF_LONG > 0 && LZO_SIZEOF_LONG < 8) +# if defined(__LONG_MAX__) && defined(__LONG_LONG_MAX__) +# if (LZO_CC_GNUC >= 0x030300ul) +# if ((__LONG_MAX__-0) == (__LONG_LONG_MAX__-0)) +# define LZO_SIZEOF_LONG_LONG LZO_SIZEOF_LONG +# elif (__LZO_LSR(__LONG_LONG_MAX__,30) == 1) +# define LZO_SIZEOF_LONG_LONG 4 +# endif +# endif +# endif +#endif +#endif +#if !defined(LZO_SIZEOF_LONG_LONG) && !defined(LZO_SIZEOF___INT64) +#if (LZO_SIZEOF_LONG > 0 && LZO_SIZEOF_LONG < 8) +#if (LZO_ARCH_I086 && LZO_CC_DMC) +#elif (LZO_CC_CILLY) && defined(__GNUC__) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define LZO_SIZEOF_LONG_LONG 8 +#elif ((LZO_OS_WIN32 || LZO_OS_WIN64 || defined(_WIN32)) && LZO_CC_MSC && (_MSC_VER >= 1400)) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_OS_WIN64 || defined(_WIN64)) +# define LZO_SIZEOF___INT64 8 +#elif (LZO_ARCH_I386 && (LZO_CC_DMC)) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_ARCH_I386 && (LZO_CC_SYMANTECC && (__SC__ >= 0x700))) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_ARCH_I386 && (LZO_CC_INTELC && defined(__linux__))) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_ARCH_I386 && (LZO_CC_MWERKS || LZO_CC_PELLESC || LZO_CC_PGI || LZO_CC_SUNPROC)) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_ARCH_I386 && (LZO_CC_INTELC || LZO_CC_MSC)) +# define LZO_SIZEOF___INT64 8 +#elif ((LZO_OS_WIN32 || defined(_WIN32)) && (LZO_CC_MSC)) +# define LZO_SIZEOF___INT64 8 +#elif (LZO_ARCH_I386 && (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0520))) +# define LZO_SIZEOF___INT64 8 +#elif (LZO_ARCH_I386 && (LZO_CC_WATCOMC && (__WATCOMC__ >= 1100))) +# define LZO_SIZEOF___INT64 8 +#elif (LZO_CC_GHS && defined(__LLONG_BIT) && ((__LLONG_BIT-0) == 64)) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_CC_WATCOMC && defined(_INTEGRAL_MAX_BITS) && ((_INTEGRAL_MAX_BITS-0) == 64)) +# define LZO_SIZEOF___INT64 8 +#elif (LZO_OS_OS400 || defined(__OS400__)) && defined(__LLP64_IFC__) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (defined(__vms) || defined(__VMS)) && ((__INITIAL_POINTER_SIZE-0) == 64) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_CC_SDCC) && (LZO_SIZEOF_INT == 2) +#elif 1 && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +# define LZO_SIZEOF_LONG_LONG 8 +#endif +#endif +#endif +#if defined(__cplusplus) && (LZO_CC_GNUC) +# if (LZO_CC_GNUC < 0x020800ul) +# undef LZO_SIZEOF_LONG_LONG +# endif +#endif +#if (LZO_CFG_NO_LONG_LONG) +# undef LZO_SIZEOF_LONG_LONG +#elif defined(__NO_LONG_LONG) +# undef LZO_SIZEOF_LONG_LONG +#elif defined(_NO_LONGLONG) +# undef LZO_SIZEOF_LONG_LONG +#endif +#if !defined(LZO_WORDSIZE) +#if (LZO_ARCH_ALPHA) +# define LZO_WORDSIZE 8 +#elif (LZO_ARCH_AMD64) +# define LZO_WORDSIZE 8 +#elif (LZO_ARCH_ARM64) +# define LZO_WORDSIZE 8 +#elif (LZO_ARCH_AVR) +# define LZO_WORDSIZE 1 +#elif (LZO_ARCH_H8300) +# if defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) +# define LZO_WORDSIZE 4 +# else +# define LZO_WORDSIZE 2 +# endif +#elif (LZO_ARCH_I086) +# define LZO_WORDSIZE 2 +#elif (LZO_ARCH_IA64) +# define LZO_WORDSIZE 8 +#elif (LZO_ARCH_M16C) +# define LZO_WORDSIZE 2 +#elif (LZO_ARCH_SPU) +# define LZO_WORDSIZE 4 +#elif (LZO_ARCH_Z80) +# define LZO_WORDSIZE 1 +#elif (LZO_SIZEOF_LONG == 8) && ((defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__)) +# define LZO_WORDSIZE 8 +#elif (LZO_OS_OS400 || defined(__OS400__)) +# define LZO_WORDSIZE 8 +#elif (defined(__vms) || defined(__VMS)) && (__INITIAL_POINTER_SIZE+0 == 64) +# define LZO_WORDSIZE 8 +#endif +#endif +#if !defined(LZO_SIZEOF_VOID_P) +#if defined(__ILP32__) || defined(__ILP32) || defined(_ILP32) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(int) == 4) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 4) +# define LZO_SIZEOF_VOID_P 4 +#elif defined(__ILP64__) || defined(__ILP64) || defined(_ILP64) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(int) == 8) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 8) +# define LZO_SIZEOF_VOID_P 8 +#elif defined(__LLP64__) || defined(__LLP64) || defined(_LLP64) || defined(_WIN64) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 4) +# define LZO_SIZEOF_VOID_P 8 +#elif defined(__LP64__) || defined(__LP64) || defined(_LP64) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 8) +# define LZO_SIZEOF_VOID_P 8 +#elif (LZO_ARCH_AVR) +# define LZO_SIZEOF_VOID_P 2 +#elif (LZO_ARCH_C166 || LZO_ARCH_MCS51 || LZO_ARCH_MCS251 || LZO_ARCH_MSP430) +# define LZO_SIZEOF_VOID_P 2 +#elif (LZO_ARCH_H8300) +# if defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) + LZO_COMPILE_TIME_ASSERT_HEADER(LZO_WORDSIZE == 4) +# if defined(__NORMAL_MODE__) +# define LZO_SIZEOF_VOID_P 2 +# else +# define LZO_SIZEOF_VOID_P 4 +# endif +# else + LZO_COMPILE_TIME_ASSERT_HEADER(LZO_WORDSIZE == 2) +# define LZO_SIZEOF_VOID_P 2 +# endif +# if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x040000ul)) && (LZO_SIZEOF_INT == 4) +# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_INT +# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_INT +# endif +#elif (LZO_ARCH_I086) +# if (LZO_MM_TINY || LZO_MM_SMALL || LZO_MM_MEDIUM) +# define LZO_SIZEOF_VOID_P 2 +# elif (LZO_MM_COMPACT || LZO_MM_LARGE || LZO_MM_HUGE) +# define LZO_SIZEOF_VOID_P 4 +# else +# error "invalid LZO_ARCH_I086 memory model" +# endif +#elif (LZO_ARCH_M16C) +# if defined(__m32c_cpu__) || defined(__m32cm_cpu__) +# define LZO_SIZEOF_VOID_P 4 +# else +# define LZO_SIZEOF_VOID_P 2 +# endif +#elif (LZO_ARCH_SPU) +# define LZO_SIZEOF_VOID_P 4 +#elif (LZO_ARCH_Z80) +# define LZO_SIZEOF_VOID_P 2 +#elif (LZO_SIZEOF_LONG == 8) && ((defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__)) +# define LZO_SIZEOF_VOID_P 4 +#elif (LZO_OS_OS400 || defined(__OS400__)) +# if defined(__LLP64_IFC__) +# define LZO_SIZEOF_VOID_P 8 +# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG +# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG +# else +# define LZO_SIZEOF_VOID_P 16 +# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG +# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG +# endif +#elif (defined(__vms) || defined(__VMS)) && (__INITIAL_POINTER_SIZE+0 == 64) +# define LZO_SIZEOF_VOID_P 8 +# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG +# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG +#endif +#endif +#if !defined(LZO_SIZEOF_VOID_P) +# define LZO_SIZEOF_VOID_P LZO_SIZEOF_LONG +#endif +LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_VOID_P == sizeof(void *)) +#if !defined(LZO_SIZEOF_SIZE_T) +#if (LZO_ARCH_I086 || LZO_ARCH_M16C) +# define LZO_SIZEOF_SIZE_T 2 +#endif +#endif +#if !defined(LZO_SIZEOF_SIZE_T) +# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_VOID_P +#endif +#if defined(offsetof) +LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_SIZE_T == sizeof(size_t)) +#endif +#if !defined(LZO_SIZEOF_PTRDIFF_T) +#if (LZO_ARCH_I086) +# if (LZO_MM_TINY || LZO_MM_SMALL || LZO_MM_MEDIUM || LZO_MM_HUGE) +# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_VOID_P +# elif (LZO_MM_COMPACT || LZO_MM_LARGE) +# if (LZO_CC_BORLANDC || LZO_CC_TURBOC) +# define LZO_SIZEOF_PTRDIFF_T 4 +# else +# define LZO_SIZEOF_PTRDIFF_T 2 +# endif +# else +# error "invalid LZO_ARCH_I086 memory model" +# endif +#endif +#endif +#if !defined(LZO_SIZEOF_PTRDIFF_T) +# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_SIZE_T +#endif +#if defined(offsetof) +LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_PTRDIFF_T == sizeof(ptrdiff_t)) +#endif +#if !defined(LZO_WORDSIZE) +# define LZO_WORDSIZE LZO_SIZEOF_VOID_P +#endif +#if (LZO_ABI_NEUTRAL_ENDIAN) +# undef LZO_ABI_BIG_ENDIAN +# undef LZO_ABI_LITTLE_ENDIAN +#elif !(LZO_ABI_BIG_ENDIAN) && !(LZO_ABI_LITTLE_ENDIAN) +#if (LZO_ARCH_ALPHA) && (LZO_ARCH_CRAY_MPP) +# define LZO_ABI_BIG_ENDIAN 1 +#elif (LZO_ARCH_IA64) && (LZO_OS_POSIX_LINUX || LZO_OS_WIN64) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif (LZO_ARCH_ALPHA || LZO_ARCH_AMD64 || LZO_ARCH_BLACKFIN || LZO_ARCH_CRIS || LZO_ARCH_I086 || LZO_ARCH_I386 || LZO_ARCH_MSP430 || LZO_ARCH_RISCV) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif (LZO_ARCH_AVR32 || LZO_ARCH_M68K || LZO_ARCH_S390 || LZO_ARCH_SPU) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && defined(__IAR_SYSTEMS_ICC__) && defined(__LITTLE_ENDIAN__) +# if (__LITTLE_ENDIAN__ == 1) +# define LZO_ABI_LITTLE_ENDIAN 1 +# else +# define LZO_ABI_BIG_ENDIAN 1 +# endif +#elif 1 && defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM) && defined(__ARM_BIG_ENDIAN) && ((__ARM_BIG_ENDIAN)+0) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM) && defined(__ARMEB__) && !defined(__ARMEL__) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM) && defined(__ARMEL__) && !defined(__ARMEB__) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM) && defined(_MSC_VER) && defined(_WIN32) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM && LZO_CC_ARMCC_ARMCC) +# if defined(__BIG_ENDIAN) && defined(__LITTLE_ENDIAN) +# error "unexpected configuration - check your compiler defines" +# elif defined(__BIG_ENDIAN) +# define LZO_ABI_BIG_ENDIAN 1 +# else +# define LZO_ABI_LITTLE_ENDIAN 1 +# endif +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM64) && defined(__ARM_BIG_ENDIAN) && ((__ARM_BIG_ENDIAN)+0) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM64) && defined(__AARCH64EB__) && !defined(__AARCH64EL__) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM64) && defined(__AARCH64EL__) && !defined(__AARCH64EB__) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM64) && defined(_MSC_VER) && defined(_WIN32) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif 1 && (LZO_ARCH_MIPS) && defined(__MIPSEB__) && !defined(__MIPSEL__) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && (LZO_ARCH_MIPS) && defined(__MIPSEL__) && !defined(__MIPSEB__) +# define LZO_ABI_LITTLE_ENDIAN 1 +#endif +#endif +#if (LZO_ABI_BIG_ENDIAN) && (LZO_ABI_LITTLE_ENDIAN) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ABI_BIG_ENDIAN) +# define LZO_INFO_ABI_ENDIAN "be" +#elif (LZO_ABI_LITTLE_ENDIAN) +# define LZO_INFO_ABI_ENDIAN "le" +#elif (LZO_ABI_NEUTRAL_ENDIAN) +# define LZO_INFO_ABI_ENDIAN "neutral" +#endif +#if (LZO_SIZEOF_INT == 1 && LZO_SIZEOF_LONG == 2 && LZO_SIZEOF_VOID_P == 2) +# define LZO_ABI_I8LP16 1 +# define LZO_INFO_ABI_PM "i8lp16" +#elif (LZO_SIZEOF_INT == 2 && LZO_SIZEOF_LONG == 2 && LZO_SIZEOF_VOID_P == 2) +# define LZO_ABI_ILP16 1 +# define LZO_INFO_ABI_PM "ilp16" +#elif (LZO_SIZEOF_INT == 2 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 4) +# define LZO_ABI_LP32 1 +# define LZO_INFO_ABI_PM "lp32" +#elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 4) +# define LZO_ABI_ILP32 1 +# define LZO_INFO_ABI_PM "ilp32" +#elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 8 && LZO_SIZEOF_SIZE_T == 8) +# define LZO_ABI_LLP64 1 +# define LZO_INFO_ABI_PM "llp64" +#elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 8) +# define LZO_ABI_LP64 1 +# define LZO_INFO_ABI_PM "lp64" +#elif (LZO_SIZEOF_INT == 8 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 8) +# define LZO_ABI_ILP64 1 +# define LZO_INFO_ABI_PM "ilp64" +#elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 4) +# define LZO_ABI_IP32L64 1 +# define LZO_INFO_ABI_PM "ip32l64" +#endif +#if (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_VOID_P == 4 && LZO_WORDSIZE == 8) +# define LZO_ABI_IP32W64 1 +# ifndef LZO_INFO_ABI_PM +# define LZO_INFO_ABI_PM "ip32w64" +# endif +#endif +#if 0 +#elif !defined(__LZO_LIBC_OVERRIDE) +#if (LZO_LIBC_NAKED) +# define LZO_INFO_LIBC "naked" +#elif (LZO_LIBC_FREESTANDING) +# define LZO_INFO_LIBC "freestanding" +#elif (LZO_LIBC_MOSTLY_FREESTANDING) +# define LZO_INFO_LIBC "mfreestanding" +#elif (LZO_LIBC_ISOC90) +# define LZO_INFO_LIBC "isoc90" +#elif (LZO_LIBC_ISOC99) +# define LZO_INFO_LIBC "isoc99" +#elif (LZO_CC_ARMCC_ARMCC) && defined(__ARMCLIB_VERSION) +# define LZO_LIBC_ISOC90 1 +# define LZO_INFO_LIBC "isoc90" +#elif defined(__dietlibc__) +# define LZO_LIBC_DIETLIBC 1 +# define LZO_INFO_LIBC "dietlibc" +#elif defined(_NEWLIB_VERSION) +# define LZO_LIBC_NEWLIB 1 +# define LZO_INFO_LIBC "newlib" +#elif defined(__UCLIBC__) && defined(__UCLIBC_MAJOR__) && defined(__UCLIBC_MINOR__) +# if defined(__UCLIBC_SUBLEVEL__) +# define LZO_LIBC_UCLIBC (__UCLIBC_MAJOR__ * 0x10000L + (__UCLIBC_MINOR__-0) * 0x100 + (__UCLIBC_SUBLEVEL__-0)) +# else +# define LZO_LIBC_UCLIBC 0x00090bL +# endif +# define LZO_INFO_LIBC "uc" "libc" +#elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) +# define LZO_LIBC_GLIBC (__GLIBC__ * 0x10000L + (__GLIBC_MINOR__-0) * 0x100) +# define LZO_INFO_LIBC "glibc" +#elif (LZO_CC_MWERKS) && defined(__MSL__) +# define LZO_LIBC_MSL __MSL__ +# define LZO_INFO_LIBC "msl" +#elif 1 && defined(__IAR_SYSTEMS_ICC__) +# define LZO_LIBC_ISOC90 1 +# define LZO_INFO_LIBC "isoc90" +#else +# define LZO_LIBC_DEFAULT 1 +# define LZO_INFO_LIBC "default" +#endif +#endif +#if (LZO_ARCH_I386 && (LZO_OS_DOS32 || LZO_OS_WIN32) && (LZO_CC_DMC || LZO_CC_INTELC || LZO_CC_MSC || LZO_CC_PELLESC)) +# define LZO_ASM_SYNTAX_MSC 1 +#elif (LZO_OS_WIN64 && (LZO_CC_DMC || LZO_CC_INTELC || LZO_CC_MSC || LZO_CC_PELLESC)) +#elif (LZO_ARCH_I386 && LZO_CC_GNUC && (LZO_CC_GNUC == 0x011f00ul)) +#elif (LZO_ARCH_I386 && (LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC || LZO_CC_PATHSCALE)) +# define LZO_ASM_SYNTAX_GNUC 1 +#elif (LZO_ARCH_AMD64 && (LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC || LZO_CC_PATHSCALE)) +# define LZO_ASM_SYNTAX_GNUC 1 +#elif (LZO_CC_GNUC) +# define LZO_ASM_SYNTAX_GNUC 1 +#endif +#if (LZO_ASM_SYNTAX_GNUC) +#if (LZO_ARCH_I386 && LZO_CC_GNUC && (LZO_CC_GNUC < 0x020000ul)) +# define __LZO_ASM_CLOBBER "ax" +# define __LZO_ASM_CLOBBER_LIST_CC /*empty*/ +# define __LZO_ASM_CLOBBER_LIST_CC_MEMORY /*empty*/ +# define __LZO_ASM_CLOBBER_LIST_EMPTY /*empty*/ +#elif (LZO_CC_INTELC && (__INTEL_COMPILER < 1000)) +# define __LZO_ASM_CLOBBER "memory" +# define __LZO_ASM_CLOBBER_LIST_CC /*empty*/ +# define __LZO_ASM_CLOBBER_LIST_CC_MEMORY : "memory" +# define __LZO_ASM_CLOBBER_LIST_EMPTY /*empty*/ +#else +# define __LZO_ASM_CLOBBER "cc", "memory" +# define __LZO_ASM_CLOBBER_LIST_CC : "cc" +# define __LZO_ASM_CLOBBER_LIST_CC_MEMORY : "cc", "memory" +# define __LZO_ASM_CLOBBER_LIST_EMPTY /*empty*/ +#endif +#endif +#if (LZO_ARCH_ALPHA) +# define LZO_OPT_AVOID_UINT_INDEX 1 +#elif (LZO_ARCH_AMD64) +# define LZO_OPT_AVOID_INT_INDEX 1 +# define LZO_OPT_AVOID_UINT_INDEX 1 +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# ifndef LZO_OPT_UNALIGNED64 +# define LZO_OPT_UNALIGNED64 1 +# endif +#elif (LZO_ARCH_ARM) +# if defined(__ARM_FEATURE_UNALIGNED) +# if ((__ARM_FEATURE_UNALIGNED)+0) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# endif +# elif 1 && (LZO_ARCH_ARM_THUMB2) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# elif 1 && defined(__ARM_ARCH) && ((__ARM_ARCH)+0 >= 7) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# elif 1 && defined(__TARGET_ARCH_ARM) && ((__TARGET_ARCH_ARM)+0 >= 7) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# elif 1 && defined(__TARGET_ARCH_ARM) && ((__TARGET_ARCH_ARM)+0 >= 6) && (defined(__TARGET_PROFILE_A) || defined(__TARGET_PROFILE_R)) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# elif 1 && defined(_MSC_VER) && defined(_M_ARM) && ((_M_ARM)+0 >= 7) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# endif +#elif (LZO_ARCH_ARM64) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# ifndef LZO_OPT_UNALIGNED64 +# define LZO_OPT_UNALIGNED64 1 +# endif +#elif (LZO_ARCH_CRIS) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +#elif (LZO_ARCH_I386) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +#elif (LZO_ARCH_IA64) +# define LZO_OPT_AVOID_INT_INDEX 1 +# define LZO_OPT_AVOID_UINT_INDEX 1 +# define LZO_OPT_PREFER_POSTINC 1 +#elif (LZO_ARCH_M68K) +# define LZO_OPT_PREFER_POSTINC 1 +# define LZO_OPT_PREFER_PREDEC 1 +# if defined(__mc68020__) && !defined(__mcoldfire__) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# endif +#elif (LZO_ARCH_MIPS) +# define LZO_OPT_AVOID_UINT_INDEX 1 +#elif (LZO_ARCH_POWERPC) +# define LZO_OPT_PREFER_PREINC 1 +# define LZO_OPT_PREFER_PREDEC 1 +# if (LZO_ABI_BIG_ENDIAN) || (LZO_WORDSIZE == 8) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# if (LZO_WORDSIZE == 8) +# ifndef LZO_OPT_UNALIGNED64 +# define LZO_OPT_UNALIGNED64 1 +# endif +# endif +# endif +#elif (LZO_ARCH_RISCV) +# define LZO_OPT_AVOID_UINT_INDEX 1 +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# if (LZO_WORDSIZE == 8) +# ifndef LZO_OPT_UNALIGNED64 +# define LZO_OPT_UNALIGNED64 1 +# endif +# endif +#elif (LZO_ARCH_S390) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# if (LZO_WORDSIZE == 8) +# ifndef LZO_OPT_UNALIGNED64 +# define LZO_OPT_UNALIGNED64 1 +# endif +# endif +#elif (LZO_ARCH_SH) +# define LZO_OPT_PREFER_POSTINC 1 +# define LZO_OPT_PREFER_PREDEC 1 +#endif +#ifndef LZO_CFG_NO_INLINE_ASM +#if (LZO_ABI_NEUTRAL_ENDIAN) || (LZO_ARCH_GENERIC) +# define LZO_CFG_NO_INLINE_ASM 1 +#elif (LZO_CC_LLVM) +# define LZO_CFG_NO_INLINE_ASM 1 +#endif +#endif +#if (LZO_CFG_NO_INLINE_ASM) +# undef LZO_ASM_SYNTAX_MSC +# undef LZO_ASM_SYNTAX_GNUC +# undef __LZO_ASM_CLOBBER +# undef __LZO_ASM_CLOBBER_LIST_CC +# undef __LZO_ASM_CLOBBER_LIST_CC_MEMORY +# undef __LZO_ASM_CLOBBER_LIST_EMPTY +#endif +#ifndef LZO_CFG_NO_UNALIGNED +#if (LZO_ABI_NEUTRAL_ENDIAN) || (LZO_ARCH_GENERIC) +# define LZO_CFG_NO_UNALIGNED 1 +#endif +#endif +#if (LZO_CFG_NO_UNALIGNED) +# undef LZO_OPT_UNALIGNED16 +# undef LZO_OPT_UNALIGNED32 +# undef LZO_OPT_UNALIGNED64 +#endif +#if defined(__LZO_INFOSTR_MM) +#elif (LZO_MM_FLAT) && (defined(__LZO_INFOSTR_PM) || defined(LZO_INFO_ABI_PM)) +# define __LZO_INFOSTR_MM "" +#elif defined(LZO_INFO_MM) +# define __LZO_INFOSTR_MM "." LZO_INFO_MM +#else +# define __LZO_INFOSTR_MM "" +#endif +#if defined(__LZO_INFOSTR_PM) +#elif defined(LZO_INFO_ABI_PM) +# define __LZO_INFOSTR_PM "." LZO_INFO_ABI_PM +#else +# define __LZO_INFOSTR_PM "" +#endif +#if defined(__LZO_INFOSTR_ENDIAN) +#elif defined(LZO_INFO_ABI_ENDIAN) +# define __LZO_INFOSTR_ENDIAN "." LZO_INFO_ABI_ENDIAN +#else +# define __LZO_INFOSTR_ENDIAN "" +#endif +#if defined(__LZO_INFOSTR_OSNAME) +#elif defined(LZO_INFO_OS_CONSOLE) +# define __LZO_INFOSTR_OSNAME LZO_INFO_OS "." LZO_INFO_OS_CONSOLE +#elif defined(LZO_INFO_OS_POSIX) +# define __LZO_INFOSTR_OSNAME LZO_INFO_OS "." LZO_INFO_OS_POSIX +#else +# define __LZO_INFOSTR_OSNAME LZO_INFO_OS +#endif +#if defined(__LZO_INFOSTR_LIBC) +#elif defined(LZO_INFO_LIBC) +# define __LZO_INFOSTR_LIBC "." LZO_INFO_LIBC +#else +# define __LZO_INFOSTR_LIBC "" +#endif +#if defined(__LZO_INFOSTR_CCVER) +#elif defined(LZO_INFO_CCVER) +# define __LZO_INFOSTR_CCVER " " LZO_INFO_CCVER +#else +# define __LZO_INFOSTR_CCVER "" +#endif +#define LZO_INFO_STRING \ + LZO_INFO_ARCH __LZO_INFOSTR_MM __LZO_INFOSTR_PM __LZO_INFOSTR_ENDIAN \ + " " __LZO_INFOSTR_OSNAME __LZO_INFOSTR_LIBC " " LZO_INFO_CC __LZO_INFOSTR_CCVER +#if !(LZO_CFG_SKIP_LZO_TYPES) +#if (!(LZO_SIZEOF_SHORT+0 > 0 && LZO_SIZEOF_INT+0 > 0 && LZO_SIZEOF_LONG+0 > 0)) +# error "missing defines for sizes" +#endif +#if (!(LZO_SIZEOF_PTRDIFF_T+0 > 0 && LZO_SIZEOF_SIZE_T+0 > 0 && LZO_SIZEOF_VOID_P+0 > 0)) +# error "missing defines for sizes" +#endif +#define LZO_TYPEOF_CHAR 1u +#define LZO_TYPEOF_SHORT 2u +#define LZO_TYPEOF_INT 3u +#define LZO_TYPEOF_LONG 4u +#define LZO_TYPEOF_LONG_LONG 5u +#define LZO_TYPEOF___INT8 17u +#define LZO_TYPEOF___INT16 18u +#define LZO_TYPEOF___INT32 19u +#define LZO_TYPEOF___INT64 20u +#define LZO_TYPEOF___INT128 21u +#define LZO_TYPEOF___INT256 22u +#define LZO_TYPEOF___MODE_QI 33u +#define LZO_TYPEOF___MODE_HI 34u +#define LZO_TYPEOF___MODE_SI 35u +#define LZO_TYPEOF___MODE_DI 36u +#define LZO_TYPEOF___MODE_TI 37u +#define LZO_TYPEOF_CHAR_P 129u +#if !defined(lzo_llong_t) +#if (LZO_SIZEOF_LONG_LONG+0 > 0) +# if !(LZO_LANG_ASSEMBLER) + __lzo_gnuc_extension__ typedef long long lzo_llong_t__; + __lzo_gnuc_extension__ typedef unsigned long long lzo_ullong_t__; +# endif +# define lzo_llong_t lzo_llong_t__ +# define lzo_ullong_t lzo_ullong_t__ +#endif +#endif +#if !defined(lzo_int16e_t) +#if (LZO_CFG_PREFER_TYPEOF_ACC_INT16E_T == LZO_TYPEOF_SHORT) && (LZO_SIZEOF_SHORT != 2) +# undef LZO_CFG_PREFER_TYPEOF_ACC_INT16E_T +#endif +#if (LZO_SIZEOF_LONG == 2) && !(LZO_CFG_PREFER_TYPEOF_ACC_INT16E_T == LZO_TYPEOF_SHORT) +# define lzo_int16e_t long +# define lzo_uint16e_t unsigned long +# define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF_LONG +#elif (LZO_SIZEOF_INT == 2) && !(LZO_CFG_PREFER_TYPEOF_ACC_INT16E_T == LZO_TYPEOF_SHORT) +# define lzo_int16e_t int +# define lzo_uint16e_t unsigned int +# define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF_INT +#elif (LZO_SIZEOF_SHORT == 2) +# define lzo_int16e_t short int +# define lzo_uint16e_t unsigned short int +# define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF_SHORT +#elif 1 && !(LZO_CFG_TYPE_NO_MODE_HI) && (LZO_CC_CLANG || (LZO_CC_GNUC >= 0x025f00ul) || LZO_CC_LLVM) +# if !(LZO_LANG_ASSEMBLER) + typedef int lzo_int16e_hi_t__ __attribute__((__mode__(__HI__))); + typedef unsigned int lzo_uint16e_hi_t__ __attribute__((__mode__(__HI__))); +# endif +# define lzo_int16e_t lzo_int16e_hi_t__ +# define lzo_uint16e_t lzo_uint16e_hi_t__ +# define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF___MODE_HI +#elif (LZO_SIZEOF___INT16 == 2) +# define lzo_int16e_t __int16 +# define lzo_uint16e_t unsigned __int16 +# define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF___INT16 +#else +#endif +#endif +#if defined(lzo_int16e_t) +# define LZO_SIZEOF_LZO_INT16E_T 2 + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16e_t) == 2) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16e_t) == LZO_SIZEOF_LZO_INT16E_T) +#endif +#if !defined(lzo_int32e_t) +#if (LZO_CFG_PREFER_TYPEOF_ACC_INT32E_T == LZO_TYPEOF_INT) && (LZO_SIZEOF_INT != 4) +# undef LZO_CFG_PREFER_TYPEOF_ACC_INT32E_T +#endif +#if (LZO_SIZEOF_LONG == 4) && !(LZO_CFG_PREFER_TYPEOF_ACC_INT32E_T == LZO_TYPEOF_INT) +# define lzo_int32e_t long int +# define lzo_uint32e_t unsigned long int +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_LONG +#elif (LZO_SIZEOF_INT == 4) +# define lzo_int32e_t int +# define lzo_uint32e_t unsigned int +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_INT +#elif (LZO_SIZEOF_SHORT == 4) +# define lzo_int32e_t short int +# define lzo_uint32e_t unsigned short int +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_SHORT +#elif (LZO_SIZEOF_LONG_LONG == 4) +# define lzo_int32e_t lzo_llong_t +# define lzo_uint32e_t lzo_ullong_t +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_LONG_LONG +#elif 1 && !(LZO_CFG_TYPE_NO_MODE_SI) && (LZO_CC_CLANG || (LZO_CC_GNUC >= 0x025f00ul) || LZO_CC_LLVM) && (__INT_MAX__+0 > 2147483647L) +# if !(LZO_LANG_ASSEMBLER) + typedef int lzo_int32e_si_t__ __attribute__((__mode__(__SI__))); + typedef unsigned int lzo_uint32e_si_t__ __attribute__((__mode__(__SI__))); +# endif +# define lzo_int32e_t lzo_int32e_si_t__ +# define lzo_uint32e_t lzo_uint32e_si_t__ +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF___MODE_SI +#elif 1 && !(LZO_CFG_TYPE_NO_MODE_SI) && (LZO_CC_GNUC >= 0x025f00ul) && defined(__AVR__) && (__LONG_MAX__+0 == 32767L) +# if !(LZO_LANG_ASSEMBLER) + typedef int lzo_int32e_si_t__ __attribute__((__mode__(__SI__))); + typedef unsigned int lzo_uint32e_si_t__ __attribute__((__mode__(__SI__))); +# endif +# define lzo_int32e_t lzo_int32e_si_t__ +# define lzo_uint32e_t lzo_uint32e_si_t__ +# define LZO_INT32_C(c) (c##LL) +# define LZO_UINT32_C(c) (c##ULL) +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF___MODE_SI +#elif (LZO_SIZEOF___INT32 == 4) +# define lzo_int32e_t __int32 +# define lzo_uint32e_t unsigned __int32 +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF___INT32 +#else +#endif +#endif +#if defined(lzo_int32e_t) +# define LZO_SIZEOF_LZO_INT32E_T 4 + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32e_t) == 4) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32e_t) == LZO_SIZEOF_LZO_INT32E_T) +#endif +#if !defined(lzo_int64e_t) +#if (LZO_SIZEOF___INT64 == 8) +# if (LZO_CC_BORLANDC) && !defined(LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T) +# define LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T LZO_TYPEOF___INT64 +# endif +#endif +#if (LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T == LZO_TYPEOF_LONG_LONG) && (LZO_SIZEOF_LONG_LONG != 8) +# undef LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T +#endif +#if (LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T == LZO_TYPEOF___INT64) && (LZO_SIZEOF___INT64 != 8) +# undef LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T +#endif +#if (LZO_SIZEOF_INT == 8) && (LZO_SIZEOF_INT < LZO_SIZEOF_LONG) +# define lzo_int64e_t int +# define lzo_uint64e_t unsigned int +# define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF_INT +#elif (LZO_SIZEOF_LONG == 8) && !(LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T == LZO_TYPEOF_LONG_LONG) && !(LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T == LZO_TYPEOF___INT64) +# define lzo_int64e_t long int +# define lzo_uint64e_t unsigned long int +# define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF_LONG +#elif (LZO_SIZEOF_LONG_LONG == 8) && !(LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T == LZO_TYPEOF___INT64) +# define lzo_int64e_t lzo_llong_t +# define lzo_uint64e_t lzo_ullong_t +# define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF_LONG_LONG +# if (LZO_CC_BORLANDC) +# define LZO_INT64_C(c) ((c) + 0ll) +# define LZO_UINT64_C(c) ((c) + 0ull) +# elif 0 +# define LZO_INT64_C(c) (__lzo_gnuc_extension__ (c##LL)) +# define LZO_UINT64_C(c) (__lzo_gnuc_extension__ (c##ULL)) +# else +# define LZO_INT64_C(c) (c##LL) +# define LZO_UINT64_C(c) (c##ULL) +# endif +#elif (LZO_SIZEOF___INT64 == 8) +# define lzo_int64e_t __int64 +# define lzo_uint64e_t unsigned __int64 +# define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF___INT64 +# if (LZO_CC_BORLANDC) +# define LZO_INT64_C(c) ((c) + 0i64) +# define LZO_UINT64_C(c) ((c) + 0ui64) +# else +# define LZO_INT64_C(c) (c##i64) +# define LZO_UINT64_C(c) (c##ui64) +# endif +#else +#endif +#endif +#if defined(lzo_int64e_t) +# define LZO_SIZEOF_LZO_INT64E_T 8 + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64e_t) == 8) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64e_t) == LZO_SIZEOF_LZO_INT64E_T) +#endif +#if !defined(lzo_int32l_t) +#if defined(lzo_int32e_t) +# define lzo_int32l_t lzo_int32e_t +# define lzo_uint32l_t lzo_uint32e_t +# define LZO_SIZEOF_LZO_INT32L_T LZO_SIZEOF_LZO_INT32E_T +# define LZO_TYPEOF_LZO_INT32L_T LZO_TYPEOF_LZO_INT32E_T +#elif (LZO_SIZEOF_INT >= 4) && (LZO_SIZEOF_INT < LZO_SIZEOF_LONG) +# define lzo_int32l_t int +# define lzo_uint32l_t unsigned int +# define LZO_SIZEOF_LZO_INT32L_T LZO_SIZEOF_INT +# define LZO_TYPEOF_LZO_INT32L_T LZO_SIZEOF_INT +#elif (LZO_SIZEOF_LONG >= 4) +# define lzo_int32l_t long int +# define lzo_uint32l_t unsigned long int +# define LZO_SIZEOF_LZO_INT32L_T LZO_SIZEOF_LONG +# define LZO_TYPEOF_LZO_INT32L_T LZO_SIZEOF_LONG +#else +# error "lzo_int32l_t" +#endif +#endif +#if 1 + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32l_t) >= 4) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32l_t) == LZO_SIZEOF_LZO_INT32L_T) +#endif +#if !defined(lzo_int64l_t) +#if defined(lzo_int64e_t) +# define lzo_int64l_t lzo_int64e_t +# define lzo_uint64l_t lzo_uint64e_t +# define LZO_SIZEOF_LZO_INT64L_T LZO_SIZEOF_LZO_INT64E_T +# define LZO_TYPEOF_LZO_INT64L_T LZO_TYPEOF_LZO_INT64E_T +#else +#endif +#endif +#if defined(lzo_int64l_t) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64l_t) >= 8) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64l_t) == LZO_SIZEOF_LZO_INT64L_T) +#endif +#if !defined(lzo_int32f_t) +#if (LZO_SIZEOF_SIZE_T >= 8) +# define lzo_int32f_t lzo_int64l_t +# define lzo_uint32f_t lzo_uint64l_t +# define LZO_SIZEOF_LZO_INT32F_T LZO_SIZEOF_LZO_INT64L_T +# define LZO_TYPEOF_LZO_INT32F_T LZO_TYPEOF_LZO_INT64L_T +#else +# define lzo_int32f_t lzo_int32l_t +# define lzo_uint32f_t lzo_uint32l_t +# define LZO_SIZEOF_LZO_INT32F_T LZO_SIZEOF_LZO_INT32L_T +# define LZO_TYPEOF_LZO_INT32F_T LZO_TYPEOF_LZO_INT32L_T +#endif +#endif +#if 1 + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32f_t) >= 4) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32f_t) == LZO_SIZEOF_LZO_INT32F_T) +#endif +#if !defined(lzo_int64f_t) +#if defined(lzo_int64l_t) +# define lzo_int64f_t lzo_int64l_t +# define lzo_uint64f_t lzo_uint64l_t +# define LZO_SIZEOF_LZO_INT64F_T LZO_SIZEOF_LZO_INT64L_T +# define LZO_TYPEOF_LZO_INT64F_T LZO_TYPEOF_LZO_INT64L_T +#else +#endif +#endif +#if defined(lzo_int64f_t) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64f_t) >= 8) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64f_t) == LZO_SIZEOF_LZO_INT64F_T) +#endif +#if !defined(lzo_intptr_t) +#if 1 && (LZO_OS_OS400 && (LZO_SIZEOF_VOID_P == 16)) +# define __LZO_INTPTR_T_IS_POINTER 1 +# if !(LZO_LANG_ASSEMBLER) + typedef char * lzo_intptr_t; + typedef char * lzo_uintptr_t; +# endif +# define lzo_intptr_t lzo_intptr_t +# define lzo_uintptr_t lzo_uintptr_t +# define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_VOID_P +# define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_CHAR_P +#elif (LZO_CC_MSC && (_MSC_VER >= 1300) && (LZO_SIZEOF_VOID_P == 4) && (LZO_SIZEOF_INT == 4)) +# if !(LZO_LANG_ASSEMBLER) + typedef __w64 int lzo_intptr_t; + typedef __w64 unsigned int lzo_uintptr_t; +# endif +# define lzo_intptr_t lzo_intptr_t +# define lzo_uintptr_t lzo_uintptr_t +# define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_INT +# define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_INT +#elif (LZO_SIZEOF_SHORT == LZO_SIZEOF_VOID_P) && (LZO_SIZEOF_INT > LZO_SIZEOF_VOID_P) +# define lzo_intptr_t short +# define lzo_uintptr_t unsigned short +# define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_SHORT +# define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_SHORT +#elif (LZO_SIZEOF_INT >= LZO_SIZEOF_VOID_P) && (LZO_SIZEOF_INT < LZO_SIZEOF_LONG) +# define lzo_intptr_t int +# define lzo_uintptr_t unsigned int +# define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_INT +# define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_INT +#elif (LZO_SIZEOF_LONG >= LZO_SIZEOF_VOID_P) +# define lzo_intptr_t long +# define lzo_uintptr_t unsigned long +# define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_LONG +# define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_LONG +#elif (LZO_SIZEOF_LZO_INT64L_T >= LZO_SIZEOF_VOID_P) +# define lzo_intptr_t lzo_int64l_t +# define lzo_uintptr_t lzo_uint64l_t +# define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_LZO_INT64L_T +# define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_LZO_INT64L_T +#else +# error "lzo_intptr_t" +#endif +#endif +#if 1 + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_intptr_t) >= sizeof(void *)) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_intptr_t) == sizeof(lzo_uintptr_t)) +#endif +#if !defined(lzo_word_t) +#if defined(LZO_WORDSIZE) && (LZO_WORDSIZE+0 > 0) +#if (LZO_WORDSIZE == LZO_SIZEOF_LZO_INTPTR_T) && !(__LZO_INTPTR_T_IS_POINTER) +# define lzo_word_t lzo_uintptr_t +# define lzo_sword_t lzo_intptr_t +# define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_LZO_INTPTR_T +# define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_LZO_INTPTR_T +#elif (LZO_WORDSIZE == LZO_SIZEOF_LONG) +# define lzo_word_t unsigned long +# define lzo_sword_t long +# define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_LONG +# define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_LONG +#elif (LZO_WORDSIZE == LZO_SIZEOF_INT) +# define lzo_word_t unsigned int +# define lzo_sword_t int +# define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_INT +# define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_INT +#elif (LZO_WORDSIZE == LZO_SIZEOF_SHORT) +# define lzo_word_t unsigned short +# define lzo_sword_t short +# define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_SHORT +# define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_SHORT +#elif (LZO_WORDSIZE == 1) +# define lzo_word_t unsigned char +# define lzo_sword_t signed char +# define LZO_SIZEOF_LZO_WORD_T 1 +# define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_CHAR +#elif (LZO_WORDSIZE == LZO_SIZEOF_LZO_INT64L_T) +# define lzo_word_t lzo_uint64l_t +# define lzo_sword_t lzo_int64l_t +# define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_LZO_INT64L_T +# define LZO_TYPEOF_LZO_WORD_T LZO_SIZEOF_LZO_INT64L_T +#elif (LZO_ARCH_SPU) && (LZO_CC_GNUC) +#if 0 +# if !(LZO_LANG_ASSEMBLER) + typedef unsigned lzo_word_t __attribute__((__mode__(__V16QI__))); + typedef int lzo_sword_t __attribute__((__mode__(__V16QI__))); +# endif +# define lzo_word_t lzo_word_t +# define lzo_sword_t lzo_sword_t +# define LZO_SIZEOF_LZO_WORD_T 16 +# define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF___MODE_V16QI +#endif +#else +# error "lzo_word_t" +#endif +#endif +#endif +#if 1 && defined(lzo_word_t) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_word_t) == LZO_WORDSIZE) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_sword_t) == LZO_WORDSIZE) +#endif +#if 1 +#define lzo_int8_t signed char +#define lzo_uint8_t unsigned char +#define LZO_SIZEOF_LZO_INT8_T 1 +#define LZO_TYPEOF_LZO_INT8_T LZO_TYPEOF_CHAR +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int8_t) == 1) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int8_t) == sizeof(lzo_uint8_t)) +#endif +#if defined(lzo_int16e_t) +#define lzo_int16_t lzo_int16e_t +#define lzo_uint16_t lzo_uint16e_t +#define LZO_SIZEOF_LZO_INT16_T LZO_SIZEOF_LZO_INT16E_T +#define LZO_TYPEOF_LZO_INT16_T LZO_TYPEOF_LZO_INT16E_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16_t) == 2) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16_t) == sizeof(lzo_uint16_t)) +#endif +#if defined(lzo_int32e_t) +#define lzo_int32_t lzo_int32e_t +#define lzo_uint32_t lzo_uint32e_t +#define LZO_SIZEOF_LZO_INT32_T LZO_SIZEOF_LZO_INT32E_T +#define LZO_TYPEOF_LZO_INT32_T LZO_TYPEOF_LZO_INT32E_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32_t) == 4) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32_t) == sizeof(lzo_uint32_t)) +#endif +#if defined(lzo_int64e_t) +#define lzo_int64_t lzo_int64e_t +#define lzo_uint64_t lzo_uint64e_t +#define LZO_SIZEOF_LZO_INT64_T LZO_SIZEOF_LZO_INT64E_T +#define LZO_TYPEOF_LZO_INT64_T LZO_TYPEOF_LZO_INT64E_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64_t) == 8) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64_t) == sizeof(lzo_uint64_t)) +#endif +#if 1 +#define lzo_int_least32_t lzo_int32l_t +#define lzo_uint_least32_t lzo_uint32l_t +#define LZO_SIZEOF_LZO_INT_LEAST32_T LZO_SIZEOF_LZO_INT32L_T +#define LZO_TYPEOF_LZO_INT_LEAST32_T LZO_TYPEOF_LZO_INT32L_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least32_t) >= 4) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least32_t) == sizeof(lzo_uint_least32_t)) +#endif +#if defined(lzo_int64l_t) +#define lzo_int_least64_t lzo_int64l_t +#define lzo_uint_least64_t lzo_uint64l_t +#define LZO_SIZEOF_LZO_INT_LEAST64_T LZO_SIZEOF_LZO_INT64L_T +#define LZO_TYPEOF_LZO_INT_LEAST64_T LZO_TYPEOF_LZO_INT64L_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least64_t) >= 8) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least64_t) == sizeof(lzo_uint_least64_t)) +#endif +#if 1 +#define lzo_int_fast32_t lzo_int32f_t +#define lzo_uint_fast32_t lzo_uint32f_t +#define LZO_SIZEOF_LZO_INT_FAST32_T LZO_SIZEOF_LZO_INT32F_T +#define LZO_TYPEOF_LZO_INT_FAST32_T LZO_TYPEOF_LZO_INT32F_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast32_t) >= 4) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast32_t) == sizeof(lzo_uint_fast32_t)) +#endif +#if defined(lzo_int64f_t) +#define lzo_int_fast64_t lzo_int64f_t +#define lzo_uint_fast64_t lzo_uint64f_t +#define LZO_SIZEOF_LZO_INT_FAST64_T LZO_SIZEOF_LZO_INT64F_T +#define LZO_TYPEOF_LZO_INT_FAST64_T LZO_TYPEOF_LZO_INT64F_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast64_t) >= 8) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast64_t) == sizeof(lzo_uint_fast64_t)) +#endif +#if !defined(LZO_INT16_C) +# if (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_INT >= 2) +# define LZO_INT16_C(c) ((c) + 0) +# define LZO_UINT16_C(c) ((c) + 0U) +# elif (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_LONG >= 2) +# define LZO_INT16_C(c) ((c) + 0L) +# define LZO_UINT16_C(c) ((c) + 0UL) +# elif (LZO_SIZEOF_INT >= 2) +# define LZO_INT16_C(c) (c) +# define LZO_UINT16_C(c) (c##U) +# elif (LZO_SIZEOF_LONG >= 2) +# define LZO_INT16_C(c) (c##L) +# define LZO_UINT16_C(c) (c##UL) +# else +# error "LZO_INT16_C" +# endif +#endif +#if !defined(LZO_INT32_C) +# if (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_INT >= 4) +# define LZO_INT32_C(c) ((c) + 0) +# define LZO_UINT32_C(c) ((c) + 0U) +# elif (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_LONG >= 4) +# define LZO_INT32_C(c) ((c) + 0L) +# define LZO_UINT32_C(c) ((c) + 0UL) +# elif (LZO_SIZEOF_INT >= 4) +# define LZO_INT32_C(c) (c) +# define LZO_UINT32_C(c) (c##U) +# elif (LZO_SIZEOF_LONG >= 4) +# define LZO_INT32_C(c) (c##L) +# define LZO_UINT32_C(c) (c##UL) +# elif (LZO_SIZEOF_LONG_LONG >= 4) +# define LZO_INT32_C(c) (c##LL) +# define LZO_UINT32_C(c) (c##ULL) +# else +# error "LZO_INT32_C" +# endif +#endif +#if !defined(LZO_INT64_C) && defined(lzo_int64l_t) +# if (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_INT >= 8) +# define LZO_INT64_C(c) ((c) + 0) +# define LZO_UINT64_C(c) ((c) + 0U) +# elif (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_LONG >= 8) +# define LZO_INT64_C(c) ((c) + 0L) +# define LZO_UINT64_C(c) ((c) + 0UL) +# elif (LZO_SIZEOF_INT >= 8) +# define LZO_INT64_C(c) (c) +# define LZO_UINT64_C(c) (c##U) +# elif (LZO_SIZEOF_LONG >= 8) +# define LZO_INT64_C(c) (c##L) +# define LZO_UINT64_C(c) (c##UL) +# else +# error "LZO_INT64_C" +# endif +#endif +#endif + +#endif /* already included */ + +/* vim:set ts=4 sw=4 et: */ diff --git a/minilzo/minilzo.c b/minilzo/minilzo.c new file mode 100644 index 0000000..8fd8664 --- /dev/null +++ b/minilzo/minilzo.c @@ -0,0 +1,6365 @@ +/* minilzo.c -- mini subset of the LZO real-time data compression library + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996-2017 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Markus F.X.J. Oberhumer + + http://www.oberhumer.com/opensource/lzo/ + */ + +/* + * NOTE: + * the full LZO package can be found at + * http://www.oberhumer.com/opensource/lzo/ + */ + +#define __LZO_IN_MINILZO 1 + +#if defined(LZO_CFG_FREESTANDING) +# undef MINILZO_HAVE_CONFIG_H +# define LZO_LIBC_FREESTANDING 1 +# define LZO_OS_FREESTANDING 1 +#endif + +#ifdef MINILZO_HAVE_CONFIG_H +# include +#endif +#include +#include +#if defined(MINILZO_CFG_USE_INTERNAL_LZODEFS) + +#ifndef __LZODEFS_H_INCLUDED +#define __LZODEFS_H_INCLUDED 1 + +#if defined(__CYGWIN32__) && !defined(__CYGWIN__) +# define __CYGWIN__ __CYGWIN32__ +#endif +#if 1 && defined(__INTERIX) && defined(__GNUC__) && !defined(_ALL_SOURCE) +# define _ALL_SOURCE 1 +#endif +#if defined(__mips__) && defined(__R5900__) +# if !defined(__LONG_MAX__) +# define __LONG_MAX__ 9223372036854775807L +# endif +#endif +#if 0 +#elif !defined(__LZO_LANG_OVERRIDE) +#if (defined(__clang__) || defined(__GNUC__)) && defined(__ASSEMBLER__) +# if (__ASSEMBLER__+0) <= 0 +# error "__ASSEMBLER__" +# else +# define LZO_LANG_ASSEMBLER 1 +# endif +#elif defined(__cplusplus) +# if (__cplusplus+0) <= 0 +# error "__cplusplus" +# elif (__cplusplus < 199711L) +# define LZO_LANG_CXX 1 +# elif defined(_MSC_VER) && defined(_MSVC_LANG) && (_MSVC_LANG+0 >= 201402L) && 1 +# define LZO_LANG_CXX _MSVC_LANG +# else +# define LZO_LANG_CXX __cplusplus +# endif +# define LZO_LANG_CPLUSPLUS LZO_LANG_CXX +#else +# if defined(__STDC_VERSION__) && (__STDC_VERSION__+0 >= 199409L) +# define LZO_LANG_C __STDC_VERSION__ +# else +# define LZO_LANG_C 1 +# endif +#endif +#endif +#if !defined(LZO_CFG_NO_DISABLE_WUNDEF) +#if defined(__ARMCC_VERSION) +# pragma diag_suppress 193 +#elif defined(__clang__) && defined(__clang_minor__) +# pragma clang diagnostic ignored "-Wundef" +#elif defined(__INTEL_COMPILER) +# pragma warning(disable: 193) +#elif defined(__KEIL__) && defined(__C166__) +# pragma warning disable = 322 +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && !defined(__PATHSCALE__) +# if ((__GNUC__-0) >= 5 || ((__GNUC__-0) == 4 && (__GNUC_MINOR__-0) >= 2)) +# pragma GCC diagnostic ignored "-Wundef" +# endif +#elif defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__MWERKS__) +# if ((_MSC_VER-0) >= 1300) +# pragma warning(disable: 4668) +# endif +#endif +#endif +#if 0 && defined(__POCC__) && defined(_WIN32) +# if (__POCC__ >= 400) +# pragma warn(disable: 2216) +# endif +#endif +#if 0 && defined(__WATCOMC__) +# if (__WATCOMC__ >= 1050) && (__WATCOMC__ < 1060) +# pragma warning 203 9 +# endif +#endif +#if defined(__BORLANDC__) && defined(__MSDOS__) && !defined(__FLAT__) +# pragma option -h +#endif +#if !(LZO_CFG_NO_DISABLE_WCRTNONSTDC) +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#endif +#ifndef _CRT_NONSTDC_NO_WARNINGS +#define _CRT_NONSTDC_NO_WARNINGS 1 +#endif +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS 1 +#endif +#endif +#if 0 +#define LZO_0xffffUL 0xfffful +#define LZO_0xffffffffUL 0xfffffffful +#else +#define LZO_0xffffUL 65535ul +#define LZO_0xffffffffUL 4294967295ul +#endif +#define LZO_0xffffL LZO_0xffffUL +#define LZO_0xffffffffL LZO_0xffffffffUL +#if (LZO_0xffffL == LZO_0xffffffffL) +# error "your preprocessor is broken 1" +#endif +#if (16ul * 16384ul != 262144ul) +# error "your preprocessor is broken 2" +#endif +#if 0 +#if (32767 >= 4294967295ul) +# error "your preprocessor is broken 3" +#endif +#if (65535u >= 4294967295ul) +# error "your preprocessor is broken 4" +#endif +#endif +#if defined(__COUNTER__) +# ifndef LZO_CFG_USE_COUNTER +# define LZO_CFG_USE_COUNTER 1 +# endif +#else +# undef LZO_CFG_USE_COUNTER +#endif +#if (UINT_MAX == LZO_0xffffL) +#if defined(__ZTC__) && defined(__I86__) && !defined(__OS2__) +# if !defined(MSDOS) +# define MSDOS 1 +# endif +# if !defined(_MSDOS) +# define _MSDOS 1 +# endif +#elif 0 && defined(__VERSION) && defined(MB_LEN_MAX) +# if (__VERSION == 520) && (MB_LEN_MAX == 1) +# if !defined(__AZTEC_C__) +# define __AZTEC_C__ __VERSION +# endif +# if !defined(__DOS__) +# define __DOS__ 1 +# endif +# endif +#endif +#endif +#if (UINT_MAX == LZO_0xffffL) +#if defined(_MSC_VER) && defined(M_I86HM) +# define ptrdiff_t long +# define _PTRDIFF_T_DEFINED 1 +#endif +#endif +#if (UINT_MAX == LZO_0xffffL) +# undef __LZO_RENAME_A +# undef __LZO_RENAME_B +# if defined(__AZTEC_C__) && defined(__DOS__) +# define __LZO_RENAME_A 1 +# elif defined(_MSC_VER) && defined(MSDOS) +# if (_MSC_VER < 600) +# define __LZO_RENAME_A 1 +# elif (_MSC_VER < 700) +# define __LZO_RENAME_B 1 +# endif +# elif defined(__TSC__) && defined(__OS2__) +# define __LZO_RENAME_A 1 +# elif defined(__MSDOS__) && defined(__TURBOC__) && (__TURBOC__ < 0x0410) +# define __LZO_RENAME_A 1 +# elif defined(__PACIFIC__) && defined(DOS) +# if !defined(__far) +# define __far far +# endif +# if !defined(__near) +# define __near near +# endif +# endif +# if defined(__LZO_RENAME_A) +# if !defined(__cdecl) +# define __cdecl cdecl +# endif +# if !defined(__far) +# define __far far +# endif +# if !defined(__huge) +# define __huge huge +# endif +# if !defined(__near) +# define __near near +# endif +# if !defined(__pascal) +# define __pascal pascal +# endif +# if !defined(__huge) +# define __huge huge +# endif +# elif defined(__LZO_RENAME_B) +# if !defined(__cdecl) +# define __cdecl _cdecl +# endif +# if !defined(__far) +# define __far _far +# endif +# if !defined(__huge) +# define __huge _huge +# endif +# if !defined(__near) +# define __near _near +# endif +# if !defined(__pascal) +# define __pascal _pascal +# endif +# elif (defined(__PUREC__) || defined(__TURBOC__)) && defined(__TOS__) +# if !defined(__cdecl) +# define __cdecl cdecl +# endif +# if !defined(__pascal) +# define __pascal pascal +# endif +# endif +# undef __LZO_RENAME_A +# undef __LZO_RENAME_B +#endif +#if (UINT_MAX == LZO_0xffffL) +#if defined(__AZTEC_C__) && defined(__DOS__) +# define LZO_BROKEN_CDECL_ALT_SYNTAX 1 +#elif defined(_MSC_VER) && defined(MSDOS) +# if (_MSC_VER < 600) +# define LZO_BROKEN_INTEGRAL_CONSTANTS 1 +# endif +# if (_MSC_VER < 700) +# define LZO_BROKEN_INTEGRAL_PROMOTION 1 +# define LZO_BROKEN_SIZEOF 1 +# endif +#elif defined(__PACIFIC__) && defined(DOS) +# define LZO_BROKEN_INTEGRAL_CONSTANTS 1 +#elif defined(__TURBOC__) && defined(__MSDOS__) +# if (__TURBOC__ < 0x0150) +# define LZO_BROKEN_CDECL_ALT_SYNTAX 1 +# define LZO_BROKEN_INTEGRAL_CONSTANTS 1 +# define LZO_BROKEN_INTEGRAL_PROMOTION 1 +# endif +# if (__TURBOC__ < 0x0200) +# define LZO_BROKEN_SIZEOF 1 +# endif +# if (__TURBOC__ < 0x0400) && defined(__cplusplus) +# define LZO_BROKEN_CDECL_ALT_SYNTAX 1 +# endif +#elif (defined(__PUREC__) || defined(__TURBOC__)) && defined(__TOS__) +# define LZO_BROKEN_CDECL_ALT_SYNTAX 1 +# define LZO_BROKEN_SIZEOF 1 +#endif +#endif +#if defined(__WATCOMC__) && (__WATCOMC__ < 900) +# define LZO_BROKEN_INTEGRAL_CONSTANTS 1 +#endif +#if defined(_CRAY) && defined(_CRAY1) +# define LZO_BROKEN_SIGNED_RIGHT_SHIFT 1 +#endif +#define LZO_PP_STRINGIZE(x) #x +#define LZO_PP_MACRO_EXPAND(x) LZO_PP_STRINGIZE(x) +#define LZO_PP_CONCAT0() /*empty*/ +#define LZO_PP_CONCAT1(a) a +#define LZO_PP_CONCAT2(a,b) a ## b +#define LZO_PP_CONCAT3(a,b,c) a ## b ## c +#define LZO_PP_CONCAT4(a,b,c,d) a ## b ## c ## d +#define LZO_PP_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e +#define LZO_PP_CONCAT6(a,b,c,d,e,f) a ## b ## c ## d ## e ## f +#define LZO_PP_CONCAT7(a,b,c,d,e,f,g) a ## b ## c ## d ## e ## f ## g +#define LZO_PP_ECONCAT0() LZO_PP_CONCAT0() +#define LZO_PP_ECONCAT1(a) LZO_PP_CONCAT1(a) +#define LZO_PP_ECONCAT2(a,b) LZO_PP_CONCAT2(a,b) +#define LZO_PP_ECONCAT3(a,b,c) LZO_PP_CONCAT3(a,b,c) +#define LZO_PP_ECONCAT4(a,b,c,d) LZO_PP_CONCAT4(a,b,c,d) +#define LZO_PP_ECONCAT5(a,b,c,d,e) LZO_PP_CONCAT5(a,b,c,d,e) +#define LZO_PP_ECONCAT6(a,b,c,d,e,f) LZO_PP_CONCAT6(a,b,c,d,e,f) +#define LZO_PP_ECONCAT7(a,b,c,d,e,f,g) LZO_PP_CONCAT7(a,b,c,d,e,f,g) +#define LZO_PP_EMPTY /*empty*/ +#define LZO_PP_EMPTY0() /*empty*/ +#define LZO_PP_EMPTY1(a) /*empty*/ +#define LZO_PP_EMPTY2(a,b) /*empty*/ +#define LZO_PP_EMPTY3(a,b,c) /*empty*/ +#define LZO_PP_EMPTY4(a,b,c,d) /*empty*/ +#define LZO_PP_EMPTY5(a,b,c,d,e) /*empty*/ +#define LZO_PP_EMPTY6(a,b,c,d,e,f) /*empty*/ +#define LZO_PP_EMPTY7(a,b,c,d,e,f,g) /*empty*/ +#if 1 +#define LZO_CPP_STRINGIZE(x) #x +#define LZO_CPP_MACRO_EXPAND(x) LZO_CPP_STRINGIZE(x) +#define LZO_CPP_CONCAT2(a,b) a ## b +#define LZO_CPP_CONCAT3(a,b,c) a ## b ## c +#define LZO_CPP_CONCAT4(a,b,c,d) a ## b ## c ## d +#define LZO_CPP_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e +#define LZO_CPP_CONCAT6(a,b,c,d,e,f) a ## b ## c ## d ## e ## f +#define LZO_CPP_CONCAT7(a,b,c,d,e,f,g) a ## b ## c ## d ## e ## f ## g +#define LZO_CPP_ECONCAT2(a,b) LZO_CPP_CONCAT2(a,b) +#define LZO_CPP_ECONCAT3(a,b,c) LZO_CPP_CONCAT3(a,b,c) +#define LZO_CPP_ECONCAT4(a,b,c,d) LZO_CPP_CONCAT4(a,b,c,d) +#define LZO_CPP_ECONCAT5(a,b,c,d,e) LZO_CPP_CONCAT5(a,b,c,d,e) +#define LZO_CPP_ECONCAT6(a,b,c,d,e,f) LZO_CPP_CONCAT6(a,b,c,d,e,f) +#define LZO_CPP_ECONCAT7(a,b,c,d,e,f,g) LZO_CPP_CONCAT7(a,b,c,d,e,f,g) +#endif +#define __LZO_MASK_GEN(o,b) (((((o) << ((b)-((b)!=0))) - (o)) << 1) + (o)*((b)!=0)) +#if 1 && defined(__cplusplus) +# if !defined(__STDC_CONSTANT_MACROS) +# define __STDC_CONSTANT_MACROS 1 +# endif +# if !defined(__STDC_LIMIT_MACROS) +# define __STDC_LIMIT_MACROS 1 +# endif +#endif +#if defined(__cplusplus) +# define LZO_EXTERN_C extern "C" +# define LZO_EXTERN_C_BEGIN extern "C" { +# define LZO_EXTERN_C_END } +#else +# define LZO_EXTERN_C extern +# define LZO_EXTERN_C_BEGIN /*empty*/ +# define LZO_EXTERN_C_END /*empty*/ +#endif +#if !defined(__LZO_OS_OVERRIDE) +#if (LZO_OS_FREESTANDING) +# define LZO_INFO_OS "freestanding" +#elif (LZO_OS_EMBEDDED) +# define LZO_INFO_OS "embedded" +#elif 1 && defined(__IAR_SYSTEMS_ICC__) +# define LZO_OS_EMBEDDED 1 +# define LZO_INFO_OS "embedded" +#elif defined(__CYGWIN__) && defined(__GNUC__) +# define LZO_OS_CYGWIN 1 +# define LZO_INFO_OS "cygwin" +#elif defined(__EMX__) && defined(__GNUC__) +# define LZO_OS_EMX 1 +# define LZO_INFO_OS "emx" +#elif defined(__BEOS__) +# define LZO_OS_BEOS 1 +# define LZO_INFO_OS "beos" +#elif defined(__Lynx__) +# define LZO_OS_LYNXOS 1 +# define LZO_INFO_OS "lynxos" +#elif defined(__OS400__) +# define LZO_OS_OS400 1 +# define LZO_INFO_OS "os400" +#elif defined(__QNX__) +# define LZO_OS_QNX 1 +# define LZO_INFO_OS "qnx" +#elif defined(__BORLANDC__) && defined(__DPMI32__) && (__BORLANDC__ >= 0x0460) +# define LZO_OS_DOS32 1 +# define LZO_INFO_OS "dos32" +#elif defined(__BORLANDC__) && defined(__DPMI16__) +# define LZO_OS_DOS16 1 +# define LZO_INFO_OS "dos16" +#elif defined(__ZTC__) && defined(DOS386) +# define LZO_OS_DOS32 1 +# define LZO_INFO_OS "dos32" +#elif defined(__OS2__) || defined(__OS2V2__) +# if (UINT_MAX == LZO_0xffffL) +# define LZO_OS_OS216 1 +# define LZO_INFO_OS "os216" +# elif (UINT_MAX == LZO_0xffffffffL) +# define LZO_OS_OS2 1 +# define LZO_INFO_OS "os2" +# else +# error "check your limits.h header" +# endif +#elif defined(__WIN64__) || defined(_WIN64) || defined(WIN64) +# define LZO_OS_WIN64 1 +# define LZO_INFO_OS "win64" +#elif defined(__WIN32__) || defined(_WIN32) || defined(WIN32) || defined(__WINDOWS_386__) +# define LZO_OS_WIN32 1 +# define LZO_INFO_OS "win32" +#elif defined(__MWERKS__) && defined(__INTEL__) +# define LZO_OS_WIN32 1 +# define LZO_INFO_OS "win32" +#elif defined(__WINDOWS__) || defined(_WINDOWS) || defined(_Windows) +# if (UINT_MAX == LZO_0xffffL) +# define LZO_OS_WIN16 1 +# define LZO_INFO_OS "win16" +# elif (UINT_MAX == LZO_0xffffffffL) +# define LZO_OS_WIN32 1 +# define LZO_INFO_OS "win32" +# else +# error "check your limits.h header" +# endif +#elif defined(__DOS__) || defined(__MSDOS__) || defined(_MSDOS) || defined(MSDOS) || (defined(__PACIFIC__) && defined(DOS)) +# if (UINT_MAX == LZO_0xffffL) +# define LZO_OS_DOS16 1 +# define LZO_INFO_OS "dos16" +# elif (UINT_MAX == LZO_0xffffffffL) +# define LZO_OS_DOS32 1 +# define LZO_INFO_OS "dos32" +# else +# error "check your limits.h header" +# endif +#elif defined(__WATCOMC__) +# if defined(__NT__) && (UINT_MAX == LZO_0xffffL) +# define LZO_OS_DOS16 1 +# define LZO_INFO_OS "dos16" +# elif defined(__NT__) && (__WATCOMC__ < 1100) +# define LZO_OS_WIN32 1 +# define LZO_INFO_OS "win32" +# elif defined(__linux__) || defined(__LINUX__) +# define LZO_OS_POSIX 1 +# define LZO_INFO_OS "posix" +# else +# error "please specify a target using the -bt compiler option" +# endif +#elif defined(__palmos__) +# define LZO_OS_PALMOS 1 +# define LZO_INFO_OS "palmos" +#elif defined(__TOS__) || defined(__atarist__) +# define LZO_OS_TOS 1 +# define LZO_INFO_OS "tos" +#elif defined(macintosh) && !defined(__arm__) && !defined(__i386__) && !defined(__ppc__) && !defined(__x64_64__) +# define LZO_OS_MACCLASSIC 1 +# define LZO_INFO_OS "macclassic" +#elif defined(__VMS) +# define LZO_OS_VMS 1 +# define LZO_INFO_OS "vms" +#elif (defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__) +# define LZO_OS_CONSOLE 1 +# define LZO_OS_CONSOLE_PS2 1 +# define LZO_INFO_OS "console" +# define LZO_INFO_OS_CONSOLE "ps2" +#elif defined(__mips__) && defined(__psp__) +# define LZO_OS_CONSOLE 1 +# define LZO_OS_CONSOLE_PSP 1 +# define LZO_INFO_OS "console" +# define LZO_INFO_OS_CONSOLE "psp" +#else +# define LZO_OS_POSIX 1 +# define LZO_INFO_OS "posix" +#endif +#if (LZO_OS_POSIX) +# if defined(_AIX) || defined(__AIX__) || defined(__aix__) +# define LZO_OS_POSIX_AIX 1 +# define LZO_INFO_OS_POSIX "aix" +# elif defined(__FreeBSD__) +# define LZO_OS_POSIX_FREEBSD 1 +# define LZO_INFO_OS_POSIX "freebsd" +# elif defined(__hpux__) || defined(__hpux) +# define LZO_OS_POSIX_HPUX 1 +# define LZO_INFO_OS_POSIX "hpux" +# elif defined(__INTERIX) +# define LZO_OS_POSIX_INTERIX 1 +# define LZO_INFO_OS_POSIX "interix" +# elif defined(__IRIX__) || defined(__irix__) +# define LZO_OS_POSIX_IRIX 1 +# define LZO_INFO_OS_POSIX "irix" +# elif defined(__linux__) || defined(__linux) || defined(__LINUX__) +# define LZO_OS_POSIX_LINUX 1 +# define LZO_INFO_OS_POSIX "linux" +# elif defined(__APPLE__) && defined(__MACH__) +# if ((__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__-0) >= 20000) +# define LZO_OS_POSIX_DARWIN 1040 +# define LZO_INFO_OS_POSIX "darwin_iphone" +# elif ((__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__-0) >= 1040) +# define LZO_OS_POSIX_DARWIN __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +# define LZO_INFO_OS_POSIX "darwin" +# else +# define LZO_OS_POSIX_DARWIN 1 +# define LZO_INFO_OS_POSIX "darwin" +# endif +# define LZO_OS_POSIX_MACOSX LZO_OS_POSIX_DARWIN +# elif defined(__minix__) || defined(__minix) +# define LZO_OS_POSIX_MINIX 1 +# define LZO_INFO_OS_POSIX "minix" +# elif defined(__NetBSD__) +# define LZO_OS_POSIX_NETBSD 1 +# define LZO_INFO_OS_POSIX "netbsd" +# elif defined(__OpenBSD__) +# define LZO_OS_POSIX_OPENBSD 1 +# define LZO_INFO_OS_POSIX "openbsd" +# elif defined(__osf__) +# define LZO_OS_POSIX_OSF 1 +# define LZO_INFO_OS_POSIX "osf" +# elif defined(__solaris__) || defined(__sun) +# if defined(__SVR4) || defined(__svr4__) +# define LZO_OS_POSIX_SOLARIS 1 +# define LZO_INFO_OS_POSIX "solaris" +# else +# define LZO_OS_POSIX_SUNOS 1 +# define LZO_INFO_OS_POSIX "sunos" +# endif +# elif defined(__ultrix__) || defined(__ultrix) +# define LZO_OS_POSIX_ULTRIX 1 +# define LZO_INFO_OS_POSIX "ultrix" +# elif defined(_UNICOS) +# define LZO_OS_POSIX_UNICOS 1 +# define LZO_INFO_OS_POSIX "unicos" +# else +# define LZO_OS_POSIX_UNKNOWN 1 +# define LZO_INFO_OS_POSIX "unknown" +# endif +#endif +#endif +#if (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) +# if (UINT_MAX != LZO_0xffffL) +# error "unexpected configuration - check your compiler defines" +# endif +# if (ULONG_MAX != LZO_0xffffffffL) +# error "unexpected configuration - check your compiler defines" +# endif +#endif +#if (LZO_OS_DOS32 || LZO_OS_OS2 || LZO_OS_WIN32 || LZO_OS_WIN64) +# if (UINT_MAX != LZO_0xffffffffL) +# error "unexpected configuration - check your compiler defines" +# endif +# if (ULONG_MAX != LZO_0xffffffffL) +# error "unexpected configuration - check your compiler defines" +# endif +#endif +#if defined(CIL) && defined(_GNUCC) && defined(__GNUC__) +# define LZO_CC_CILLY 1 +# define LZO_INFO_CC "Cilly" +# if defined(__CILLY__) +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__CILLY__) +# else +# define LZO_INFO_CCVER "unknown" +# endif +#elif 0 && defined(SDCC) && defined(__VERSION__) && !defined(__GNUC__) +# define LZO_CC_SDCC 1 +# define LZO_INFO_CC "sdcc" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(SDCC) +#elif defined(__PATHSCALE__) && defined(__PATHCC_PATCHLEVEL__) +# define LZO_CC_PATHSCALE (__PATHCC__ * 0x10000L + (__PATHCC_MINOR__-0) * 0x100 + (__PATHCC_PATCHLEVEL__-0)) +# define LZO_INFO_CC "Pathscale C" +# define LZO_INFO_CCVER __PATHSCALE__ +# if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) +# define LZO_CC_PATHSCALE_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# endif +#elif defined(__INTEL_COMPILER) && ((__INTEL_COMPILER-0) > 0) +# define LZO_CC_INTELC __INTEL_COMPILER +# define LZO_INFO_CC "Intel C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__INTEL_COMPILER) +# if defined(_MSC_VER) && ((_MSC_VER-0) > 0) +# define LZO_CC_INTELC_MSC _MSC_VER +# elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) +# define LZO_CC_INTELC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# endif +#elif defined(__POCC__) && defined(_WIN32) +# define LZO_CC_PELLESC 1 +# define LZO_INFO_CC "Pelles C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__POCC__) +#elif defined(__ARMCC_VERSION) && defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) +# if defined(__GNUC_PATCHLEVEL__) +# define LZO_CC_ARMCC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# else +# define LZO_CC_ARMCC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100) +# endif +# define LZO_CC_ARMCC __ARMCC_VERSION +# define LZO_INFO_CC "ARM C Compiler" +# define LZO_INFO_CCVER __VERSION__ +#elif defined(__clang__) && defined(__c2__) && defined(__c2_version__) && defined(_MSC_VER) +# define LZO_CC_CLANG (__clang_major__ * 0x10000L + (__clang_minor__-0) * 0x100 + (__clang_patchlevel__-0)) +# define LZO_CC_CLANG_C2 _MSC_VER +# define LZO_CC_CLANG_VENDOR_MICROSOFT 1 +# define LZO_INFO_CC "clang/c2" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__c2_version__) +#elif defined(__clang__) && defined(__llvm__) && defined(__VERSION__) +# if defined(__clang_major__) && defined(__clang_minor__) && defined(__clang_patchlevel__) +# define LZO_CC_CLANG (__clang_major__ * 0x10000L + (__clang_minor__-0) * 0x100 + (__clang_patchlevel__-0)) +# else +# define LZO_CC_CLANG 0x010000L +# endif +# if defined(_MSC_VER) && ((_MSC_VER-0) > 0) +# define LZO_CC_CLANG_MSC _MSC_VER +# elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) +# define LZO_CC_CLANG_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# endif +# if defined(__APPLE_CC__) +# define LZO_CC_CLANG_VENDOR_APPLE 1 +# define LZO_INFO_CC "clang/apple" +# else +# define LZO_CC_CLANG_VENDOR_LLVM 1 +# define LZO_INFO_CC "clang" +# endif +# if defined(__clang_version__) +# define LZO_INFO_CCVER __clang_version__ +# else +# define LZO_INFO_CCVER __VERSION__ +# endif +#elif defined(__llvm__) && defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) +# if defined(__GNUC_PATCHLEVEL__) +# define LZO_CC_LLVM_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# else +# define LZO_CC_LLVM_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100) +# endif +# define LZO_CC_LLVM LZO_CC_LLVM_GNUC +# define LZO_INFO_CC "llvm-gcc" +# define LZO_INFO_CCVER __VERSION__ +#elif defined(__ACK__) && defined(_ACK) +# define LZO_CC_ACK 1 +# define LZO_INFO_CC "Amsterdam Compiler Kit C" +# define LZO_INFO_CCVER "unknown" +#elif defined(__ARMCC_VERSION) && !defined(__GNUC__) +# define LZO_CC_ARMCC __ARMCC_VERSION +# define LZO_CC_ARMCC_ARMCC __ARMCC_VERSION +# define LZO_INFO_CC "ARM C Compiler" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__ARMCC_VERSION) +#elif defined(__AZTEC_C__) +# define LZO_CC_AZTECC 1 +# define LZO_INFO_CC "Aztec C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__AZTEC_C__) +#elif defined(__CODEGEARC__) +# define LZO_CC_CODEGEARC 1 +# define LZO_INFO_CC "CodeGear C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__CODEGEARC__) +#elif defined(__BORLANDC__) +# define LZO_CC_BORLANDC 1 +# define LZO_INFO_CC "Borland C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__BORLANDC__) +#elif defined(_CRAYC) && defined(_RELEASE) +# define LZO_CC_CRAYC 1 +# define LZO_INFO_CC "Cray C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(_RELEASE) +#elif defined(__DMC__) && defined(__SC__) +# define LZO_CC_DMC 1 +# define LZO_INFO_CC "Digital Mars C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__DMC__) +#elif defined(__DECC) +# define LZO_CC_DECC 1 +# define LZO_INFO_CC "DEC C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__DECC) +#elif (defined(__ghs) || defined(__ghs__)) && defined(__GHS_VERSION_NUMBER) && ((__GHS_VERSION_NUMBER-0) > 0) +# define LZO_CC_GHS 1 +# define LZO_INFO_CC "Green Hills C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__GHS_VERSION_NUMBER) +# if defined(_MSC_VER) && ((_MSC_VER-0) > 0) +# define LZO_CC_GHS_MSC _MSC_VER +# elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__VERSION__) +# define LZO_CC_GHS_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# endif +#elif defined(__HIGHC__) +# define LZO_CC_HIGHC 1 +# define LZO_INFO_CC "MetaWare High C" +# define LZO_INFO_CCVER "unknown" +#elif defined(__HP_aCC) && ((__HP_aCC-0) > 0) +# define LZO_CC_HPACC __HP_aCC +# define LZO_INFO_CC "HP aCC" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__HP_aCC) +#elif defined(__IAR_SYSTEMS_ICC__) +# define LZO_CC_IARC 1 +# define LZO_INFO_CC "IAR C" +# if defined(__VER__) +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__VER__) +# else +# define LZO_INFO_CCVER "unknown" +# endif +#elif defined(__IBMC__) && ((__IBMC__-0) > 0) +# define LZO_CC_IBMC __IBMC__ +# define LZO_INFO_CC "IBM C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__IBMC__) +#elif defined(__IBMCPP__) && ((__IBMCPP__-0) > 0) +# define LZO_CC_IBMC __IBMCPP__ +# define LZO_INFO_CC "IBM C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__IBMCPP__) +#elif defined(__KEIL__) && defined(__C166__) +# define LZO_CC_KEILC 1 +# define LZO_INFO_CC "Keil C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__C166__) +#elif defined(__LCC__) && defined(_WIN32) && defined(__LCCOPTIMLEVEL) +# define LZO_CC_LCCWIN32 1 +# define LZO_INFO_CC "lcc-win32" +# define LZO_INFO_CCVER "unknown" +#elif defined(__LCC__) +# define LZO_CC_LCC 1 +# define LZO_INFO_CC "lcc" +# if defined(__LCC_VERSION__) +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__LCC_VERSION__) +# else +# define LZO_INFO_CCVER "unknown" +# endif +#elif defined(__MWERKS__) && ((__MWERKS__-0) > 0) +# define LZO_CC_MWERKS __MWERKS__ +# define LZO_INFO_CC "Metrowerks C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__MWERKS__) +#elif (defined(__NDPC__) || defined(__NDPX__)) && defined(__i386) +# define LZO_CC_NDPC 1 +# define LZO_INFO_CC "Microway NDP C" +# define LZO_INFO_CCVER "unknown" +#elif defined(__PACIFIC__) +# define LZO_CC_PACIFICC 1 +# define LZO_INFO_CC "Pacific C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PACIFIC__) +#elif defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) +# if defined(__PGIC_PATCHLEVEL__) +# define LZO_CC_PGI (__PGIC__ * 0x10000L + (__PGIC_MINOR__-0) * 0x100 + (__PGIC_PATCHLEVEL__-0)) +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PGIC__) "." LZO_PP_MACRO_EXPAND(__PGIC_MINOR__) "." LZO_PP_MACRO_EXPAND(__PGIC_PATCHLEVEL__) +# else +# define LZO_CC_PGI (__PGIC__ * 0x10000L + (__PGIC_MINOR__-0) * 0x100) +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PGIC__) "." LZO_PP_MACRO_EXPAND(__PGIC_MINOR__) ".0" +# endif +# define LZO_INFO_CC "Portland Group PGI C" +#elif defined(__PGI) && (defined(__linux__) || defined(__WIN32__)) +# define LZO_CC_PGI 1 +# define LZO_INFO_CC "Portland Group PGI C" +# define LZO_INFO_CCVER "unknown" +#elif defined(__PUREC__) && defined(__TOS__) +# define LZO_CC_PUREC 1 +# define LZO_INFO_CC "Pure C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__PUREC__) +#elif defined(__SC__) && defined(__ZTC__) +# define LZO_CC_SYMANTECC 1 +# define LZO_INFO_CC "Symantec C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__SC__) +#elif defined(__SUNPRO_C) +# define LZO_INFO_CC "SunPro C" +# if ((__SUNPRO_C-0) > 0) +# define LZO_CC_SUNPROC __SUNPRO_C +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__SUNPRO_C) +# else +# define LZO_CC_SUNPROC 1 +# define LZO_INFO_CCVER "unknown" +# endif +#elif defined(__SUNPRO_CC) +# define LZO_INFO_CC "SunPro C" +# if ((__SUNPRO_CC-0) > 0) +# define LZO_CC_SUNPROC __SUNPRO_CC +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__SUNPRO_CC) +# else +# define LZO_CC_SUNPROC 1 +# define LZO_INFO_CCVER "unknown" +# endif +#elif defined(__TINYC__) +# define LZO_CC_TINYC 1 +# define LZO_INFO_CC "Tiny C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__TINYC__) +#elif defined(__TSC__) +# define LZO_CC_TOPSPEEDC 1 +# define LZO_INFO_CC "TopSpeed C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__TSC__) +#elif defined(__WATCOMC__) +# define LZO_CC_WATCOMC 1 +# define LZO_INFO_CC "Watcom C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__WATCOMC__) +#elif defined(__TURBOC__) +# define LZO_CC_TURBOC 1 +# define LZO_INFO_CC "Turbo C" +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__TURBOC__) +#elif defined(__ZTC__) +# define LZO_CC_ZORTECHC 1 +# define LZO_INFO_CC "Zortech C" +# if ((__ZTC__-0) == 0x310) +# define LZO_INFO_CCVER "0x310" +# else +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(__ZTC__) +# endif +#elif defined(__GNUC__) && defined(__VERSION__) +# if defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +# define LZO_CC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100 + (__GNUC_PATCHLEVEL__-0)) +# elif defined(__GNUC_MINOR__) +# define LZO_CC_GNUC (__GNUC__ * 0x10000L + (__GNUC_MINOR__-0) * 0x100) +# else +# define LZO_CC_GNUC (__GNUC__ * 0x10000L) +# endif +# define LZO_INFO_CC "gcc" +# define LZO_INFO_CCVER __VERSION__ +#elif defined(_MSC_VER) && ((_MSC_VER-0) > 0) +# define LZO_CC_MSC _MSC_VER +# define LZO_INFO_CC "Microsoft C" +# if defined(_MSC_FULL_VER) +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(_MSC_VER) "." LZO_PP_MACRO_EXPAND(_MSC_FULL_VER) +# else +# define LZO_INFO_CCVER LZO_PP_MACRO_EXPAND(_MSC_VER) +# endif +#else +# define LZO_CC_UNKNOWN 1 +# define LZO_INFO_CC "unknown" +# define LZO_INFO_CCVER "unknown" +#endif +#if (LZO_CC_GNUC) && defined(__OPEN64__) +# if defined(__OPENCC__) && defined(__OPENCC_MINOR__) && defined(__OPENCC_PATCHLEVEL__) +# define LZO_CC_OPEN64 (__OPENCC__ * 0x10000L + (__OPENCC_MINOR__-0) * 0x100 + (__OPENCC_PATCHLEVEL__-0)) +# define LZO_CC_OPEN64_GNUC LZO_CC_GNUC +# endif +#endif +#if (LZO_CC_GNUC) && defined(__PCC__) +# if defined(__PCC__) && defined(__PCC_MINOR__) && defined(__PCC_MINORMINOR__) +# define LZO_CC_PCC (__PCC__ * 0x10000L + (__PCC_MINOR__-0) * 0x100 + (__PCC_MINORMINOR__-0)) +# define LZO_CC_PCC_GNUC LZO_CC_GNUC +# endif +#endif +#if 0 && (LZO_CC_MSC && (_MSC_VER >= 1200)) && !defined(_MSC_FULL_VER) +# error "LZO_CC_MSC: _MSC_FULL_VER is not defined" +#endif +#if !defined(__LZO_ARCH_OVERRIDE) && !(LZO_ARCH_GENERIC) && defined(_CRAY) +# if (UINT_MAX > LZO_0xffffffffL) && defined(_CRAY) +# if defined(_CRAYMPP) || defined(_CRAYT3D) || defined(_CRAYT3E) +# define LZO_ARCH_CRAY_MPP 1 +# elif defined(_CRAY1) +# define LZO_ARCH_CRAY_PVP 1 +# endif +# endif +#endif +#if !defined(__LZO_ARCH_OVERRIDE) +#if (LZO_ARCH_GENERIC) +# define LZO_INFO_ARCH "generic" +#elif (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) +# define LZO_ARCH_I086 1 +# define LZO_INFO_ARCH "i086" +#elif defined(__aarch64__) || defined(_M_ARM64) +# define LZO_ARCH_ARM64 1 +# define LZO_INFO_ARCH "arm64" +#elif defined(__alpha__) || defined(__alpha) || defined(_M_ALPHA) +# define LZO_ARCH_ALPHA 1 +# define LZO_INFO_ARCH "alpha" +#elif (LZO_ARCH_CRAY_MPP) && (defined(_CRAYT3D) || defined(_CRAYT3E)) +# define LZO_ARCH_ALPHA 1 +# define LZO_INFO_ARCH "alpha" +#elif defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64) +# define LZO_ARCH_AMD64 1 +# define LZO_INFO_ARCH "amd64" +#elif defined(__arm__) || defined(_M_ARM) +# define LZO_ARCH_ARM 1 +# define LZO_INFO_ARCH "arm" +#elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICCARM__) +# define LZO_ARCH_ARM 1 +# define LZO_INFO_ARCH "arm" +#elif (UINT_MAX <= LZO_0xffffL) && defined(__AVR__) +# define LZO_ARCH_AVR 1 +# define LZO_INFO_ARCH "avr" +#elif defined(__avr32__) || defined(__AVR32__) +# define LZO_ARCH_AVR32 1 +# define LZO_INFO_ARCH "avr32" +#elif defined(__bfin__) +# define LZO_ARCH_BLACKFIN 1 +# define LZO_INFO_ARCH "blackfin" +#elif (UINT_MAX == LZO_0xffffL) && defined(__C166__) +# define LZO_ARCH_C166 1 +# define LZO_INFO_ARCH "c166" +#elif defined(__cris__) +# define LZO_ARCH_CRIS 1 +# define LZO_INFO_ARCH "cris" +#elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICCEZ80__) +# define LZO_ARCH_EZ80 1 +# define LZO_INFO_ARCH "ez80" +#elif defined(__H8300__) || defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) +# define LZO_ARCH_H8300 1 +# define LZO_INFO_ARCH "h8300" +#elif defined(__hppa__) || defined(__hppa) +# define LZO_ARCH_HPPA 1 +# define LZO_INFO_ARCH "hppa" +#elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386) +# define LZO_ARCH_I386 1 +# define LZO_ARCH_IA32 1 +# define LZO_INFO_ARCH "i386" +#elif (LZO_CC_ZORTECHC && defined(__I86__)) +# define LZO_ARCH_I386 1 +# define LZO_ARCH_IA32 1 +# define LZO_INFO_ARCH "i386" +#elif (LZO_OS_DOS32 && LZO_CC_HIGHC) && defined(_I386) +# define LZO_ARCH_I386 1 +# define LZO_ARCH_IA32 1 +# define LZO_INFO_ARCH "i386" +#elif defined(__ia64__) || defined(__ia64) || defined(_M_IA64) +# define LZO_ARCH_IA64 1 +# define LZO_INFO_ARCH "ia64" +#elif (UINT_MAX == LZO_0xffffL) && defined(__m32c__) +# define LZO_ARCH_M16C 1 +# define LZO_INFO_ARCH "m16c" +#elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICCM16C__) +# define LZO_ARCH_M16C 1 +# define LZO_INFO_ARCH "m16c" +#elif defined(__m32r__) +# define LZO_ARCH_M32R 1 +# define LZO_INFO_ARCH "m32r" +#elif (LZO_OS_TOS) || defined(__m68k__) || defined(__m68000__) || defined(__mc68000__) || defined(__mc68020__) || defined(_M_M68K) +# define LZO_ARCH_M68K 1 +# define LZO_INFO_ARCH "m68k" +#elif (UINT_MAX == LZO_0xffffL) && defined(__C251__) +# define LZO_ARCH_MCS251 1 +# define LZO_INFO_ARCH "mcs251" +#elif (UINT_MAX == LZO_0xffffL) && defined(__C51__) +# define LZO_ARCH_MCS51 1 +# define LZO_INFO_ARCH "mcs51" +#elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICC8051__) +# define LZO_ARCH_MCS51 1 +# define LZO_INFO_ARCH "mcs51" +#elif defined(__mips__) || defined(__mips) || defined(_MIPS_ARCH) || defined(_M_MRX000) +# define LZO_ARCH_MIPS 1 +# define LZO_INFO_ARCH "mips" +#elif (UINT_MAX == LZO_0xffffL) && defined(__MSP430__) +# define LZO_ARCH_MSP430 1 +# define LZO_INFO_ARCH "msp430" +#elif defined(__IAR_SYSTEMS_ICC__) && defined(__ICC430__) +# define LZO_ARCH_MSP430 1 +# define LZO_INFO_ARCH "msp430" +#elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR) +# define LZO_ARCH_POWERPC 1 +# define LZO_INFO_ARCH "powerpc" +#elif defined(__powerpc64__) || defined(__powerpc64) || defined(__ppc64__) || defined(__PPC64__) +# define LZO_ARCH_POWERPC 1 +# define LZO_INFO_ARCH "powerpc" +#elif defined(__powerpc64le__) || defined(__powerpc64le) || defined(__ppc64le__) || defined(__PPC64LE__) +# define LZO_ARCH_POWERPC 1 +# define LZO_INFO_ARCH "powerpc" +#elif defined(__riscv) +# define LZO_ARCH_RISCV 1 +# define LZO_INFO_ARCH "riscv" +#elif defined(__s390__) || defined(__s390) || defined(__s390x__) || defined(__s390x) +# define LZO_ARCH_S390 1 +# define LZO_INFO_ARCH "s390" +#elif defined(__sh__) || defined(_M_SH) +# define LZO_ARCH_SH 1 +# define LZO_INFO_ARCH "sh" +#elif defined(__sparc__) || defined(__sparc) || defined(__sparcv8) +# define LZO_ARCH_SPARC 1 +# define LZO_INFO_ARCH "sparc" +#elif defined(__SPU__) +# define LZO_ARCH_SPU 1 +# define LZO_INFO_ARCH "spu" +#elif (UINT_MAX == LZO_0xffffL) && defined(__z80) +# define LZO_ARCH_Z80 1 +# define LZO_INFO_ARCH "z80" +#elif (LZO_ARCH_CRAY_PVP) +# if defined(_CRAYSV1) +# define LZO_ARCH_CRAY_SV1 1 +# define LZO_INFO_ARCH "cray_sv1" +# elif (_ADDR64) +# define LZO_ARCH_CRAY_T90 1 +# define LZO_INFO_ARCH "cray_t90" +# elif (_ADDR32) +# define LZO_ARCH_CRAY_YMP 1 +# define LZO_INFO_ARCH "cray_ymp" +# else +# define LZO_ARCH_CRAY_XMP 1 +# define LZO_INFO_ARCH "cray_xmp" +# endif +#else +# define LZO_ARCH_UNKNOWN 1 +# define LZO_INFO_ARCH "unknown" +#endif +#endif +#if !defined(LZO_ARCH_ARM_THUMB2) +#if (LZO_ARCH_ARM) +# if defined(__thumb__) || defined(__thumb) || defined(_M_THUMB) +# if defined(__thumb2__) +# define LZO_ARCH_ARM_THUMB2 1 +# elif 1 && defined(__TARGET_ARCH_THUMB) && ((__TARGET_ARCH_THUMB)+0 >= 4) +# define LZO_ARCH_ARM_THUMB2 1 +# elif 1 && defined(_MSC_VER) && defined(_M_THUMB) && ((_M_THUMB)+0 >= 7) +# define LZO_ARCH_ARM_THUMB2 1 +# endif +# endif +#endif +#endif +#if (LZO_ARCH_ARM_THUMB2) +# undef LZO_INFO_ARCH +# define LZO_INFO_ARCH "arm_thumb2" +#endif +#if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_DOS32 || LZO_OS_OS2) +# error "FIXME - missing define for CPU architecture" +#endif +#if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_WIN32) +# error "FIXME - missing LZO_OS_WIN32 define for CPU architecture" +#endif +#if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_WIN64) +# error "FIXME - missing LZO_OS_WIN64 define for CPU architecture" +#endif +#if (LZO_OS_OS216 || LZO_OS_WIN16) +# define LZO_ARCH_I086PM 1 +#elif 1 && (LZO_OS_DOS16 && defined(BLX286)) +# define LZO_ARCH_I086PM 1 +#elif 1 && (LZO_OS_DOS16 && defined(DOSX286)) +# define LZO_ARCH_I086PM 1 +#elif 1 && (LZO_OS_DOS16 && LZO_CC_BORLANDC && defined(__DPMI16__)) +# define LZO_ARCH_I086PM 1 +#endif +#if (LZO_ARCH_AMD64 && !LZO_ARCH_X64) +# define LZO_ARCH_X64 1 +#elif (!LZO_ARCH_AMD64 && LZO_ARCH_X64) && defined(__LZO_ARCH_OVERRIDE) +# define LZO_ARCH_AMD64 1 +#endif +#if (LZO_ARCH_ARM64 && !LZO_ARCH_AARCH64) +# define LZO_ARCH_AARCH64 1 +#elif (!LZO_ARCH_ARM64 && LZO_ARCH_AARCH64) && defined(__LZO_ARCH_OVERRIDE) +# define LZO_ARCH_ARM64 1 +#endif +#if (LZO_ARCH_I386 && !LZO_ARCH_X86) +# define LZO_ARCH_X86 1 +#elif (!LZO_ARCH_I386 && LZO_ARCH_X86) && defined(__LZO_ARCH_OVERRIDE) +# define LZO_ARCH_I386 1 +#endif +#if (LZO_ARCH_AMD64 && !LZO_ARCH_X64) || (!LZO_ARCH_AMD64 && LZO_ARCH_X64) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_ARM64 && !LZO_ARCH_AARCH64) || (!LZO_ARCH_ARM64 && LZO_ARCH_AARCH64) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_I386 && !LZO_ARCH_X86) || (!LZO_ARCH_I386 && LZO_ARCH_X86) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_ARM_THUMB1 && !LZO_ARCH_ARM) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_ARM_THUMB2 && !LZO_ARCH_ARM) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_ARM_THUMB1 && LZO_ARCH_ARM_THUMB2) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_I086PM && !LZO_ARCH_I086) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_I086) +# if (UINT_MAX != LZO_0xffffL) +# error "unexpected configuration - check your compiler defines" +# endif +# if (ULONG_MAX != LZO_0xffffffffL) +# error "unexpected configuration - check your compiler defines" +# endif +#endif +#if (LZO_ARCH_I386) +# if (UINT_MAX != LZO_0xffffL) && defined(__i386_int16__) +# error "unexpected configuration - check your compiler defines" +# endif +# if (UINT_MAX != LZO_0xffffffffL) && !defined(__i386_int16__) +# error "unexpected configuration - check your compiler defines" +# endif +# if (ULONG_MAX != LZO_0xffffffffL) +# error "unexpected configuration - check your compiler defines" +# endif +#endif +#if (LZO_ARCH_AMD64 || LZO_ARCH_I386) +# if !defined(LZO_TARGET_FEATURE_SSE2) +# if defined(__SSE2__) +# define LZO_TARGET_FEATURE_SSE2 1 +# elif defined(_MSC_VER) && (defined(_M_IX86_FP) && ((_M_IX86_FP)+0 >= 2)) +# define LZO_TARGET_FEATURE_SSE2 1 +# elif (LZO_CC_INTELC_MSC || LZO_CC_MSC) && defined(_M_AMD64) +# define LZO_TARGET_FEATURE_SSE2 1 +# endif +# endif +# if !defined(LZO_TARGET_FEATURE_SSSE3) +# if (LZO_TARGET_FEATURE_SSE2) +# if defined(__SSSE3__) +# define LZO_TARGET_FEATURE_SSSE3 1 +# elif defined(_MSC_VER) && defined(__AVX__) +# define LZO_TARGET_FEATURE_SSSE3 1 +# endif +# endif +# endif +# if !defined(LZO_TARGET_FEATURE_SSE4_2) +# if (LZO_TARGET_FEATURE_SSSE3) +# if defined(__SSE4_2__) +# define LZO_TARGET_FEATURE_SSE4_2 1 +# endif +# endif +# endif +# if !defined(LZO_TARGET_FEATURE_AVX) +# if (LZO_TARGET_FEATURE_SSSE3) +# if defined(__AVX__) +# define LZO_TARGET_FEATURE_AVX 1 +# endif +# endif +# endif +# if !defined(LZO_TARGET_FEATURE_AVX2) +# if (LZO_TARGET_FEATURE_AVX) +# if defined(__AVX2__) +# define LZO_TARGET_FEATURE_AVX2 1 +# endif +# endif +# endif +#endif +#if (LZO_TARGET_FEATURE_SSSE3 && !(LZO_TARGET_FEATURE_SSE2)) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_TARGET_FEATURE_SSE4_2 && !(LZO_TARGET_FEATURE_SSSE3)) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_TARGET_FEATURE_AVX && !(LZO_TARGET_FEATURE_SSSE3)) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_TARGET_FEATURE_AVX2 && !(LZO_TARGET_FEATURE_AVX)) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ARCH_ARM) +# if !defined(LZO_TARGET_FEATURE_NEON) +# if defined(__ARM_NEON) && ((__ARM_NEON)+0) +# define LZO_TARGET_FEATURE_NEON 1 +# elif 1 && defined(__ARM_NEON__) && ((__ARM_NEON__)+0) +# define LZO_TARGET_FEATURE_NEON 1 +# elif 1 && defined(__TARGET_FEATURE_NEON) && ((__TARGET_FEATURE_NEON)+0) +# define LZO_TARGET_FEATURE_NEON 1 +# endif +# endif +#elif (LZO_ARCH_ARM64) +# if !defined(LZO_TARGET_FEATURE_NEON) +# if 1 +# define LZO_TARGET_FEATURE_NEON 1 +# endif +# endif +#endif +#if 0 +#elif !defined(__LZO_MM_OVERRIDE) +#if (LZO_ARCH_I086) +#if (UINT_MAX != LZO_0xffffL) +# error "unexpected configuration - check your compiler defines" +#endif +#if defined(__TINY__) || defined(M_I86TM) || defined(_M_I86TM) +# define LZO_MM_TINY 1 +#elif defined(__HUGE__) || defined(_HUGE_) || defined(M_I86HM) || defined(_M_I86HM) +# define LZO_MM_HUGE 1 +#elif defined(__SMALL__) || defined(M_I86SM) || defined(_M_I86SM) || defined(SMALL_MODEL) +# define LZO_MM_SMALL 1 +#elif defined(__MEDIUM__) || defined(M_I86MM) || defined(_M_I86MM) +# define LZO_MM_MEDIUM 1 +#elif defined(__COMPACT__) || defined(M_I86CM) || defined(_M_I86CM) +# define LZO_MM_COMPACT 1 +#elif defined(__LARGE__) || defined(M_I86LM) || defined(_M_I86LM) || defined(LARGE_MODEL) +# define LZO_MM_LARGE 1 +#elif (LZO_CC_AZTECC) +# if defined(_LARGE_CODE) && defined(_LARGE_DATA) +# define LZO_MM_LARGE 1 +# elif defined(_LARGE_CODE) +# define LZO_MM_MEDIUM 1 +# elif defined(_LARGE_DATA) +# define LZO_MM_COMPACT 1 +# else +# define LZO_MM_SMALL 1 +# endif +#elif (LZO_CC_ZORTECHC && defined(__VCM__)) +# define LZO_MM_LARGE 1 +#else +# error "unknown LZO_ARCH_I086 memory model" +#endif +#if (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) +#define LZO_HAVE_MM_HUGE_PTR 1 +#define LZO_HAVE_MM_HUGE_ARRAY 1 +#if (LZO_MM_TINY) +# undef LZO_HAVE_MM_HUGE_ARRAY +#endif +#if (LZO_CC_AZTECC || LZO_CC_PACIFICC || LZO_CC_ZORTECHC) +# undef LZO_HAVE_MM_HUGE_PTR +# undef LZO_HAVE_MM_HUGE_ARRAY +#elif (LZO_CC_DMC || LZO_CC_SYMANTECC) +# undef LZO_HAVE_MM_HUGE_ARRAY +#elif (LZO_CC_MSC && defined(_QC)) +# undef LZO_HAVE_MM_HUGE_ARRAY +# if (_MSC_VER < 600) +# undef LZO_HAVE_MM_HUGE_PTR +# endif +#elif (LZO_CC_TURBOC && (__TURBOC__ < 0x0295)) +# undef LZO_HAVE_MM_HUGE_ARRAY +#endif +#if (LZO_ARCH_I086PM) && !(LZO_HAVE_MM_HUGE_PTR) +# if (LZO_OS_DOS16) +# error "unexpected configuration - check your compiler defines" +# elif (LZO_CC_ZORTECHC) +# else +# error "unexpected configuration - check your compiler defines" +# endif +#endif +#if defined(__cplusplus) +extern "C" { +#endif +#if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0200)) + extern void __near __cdecl _AHSHIFT(void); +# define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) +#elif (LZO_CC_DMC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) + extern void __near __cdecl _AHSHIFT(void); +# define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) +#elif (LZO_CC_MSC || LZO_CC_TOPSPEEDC) + extern void __near __cdecl _AHSHIFT(void); +# define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) +#elif (LZO_CC_TURBOC && (__TURBOC__ >= 0x0295)) + extern void __near __cdecl _AHSHIFT(void); +# define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) +#elif ((LZO_CC_AZTECC || LZO_CC_PACIFICC || LZO_CC_TURBOC) && LZO_OS_DOS16) +# define LZO_MM_AHSHIFT 12 +#elif (LZO_CC_WATCOMC) + extern unsigned char _HShift; +# define LZO_MM_AHSHIFT ((unsigned) _HShift) +#else +# error "FIXME - implement LZO_MM_AHSHIFT" +#endif +#if defined(__cplusplus) +} +#endif +#endif +#elif (LZO_ARCH_C166) +#if !defined(__MODEL__) +# error "FIXME - LZO_ARCH_C166 __MODEL__" +#elif ((__MODEL__) == 0) +# define LZO_MM_SMALL 1 +#elif ((__MODEL__) == 1) +# define LZO_MM_SMALL 1 +#elif ((__MODEL__) == 2) +# define LZO_MM_LARGE 1 +#elif ((__MODEL__) == 3) +# define LZO_MM_TINY 1 +#elif ((__MODEL__) == 4) +# define LZO_MM_XTINY 1 +#elif ((__MODEL__) == 5) +# define LZO_MM_XSMALL 1 +#else +# error "FIXME - LZO_ARCH_C166 __MODEL__" +#endif +#elif (LZO_ARCH_MCS251) +#if !defined(__MODEL__) +# error "FIXME - LZO_ARCH_MCS251 __MODEL__" +#elif ((__MODEL__) == 0) +# define LZO_MM_SMALL 1 +#elif ((__MODEL__) == 2) +# define LZO_MM_LARGE 1 +#elif ((__MODEL__) == 3) +# define LZO_MM_TINY 1 +#elif ((__MODEL__) == 4) +# define LZO_MM_XTINY 1 +#elif ((__MODEL__) == 5) +# define LZO_MM_XSMALL 1 +#else +# error "FIXME - LZO_ARCH_MCS251 __MODEL__" +#endif +#elif (LZO_ARCH_MCS51) +#if !defined(__MODEL__) +# error "FIXME - LZO_ARCH_MCS51 __MODEL__" +#elif ((__MODEL__) == 1) +# define LZO_MM_SMALL 1 +#elif ((__MODEL__) == 2) +# define LZO_MM_LARGE 1 +#elif ((__MODEL__) == 3) +# define LZO_MM_TINY 1 +#elif ((__MODEL__) == 4) +# define LZO_MM_XTINY 1 +#elif ((__MODEL__) == 5) +# define LZO_MM_XSMALL 1 +#else +# error "FIXME - LZO_ARCH_MCS51 __MODEL__" +#endif +#elif (LZO_ARCH_CRAY_PVP) +# define LZO_MM_PVP 1 +#else +# define LZO_MM_FLAT 1 +#endif +#if (LZO_MM_COMPACT) +# define LZO_INFO_MM "compact" +#elif (LZO_MM_FLAT) +# define LZO_INFO_MM "flat" +#elif (LZO_MM_HUGE) +# define LZO_INFO_MM "huge" +#elif (LZO_MM_LARGE) +# define LZO_INFO_MM "large" +#elif (LZO_MM_MEDIUM) +# define LZO_INFO_MM "medium" +#elif (LZO_MM_PVP) +# define LZO_INFO_MM "pvp" +#elif (LZO_MM_SMALL) +# define LZO_INFO_MM "small" +#elif (LZO_MM_TINY) +# define LZO_INFO_MM "tiny" +#else +# error "unknown memory model" +#endif +#endif +#if !defined(__lzo_gnuc_extension__) +#if (LZO_CC_GNUC >= 0x020800ul) +# define __lzo_gnuc_extension__ __extension__ +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_gnuc_extension__ __extension__ +#elif (LZO_CC_IBMC >= 600) +# define __lzo_gnuc_extension__ __extension__ +#endif +#endif +#if !defined(__lzo_gnuc_extension__) +# define __lzo_gnuc_extension__ /*empty*/ +#endif +#if !defined(lzo_has_builtin) +#if (LZO_CC_CLANG) && defined(__has_builtin) +# define lzo_has_builtin __has_builtin +#endif +#endif +#if !defined(lzo_has_builtin) +# define lzo_has_builtin(x) 0 +#endif +#if !defined(lzo_has_attribute) +#if (LZO_CC_CLANG) && defined(__has_attribute) +# define lzo_has_attribute __has_attribute +#endif +#endif +#if !defined(lzo_has_attribute) +# define lzo_has_attribute(x) 0 +#endif +#if !defined(lzo_has_declspec_attribute) +#if (LZO_CC_CLANG) && defined(__has_declspec_attribute) +# define lzo_has_declspec_attribute __has_declspec_attribute +#endif +#endif +#if !defined(lzo_has_declspec_attribute) +# define lzo_has_declspec_attribute(x) 0 +#endif +#if !defined(lzo_has_feature) +#if (LZO_CC_CLANG) && defined(__has_feature) +# define lzo_has_feature __has_feature +#endif +#endif +#if !defined(lzo_has_feature) +# define lzo_has_feature(x) 0 +#endif +#if !defined(lzo_has_extension) +#if (LZO_CC_CLANG) && defined(__has_extension) +# define lzo_has_extension __has_extension +#elif (LZO_CC_CLANG) && defined(__has_feature) +# define lzo_has_extension __has_feature +#endif +#endif +#if !defined(lzo_has_extension) +# define lzo_has_extension(x) 0 +#endif +#if !defined(LZO_CFG_USE_NEW_STYLE_CASTS) && defined(__cplusplus) && 0 +# if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020800ul)) +# define LZO_CFG_USE_NEW_STYLE_CASTS 0 +# elif (LZO_CC_INTELC && (__INTEL_COMPILER < 1200)) +# define LZO_CFG_USE_NEW_STYLE_CASTS 0 +# else +# define LZO_CFG_USE_NEW_STYLE_CASTS 1 +# endif +#endif +#if !defined(LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_CFG_USE_NEW_STYLE_CASTS 0 +#endif +#if !defined(__cplusplus) +# if defined(LZO_CFG_USE_NEW_STYLE_CASTS) +# undef LZO_CFG_USE_NEW_STYLE_CASTS +# endif +# define LZO_CFG_USE_NEW_STYLE_CASTS 0 +#endif +#if !defined(LZO_REINTERPRET_CAST) +# if (LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_REINTERPRET_CAST(t,e) (reinterpret_cast (e)) +# endif +#endif +#if !defined(LZO_REINTERPRET_CAST) +# define LZO_REINTERPRET_CAST(t,e) ((t) (e)) +#endif +#if !defined(LZO_STATIC_CAST) +# if (LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_STATIC_CAST(t,e) (static_cast (e)) +# endif +#endif +#if !defined(LZO_STATIC_CAST) +# define LZO_STATIC_CAST(t,e) ((t) (e)) +#endif +#if !defined(LZO_STATIC_CAST2) +# define LZO_STATIC_CAST2(t1,t2,e) LZO_STATIC_CAST(t1, LZO_STATIC_CAST(t2, e)) +#endif +#if !defined(LZO_UNCONST_CAST) +# if (LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_UNCONST_CAST(t,e) (const_cast (e)) +# elif (LZO_HAVE_MM_HUGE_PTR) +# define LZO_UNCONST_CAST(t,e) ((t) (e)) +# elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define LZO_UNCONST_CAST(t,e) ((t) ((void *) ((lzo_uintptr_t) ((const void *) (e))))) +# endif +#endif +#if !defined(LZO_UNCONST_CAST) +# define LZO_UNCONST_CAST(t,e) ((t) ((void *) ((const void *) (e)))) +#endif +#if !defined(LZO_UNCONST_VOLATILE_CAST) +# if (LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_UNCONST_VOLATILE_CAST(t,e) (const_cast (e)) +# elif (LZO_HAVE_MM_HUGE_PTR) +# define LZO_UNCONST_VOLATILE_CAST(t,e) ((t) (e)) +# elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define LZO_UNCONST_VOLATILE_CAST(t,e) ((t) ((volatile void *) ((lzo_uintptr_t) ((volatile const void *) (e))))) +# endif +#endif +#if !defined(LZO_UNCONST_VOLATILE_CAST) +# define LZO_UNCONST_VOLATILE_CAST(t,e) ((t) ((volatile void *) ((volatile const void *) (e)))) +#endif +#if !defined(LZO_UNVOLATILE_CAST) +# if (LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_UNVOLATILE_CAST(t,e) (const_cast (e)) +# elif (LZO_HAVE_MM_HUGE_PTR) +# define LZO_UNVOLATILE_CAST(t,e) ((t) (e)) +# elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define LZO_UNVOLATILE_CAST(t,e) ((t) ((void *) ((lzo_uintptr_t) ((volatile void *) (e))))) +# endif +#endif +#if !defined(LZO_UNVOLATILE_CAST) +# define LZO_UNVOLATILE_CAST(t,e) ((t) ((void *) ((volatile void *) (e)))) +#endif +#if !defined(LZO_UNVOLATILE_CONST_CAST) +# if (LZO_CFG_USE_NEW_STYLE_CASTS) +# define LZO_UNVOLATILE_CONST_CAST(t,e) (const_cast (e)) +# elif (LZO_HAVE_MM_HUGE_PTR) +# define LZO_UNVOLATILE_CONST_CAST(t,e) ((t) (e)) +# elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define LZO_UNVOLATILE_CONST_CAST(t,e) ((t) ((const void *) ((lzo_uintptr_t) ((volatile const void *) (e))))) +# endif +#endif +#if !defined(LZO_UNVOLATILE_CONST_CAST) +# define LZO_UNVOLATILE_CONST_CAST(t,e) ((t) ((const void *) ((volatile const void *) (e)))) +#endif +#if !defined(LZO_PCAST) +# if (LZO_HAVE_MM_HUGE_PTR) +# define LZO_PCAST(t,e) ((t) (e)) +# endif +#endif +#if !defined(LZO_PCAST) +# define LZO_PCAST(t,e) LZO_STATIC_CAST(t, LZO_STATIC_CAST(void *, e)) +#endif +#if !defined(LZO_CCAST) +# if (LZO_HAVE_MM_HUGE_PTR) +# define LZO_CCAST(t,e) ((t) (e)) +# endif +#endif +#if !defined(LZO_CCAST) +# define LZO_CCAST(t,e) LZO_STATIC_CAST(t, LZO_STATIC_CAST(const void *, e)) +#endif +#if !defined(LZO_ICONV) +# define LZO_ICONV(t,e) LZO_STATIC_CAST(t, e) +#endif +#if !defined(LZO_ICAST) +# define LZO_ICAST(t,e) LZO_STATIC_CAST(t, e) +#endif +#if !defined(LZO_ITRUNC) +# define LZO_ITRUNC(t,e) LZO_STATIC_CAST(t, e) +#endif +#if !defined(__lzo_cte) +# if (LZO_CC_MSC || LZO_CC_WATCOMC) +# define __lzo_cte(e) ((void)0,(e)) +# elif 1 +# define __lzo_cte(e) ((void)0,(e)) +# endif +#endif +#if !defined(__lzo_cte) +# define __lzo_cte(e) (e) +#endif +#if !defined(LZO_BLOCK_BEGIN) +# define LZO_BLOCK_BEGIN do { +# define LZO_BLOCK_END } while __lzo_cte(0) +#endif +#if !defined(LZO_UNUSED) +# if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0600)) +# define LZO_UNUSED(var) ((void) &var) +# elif (LZO_CC_BORLANDC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PELLESC || LZO_CC_TURBOC) +# define LZO_UNUSED(var) if (&var) ; else +# elif (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x030200ul)) +# define LZO_UNUSED(var) ((void) &var) +# elif (LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define LZO_UNUSED(var) ((void) var) +# elif (LZO_CC_MSC && (_MSC_VER < 900)) +# define LZO_UNUSED(var) if (&var) ; else +# elif (LZO_CC_KEILC) +# define LZO_UNUSED(var) {extern int lzo_unused__[1-2*!(sizeof(var)>0)]; (void)lzo_unused__;} +# elif (LZO_CC_PACIFICC) +# define LZO_UNUSED(var) ((void) sizeof(var)) +# elif (LZO_CC_WATCOMC) && defined(__cplusplus) +# define LZO_UNUSED(var) ((void) var) +# else +# define LZO_UNUSED(var) ((void) &var) +# endif +#endif +#if !defined(LZO_UNUSED_RESULT) +# define LZO_UNUSED_RESULT(var) LZO_UNUSED(var) +#endif +#if !defined(LZO_UNUSED_FUNC) +# if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0600)) +# define LZO_UNUSED_FUNC(func) ((void) func) +# elif (LZO_CC_BORLANDC || LZO_CC_NDPC || LZO_CC_TURBOC) +# define LZO_UNUSED_FUNC(func) if (func) ; else +# elif (LZO_CC_CLANG || LZO_CC_LLVM) +# define LZO_UNUSED_FUNC(func) ((void) &func) +# elif (LZO_CC_MSC && (_MSC_VER < 900)) +# define LZO_UNUSED_FUNC(func) if (func) ; else +# elif (LZO_CC_MSC) +# define LZO_UNUSED_FUNC(func) ((void) &func) +# elif (LZO_CC_KEILC || LZO_CC_PELLESC) +# define LZO_UNUSED_FUNC(func) {extern int lzo_unused__[1-2*!(sizeof((int)func)>0)]; (void)lzo_unused__;} +# else +# define LZO_UNUSED_FUNC(func) ((void) func) +# endif +#endif +#if !defined(LZO_UNUSED_LABEL) +# if (LZO_CC_CLANG >= 0x020800ul) +# define LZO_UNUSED_LABEL(l) (__lzo_gnuc_extension__ ((void) ((const void *) &&l))) +# elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_INTELC || LZO_CC_WATCOMC) +# define LZO_UNUSED_LABEL(l) if __lzo_cte(0) goto l +# else +# define LZO_UNUSED_LABEL(l) switch (0) case 1:goto l +# endif +#endif +#if !defined(LZO_DEFINE_UNINITIALIZED_VAR) +# if 0 +# define LZO_DEFINE_UNINITIALIZED_VAR(type,var,init) type var +# elif 0 && (LZO_CC_GNUC) +# define LZO_DEFINE_UNINITIALIZED_VAR(type,var,init) type var = var +# else +# define LZO_DEFINE_UNINITIALIZED_VAR(type,var,init) type var = init +# endif +#endif +#if !defined(__lzo_inline) +#if (LZO_CC_TURBOC && (__TURBOC__ <= 0x0295)) +#elif defined(__cplusplus) +# define __lzo_inline inline +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__-0 >= 199901L) +# define __lzo_inline inline +#elif (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0550)) +# define __lzo_inline __inline +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CILLY || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) +# define __lzo_inline __inline__ +#elif (LZO_CC_DMC) +# define __lzo_inline __inline +#elif (LZO_CC_GHS) +# define __lzo_inline __inline__ +#elif (LZO_CC_IBMC >= 600) +# define __lzo_inline __inline__ +#elif (LZO_CC_INTELC) +# define __lzo_inline __inline +#elif (LZO_CC_MWERKS && (__MWERKS__ >= 0x2405)) +# define __lzo_inline __inline +#elif (LZO_CC_MSC && (_MSC_VER >= 900)) +# define __lzo_inline __inline +#elif (LZO_CC_SUNPROC >= 0x5100) +# define __lzo_inline __inline__ +#endif +#endif +#if defined(__lzo_inline) +# ifndef __lzo_HAVE_inline +# define __lzo_HAVE_inline 1 +# endif +#else +# define __lzo_inline /*empty*/ +#endif +#if !defined(__lzo_forceinline) +#if (LZO_CC_GNUC >= 0x030200ul) +# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) +#elif (LZO_CC_IBMC >= 700) +# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) +#elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 450)) +# define __lzo_forceinline __forceinline +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) +# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) +#elif (LZO_CC_MSC && (_MSC_VER >= 1200)) +# define __lzo_forceinline __forceinline +#elif (LZO_CC_PGI >= 0x0d0a00ul) +# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) +#elif (LZO_CC_SUNPROC >= 0x5100) +# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) +#endif +#endif +#if defined(__lzo_forceinline) +# ifndef __lzo_HAVE_forceinline +# define __lzo_HAVE_forceinline 1 +# endif +#else +# define __lzo_forceinline __lzo_inline +#endif +#if !defined(__lzo_noinline) +#if 1 && (LZO_ARCH_I386) && (LZO_CC_GNUC >= 0x040000ul) && (LZO_CC_GNUC < 0x040003ul) +# define __lzo_noinline __attribute__((__noinline__,__used__)) +#elif (LZO_CC_GNUC >= 0x030200ul) +# define __lzo_noinline __attribute__((__noinline__)) +#elif (LZO_CC_IBMC >= 700) +# define __lzo_noinline __attribute__((__noinline__)) +#elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 600)) +# define __lzo_noinline __declspec(noinline) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) +# define __lzo_noinline __attribute__((__noinline__)) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_noinline __attribute__((__noinline__)) +#elif (LZO_CC_MSC && (_MSC_VER >= 1300)) +# define __lzo_noinline __declspec(noinline) +#elif (LZO_CC_MWERKS && (__MWERKS__ >= 0x3200) && (LZO_OS_WIN32 || LZO_OS_WIN64)) +# if defined(__cplusplus) +# else +# define __lzo_noinline __declspec(noinline) +# endif +#elif (LZO_CC_PGI >= 0x0d0a00ul) +# define __lzo_noinline __attribute__((__noinline__)) +#elif (LZO_CC_SUNPROC >= 0x5100) +# define __lzo_noinline __attribute__((__noinline__)) +#endif +#endif +#if defined(__lzo_noinline) +# ifndef __lzo_HAVE_noinline +# define __lzo_HAVE_noinline 1 +# endif +#else +# define __lzo_noinline /*empty*/ +#endif +#if (__lzo_HAVE_forceinline || __lzo_HAVE_noinline) && !(__lzo_HAVE_inline) +# error "unexpected configuration - check your compiler defines" +#endif +#if !defined(__lzo_static_inline) +#if (LZO_CC_IBMC) +# define __lzo_static_inline __lzo_gnuc_extension__ static __lzo_inline +#endif +#endif +#if !defined(__lzo_static_inline) +# define __lzo_static_inline static __lzo_inline +#endif +#if !defined(__lzo_static_forceinline) +#if (LZO_CC_IBMC) +# define __lzo_static_forceinline __lzo_gnuc_extension__ static __lzo_forceinline +#endif +#endif +#if !defined(__lzo_static_forceinline) +# define __lzo_static_forceinline static __lzo_forceinline +#endif +#if !defined(__lzo_static_noinline) +#if (LZO_CC_IBMC) +# define __lzo_static_noinline __lzo_gnuc_extension__ static __lzo_noinline +#endif +#endif +#if !defined(__lzo_static_noinline) +# define __lzo_static_noinline static __lzo_noinline +#endif +#if !defined(__lzo_c99_extern_inline) +#if defined(__GNUC_GNU_INLINE__) +# define __lzo_c99_extern_inline __lzo_inline +#elif defined(__GNUC_STDC_INLINE__) +# define __lzo_c99_extern_inline extern __lzo_inline +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__-0 >= 199901L) +# define __lzo_c99_extern_inline extern __lzo_inline +#endif +#if !defined(__lzo_c99_extern_inline) && (__lzo_HAVE_inline) +# define __lzo_c99_extern_inline __lzo_inline +#endif +#endif +#if defined(__lzo_c99_extern_inline) +# ifndef __lzo_HAVE_c99_extern_inline +# define __lzo_HAVE_c99_extern_inline 1 +# endif +#else +# define __lzo_c99_extern_inline /*empty*/ +#endif +#if !defined(__lzo_may_alias) +#if (LZO_CC_GNUC >= 0x030400ul) +# define __lzo_may_alias __attribute__((__may_alias__)) +#elif (LZO_CC_CLANG >= 0x020900ul) +# define __lzo_may_alias __attribute__((__may_alias__)) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 1210)) && 0 +# define __lzo_may_alias __attribute__((__may_alias__)) +#elif (LZO_CC_PGI >= 0x0d0a00ul) && 0 +# define __lzo_may_alias __attribute__((__may_alias__)) +#endif +#endif +#if defined(__lzo_may_alias) +# ifndef __lzo_HAVE_may_alias +# define __lzo_HAVE_may_alias 1 +# endif +#else +# define __lzo_may_alias /*empty*/ +#endif +#if !defined(__lzo_noreturn) +#if (LZO_CC_GNUC >= 0x020700ul) +# define __lzo_noreturn __attribute__((__noreturn__)) +#elif (LZO_CC_IBMC >= 700) +# define __lzo_noreturn __attribute__((__noreturn__)) +#elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 450)) +# define __lzo_noreturn __declspec(noreturn) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 600)) +# define __lzo_noreturn __attribute__((__noreturn__)) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_noreturn __attribute__((__noreturn__)) +#elif (LZO_CC_MSC && (_MSC_VER >= 1200)) +# define __lzo_noreturn __declspec(noreturn) +#elif (LZO_CC_PGI >= 0x0d0a00ul) +# define __lzo_noreturn __attribute__((__noreturn__)) +#endif +#endif +#if defined(__lzo_noreturn) +# ifndef __lzo_HAVE_noreturn +# define __lzo_HAVE_noreturn 1 +# endif +#else +# define __lzo_noreturn /*empty*/ +#endif +#if !defined(__lzo_nothrow) +#if (LZO_CC_GNUC >= 0x030300ul) +# define __lzo_nothrow __attribute__((__nothrow__)) +#elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 450)) && defined(__cplusplus) +# define __lzo_nothrow __declspec(nothrow) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 900)) +# define __lzo_nothrow __attribute__((__nothrow__)) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_nothrow __attribute__((__nothrow__)) +#elif (LZO_CC_MSC && (_MSC_VER >= 1200)) && defined(__cplusplus) +# define __lzo_nothrow __declspec(nothrow) +#endif +#endif +#if defined(__lzo_nothrow) +# ifndef __lzo_HAVE_nothrow +# define __lzo_HAVE_nothrow 1 +# endif +#else +# define __lzo_nothrow /*empty*/ +#endif +#if !defined(__lzo_restrict) +#if (LZO_CC_GNUC >= 0x030400ul) +# define __lzo_restrict __restrict__ +#elif (LZO_CC_IBMC >= 800) && !defined(__cplusplus) +# define __lzo_restrict __restrict__ +#elif (LZO_CC_IBMC >= 1210) +# define __lzo_restrict __restrict__ +#elif (LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 600)) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 600)) +# define __lzo_restrict __restrict__ +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM) +# define __lzo_restrict __restrict__ +#elif (LZO_CC_MSC && (_MSC_VER >= 1400)) +# define __lzo_restrict __restrict +#elif (LZO_CC_PGI >= 0x0d0a00ul) +# define __lzo_restrict __restrict__ +#endif +#endif +#if defined(__lzo_restrict) +# ifndef __lzo_HAVE_restrict +# define __lzo_HAVE_restrict 1 +# endif +#else +# define __lzo_restrict /*empty*/ +#endif +#if !defined(__lzo_alignof) +#if (LZO_CC_ARMCC || LZO_CC_CILLY || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) +# define __lzo_alignof(e) __alignof__(e) +#elif (LZO_CC_GHS) && !defined(__cplusplus) +# define __lzo_alignof(e) __alignof__(e) +#elif (LZO_CC_IBMC >= 600) +# define __lzo_alignof(e) (__lzo_gnuc_extension__ __alignof__(e)) +#elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 700)) +# define __lzo_alignof(e) __alignof__(e) +#elif (LZO_CC_MSC && (_MSC_VER >= 1300)) +# define __lzo_alignof(e) __alignof(e) +#elif (LZO_CC_SUNPROC >= 0x5100) +# define __lzo_alignof(e) __alignof__(e) +#endif +#endif +#if defined(__lzo_alignof) +# ifndef __lzo_HAVE_alignof +# define __lzo_HAVE_alignof 1 +# endif +#endif +#if !defined(__lzo_struct_packed) +#if (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020800ul)) && defined(__cplusplus) +#elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020700ul)) +#elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020800ul)) && defined(__cplusplus) +#elif (LZO_CC_PCC && (LZO_CC_PCC < 0x010100ul)) +#elif (LZO_CC_SUNPROC && (LZO_CC_SUNPROC < 0x5110)) && !defined(__cplusplus) +#elif (LZO_CC_GNUC >= 0x030400ul) && !(LZO_CC_PCC_GNUC) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) +# define __lzo_struct_packed(s) struct s { +# define __lzo_struct_packed_end() } __attribute__((__gcc_struct__,__packed__)); +# define __lzo_struct_packed_ma_end() } __lzo_may_alias __attribute__((__gcc_struct__,__packed__)); +#elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || (LZO_CC_PGI >= 0x0d0a00ul) || (LZO_CC_SUNPROC >= 0x5100)) +# define __lzo_struct_packed(s) struct s { +# define __lzo_struct_packed_end() } __attribute__((__packed__)); +# define __lzo_struct_packed_ma_end() } __lzo_may_alias __attribute__((__packed__)); +#elif (LZO_CC_IBMC >= 700) +# define __lzo_struct_packed(s) __lzo_gnuc_extension__ struct s { +# define __lzo_struct_packed_end() } __attribute__((__packed__)); +# define __lzo_struct_packed_ma_end() } __lzo_may_alias __attribute__((__packed__)); +#elif (LZO_CC_INTELC_MSC) || (LZO_CC_MSC && (_MSC_VER >= 1300)) +# define __lzo_struct_packed(s) __pragma(pack(push,1)) struct s { +# define __lzo_struct_packed_end() } __pragma(pack(pop)); +#elif (LZO_CC_WATCOMC && (__WATCOMC__ >= 900)) +# define __lzo_struct_packed(s) _Packed struct s { +# define __lzo_struct_packed_end() }; +#endif +#endif +#if defined(__lzo_struct_packed) && !defined(__lzo_struct_packed_ma) +# define __lzo_struct_packed_ma(s) __lzo_struct_packed(s) +#endif +#if defined(__lzo_struct_packed_end) && !defined(__lzo_struct_packed_ma_end) +# define __lzo_struct_packed_ma_end() __lzo_struct_packed_end() +#endif +#if !defined(__lzo_byte_struct) +#if defined(__lzo_struct_packed) +# define __lzo_byte_struct(s,n) __lzo_struct_packed(s) unsigned char a[n]; __lzo_struct_packed_end() +# define __lzo_byte_struct_ma(s,n) __lzo_struct_packed_ma(s) unsigned char a[n]; __lzo_struct_packed_ma_end() +#elif (LZO_CC_CILLY || LZO_CC_CLANG || LZO_CC_PGI || (LZO_CC_SUNPROC >= 0x5100)) +# define __lzo_byte_struct(s,n) struct s { unsigned char a[n]; } __attribute__((__packed__)); +# define __lzo_byte_struct_ma(s,n) struct s { unsigned char a[n]; } __lzo_may_alias __attribute__((__packed__)); +#endif +#endif +#if defined(__lzo_byte_struct) && !defined(__lzo_byte_struct_ma) +# define __lzo_byte_struct_ma(s,n) __lzo_byte_struct(s,n) +#endif +#if !defined(__lzo_struct_align16) && (__lzo_HAVE_alignof) +#if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x030000ul)) +#elif (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020800ul)) && defined(__cplusplus) +#elif (LZO_CC_CILLY || LZO_CC_PCC) +#elif (LZO_CC_INTELC_MSC) || (LZO_CC_MSC && (_MSC_VER >= 1300)) +# define __lzo_struct_align16(s) struct __declspec(align(16)) s { +# define __lzo_struct_align16_end() }; +# define __lzo_struct_align32(s) struct __declspec(align(32)) s { +# define __lzo_struct_align32_end() }; +# define __lzo_struct_align64(s) struct __declspec(align(64)) s { +# define __lzo_struct_align64_end() }; +#elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_GNUC || (LZO_CC_IBMC >= 700) || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_struct_align16(s) struct s { +# define __lzo_struct_align16_end() } __attribute__((__aligned__(16))); +# define __lzo_struct_align32(s) struct s { +# define __lzo_struct_align32_end() } __attribute__((__aligned__(32))); +# define __lzo_struct_align64(s) struct s { +# define __lzo_struct_align64_end() } __attribute__((__aligned__(64))); +#endif +#endif +#if !defined(__lzo_union_um) +#if (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020800ul)) && defined(__cplusplus) +#elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020700ul)) +#elif (LZO_CC_GNUC && (LZO_CC_GNUC < 0x020800ul)) && defined(__cplusplus) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER < 810)) +#elif (LZO_CC_PCC && (LZO_CC_PCC < 0x010100ul)) +#elif (LZO_CC_SUNPROC && (LZO_CC_SUNPROC < 0x5110)) && !defined(__cplusplus) +#elif (LZO_CC_ARMCC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || (LZO_CC_PGI >= 0x0d0a00ul) || (LZO_CC_SUNPROC >= 0x5100)) +# define __lzo_union_am(s) union s { +# define __lzo_union_am_end() } __lzo_may_alias; +# define __lzo_union_um(s) union s { +# define __lzo_union_um_end() } __lzo_may_alias __attribute__((__packed__)); +#elif (LZO_CC_IBMC >= 700) +# define __lzo_union_am(s) __lzo_gnuc_extension__ union s { +# define __lzo_union_am_end() } __lzo_may_alias; +# define __lzo_union_um(s) __lzo_gnuc_extension__ union s { +# define __lzo_union_um_end() } __lzo_may_alias __attribute__((__packed__)); +#elif (LZO_CC_INTELC_MSC) || (LZO_CC_MSC && (_MSC_VER >= 1300)) +# define __lzo_union_um(s) __pragma(pack(push,1)) union s { +# define __lzo_union_um_end() } __pragma(pack(pop)); +#elif (LZO_CC_WATCOMC && (__WATCOMC__ >= 900)) +# define __lzo_union_um(s) _Packed union s { +# define __lzo_union_um_end() }; +#endif +#endif +#if !defined(__lzo_union_am) +# define __lzo_union_am(s) union s { +# define __lzo_union_am_end() }; +#endif +#if !defined(__lzo_constructor) +#if (LZO_CC_GNUC >= 0x030400ul) +# define __lzo_constructor __attribute__((__constructor__,__used__)) +#elif (LZO_CC_GNUC >= 0x020700ul) +# define __lzo_constructor __attribute__((__constructor__)) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) +# define __lzo_constructor __attribute__((__constructor__,__used__)) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_constructor __attribute__((__constructor__)) +#endif +#endif +#if defined(__lzo_constructor) +# ifndef __lzo_HAVE_constructor +# define __lzo_HAVE_constructor 1 +# endif +#endif +#if !defined(__lzo_destructor) +#if (LZO_CC_GNUC >= 0x030400ul) +# define __lzo_destructor __attribute__((__destructor__,__used__)) +#elif (LZO_CC_GNUC >= 0x020700ul) +# define __lzo_destructor __attribute__((__destructor__)) +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 800)) +# define __lzo_destructor __attribute__((__destructor__,__used__)) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_destructor __attribute__((__destructor__)) +#endif +#endif +#if defined(__lzo_destructor) +# ifndef __lzo_HAVE_destructor +# define __lzo_HAVE_destructor 1 +# endif +#endif +#if (__lzo_HAVE_destructor) && !(__lzo_HAVE_constructor) +# error "unexpected configuration - check your compiler defines" +#endif +#if !defined(__lzo_likely) && !defined(__lzo_unlikely) +#if (LZO_CC_GNUC >= 0x030200ul) +# define __lzo_likely(e) (__builtin_expect(!!(e),1)) +# define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) +#elif (LZO_CC_IBMC >= 1010) +# define __lzo_likely(e) (__builtin_expect(!!(e),1)) +# define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) +#elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 800)) +# define __lzo_likely(e) (__builtin_expect(!!(e),1)) +# define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) +#elif (LZO_CC_CLANG && LZO_CC_CLANG_C2) +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define __lzo_likely(e) (__builtin_expect(!!(e),1)) +# define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) +#endif +#endif +#if defined(__lzo_likely) +# ifndef __lzo_HAVE_likely +# define __lzo_HAVE_likely 1 +# endif +#else +# define __lzo_likely(e) (e) +#endif +#if defined(__lzo_very_likely) +# ifndef __lzo_HAVE_very_likely +# define __lzo_HAVE_very_likely 1 +# endif +#else +# define __lzo_very_likely(e) __lzo_likely(e) +#endif +#if defined(__lzo_unlikely) +# ifndef __lzo_HAVE_unlikely +# define __lzo_HAVE_unlikely 1 +# endif +#else +# define __lzo_unlikely(e) (e) +#endif +#if defined(__lzo_very_unlikely) +# ifndef __lzo_HAVE_very_unlikely +# define __lzo_HAVE_very_unlikely 1 +# endif +#else +# define __lzo_very_unlikely(e) __lzo_unlikely(e) +#endif +#if !defined(__lzo_loop_forever) +# if (LZO_CC_IBMC) +# define __lzo_loop_forever() LZO_BLOCK_BEGIN for (;;) { ; } LZO_BLOCK_END +# else +# define __lzo_loop_forever() do { ; } while __lzo_cte(1) +# endif +#endif +#if !defined(__lzo_unreachable) +#if (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x020800ul)) && lzo_has_builtin(__builtin_unreachable) +# define __lzo_unreachable() __builtin_unreachable(); +#elif (LZO_CC_GNUC >= 0x040500ul) +# define __lzo_unreachable() __builtin_unreachable(); +#elif (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 1300)) && 1 +# define __lzo_unreachable() __builtin_unreachable(); +#endif +#endif +#if defined(__lzo_unreachable) +# ifndef __lzo_HAVE_unreachable +# define __lzo_HAVE_unreachable 1 +# endif +#else +# if 0 +# define __lzo_unreachable() ((void)0); +# else +# define __lzo_unreachable() __lzo_loop_forever(); +# endif +#endif +#if !defined(lzo_unused_funcs_impl) +# if 1 && (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || (LZO_CC_GNUC >= 0x020700ul) || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) +# define lzo_unused_funcs_impl(r,f) static r __attribute__((__unused__)) f +# elif 1 && (LZO_CC_BORLANDC || LZO_CC_GNUC) +# define lzo_unused_funcs_impl(r,f) static r f +# else +# define lzo_unused_funcs_impl(r,f) __lzo_static_forceinline r f +# endif +#endif +#ifndef __LZO_CTA_NAME +#if (LZO_CFG_USE_COUNTER) +# define __LZO_CTA_NAME(a) LZO_PP_ECONCAT2(a,__COUNTER__) +#else +# define __LZO_CTA_NAME(a) LZO_PP_ECONCAT2(a,__LINE__) +#endif +#endif +#if !defined(LZO_COMPILE_TIME_ASSERT_HEADER) +# if (LZO_CC_AZTECC || LZO_CC_ZORTECHC) +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1-!(e)]; LZO_EXTERN_C_END +# elif (LZO_CC_DMC || LZO_CC_SYMANTECC) +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1u-2*!(e)]; LZO_EXTERN_C_END +# elif (LZO_CC_TURBOC && (__TURBOC__ == 0x0295)) +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1-!(e)]; LZO_EXTERN_C_END +# elif (LZO_CC_CLANG && (LZO_CC_CLANG < 0x020900ul)) && defined(__cplusplus) +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN int __LZO_CTA_NAME(lzo_cta_f__)(int [1-2*!(e)]); LZO_EXTERN_C_END +# elif (LZO_CC_GNUC) && defined(__CHECKER__) && defined(__SPARSE_CHECKER__) +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN enum {__LZO_CTA_NAME(lzo_cta_e__)=1/!!(e)} __attribute__((__unused__)); LZO_EXTERN_C_END +# else +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) LZO_EXTERN_C_BEGIN extern int __LZO_CTA_NAME(lzo_cta__)[1-2*!(e)]; LZO_EXTERN_C_END +# endif +#endif +#if !defined(LZO_COMPILE_TIME_ASSERT) +# if (LZO_CC_AZTECC) +# define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-!(e)];} +# elif (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x030000ul)) +# define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-2*!(e)] __attribute__((__unused__));} +# elif (LZO_CC_DMC || LZO_CC_PACIFICC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) +# define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; +# elif (LZO_CC_GNUC) && defined(__CHECKER__) && defined(__SPARSE_CHECKER__) +# define LZO_COMPILE_TIME_ASSERT(e) {(void) (0/!!(e));} +# elif (LZO_CC_GNUC >= 0x040700ul) && (LZO_CFG_USE_COUNTER) && defined(__cplusplus) +# define LZO_COMPILE_TIME_ASSERT(e) {enum {__LZO_CTA_NAME(lzo_cta_e__)=1/!!(e)} __attribute__((__unused__));} +# elif (LZO_CC_GNUC >= 0x040700ul) +# define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-2*!(e)] __attribute__((__unused__));} +# elif (LZO_CC_MSC && (_MSC_VER < 900)) +# define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; +# elif (LZO_CC_TURBOC && (__TURBOC__ == 0x0295)) +# define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; +# else +# define LZO_COMPILE_TIME_ASSERT(e) {typedef int __LZO_CTA_NAME(lzo_cta_t__)[1-2*!(e)];} +# endif +#endif +#if (LZO_LANG_ASSEMBLER) +# undef LZO_COMPILE_TIME_ASSERT_HEADER +# define LZO_COMPILE_TIME_ASSERT_HEADER(e) /*empty*/ +#else +LZO_COMPILE_TIME_ASSERT_HEADER(1 == 1) +#if defined(__cplusplus) +extern "C" { LZO_COMPILE_TIME_ASSERT_HEADER(2 == 2) } +#endif +LZO_COMPILE_TIME_ASSERT_HEADER(3 == 3) +#endif +#if (LZO_ARCH_I086 || LZO_ARCH_I386) && (LZO_OS_DOS16 || LZO_OS_DOS32 || LZO_OS_OS2 || LZO_OS_OS216 || LZO_OS_WIN16 || LZO_OS_WIN32 || LZO_OS_WIN64) +# if (LZO_CC_GNUC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PACIFICC) +# elif (LZO_CC_DMC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) +# define __lzo_cdecl __cdecl +# define __lzo_cdecl_atexit /*empty*/ +# define __lzo_cdecl_main __cdecl +# if (LZO_OS_OS2 && (LZO_CC_DMC || LZO_CC_SYMANTECC)) +# define __lzo_cdecl_qsort __pascal +# elif (LZO_OS_OS2 && (LZO_CC_ZORTECHC)) +# define __lzo_cdecl_qsort _stdcall +# else +# define __lzo_cdecl_qsort __cdecl +# endif +# elif (LZO_CC_WATCOMC) +# define __lzo_cdecl __cdecl +# else +# define __lzo_cdecl __cdecl +# define __lzo_cdecl_atexit __cdecl +# define __lzo_cdecl_main __cdecl +# define __lzo_cdecl_qsort __cdecl +# endif +# if (LZO_CC_GNUC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PACIFICC || LZO_CC_WATCOMC) +# elif (LZO_OS_OS2 && (LZO_CC_DMC || LZO_CC_SYMANTECC)) +# define __lzo_cdecl_sighandler __pascal +# elif (LZO_OS_OS2 && (LZO_CC_ZORTECHC)) +# define __lzo_cdecl_sighandler _stdcall +# elif (LZO_CC_MSC && (_MSC_VER >= 1400)) && defined(_M_CEE_PURE) +# define __lzo_cdecl_sighandler __clrcall +# elif (LZO_CC_MSC && (_MSC_VER >= 600 && _MSC_VER < 700)) +# if defined(_DLL) +# define __lzo_cdecl_sighandler _far _cdecl _loadds +# elif defined(_MT) +# define __lzo_cdecl_sighandler _far _cdecl +# else +# define __lzo_cdecl_sighandler _cdecl +# endif +# else +# define __lzo_cdecl_sighandler __cdecl +# endif +#elif (LZO_ARCH_I386) && (LZO_CC_WATCOMC) +# define __lzo_cdecl __cdecl +#elif (LZO_ARCH_M68K && LZO_OS_TOS && (LZO_CC_PUREC || LZO_CC_TURBOC)) +# define __lzo_cdecl cdecl +#endif +#if !defined(__lzo_cdecl) +# define __lzo_cdecl /*empty*/ +#endif +#if !defined(__lzo_cdecl_atexit) +# define __lzo_cdecl_atexit /*empty*/ +#endif +#if !defined(__lzo_cdecl_main) +# define __lzo_cdecl_main /*empty*/ +#endif +#if !defined(__lzo_cdecl_qsort) +# define __lzo_cdecl_qsort /*empty*/ +#endif +#if !defined(__lzo_cdecl_sighandler) +# define __lzo_cdecl_sighandler /*empty*/ +#endif +#if !defined(__lzo_cdecl_va) +# define __lzo_cdecl_va __lzo_cdecl +#endif +#if !(LZO_CFG_NO_WINDOWS_H) +#if !defined(LZO_HAVE_WINDOWS_H) +#if (LZO_OS_CYGWIN || (LZO_OS_EMX && defined(__RSXNT__)) || LZO_OS_WIN32 || LZO_OS_WIN64) +# if (LZO_CC_WATCOMC && (__WATCOMC__ < 1000)) +# elif ((LZO_OS_WIN32 && defined(__PW32__)) && (LZO_CC_GNUC && (LZO_CC_GNUC < 0x030000ul))) +# elif ((LZO_OS_CYGWIN || defined(__MINGW32__)) && (LZO_CC_GNUC && (LZO_CC_GNUC < 0x025f00ul))) +# else +# define LZO_HAVE_WINDOWS_H 1 +# endif +#endif +#endif +#endif +#define LZO_SIZEOF_CHAR 1 +#ifndef LZO_SIZEOF_SHORT +#if defined(SIZEOF_SHORT) +# define LZO_SIZEOF_SHORT (SIZEOF_SHORT) +#elif defined(__SIZEOF_SHORT__) +# define LZO_SIZEOF_SHORT (__SIZEOF_SHORT__) +#endif +#endif +#ifndef LZO_SIZEOF_INT +#if defined(SIZEOF_INT) +# define LZO_SIZEOF_INT (SIZEOF_INT) +#elif defined(__SIZEOF_INT__) +# define LZO_SIZEOF_INT (__SIZEOF_INT__) +#endif +#endif +#ifndef LZO_SIZEOF_LONG +#if defined(SIZEOF_LONG) +# define LZO_SIZEOF_LONG (SIZEOF_LONG) +#elif defined(__SIZEOF_LONG__) +# define LZO_SIZEOF_LONG (__SIZEOF_LONG__) +#endif +#endif +#ifndef LZO_SIZEOF_LONG_LONG +#if defined(SIZEOF_LONG_LONG) +# define LZO_SIZEOF_LONG_LONG (SIZEOF_LONG_LONG) +#elif defined(__SIZEOF_LONG_LONG__) +# define LZO_SIZEOF_LONG_LONG (__SIZEOF_LONG_LONG__) +#endif +#endif +#ifndef LZO_SIZEOF___INT16 +#if defined(SIZEOF___INT16) +# define LZO_SIZEOF___INT16 (SIZEOF___INT16) +#endif +#endif +#ifndef LZO_SIZEOF___INT32 +#if defined(SIZEOF___INT32) +# define LZO_SIZEOF___INT32 (SIZEOF___INT32) +#endif +#endif +#ifndef LZO_SIZEOF___INT64 +#if defined(SIZEOF___INT64) +# define LZO_SIZEOF___INT64 (SIZEOF___INT64) +#endif +#endif +#ifndef LZO_SIZEOF_VOID_P +#if defined(SIZEOF_VOID_P) +# define LZO_SIZEOF_VOID_P (SIZEOF_VOID_P) +#elif defined(__SIZEOF_POINTER__) +# define LZO_SIZEOF_VOID_P (__SIZEOF_POINTER__) +#endif +#endif +#ifndef LZO_SIZEOF_SIZE_T +#if defined(SIZEOF_SIZE_T) +# define LZO_SIZEOF_SIZE_T (SIZEOF_SIZE_T) +#elif defined(__SIZEOF_SIZE_T__) +# define LZO_SIZEOF_SIZE_T (__SIZEOF_SIZE_T__) +#endif +#endif +#ifndef LZO_SIZEOF_PTRDIFF_T +#if defined(SIZEOF_PTRDIFF_T) +# define LZO_SIZEOF_PTRDIFF_T (SIZEOF_PTRDIFF_T) +#elif defined(__SIZEOF_PTRDIFF_T__) +# define LZO_SIZEOF_PTRDIFF_T (__SIZEOF_PTRDIFF_T__) +#endif +#endif +#define __LZO_LSR(x,b) (((x)+0ul) >> (b)) +#if !defined(LZO_SIZEOF_SHORT) +# if (LZO_ARCH_CRAY_PVP) +# define LZO_SIZEOF_SHORT 8 +# elif (USHRT_MAX == LZO_0xffffL) +# define LZO_SIZEOF_SHORT 2 +# elif (__LZO_LSR(USHRT_MAX,7) == 1) +# define LZO_SIZEOF_SHORT 1 +# elif (__LZO_LSR(USHRT_MAX,15) == 1) +# define LZO_SIZEOF_SHORT 2 +# elif (__LZO_LSR(USHRT_MAX,31) == 1) +# define LZO_SIZEOF_SHORT 4 +# elif (__LZO_LSR(USHRT_MAX,63) == 1) +# define LZO_SIZEOF_SHORT 8 +# elif (__LZO_LSR(USHRT_MAX,127) == 1) +# define LZO_SIZEOF_SHORT 16 +# else +# error "LZO_SIZEOF_SHORT" +# endif +#endif +LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_SHORT == sizeof(short)) +#if !defined(LZO_SIZEOF_INT) +# if (LZO_ARCH_CRAY_PVP) +# define LZO_SIZEOF_INT 8 +# elif (UINT_MAX == LZO_0xffffL) +# define LZO_SIZEOF_INT 2 +# elif (UINT_MAX == LZO_0xffffffffL) +# define LZO_SIZEOF_INT 4 +# elif (__LZO_LSR(UINT_MAX,7) == 1) +# define LZO_SIZEOF_INT 1 +# elif (__LZO_LSR(UINT_MAX,15) == 1) +# define LZO_SIZEOF_INT 2 +# elif (__LZO_LSR(UINT_MAX,31) == 1) +# define LZO_SIZEOF_INT 4 +# elif (__LZO_LSR(UINT_MAX,63) == 1) +# define LZO_SIZEOF_INT 8 +# elif (__LZO_LSR(UINT_MAX,127) == 1) +# define LZO_SIZEOF_INT 16 +# else +# error "LZO_SIZEOF_INT" +# endif +#endif +LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_INT == sizeof(int)) +#if !defined(LZO_SIZEOF_LONG) +# if (ULONG_MAX == LZO_0xffffffffL) +# define LZO_SIZEOF_LONG 4 +# elif (__LZO_LSR(ULONG_MAX,7) == 1) +# define LZO_SIZEOF_LONG 1 +# elif (__LZO_LSR(ULONG_MAX,15) == 1) +# define LZO_SIZEOF_LONG 2 +# elif (__LZO_LSR(ULONG_MAX,31) == 1) +# define LZO_SIZEOF_LONG 4 +# elif (__LZO_LSR(ULONG_MAX,39) == 1) +# define LZO_SIZEOF_LONG 5 +# elif (__LZO_LSR(ULONG_MAX,63) == 1) +# define LZO_SIZEOF_LONG 8 +# elif (__LZO_LSR(ULONG_MAX,127) == 1) +# define LZO_SIZEOF_LONG 16 +# else +# error "LZO_SIZEOF_LONG" +# endif +#endif +LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_LONG == sizeof(long)) +#if !defined(LZO_SIZEOF_LONG_LONG) && !defined(LZO_SIZEOF___INT64) +#if (LZO_SIZEOF_LONG > 0 && LZO_SIZEOF_LONG < 8) +# if defined(__LONG_MAX__) && defined(__LONG_LONG_MAX__) +# if (LZO_CC_GNUC >= 0x030300ul) +# if ((__LONG_MAX__-0) == (__LONG_LONG_MAX__-0)) +# define LZO_SIZEOF_LONG_LONG LZO_SIZEOF_LONG +# elif (__LZO_LSR(__LONG_LONG_MAX__,30) == 1) +# define LZO_SIZEOF_LONG_LONG 4 +# endif +# endif +# endif +#endif +#endif +#if !defined(LZO_SIZEOF_LONG_LONG) && !defined(LZO_SIZEOF___INT64) +#if (LZO_SIZEOF_LONG > 0 && LZO_SIZEOF_LONG < 8) +#if (LZO_ARCH_I086 && LZO_CC_DMC) +#elif (LZO_CC_CILLY) && defined(__GNUC__) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) +# define LZO_SIZEOF_LONG_LONG 8 +#elif ((LZO_OS_WIN32 || LZO_OS_WIN64 || defined(_WIN32)) && LZO_CC_MSC && (_MSC_VER >= 1400)) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_OS_WIN64 || defined(_WIN64)) +# define LZO_SIZEOF___INT64 8 +#elif (LZO_ARCH_I386 && (LZO_CC_DMC)) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_ARCH_I386 && (LZO_CC_SYMANTECC && (__SC__ >= 0x700))) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_ARCH_I386 && (LZO_CC_INTELC && defined(__linux__))) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_ARCH_I386 && (LZO_CC_MWERKS || LZO_CC_PELLESC || LZO_CC_PGI || LZO_CC_SUNPROC)) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_ARCH_I386 && (LZO_CC_INTELC || LZO_CC_MSC)) +# define LZO_SIZEOF___INT64 8 +#elif ((LZO_OS_WIN32 || defined(_WIN32)) && (LZO_CC_MSC)) +# define LZO_SIZEOF___INT64 8 +#elif (LZO_ARCH_I386 && (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0520))) +# define LZO_SIZEOF___INT64 8 +#elif (LZO_ARCH_I386 && (LZO_CC_WATCOMC && (__WATCOMC__ >= 1100))) +# define LZO_SIZEOF___INT64 8 +#elif (LZO_CC_GHS && defined(__LLONG_BIT) && ((__LLONG_BIT-0) == 64)) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_CC_WATCOMC && defined(_INTEGRAL_MAX_BITS) && ((_INTEGRAL_MAX_BITS-0) == 64)) +# define LZO_SIZEOF___INT64 8 +#elif (LZO_OS_OS400 || defined(__OS400__)) && defined(__LLP64_IFC__) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (defined(__vms) || defined(__VMS)) && ((__INITIAL_POINTER_SIZE-0) == 64) +# define LZO_SIZEOF_LONG_LONG 8 +#elif (LZO_CC_SDCC) && (LZO_SIZEOF_INT == 2) +#elif 1 && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +# define LZO_SIZEOF_LONG_LONG 8 +#endif +#endif +#endif +#if defined(__cplusplus) && (LZO_CC_GNUC) +# if (LZO_CC_GNUC < 0x020800ul) +# undef LZO_SIZEOF_LONG_LONG +# endif +#endif +#if (LZO_CFG_NO_LONG_LONG) +# undef LZO_SIZEOF_LONG_LONG +#elif defined(__NO_LONG_LONG) +# undef LZO_SIZEOF_LONG_LONG +#elif defined(_NO_LONGLONG) +# undef LZO_SIZEOF_LONG_LONG +#endif +#if !defined(LZO_WORDSIZE) +#if (LZO_ARCH_ALPHA) +# define LZO_WORDSIZE 8 +#elif (LZO_ARCH_AMD64) +# define LZO_WORDSIZE 8 +#elif (LZO_ARCH_ARM64) +# define LZO_WORDSIZE 8 +#elif (LZO_ARCH_AVR) +# define LZO_WORDSIZE 1 +#elif (LZO_ARCH_H8300) +# if defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) +# define LZO_WORDSIZE 4 +# else +# define LZO_WORDSIZE 2 +# endif +#elif (LZO_ARCH_I086) +# define LZO_WORDSIZE 2 +#elif (LZO_ARCH_IA64) +# define LZO_WORDSIZE 8 +#elif (LZO_ARCH_M16C) +# define LZO_WORDSIZE 2 +#elif (LZO_ARCH_SPU) +# define LZO_WORDSIZE 4 +#elif (LZO_ARCH_Z80) +# define LZO_WORDSIZE 1 +#elif (LZO_SIZEOF_LONG == 8) && ((defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__)) +# define LZO_WORDSIZE 8 +#elif (LZO_OS_OS400 || defined(__OS400__)) +# define LZO_WORDSIZE 8 +#elif (defined(__vms) || defined(__VMS)) && (__INITIAL_POINTER_SIZE+0 == 64) +# define LZO_WORDSIZE 8 +#endif +#endif +#if !defined(LZO_SIZEOF_VOID_P) +#if defined(__ILP32__) || defined(__ILP32) || defined(_ILP32) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(int) == 4) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 4) +# define LZO_SIZEOF_VOID_P 4 +#elif defined(__ILP64__) || defined(__ILP64) || defined(_ILP64) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(int) == 8) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 8) +# define LZO_SIZEOF_VOID_P 8 +#elif defined(__LLP64__) || defined(__LLP64) || defined(_LLP64) || defined(_WIN64) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 4) +# define LZO_SIZEOF_VOID_P 8 +#elif defined(__LP64__) || defined(__LP64) || defined(_LP64) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 8) +# define LZO_SIZEOF_VOID_P 8 +#elif (LZO_ARCH_AVR) +# define LZO_SIZEOF_VOID_P 2 +#elif (LZO_ARCH_C166 || LZO_ARCH_MCS51 || LZO_ARCH_MCS251 || LZO_ARCH_MSP430) +# define LZO_SIZEOF_VOID_P 2 +#elif (LZO_ARCH_H8300) +# if defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) + LZO_COMPILE_TIME_ASSERT_HEADER(LZO_WORDSIZE == 4) +# if defined(__NORMAL_MODE__) +# define LZO_SIZEOF_VOID_P 2 +# else +# define LZO_SIZEOF_VOID_P 4 +# endif +# else + LZO_COMPILE_TIME_ASSERT_HEADER(LZO_WORDSIZE == 2) +# define LZO_SIZEOF_VOID_P 2 +# endif +# if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x040000ul)) && (LZO_SIZEOF_INT == 4) +# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_INT +# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_INT +# endif +#elif (LZO_ARCH_I086) +# if (LZO_MM_TINY || LZO_MM_SMALL || LZO_MM_MEDIUM) +# define LZO_SIZEOF_VOID_P 2 +# elif (LZO_MM_COMPACT || LZO_MM_LARGE || LZO_MM_HUGE) +# define LZO_SIZEOF_VOID_P 4 +# else +# error "invalid LZO_ARCH_I086 memory model" +# endif +#elif (LZO_ARCH_M16C) +# if defined(__m32c_cpu__) || defined(__m32cm_cpu__) +# define LZO_SIZEOF_VOID_P 4 +# else +# define LZO_SIZEOF_VOID_P 2 +# endif +#elif (LZO_ARCH_SPU) +# define LZO_SIZEOF_VOID_P 4 +#elif (LZO_ARCH_Z80) +# define LZO_SIZEOF_VOID_P 2 +#elif (LZO_SIZEOF_LONG == 8) && ((defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__)) +# define LZO_SIZEOF_VOID_P 4 +#elif (LZO_OS_OS400 || defined(__OS400__)) +# if defined(__LLP64_IFC__) +# define LZO_SIZEOF_VOID_P 8 +# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG +# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG +# else +# define LZO_SIZEOF_VOID_P 16 +# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG +# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG +# endif +#elif (defined(__vms) || defined(__VMS)) && (__INITIAL_POINTER_SIZE+0 == 64) +# define LZO_SIZEOF_VOID_P 8 +# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG +# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG +#endif +#endif +#if !defined(LZO_SIZEOF_VOID_P) +# define LZO_SIZEOF_VOID_P LZO_SIZEOF_LONG +#endif +LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_VOID_P == sizeof(void *)) +#if !defined(LZO_SIZEOF_SIZE_T) +#if (LZO_ARCH_I086 || LZO_ARCH_M16C) +# define LZO_SIZEOF_SIZE_T 2 +#endif +#endif +#if !defined(LZO_SIZEOF_SIZE_T) +# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_VOID_P +#endif +#if defined(offsetof) +LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_SIZE_T == sizeof(size_t)) +#endif +#if !defined(LZO_SIZEOF_PTRDIFF_T) +#if (LZO_ARCH_I086) +# if (LZO_MM_TINY || LZO_MM_SMALL || LZO_MM_MEDIUM || LZO_MM_HUGE) +# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_VOID_P +# elif (LZO_MM_COMPACT || LZO_MM_LARGE) +# if (LZO_CC_BORLANDC || LZO_CC_TURBOC) +# define LZO_SIZEOF_PTRDIFF_T 4 +# else +# define LZO_SIZEOF_PTRDIFF_T 2 +# endif +# else +# error "invalid LZO_ARCH_I086 memory model" +# endif +#endif +#endif +#if !defined(LZO_SIZEOF_PTRDIFF_T) +# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_SIZE_T +#endif +#if defined(offsetof) +LZO_COMPILE_TIME_ASSERT_HEADER(LZO_SIZEOF_PTRDIFF_T == sizeof(ptrdiff_t)) +#endif +#if !defined(LZO_WORDSIZE) +# define LZO_WORDSIZE LZO_SIZEOF_VOID_P +#endif +#if (LZO_ABI_NEUTRAL_ENDIAN) +# undef LZO_ABI_BIG_ENDIAN +# undef LZO_ABI_LITTLE_ENDIAN +#elif !(LZO_ABI_BIG_ENDIAN) && !(LZO_ABI_LITTLE_ENDIAN) +#if (LZO_ARCH_ALPHA) && (LZO_ARCH_CRAY_MPP) +# define LZO_ABI_BIG_ENDIAN 1 +#elif (LZO_ARCH_IA64) && (LZO_OS_POSIX_LINUX || LZO_OS_WIN64) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif (LZO_ARCH_ALPHA || LZO_ARCH_AMD64 || LZO_ARCH_BLACKFIN || LZO_ARCH_CRIS || LZO_ARCH_I086 || LZO_ARCH_I386 || LZO_ARCH_MSP430 || LZO_ARCH_RISCV) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif (LZO_ARCH_AVR32 || LZO_ARCH_M68K || LZO_ARCH_S390 || LZO_ARCH_SPU) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && defined(__IAR_SYSTEMS_ICC__) && defined(__LITTLE_ENDIAN__) +# if (__LITTLE_ENDIAN__ == 1) +# define LZO_ABI_LITTLE_ENDIAN 1 +# else +# define LZO_ABI_BIG_ENDIAN 1 +# endif +#elif 1 && defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM) && defined(__ARM_BIG_ENDIAN) && ((__ARM_BIG_ENDIAN)+0) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM) && defined(__ARMEB__) && !defined(__ARMEL__) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM) && defined(__ARMEL__) && !defined(__ARMEB__) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM) && defined(_MSC_VER) && defined(_WIN32) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM && LZO_CC_ARMCC_ARMCC) +# if defined(__BIG_ENDIAN) && defined(__LITTLE_ENDIAN) +# error "unexpected configuration - check your compiler defines" +# elif defined(__BIG_ENDIAN) +# define LZO_ABI_BIG_ENDIAN 1 +# else +# define LZO_ABI_LITTLE_ENDIAN 1 +# endif +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM64) && defined(__ARM_BIG_ENDIAN) && ((__ARM_BIG_ENDIAN)+0) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM64) && defined(__AARCH64EB__) && !defined(__AARCH64EL__) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM64) && defined(__AARCH64EL__) && !defined(__AARCH64EB__) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif 1 && (LZO_ARCH_ARM64) && defined(_MSC_VER) && defined(_WIN32) +# define LZO_ABI_LITTLE_ENDIAN 1 +#elif 1 && (LZO_ARCH_MIPS) && defined(__MIPSEB__) && !defined(__MIPSEL__) +# define LZO_ABI_BIG_ENDIAN 1 +#elif 1 && (LZO_ARCH_MIPS) && defined(__MIPSEL__) && !defined(__MIPSEB__) +# define LZO_ABI_LITTLE_ENDIAN 1 +#endif +#endif +#if (LZO_ABI_BIG_ENDIAN) && (LZO_ABI_LITTLE_ENDIAN) +# error "unexpected configuration - check your compiler defines" +#endif +#if (LZO_ABI_BIG_ENDIAN) +# define LZO_INFO_ABI_ENDIAN "be" +#elif (LZO_ABI_LITTLE_ENDIAN) +# define LZO_INFO_ABI_ENDIAN "le" +#elif (LZO_ABI_NEUTRAL_ENDIAN) +# define LZO_INFO_ABI_ENDIAN "neutral" +#endif +#if (LZO_SIZEOF_INT == 1 && LZO_SIZEOF_LONG == 2 && LZO_SIZEOF_VOID_P == 2) +# define LZO_ABI_I8LP16 1 +# define LZO_INFO_ABI_PM "i8lp16" +#elif (LZO_SIZEOF_INT == 2 && LZO_SIZEOF_LONG == 2 && LZO_SIZEOF_VOID_P == 2) +# define LZO_ABI_ILP16 1 +# define LZO_INFO_ABI_PM "ilp16" +#elif (LZO_SIZEOF_INT == 2 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 4) +# define LZO_ABI_LP32 1 +# define LZO_INFO_ABI_PM "lp32" +#elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 4) +# define LZO_ABI_ILP32 1 +# define LZO_INFO_ABI_PM "ilp32" +#elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 8 && LZO_SIZEOF_SIZE_T == 8) +# define LZO_ABI_LLP64 1 +# define LZO_INFO_ABI_PM "llp64" +#elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 8) +# define LZO_ABI_LP64 1 +# define LZO_INFO_ABI_PM "lp64" +#elif (LZO_SIZEOF_INT == 8 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 8) +# define LZO_ABI_ILP64 1 +# define LZO_INFO_ABI_PM "ilp64" +#elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 4) +# define LZO_ABI_IP32L64 1 +# define LZO_INFO_ABI_PM "ip32l64" +#endif +#if (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_VOID_P == 4 && LZO_WORDSIZE == 8) +# define LZO_ABI_IP32W64 1 +# ifndef LZO_INFO_ABI_PM +# define LZO_INFO_ABI_PM "ip32w64" +# endif +#endif +#if 0 +#elif !defined(__LZO_LIBC_OVERRIDE) +#if (LZO_LIBC_NAKED) +# define LZO_INFO_LIBC "naked" +#elif (LZO_LIBC_FREESTANDING) +# define LZO_INFO_LIBC "freestanding" +#elif (LZO_LIBC_MOSTLY_FREESTANDING) +# define LZO_INFO_LIBC "mfreestanding" +#elif (LZO_LIBC_ISOC90) +# define LZO_INFO_LIBC "isoc90" +#elif (LZO_LIBC_ISOC99) +# define LZO_INFO_LIBC "isoc99" +#elif (LZO_CC_ARMCC_ARMCC) && defined(__ARMCLIB_VERSION) +# define LZO_LIBC_ISOC90 1 +# define LZO_INFO_LIBC "isoc90" +#elif defined(__dietlibc__) +# define LZO_LIBC_DIETLIBC 1 +# define LZO_INFO_LIBC "dietlibc" +#elif defined(_NEWLIB_VERSION) +# define LZO_LIBC_NEWLIB 1 +# define LZO_INFO_LIBC "newlib" +#elif defined(__UCLIBC__) && defined(__UCLIBC_MAJOR__) && defined(__UCLIBC_MINOR__) +# if defined(__UCLIBC_SUBLEVEL__) +# define LZO_LIBC_UCLIBC (__UCLIBC_MAJOR__ * 0x10000L + (__UCLIBC_MINOR__-0) * 0x100 + (__UCLIBC_SUBLEVEL__-0)) +# else +# define LZO_LIBC_UCLIBC 0x00090bL +# endif +# define LZO_INFO_LIBC "uc" "libc" +#elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) +# define LZO_LIBC_GLIBC (__GLIBC__ * 0x10000L + (__GLIBC_MINOR__-0) * 0x100) +# define LZO_INFO_LIBC "glibc" +#elif (LZO_CC_MWERKS) && defined(__MSL__) +# define LZO_LIBC_MSL __MSL__ +# define LZO_INFO_LIBC "msl" +#elif 1 && defined(__IAR_SYSTEMS_ICC__) +# define LZO_LIBC_ISOC90 1 +# define LZO_INFO_LIBC "isoc90" +#else +# define LZO_LIBC_DEFAULT 1 +# define LZO_INFO_LIBC "default" +#endif +#endif +#if (LZO_ARCH_I386 && (LZO_OS_DOS32 || LZO_OS_WIN32) && (LZO_CC_DMC || LZO_CC_INTELC || LZO_CC_MSC || LZO_CC_PELLESC)) +# define LZO_ASM_SYNTAX_MSC 1 +#elif (LZO_OS_WIN64 && (LZO_CC_DMC || LZO_CC_INTELC || LZO_CC_MSC || LZO_CC_PELLESC)) +#elif (LZO_ARCH_I386 && LZO_CC_GNUC && (LZO_CC_GNUC == 0x011f00ul)) +#elif (LZO_ARCH_I386 && (LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC || LZO_CC_PATHSCALE)) +# define LZO_ASM_SYNTAX_GNUC 1 +#elif (LZO_ARCH_AMD64 && (LZO_CC_CLANG || LZO_CC_GNUC || LZO_CC_INTELC || LZO_CC_PATHSCALE)) +# define LZO_ASM_SYNTAX_GNUC 1 +#elif (LZO_CC_GNUC) +# define LZO_ASM_SYNTAX_GNUC 1 +#endif +#if (LZO_ASM_SYNTAX_GNUC) +#if (LZO_ARCH_I386 && LZO_CC_GNUC && (LZO_CC_GNUC < 0x020000ul)) +# define __LZO_ASM_CLOBBER "ax" +# define __LZO_ASM_CLOBBER_LIST_CC /*empty*/ +# define __LZO_ASM_CLOBBER_LIST_CC_MEMORY /*empty*/ +# define __LZO_ASM_CLOBBER_LIST_EMPTY /*empty*/ +#elif (LZO_CC_INTELC && (__INTEL_COMPILER < 1000)) +# define __LZO_ASM_CLOBBER "memory" +# define __LZO_ASM_CLOBBER_LIST_CC /*empty*/ +# define __LZO_ASM_CLOBBER_LIST_CC_MEMORY : "memory" +# define __LZO_ASM_CLOBBER_LIST_EMPTY /*empty*/ +#else +# define __LZO_ASM_CLOBBER "cc", "memory" +# define __LZO_ASM_CLOBBER_LIST_CC : "cc" +# define __LZO_ASM_CLOBBER_LIST_CC_MEMORY : "cc", "memory" +# define __LZO_ASM_CLOBBER_LIST_EMPTY /*empty*/ +#endif +#endif +#if (LZO_ARCH_ALPHA) +# define LZO_OPT_AVOID_UINT_INDEX 1 +#elif (LZO_ARCH_AMD64) +# define LZO_OPT_AVOID_INT_INDEX 1 +# define LZO_OPT_AVOID_UINT_INDEX 1 +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# ifndef LZO_OPT_UNALIGNED64 +# define LZO_OPT_UNALIGNED64 1 +# endif +#elif (LZO_ARCH_ARM) +# if defined(__ARM_FEATURE_UNALIGNED) +# if ((__ARM_FEATURE_UNALIGNED)+0) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# endif +# elif 1 && (LZO_ARCH_ARM_THUMB2) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# elif 1 && defined(__ARM_ARCH) && ((__ARM_ARCH)+0 >= 7) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# elif 1 && defined(__TARGET_ARCH_ARM) && ((__TARGET_ARCH_ARM)+0 >= 7) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# elif 1 && defined(__TARGET_ARCH_ARM) && ((__TARGET_ARCH_ARM)+0 >= 6) && (defined(__TARGET_PROFILE_A) || defined(__TARGET_PROFILE_R)) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# elif 1 && defined(_MSC_VER) && defined(_M_ARM) && ((_M_ARM)+0 >= 7) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# endif +#elif (LZO_ARCH_ARM64) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# ifndef LZO_OPT_UNALIGNED64 +# define LZO_OPT_UNALIGNED64 1 +# endif +#elif (LZO_ARCH_CRIS) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +#elif (LZO_ARCH_I386) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +#elif (LZO_ARCH_IA64) +# define LZO_OPT_AVOID_INT_INDEX 1 +# define LZO_OPT_AVOID_UINT_INDEX 1 +# define LZO_OPT_PREFER_POSTINC 1 +#elif (LZO_ARCH_M68K) +# define LZO_OPT_PREFER_POSTINC 1 +# define LZO_OPT_PREFER_PREDEC 1 +# if defined(__mc68020__) && !defined(__mcoldfire__) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# endif +#elif (LZO_ARCH_MIPS) +# define LZO_OPT_AVOID_UINT_INDEX 1 +#elif (LZO_ARCH_POWERPC) +# define LZO_OPT_PREFER_PREINC 1 +# define LZO_OPT_PREFER_PREDEC 1 +# if (LZO_ABI_BIG_ENDIAN) || (LZO_WORDSIZE == 8) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# if (LZO_WORDSIZE == 8) +# ifndef LZO_OPT_UNALIGNED64 +# define LZO_OPT_UNALIGNED64 1 +# endif +# endif +# endif +#elif (LZO_ARCH_RISCV) +# define LZO_OPT_AVOID_UINT_INDEX 1 +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# if (LZO_WORDSIZE == 8) +# ifndef LZO_OPT_UNALIGNED64 +# define LZO_OPT_UNALIGNED64 1 +# endif +# endif +#elif (LZO_ARCH_S390) +# ifndef LZO_OPT_UNALIGNED16 +# define LZO_OPT_UNALIGNED16 1 +# endif +# ifndef LZO_OPT_UNALIGNED32 +# define LZO_OPT_UNALIGNED32 1 +# endif +# if (LZO_WORDSIZE == 8) +# ifndef LZO_OPT_UNALIGNED64 +# define LZO_OPT_UNALIGNED64 1 +# endif +# endif +#elif (LZO_ARCH_SH) +# define LZO_OPT_PREFER_POSTINC 1 +# define LZO_OPT_PREFER_PREDEC 1 +#endif +#ifndef LZO_CFG_NO_INLINE_ASM +#if (LZO_ABI_NEUTRAL_ENDIAN) || (LZO_ARCH_GENERIC) +# define LZO_CFG_NO_INLINE_ASM 1 +#elif (LZO_CC_LLVM) +# define LZO_CFG_NO_INLINE_ASM 1 +#endif +#endif +#if (LZO_CFG_NO_INLINE_ASM) +# undef LZO_ASM_SYNTAX_MSC +# undef LZO_ASM_SYNTAX_GNUC +# undef __LZO_ASM_CLOBBER +# undef __LZO_ASM_CLOBBER_LIST_CC +# undef __LZO_ASM_CLOBBER_LIST_CC_MEMORY +# undef __LZO_ASM_CLOBBER_LIST_EMPTY +#endif +#ifndef LZO_CFG_NO_UNALIGNED +#if (LZO_ABI_NEUTRAL_ENDIAN) || (LZO_ARCH_GENERIC) +# define LZO_CFG_NO_UNALIGNED 1 +#endif +#endif +#if (LZO_CFG_NO_UNALIGNED) +# undef LZO_OPT_UNALIGNED16 +# undef LZO_OPT_UNALIGNED32 +# undef LZO_OPT_UNALIGNED64 +#endif +#if defined(__LZO_INFOSTR_MM) +#elif (LZO_MM_FLAT) && (defined(__LZO_INFOSTR_PM) || defined(LZO_INFO_ABI_PM)) +# define __LZO_INFOSTR_MM "" +#elif defined(LZO_INFO_MM) +# define __LZO_INFOSTR_MM "." LZO_INFO_MM +#else +# define __LZO_INFOSTR_MM "" +#endif +#if defined(__LZO_INFOSTR_PM) +#elif defined(LZO_INFO_ABI_PM) +# define __LZO_INFOSTR_PM "." LZO_INFO_ABI_PM +#else +# define __LZO_INFOSTR_PM "" +#endif +#if defined(__LZO_INFOSTR_ENDIAN) +#elif defined(LZO_INFO_ABI_ENDIAN) +# define __LZO_INFOSTR_ENDIAN "." LZO_INFO_ABI_ENDIAN +#else +# define __LZO_INFOSTR_ENDIAN "" +#endif +#if defined(__LZO_INFOSTR_OSNAME) +#elif defined(LZO_INFO_OS_CONSOLE) +# define __LZO_INFOSTR_OSNAME LZO_INFO_OS "." LZO_INFO_OS_CONSOLE +#elif defined(LZO_INFO_OS_POSIX) +# define __LZO_INFOSTR_OSNAME LZO_INFO_OS "." LZO_INFO_OS_POSIX +#else +# define __LZO_INFOSTR_OSNAME LZO_INFO_OS +#endif +#if defined(__LZO_INFOSTR_LIBC) +#elif defined(LZO_INFO_LIBC) +# define __LZO_INFOSTR_LIBC "." LZO_INFO_LIBC +#else +# define __LZO_INFOSTR_LIBC "" +#endif +#if defined(__LZO_INFOSTR_CCVER) +#elif defined(LZO_INFO_CCVER) +# define __LZO_INFOSTR_CCVER " " LZO_INFO_CCVER +#else +# define __LZO_INFOSTR_CCVER "" +#endif +#define LZO_INFO_STRING \ + LZO_INFO_ARCH __LZO_INFOSTR_MM __LZO_INFOSTR_PM __LZO_INFOSTR_ENDIAN \ + " " __LZO_INFOSTR_OSNAME __LZO_INFOSTR_LIBC " " LZO_INFO_CC __LZO_INFOSTR_CCVER +#if !(LZO_CFG_SKIP_LZO_TYPES) +#if (!(LZO_SIZEOF_SHORT+0 > 0 && LZO_SIZEOF_INT+0 > 0 && LZO_SIZEOF_LONG+0 > 0)) +# error "missing defines for sizes" +#endif +#if (!(LZO_SIZEOF_PTRDIFF_T+0 > 0 && LZO_SIZEOF_SIZE_T+0 > 0 && LZO_SIZEOF_VOID_P+0 > 0)) +# error "missing defines for sizes" +#endif +#define LZO_TYPEOF_CHAR 1u +#define LZO_TYPEOF_SHORT 2u +#define LZO_TYPEOF_INT 3u +#define LZO_TYPEOF_LONG 4u +#define LZO_TYPEOF_LONG_LONG 5u +#define LZO_TYPEOF___INT8 17u +#define LZO_TYPEOF___INT16 18u +#define LZO_TYPEOF___INT32 19u +#define LZO_TYPEOF___INT64 20u +#define LZO_TYPEOF___INT128 21u +#define LZO_TYPEOF___INT256 22u +#define LZO_TYPEOF___MODE_QI 33u +#define LZO_TYPEOF___MODE_HI 34u +#define LZO_TYPEOF___MODE_SI 35u +#define LZO_TYPEOF___MODE_DI 36u +#define LZO_TYPEOF___MODE_TI 37u +#define LZO_TYPEOF_CHAR_P 129u +#if !defined(lzo_llong_t) +#if (LZO_SIZEOF_LONG_LONG+0 > 0) +# if !(LZO_LANG_ASSEMBLER) + __lzo_gnuc_extension__ typedef long long lzo_llong_t__; + __lzo_gnuc_extension__ typedef unsigned long long lzo_ullong_t__; +# endif +# define lzo_llong_t lzo_llong_t__ +# define lzo_ullong_t lzo_ullong_t__ +#endif +#endif +#if !defined(lzo_int16e_t) +#if (LZO_CFG_PREFER_TYPEOF_ACC_INT16E_T == LZO_TYPEOF_SHORT) && (LZO_SIZEOF_SHORT != 2) +# undef LZO_CFG_PREFER_TYPEOF_ACC_INT16E_T +#endif +#if (LZO_SIZEOF_LONG == 2) && !(LZO_CFG_PREFER_TYPEOF_ACC_INT16E_T == LZO_TYPEOF_SHORT) +# define lzo_int16e_t long +# define lzo_uint16e_t unsigned long +# define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF_LONG +#elif (LZO_SIZEOF_INT == 2) && !(LZO_CFG_PREFER_TYPEOF_ACC_INT16E_T == LZO_TYPEOF_SHORT) +# define lzo_int16e_t int +# define lzo_uint16e_t unsigned int +# define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF_INT +#elif (LZO_SIZEOF_SHORT == 2) +# define lzo_int16e_t short int +# define lzo_uint16e_t unsigned short int +# define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF_SHORT +#elif 1 && !(LZO_CFG_TYPE_NO_MODE_HI) && (LZO_CC_CLANG || (LZO_CC_GNUC >= 0x025f00ul) || LZO_CC_LLVM) +# if !(LZO_LANG_ASSEMBLER) + typedef int lzo_int16e_hi_t__ __attribute__((__mode__(__HI__))); + typedef unsigned int lzo_uint16e_hi_t__ __attribute__((__mode__(__HI__))); +# endif +# define lzo_int16e_t lzo_int16e_hi_t__ +# define lzo_uint16e_t lzo_uint16e_hi_t__ +# define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF___MODE_HI +#elif (LZO_SIZEOF___INT16 == 2) +# define lzo_int16e_t __int16 +# define lzo_uint16e_t unsigned __int16 +# define LZO_TYPEOF_LZO_INT16E_T LZO_TYPEOF___INT16 +#else +#endif +#endif +#if defined(lzo_int16e_t) +# define LZO_SIZEOF_LZO_INT16E_T 2 + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16e_t) == 2) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16e_t) == LZO_SIZEOF_LZO_INT16E_T) +#endif +#if !defined(lzo_int32e_t) +#if (LZO_CFG_PREFER_TYPEOF_ACC_INT32E_T == LZO_TYPEOF_INT) && (LZO_SIZEOF_INT != 4) +# undef LZO_CFG_PREFER_TYPEOF_ACC_INT32E_T +#endif +#if (LZO_SIZEOF_LONG == 4) && !(LZO_CFG_PREFER_TYPEOF_ACC_INT32E_T == LZO_TYPEOF_INT) +# define lzo_int32e_t long int +# define lzo_uint32e_t unsigned long int +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_LONG +#elif (LZO_SIZEOF_INT == 4) +# define lzo_int32e_t int +# define lzo_uint32e_t unsigned int +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_INT +#elif (LZO_SIZEOF_SHORT == 4) +# define lzo_int32e_t short int +# define lzo_uint32e_t unsigned short int +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_SHORT +#elif (LZO_SIZEOF_LONG_LONG == 4) +# define lzo_int32e_t lzo_llong_t +# define lzo_uint32e_t lzo_ullong_t +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF_LONG_LONG +#elif 1 && !(LZO_CFG_TYPE_NO_MODE_SI) && (LZO_CC_CLANG || (LZO_CC_GNUC >= 0x025f00ul) || LZO_CC_LLVM) && (__INT_MAX__+0 > 2147483647L) +# if !(LZO_LANG_ASSEMBLER) + typedef int lzo_int32e_si_t__ __attribute__((__mode__(__SI__))); + typedef unsigned int lzo_uint32e_si_t__ __attribute__((__mode__(__SI__))); +# endif +# define lzo_int32e_t lzo_int32e_si_t__ +# define lzo_uint32e_t lzo_uint32e_si_t__ +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF___MODE_SI +#elif 1 && !(LZO_CFG_TYPE_NO_MODE_SI) && (LZO_CC_GNUC >= 0x025f00ul) && defined(__AVR__) && (__LONG_MAX__+0 == 32767L) +# if !(LZO_LANG_ASSEMBLER) + typedef int lzo_int32e_si_t__ __attribute__((__mode__(__SI__))); + typedef unsigned int lzo_uint32e_si_t__ __attribute__((__mode__(__SI__))); +# endif +# define lzo_int32e_t lzo_int32e_si_t__ +# define lzo_uint32e_t lzo_uint32e_si_t__ +# define LZO_INT32_C(c) (c##LL) +# define LZO_UINT32_C(c) (c##ULL) +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF___MODE_SI +#elif (LZO_SIZEOF___INT32 == 4) +# define lzo_int32e_t __int32 +# define lzo_uint32e_t unsigned __int32 +# define LZO_TYPEOF_LZO_INT32E_T LZO_TYPEOF___INT32 +#else +#endif +#endif +#if defined(lzo_int32e_t) +# define LZO_SIZEOF_LZO_INT32E_T 4 + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32e_t) == 4) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32e_t) == LZO_SIZEOF_LZO_INT32E_T) +#endif +#if !defined(lzo_int64e_t) +#if (LZO_SIZEOF___INT64 == 8) +# if (LZO_CC_BORLANDC) && !defined(LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T) +# define LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T LZO_TYPEOF___INT64 +# endif +#endif +#if (LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T == LZO_TYPEOF_LONG_LONG) && (LZO_SIZEOF_LONG_LONG != 8) +# undef LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T +#endif +#if (LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T == LZO_TYPEOF___INT64) && (LZO_SIZEOF___INT64 != 8) +# undef LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T +#endif +#if (LZO_SIZEOF_INT == 8) && (LZO_SIZEOF_INT < LZO_SIZEOF_LONG) +# define lzo_int64e_t int +# define lzo_uint64e_t unsigned int +# define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF_INT +#elif (LZO_SIZEOF_LONG == 8) && !(LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T == LZO_TYPEOF_LONG_LONG) && !(LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T == LZO_TYPEOF___INT64) +# define lzo_int64e_t long int +# define lzo_uint64e_t unsigned long int +# define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF_LONG +#elif (LZO_SIZEOF_LONG_LONG == 8) && !(LZO_CFG_PREFER_TYPEOF_ACC_INT64E_T == LZO_TYPEOF___INT64) +# define lzo_int64e_t lzo_llong_t +# define lzo_uint64e_t lzo_ullong_t +# define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF_LONG_LONG +# if (LZO_CC_BORLANDC) +# define LZO_INT64_C(c) ((c) + 0ll) +# define LZO_UINT64_C(c) ((c) + 0ull) +# elif 0 +# define LZO_INT64_C(c) (__lzo_gnuc_extension__ (c##LL)) +# define LZO_UINT64_C(c) (__lzo_gnuc_extension__ (c##ULL)) +# else +# define LZO_INT64_C(c) (c##LL) +# define LZO_UINT64_C(c) (c##ULL) +# endif +#elif (LZO_SIZEOF___INT64 == 8) +# define lzo_int64e_t __int64 +# define lzo_uint64e_t unsigned __int64 +# define LZO_TYPEOF_LZO_INT64E_T LZO_TYPEOF___INT64 +# if (LZO_CC_BORLANDC) +# define LZO_INT64_C(c) ((c) + 0i64) +# define LZO_UINT64_C(c) ((c) + 0ui64) +# else +# define LZO_INT64_C(c) (c##i64) +# define LZO_UINT64_C(c) (c##ui64) +# endif +#else +#endif +#endif +#if defined(lzo_int64e_t) +# define LZO_SIZEOF_LZO_INT64E_T 8 + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64e_t) == 8) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64e_t) == LZO_SIZEOF_LZO_INT64E_T) +#endif +#if !defined(lzo_int32l_t) +#if defined(lzo_int32e_t) +# define lzo_int32l_t lzo_int32e_t +# define lzo_uint32l_t lzo_uint32e_t +# define LZO_SIZEOF_LZO_INT32L_T LZO_SIZEOF_LZO_INT32E_T +# define LZO_TYPEOF_LZO_INT32L_T LZO_TYPEOF_LZO_INT32E_T +#elif (LZO_SIZEOF_INT >= 4) && (LZO_SIZEOF_INT < LZO_SIZEOF_LONG) +# define lzo_int32l_t int +# define lzo_uint32l_t unsigned int +# define LZO_SIZEOF_LZO_INT32L_T LZO_SIZEOF_INT +# define LZO_TYPEOF_LZO_INT32L_T LZO_SIZEOF_INT +#elif (LZO_SIZEOF_LONG >= 4) +# define lzo_int32l_t long int +# define lzo_uint32l_t unsigned long int +# define LZO_SIZEOF_LZO_INT32L_T LZO_SIZEOF_LONG +# define LZO_TYPEOF_LZO_INT32L_T LZO_SIZEOF_LONG +#else +# error "lzo_int32l_t" +#endif +#endif +#if 1 + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32l_t) >= 4) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32l_t) == LZO_SIZEOF_LZO_INT32L_T) +#endif +#if !defined(lzo_int64l_t) +#if defined(lzo_int64e_t) +# define lzo_int64l_t lzo_int64e_t +# define lzo_uint64l_t lzo_uint64e_t +# define LZO_SIZEOF_LZO_INT64L_T LZO_SIZEOF_LZO_INT64E_T +# define LZO_TYPEOF_LZO_INT64L_T LZO_TYPEOF_LZO_INT64E_T +#else +#endif +#endif +#if defined(lzo_int64l_t) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64l_t) >= 8) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64l_t) == LZO_SIZEOF_LZO_INT64L_T) +#endif +#if !defined(lzo_int32f_t) +#if (LZO_SIZEOF_SIZE_T >= 8) +# define lzo_int32f_t lzo_int64l_t +# define lzo_uint32f_t lzo_uint64l_t +# define LZO_SIZEOF_LZO_INT32F_T LZO_SIZEOF_LZO_INT64L_T +# define LZO_TYPEOF_LZO_INT32F_T LZO_TYPEOF_LZO_INT64L_T +#else +# define lzo_int32f_t lzo_int32l_t +# define lzo_uint32f_t lzo_uint32l_t +# define LZO_SIZEOF_LZO_INT32F_T LZO_SIZEOF_LZO_INT32L_T +# define LZO_TYPEOF_LZO_INT32F_T LZO_TYPEOF_LZO_INT32L_T +#endif +#endif +#if 1 + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32f_t) >= 4) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32f_t) == LZO_SIZEOF_LZO_INT32F_T) +#endif +#if !defined(lzo_int64f_t) +#if defined(lzo_int64l_t) +# define lzo_int64f_t lzo_int64l_t +# define lzo_uint64f_t lzo_uint64l_t +# define LZO_SIZEOF_LZO_INT64F_T LZO_SIZEOF_LZO_INT64L_T +# define LZO_TYPEOF_LZO_INT64F_T LZO_TYPEOF_LZO_INT64L_T +#else +#endif +#endif +#if defined(lzo_int64f_t) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64f_t) >= 8) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64f_t) == LZO_SIZEOF_LZO_INT64F_T) +#endif +#if !defined(lzo_intptr_t) +#if 1 && (LZO_OS_OS400 && (LZO_SIZEOF_VOID_P == 16)) +# define __LZO_INTPTR_T_IS_POINTER 1 +# if !(LZO_LANG_ASSEMBLER) + typedef char * lzo_intptr_t; + typedef char * lzo_uintptr_t; +# endif +# define lzo_intptr_t lzo_intptr_t +# define lzo_uintptr_t lzo_uintptr_t +# define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_VOID_P +# define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_CHAR_P +#elif (LZO_CC_MSC && (_MSC_VER >= 1300) && (LZO_SIZEOF_VOID_P == 4) && (LZO_SIZEOF_INT == 4)) +# if !(LZO_LANG_ASSEMBLER) + typedef __w64 int lzo_intptr_t; + typedef __w64 unsigned int lzo_uintptr_t; +# endif +# define lzo_intptr_t lzo_intptr_t +# define lzo_uintptr_t lzo_uintptr_t +# define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_INT +# define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_INT +#elif (LZO_SIZEOF_SHORT == LZO_SIZEOF_VOID_P) && (LZO_SIZEOF_INT > LZO_SIZEOF_VOID_P) +# define lzo_intptr_t short +# define lzo_uintptr_t unsigned short +# define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_SHORT +# define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_SHORT +#elif (LZO_SIZEOF_INT >= LZO_SIZEOF_VOID_P) && (LZO_SIZEOF_INT < LZO_SIZEOF_LONG) +# define lzo_intptr_t int +# define lzo_uintptr_t unsigned int +# define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_INT +# define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_INT +#elif (LZO_SIZEOF_LONG >= LZO_SIZEOF_VOID_P) +# define lzo_intptr_t long +# define lzo_uintptr_t unsigned long +# define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_LONG +# define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_LONG +#elif (LZO_SIZEOF_LZO_INT64L_T >= LZO_SIZEOF_VOID_P) +# define lzo_intptr_t lzo_int64l_t +# define lzo_uintptr_t lzo_uint64l_t +# define LZO_SIZEOF_LZO_INTPTR_T LZO_SIZEOF_LZO_INT64L_T +# define LZO_TYPEOF_LZO_INTPTR_T LZO_TYPEOF_LZO_INT64L_T +#else +# error "lzo_intptr_t" +#endif +#endif +#if 1 + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_intptr_t) >= sizeof(void *)) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_intptr_t) == sizeof(lzo_uintptr_t)) +#endif +#if !defined(lzo_word_t) +#if defined(LZO_WORDSIZE) && (LZO_WORDSIZE+0 > 0) +#if (LZO_WORDSIZE == LZO_SIZEOF_LZO_INTPTR_T) && !(__LZO_INTPTR_T_IS_POINTER) +# define lzo_word_t lzo_uintptr_t +# define lzo_sword_t lzo_intptr_t +# define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_LZO_INTPTR_T +# define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_LZO_INTPTR_T +#elif (LZO_WORDSIZE == LZO_SIZEOF_LONG) +# define lzo_word_t unsigned long +# define lzo_sword_t long +# define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_LONG +# define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_LONG +#elif (LZO_WORDSIZE == LZO_SIZEOF_INT) +# define lzo_word_t unsigned int +# define lzo_sword_t int +# define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_INT +# define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_INT +#elif (LZO_WORDSIZE == LZO_SIZEOF_SHORT) +# define lzo_word_t unsigned short +# define lzo_sword_t short +# define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_SHORT +# define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_SHORT +#elif (LZO_WORDSIZE == 1) +# define lzo_word_t unsigned char +# define lzo_sword_t signed char +# define LZO_SIZEOF_LZO_WORD_T 1 +# define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF_CHAR +#elif (LZO_WORDSIZE == LZO_SIZEOF_LZO_INT64L_T) +# define lzo_word_t lzo_uint64l_t +# define lzo_sword_t lzo_int64l_t +# define LZO_SIZEOF_LZO_WORD_T LZO_SIZEOF_LZO_INT64L_T +# define LZO_TYPEOF_LZO_WORD_T LZO_SIZEOF_LZO_INT64L_T +#elif (LZO_ARCH_SPU) && (LZO_CC_GNUC) +#if 0 +# if !(LZO_LANG_ASSEMBLER) + typedef unsigned lzo_word_t __attribute__((__mode__(__V16QI__))); + typedef int lzo_sword_t __attribute__((__mode__(__V16QI__))); +# endif +# define lzo_word_t lzo_word_t +# define lzo_sword_t lzo_sword_t +# define LZO_SIZEOF_LZO_WORD_T 16 +# define LZO_TYPEOF_LZO_WORD_T LZO_TYPEOF___MODE_V16QI +#endif +#else +# error "lzo_word_t" +#endif +#endif +#endif +#if 1 && defined(lzo_word_t) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_word_t) == LZO_WORDSIZE) + LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_sword_t) == LZO_WORDSIZE) +#endif +#if 1 +#define lzo_int8_t signed char +#define lzo_uint8_t unsigned char +#define LZO_SIZEOF_LZO_INT8_T 1 +#define LZO_TYPEOF_LZO_INT8_T LZO_TYPEOF_CHAR +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int8_t) == 1) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int8_t) == sizeof(lzo_uint8_t)) +#endif +#if defined(lzo_int16e_t) +#define lzo_int16_t lzo_int16e_t +#define lzo_uint16_t lzo_uint16e_t +#define LZO_SIZEOF_LZO_INT16_T LZO_SIZEOF_LZO_INT16E_T +#define LZO_TYPEOF_LZO_INT16_T LZO_TYPEOF_LZO_INT16E_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16_t) == 2) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16_t) == sizeof(lzo_uint16_t)) +#endif +#if defined(lzo_int32e_t) +#define lzo_int32_t lzo_int32e_t +#define lzo_uint32_t lzo_uint32e_t +#define LZO_SIZEOF_LZO_INT32_T LZO_SIZEOF_LZO_INT32E_T +#define LZO_TYPEOF_LZO_INT32_T LZO_TYPEOF_LZO_INT32E_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32_t) == 4) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32_t) == sizeof(lzo_uint32_t)) +#endif +#if defined(lzo_int64e_t) +#define lzo_int64_t lzo_int64e_t +#define lzo_uint64_t lzo_uint64e_t +#define LZO_SIZEOF_LZO_INT64_T LZO_SIZEOF_LZO_INT64E_T +#define LZO_TYPEOF_LZO_INT64_T LZO_TYPEOF_LZO_INT64E_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64_t) == 8) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64_t) == sizeof(lzo_uint64_t)) +#endif +#if 1 +#define lzo_int_least32_t lzo_int32l_t +#define lzo_uint_least32_t lzo_uint32l_t +#define LZO_SIZEOF_LZO_INT_LEAST32_T LZO_SIZEOF_LZO_INT32L_T +#define LZO_TYPEOF_LZO_INT_LEAST32_T LZO_TYPEOF_LZO_INT32L_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least32_t) >= 4) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least32_t) == sizeof(lzo_uint_least32_t)) +#endif +#if defined(lzo_int64l_t) +#define lzo_int_least64_t lzo_int64l_t +#define lzo_uint_least64_t lzo_uint64l_t +#define LZO_SIZEOF_LZO_INT_LEAST64_T LZO_SIZEOF_LZO_INT64L_T +#define LZO_TYPEOF_LZO_INT_LEAST64_T LZO_TYPEOF_LZO_INT64L_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least64_t) >= 8) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_least64_t) == sizeof(lzo_uint_least64_t)) +#endif +#if 1 +#define lzo_int_fast32_t lzo_int32f_t +#define lzo_uint_fast32_t lzo_uint32f_t +#define LZO_SIZEOF_LZO_INT_FAST32_T LZO_SIZEOF_LZO_INT32F_T +#define LZO_TYPEOF_LZO_INT_FAST32_T LZO_TYPEOF_LZO_INT32F_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast32_t) >= 4) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast32_t) == sizeof(lzo_uint_fast32_t)) +#endif +#if defined(lzo_int64f_t) +#define lzo_int_fast64_t lzo_int64f_t +#define lzo_uint_fast64_t lzo_uint64f_t +#define LZO_SIZEOF_LZO_INT_FAST64_T LZO_SIZEOF_LZO_INT64F_T +#define LZO_TYPEOF_LZO_INT_FAST64_T LZO_TYPEOF_LZO_INT64F_T +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast64_t) >= 8) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int_fast64_t) == sizeof(lzo_uint_fast64_t)) +#endif +#if !defined(LZO_INT16_C) +# if (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_INT >= 2) +# define LZO_INT16_C(c) ((c) + 0) +# define LZO_UINT16_C(c) ((c) + 0U) +# elif (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_LONG >= 2) +# define LZO_INT16_C(c) ((c) + 0L) +# define LZO_UINT16_C(c) ((c) + 0UL) +# elif (LZO_SIZEOF_INT >= 2) +# define LZO_INT16_C(c) (c) +# define LZO_UINT16_C(c) (c##U) +# elif (LZO_SIZEOF_LONG >= 2) +# define LZO_INT16_C(c) (c##L) +# define LZO_UINT16_C(c) (c##UL) +# else +# error "LZO_INT16_C" +# endif +#endif +#if !defined(LZO_INT32_C) +# if (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_INT >= 4) +# define LZO_INT32_C(c) ((c) + 0) +# define LZO_UINT32_C(c) ((c) + 0U) +# elif (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_LONG >= 4) +# define LZO_INT32_C(c) ((c) + 0L) +# define LZO_UINT32_C(c) ((c) + 0UL) +# elif (LZO_SIZEOF_INT >= 4) +# define LZO_INT32_C(c) (c) +# define LZO_UINT32_C(c) (c##U) +# elif (LZO_SIZEOF_LONG >= 4) +# define LZO_INT32_C(c) (c##L) +# define LZO_UINT32_C(c) (c##UL) +# elif (LZO_SIZEOF_LONG_LONG >= 4) +# define LZO_INT32_C(c) (c##LL) +# define LZO_UINT32_C(c) (c##ULL) +# else +# error "LZO_INT32_C" +# endif +#endif +#if !defined(LZO_INT64_C) && defined(lzo_int64l_t) +# if (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_INT >= 8) +# define LZO_INT64_C(c) ((c) + 0) +# define LZO_UINT64_C(c) ((c) + 0U) +# elif (LZO_BROKEN_INTEGRAL_CONSTANTS) && (LZO_SIZEOF_LONG >= 8) +# define LZO_INT64_C(c) ((c) + 0L) +# define LZO_UINT64_C(c) ((c) + 0UL) +# elif (LZO_SIZEOF_INT >= 8) +# define LZO_INT64_C(c) (c) +# define LZO_UINT64_C(c) (c##U) +# elif (LZO_SIZEOF_LONG >= 8) +# define LZO_INT64_C(c) (c##L) +# define LZO_UINT64_C(c) (c##UL) +# else +# error "LZO_INT64_C" +# endif +#endif +#endif + +#endif + +#endif + +#undef LZO_HAVE_CONFIG_H +#include "minilzo.h" + +#if !defined(MINILZO_VERSION) || (MINILZO_VERSION != 0x20a0) +# error "version mismatch in miniLZO source files" +#endif + +#ifdef MINILZO_HAVE_CONFIG_H +# define LZO_HAVE_CONFIG_H 1 +#endif + +#ifndef __LZO_CONF_H +#define __LZO_CONF_H 1 + +#if !defined(__LZO_IN_MINILZO) +#if defined(LZO_CFG_FREESTANDING) && (LZO_CFG_FREESTANDING) +# define LZO_LIBC_FREESTANDING 1 +# define LZO_OS_FREESTANDING 1 +#endif +#if defined(LZO_CFG_EXTRA_CONFIG_HEADER) +# include LZO_CFG_EXTRA_CONFIG_HEADER +#endif +#if defined(__LZOCONF_H) || defined(__LZOCONF_H_INCLUDED) +# error "include this file first" +#endif +#if defined(LZO_CFG_BUILD_DLL) && (LZO_CFG_BUILD_DLL+0) && !defined(__LZO_EXPORT1) && !defined(__LZO_EXPORT2) && 0 +#ifndef __LZODEFS_H_INCLUDED +#if defined(LZO_HAVE_CONFIG_H) +# include +#endif +#include +#include +#include +#endif +#endif +#include +#if defined(LZO_CFG_EXTRA_CONFIG_HEADER2) +# include LZO_CFG_EXTRA_CONFIG_HEADER2 +#endif +#endif + +#if !defined(__LZOCONF_H_INCLUDED) || (LZO_VERSION+0 != 0x20a0) +# error "version mismatch" +#endif + +#if (LZO_CC_MSC && (_MSC_VER >= 1000 && _MSC_VER < 1100)) +# pragma warning(disable: 4702) +#endif +#if (LZO_CC_MSC && (_MSC_VER >= 1000)) +# pragma warning(disable: 4127 4701) +# pragma warning(disable: 4514 4710 4711) +#endif +#if (LZO_CC_MSC && (_MSC_VER >= 1300)) +# pragma warning(disable: 4820) +#endif +#if (LZO_CC_MSC && (_MSC_VER >= 1800)) +# pragma warning(disable: 4746) +#endif +#if (LZO_CC_INTELC && (__INTEL_COMPILER >= 900)) +# pragma warning(disable: 1684) +#endif + +#if (LZO_CC_SUNPROC) +#if !defined(__cplusplus) +# pragma error_messages(off,E_END_OF_LOOP_CODE_NOT_REACHED) +# pragma error_messages(off,E_LOOP_NOT_ENTERED_AT_TOP) +# pragma error_messages(off,E_STATEMENT_NOT_REACHED) +#endif +#endif + +#if !defined(__LZO_NOEXPORT1) +# define __LZO_NOEXPORT1 /*empty*/ +#endif +#if !defined(__LZO_NOEXPORT2) +# define __LZO_NOEXPORT2 /*empty*/ +#endif + +#if 1 +# define LZO_PUBLIC_DECL(r) LZO_EXTERN(r) +#endif +#if 1 +# define LZO_PUBLIC_IMPL(r) LZO_PUBLIC(r) +#endif +#if !defined(LZO_LOCAL_DECL) +# define LZO_LOCAL_DECL(r) __LZO_EXTERN_C LZO_LOCAL_IMPL(r) +#endif +#if !defined(LZO_LOCAL_IMPL) +# define LZO_LOCAL_IMPL(r) __LZO_NOEXPORT1 r __LZO_NOEXPORT2 __LZO_CDECL +#endif +#if 1 +# define LZO_STATIC_DECL(r) LZO_PRIVATE(r) +#endif +#if 1 +# define LZO_STATIC_IMPL(r) LZO_PRIVATE(r) +#endif + +#if defined(__LZO_IN_MINILZO) || (LZO_CFG_FREESTANDING) +#elif 1 +# include +#else +# define LZO_WANT_ACC_INCD_H 1 +#endif +#if defined(LZO_HAVE_CONFIG_H) +# define LZO_CFG_NO_CONFIG_HEADER 1 +#endif + +#if 1 && !defined(LZO_CFG_FREESTANDING) +#if 1 && !defined(HAVE_STRING_H) +#define HAVE_STRING_H 1 +#endif +#if 1 && !defined(HAVE_MEMCMP) +#define HAVE_MEMCMP 1 +#endif +#if 1 && !defined(HAVE_MEMCPY) +#define HAVE_MEMCPY 1 +#endif +#if 1 && !defined(HAVE_MEMMOVE) +#define HAVE_MEMMOVE 1 +#endif +#if 1 && !defined(HAVE_MEMSET) +#define HAVE_MEMSET 1 +#endif +#endif + +#if 1 && defined(HAVE_STRING_H) +#include +#endif + +#if 1 || defined(lzo_int8_t) || defined(lzo_uint8_t) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int8_t) == 1) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint8_t) == 1) +#endif +#if 1 || defined(lzo_int16_t) || defined(lzo_uint16_t) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int16_t) == 2) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint16_t) == 2) +#endif +#if 1 || defined(lzo_int32_t) || defined(lzo_uint32_t) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int32_t) == 4) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint32_t) == 4) +#endif +#if defined(lzo_int64_t) || defined(lzo_uint64_t) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_int64_t) == 8) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(lzo_uint64_t) == 8) +#endif + +#if (LZO_CFG_FREESTANDING) +# undef HAVE_MEMCMP +# undef HAVE_MEMCPY +# undef HAVE_MEMMOVE +# undef HAVE_MEMSET +#endif + +#if !(HAVE_MEMCMP) +# undef memcmp +# define memcmp(a,b,c) lzo_memcmp(a,b,c) +#else +# undef lzo_memcmp +# define lzo_memcmp(a,b,c) memcmp(a,b,c) +#endif +#if !(HAVE_MEMCPY) +# undef memcpy +# define memcpy(a,b,c) lzo_memcpy(a,b,c) +#else +# undef lzo_memcpy +# define lzo_memcpy(a,b,c) memcpy(a,b,c) +#endif +#if !(HAVE_MEMMOVE) +# undef memmove +# define memmove(a,b,c) lzo_memmove(a,b,c) +#else +# undef lzo_memmove +# define lzo_memmove(a,b,c) memmove(a,b,c) +#endif +#if !(HAVE_MEMSET) +# undef memset +# define memset(a,b,c) lzo_memset(a,b,c) +#else +# undef lzo_memset +# define lzo_memset(a,b,c) memset(a,b,c) +#endif + +#undef NDEBUG +#if (LZO_CFG_FREESTANDING) +# undef LZO_DEBUG +# define NDEBUG 1 +# undef assert +# define assert(e) ((void)0) +#else +# if !defined(LZO_DEBUG) +# define NDEBUG 1 +# endif +# include +#endif + +#if 0 && defined(__BOUNDS_CHECKING_ON) +# include +#else +# define BOUNDS_CHECKING_OFF_DURING(stmt) stmt +# define BOUNDS_CHECKING_OFF_IN_EXPR(expr) (expr) +#endif + +#if (LZO_CFG_PGO) +# undef __lzo_likely +# undef __lzo_unlikely +# define __lzo_likely(e) (e) +# define __lzo_unlikely(e) (e) +#endif + +#undef _ +#undef __ +#undef ___ +#undef ____ +#undef _p0 +#undef _p1 +#undef _p2 +#undef _p3 +#undef _p4 +#undef _s0 +#undef _s1 +#undef _s2 +#undef _s3 +#undef _s4 +#undef _ww + +#if 1 +# define LZO_BYTE(x) ((unsigned char) (x)) +#else +# define LZO_BYTE(x) ((unsigned char) ((x) & 0xff)) +#endif + +#define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) +#define LZO_MIN(a,b) ((a) <= (b) ? (a) : (b)) +#define LZO_MAX3(a,b,c) ((a) >= (b) ? LZO_MAX(a,c) : LZO_MAX(b,c)) +#define LZO_MIN3(a,b,c) ((a) <= (b) ? LZO_MIN(a,c) : LZO_MIN(b,c)) + +#define lzo_sizeof(type) ((lzo_uint) (sizeof(type))) + +#define LZO_HIGH(array) ((lzo_uint) (sizeof(array)/sizeof(*(array)))) + +#define LZO_SIZE(bits) (1u << (bits)) +#define LZO_MASK(bits) (LZO_SIZE(bits) - 1) + +#define LZO_USIZE(bits) ((lzo_uint) 1 << (bits)) +#define LZO_UMASK(bits) (LZO_USIZE(bits) - 1) + +#if !defined(DMUL) +#if 0 + +# define DMUL(a,b) ((lzo_xint) ((lzo_uint32_t)(a) * (lzo_uint32_t)(b))) +#else +# define DMUL(a,b) ((lzo_xint) ((a) * (b))) +#endif +#endif + +#ifndef __LZO_FUNC_H +#define __LZO_FUNC_H 1 + +#if !defined(LZO_BITOPS_USE_ASM_BITSCAN) && !defined(LZO_BITOPS_USE_GNUC_BITSCAN) && !defined(LZO_BITOPS_USE_MSC_BITSCAN) +#if 1 && (LZO_ARCH_AMD64) && (LZO_CC_GNUC && (LZO_CC_GNUC < 0x040000ul)) && (LZO_ASM_SYNTAX_GNUC) +#define LZO_BITOPS_USE_ASM_BITSCAN 1 +#elif (LZO_CC_CLANG || (LZO_CC_GNUC >= 0x030400ul) || (LZO_CC_INTELC_GNUC && (__INTEL_COMPILER >= 1000)) || (LZO_CC_LLVM && (!defined(__llvm_tools_version__) || (__llvm_tools_version__+0 >= 0x010500ul)))) +#define LZO_BITOPS_USE_GNUC_BITSCAN 1 +#elif (LZO_OS_WIN32 || LZO_OS_WIN64) && ((LZO_CC_INTELC_MSC && (__INTEL_COMPILER >= 1010)) || (LZO_CC_MSC && (_MSC_VER >= 1400))) +#define LZO_BITOPS_USE_MSC_BITSCAN 1 +#if (LZO_CC_MSC) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) +#include +#endif +#if (LZO_CC_MSC) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) +#pragma intrinsic(_BitScanReverse) +#pragma intrinsic(_BitScanForward) +#endif +#if (LZO_CC_MSC) && (LZO_ARCH_AMD64) +#pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_BitScanForward64) +#endif +#endif +#endif + +__lzo_static_forceinline unsigned lzo_bitops_ctlz32_func(lzo_uint32_t v) +{ +#if (LZO_BITOPS_USE_MSC_BITSCAN) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) + unsigned long r; (void) _BitScanReverse(&r, v); return (unsigned) r ^ 31; +#define lzo_bitops_ctlz32(v) lzo_bitops_ctlz32_func(v) +#elif (LZO_BITOPS_USE_ASM_BITSCAN) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) && (LZO_ASM_SYNTAX_GNUC) + lzo_uint32_t r; + __asm__("bsr %1,%0" : "=r" (r) : "rm" (v) __LZO_ASM_CLOBBER_LIST_CC); + return (unsigned) r ^ 31; +#define lzo_bitops_ctlz32(v) lzo_bitops_ctlz32_func(v) +#elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_INT == 4) + unsigned r; r = (unsigned) __builtin_clz(v); return r; +#define lzo_bitops_ctlz32(v) ((unsigned) __builtin_clz(v)) +#elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_LONG == 8) && (LZO_WORDSIZE >= 8) + unsigned r; r = (unsigned) __builtin_clzl(v); return r ^ 32; +#define lzo_bitops_ctlz32(v) (((unsigned) __builtin_clzl(v)) ^ 32) +#else + LZO_UNUSED(v); return 0; +#endif +} + +#if defined(lzo_uint64_t) +__lzo_static_forceinline unsigned lzo_bitops_ctlz64_func(lzo_uint64_t v) +{ +#if (LZO_BITOPS_USE_MSC_BITSCAN) && (LZO_ARCH_AMD64) + unsigned long r; (void) _BitScanReverse64(&r, v); return (unsigned) r ^ 63; +#define lzo_bitops_ctlz64(v) lzo_bitops_ctlz64_func(v) +#elif (LZO_BITOPS_USE_ASM_BITSCAN) && (LZO_ARCH_AMD64) && (LZO_ASM_SYNTAX_GNUC) + lzo_uint64_t r; + __asm__("bsr %1,%0" : "=r" (r) : "rm" (v) __LZO_ASM_CLOBBER_LIST_CC); + return (unsigned) r ^ 63; +#define lzo_bitops_ctlz64(v) lzo_bitops_ctlz64_func(v) +#elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_LONG == 8) && (LZO_WORDSIZE >= 8) + unsigned r; r = (unsigned) __builtin_clzl(v); return r; +#define lzo_bitops_ctlz64(v) ((unsigned) __builtin_clzl(v)) +#elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_LONG_LONG == 8) && (LZO_WORDSIZE >= 8) + unsigned r; r = (unsigned) __builtin_clzll(v); return r; +#define lzo_bitops_ctlz64(v) ((unsigned) __builtin_clzll(v)) +#else + LZO_UNUSED(v); return 0; +#endif +} +#endif + +__lzo_static_forceinline unsigned lzo_bitops_cttz32_func(lzo_uint32_t v) +{ +#if (LZO_BITOPS_USE_MSC_BITSCAN) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) + unsigned long r; (void) _BitScanForward(&r, v); return (unsigned) r; +#define lzo_bitops_cttz32(v) lzo_bitops_cttz32_func(v) +#elif (LZO_BITOPS_USE_ASM_BITSCAN) && (LZO_ARCH_AMD64 || LZO_ARCH_I386) && (LZO_ASM_SYNTAX_GNUC) + lzo_uint32_t r; + __asm__("bsf %1,%0" : "=r" (r) : "rm" (v) __LZO_ASM_CLOBBER_LIST_CC); + return (unsigned) r; +#define lzo_bitops_cttz32(v) lzo_bitops_cttz32_func(v) +#elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_INT >= 4) + unsigned r; r = (unsigned) __builtin_ctz(v); return r; +#define lzo_bitops_cttz32(v) ((unsigned) __builtin_ctz(v)) +#else + LZO_UNUSED(v); return 0; +#endif +} + +#if defined(lzo_uint64_t) +__lzo_static_forceinline unsigned lzo_bitops_cttz64_func(lzo_uint64_t v) +{ +#if (LZO_BITOPS_USE_MSC_BITSCAN) && (LZO_ARCH_AMD64) + unsigned long r; (void) _BitScanForward64(&r, v); return (unsigned) r; +#define lzo_bitops_cttz64(v) lzo_bitops_cttz64_func(v) +#elif (LZO_BITOPS_USE_ASM_BITSCAN) && (LZO_ARCH_AMD64) && (LZO_ASM_SYNTAX_GNUC) + lzo_uint64_t r; + __asm__("bsf %1,%0" : "=r" (r) : "rm" (v) __LZO_ASM_CLOBBER_LIST_CC); + return (unsigned) r; +#define lzo_bitops_cttz64(v) lzo_bitops_cttz64_func(v) +#elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_LONG >= 8) && (LZO_WORDSIZE >= 8) + unsigned r; r = (unsigned) __builtin_ctzl(v); return r; +#define lzo_bitops_cttz64(v) ((unsigned) __builtin_ctzl(v)) +#elif (LZO_BITOPS_USE_GNUC_BITSCAN) && (LZO_SIZEOF_LONG_LONG >= 8) && (LZO_WORDSIZE >= 8) + unsigned r; r = (unsigned) __builtin_ctzll(v); return r; +#define lzo_bitops_cttz64(v) ((unsigned) __builtin_ctzll(v)) +#else + LZO_UNUSED(v); return 0; +#endif +} +#endif + +lzo_unused_funcs_impl(void, lzo_bitops_unused_funcs)(void) +{ + LZO_UNUSED_FUNC(lzo_bitops_unused_funcs); + LZO_UNUSED_FUNC(lzo_bitops_ctlz32_func); + LZO_UNUSED_FUNC(lzo_bitops_cttz32_func); +#if defined(lzo_uint64_t) + LZO_UNUSED_FUNC(lzo_bitops_ctlz64_func); + LZO_UNUSED_FUNC(lzo_bitops_cttz64_func); +#endif +} + +#if defined(__lzo_alignof) && !(LZO_CFG_NO_UNALIGNED) +#if !defined(lzo_memops_tcheck__) && 0 +#define lzo_memops_tcheck__(t,a,b) ((void)0, sizeof(t) == (a) && __lzo_alignof(t) == (b)) +#endif +#endif +#ifndef lzo_memops_TU0p +#define lzo_memops_TU0p void __LZO_MMODEL * +#endif +#ifndef lzo_memops_TU1p +#define lzo_memops_TU1p unsigned char __LZO_MMODEL * +#endif +#ifndef lzo_memops_TU2p +#if (LZO_OPT_UNALIGNED16) +typedef lzo_uint16_t __lzo_may_alias lzo_memops_TU2; +#define lzo_memops_TU2p volatile lzo_memops_TU2 * +#elif defined(__lzo_byte_struct) +__lzo_byte_struct(lzo_memops_TU2_struct,2) +typedef struct lzo_memops_TU2_struct lzo_memops_TU2; +#else +struct lzo_memops_TU2_struct { unsigned char a[2]; } __lzo_may_alias; +typedef struct lzo_memops_TU2_struct lzo_memops_TU2; +#endif +#ifndef lzo_memops_TU2p +#define lzo_memops_TU2p lzo_memops_TU2 * +#endif +#endif +#ifndef lzo_memops_TU4p +#if (LZO_OPT_UNALIGNED32) +typedef lzo_uint32_t __lzo_may_alias lzo_memops_TU4; +#define lzo_memops_TU4p volatile lzo_memops_TU4 __LZO_MMODEL * +#elif defined(__lzo_byte_struct) +__lzo_byte_struct(lzo_memops_TU4_struct,4) +typedef struct lzo_memops_TU4_struct lzo_memops_TU4; +#else +struct lzo_memops_TU4_struct { unsigned char a[4]; } __lzo_may_alias; +typedef struct lzo_memops_TU4_struct lzo_memops_TU4; +#endif +#ifndef lzo_memops_TU4p +#define lzo_memops_TU4p lzo_memops_TU4 __LZO_MMODEL * +#endif +#endif +#ifndef lzo_memops_TU8p +#if (LZO_OPT_UNALIGNED64) +typedef lzo_uint64_t __lzo_may_alias lzo_memops_TU8; +#define lzo_memops_TU8p volatile lzo_memops_TU8 __LZO_MMODEL * +#elif defined(__lzo_byte_struct) +__lzo_byte_struct(lzo_memops_TU8_struct,8) +typedef struct lzo_memops_TU8_struct lzo_memops_TU8; +#else +struct lzo_memops_TU8_struct { unsigned char a[8]; } __lzo_may_alias; +typedef struct lzo_memops_TU8_struct lzo_memops_TU8; +#endif +#ifndef lzo_memops_TU8p +#define lzo_memops_TU8p lzo_memops_TU8 __LZO_MMODEL * +#endif +#endif +#ifndef lzo_memops_set_TU1p +#define lzo_memops_set_TU1p volatile lzo_memops_TU1p +#endif +#ifndef lzo_memops_move_TU1p +#define lzo_memops_move_TU1p lzo_memops_TU1p +#endif +#define LZO_MEMOPS_SET1(dd,cc) \ + LZO_BLOCK_BEGIN \ + lzo_memops_set_TU1p d__1 = (lzo_memops_set_TU1p) (lzo_memops_TU0p) (dd); \ + d__1[0] = LZO_BYTE(cc); \ + LZO_BLOCK_END +#define LZO_MEMOPS_SET2(dd,cc) \ + LZO_BLOCK_BEGIN \ + lzo_memops_set_TU1p d__2 = (lzo_memops_set_TU1p) (lzo_memops_TU0p) (dd); \ + d__2[0] = LZO_BYTE(cc); d__2[1] = LZO_BYTE(cc); \ + LZO_BLOCK_END +#define LZO_MEMOPS_SET3(dd,cc) \ + LZO_BLOCK_BEGIN \ + lzo_memops_set_TU1p d__3 = (lzo_memops_set_TU1p) (lzo_memops_TU0p) (dd); \ + d__3[0] = LZO_BYTE(cc); d__3[1] = LZO_BYTE(cc); d__3[2] = LZO_BYTE(cc); \ + LZO_BLOCK_END +#define LZO_MEMOPS_SET4(dd,cc) \ + LZO_BLOCK_BEGIN \ + lzo_memops_set_TU1p d__4 = (lzo_memops_set_TU1p) (lzo_memops_TU0p) (dd); \ + d__4[0] = LZO_BYTE(cc); d__4[1] = LZO_BYTE(cc); d__4[2] = LZO_BYTE(cc); d__4[3] = LZO_BYTE(cc); \ + LZO_BLOCK_END +#define LZO_MEMOPS_MOVE1(dd,ss) \ + LZO_BLOCK_BEGIN \ + lzo_memops_move_TU1p d__1 = (lzo_memops_move_TU1p) (lzo_memops_TU0p) (dd); \ + const lzo_memops_move_TU1p s__1 = (const lzo_memops_move_TU1p) (const lzo_memops_TU0p) (ss); \ + d__1[0] = s__1[0]; \ + LZO_BLOCK_END +#define LZO_MEMOPS_MOVE2(dd,ss) \ + LZO_BLOCK_BEGIN \ + lzo_memops_move_TU1p d__2 = (lzo_memops_move_TU1p) (lzo_memops_TU0p) (dd); \ + const lzo_memops_move_TU1p s__2 = (const lzo_memops_move_TU1p) (const lzo_memops_TU0p) (ss); \ + d__2[0] = s__2[0]; d__2[1] = s__2[1]; \ + LZO_BLOCK_END +#define LZO_MEMOPS_MOVE3(dd,ss) \ + LZO_BLOCK_BEGIN \ + lzo_memops_move_TU1p d__3 = (lzo_memops_move_TU1p) (lzo_memops_TU0p) (dd); \ + const lzo_memops_move_TU1p s__3 = (const lzo_memops_move_TU1p) (const lzo_memops_TU0p) (ss); \ + d__3[0] = s__3[0]; d__3[1] = s__3[1]; d__3[2] = s__3[2]; \ + LZO_BLOCK_END +#define LZO_MEMOPS_MOVE4(dd,ss) \ + LZO_BLOCK_BEGIN \ + lzo_memops_move_TU1p d__4 = (lzo_memops_move_TU1p) (lzo_memops_TU0p) (dd); \ + const lzo_memops_move_TU1p s__4 = (const lzo_memops_move_TU1p) (const lzo_memops_TU0p) (ss); \ + d__4[0] = s__4[0]; d__4[1] = s__4[1]; d__4[2] = s__4[2]; d__4[3] = s__4[3]; \ + LZO_BLOCK_END +#define LZO_MEMOPS_MOVE8(dd,ss) \ + LZO_BLOCK_BEGIN \ + lzo_memops_move_TU1p d__8 = (lzo_memops_move_TU1p) (lzo_memops_TU0p) (dd); \ + const lzo_memops_move_TU1p s__8 = (const lzo_memops_move_TU1p) (const lzo_memops_TU0p) (ss); \ + d__8[0] = s__8[0]; d__8[1] = s__8[1]; d__8[2] = s__8[2]; d__8[3] = s__8[3]; \ + d__8[4] = s__8[4]; d__8[5] = s__8[5]; d__8[6] = s__8[6]; d__8[7] = s__8[7]; \ + LZO_BLOCK_END +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU1p)0)==1) +#define LZO_MEMOPS_COPY1(dd,ss) LZO_MEMOPS_MOVE1(dd,ss) +#if (LZO_OPT_UNALIGNED16) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU2p)0)==2) +#define LZO_MEMOPS_COPY2(dd,ss) \ + * (lzo_memops_TU2p) (lzo_memops_TU0p) (dd) = * (const lzo_memops_TU2p) (const lzo_memops_TU0p) (ss) +#elif defined(lzo_memops_tcheck__) +#define LZO_MEMOPS_COPY2(dd,ss) \ + LZO_BLOCK_BEGIN if (lzo_memops_tcheck__(lzo_memops_TU2,2,1)) { \ + * (lzo_memops_TU2p) (lzo_memops_TU0p) (dd) = * (const lzo_memops_TU2p) (const lzo_memops_TU0p) (ss); \ + } else { LZO_MEMOPS_MOVE2(dd,ss); } LZO_BLOCK_END +#else +#define LZO_MEMOPS_COPY2(dd,ss) LZO_MEMOPS_MOVE2(dd,ss) +#endif +#if (LZO_OPT_UNALIGNED32) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU4p)0)==4) +#define LZO_MEMOPS_COPY4(dd,ss) \ + * (lzo_memops_TU4p) (lzo_memops_TU0p) (dd) = * (const lzo_memops_TU4p) (const lzo_memops_TU0p) (ss) +#elif defined(lzo_memops_tcheck__) +#define LZO_MEMOPS_COPY4(dd,ss) \ + LZO_BLOCK_BEGIN if (lzo_memops_tcheck__(lzo_memops_TU4,4,1)) { \ + * (lzo_memops_TU4p) (lzo_memops_TU0p) (dd) = * (const lzo_memops_TU4p) (const lzo_memops_TU0p) (ss); \ + } else { LZO_MEMOPS_MOVE4(dd,ss); } LZO_BLOCK_END +#else +#define LZO_MEMOPS_COPY4(dd,ss) LZO_MEMOPS_MOVE4(dd,ss) +#endif +#if (LZO_WORDSIZE != 8) +#define LZO_MEMOPS_COPY8(dd,ss) \ + LZO_BLOCK_BEGIN LZO_MEMOPS_COPY4(dd,ss); LZO_MEMOPS_COPY4((lzo_memops_TU1p)(lzo_memops_TU0p)(dd)+4,(const lzo_memops_TU1p)(const lzo_memops_TU0p)(ss)+4); LZO_BLOCK_END +#else +#if (LZO_OPT_UNALIGNED64) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU8p)0)==8) +#define LZO_MEMOPS_COPY8(dd,ss) \ + * (lzo_memops_TU8p) (lzo_memops_TU0p) (dd) = * (const lzo_memops_TU8p) (const lzo_memops_TU0p) (ss) +#elif (LZO_OPT_UNALIGNED32) +#define LZO_MEMOPS_COPY8(dd,ss) \ + LZO_BLOCK_BEGIN LZO_MEMOPS_COPY4(dd,ss); LZO_MEMOPS_COPY4((lzo_memops_TU1p)(lzo_memops_TU0p)(dd)+4,(const lzo_memops_TU1p)(const lzo_memops_TU0p)(ss)+4); LZO_BLOCK_END +#elif defined(lzo_memops_tcheck__) +#define LZO_MEMOPS_COPY8(dd,ss) \ + LZO_BLOCK_BEGIN if (lzo_memops_tcheck__(lzo_memops_TU8,8,1)) { \ + * (lzo_memops_TU8p) (lzo_memops_TU0p) (dd) = * (const lzo_memops_TU8p) (const lzo_memops_TU0p) (ss); \ + } else { LZO_MEMOPS_MOVE8(dd,ss); } LZO_BLOCK_END +#else +#define LZO_MEMOPS_COPY8(dd,ss) LZO_MEMOPS_MOVE8(dd,ss) +#endif +#endif +#define LZO_MEMOPS_COPYN(dd,ss,nn) \ + LZO_BLOCK_BEGIN \ + lzo_memops_TU1p d__n = (lzo_memops_TU1p) (lzo_memops_TU0p) (dd); \ + const lzo_memops_TU1p s__n = (const lzo_memops_TU1p) (const lzo_memops_TU0p) (ss); \ + lzo_uint n__n = (nn); \ + while ((void)0, n__n >= 8) { LZO_MEMOPS_COPY8(d__n, s__n); d__n += 8; s__n += 8; n__n -= 8; } \ + if ((void)0, n__n >= 4) { LZO_MEMOPS_COPY4(d__n, s__n); d__n += 4; s__n += 4; n__n -= 4; } \ + if ((void)0, n__n > 0) do { *d__n++ = *s__n++; } while (--n__n > 0); \ + LZO_BLOCK_END + +__lzo_static_forceinline lzo_uint16_t lzo_memops_get_le16(const lzo_voidp ss) +{ + lzo_uint16_t v; +#if (LZO_ABI_LITTLE_ENDIAN) + LZO_MEMOPS_COPY2(&v, ss); +#elif (LZO_OPT_UNALIGNED16 && LZO_ARCH_POWERPC && LZO_ABI_BIG_ENDIAN) && (LZO_ASM_SYNTAX_GNUC) + const lzo_memops_TU2p s = (const lzo_memops_TU2p) ss; + unsigned long vv; + __asm__("lhbrx %0,0,%1" : "=r" (vv) : "r" (s), "m" (*s)); + v = (lzo_uint16_t) vv; +#else + const lzo_memops_TU1p s = (const lzo_memops_TU1p) ss; + v = (lzo_uint16_t) (((lzo_uint16_t)s[0]) | ((lzo_uint16_t)s[1] << 8)); +#endif + return v; +} +#if (LZO_OPT_UNALIGNED16) && (LZO_ABI_LITTLE_ENDIAN) +#define LZO_MEMOPS_GET_LE16(ss) (* (const lzo_memops_TU2p) (const lzo_memops_TU0p) (ss)) +#else +#define LZO_MEMOPS_GET_LE16(ss) lzo_memops_get_le16(ss) +#endif + +__lzo_static_forceinline lzo_uint32_t lzo_memops_get_le32(const lzo_voidp ss) +{ + lzo_uint32_t v; +#if (LZO_ABI_LITTLE_ENDIAN) + LZO_MEMOPS_COPY4(&v, ss); +#elif (LZO_OPT_UNALIGNED32 && LZO_ARCH_POWERPC && LZO_ABI_BIG_ENDIAN) && (LZO_ASM_SYNTAX_GNUC) + const lzo_memops_TU4p s = (const lzo_memops_TU4p) ss; + unsigned long vv; + __asm__("lwbrx %0,0,%1" : "=r" (vv) : "r" (s), "m" (*s)); + v = (lzo_uint32_t) vv; +#else + const lzo_memops_TU1p s = (const lzo_memops_TU1p) ss; + v = (lzo_uint32_t) (((lzo_uint32_t)s[0]) | ((lzo_uint32_t)s[1] << 8) | ((lzo_uint32_t)s[2] << 16) | ((lzo_uint32_t)s[3] << 24)); +#endif + return v; +} +#if (LZO_OPT_UNALIGNED32) && (LZO_ABI_LITTLE_ENDIAN) +#define LZO_MEMOPS_GET_LE32(ss) (* (const lzo_memops_TU4p) (const lzo_memops_TU0p) (ss)) +#else +#define LZO_MEMOPS_GET_LE32(ss) lzo_memops_get_le32(ss) +#endif + +#if (LZO_OPT_UNALIGNED64) && (LZO_ABI_LITTLE_ENDIAN) +#define LZO_MEMOPS_GET_LE64(ss) (* (const lzo_memops_TU8p) (const lzo_memops_TU0p) (ss)) +#endif + +__lzo_static_forceinline lzo_uint16_t lzo_memops_get_ne16(const lzo_voidp ss) +{ + lzo_uint16_t v; + LZO_MEMOPS_COPY2(&v, ss); + return v; +} +#if (LZO_OPT_UNALIGNED16) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU2p)0)==2) +#define LZO_MEMOPS_GET_NE16(ss) (* (const lzo_memops_TU2p) (const lzo_memops_TU0p) (ss)) +#else +#define LZO_MEMOPS_GET_NE16(ss) lzo_memops_get_ne16(ss) +#endif + +__lzo_static_forceinline lzo_uint32_t lzo_memops_get_ne32(const lzo_voidp ss) +{ + lzo_uint32_t v; + LZO_MEMOPS_COPY4(&v, ss); + return v; +} +#if (LZO_OPT_UNALIGNED32) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU4p)0)==4) +#define LZO_MEMOPS_GET_NE32(ss) (* (const lzo_memops_TU4p) (const lzo_memops_TU0p) (ss)) +#else +#define LZO_MEMOPS_GET_NE32(ss) lzo_memops_get_ne32(ss) +#endif + +#if (LZO_OPT_UNALIGNED64) +LZO_COMPILE_TIME_ASSERT_HEADER(sizeof(*(lzo_memops_TU8p)0)==8) +#define LZO_MEMOPS_GET_NE64(ss) (* (const lzo_memops_TU8p) (const lzo_memops_TU0p) (ss)) +#endif + +__lzo_static_forceinline void lzo_memops_put_le16(lzo_voidp dd, lzo_uint16_t vv) +{ +#if (LZO_ABI_LITTLE_ENDIAN) + LZO_MEMOPS_COPY2(dd, &vv); +#elif (LZO_OPT_UNALIGNED16 && LZO_ARCH_POWERPC && LZO_ABI_BIG_ENDIAN) && (LZO_ASM_SYNTAX_GNUC) + lzo_memops_TU2p d = (lzo_memops_TU2p) dd; + unsigned long v = vv; + __asm__("sthbrx %2,0,%1" : "=m" (*d) : "r" (d), "r" (v)); +#else + lzo_memops_TU1p d = (lzo_memops_TU1p) dd; + d[0] = LZO_BYTE((vv ) & 0xff); + d[1] = LZO_BYTE((vv >> 8) & 0xff); +#endif +} +#if (LZO_OPT_UNALIGNED16) && (LZO_ABI_LITTLE_ENDIAN) +#define LZO_MEMOPS_PUT_LE16(dd,vv) (* (lzo_memops_TU2p) (lzo_memops_TU0p) (dd) = (vv)) +#else +#define LZO_MEMOPS_PUT_LE16(dd,vv) lzo_memops_put_le16(dd,vv) +#endif + +__lzo_static_forceinline void lzo_memops_put_le32(lzo_voidp dd, lzo_uint32_t vv) +{ +#if (LZO_ABI_LITTLE_ENDIAN) + LZO_MEMOPS_COPY4(dd, &vv); +#elif (LZO_OPT_UNALIGNED32 && LZO_ARCH_POWERPC && LZO_ABI_BIG_ENDIAN) && (LZO_ASM_SYNTAX_GNUC) + lzo_memops_TU4p d = (lzo_memops_TU4p) dd; + unsigned long v = vv; + __asm__("stwbrx %2,0,%1" : "=m" (*d) : "r" (d), "r" (v)); +#else + lzo_memops_TU1p d = (lzo_memops_TU1p) dd; + d[0] = LZO_BYTE((vv ) & 0xff); + d[1] = LZO_BYTE((vv >> 8) & 0xff); + d[2] = LZO_BYTE((vv >> 16) & 0xff); + d[3] = LZO_BYTE((vv >> 24) & 0xff); +#endif +} +#if (LZO_OPT_UNALIGNED32) && (LZO_ABI_LITTLE_ENDIAN) +#define LZO_MEMOPS_PUT_LE32(dd,vv) (* (lzo_memops_TU4p) (lzo_memops_TU0p) (dd) = (vv)) +#else +#define LZO_MEMOPS_PUT_LE32(dd,vv) lzo_memops_put_le32(dd,vv) +#endif + +__lzo_static_forceinline void lzo_memops_put_ne16(lzo_voidp dd, lzo_uint16_t vv) +{ + LZO_MEMOPS_COPY2(dd, &vv); +} +#if (LZO_OPT_UNALIGNED16) +#define LZO_MEMOPS_PUT_NE16(dd,vv) (* (lzo_memops_TU2p) (lzo_memops_TU0p) (dd) = (vv)) +#else +#define LZO_MEMOPS_PUT_NE16(dd,vv) lzo_memops_put_ne16(dd,vv) +#endif + +__lzo_static_forceinline void lzo_memops_put_ne32(lzo_voidp dd, lzo_uint32_t vv) +{ + LZO_MEMOPS_COPY4(dd, &vv); +} +#if (LZO_OPT_UNALIGNED32) +#define LZO_MEMOPS_PUT_NE32(dd,vv) (* (lzo_memops_TU4p) (lzo_memops_TU0p) (dd) = (vv)) +#else +#define LZO_MEMOPS_PUT_NE32(dd,vv) lzo_memops_put_ne32(dd,vv) +#endif + +lzo_unused_funcs_impl(void, lzo_memops_unused_funcs)(void) +{ + LZO_UNUSED_FUNC(lzo_memops_unused_funcs); + LZO_UNUSED_FUNC(lzo_memops_get_le16); + LZO_UNUSED_FUNC(lzo_memops_get_le32); + LZO_UNUSED_FUNC(lzo_memops_get_ne16); + LZO_UNUSED_FUNC(lzo_memops_get_ne32); + LZO_UNUSED_FUNC(lzo_memops_put_le16); + LZO_UNUSED_FUNC(lzo_memops_put_le32); + LZO_UNUSED_FUNC(lzo_memops_put_ne16); + LZO_UNUSED_FUNC(lzo_memops_put_ne32); +} + +#endif + +#ifndef UA_SET1 +#define UA_SET1 LZO_MEMOPS_SET1 +#endif +#ifndef UA_SET2 +#define UA_SET2 LZO_MEMOPS_SET2 +#endif +#ifndef UA_SET3 +#define UA_SET3 LZO_MEMOPS_SET3 +#endif +#ifndef UA_SET4 +#define UA_SET4 LZO_MEMOPS_SET4 +#endif +#ifndef UA_MOVE1 +#define UA_MOVE1 LZO_MEMOPS_MOVE1 +#endif +#ifndef UA_MOVE2 +#define UA_MOVE2 LZO_MEMOPS_MOVE2 +#endif +#ifndef UA_MOVE3 +#define UA_MOVE3 LZO_MEMOPS_MOVE3 +#endif +#ifndef UA_MOVE4 +#define UA_MOVE4 LZO_MEMOPS_MOVE4 +#endif +#ifndef UA_MOVE8 +#define UA_MOVE8 LZO_MEMOPS_MOVE8 +#endif +#ifndef UA_COPY1 +#define UA_COPY1 LZO_MEMOPS_COPY1 +#endif +#ifndef UA_COPY2 +#define UA_COPY2 LZO_MEMOPS_COPY2 +#endif +#ifndef UA_COPY3 +#define UA_COPY3 LZO_MEMOPS_COPY3 +#endif +#ifndef UA_COPY4 +#define UA_COPY4 LZO_MEMOPS_COPY4 +#endif +#ifndef UA_COPY8 +#define UA_COPY8 LZO_MEMOPS_COPY8 +#endif +#ifndef UA_COPYN +#define UA_COPYN LZO_MEMOPS_COPYN +#endif +#ifndef UA_COPYN_X +#define UA_COPYN_X LZO_MEMOPS_COPYN +#endif +#ifndef UA_GET_LE16 +#define UA_GET_LE16 LZO_MEMOPS_GET_LE16 +#endif +#ifndef UA_GET_LE32 +#define UA_GET_LE32 LZO_MEMOPS_GET_LE32 +#endif +#ifdef LZO_MEMOPS_GET_LE64 +#ifndef UA_GET_LE64 +#define UA_GET_LE64 LZO_MEMOPS_GET_LE64 +#endif +#endif +#ifndef UA_GET_NE16 +#define UA_GET_NE16 LZO_MEMOPS_GET_NE16 +#endif +#ifndef UA_GET_NE32 +#define UA_GET_NE32 LZO_MEMOPS_GET_NE32 +#endif +#ifdef LZO_MEMOPS_GET_NE64 +#ifndef UA_GET_NE64 +#define UA_GET_NE64 LZO_MEMOPS_GET_NE64 +#endif +#endif +#ifndef UA_PUT_LE16 +#define UA_PUT_LE16 LZO_MEMOPS_PUT_LE16 +#endif +#ifndef UA_PUT_LE32 +#define UA_PUT_LE32 LZO_MEMOPS_PUT_LE32 +#endif +#ifndef UA_PUT_NE16 +#define UA_PUT_NE16 LZO_MEMOPS_PUT_NE16 +#endif +#ifndef UA_PUT_NE32 +#define UA_PUT_NE32 LZO_MEMOPS_PUT_NE32 +#endif + +#define MEMCPY8_DS(dest,src,len) \ + lzo_memcpy(dest,src,len); dest += len; src += len + +#define BZERO8_PTR(s,l,n) \ + lzo_memset((lzo_voidp)(s),0,(lzo_uint)(l)*(n)) + +#define MEMCPY_DS(dest,src,len) \ + do *dest++ = *src++; while (--len > 0) + +LZO_EXTERN(const lzo_bytep) lzo_copyright(void); + +#ifndef __LZO_PTR_H +#define __LZO_PTR_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#if (LZO_ARCH_I086) +#error "LZO_ARCH_I086 is unsupported" +#elif (LZO_MM_PVP) +#error "LZO_MM_PVP is unsupported" +#else +#define PTR(a) ((lzo_uintptr_t) (a)) +#define PTR_LINEAR(a) PTR(a) +#define PTR_ALIGNED_4(a) ((PTR_LINEAR(a) & 3) == 0) +#define PTR_ALIGNED_8(a) ((PTR_LINEAR(a) & 7) == 0) +#define PTR_ALIGNED2_4(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 3) == 0) +#define PTR_ALIGNED2_8(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 7) == 0) +#endif + +#define PTR_LT(a,b) (PTR(a) < PTR(b)) +#define PTR_GE(a,b) (PTR(a) >= PTR(b)) +#define PTR_DIFF(a,b) (PTR(a) - PTR(b)) +#define pd(a,b) ((lzo_uint) ((a)-(b))) + +LZO_EXTERN(lzo_uintptr_t) +__lzo_ptr_linear(const lzo_voidp ptr); + +typedef union +{ + char a_char; + unsigned char a_uchar; + short a_short; + unsigned short a_ushort; + int a_int; + unsigned int a_uint; + long a_long; + unsigned long a_ulong; + lzo_int a_lzo_int; + lzo_uint a_lzo_uint; + lzo_xint a_lzo_xint; + lzo_int16_t a_lzo_int16_t; + lzo_uint16_t a_lzo_uint16_t; + lzo_int32_t a_lzo_int32_t; + lzo_uint32_t a_lzo_uint32_t; +#if defined(lzo_uint64_t) + lzo_int64_t a_lzo_int64_t; + lzo_uint64_t a_lzo_uint64_t; +#endif + size_t a_size_t; + ptrdiff_t a_ptrdiff_t; + lzo_uintptr_t a_lzo_uintptr_t; + void * a_void_p; + char * a_char_p; + unsigned char * a_uchar_p; + const void * a_c_void_p; + const char * a_c_char_p; + const unsigned char * a_c_uchar_p; + lzo_voidp a_lzo_voidp; + lzo_bytep a_lzo_bytep; + const lzo_voidp a_c_lzo_voidp; + const lzo_bytep a_c_lzo_bytep; +} +lzo_full_align_t; + +#ifdef __cplusplus +} +#endif + +#endif + +#ifndef LZO_DETERMINISTIC +#define LZO_DETERMINISTIC 1 +#endif + +#ifndef LZO_DICT_USE_PTR +#define LZO_DICT_USE_PTR 1 +#endif + +#if (LZO_DICT_USE_PTR) +# define lzo_dict_t const lzo_bytep +# define lzo_dict_p lzo_dict_t * +#else +# define lzo_dict_t lzo_uint +# define lzo_dict_p lzo_dict_t * +#endif + +#endif + +#if !defined(MINILZO_CFG_SKIP_LZO_PTR) + +LZO_PUBLIC(lzo_uintptr_t) +__lzo_ptr_linear(const lzo_voidp ptr) +{ + lzo_uintptr_t p; + +#if (LZO_ARCH_I086) +#error "LZO_ARCH_I086 is unsupported" +#elif (LZO_MM_PVP) +#error "LZO_MM_PVP is unsupported" +#else + p = (lzo_uintptr_t) PTR_LINEAR(ptr); +#endif + + return p; +} + +LZO_PUBLIC(unsigned) +__lzo_align_gap(const lzo_voidp ptr, lzo_uint size) +{ +#if (__LZO_UINTPTR_T_IS_POINTER) +#error "__LZO_UINTPTR_T_IS_POINTER is unsupported" +#else + lzo_uintptr_t p, n; + if (size < 2) return 0; + p = __lzo_ptr_linear(ptr); +#if 0 + n = (((p + size - 1) / size) * size) - p; +#else + if ((size & (size - 1)) != 0) + return 0; + n = size; n = ((p + n - 1) & ~(n - 1)) - p; +#endif +#endif + assert((long)n >= 0); + assert(n <= size); + return (unsigned)n; +} + +#endif +#if !defined(MINILZO_CFG_SKIP_LZO_UTIL) + +/* If you use the LZO library in a product, I would appreciate that you + * keep this copyright string in the executable of your product. + */ + +static const char lzo_copyright_[] = +#if !defined(__LZO_IN_MINLZO) + LZO_VERSION_STRING; +#else + "\r\n\n" + "LZO data compression library.\n" + "$Copyright: LZO Copyright (C) 1996-2017 Markus Franz Xaver Johannes Oberhumer\n" + "\n" + "http://www.oberhumer.com $\n\n" + "$Id: LZO version: v" LZO_VERSION_STRING ", " LZO_VERSION_DATE " $\n" + "$Info: " LZO_INFO_STRING " $\n"; +#endif +static const char lzo_version_string_[] = LZO_VERSION_STRING; +static const char lzo_version_date_[] = LZO_VERSION_DATE; + +LZO_PUBLIC(const lzo_bytep) +lzo_copyright(void) +{ + return (const lzo_bytep) lzo_copyright_; +} + +LZO_PUBLIC(unsigned) +lzo_version(void) +{ + return LZO_VERSION; +} + +LZO_PUBLIC(const char *) +lzo_version_string(void) +{ + return lzo_version_string_; +} + +LZO_PUBLIC(const char *) +lzo_version_date(void) +{ + return lzo_version_date_; +} + +LZO_PUBLIC(const lzo_charp) +_lzo_version_string(void) +{ + return lzo_version_string_; +} + +LZO_PUBLIC(const lzo_charp) +_lzo_version_date(void) +{ + return lzo_version_date_; +} + +#define LZO_BASE 65521u +#define LZO_NMAX 5552 + +#define LZO_DO1(buf,i) s1 += buf[i]; s2 += s1 +#define LZO_DO2(buf,i) LZO_DO1(buf,i); LZO_DO1(buf,i+1) +#define LZO_DO4(buf,i) LZO_DO2(buf,i); LZO_DO2(buf,i+2) +#define LZO_DO8(buf,i) LZO_DO4(buf,i); LZO_DO4(buf,i+4) +#define LZO_DO16(buf,i) LZO_DO8(buf,i); LZO_DO8(buf,i+8) + +LZO_PUBLIC(lzo_uint32_t) +lzo_adler32(lzo_uint32_t adler, const lzo_bytep buf, lzo_uint len) +{ + lzo_uint32_t s1 = adler & 0xffff; + lzo_uint32_t s2 = (adler >> 16) & 0xffff; + unsigned k; + + if (buf == NULL) + return 1; + + while (len > 0) + { + k = len < LZO_NMAX ? (unsigned) len : LZO_NMAX; + len -= k; + if (k >= 16) do + { + LZO_DO16(buf,0); + buf += 16; + k -= 16; + } while (k >= 16); + if (k != 0) do + { + s1 += *buf++; + s2 += s1; + } while (--k > 0); + s1 %= LZO_BASE; + s2 %= LZO_BASE; + } + return (s2 << 16) | s1; +} + +#undef LZO_DO1 +#undef LZO_DO2 +#undef LZO_DO4 +#undef LZO_DO8 +#undef LZO_DO16 + +#endif +#if !defined(MINILZO_CFG_SKIP_LZO_STRING) +#undef lzo_memcmp +#undef lzo_memcpy +#undef lzo_memmove +#undef lzo_memset +#if !defined(__LZO_MMODEL_HUGE) +# undef LZO_HAVE_MM_HUGE_PTR +#endif +#define lzo_hsize_t lzo_uint +#define lzo_hvoid_p lzo_voidp +#define lzo_hbyte_p lzo_bytep +#define LZOLIB_PUBLIC(r,f) LZO_PUBLIC(r) f +#define lzo_hmemcmp lzo_memcmp +#define lzo_hmemcpy lzo_memcpy +#define lzo_hmemmove lzo_memmove +#define lzo_hmemset lzo_memset +#define __LZOLIB_HMEMCPY_CH_INCLUDED 1 +#if !defined(LZOLIB_PUBLIC) +# define LZOLIB_PUBLIC(r,f) r __LZOLIB_FUNCNAME(f) +#endif +LZOLIB_PUBLIC(int, lzo_hmemcmp) (const lzo_hvoid_p s1, const lzo_hvoid_p s2, lzo_hsize_t len) +{ +#if (LZO_HAVE_MM_HUGE_PTR) || !(HAVE_MEMCMP) + const lzo_hbyte_p p1 = LZO_STATIC_CAST(const lzo_hbyte_p, s1); + const lzo_hbyte_p p2 = LZO_STATIC_CAST(const lzo_hbyte_p, s2); + if __lzo_likely(len > 0) do + { + int d = *p1 - *p2; + if (d != 0) + return d; + p1++; p2++; + } while __lzo_likely(--len > 0); + return 0; +#else + return memcmp(s1, s2, len); +#endif +} +LZOLIB_PUBLIC(lzo_hvoid_p, lzo_hmemcpy) (lzo_hvoid_p dest, const lzo_hvoid_p src, lzo_hsize_t len) +{ +#if (LZO_HAVE_MM_HUGE_PTR) || !(HAVE_MEMCPY) + lzo_hbyte_p p1 = LZO_STATIC_CAST(lzo_hbyte_p, dest); + const lzo_hbyte_p p2 = LZO_STATIC_CAST(const lzo_hbyte_p, src); + if (!(len > 0) || p1 == p2) + return dest; + do + *p1++ = *p2++; + while __lzo_likely(--len > 0); + return dest; +#else + return memcpy(dest, src, len); +#endif +} +LZOLIB_PUBLIC(lzo_hvoid_p, lzo_hmemmove) (lzo_hvoid_p dest, const lzo_hvoid_p src, lzo_hsize_t len) +{ +#if (LZO_HAVE_MM_HUGE_PTR) || !(HAVE_MEMMOVE) + lzo_hbyte_p p1 = LZO_STATIC_CAST(lzo_hbyte_p, dest); + const lzo_hbyte_p p2 = LZO_STATIC_CAST(const lzo_hbyte_p, src); + if (!(len > 0) || p1 == p2) + return dest; + if (p1 < p2) + { + do + *p1++ = *p2++; + while __lzo_likely(--len > 0); + } + else + { + p1 += len; + p2 += len; + do + *--p1 = *--p2; + while __lzo_likely(--len > 0); + } + return dest; +#else + return memmove(dest, src, len); +#endif +} +LZOLIB_PUBLIC(lzo_hvoid_p, lzo_hmemset) (lzo_hvoid_p s, int cc, lzo_hsize_t len) +{ +#if (LZO_HAVE_MM_HUGE_PTR) || !(HAVE_MEMSET) + lzo_hbyte_p p = LZO_STATIC_CAST(lzo_hbyte_p, s); + unsigned char c = LZO_ITRUNC(unsigned char, cc); + if __lzo_likely(len > 0) do + *p++ = c; + while __lzo_likely(--len > 0); + return s; +#else + return memset(s, cc, len); +#endif +} +#undef LZOLIB_PUBLIC +#endif +#if !defined(MINILZO_CFG_SKIP_LZO_INIT) + +#if !defined(__LZO_IN_MINILZO) + +#define LZO_WANT_ACC_CHK_CH 1 +#undef LZOCHK_ASSERT + + LZOCHK_ASSERT((LZO_UINT32_C(1) << (int)(8*sizeof(LZO_UINT32_C(1))-1)) > 0) + LZOCHK_ASSERT_IS_SIGNED_T(lzo_int) + LZOCHK_ASSERT_IS_UNSIGNED_T(lzo_uint) +#if !(__LZO_UINTPTR_T_IS_POINTER) + LZOCHK_ASSERT_IS_UNSIGNED_T(lzo_uintptr_t) +#endif + LZOCHK_ASSERT(sizeof(lzo_uintptr_t) >= sizeof(lzo_voidp)) + LZOCHK_ASSERT_IS_UNSIGNED_T(lzo_xint) + +#endif +#undef LZOCHK_ASSERT + +union lzo_config_check_union { + lzo_uint a[2]; + unsigned char b[2*LZO_MAX(8,sizeof(lzo_uint))]; +#if defined(lzo_uint64_t) + lzo_uint64_t c[2]; +#endif +}; + +#if 0 +#define u2p(ptr,off) ((lzo_voidp) (((lzo_bytep)(lzo_voidp)(ptr)) + (off))) +#else +static __lzo_noinline lzo_voidp u2p(lzo_voidp ptr, lzo_uint off) +{ + return (lzo_voidp) ((lzo_bytep) ptr + off); +} +#endif + +LZO_PUBLIC(int) +_lzo_config_check(void) +{ +#if (LZO_CC_CLANG && (LZO_CC_CLANG >= 0x030100ul && LZO_CC_CLANG < 0x030300ul)) +# if 0 + volatile +# endif +#endif + union lzo_config_check_union u; + lzo_voidp p; + unsigned r = 1; + + u.a[0] = u.a[1] = 0; + p = u2p(&u, 0); + r &= ((* (lzo_bytep) p) == 0); +#if !(LZO_CFG_NO_CONFIG_CHECK) +#if (LZO_ABI_BIG_ENDIAN) + u.a[0] = u.a[1] = 0; u.b[sizeof(lzo_uint) - 1] = 128; + p = u2p(&u, 0); + r &= ((* (lzo_uintp) p) == 128); +#endif +#if (LZO_ABI_LITTLE_ENDIAN) + u.a[0] = u.a[1] = 0; u.b[0] = 128; + p = u2p(&u, 0); + r &= ((* (lzo_uintp) p) == 128); +#endif + u.a[0] = u.a[1] = 0; + u.b[0] = 1; u.b[3] = 2; + p = u2p(&u, 1); + r &= UA_GET_NE16(p) == 0; + r &= UA_GET_LE16(p) == 0; + u.b[1] = 128; + r &= UA_GET_LE16(p) == 128; + u.b[2] = 129; + r &= UA_GET_LE16(p) == LZO_UINT16_C(0x8180); +#if (LZO_ABI_BIG_ENDIAN) + r &= UA_GET_NE16(p) == LZO_UINT16_C(0x8081); +#endif +#if (LZO_ABI_LITTLE_ENDIAN) + r &= UA_GET_NE16(p) == LZO_UINT16_C(0x8180); +#endif + u.a[0] = u.a[1] = 0; + u.b[0] = 3; u.b[5] = 4; + p = u2p(&u, 1); + r &= UA_GET_NE32(p) == 0; + r &= UA_GET_LE32(p) == 0; + u.b[1] = 128; + r &= UA_GET_LE32(p) == 128; + u.b[2] = 129; u.b[3] = 130; u.b[4] = 131; + r &= UA_GET_LE32(p) == LZO_UINT32_C(0x83828180); +#if (LZO_ABI_BIG_ENDIAN) + r &= UA_GET_NE32(p) == LZO_UINT32_C(0x80818283); +#endif +#if (LZO_ABI_LITTLE_ENDIAN) + r &= UA_GET_NE32(p) == LZO_UINT32_C(0x83828180); +#endif +#if defined(UA_GET_NE64) + u.c[0] = u.c[1] = 0; + u.b[0] = 5; u.b[9] = 6; + p = u2p(&u, 1); + u.c[0] = u.c[1] = 0; + r &= UA_GET_NE64(p) == 0; +#if defined(UA_GET_LE64) + r &= UA_GET_LE64(p) == 0; + u.b[1] = 128; + r &= UA_GET_LE64(p) == 128; +#endif +#endif +#if defined(lzo_bitops_ctlz32) + { unsigned i = 0; lzo_uint32_t v; + for (v = 1; v != 0 && r == 1; v <<= 1, i++) { + r &= lzo_bitops_ctlz32(v) == 31 - i; + r &= lzo_bitops_ctlz32_func(v) == 31 - i; + }} +#endif +#if defined(lzo_bitops_ctlz64) + { unsigned i = 0; lzo_uint64_t v; + for (v = 1; v != 0 && r == 1; v <<= 1, i++) { + r &= lzo_bitops_ctlz64(v) == 63 - i; + r &= lzo_bitops_ctlz64_func(v) == 63 - i; + }} +#endif +#if defined(lzo_bitops_cttz32) + { unsigned i = 0; lzo_uint32_t v; + for (v = 1; v != 0 && r == 1; v <<= 1, i++) { + r &= lzo_bitops_cttz32(v) == i; + r &= lzo_bitops_cttz32_func(v) == i; + }} +#endif +#if defined(lzo_bitops_cttz64) + { unsigned i = 0; lzo_uint64_t v; + for (v = 1; v != 0 && r == 1; v <<= 1, i++) { + r &= lzo_bitops_cttz64(v) == i; + r &= lzo_bitops_cttz64_func(v) == i; + }} +#endif +#endif + LZO_UNUSED_FUNC(lzo_bitops_unused_funcs); + + return r == 1 ? LZO_E_OK : LZO_E_ERROR; +} + +LZO_PUBLIC(int) +__lzo_init_v2(unsigned v, int s1, int s2, int s3, int s4, int s5, + int s6, int s7, int s8, int s9) +{ + int r; + +#if defined(__LZO_IN_MINILZO) +#elif (LZO_CC_MSC && ((_MSC_VER) < 700)) +#else +#define LZO_WANT_ACC_CHK_CH 1 +#undef LZOCHK_ASSERT +#define LZOCHK_ASSERT(expr) LZO_COMPILE_TIME_ASSERT(expr) +#endif +#undef LZOCHK_ASSERT + + if (v == 0) + return LZO_E_ERROR; + + r = (s1 == -1 || s1 == (int) sizeof(short)) && + (s2 == -1 || s2 == (int) sizeof(int)) && + (s3 == -1 || s3 == (int) sizeof(long)) && + (s4 == -1 || s4 == (int) sizeof(lzo_uint32_t)) && + (s5 == -1 || s5 == (int) sizeof(lzo_uint)) && + (s6 == -1 || s6 == (int) lzo_sizeof_dict_t) && + (s7 == -1 || s7 == (int) sizeof(char *)) && + (s8 == -1 || s8 == (int) sizeof(lzo_voidp)) && + (s9 == -1 || s9 == (int) sizeof(lzo_callback_t)); + if (!r) + return LZO_E_ERROR; + + r = _lzo_config_check(); + if (r != LZO_E_OK) + return r; + + return r; +} + +#if !defined(__LZO_IN_MINILZO) + +#if (LZO_OS_WIN16 && LZO_CC_WATCOMC) && defined(__SW_BD) + +#if 0 +BOOL FAR PASCAL LibMain ( HANDLE hInstance, WORD wDataSegment, + WORD wHeapSize, LPSTR lpszCmdLine ) +#else +int __far __pascal LibMain ( int a, short b, short c, long d ) +#endif +{ + LZO_UNUSED(a); LZO_UNUSED(b); LZO_UNUSED(c); LZO_UNUSED(d); + return 1; +} + +#endif + +#endif + +#endif + +#define LZO1X 1 +#define LZO_EOF_CODE 1 +#define M2_MAX_OFFSET 0x0800 + +#if !defined(MINILZO_CFG_SKIP_LZO1X_1_COMPRESS) + +#if 1 && defined(UA_GET_LE32) +#undef LZO_DICT_USE_PTR +#define LZO_DICT_USE_PTR 0 +#undef lzo_dict_t +#define lzo_dict_t lzo_uint16_t +#endif + +#define LZO_NEED_DICT_H 1 +#ifndef D_BITS +#define D_BITS 14 +#endif +#define D_INDEX1(d,p) d = DM(DMUL(0x21,DX3(p,5,5,6)) >> 5) +#define D_INDEX2(d,p) d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f) +#if 1 +#define DINDEX(dv,p) DM(((DMUL(0x1824429d,dv)) >> (32-D_BITS))) +#else +#define DINDEX(dv,p) DM((dv) + ((dv) >> (32-D_BITS))) +#endif + +#ifndef __LZO_CONFIG1X_H +#define __LZO_CONFIG1X_H 1 + +#if !defined(LZO1X) && !defined(LZO1Y) && !defined(LZO1Z) +# define LZO1X 1 +#endif + +#if !defined(__LZO_IN_MINILZO) +#include +#endif + +#ifndef LZO_EOF_CODE +#define LZO_EOF_CODE 1 +#endif +#undef LZO_DETERMINISTIC + +#define M1_MAX_OFFSET 0x0400 +#ifndef M2_MAX_OFFSET +#define M2_MAX_OFFSET 0x0800 +#endif +#define M3_MAX_OFFSET 0x4000 +#define M4_MAX_OFFSET 0xbfff + +#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET) + +#define M1_MIN_LEN 2 +#define M1_MAX_LEN 2 +#define M2_MIN_LEN 3 +#ifndef M2_MAX_LEN +#define M2_MAX_LEN 8 +#endif +#define M3_MIN_LEN 3 +#define M3_MAX_LEN 33 +#define M4_MIN_LEN 3 +#define M4_MAX_LEN 9 + +#define M1_MARKER 0 +#define M2_MARKER 64 +#define M3_MARKER 32 +#define M4_MARKER 16 + +#ifndef MIN_LOOKAHEAD +#define MIN_LOOKAHEAD (M2_MAX_LEN + 1) +#endif + +#if defined(LZO_NEED_DICT_H) + +#ifndef LZO_HASH +#define LZO_HASH LZO_HASH_LZO_INCREMENTAL_B +#endif +#define DL_MIN_LEN M2_MIN_LEN + +#ifndef __LZO_DICT_H +#define __LZO_DICT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(D_BITS) && defined(DBITS) +# define D_BITS DBITS +#endif +#if !defined(D_BITS) +# error "D_BITS is not defined" +#endif +#if (D_BITS < 16) +# define D_SIZE LZO_SIZE(D_BITS) +# define D_MASK LZO_MASK(D_BITS) +#else +# define D_SIZE LZO_USIZE(D_BITS) +# define D_MASK LZO_UMASK(D_BITS) +#endif +#define D_HIGH ((D_MASK >> 1) + 1) + +#if !defined(DD_BITS) +# define DD_BITS 0 +#endif +#define DD_SIZE LZO_SIZE(DD_BITS) +#define DD_MASK LZO_MASK(DD_BITS) + +#if !defined(DL_BITS) +# define DL_BITS (D_BITS - DD_BITS) +#endif +#if (DL_BITS < 16) +# define DL_SIZE LZO_SIZE(DL_BITS) +# define DL_MASK LZO_MASK(DL_BITS) +#else +# define DL_SIZE LZO_USIZE(DL_BITS) +# define DL_MASK LZO_UMASK(DL_BITS) +#endif + +#if (D_BITS != DL_BITS + DD_BITS) +# error "D_BITS does not match" +#endif +#if (D_BITS < 6 || D_BITS > 18) +# error "invalid D_BITS" +#endif +#if (DL_BITS < 6 || DL_BITS > 20) +# error "invalid DL_BITS" +#endif +#if (DD_BITS < 0 || DD_BITS > 6) +# error "invalid DD_BITS" +#endif + +#if !defined(DL_MIN_LEN) +# define DL_MIN_LEN 3 +#endif +#if !defined(DL_SHIFT) +# define DL_SHIFT ((DL_BITS + (DL_MIN_LEN - 1)) / DL_MIN_LEN) +#endif + +#define LZO_HASH_GZIP 1 +#define LZO_HASH_GZIP_INCREMENTAL 2 +#define LZO_HASH_LZO_INCREMENTAL_A 3 +#define LZO_HASH_LZO_INCREMENTAL_B 4 + +#if !defined(LZO_HASH) +# error "choose a hashing strategy" +#endif + +#undef DM +#undef DX + +#if (DL_MIN_LEN == 3) +# define _DV2_A(p,shift1,shift2) \ + (((( (lzo_xint)((p)[0]) << shift1) ^ (p)[1]) << shift2) ^ (p)[2]) +# define _DV2_B(p,shift1,shift2) \ + (((( (lzo_xint)((p)[2]) << shift1) ^ (p)[1]) << shift2) ^ (p)[0]) +# define _DV3_B(p,shift1,shift2,shift3) \ + ((_DV2_B((p)+1,shift1,shift2) << (shift3)) ^ (p)[0]) +#elif (DL_MIN_LEN == 2) +# define _DV2_A(p,shift1,shift2) \ + (( (lzo_xint)(p[0]) << shift1) ^ p[1]) +# define _DV2_B(p,shift1,shift2) \ + (( (lzo_xint)(p[1]) << shift1) ^ p[2]) +#else +# error "invalid DL_MIN_LEN" +#endif +#define _DV_A(p,shift) _DV2_A(p,shift,shift) +#define _DV_B(p,shift) _DV2_B(p,shift,shift) +#define DA2(p,s1,s2) \ + (((((lzo_xint)((p)[2]) << (s2)) + (p)[1]) << (s1)) + (p)[0]) +#define DS2(p,s1,s2) \ + (((((lzo_xint)((p)[2]) << (s2)) - (p)[1]) << (s1)) - (p)[0]) +#define DX2(p,s1,s2) \ + (((((lzo_xint)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0]) +#define DA3(p,s1,s2,s3) ((DA2((p)+1,s2,s3) << (s1)) + (p)[0]) +#define DS3(p,s1,s2,s3) ((DS2((p)+1,s2,s3) << (s1)) - (p)[0]) +#define DX3(p,s1,s2,s3) ((DX2((p)+1,s2,s3) << (s1)) ^ (p)[0]) +#define DMS(v,s) ((lzo_uint) (((v) & (D_MASK >> (s))) << (s))) +#define DM(v) DMS(v,0) + +#if (LZO_HASH == LZO_HASH_GZIP) +# define _DINDEX(dv,p) (_DV_A((p),DL_SHIFT)) + +#elif (LZO_HASH == LZO_HASH_GZIP_INCREMENTAL) +# define __LZO_HASH_INCREMENTAL 1 +# define DVAL_FIRST(dv,p) dv = _DV_A((p),DL_SHIFT) +# define DVAL_NEXT(dv,p) dv = (((dv) << DL_SHIFT) ^ p[2]) +# define _DINDEX(dv,p) (dv) +# define DVAL_LOOKAHEAD DL_MIN_LEN + +#elif (LZO_HASH == LZO_HASH_LZO_INCREMENTAL_A) +# define __LZO_HASH_INCREMENTAL 1 +# define DVAL_FIRST(dv,p) dv = _DV_A((p),5) +# define DVAL_NEXT(dv,p) \ + dv ^= (lzo_xint)(p[-1]) << (2*5); dv = (((dv) << 5) ^ p[2]) +# define _DINDEX(dv,p) ((DMUL(0x9f5f,dv)) >> 5) +# define DVAL_LOOKAHEAD DL_MIN_LEN + +#elif (LZO_HASH == LZO_HASH_LZO_INCREMENTAL_B) +# define __LZO_HASH_INCREMENTAL 1 +# define DVAL_FIRST(dv,p) dv = _DV_B((p),5) +# define DVAL_NEXT(dv,p) \ + dv ^= p[-1]; dv = (((dv) >> 5) ^ ((lzo_xint)(p[2]) << (2*5))) +# define _DINDEX(dv,p) ((DMUL(0x9f5f,dv)) >> 5) +# define DVAL_LOOKAHEAD DL_MIN_LEN + +#else +# error "choose a hashing strategy" +#endif + +#ifndef DINDEX +#define DINDEX(dv,p) ((lzo_uint)((_DINDEX(dv,p)) & DL_MASK) << DD_BITS) +#endif +#if !defined(DINDEX1) && defined(D_INDEX1) +#define DINDEX1 D_INDEX1 +#endif +#if !defined(DINDEX2) && defined(D_INDEX2) +#define DINDEX2 D_INDEX2 +#endif + +#if !defined(__LZO_HASH_INCREMENTAL) +# define DVAL_FIRST(dv,p) ((void) 0) +# define DVAL_NEXT(dv,p) ((void) 0) +# define DVAL_LOOKAHEAD 0 +#endif + +#if !defined(DVAL_ASSERT) +#if defined(__LZO_HASH_INCREMENTAL) && !defined(NDEBUG) +#if 1 && (LZO_CC_ARMCC_GNUC || LZO_CC_CLANG || (LZO_CC_GNUC >= 0x020700ul) || LZO_CC_INTELC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) +static void __attribute__((__unused__)) +#else +static void +#endif +DVAL_ASSERT(lzo_xint dv, const lzo_bytep p) +{ + lzo_xint df; + DVAL_FIRST(df,(p)); + assert(DINDEX(dv,p) == DINDEX(df,p)); +} +#else +# define DVAL_ASSERT(dv,p) ((void) 0) +#endif +#endif + +#if (LZO_DICT_USE_PTR) +# define DENTRY(p,in) (p) +# define GINDEX(m_pos,m_off,dict,dindex,in) m_pos = dict[dindex] +#else +# define DENTRY(p,in) ((lzo_dict_t) pd(p, in)) +# define GINDEX(m_pos,m_off,dict,dindex,in) m_off = dict[dindex] +#endif + +#if (DD_BITS == 0) + +# define UPDATE_D(dict,drun,dv,p,in) dict[ DINDEX(dv,p) ] = DENTRY(p,in) +# define UPDATE_I(dict,drun,index,p,in) dict[index] = DENTRY(p,in) +# define UPDATE_P(ptr,drun,p,in) (ptr)[0] = DENTRY(p,in) + +#else + +# define UPDATE_D(dict,drun,dv,p,in) \ + dict[ DINDEX(dv,p) + drun++ ] = DENTRY(p,in); drun &= DD_MASK +# define UPDATE_I(dict,drun,index,p,in) \ + dict[ (index) + drun++ ] = DENTRY(p,in); drun &= DD_MASK +# define UPDATE_P(ptr,drun,p,in) \ + (ptr) [ drun++ ] = DENTRY(p,in); drun &= DD_MASK + +#endif + +#if (LZO_DICT_USE_PTR) + +#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ + (m_pos == NULL || (m_off = pd(ip, m_pos)) > max_offset) + +#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \ + (BOUNDS_CHECKING_OFF_IN_EXPR(( \ + m_pos = ip - (lzo_uint) PTR_DIFF(ip,m_pos), \ + PTR_LT(m_pos,in) || \ + (m_off = (lzo_uint) PTR_DIFF(ip,m_pos)) == 0 || \ + m_off > max_offset ))) + +#else + +#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ + (m_off == 0 || \ + ((m_off = pd(ip, in) - m_off) > max_offset) || \ + (m_pos = (ip) - (m_off), 0) ) + +#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \ + (pd(ip, in) <= m_off || \ + ((m_off = pd(ip, in) - m_off) > max_offset) || \ + (m_pos = (ip) - (m_off), 0) ) + +#endif + +#if (LZO_DETERMINISTIC) +# define LZO_CHECK_MPOS LZO_CHECK_MPOS_DET +#else +# define LZO_CHECK_MPOS LZO_CHECK_MPOS_NON_DET +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + +#endif + +#define LZO_DETERMINISTIC !(LZO_DICT_USE_PTR) + +#ifndef DO_COMPRESS +#define DO_COMPRESS lzo1x_1_compress +#endif + +#if 1 && defined(DO_COMPRESS) && !defined(do_compress) +# define do_compress LZO_PP_ECONCAT2(DO_COMPRESS,_core) +#endif + +static __lzo_noinline lzo_uint +do_compress ( const lzo_bytep in , lzo_uint in_len, + lzo_bytep out, lzo_uintp out_len, + lzo_uint ti, lzo_voidp wrkmem) +{ + const lzo_bytep ip; + lzo_bytep op; + const lzo_bytep const in_end = in + in_len; + const lzo_bytep const ip_end = in + in_len - 20; + const lzo_bytep ii; + lzo_dict_p const dict = (lzo_dict_p) wrkmem; + + op = out; + ip = in; + ii = ip; + + ip += ti < 4 ? 4 - ti : 0; + for (;;) + { + const lzo_bytep m_pos; +#if !(LZO_DETERMINISTIC) + LZO_DEFINE_UNINITIALIZED_VAR(lzo_uint, m_off, 0); + lzo_uint m_len; + lzo_uint dindex; +next: + if __lzo_unlikely(ip >= ip_end) + break; + DINDEX1(dindex,ip); + GINDEX(m_pos,m_off,dict,dindex,in); + if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) + goto literal; +#if 1 + if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) + goto try_match; + DINDEX2(dindex,ip); +#endif + GINDEX(m_pos,m_off,dict,dindex,in); + if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) + goto literal; + if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) + goto try_match; + goto literal; + +try_match: +#if (LZO_OPT_UNALIGNED32) + if (UA_GET_NE32(m_pos) != UA_GET_NE32(ip)) +#else + if (m_pos[0] != ip[0] || m_pos[1] != ip[1] || m_pos[2] != ip[2] || m_pos[3] != ip[3]) +#endif + { +literal: + UPDATE_I(dict,0,dindex,ip,in); + ip += 1 + ((ip - ii) >> 5); + continue; + } + UPDATE_I(dict,0,dindex,ip,in); +#else + lzo_uint m_off; + lzo_uint m_len; + { + lzo_uint32_t dv; + lzo_uint dindex; +literal: + ip += 1 + ((ip - ii) >> 5); +next: + if __lzo_unlikely(ip >= ip_end) + break; + dv = UA_GET_LE32(ip); + dindex = DINDEX(dv,ip); + GINDEX(m_off,m_pos,in+dict,dindex,in); + UPDATE_I(dict,0,dindex,ip,in); + if __lzo_unlikely(dv != UA_GET_LE32(m_pos)) + goto literal; + } +#endif + + ii -= ti; ti = 0; + { + lzo_uint t = pd(ip,ii); + if (t != 0) + { + if (t <= 3) + { + op[-2] = LZO_BYTE(op[-2] | t); +#if (LZO_OPT_UNALIGNED32) + UA_COPY4(op, ii); + op += t; +#else + { do *op++ = *ii++; while (--t > 0); } +#endif + } +#if (LZO_OPT_UNALIGNED32) || (LZO_OPT_UNALIGNED64) + else if (t <= 16) + { + *op++ = LZO_BYTE(t - 3); + UA_COPY8(op, ii); + UA_COPY8(op+8, ii+8); + op += t; + } +#endif + else + { + if (t <= 18) + *op++ = LZO_BYTE(t - 3); + else + { + lzo_uint tt = t - 18; + *op++ = 0; + while __lzo_unlikely(tt > 255) + { + tt -= 255; + UA_SET1(op, 0); + op++; + } + assert(tt > 0); + *op++ = LZO_BYTE(tt); + } +#if (LZO_OPT_UNALIGNED32) || (LZO_OPT_UNALIGNED64) + do { + UA_COPY8(op, ii); + UA_COPY8(op+8, ii+8); + op += 16; ii += 16; t -= 16; + } while (t >= 16); if (t > 0) +#endif + { do *op++ = *ii++; while (--t > 0); } + } + } + } + m_len = 4; + { +#if (LZO_OPT_UNALIGNED64) + lzo_uint64_t v; + v = UA_GET_NE64(ip + m_len) ^ UA_GET_NE64(m_pos + m_len); + if __lzo_unlikely(v == 0) { + do { + m_len += 8; + v = UA_GET_NE64(ip + m_len) ^ UA_GET_NE64(m_pos + m_len); + if __lzo_unlikely(ip + m_len >= ip_end) + goto m_len_done; + } while (v == 0); + } +#if (LZO_ABI_BIG_ENDIAN) && defined(lzo_bitops_ctlz64) + m_len += lzo_bitops_ctlz64(v) / CHAR_BIT; +#elif (LZO_ABI_BIG_ENDIAN) + if ((v >> (64 - CHAR_BIT)) == 0) do { + v <<= CHAR_BIT; + m_len += 1; + } while ((v >> (64 - CHAR_BIT)) == 0); +#elif (LZO_ABI_LITTLE_ENDIAN) && defined(lzo_bitops_cttz64) + m_len += lzo_bitops_cttz64(v) / CHAR_BIT; +#elif (LZO_ABI_LITTLE_ENDIAN) + if ((v & UCHAR_MAX) == 0) do { + v >>= CHAR_BIT; + m_len += 1; + } while ((v & UCHAR_MAX) == 0); +#else + if (ip[m_len] == m_pos[m_len]) do { + m_len += 1; + } while (ip[m_len] == m_pos[m_len]); +#endif +#elif (LZO_OPT_UNALIGNED32) + lzo_uint32_t v; + v = UA_GET_NE32(ip + m_len) ^ UA_GET_NE32(m_pos + m_len); + if __lzo_unlikely(v == 0) { + do { + m_len += 4; + v = UA_GET_NE32(ip + m_len) ^ UA_GET_NE32(m_pos + m_len); + if (v != 0) + break; + m_len += 4; + v = UA_GET_NE32(ip + m_len) ^ UA_GET_NE32(m_pos + m_len); + if __lzo_unlikely(ip + m_len >= ip_end) + goto m_len_done; + } while (v == 0); + } +#if (LZO_ABI_BIG_ENDIAN) && defined(lzo_bitops_ctlz32) + m_len += lzo_bitops_ctlz32(v) / CHAR_BIT; +#elif (LZO_ABI_BIG_ENDIAN) + if ((v >> (32 - CHAR_BIT)) == 0) do { + v <<= CHAR_BIT; + m_len += 1; + } while ((v >> (32 - CHAR_BIT)) == 0); +#elif (LZO_ABI_LITTLE_ENDIAN) && defined(lzo_bitops_cttz32) + m_len += lzo_bitops_cttz32(v) / CHAR_BIT; +#elif (LZO_ABI_LITTLE_ENDIAN) + if ((v & UCHAR_MAX) == 0) do { + v >>= CHAR_BIT; + m_len += 1; + } while ((v & UCHAR_MAX) == 0); +#else + if (ip[m_len] == m_pos[m_len]) do { + m_len += 1; + } while (ip[m_len] == m_pos[m_len]); +#endif +#else + if __lzo_unlikely(ip[m_len] == m_pos[m_len]) { + do { + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if __lzo_unlikely(ip + m_len >= ip_end) + goto m_len_done; + } while (ip[m_len] == m_pos[m_len]); + } +#endif + } +m_len_done: + m_off = pd(ip,m_pos); + ip += m_len; + ii = ip; + if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) + { + m_off -= 1; +#if defined(LZO1X) + *op++ = LZO_BYTE(((m_len - 1) << 5) | ((m_off & 7) << 2)); + *op++ = LZO_BYTE(m_off >> 3); +#elif defined(LZO1Y) + *op++ = LZO_BYTE(((m_len + 1) << 4) | ((m_off & 3) << 2)); + *op++ = LZO_BYTE(m_off >> 2); +#endif + } + else if (m_off <= M3_MAX_OFFSET) + { + m_off -= 1; + if (m_len <= M3_MAX_LEN) + *op++ = LZO_BYTE(M3_MARKER | (m_len - 2)); + else + { + m_len -= M3_MAX_LEN; + *op++ = M3_MARKER | 0; + while __lzo_unlikely(m_len > 255) + { + m_len -= 255; + UA_SET1(op, 0); + op++; + } + *op++ = LZO_BYTE(m_len); + } + *op++ = LZO_BYTE(m_off << 2); + *op++ = LZO_BYTE(m_off >> 6); + } + else + { + m_off -= 0x4000; + if (m_len <= M4_MAX_LEN) + *op++ = LZO_BYTE(M4_MARKER | ((m_off >> 11) & 8) | (m_len - 2)); + else + { + m_len -= M4_MAX_LEN; + *op++ = LZO_BYTE(M4_MARKER | ((m_off >> 11) & 8)); + while __lzo_unlikely(m_len > 255) + { + m_len -= 255; + UA_SET1(op, 0); + op++; + } + *op++ = LZO_BYTE(m_len); + } + *op++ = LZO_BYTE(m_off << 2); + *op++ = LZO_BYTE(m_off >> 6); + } + goto next; + } + + *out_len = pd(op, out); + return pd(in_end,ii-ti); +} + +LZO_PUBLIC(int) +DO_COMPRESS ( const lzo_bytep in , lzo_uint in_len, + lzo_bytep out, lzo_uintp out_len, + lzo_voidp wrkmem ) +{ + const lzo_bytep ip = in; + lzo_bytep op = out; + lzo_uint l = in_len; + lzo_uint t = 0; + + while (l > 20) + { + lzo_uint ll = l; + lzo_uintptr_t ll_end; +#if 0 || (LZO_DETERMINISTIC) + ll = LZO_MIN(ll, 49152); +#endif + ll_end = (lzo_uintptr_t)ip + ll; + if ((ll_end + ((t + ll) >> 5)) <= ll_end || (const lzo_bytep)(ll_end + ((t + ll) >> 5)) <= ip + ll) + break; +#if (LZO_DETERMINISTIC) + lzo_memset(wrkmem, 0, ((lzo_uint)1 << D_BITS) * sizeof(lzo_dict_t)); +#endif + t = do_compress(ip,ll,op,out_len,t,wrkmem); + ip += ll; + op += *out_len; + l -= ll; + } + t += l; + + if (t > 0) + { + const lzo_bytep ii = in + in_len - t; + + if (op == out && t <= 238) + *op++ = LZO_BYTE(17 + t); + else if (t <= 3) + op[-2] = LZO_BYTE(op[-2] | t); + else if (t <= 18) + *op++ = LZO_BYTE(t - 3); + else + { + lzo_uint tt = t - 18; + + *op++ = 0; + while (tt > 255) + { + tt -= 255; + UA_SET1(op, 0); + op++; + } + assert(tt > 0); + *op++ = LZO_BYTE(tt); + } + UA_COPYN(op, ii, t); + op += t; + } + + *op++ = M4_MARKER | 1; + *op++ = 0; + *op++ = 0; + + *out_len = pd(op, out); + return LZO_E_OK; +} + +#endif + +#undef do_compress +#undef DO_COMPRESS +#undef LZO_HASH + +#undef LZO_TEST_OVERRUN +#undef DO_DECOMPRESS +#define DO_DECOMPRESS lzo1x_decompress + +#if !defined(MINILZO_CFG_SKIP_LZO1X_DECOMPRESS) + +#if defined(LZO_TEST_OVERRUN) +# if !defined(LZO_TEST_OVERRUN_INPUT) +# define LZO_TEST_OVERRUN_INPUT 2 +# endif +# if !defined(LZO_TEST_OVERRUN_OUTPUT) +# define LZO_TEST_OVERRUN_OUTPUT 2 +# endif +# if !defined(LZO_TEST_OVERRUN_LOOKBEHIND) +# define LZO_TEST_OVERRUN_LOOKBEHIND 1 +# endif +#endif + +#undef TEST_IP +#undef TEST_OP +#undef TEST_IP_AND_TEST_OP +#undef TEST_LB +#undef TEST_LBO +#undef NEED_IP +#undef NEED_OP +#undef TEST_IV +#undef TEST_OV +#undef HAVE_TEST_IP +#undef HAVE_TEST_OP +#undef HAVE_NEED_IP +#undef HAVE_NEED_OP +#undef HAVE_ANY_IP +#undef HAVE_ANY_OP + +#if defined(LZO_TEST_OVERRUN_INPUT) +# if (LZO_TEST_OVERRUN_INPUT >= 1) +# define TEST_IP (ip < ip_end) +# endif +# if (LZO_TEST_OVERRUN_INPUT >= 2) +# define NEED_IP(x) \ + if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x)) goto input_overrun +# define TEST_IV(x) if ((x) > (lzo_uint)0 - (511)) goto input_overrun +# endif +#endif + +#if defined(LZO_TEST_OVERRUN_OUTPUT) +# if (LZO_TEST_OVERRUN_OUTPUT >= 1) +# define TEST_OP (op <= op_end) +# endif +# if (LZO_TEST_OVERRUN_OUTPUT >= 2) +# undef TEST_OP +# define NEED_OP(x) \ + if ((lzo_uint)(op_end - op) < (lzo_uint)(x)) goto output_overrun +# define TEST_OV(x) if ((x) > (lzo_uint)0 - (511)) goto output_overrun +# endif +#endif + +#if defined(LZO_TEST_OVERRUN_LOOKBEHIND) +# define TEST_LB(m_pos) if (PTR_LT(m_pos,out) || PTR_GE(m_pos,op)) goto lookbehind_overrun +# define TEST_LBO(m_pos,o) if (PTR_LT(m_pos,out) || PTR_GE(m_pos,op-(o))) goto lookbehind_overrun +#else +# define TEST_LB(m_pos) ((void) 0) +# define TEST_LBO(m_pos,o) ((void) 0) +#endif + +#if !defined(LZO_EOF_CODE) && !defined(TEST_IP) +# define TEST_IP (ip < ip_end) +#endif + +#if defined(TEST_IP) +# define HAVE_TEST_IP 1 +#else +# define TEST_IP 1 +#endif +#if defined(TEST_OP) +# define HAVE_TEST_OP 1 +#else +# define TEST_OP 1 +#endif + +#if defined(HAVE_TEST_IP) && defined(HAVE_TEST_OP) +# define TEST_IP_AND_TEST_OP (TEST_IP && TEST_OP) +#elif defined(HAVE_TEST_IP) +# define TEST_IP_AND_TEST_OP TEST_IP +#elif defined(HAVE_TEST_OP) +# define TEST_IP_AND_TEST_OP TEST_OP +#else +# define TEST_IP_AND_TEST_OP 1 +#endif + +#if defined(NEED_IP) +# define HAVE_NEED_IP 1 +#else +# define NEED_IP(x) ((void) 0) +# define TEST_IV(x) ((void) 0) +#endif +#if defined(NEED_OP) +# define HAVE_NEED_OP 1 +#else +# define NEED_OP(x) ((void) 0) +# define TEST_OV(x) ((void) 0) +#endif + +#if defined(HAVE_TEST_IP) || defined(HAVE_NEED_IP) +# define HAVE_ANY_IP 1 +#endif +#if defined(HAVE_TEST_OP) || defined(HAVE_NEED_OP) +# define HAVE_ANY_OP 1 +#endif + +#if defined(DO_DECOMPRESS) +LZO_PUBLIC(int) +DO_DECOMPRESS ( const lzo_bytep in , lzo_uint in_len, + lzo_bytep out, lzo_uintp out_len, + lzo_voidp wrkmem ) +#endif +{ + lzo_bytep op; + const lzo_bytep ip; + lzo_uint t; +#if defined(COPY_DICT) + lzo_uint m_off; + const lzo_bytep dict_end; +#else + const lzo_bytep m_pos; +#endif + + const lzo_bytep const ip_end = in + in_len; +#if defined(HAVE_ANY_OP) + lzo_bytep const op_end = out + *out_len; +#endif +#if defined(LZO1Z) + lzo_uint last_m_off = 0; +#endif + + LZO_UNUSED(wrkmem); + +#if defined(COPY_DICT) + if (dict) + { + if (dict_len > M4_MAX_OFFSET) + { + dict += dict_len - M4_MAX_OFFSET; + dict_len = M4_MAX_OFFSET; + } + dict_end = dict + dict_len; + } + else + { + dict_len = 0; + dict_end = NULL; + } +#endif + + *out_len = 0; + + op = out; + ip = in; + + NEED_IP(1); + if (*ip > 17) + { + t = *ip++ - 17; + if (t < 4) + goto match_next; + assert(t > 0); NEED_OP(t); NEED_IP(t+3); + do *op++ = *ip++; while (--t > 0); + goto first_literal_run; + } + + for (;;) + { + NEED_IP(3); + t = *ip++; + if (t >= 16) + goto match; + if (t == 0) + { + while (*ip == 0) + { + t += 255; + ip++; + TEST_IV(t); + NEED_IP(1); + } + t += 15 + *ip++; + } + assert(t > 0); NEED_OP(t+3); NEED_IP(t+6); +#if (LZO_OPT_UNALIGNED64) && (LZO_OPT_UNALIGNED32) + t += 3; + if (t >= 8) do + { + UA_COPY8(op,ip); + op += 8; ip += 8; t -= 8; + } while (t >= 8); + if (t >= 4) + { + UA_COPY4(op,ip); + op += 4; ip += 4; t -= 4; + } + if (t > 0) + { + *op++ = *ip++; + if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } } + } +#elif (LZO_OPT_UNALIGNED32) || (LZO_ALIGNED_OK_4) +#if !(LZO_OPT_UNALIGNED32) + if (PTR_ALIGNED2_4(op,ip)) + { +#endif + UA_COPY4(op,ip); + op += 4; ip += 4; + if (--t > 0) + { + if (t >= 4) + { + do { + UA_COPY4(op,ip); + op += 4; ip += 4; t -= 4; + } while (t >= 4); + if (t > 0) do *op++ = *ip++; while (--t > 0); + } + else + do *op++ = *ip++; while (--t > 0); + } +#if !(LZO_OPT_UNALIGNED32) + } + else +#endif +#endif +#if !(LZO_OPT_UNALIGNED32) + { + *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; + do *op++ = *ip++; while (--t > 0); + } +#endif + +first_literal_run: + + t = *ip++; + if (t >= 16) + goto match; +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(3); + t = 3; COPY_DICT(t,m_off) +#else +#if defined(LZO1Z) + t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - (1 + M2_MAX_OFFSET); + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LB(m_pos); NEED_OP(3); + *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos; +#endif + goto match_done; + + for (;;) { +match: + if (t >= 64) + { +#if defined(COPY_DICT) +#if defined(LZO1X) + m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3); + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2); + t = (t >> 4) - 3; +#elif defined(LZO1Z) + m_off = t & 0x1f; + if (m_off >= 0x1c) + m_off = last_m_off; + else + { + m_off = 1 + (m_off << 6) + (*ip++ >> 2); + last_m_off = m_off; + } + t = (t >> 5) - 1; +#endif +#else +#if defined(LZO1X) + m_pos = op - 1; + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_pos = op - 1; + m_pos -= (t >> 2) & 3; + m_pos -= *ip++ << 2; + t = (t >> 4) - 3; +#elif defined(LZO1Z) + { + lzo_uint off = t & 0x1f; + m_pos = op; + if (off >= 0x1c) + { + assert(last_m_off > 0); + m_pos -= last_m_off; + } + else + { + off = 1 + (off << 6) + (*ip++ >> 2); + m_pos -= off; + last_m_off = off; + } + } + t = (t >> 5) - 1; +#endif + TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); + goto copy_match; +#endif + } + else if (t >= 32) + { + t &= 31; + if (t == 0) + { + while (*ip == 0) + { + t += 255; + ip++; + TEST_OV(t); + NEED_IP(1); + } + t += 31 + *ip++; + NEED_IP(2); + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (ip[0] << 6) + (ip[1] >> 2); + last_m_off = m_off; +#else + m_off = 1 + (ip[0] >> 2) + (ip[1] << 6); +#endif +#else +#if defined(LZO1Z) + { + lzo_uint off = 1 + (ip[0] << 6) + (ip[1] >> 2); + m_pos = op - off; + last_m_off = off; + } +#elif (LZO_OPT_UNALIGNED16) && (LZO_ABI_LITTLE_ENDIAN) + m_pos = op - 1; + m_pos -= UA_GET_LE16(ip) >> 2; +#else + m_pos = op - 1; + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif +#endif + ip += 2; + } + else if (t >= 16) + { +#if defined(COPY_DICT) + m_off = (t & 8) << 11; +#else + m_pos = op; + m_pos -= (t & 8) << 11; +#endif + t &= 7; + if (t == 0) + { + while (*ip == 0) + { + t += 255; + ip++; + TEST_OV(t); + NEED_IP(1); + } + t += 7 + *ip++; + NEED_IP(2); + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off += (ip[0] << 6) + (ip[1] >> 2); +#else + m_off += (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_off == 0) + goto eof_found; + m_off += 0x4000; +#if defined(LZO1Z) + last_m_off = m_off; +#endif +#else +#if defined(LZO1Z) + m_pos -= (ip[0] << 6) + (ip[1] >> 2); +#elif (LZO_OPT_UNALIGNED16) && (LZO_ABI_LITTLE_ENDIAN) + m_pos -= UA_GET_LE16(ip) >> 2; +#else + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; +#if defined(LZO1Z) + last_m_off = pd((const lzo_bytep)op, m_pos); +#endif +#endif + } + else + { +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = 1 + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(2); + t = 2; COPY_DICT(t,m_off) +#else +#if defined(LZO1Z) + t = 1 + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LB(m_pos); NEED_OP(2); + *op++ = *m_pos++; *op++ = *m_pos; +#endif + goto match_done; + } + +#if defined(COPY_DICT) + + NEED_OP(t+3-1); + t += 3-1; COPY_DICT(t,m_off) + +#else + + TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); +#if (LZO_OPT_UNALIGNED64) && (LZO_OPT_UNALIGNED32) + if (op - m_pos >= 8) + { + t += (3 - 1); + if (t >= 8) do + { + UA_COPY8(op,m_pos); + op += 8; m_pos += 8; t -= 8; + } while (t >= 8); + if (t >= 4) + { + UA_COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4; + } + if (t > 0) + { + *op++ = m_pos[0]; + if (t > 1) { *op++ = m_pos[1]; if (t > 2) { *op++ = m_pos[2]; } } + } + } + else +#elif (LZO_OPT_UNALIGNED32) || (LZO_ALIGNED_OK_4) +#if !(LZO_OPT_UNALIGNED32) + if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op,m_pos)) + { + assert((op - m_pos) >= 4); +#else + if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) + { +#endif + UA_COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4 - (3 - 1); + do { + UA_COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4; + } while (t >= 4); + if (t > 0) do *op++ = *m_pos++; while (--t > 0); + } + else +#endif + { +copy_match: + *op++ = *m_pos++; *op++ = *m_pos++; + do *op++ = *m_pos++; while (--t > 0); + } + +#endif + +match_done: +#if defined(LZO1Z) + t = ip[-1] & 3; +#else + t = ip[-2] & 3; +#endif + if (t == 0) + break; + +match_next: + assert(t > 0); assert(t < 4); NEED_OP(t); NEED_IP(t+3); +#if 0 + do *op++ = *ip++; while (--t > 0); +#else + *op++ = *ip++; + if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } } +#endif + t = *ip++; + } + } + +eof_found: + *out_len = pd(op, out); + return (ip == ip_end ? LZO_E_OK : + (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); + +#if defined(HAVE_NEED_IP) +input_overrun: + *out_len = pd(op, out); + return LZO_E_INPUT_OVERRUN; +#endif + +#if defined(HAVE_NEED_OP) +output_overrun: + *out_len = pd(op, out); + return LZO_E_OUTPUT_OVERRUN; +#endif + +#if defined(LZO_TEST_OVERRUN_LOOKBEHIND) +lookbehind_overrun: + *out_len = pd(op, out); + return LZO_E_LOOKBEHIND_OVERRUN; +#endif +} + +#endif + +#define LZO_TEST_OVERRUN 1 +#undef DO_DECOMPRESS +#define DO_DECOMPRESS lzo1x_decompress_safe + +#if !defined(MINILZO_CFG_SKIP_LZO1X_DECOMPRESS_SAFE) + +#if defined(LZO_TEST_OVERRUN) +# if !defined(LZO_TEST_OVERRUN_INPUT) +# define LZO_TEST_OVERRUN_INPUT 2 +# endif +# if !defined(LZO_TEST_OVERRUN_OUTPUT) +# define LZO_TEST_OVERRUN_OUTPUT 2 +# endif +# if !defined(LZO_TEST_OVERRUN_LOOKBEHIND) +# define LZO_TEST_OVERRUN_LOOKBEHIND 1 +# endif +#endif + +#undef TEST_IP +#undef TEST_OP +#undef TEST_IP_AND_TEST_OP +#undef TEST_LB +#undef TEST_LBO +#undef NEED_IP +#undef NEED_OP +#undef TEST_IV +#undef TEST_OV +#undef HAVE_TEST_IP +#undef HAVE_TEST_OP +#undef HAVE_NEED_IP +#undef HAVE_NEED_OP +#undef HAVE_ANY_IP +#undef HAVE_ANY_OP + +#if defined(LZO_TEST_OVERRUN_INPUT) +# if (LZO_TEST_OVERRUN_INPUT >= 1) +# define TEST_IP (ip < ip_end) +# endif +# if (LZO_TEST_OVERRUN_INPUT >= 2) +# define NEED_IP(x) \ + if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x)) goto input_overrun +# define TEST_IV(x) if ((x) > (lzo_uint)0 - (511)) goto input_overrun +# endif +#endif + +#if defined(LZO_TEST_OVERRUN_OUTPUT) +# if (LZO_TEST_OVERRUN_OUTPUT >= 1) +# define TEST_OP (op <= op_end) +# endif +# if (LZO_TEST_OVERRUN_OUTPUT >= 2) +# undef TEST_OP +# define NEED_OP(x) \ + if ((lzo_uint)(op_end - op) < (lzo_uint)(x)) goto output_overrun +# define TEST_OV(x) if ((x) > (lzo_uint)0 - (511)) goto output_overrun +# endif +#endif + +#if defined(LZO_TEST_OVERRUN_LOOKBEHIND) +# define TEST_LB(m_pos) if (PTR_LT(m_pos,out) || PTR_GE(m_pos,op)) goto lookbehind_overrun +# define TEST_LBO(m_pos,o) if (PTR_LT(m_pos,out) || PTR_GE(m_pos,op-(o))) goto lookbehind_overrun +#else +# define TEST_LB(m_pos) ((void) 0) +# define TEST_LBO(m_pos,o) ((void) 0) +#endif + +#if !defined(LZO_EOF_CODE) && !defined(TEST_IP) +# define TEST_IP (ip < ip_end) +#endif + +#if defined(TEST_IP) +# define HAVE_TEST_IP 1 +#else +# define TEST_IP 1 +#endif +#if defined(TEST_OP) +# define HAVE_TEST_OP 1 +#else +# define TEST_OP 1 +#endif + +#if defined(HAVE_TEST_IP) && defined(HAVE_TEST_OP) +# define TEST_IP_AND_TEST_OP (TEST_IP && TEST_OP) +#elif defined(HAVE_TEST_IP) +# define TEST_IP_AND_TEST_OP TEST_IP +#elif defined(HAVE_TEST_OP) +# define TEST_IP_AND_TEST_OP TEST_OP +#else +# define TEST_IP_AND_TEST_OP 1 +#endif + +#if defined(NEED_IP) +# define HAVE_NEED_IP 1 +#else +# define NEED_IP(x) ((void) 0) +# define TEST_IV(x) ((void) 0) +#endif +#if defined(NEED_OP) +# define HAVE_NEED_OP 1 +#else +# define NEED_OP(x) ((void) 0) +# define TEST_OV(x) ((void) 0) +#endif + +#if defined(HAVE_TEST_IP) || defined(HAVE_NEED_IP) +# define HAVE_ANY_IP 1 +#endif +#if defined(HAVE_TEST_OP) || defined(HAVE_NEED_OP) +# define HAVE_ANY_OP 1 +#endif + +#if defined(DO_DECOMPRESS) +LZO_PUBLIC(int) +DO_DECOMPRESS ( const lzo_bytep in , lzo_uint in_len, + lzo_bytep out, lzo_uintp out_len, + lzo_voidp wrkmem ) +#endif +{ + lzo_bytep op; + const lzo_bytep ip; + lzo_uint t; +#if defined(COPY_DICT) + lzo_uint m_off; + const lzo_bytep dict_end; +#else + const lzo_bytep m_pos; +#endif + + const lzo_bytep const ip_end = in + in_len; +#if defined(HAVE_ANY_OP) + lzo_bytep const op_end = out + *out_len; +#endif +#if defined(LZO1Z) + lzo_uint last_m_off = 0; +#endif + + LZO_UNUSED(wrkmem); + +#if defined(COPY_DICT) + if (dict) + { + if (dict_len > M4_MAX_OFFSET) + { + dict += dict_len - M4_MAX_OFFSET; + dict_len = M4_MAX_OFFSET; + } + dict_end = dict + dict_len; + } + else + { + dict_len = 0; + dict_end = NULL; + } +#endif + + *out_len = 0; + + op = out; + ip = in; + + NEED_IP(1); + if (*ip > 17) + { + t = *ip++ - 17; + if (t < 4) + goto match_next; + assert(t > 0); NEED_OP(t); NEED_IP(t+3); + do *op++ = *ip++; while (--t > 0); + goto first_literal_run; + } + + for (;;) + { + NEED_IP(3); + t = *ip++; + if (t >= 16) + goto match; + if (t == 0) + { + while (*ip == 0) + { + t += 255; + ip++; + TEST_IV(t); + NEED_IP(1); + } + t += 15 + *ip++; + } + assert(t > 0); NEED_OP(t+3); NEED_IP(t+6); +#if (LZO_OPT_UNALIGNED64) && (LZO_OPT_UNALIGNED32) + t += 3; + if (t >= 8) do + { + UA_COPY8(op,ip); + op += 8; ip += 8; t -= 8; + } while (t >= 8); + if (t >= 4) + { + UA_COPY4(op,ip); + op += 4; ip += 4; t -= 4; + } + if (t > 0) + { + *op++ = *ip++; + if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } } + } +#elif (LZO_OPT_UNALIGNED32) || (LZO_ALIGNED_OK_4) +#if !(LZO_OPT_UNALIGNED32) + if (PTR_ALIGNED2_4(op,ip)) + { +#endif + UA_COPY4(op,ip); + op += 4; ip += 4; + if (--t > 0) + { + if (t >= 4) + { + do { + UA_COPY4(op,ip); + op += 4; ip += 4; t -= 4; + } while (t >= 4); + if (t > 0) do *op++ = *ip++; while (--t > 0); + } + else + do *op++ = *ip++; while (--t > 0); + } +#if !(LZO_OPT_UNALIGNED32) + } + else +#endif +#endif +#if !(LZO_OPT_UNALIGNED32) + { + *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; + do *op++ = *ip++; while (--t > 0); + } +#endif + +first_literal_run: + + t = *ip++; + if (t >= 16) + goto match; +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(3); + t = 3; COPY_DICT(t,m_off) +#else +#if defined(LZO1Z) + t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - (1 + M2_MAX_OFFSET); + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LB(m_pos); NEED_OP(3); + *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos; +#endif + goto match_done; + + for (;;) { +match: + if (t >= 64) + { +#if defined(COPY_DICT) +#if defined(LZO1X) + m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3); + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2); + t = (t >> 4) - 3; +#elif defined(LZO1Z) + m_off = t & 0x1f; + if (m_off >= 0x1c) + m_off = last_m_off; + else + { + m_off = 1 + (m_off << 6) + (*ip++ >> 2); + last_m_off = m_off; + } + t = (t >> 5) - 1; +#endif +#else +#if defined(LZO1X) + m_pos = op - 1; + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_pos = op - 1; + m_pos -= (t >> 2) & 3; + m_pos -= *ip++ << 2; + t = (t >> 4) - 3; +#elif defined(LZO1Z) + { + lzo_uint off = t & 0x1f; + m_pos = op; + if (off >= 0x1c) + { + assert(last_m_off > 0); + m_pos -= last_m_off; + } + else + { + off = 1 + (off << 6) + (*ip++ >> 2); + m_pos -= off; + last_m_off = off; + } + } + t = (t >> 5) - 1; +#endif + TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); + goto copy_match; +#endif + } + else if (t >= 32) + { + t &= 31; + if (t == 0) + { + while (*ip == 0) + { + t += 255; + ip++; + TEST_OV(t); + NEED_IP(1); + } + t += 31 + *ip++; + NEED_IP(2); + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (ip[0] << 6) + (ip[1] >> 2); + last_m_off = m_off; +#else + m_off = 1 + (ip[0] >> 2) + (ip[1] << 6); +#endif +#else +#if defined(LZO1Z) + { + lzo_uint off = 1 + (ip[0] << 6) + (ip[1] >> 2); + m_pos = op - off; + last_m_off = off; + } +#elif (LZO_OPT_UNALIGNED16) && (LZO_ABI_LITTLE_ENDIAN) + m_pos = op - 1; + m_pos -= UA_GET_LE16(ip) >> 2; +#else + m_pos = op - 1; + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif +#endif + ip += 2; + } + else if (t >= 16) + { +#if defined(COPY_DICT) + m_off = (t & 8) << 11; +#else + m_pos = op; + m_pos -= (t & 8) << 11; +#endif + t &= 7; + if (t == 0) + { + while (*ip == 0) + { + t += 255; + ip++; + TEST_OV(t); + NEED_IP(1); + } + t += 7 + *ip++; + NEED_IP(2); + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off += (ip[0] << 6) + (ip[1] >> 2); +#else + m_off += (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_off == 0) + goto eof_found; + m_off += 0x4000; +#if defined(LZO1Z) + last_m_off = m_off; +#endif +#else +#if defined(LZO1Z) + m_pos -= (ip[0] << 6) + (ip[1] >> 2); +#elif (LZO_OPT_UNALIGNED16) && (LZO_ABI_LITTLE_ENDIAN) + m_pos -= UA_GET_LE16(ip) >> 2; +#else + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; +#if defined(LZO1Z) + last_m_off = pd((const lzo_bytep)op, m_pos); +#endif +#endif + } + else + { +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = 1 + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(2); + t = 2; COPY_DICT(t,m_off) +#else +#if defined(LZO1Z) + t = 1 + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LB(m_pos); NEED_OP(2); + *op++ = *m_pos++; *op++ = *m_pos; +#endif + goto match_done; + } + +#if defined(COPY_DICT) + + NEED_OP(t+3-1); + t += 3-1; COPY_DICT(t,m_off) + +#else + + TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); +#if (LZO_OPT_UNALIGNED64) && (LZO_OPT_UNALIGNED32) + if (op - m_pos >= 8) + { + t += (3 - 1); + if (t >= 8) do + { + UA_COPY8(op,m_pos); + op += 8; m_pos += 8; t -= 8; + } while (t >= 8); + if (t >= 4) + { + UA_COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4; + } + if (t > 0) + { + *op++ = m_pos[0]; + if (t > 1) { *op++ = m_pos[1]; if (t > 2) { *op++ = m_pos[2]; } } + } + } + else +#elif (LZO_OPT_UNALIGNED32) || (LZO_ALIGNED_OK_4) +#if !(LZO_OPT_UNALIGNED32) + if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op,m_pos)) + { + assert((op - m_pos) >= 4); +#else + if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) + { +#endif + UA_COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4 - (3 - 1); + do { + UA_COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4; + } while (t >= 4); + if (t > 0) do *op++ = *m_pos++; while (--t > 0); + } + else +#endif + { +copy_match: + *op++ = *m_pos++; *op++ = *m_pos++; + do *op++ = *m_pos++; while (--t > 0); + } + +#endif + +match_done: +#if defined(LZO1Z) + t = ip[-1] & 3; +#else + t = ip[-2] & 3; +#endif + if (t == 0) + break; + +match_next: + assert(t > 0); assert(t < 4); NEED_OP(t); NEED_IP(t+3); +#if 0 + do *op++ = *ip++; while (--t > 0); +#else + *op++ = *ip++; + if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } } +#endif + t = *ip++; + } + } + +eof_found: + *out_len = pd(op, out); + return (ip == ip_end ? LZO_E_OK : + (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); + +#if defined(HAVE_NEED_IP) +input_overrun: + *out_len = pd(op, out); + return LZO_E_INPUT_OVERRUN; +#endif + +#if defined(HAVE_NEED_OP) +output_overrun: + *out_len = pd(op, out); + return LZO_E_OUTPUT_OVERRUN; +#endif + +#if defined(LZO_TEST_OVERRUN_LOOKBEHIND) +lookbehind_overrun: + *out_len = pd(op, out); + return LZO_E_LOOKBEHIND_OVERRUN; +#endif +} + +#endif + +/***** End of minilzo.c *****/ diff --git a/minilzo/minilzo.h b/minilzo/minilzo.h new file mode 100644 index 0000000..c1c2297 --- /dev/null +++ b/minilzo/minilzo.h @@ -0,0 +1,106 @@ +/* minilzo.h -- mini subset of the LZO real-time data compression library + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996-2017 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Markus F.X.J. Oberhumer + + http://www.oberhumer.com/opensource/lzo/ + */ + +/* + * NOTE: + * the full LZO package can be found at + * http://www.oberhumer.com/opensource/lzo/ + */ + + +#ifndef __MINILZO_H_INCLUDED +#define __MINILZO_H_INCLUDED 1 + +#define MINILZO_VERSION 0x20a0 /* 2.10 */ + +#if defined(__LZOCONF_H_INCLUDED) +# error "you cannot use both LZO and miniLZO" +#endif + +/* internal Autoconf configuration file - only used when building miniLZO */ +#ifdef MINILZO_HAVE_CONFIG_H +# include +#endif +#include +#include + +#ifndef __LZODEFS_H_INCLUDED +#include "lzodefs.h" +#endif +#undef LZO_HAVE_CONFIG_H +#include "lzoconf.h" + +#if !defined(LZO_VERSION) || (LZO_VERSION != MINILZO_VERSION) +# error "version mismatch in header files" +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +/*********************************************************************** +// +************************************************************************/ + +/* Memory required for the wrkmem parameter. + * When the required size is 0, you can also pass a NULL pointer. + */ + +#define LZO1X_MEM_COMPRESS LZO1X_1_MEM_COMPRESS +#define LZO1X_1_MEM_COMPRESS ((lzo_uint32_t) (16384L * lzo_sizeof_dict_t)) +#define LZO1X_MEM_DECOMPRESS (0) + + +/* compression */ +LZO_EXTERN(int) +lzo1x_1_compress ( const lzo_bytep src, lzo_uint src_len, + lzo_bytep dst, lzo_uintp dst_len, + lzo_voidp wrkmem ); + +/* decompression */ +LZO_EXTERN(int) +lzo1x_decompress ( const lzo_bytep src, lzo_uint src_len, + lzo_bytep dst, lzo_uintp dst_len, + lzo_voidp wrkmem /* NOT USED */ ); + +/* safe decompression with overrun testing */ +LZO_EXTERN(int) +lzo1x_decompress_safe ( const lzo_bytep src, lzo_uint src_len, + lzo_bytep dst, lzo_uintp dst_len, + lzo_voidp wrkmem /* NOT USED */ ); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* already included */ + + +/* vim:set ts=4 sw=4 et: */ diff --git a/module-anticasc.c b/module-anticasc.c new file mode 100644 index 0000000..6caba48 --- /dev/null +++ b/module-anticasc.c @@ -0,0 +1,490 @@ +#define MODULE_LOG_PREFIX "anticasc" + +//FIXME Not checked on threadsafety yet; after checking please remove this line +#include "globals.h" + +#ifdef CS_ANTICASC + +#include "module-anticasc.h" +#include "oscam-conf.h" +#include "oscam-client.h" +#include "oscam-garbage.h" +#include "oscam-string.h" +#include "oscam-time.h" + +#define cs_ac "oscam.ac" + +static FILE *ac_log; +static uint8_t ac_ecmd5[CS_ECMSTORESIZE]; + +bool anticasc_logging(char *txt) +{ + if (!ac_log) + return false; + if (strstr(txt, "acasc: ")) + { + fprintf(ac_log, "%s\n", txt); + fflush(ac_log); + return true; + } + return false; +} + +static int32_t ac_init_log(void) +{ + if(ac_log) + { return 1; } + if(!cfg.ac_logfile) + { + cs_log("ERROR: anti cascading is enabled but ac_logfile is not set."); + return 0; + } + ac_log = fopen(cfg.ac_logfile, "a+"); + if(!ac_log) + { + cs_log("ERROR: Can't open anti cascading logfile: %s (errno=%d %s)", + cfg.ac_logfile, errno, strerror(errno)); + return 0; + } + cs_log("anti cascading log initialized"); + return 1; +} + + +void ac_clear(void) +{ + struct s_client *client; + struct s_auth *account; + + for(client = first_client; client; client = client->next) + { + if(client->typ != 'c') { continue; } + memset(&client->acasc, 0, sizeof(client->acasc)); + } + + for(account = cfg.account; account; account = account->next) + { memset(&account->ac_stat, 0, sizeof(account->ac_stat)); } +} + +void ac_init_stat(void) +{ + if(!cfg.ac_enabled) + { return; } + ac_clear(); + ac_init_log(); +} + +void ac_do_stat(void) +{ + int32_t j, idx, exceeds, maxval, prev_deny = 0; + + struct s_client *client; + for(client = first_client; client; client = client->next) + { + if(client->typ != 'c') { continue; } + + struct s_acasc *ac_stat = &client->account->ac_stat; + struct s_acasc_shm *acasc = &client->acasc; + + idx = ac_stat->idx; + ac_stat->stat[idx] = acasc->ac_count; + acasc->ac_count = 0; + + if(ac_stat->stat[idx]) + { + if(client->ac_penalty == 2) // banned + { + cs_log_dbg(D_CLIENT, "acasc: user '%s' banned", client->account->usr); + acasc->ac_deny = 1; + } + else + { + for(j = exceeds = maxval = 0; j < cfg.ac_samples; j++) + { + if(ac_stat->stat[j] > maxval) + { maxval = ac_stat->stat[j]; } + exceeds += (ac_stat->stat[j] > client->ac_limit); + } + prev_deny = acasc->ac_deny; + acasc->ac_deny = (exceeds >= cfg.ac_denysamples); + + cs_log_dbg(D_CLIENT, "acasc: %s limit=%d, max=%d, samples=%d, dsamples=%d, [idx=%d]:", + client->account->usr, client->ac_limit, maxval, + cfg.ac_samples, cfg.ac_denysamples, idx); + cs_log_dbg(D_CLIENT, "acasc: %d %d %d %d %d %d %d %d %d %d ", ac_stat->stat[0], + ac_stat->stat[1], ac_stat->stat[2], ac_stat->stat[3], + ac_stat->stat[4], ac_stat->stat[5], ac_stat->stat[6], + ac_stat->stat[7], ac_stat->stat[8], ac_stat->stat[9]); + if(acasc->ac_deny) + { + cs_log("acasc: user '%s' exceeds limit", client->account->usr); + ac_stat->stat[idx] = 0; + } + else if(prev_deny) + { cs_log("acasc: user '%s' restored access", client->account->usr); } + } + } + else if(acasc->ac_deny) + { + prev_deny = 1; + acasc->ac_deny = 0; + cs_log("acasc: restored access for inactive user '%s'", client->account->usr); + } + + if(!acasc->ac_deny && !prev_deny) + { ac_stat->idx = (ac_stat->idx + 1) % cfg.ac_samples; } + } +} + +void ac_init_client(struct s_client *client, struct s_auth *account) +{ + client->ac_limit = 0; + client->ac_penalty = account->ac_penalty == -1 ? cfg.ac_penalty : account->ac_penalty; + client->ac_fakedelay = account->ac_fakedelay == -1 ? cfg.ac_fakedelay : account->ac_fakedelay; + if(cfg.ac_enabled) + { + int32_t numusers = account->ac_users; + if(numusers == -1) + { numusers = cfg.ac_users; } + + if(numusers) + { + client->ac_limit = (numusers * 100 + 80) * cfg.ac_stime; + cs_log_dbg(D_CLIENT, "acasc: user '%s', users=%d, stime=%d min, dwlimit=%d per min, penalty=%d", + account->usr, numusers, cfg.ac_stime, + numusers * 100 + 80, client->ac_penalty); + } + else + { + cs_log_dbg(D_CLIENT, "acasc: anti-cascading not used for user '%s'", account->usr); + } + } +} + +static int32_t ac_dw_weight(ECM_REQUEST *er) +{ + struct s_cpmap *cpmap; + + for(cpmap = cfg.cpmap; (cpmap) ; cpmap = cpmap->next) + if((cpmap->caid == 0 || cpmap->caid == er->caid) && + (cpmap->provid == 0 || cpmap->provid == er->prid) && + (cpmap->sid == 0 || cpmap->sid == er->srvid) && + (cpmap->chid == 0 || cpmap->chid == er->chid)) + { return (cpmap->dwtime * 100 / 60); } + + cs_log_dbg(D_CLIENT, "acasc: WARNING: CAID %04X, PROVID %06X, SID %04X, CHID %04X not found in oscam.ac", + er->caid, er->prid, er->srvid, er->chid); + cs_log_dbg(D_CLIENT, "acasc: set DW lifetime 10 sec"); + return 16; // 10*100/60 +} + +void ac_chk(struct s_client *cl, ECM_REQUEST *er, int32_t level) +{ + if(!cl->ac_limit || !cfg.ac_enabled) { return; } + + struct s_acasc_shm *acasc = &cl->acasc; + + if(level == 1) + { + if(er->rc == E_FAKE) + { acasc->ac_count++; } + + if(er->rc >= E_NOTFOUND) + { return; } // not found + + if(memcmp(ac_ecmd5, er->ecmd5, CS_ECMSTORESIZE) != 0) + { + acasc->ac_count += ac_dw_weight(er); + memcpy(ac_ecmd5, er->ecmd5, CS_ECMSTORESIZE); + } + return; + } + + if(acasc->ac_deny) + { + if(cl->ac_penalty) + { + if(cl->ac_penalty == 3) + { + if(cl->ac_fakedelay > 0) + { cs_log_dbg(D_CLIENT, "acasc: fake delay %d ms", cl->ac_fakedelay); } + } + else + { + cs_log_dbg(D_CLIENT, "acasc: send fake dw"); + er->rc = E_FAKE; // fake + er->rcEx = 0; + } + if(cl->ac_fakedelay > 0) + { cs_sleepms(cl->ac_fakedelay); } + } + } +} + +static void ac_load_config(void) +{ + FILE *fp = open_config_file(cs_ac); + if(!fp) + { return; } + + int32_t nr; + char *saveptr1 = NULL, *token; + if(!cs_malloc(&token, MAXLINESIZE)) + { return; } + struct s_cpmap *cur_cpmap, *first_cpmap = NULL, *last_cpmap = NULL; + + for(nr = 0; fgets(token, MAXLINESIZE, fp);) + { + int32_t i, skip; + uint16_t caid, sid, chid, dwtime; + uint32_t provid; + char *ptr, *ptr1; + + if(cs_strlen(token) < 4) { continue; } + + caid = sid = chid = dwtime = 0; + provid = 0; + skip = 0; + ptr1 = 0; + for(i = 0, ptr = strtok_r(token, "=", &saveptr1); (i < 2) && (ptr); ptr = strtok_r(NULL, "=", &saveptr1), i++) + { + trim(ptr); + if(*ptr == ';' || *ptr == '#' || *ptr == '-') + { + skip = 1; + break; + } + switch(i) + { + case 0: + ptr1 = ptr; + break; + case 1: + dwtime = atoi(ptr); + break; + } + } + + if(!skip) + { + for(i = 0, ptr = strtok_r(ptr1, ":", &saveptr1); (i < 4) && (ptr); ptr = strtok_r(NULL, ":", &saveptr1), i++) + { + trim(ptr); + switch(i) + { + case 0: + if(*ptr == '*') { caid = 0; } + else { caid = a2i(ptr, 4); } + break; + case 1: + if(*ptr == '*') { provid = 0; } + else { provid = a2i(ptr, 6); } + break; + case 2: + if(*ptr == '*') { sid = 0; } + else { sid = a2i(ptr, 4); } + break; + case 3: + if(*ptr == '*') { chid = 0; } + else { chid = a2i(ptr, 4); } + break; + } + } + if(!cs_malloc(&cur_cpmap, sizeof(struct s_cpmap))) + { + for(cur_cpmap = first_cpmap; cur_cpmap;) + { + last_cpmap = cur_cpmap; + cur_cpmap = cur_cpmap->next; + NULLFREE(last_cpmap); + } + NULLFREE(token); + return; + } + if(last_cpmap) + { last_cpmap->next = cur_cpmap; } + else + { first_cpmap = cur_cpmap; } + last_cpmap = cur_cpmap; + + cur_cpmap->caid = caid; + cur_cpmap->provid = provid; + cur_cpmap->sid = sid; + cur_cpmap->chid = chid; + cur_cpmap->dwtime = dwtime; + cur_cpmap->next = 0; + + cs_log_dbg(D_CLIENT, "nr=%d, caid=%04X, provid=%06X, sid=%04X, chid=%04X, dwtime=%d", + nr, caid, provid, sid, chid, dwtime); + nr++; + } + } + NULLFREE(token); + fclose(fp); + + last_cpmap = cfg.cpmap; + cfg.cpmap = first_cpmap; + for(cur_cpmap = last_cpmap; cur_cpmap; cur_cpmap = cur_cpmap->next) + { add_garbage(cur_cpmap); } + //cs_log("%d lengths for caid guessing loaded", nr); + return; +} + +void ac_copy_vars(struct s_auth *src, struct s_auth *dst) +{ + dst->ac_stat = src->ac_stat; +} + +void ac_init(void) +{ + if(!cfg.ac_enabled) + { + cs_log("anti cascading disabled"); + return; + } + + ac_load_config(); + ac_init_stat(); +} + +int8_t get_caid_weight(ECM_REQUEST *er) +{ + switch(er->caid) + { + case 0x0100: + switch (er->prid) + { + case 0x00003D: + return 20; + case 0x000065: + return 7; + default: + return 10; + } + case 0x0500: + switch(er->prid) + { + case 0x020910: + return 30; + case 0x024400: + case 0x032830: + return 10; + case 0x043800: + return 25; + default: + return 15; + } + case 0x0604: + return 11; + case 0x1702: + case 0x1722: + case 0x1833: + switch(er->srvid) + { + case 0x0022: // Disney Channel + case 0x0016: // Heimatkanal + case 0x0203: // MGM + case 0x0008: // Sky Comedy + case 0x002B: // Sky Cinema +24 + case 0x000B: // Sky Cinema +1 + case 0x0009: // Sky Action + case 0x000A: // Sky Cinema + case 0x0014: // Sky Emotion + case 0x0029: // Sky Hits + case 0x0204: // Sky Nostalgie + case 0x0011: // Sky Sport News + case 0x0024: // Syfy + return 10; + default: + return 7; + } + case 0x1830: + return 15; + case 0x1843: + case 0x1834: + case 0x09C7: + return 7; + case 0x4A70: + return 14; + case 0x183D: + return 13; + case 0x1810: + case 0x0D05: + case 0x0D95: + case 0x093B: + case 0x098C: + case 0x098D: + case 0x0B00: + default: + return 10; + + } +} + +void insert_zaplist(ECM_REQUEST *er, struct s_client *client) +{ + bool new_zaplist_entry = false; + int8_t zap_caid_weight; + zap_caid_weight = get_caid_weight(er); + int8_t k = 0; + bool found = false; + time_t zaptime = time(NULL); + + for(k=0; k<15 ; k++) + { + if(er->caid == client->client_zap_list[k].caid && er->prid == client->client_zap_list[k].provid && er->chid == client->client_zap_list[k].chid && er->srvid == client->client_zap_list[k].sid) //found + { + if(zaptime-zap_caid_weight*2 < client->client_zap_list[k].lasttime) + { + cs_log_dbg(D_TRACE, "[zaplist] update Entry [%i] for Client: %s %04X@%06X/%04X/%04X TIME: %" PRId64 " Diff: %" PRId64 " zcw: %i(%i)", k, username(client), er->caid, er->prid, er->chid, er->srvid, (int64_t)zaptime, (int64_t)(zaptime - client->client_zap_list[k].lasttime), zap_caid_weight, zap_caid_weight*2); + client->client_zap_list[k].lasttime = zaptime; + if(client->client_zap_list[k].request_stage < 10) + { + client->client_zap_list[k].request_stage ++; + } + found = true; + break; + } + } + } + + if(!found) + { + for(k=0; k<15 ; k++) + { + if(zaptime-30 > client->client_zap_list[k].lasttime) //make a new Entry and use a memoryplace of a old entry + { + client->client_zap_list[k].caid = er->caid; + client->client_zap_list[k].provid = er->prid; + client->client_zap_list[k].chid = er->chid; + client->client_zap_list[k].sid = er->srvid; + client->client_zap_list[k].request_stage = 1; //need for ACoSC + client->client_zap_list[k].lasttime = zaptime; + cs_log_dbg(D_TRACE, "[zaplist] new Entry [%i] for Client: %s %04X@%06X/%04X/%04X TIME: %" PRId64, k, username(client), er->caid, er->prid, er->chid, er->srvid, (int64_t)zaptime); + new_zaplist_entry = true; + break; + } + } + if(!new_zaplist_entry) + { cs_log_dbg(D_TRACE, "[zaplist] no free slot for client: %s", username(client)); } + + if(client->account->acosc_user_zap_count_start_time+60 > zaptime) + { client->account->acosc_user_zap_count ++; } + else + { + client->account->acosc_user_zap_count_start_time = zaptime; + client->account->acosc_user_zap_count = 0; + cs_log_dbg(D_TRACE, "[zaplist] Client: %s reset acosc_user_zap_count_start_time", username(client)); + for(k=0; k<15 ; k++) + { + if(client->client_zap_list[k].lasttime > zaptime-60) + { + client->account->acosc_user_zap_count ++; + } + } + cs_log_dbg(D_TRACE, "[zaplist] Client: %s zap_count: %i", username(client), client->account->acosc_user_zap_count); + } + } +} + +#endif diff --git a/module-anticasc.h b/module-anticasc.h new file mode 100644 index 0000000..19bc43b --- /dev/null +++ b/module-anticasc.h @@ -0,0 +1,28 @@ +#ifndef MODULE_ANTICASC_H_ +#define MODULE_ANTICASC_H_ + +#ifdef CS_ANTICASC +extern void ac_init(void); +extern void ac_init_stat(void); +extern void ac_do_stat(void); +extern void ac_clear(void); +extern void ac_copy_vars(struct s_auth *src, struct s_auth *dst); +extern void ac_init_client(struct s_client *cl, struct s_auth *account); +extern void ac_chk(struct s_client *cl, ECM_REQUEST *er, int32_t level); +extern void insert_zaplist(ECM_REQUEST *er, struct s_client *client); +static inline bool acosc_enabled(void) { return cfg.acosc_enabled; } +extern bool anticasc_logging(char *txt); +#else +static inline void ac_init(void) { } +static inline void ac_init_stat(void) { } +static inline void ac_do_stat(void) { } +static inline void ac_clear(void) { } +static inline void ac_copy_vars(struct s_auth *UNUSED(src), struct s_auth *UNUSED(dst)) { } +static inline void ac_init_client(struct s_client *UNUSED(cl), struct s_auth *UNUSED(account)) { } +static inline void ac_chk(struct s_client *UNUSED(cl), ECM_REQUEST *UNUSED(er), int32_t UNUSED(level)) { } +static inline void insert_zaplist(ECM_REQUEST *UNUSED(er), struct s_client *UNUSED(client)) { } +static inline bool acosc_enabled(void) { return 0; } +static inline bool anticasc_logging(char *UNUSED(txt)) { return 0; } +#endif + +#endif diff --git a/module-cacheex.c b/module-cacheex.c new file mode 100644 index 0000000..04e2769 --- /dev/null +++ b/module-cacheex.c @@ -0,0 +1,1559 @@ +#define MODULE_LOG_PREFIX "cacheex" + +#include "globals.h" + +#ifdef CS_CACHEEX + +#include "cscrypt/md5.h" +#include "module-cacheex.h" +#include "module-cw-cycle-check.h" +#include "oscam-cache.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-conf.h" +#include "oscam-ecm.h" +#include "oscam-hashtable.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" +#ifdef CS_CACHEEX_AIO +#include "oscam-array.h" +#endif + +#define cs_cacheex_matcher "oscam.cacheex" + +extern uint8_t cc_node_id[8]; +extern uint8_t camd35_node_id[8]; + +uint8_t cacheex_peer_id[8]; + +extern CS_MUTEX_LOCK ecm_pushed_deleted_lock; +extern struct ecm_request_t *ecm_pushed_deleted; +extern CS_MUTEX_LOCK ecmcache_lock; +extern struct ecm_request_t *ecmcwcache; + +// HIT CACHE functions ************************************************************** + +typedef struct hit_key_t { + uint16_t caid; + uint32_t prid; + uint16_t srvid; +} HIT_KEY; + +typedef struct cache_hit_t { + HIT_KEY key; + struct timeb time; + struct timeb max_hitcache_time; + uint64_t grp; + uint64_t grp_last_max_hitcache_time; +#ifdef CS_CACHEEX_AIO + int32_t waittime_block; +#endif + node ht_node; + node ll_node; +} CACHE_HIT; + +static pthread_rwlock_t hitcache_lock; +static hash_table ht_hitcache; +static list ll_hitcache; +static bool cacheex_running; + +void cacheex_init_hitcache(void) +{ + init_hash_table(&ht_hitcache, &ll_hitcache); + if (pthread_rwlock_init(&hitcache_lock,NULL) != 0) + cs_log("Error creating lock hitcache_lock!"); + cacheex_running = true; +} + +void cacheex_free_hitcache(void) +{ + cacheex_running = false; + cacheex_cleanup_hitcache(true); + deinitialize_hash_table(&ht_hitcache); + pthread_rwlock_destroy(&hitcache_lock); +} + +static int cacheex_compare_hitkey(const void *arg, const void *obj) +{ + if(((const HIT_KEY*)arg)->caid==((const CACHE_HIT*)obj)->key.caid + && ((const HIT_KEY*)arg)->prid==((const CACHE_HIT*)obj)->key.prid + && ((const HIT_KEY*)arg)->srvid==((const CACHE_HIT*)obj)->key.srvid) + { + return 0; + } + return 1; +} + +static int32_t cacheex_check_hitcache(ECM_REQUEST *er, struct s_client *cl) +{ + CACHE_HIT *result; + HIT_KEY search; + memset(&search, 0, sizeof(HIT_KEY)); + search.caid = er->caid; + search.prid = er->prid; + search.srvid = er->srvid; + SAFE_RWLOCK_RDLOCK(&hitcache_lock); + result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey); + if(result){ + struct timeb now; + cs_ftime(&now); + int64_t gone = comp_timeb(&now, &result->time); + uint64_t grp = cl?cl->grp:0; + + if( + gone <= (cfg.max_hitcache_time*1000) + && + (!grp || !result->grp || (grp & result->grp)) +#ifdef CS_CACHEEX_AIO + && + result->waittime_block <= cfg.waittime_block_start +#endif + ) + { + SAFE_RWLOCK_UNLOCK(&hitcache_lock); + return 1; + } + } + SAFE_RWLOCK_UNLOCK(&hitcache_lock); + return 0; +} + +static void cacheex_add_hitcache(struct s_client *cl, ECM_REQUEST *er) +{ + if (!cfg.max_hitcache_time) // we don't want check/save hitcache + return; + if (!cfg.cacheex_wait_timetab.cevnum) + return; + uint32_t cacheex_wait_time = get_cacheex_wait_time(er,NULL); + if (!cacheex_wait_time) + return; + + CACHE_HIT *result; + HIT_KEY search; + + memset(&search, 0, sizeof(HIT_KEY)); + search.caid = er->caid; + search.prid = er->prid; + search.srvid = er->srvid; + + SAFE_RWLOCK_WRLOCK(&hitcache_lock); + + result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey); + if(!result) // not found, add it! + { + if(cs_malloc(&result, sizeof(CACHE_HIT))) + { + memset(result, 0, sizeof(CACHE_HIT)); + result->key.caid = er->caid; + result->key.prid = er->prid; + result->key.srvid = er->srvid; + cs_ftime(&result->max_hitcache_time); +#ifdef CS_CACHEEX_AIO + result->waittime_block = 0; +#endif + add_hash_table(&ht_hitcache, &result->ht_node, &ll_hitcache, &result->ll_node, result, &result->key, sizeof(HIT_KEY)); + } + } + + if(result) + { + if(cl) + { + result->grp |= cl->grp; + result->grp_last_max_hitcache_time |= cl->grp; + } + cs_ftime(&result->time); //always update time; + } + + SAFE_RWLOCK_UNLOCK(&hitcache_lock); +} + +static void cacheex_del_hitcache(struct s_client *cl, ECM_REQUEST *er) +{ + HIT_KEY search; + CACHE_HIT *result; + + memset(&search, 0, sizeof(HIT_KEY)); + search.caid = er->caid; + search.prid = er->prid; + search.srvid = er->srvid; + + if(cl && cl->grp) + { + result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey); + while(result) + { + result->grp &= ~cl->grp; + result->grp_last_max_hitcache_time &= ~cl->grp; + result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey); + } + } + + SAFE_RWLOCK_WRLOCK(&hitcache_lock); + search_remove_elem_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey); + SAFE_RWLOCK_UNLOCK(&hitcache_lock); +} + +void cacheex_cleanup_hitcache(bool force) +{ + CACHE_HIT *cachehit; + node *i,*i_next; + struct timeb now; + int64_t gone, gone_max_hitcache_time; + int32_t timeout = (cfg.max_hitcache_time + (cfg.max_hitcache_time / 2)) * 1000; // 1,5 + int32_t clean_grp = (cfg.max_hitcache_time * 1000); + + SAFE_RWLOCK_WRLOCK(&hitcache_lock); + i = get_first_node_list(&ll_hitcache); + while (i) + { + i_next = i->next; + cachehit = get_data_from_node(i); + + if(!cachehit) + { + i = i_next; + continue; + } + + cs_ftime(&now); + gone = comp_timeb(&now, &cachehit->time); + gone_max_hitcache_time = comp_timeb(&now, &cachehit->max_hitcache_time); + + if(force || gone>timeout +#ifdef CS_CACHEEX_AIO + || (cachehit->waittime_block > (cfg.waittime_block_time / 3 + 1)) +#endif + ) + { + remove_elem_list(&ll_hitcache, &cachehit->ll_node); + remove_elem_hash_table(&ht_hitcache, &cachehit->ht_node); + NULLFREE(cachehit); + } + else if(gone_max_hitcache_time >= clean_grp){ + cachehit->grp = cachehit->grp_last_max_hitcache_time; + cachehit->grp_last_max_hitcache_time = 0; + cs_ftime(&cachehit->max_hitcache_time); + } + +#ifdef CS_CACHEEX_AIO + if(cfg.waittime_block_start && (cachehit && cachehit->waittime_block >= cfg.waittime_block_start)) + { + cachehit->waittime_block++; + } +#endif + i = i_next; + } + SAFE_RWLOCK_UNLOCK(&hitcache_lock); +} + +static int32_t cacheex_ecm_hash_calc(uint8_t *buf, int32_t n) +{ + int32_t i, h = 0; + for(i = 0; i < n; i++) + { + h = 31 * h + buf[i]; + } + return h; +} + +void cacheex_update_hash(ECM_REQUEST *er) +{ + er->csp_hash = cacheex_ecm_hash_calc(er->ecm + 3, er->ecmlen - 3); +} + +void cacheex_free_csp_lastnodes(ECM_REQUEST *er) +{ + ll_destroy_free_data(&er->csp_lastnodes); +} + +void cacheex_set_csp_lastnode(ECM_REQUEST *er) +{ + er->csp_lastnodes = NULL; +} + +void cacheex_set_cacheex_src(ECM_REQUEST *ecm, struct s_client *cl) +{ + if(ecm->cacheex_src == cl) + ecm->cacheex_src = NULL; +} + +void cacheex_init_cacheex_src(ECM_REQUEST *ecm, ECM_REQUEST *er) +{ + if(!ecm->cacheex_src) + ecm->cacheex_src = er->cacheex_src; +} + +static void *chkcache_process(void) +{ + set_thread_name(__func__); + + time_t timeout; + struct ecm_request_t *er, *ecm; + uint8_t add_hitcache_er; + struct s_reader *cl_rdr; + struct s_reader *rdr; + struct s_ecm_answer *ea; + struct s_client *cex_src = NULL; + struct s_write_from_cache *wfc = NULL; + + while(cacheex_running) + { + cs_readlock(__func__, &ecmcache_lock); + for(er = ecmcwcache; er; er = er->next) + { + timeout = time(NULL) - ((cfg.ctimeout + 500) / 1000 + 1); + if(er->tps.time < timeout) + { break; } + + if(er->rc < E_UNHANDLED || er->readers_timeout_check) // already answered + { continue; } + + // CHECK IF FOUND ECM IN CACHE + ecm = check_cache(er, er->client); + if(ecm) // found in cache + { + // check for add_hitcache + if(ecm->cacheex_src) // cw from cacheex + { + // only when no wait_time expires (or not wait_time) + if((er->cacheex_wait_time && !er->cacheex_wait_time_expired) || !er->cacheex_wait_time) + { + // add_hitcache already called, but we check if we have to call it for these (er) caid|prid|srvid + if(ecm->prid!=er->prid || ecm->srvid!=er->srvid) + { + // here we should be sure cex client has not been freed! + cex_src = ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill ? ecm->cacheex_src : NULL; + + if(cex_src) // add_hitcache only if client is really active + { + add_hitcache_er = 1; + cl_rdr = cex_src->reader; + + if(cl_rdr && cl_rdr->cacheex.mode == 2) + { + for(ea = er->matching_rdr; ea; ea = ea->next) + { + rdr = ea->reader; + if(cl_rdr == rdr && ((ea->status & REQUEST_ANSWERED) == REQUEST_ANSWERED)) + { + cs_log_dbg(D_CACHEEX | D_CSP | D_LB,"{client %s, caid %04X, prid %06X, srvid %04X} [CACHEEX] skip ADD self request!", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + + add_hitcache_er=0; // don't add hit cache, reader requested self + } + } + } + + // USE cacheex client (to get correct group) and ecm + // from requesting client (to get correct caid|prid|srvid)!!! + if(add_hitcache_er) + { + cacheex_add_hitcache(cex_src, er); + } + } + } + + } + else + { + // add_hitcache already called, but we have to remove it because cacheex not coming before wait_time + if(ecm->prid == er->prid && ecm->srvid == er->srvid) + { cacheex_del_hitcache(er->client, ecm); } + } + } + // END check for add_hitcache + + if(check_client(er->client)) + { + wfc = NULL; + if(!cs_malloc(&wfc, sizeof(struct s_write_from_cache))) + { + NULLFREE(ecm); + continue; + } + + wfc->er_new = er; + wfc->er_cache = ecm; + + if(!add_job(er->client, ACTION_ECM_ANSWER_CACHE, wfc, sizeof(struct s_write_from_cache))) // write_ecm_answer_fromcache + { + NULLFREE(ecm); + continue; + } + } + else + { + NULLFREE(ecm); + } + } + } + cs_readunlock(__func__, &ecmcache_lock); + cs_sleepms(10); + } + + return NULL; +} + +void checkcache_process_thread_start(void) +{ + start_thread("chkcache_process", (void *)&chkcache_process, NULL, NULL, 1, 1); +} + +void cacheex_init(void) +{ + // Init random node id + get_random_bytes(cacheex_peer_id, 8); +#ifdef MODULE_CCCAM + memcpy(cacheex_peer_id, cc_node_id, 8); +#endif +#ifdef MODULE_CAMD35_TCP + memcpy(camd35_node_id, cacheex_peer_id, 8); +#endif +} + +void cacheex_clear_account_stats(struct s_auth *account) +{ + account->cwcacheexgot = 0; + account->cwcacheexpush = 0; + account->cwcacheexhit = 0; +#ifdef CS_CACHEEX_AIO + account->cwcacheexgotlg = 0; + account->cwcacheexpushlg = 0; +#endif +} + +void cacheex_clear_client_stats(struct s_client *client) +{ + client->cwcacheexgot = 0; + client->cwcacheexpush = 0; + client->cwcacheexhit = 0; +#ifdef CS_CACHEEX_AIO + client->cwcacheexgotlg = 0; + client->cwcacheexpushlg = 0; +#endif +} + +int32_t cacheex_add_stats(struct s_client *cl, uint16_t caid, uint16_t srvid, uint32_t prid, uint8_t direction +#ifdef CS_CACHEEX_AIO + , uint8_t localgenerated +#endif +) +{ + if(!cfg.cacheex_enable_stats) + { return -1; } + + // create list if doesn't exist + if(!cl->ll_cacheex_stats) + { cl->ll_cacheex_stats = ll_create("ll_cacheex_stats"); } + + time_t now = time((time_t *)0); + LL_ITER itr = ll_iter_create(cl->ll_cacheex_stats); + S_CACHEEX_STAT_ENTRY *cacheex_stats_entry; + + // check for existing entry + while((cacheex_stats_entry = ll_iter_next(&itr))) + { + if(cacheex_stats_entry->cache_srvid == srvid && + cacheex_stats_entry->cache_caid == caid && + cacheex_stats_entry->cache_prid == prid && + cacheex_stats_entry->cache_direction == direction) + { + // we already have this entry - just add count and time + cacheex_stats_entry->cache_count++; +#ifdef CS_CACHEEX_AIO + if(localgenerated) + cacheex_stats_entry->cache_count_lg++; +#endif + cacheex_stats_entry->cache_last = now; + return cacheex_stats_entry->cache_count; + } + } + + // if we land here we have to add a new entry + if(cs_malloc(&cacheex_stats_entry, sizeof(S_CACHEEX_STAT_ENTRY))) + { + cacheex_stats_entry->cache_caid = caid; + cacheex_stats_entry->cache_srvid = srvid; + cacheex_stats_entry->cache_prid = prid; + cacheex_stats_entry->cache_count = 1; +#ifdef CS_CACHEEX_AIO + if(localgenerated) + cacheex_stats_entry->cache_count_lg = 1; +#endif + cacheex_stats_entry->cache_last = now; + cacheex_stats_entry->cache_direction = direction; + ll_iter_insert(&itr, cacheex_stats_entry); + return 1; + } + return 0; +} + +int8_t cacheex_maxhop(struct s_client *cl) +{ + int maxhop = 10; + if(cl->reader && cl->reader->cacheex.maxhop) + { maxhop = cl->reader->cacheex.maxhop; } + else if(cl->account && cl->account->cacheex.maxhop) + { maxhop = cl->account->cacheex.maxhop; } + return maxhop; +} + +#ifdef CS_CACHEEX_AIO +int8_t cacheex_maxhop_lg(struct s_client *cl) +{ + int max = 10; + int maxhop = cacheex_maxhop(cl); + int maxhop_lg = maxhop; + + if(cl->reader && cl->reader->cacheex.maxhop_lg) + { + if(cl->reader->cacheex.maxhop_lg > max) + { + cl->reader->cacheex.maxhop_lg = max; + } + + if(cl->reader->cacheex.maxhop_lg < maxhop) + { + maxhop_lg = maxhop; + } + else + { + maxhop_lg = cl->reader->cacheex.maxhop_lg; + } + + cl->reader->cacheex.maxhop_lg = maxhop_lg; + } + else if(cl->account && cl->account->cacheex.maxhop_lg) + { + if(cl->account->cacheex.maxhop_lg > max) + { + cl->account->cacheex.maxhop_lg = max; + } + + if(cl->account->cacheex.maxhop_lg < maxhop) + { + maxhop_lg = maxhop; + } + else + { + maxhop_lg = cl->account->cacheex.maxhop_lg; + } + + cl->account->cacheex.maxhop_lg = maxhop_lg; + } + return maxhop_lg; +} +#endif + +static void cacheex_cache_push_to_client(struct s_client *cl, ECM_REQUEST *er) +{ + add_job(cl, ACTION_CACHE_PUSH_OUT, er, 0); +} + +/** + * Check for NULL ecmd5 + **/ +static uint8_t checkECMD5(ECM_REQUEST *er) +{ + int8_t i; + for(i = 0; i < CS_ECMSTORESIZE; i++) + if(er->ecmd5[i]) { return 1; } + return 0; +} + +#ifdef CS_CACHEEX_AIO +static uint8_t chk_cwcheck(ECM_REQUEST *er, uint8_t cw_check_for_push) +{ + if(!cw_check_for_push) + return 1; + + CWCHECK check_cw; + check_cw = get_cwcheck(er); + + if(check_cw.mode && check_cw.counter > 1) + { + if(er->cw_count >= check_cw.counter) + { + return 1; + } + else + { + cs_log_dbg(D_CACHEEX, "push denied - cacheex_check_cw.counter: %u > er->cw_count: %u", check_cw.counter, er->cw_count); + return 0; + } + } + else + { + return 1; + } +} +#endif + +/** + * cacheex modes: + * + * cacheex=1 CACHE PULL: + * Situation: oscam A reader1 has cacheex=1, oscam B account1 has cacheex=1 + * oscam A gets a ECM request, reader1 send this request to oscam B, oscam B checks his cache + * a. not found in cache: return NOK + * a. found in cache: return OK+CW + * b. not found in cache, but found pending request: wait max cacheexwaittime and check again + * oscam B never requests new ECMs + * + * CW-flow: B->A + * + * cacheex=2 CACHE PUSH: + * Situation: oscam A reader1 has cacheex=2, oscam B account1 has cacheex=2 + * if oscam B gets a CW, its pushed to oscam A + * reader has normal functionality and can request ECMs + * + * Problem: oscam B can only push if oscam A is connected + * Problem or feature?: oscam A reader can request ecms from oscam B + * + * CW-flow: B->A + * + */ +void cacheex_cache_push(ECM_REQUEST *er) +{ + if(er->rc >= E_NOTFOUND) { return; } + + //cacheex=2 mode: push (server->remote) + struct s_client *cl; + cs_readlock(__func__, &clientlist_lock); + for(cl = first_client->next; cl; cl = cl->next) + { + if(check_client(cl) && er->cacheex_src != cl) + { + if(get_module(cl)->num == R_CSP) // always send to csp cl + { + if(!er->cacheex_src || cfg.csp.allow_reforward) { cacheex_cache_push_to_client(cl, er); } // but not if the origin was cacheex (might loop) + } + else if(cl->typ == 'c' && !cl->dup && cl->account && cl->account->cacheex.mode == 2) // send cache over user + { + if(get_module(cl)->c_cache_push // cache-push able + && (!er->grp || (cl->grp & er->grp) +#ifdef CS_CACHEEX_AIO + || (er->localgenerated && ((cl->grp & cfg.cacheex_push_lg_groups) && strcmp(username(cl), username(er->cacheex_src)))) +#endif + ) // Group-check + /**** OUTGOING FILTER CHECK ***/ + && (!er->selected_reader || !cacheex_reader(er->selected_reader) || !cfg.block_same_name || strcmp(username(cl), er->selected_reader->label)) // check reader mode-1 loopback by same name + && (!er->selected_reader || !cacheex_reader(er->selected_reader) || !cfg.block_same_ip || (check_client(er->selected_reader->client) && !IP_EQUAL(cl->ip, er->selected_reader->client->ip))) // check reader mode-1 loopback by same ip + && (!cl->account->cacheex.drop_csp || checkECMD5(er)) // cacheex_drop_csp-check + && chk_ctab(er->caid, &cl->ctab) // Caid-check + && (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &cl->ftab)) // Ident-check (not for csp: prid=0 always!) + && chk_srvid(cl, er) // Service-check + && chk_csp_ctab(er, &cl->account->cacheex.filter_caidtab) // cacheex_ecm_filter +#ifdef CS_CACHEEX_AIO + && (er->localgenerated // lg-flag-check + || chk_srvid_localgenerated_only_exception(er) // lg-only-service-exception + || !(cl->account->cacheex.localgenerated_only // usr-lg-only + || ( + (cl->account->cacheex.feature_bitfield & 64) // cx-aio >= 9.2.6 => check ftab + && (chk_lg_only(er, &cl->account->cacheex.lg_only_tab) // usr-lg-only-ftab (feature 64) + || chk_lg_only(er, &cfg.cacheex_lg_only_tab)) // global-lg-only-ftab (feature 64) + ) + ) + ) + && (chk_cwcheck(er, cl->account->cacheex.cw_check_for_push)) // check cw_check-counter if enabled + && chk_nopushafter(er->caid, &cl->account->cacheex.cacheex_nopushafter_tab, er->ecm_time) // no push after check +#endif + ) + { + cacheex_cache_push_to_client(cl, er); + } + } + } + } + cs_readunlock(__func__, &clientlist_lock); + + //cacheex=3 mode: reverse push (reader->server) + cs_readlock(__func__, &readerlist_lock); + cs_readlock(__func__, &clientlist_lock); + struct s_reader *rdr; + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + cl = rdr->client; + if(check_client(cl) && er->cacheex_src != cl && rdr->cacheex.mode == 3) // send cache over reader + { + if(rdr->ph.c_cache_push // cache-push able + && (!er->grp || (rdr->grp & er->grp) +#ifdef CS_CACHEEX_AIO + || (er->localgenerated && ((rdr->grp & cfg.cacheex_push_lg_groups) && strcmp(username(cl), username(er->cacheex_src)))) +#endif + ) // Group-check + /**** OUTGOING FILTER CHECK ***/ + && (!er->selected_reader || !cacheex_reader(er->selected_reader) || !cfg.block_same_name || strcmp(username(cl), er->selected_reader->label)) // check reader mode-1 loopback by same name + && (!er->selected_reader || !cacheex_reader(er->selected_reader) || !cfg.block_same_ip || (check_client(er->selected_reader->client) && !IP_EQUAL(cl->ip, er->selected_reader->client->ip))) // check reader mode-1 loopback by same ip + && (!rdr->cacheex.drop_csp || checkECMD5(er)) // cacheex_drop_csp-check + && chk_ctab(er->caid, &rdr->ctab) // Caid-check + && (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &rdr->ftab)) // Ident-check (not for csp: prid=0 always!) + && chk_srvid(cl, er) // Service-check + && chk_csp_ctab(er, &rdr->cacheex.filter_caidtab) // cacheex_ecm_filter +#ifdef CS_CACHEEX_AIO + && (er->localgenerated // lg-only-check + || chk_srvid_localgenerated_only_exception(er) // service-exception + || !(rdr->cacheex.localgenerated_only // rdr-lg-only + || ( + (rdr->cacheex.feature_bitfield & 64) // cx-aio >= 9.2.6 => check ftab + && (chk_lg_only(er, &rdr->cacheex.lg_only_tab) // rdr-lg-only-ftab (feature 64) + || chk_lg_only(er, &cfg.cacheex_lg_only_tab)) // global-lg-only-ftab (feature 64) + ) + ) + ) + && (chk_cwcheck(er, rdr->cacheex.cw_check_for_push)) // check cw_check-counter if enabled + && chk_nopushafter(er->caid, &rdr->cacheex.cacheex_nopushafter_tab, er->ecm_time) +#endif + ) // no push after check + { + cacheex_cache_push_to_client(cl, er); + } + } + } + cs_readunlock(__func__, &clientlist_lock); + cs_readunlock(__func__, &readerlist_lock); +} + +/**** INCOMING FILTER CHECK ***/ +uint8_t check_cacheex_filter(struct s_client *cl, ECM_REQUEST *er) +{ + + if(check_client(cl) && cl->typ == 'p' && cl->reader && cl->reader->cacheex.mode == 2 + && (!cl->reader->cacheex.drop_csp || checkECMD5(er)) // cacheex_drop_csp-check + && chk_ctab(er->caid, &cl->reader->ctab) // Caid-check + && (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &cl->reader->ftab)) // Ident-check (not for csp: prid=0 always!) + && chk_srvid(cl, er)) // Service-check + { + return 1; + } + + if(check_client(cl) && cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3 + && (!cl->account->cacheex.drop_csp || checkECMD5(er)) // cacheex_drop_csp-check + && chk_ctab(er->caid, &cl->ctab) // Caid-check + && (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &cl->ftab)) // Ident-check (not for csp: prid=0 always!) + && chk_srvid(cl, er)) // Service-check + { + return 1; + } + free_ecm(er); + return 0; +} + +static struct s_cacheex_matcher *is_cacheex_matcher_matching(ECM_REQUEST *from_er, ECM_REQUEST *to_er) +{ + struct s_cacheex_matcher *entry = cfg.cacheex_matcher; + int8_t v_ok = (from_er && to_er) ? 2 : 1; + while(entry) + { + int8_t ok = 0; + if(from_er + && (!entry->caid || entry->caid == from_er->caid) + && (!entry->provid || entry->provid == from_er->prid) + && (!entry->srvid || entry->srvid == from_er->srvid) + && (!entry->chid || entry->chid == from_er->chid) + && (!entry->pid || entry->pid == from_er->pid) + && (!entry->ecmlen || entry->ecmlen == from_er->ecmlen)) + { ok++; } + + if(to_er + && (!entry->to_caid || entry->to_caid == to_er->caid) + && (!entry->to_provid || entry->to_provid == to_er->prid) + && (!entry->to_srvid || entry->to_srvid == to_er->srvid) + && (!entry->to_chid || entry->to_chid == to_er->chid) + && (!entry->to_pid || entry->to_pid == to_er->pid) + && (!entry->to_ecmlen || entry->to_ecmlen == to_er->ecmlen)) + { ok++; } + + if(ok == v_ok) + { + if(!from_er || !to_er || from_er->srvid == to_er->srvid) + { return entry; } + } + entry = entry->next; + } + return NULL; +} + +bool cacheex_is_match_alias(struct s_client *cl, ECM_REQUEST *er) +{ + return check_client(cl) && cl->account && cl->account->cacheex.mode == 1 && is_cacheex_matcher_matching(NULL, er); +} + +#ifdef WITH_DEBUG +static void log_cacheex_cw(ECM_REQUEST *er, char *reason) +{ + uint8_t *data; + uint8_t remotenodeid[8]; + data = ll_last_element(er->csp_lastnodes); + if(data) + { memcpy(remotenodeid, data, 8); } + else + { memset(remotenodeid, 0 , 8); } + + char buf_ecm[109]; + format_ecm(er, buf_ecm, 109); + cs_log_dbg(D_CACHEEX,"got pushed ecm [%s]: %s - odd/even 0x%x - CSP cw: %s - pushed from %s, at hop %d, origin node-id %" PRIu64 "X", + reason, buf_ecm, er->ecm[0], (checkECMD5(er)?"NO":"YES"), er->from_csp ? "csp" : username((er->cacheex_src?er->cacheex_src:er->client)), ll_count(er->csp_lastnodes), er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0); +} +#endif + +// check if sky_ger 64 bit CW has valid checksum bytes and therefore is probably invalid +uint8_t check_nds_cwex(ECM_REQUEST *er) +{ + uint8_t k, csum; + uint8_t hit = 0; + uint8_t oe = checkCWpart(er->cw, 0) ? 0 : 8; + for(k = 0; k < 8; k += 4) + { + csum = ((er->cw[k + oe] + er->cw[k + oe + 1] + er->cw[k + oe + 2]) & 0xff); + if(er->cw[k + oe + 3] == csum) + { + hit++; + } + } + if(hit > 1) + { + return 1; + } + return 0; +} + +static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, int8_t csp) +{ + if(er->rc >= E_NOTFOUND) { return 0; } + + if(!cl) + { return 0; } + if(!csp && cl->reader && cl->reader->cacheex.mode != 2) //from reader + { + cs_log_dbg(D_CACHEEX, "CACHEX received, but disabled for %s", username(cl)); + return 0; + } + if(!csp && !cl->reader && cl->account && cl->account->cacheex.mode != 3) //from user + { + cs_log_dbg(D_CACHEEX, "CACHEX received, but disabled for %s", username(cl)); + return 0; + } + if(!csp && !cl->reader && !cl->account) //not active! + { + cs_log_dbg(D_CACHEEX, "CACHEX received, but invalid client state %s", username(cl)); + return 0; + } + + if(!cfg.disablecrccws && ((cl->typ == 'c' && cl->account && !cl->account->disablecrccacheex) || ( cl->typ == 'p' && cl->reader && !cl->reader->disablecrccws))) + { + uint8_t selectedForIgnChecksum = chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for); + if(cl->typ == 'c') + { + selectedForIgnChecksum += chk_if_ignore_checksum(er, &cl->account->disablecrccacheex_only_for); + } + if(cl->typ == 'p') + { + selectedForIgnChecksum += chk_if_ignore_checksum(er, &cl->reader->disablecrccws_only_for); + } + if(!selectedForIgnChecksum) + { + uint8_t i, c; + for(i = 0; i < 16; i += 4) + { + c = ((er->cw[i] + er->cw[i + 1] + er->cw[i + 2]) & 0xff); + + if(er->cw[i + 3] != c) + { + cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received cw with chksum error from %s", csp ? "csp" : username(cl)); + cl->cwcacheexerr++; + if(cl->account) + { cl->account->cwcacheexerr++; } + return 0; + } + } + } + } + +#ifdef CS_CACHEEX_AIO + if(caid_is_videoguard(er->caid)) + { + if(cl->typ == 'p' && chk_if_ignore_checksum(er, &cl->reader->disablecrccws_only_for) && !chk_srvid_disablecrccws_only_for_exception(er)) + { + if(check_nds_cwex(er)) + { + if(check_client(cl) && cl->reader && cl->reader->dropbadcws) + { + if (((D_CACHEEX) & cs_dblevel)) // avoid useless operations if debug is not enabled + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + + cs_log_dbg(D_CACHEEX, "Probably got pushed bad CW from cacheex reader: %s, caid %04X, srvid %04X - dropping CW, lg: %i, hop: %i, src-nodeid %" PRIu64 "X", cl->reader->label, er->caid, er->srvid, er->localgenerated, ll_count(er->csp_lastnodes), er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0); + } + return 0; + } + else + { + if (((D_CACHEEX) & cs_dblevel)) // avoid useless operations if debug is not enabled + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + + cs_log_dbg(D_CACHEEX, "Probably got pushed bad CW from cacheex reader: %s, caid %04X, srvid %04X, lg: %i, hop: %i, src-nodeid %" PRIu64 "X", cl->reader->label, er->caid, er->srvid, er->localgenerated, ll_count(er->csp_lastnodes), er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0); + } + } + } + } + + if(cl->typ == 'c' && chk_if_ignore_checksum(er, &cl->account->disablecrccacheex_only_for) && !chk_srvid_disablecrccws_only_for_exception(er)) + { + if(check_nds_cwex(er)) + { + if (((D_CACHEEX) & cs_dblevel)) // avoid useless operations if debug is not enabled + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + + cs_log_dbg(D_CACHEEX, "Probably got bad CW from cacheex user: %s, caid %04X, srvid %04X, lg: %i, hop: %i, src-nodeid %" PRIu64 "X", username(cl), er->caid, er->srvid, er->localgenerated, ll_count(er->csp_lastnodes), er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0); + } + } + } + } +#endif + + // Skip check for BISS1 - cw could be indeed zero + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + if(chk_is_null_CW(er->cw) && !caid_is_biss(er->caid)) + { + cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received null cw from %s", csp ? "csp" : username(cl)); + cl->cwcacheexerr++; + if(cl->account) + { cl->account->cwcacheexerr++; } + return 0; + } + + // Don't check for BISS1 and BISS2 mode 1/E or fake caid (ECM is fake for them) + // Don't check for BISS2 mode CA (ECM table is always 0x80) + if(!caid_is_biss(er->caid) && !caid_is_fake(er->caid) && get_odd_even(er) == 0) + { + cs_log_dbg(D_CACHEEX, "push received ecm with null odd/even byte from %s", csp ? "csp" : username(cl)); + cl->cwcacheexerr++; + if(cl->account) + { cl->account->cwcacheexerr++; } + return 0; + } + + if(!chk_halfCW(er, er->cw)) + { +#ifdef WITH_DEBUG + if(cs_dblevel & D_CACHEEX) + { + log_cacheex_cw(er, "bad half cw"); + } +#endif + cl->cwcacheexerr++; + if(cl->account) + { cl->account->cwcacheexerr++; } + return 0; + } + + if((csp && cfg.csp.block_fakecws) || (cl->reader && cl->reader->cacheex.block_fakecws) + || (!cl->reader && cl->account && cl->account->cacheex.block_fakecws)) + { + if(chk_is_fakecw(er->cw)) + { + cs_log_dbg(D_CACHEEX, "push received fake cw from %s", csp ? "csp" : username(cl)); + cl->cwcacheexerr++; + if(cl->account) + { cl->account->cwcacheexerr++; } + return 0; + } + } + + er->grp |= cl->grp; // ok for mode2 reader too: cl->reader->grp + er->rc = E_CACHEEX; + er->cacheex_src = cl; + er->selected_reader = cl->reader; + er->client = NULL; // No Owner! So no fallback! + + if(check_client(cl)) + { + cl->cwcacheexgot++; + if(cl->account) + { cl->account->cwcacheexgot++; } + first_client->cwcacheexgot++; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + cl->cwcacheexgotlg++; + if(cl->account) + cl->account->cwcacheexgotlg++; + first_client->cwcacheexgotlg++; + } +#endif + } + + cacheex_add_hitcache(cl, er); // we have to call it before add_cache, because in chk_process we could remove it! + add_cache(er); +#ifdef CS_CACHEEX_AIO + cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 1, er->localgenerated); +#else + cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 1); +#endif + + cs_writelock(__func__, &ecm_pushed_deleted_lock); + er->next = ecm_pushed_deleted; + ecm_pushed_deleted = er; + cs_writeunlock(__func__, &ecm_pushed_deleted_lock); + + return 1; // NO free, we have to wait cache push out stuff ends. +} + +void cacheex_add_to_cache(struct s_client *cl, ECM_REQUEST *er) +{ + er->from_cacheex = 1; + if(!cacheex_add_to_cache_int(cl, er, 0)) + { free_push_in_ecm(er); } +} + +void cacheex_add_to_cache_from_csp(struct s_client *cl, ECM_REQUEST *er) +{ + if(!cacheex_add_to_cache_int(cl, er, 1)) + { free_push_in_ecm(er); } +} + +//Format: +//caid:prov:srvid:pid:chid:ecmlen=caid:prov:srvid:pid:chid:ecmlen[,validfrom,validto] +//validfrom: default=-2000 +//validto: default=4000 +//valid time if found in cache +static struct s_cacheex_matcher *cacheex_matcher_read_int(void) +{ + FILE *fp = open_config_file(cs_cacheex_matcher); + if(!fp) + { return NULL; } + + char token[1024]; + uint8_t type; + int32_t i, ret, count = 0; + struct s_cacheex_matcher *new_cacheex_matcher = NULL, *entry, *last = NULL; + uint32_t line = 0; + + while(fgets(token, sizeof(token), fp)) + { + line++; + if(cs_strlen(token) <= 1) { continue; } + if(token[0] == '#' || token[0] == '/') { continue; } + if(cs_strlen(token) > 100) { continue; } + + for(i = 0; i < (int)cs_strlen(token); i++) + { + if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':') + { + memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1); + token[i + 1] = '0'; + } + if(token[i] == '#' || token[i] == '/') + { + token[i] = '\0'; + break; + } + } + + type = 'm'; + uint32_t caid = 0, provid = 0, srvid = 0, pid = 0, chid = 0, ecmlen = 0; + uint32_t to_caid = 0, to_provid = 0, to_srvid = 0, to_pid = 0, to_chid = 0, to_ecmlen = 0; + int32_t valid_from = -2000, valid_to = 4000; + + ret = sscanf(token, "%c:%4x:%6x:%4x:%4x:%4x:%4X=%4x:%6x:%4x:%4x:%4x:%4X,%4d,%4d", + &type, + &caid, &provid, &srvid, &pid, &chid, &ecmlen, + &to_caid, &to_provid, &to_srvid, &to_pid, &to_chid, &to_ecmlen, + &valid_from, &valid_to); + + type = tolower(type); + + if(ret < 7 || type != 'm') + { continue; } + + if(!cs_malloc(&entry, sizeof(struct s_cacheex_matcher))) + { + fclose(fp); + return new_cacheex_matcher; + } + count++; + entry->line = line; + entry->type = type; + entry->caid = caid; + entry->provid = provid; + entry->srvid = srvid; + entry->pid = pid; + entry->chid = chid; + entry->ecmlen = ecmlen; + entry->to_caid = to_caid; + entry->to_provid = to_provid; + entry->to_srvid = to_srvid; + entry->to_pid = to_pid; + entry->to_chid = to_chid; + entry->to_ecmlen = to_ecmlen; + entry->valid_from = valid_from; + entry->valid_to = valid_to; + + cs_log_dbg(D_TRACE, "cacheex-matcher: %c: %04X@%06X:%04X:%04X:%04X:%02X = %04X@%06X:%04X:%04X:%04X:%02X valid %d/%d", + entry->type, entry->caid, entry->provid, entry->srvid, entry->pid, entry->chid, entry->ecmlen, + entry->to_caid, entry->to_provid, entry->to_srvid, entry->to_pid, entry->to_chid, entry->to_ecmlen, + entry->valid_from, entry->valid_to); + + if(!new_cacheex_matcher) + { + new_cacheex_matcher = entry; + last = new_cacheex_matcher; + } + else + { + last->next = entry; + last = entry; + } + } + + if(count) + { cs_log("%d entries read from %s", count, cs_cacheex_matcher); } + + fclose(fp); + + return new_cacheex_matcher; +} + +void cacheex_load_config_file(void) +{ + struct s_cacheex_matcher *entry, *old_list; + + old_list = cfg.cacheex_matcher; + cfg.cacheex_matcher = cacheex_matcher_read_int(); + + while(old_list) + { + entry = old_list->next; + NULLFREE(old_list); + old_list = entry; + } +} + +CWCHECK get_cwcheck(ECM_REQUEST *er) +{ + int32_t i; + int8_t mode = 0; + int16_t counter = 1; + + for(i = 0; i < cfg.cacheex_cwcheck_tab.cwchecknum; i++) + { + CWCHECKTAB_DATA *d = &cfg.cacheex_cwcheck_tab.cwcheckdata[i]; + + if(i == 0 && d->caid <= 0) + { + mode = d->mode; + counter = d->counter; + continue; //check other, only valid for unset + } + + if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1)) + { + if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1) + { + if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1) + { + mode = d->mode; + counter = d->counter; + break; + } + } + + } + } + + //check for correct values + if(mode>2 || mode<0) mode=0; + if(counter<1) counter=1; + + CWCHECK check_cw; + memset(&check_cw, 0, sizeof(CWCHECK)); + check_cw.mode = mode; + check_cw.counter = counter; + + return check_cw; +} + +uint16_t get_cacheex_mode1_delay(ECM_REQUEST *er) +{ + return caidvaluetab_get_value(&cfg.cacheex_mode1_delay_tab, er->caid, 0); +} + +uint32_t get_cacheex_wait_time(ECM_REQUEST *er, struct s_client *cl) +{ + int32_t i, dwtime = -1, awtime = -1; + + for(i = 0; i < cfg.cacheex_wait_timetab.cevnum; i++) + { + CECSPVALUETAB_DATA *d = &cfg.cacheex_wait_timetab.cevdata[i]; + + if(i == 0 && d->caid <= 0) + { + dwtime = d->dwtime; + awtime = d->awtime; + continue; //check other, only valid for unset + } + + if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1)) + { + if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1) + { + if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1) + { + dwtime = d->dwtime; + awtime = d->awtime; + break; + } + } + + } + + } + if(awtime > 0 && (dwtime <= 0 || awtime==dwtime) ) //if awtime==dwtime useless check hitcache + { + return awtime; + } + if(cl == NULL) + { + if(dwtime < 0) + { dwtime = 0; } + return dwtime; + } + if(awtime > 0 || dwtime > 0) + { + //if found last in cache return dynwaittime else alwayswaittime + if(cacheex_check_hitcache(er,cl)) + { return dwtime >= awtime ? dwtime : awtime; } + else + { return awtime > 0 ? awtime : 0; } + } + return 0; +} + + +int32_t chk_csp_ctab(ECM_REQUEST *er, CECSPVALUETAB *tab) +{ + if(!er->caid || !tab->cevnum) + { return 1; } // nothing setup we add all + int32_t i; + for(i = 0; i < tab->cevnum; i++) + { + CECSPVALUETAB_DATA *d = &tab->cevdata[i]; + if(d->caid > 0) + { + if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1)) + { + if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1) + { + if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1) + { + return 1; + } + } + } + } + } + return 0; +} + +void cacheex_push_out(struct s_client *cl, ECM_REQUEST *er) +{ + int32_t res = 0, stats = -1; + struct s_reader *reader = cl->reader; + struct s_module *module = get_module(cl); + + // cc-nodeid-list-check + if(reader) + { + if(reader->ph.c_cache_push_chk && !reader->ph.c_cache_push_chk(cl, er)) + return; + res = reader->ph.c_cache_push(cl, er); +#ifdef CS_CACHEEX_AIO + stats = cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 0, er->localgenerated); +#else + stats = cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 0); +#endif + } + else + { + if(module->c_cache_push_chk && !module->c_cache_push_chk(cl, er)) + return; + res = module->c_cache_push(cl, er); + } + debug_ecm(D_CACHEEX, "pushed ECM %s to %s res %d stats %d", buf, username(cl), res, stats); + cl->cwcacheexpush++; + if(cl->account) + { cl->account->cwcacheexpush++; } + first_client->cwcacheexpush++; + +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + cl->cwcacheexpushlg++; + if(cl->account) + cl->account->cwcacheexpushlg++; + first_client->cwcacheexpushlg++; + } +#endif +} + +bool cacheex_check_queue_length(struct s_client *cl) +{ + // Avoid full running queues: + if(ll_count(cl->joblist) <= 2000) + return 0; + + cs_log_dbg(D_TRACE, "WARNING: job queue %s %s has more than 2000 jobs! count=%d, dropped!", + cl->typ == 'c' ? "client" : "reader", username(cl), ll_count(cl->joblist)); + + // Thread down??? + SAFE_MUTEX_LOCK(&cl->thread_lock); + if(cl && !cl->kill && cl->thread && cl->thread_active) + { + // Just test for invalid thread id: + if(pthread_detach(cl->thread) == ESRCH) + { + cl->thread_active = 0; + cs_log_dbg(D_TRACE, "WARNING: %s %s thread died!", cl->typ == 'c' ? "client" : "reader", username(cl)); + } + } + SAFE_MUTEX_UNLOCK(&cl->thread_lock); + return 1; +} + +void cacheex_mode1_delay(ECM_REQUEST *er) +{ + if(!er->cacheex_wait_time_expired && er->cacheex_mode1_delay + && er->cacheex_reader_count > 0 && !er->stage && er->rc >= E_UNHANDLED) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} cacheex_mode1_delay timeout! ", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + + // setting stop_stage=1, we request only cacheex mode 1 readers. Others are requested at cacheex timeout! + request_cw_from_readers(er, 1); + } +} + +void cacheex_timeout(ECM_REQUEST *er) +{ + if(er->cacheex_wait_time_expired) + return; + er->cacheex_wait_time_expired = 1; + if(er->rc >= E_UNHANDLED) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} cacheex timeout! ", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + +#ifdef CS_CACHEEX_AIO + CACHE_HIT *result; + HIT_KEY search; + + memset(&search, 0, sizeof(HIT_KEY)); + search.caid = er->caid; + search.prid = er->prid; + search.srvid = er->srvid; + + SAFE_RWLOCK_WRLOCK(&hitcache_lock); + + result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey); + if(result) + { + if(cfg.waittime_block_start && (result->waittime_block <= cfg.waittime_block_start)) + { + result->waittime_block++; + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} waittime_block count: %u ", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, result->waittime_block); + } + } + + SAFE_RWLOCK_UNLOCK(&hitcache_lock); +#endif + // if check_cw mode=0, first try to get cw from cache without check counter! + CWCHECK check_cw = get_cwcheck(er); + if(!check_cw.mode) + { + struct ecm_request_t *ecm = NULL; + ecm = check_cache(er, er->client); + + if(ecm) // found in cache + { + struct s_write_from_cache *wfc = NULL; + if(!cs_malloc(&wfc, sizeof(struct s_write_from_cache))) + { + NULLFREE(ecm); + return; + } + wfc->er_new = er; + wfc->er_cache = ecm; + if(!add_job(er->client, ACTION_ECM_ANSWER_CACHE, wfc, sizeof(struct s_write_from_cache))) // write_ecm_answer_fromcache + { NULLFREE(ecm); } + return; + } + } + + // check if "normal" readers selected, if not send NOT FOUND! + // cacheex1-client (having always no "normal" reader), + // or not-cacheex-1 client with no normal readers available (or filtered by LB) + if((er->reader_count + er->fallback_reader_count - er->cacheex_reader_count) <= 0) + { + if(!cfg.wait_until_ctimeout) + { + er->rc = E_NOTFOUND; + er->selected_reader = NULL; + er->rcEx = 0; + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} cacheex timeout: NO \"normal\" readers... not_found! ", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + + send_dcw(er->client, er); + return; + } + } + else + { + if(er->stage < 2) + { + debug_ecm(D_TRACE, "request for %s %s", username(er->client), buf); + request_cw_from_readers(er, 0); + } + } + } +} + +#ifdef CS_CACHEEX_AIO +char* cxaio_ftab_to_buf(FTAB *lg_only_ftab) +{ + int32_t i, k, l = 0, strncat_sz = 0; + char *ret; + char caid[5]; + char provid[7]; + char nprids[3]; + + // get size of return-val + for(i = 0; i < lg_only_ftab->nfilts; i++) + { + l += 4; // caid + l += 2; // nprid-counter + l += 6 * lg_only_ftab->filts[i].nprids; // prid/s + + if(!lg_only_ftab->filts[i].nprids) + { + l += 6; + } + } + + if(!cs_malloc(&ret, l * sizeof(char) + sizeof(char))) { + return ""; + } + + strncat_sz += l * sizeof(char) + sizeof(char); + + for(i = 0; i < lg_only_ftab->nfilts; i++) + { + snprintf(caid, 5, "%04X", lg_only_ftab->filts[i].caid); + if (!cs_strncat(ret, caid, strncat_sz)) { + cs_log("FIXME!"); + } + + if(!lg_only_ftab->filts[i].nprids) + { + if (!cs_strncat(ret, "01", strncat_sz)) { + cs_log("FIXME2!"); + } + snprintf(provid, 7, "000000"); + if (!cs_strncat(ret, provid, strncat_sz)) { + cs_log("FIXME3!"); + } + } + else + { + snprintf(nprids, 3, "%02X", lg_only_ftab->filts[i].nprids); + if (!cs_strncat(ret, nprids, strncat_sz)) { + cs_log("FIXME4!"); + } + } + + for(k = 0; k < lg_only_ftab->filts[i].nprids; k++) + { + snprintf(provid, 7, "%06X", lg_only_ftab->filts[i].prids[k]); + if (!cs_strncat(ret, provid, strncat_sz)) { + cs_log("FIXME5!"); + } + } + } + return ret; +} + +FTAB caidtab2ftab(CAIDTAB *ctab) +{ + int i; + FTAB ftab; + memset(&ftab, 0, sizeof(ftab)); + + for(i=0; ictnum; i++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + d.caid = ctab->ctdata[i].caid; + d.prids[d.nprids] = NO_PROVID_VALUE; + d.nprids++; + ftab_add(&ftab, &d); + } + return ftab; +} + +void caidtab2ftab_add(CAIDTAB *lgonly_ctab, FTAB *lgonly_tab) +{ + int j, k, l, rc; + for(j = 0; j < lgonly_ctab->ctnum; j++) + { + CAIDTAB_DATA *d = &lgonly_ctab->ctdata[j]; + if(d->caid) + { + rc = 0; + if(lgonly_tab->nfilts) + { + for(k = 0; (k < lgonly_tab->nfilts); k++) + { + if(lgonly_tab->filts[k].caid != 0 && lgonly_tab->filts[k].caid == d->caid) + { + for(l = 0; (l < lgonly_tab->filts[k].nprids); l++) + { + if(lgonly_tab->filts[k].prids[l] == NO_PROVID_VALUE) + { + rc = 1; + break; + } + } + if(!rc) + { + lgonly_tab->filts[k].nprids = 1; + lgonly_tab->filts[k].prids[0] = NO_PROVID_VALUE; + rc = 1; + } + break; + } + } + } + if(!rc) // caid not found + { + FILTER df; + memset(&df, 0, sizeof(df)); + df.caid = d->caid; + df.prids[0] = NO_PROVID_VALUE; + df.nprids++; + ftab_add(lgonly_tab, &df); + } + } + } +} +#endif +#endif diff --git a/module-cacheex.h b/module-cacheex.h new file mode 100644 index 0000000..1d6b80c --- /dev/null +++ b/module-cacheex.h @@ -0,0 +1,78 @@ +#ifndef MODULE_CACHEEX_H_ +#define MODULE_CACHEEX_H_ + +static inline uint64_t cacheex_node_id(void *var) +{ + uint64_t *x = var; + return *x; +} + +uint32_t get_cacheex_wait_time(ECM_REQUEST *er, struct s_client *cl); +CWCHECK get_cwcheck(ECM_REQUEST *er); +uint16_t get_cacheex_mode1_delay(ECM_REQUEST *er); +int32_t chk_csp_ctab(ECM_REQUEST *er, CECSPVALUETAB *tab); +uint8_t check_cacheex_filter(struct s_client *cl, ECM_REQUEST *er); +void cacheex_add_to_cache(struct s_client *cl, ECM_REQUEST *er); +void cacheex_add_to_cache_from_csp(struct s_client *cl, ECM_REQUEST *er); +void cacheex_cache_push(ECM_REQUEST *er); +int32_t cacheex_add_stats(struct s_client *cl, uint16_t caid, uint16_t srvid, uint32_t prid, uint8_t direction +#ifdef CS_CACHEEX_AIO + , uint8_t localgenerated +#endif +); +int8_t cacheex_maxhop(struct s_client *cl); +#ifdef CS_CACHEEX_AIO +int8_t cacheex_maxhop_lg(struct s_client *cl); +#endif + +#ifdef CS_CACHEEX +extern void cacheex_init(void); +extern void cacheex_clear_account_stats(struct s_auth *account); +extern void cacheex_clear_client_stats(struct s_client *client); +extern void cacheex_load_config_file(void); +static inline bool cacheex_reader(struct s_reader *rdr) { return rdr ? (rdr->cacheex.mode == 1 ? 1 : 0) : 0; } +extern bool cacheex_is_match_alias(struct s_client *cl, ECM_REQUEST *er); +void cacheex_set_csp_lastnode(ECM_REQUEST *er); +void cacheex_set_cacheex_src(ECM_REQUEST *ecm, struct s_client *cl); +void cacheex_init_cacheex_src(ECM_REQUEST *ecm, ECM_REQUEST *er); +void cacheex_free_csp_lastnodes(ECM_REQUEST *er); +void checkcache_process_thread_start(void); +void cacheex_push_out(struct s_client *cl, ECM_REQUEST *er); +bool cacheex_check_queue_length(struct s_client *cl); +static inline int8_t cacheex_get_rdr_mode(struct s_reader *reader) { return reader ? reader->cacheex.mode : 0; } +void cacheex_init_hitcache(void); +void cacheex_free_hitcache(void); +void cacheex_cleanup_hitcache(bool force); +void cacheex_update_hash(ECM_REQUEST *er); +void cacheex_mode1_delay(ECM_REQUEST *er); +void cacheex_timeout(ECM_REQUEST *er); +#ifdef CS_CACHEEX_AIO +char* cxaio_ftab_to_buf(FTAB *lg_only_ftab); +FTAB caidtab2ftab(CAIDTAB *ctab); +void caidtab2ftab_add(CAIDTAB *lgonly_ctab, FTAB *lgonly_tab); +#define CACHEEX_FEATURES 127 +#endif +#else +static inline void cacheex_init(void) { } +static inline void cacheex_clear_account_stats(struct s_auth *UNUSED(account)) { } +static inline void cacheex_clear_client_stats(struct s_client *UNUSED(client)) { } +static inline void cacheex_load_config_file(void) { } +static inline bool cacheex_reader(struct s_reader *UNUSED(rdr)) { return false; } +static inline bool cacheex_is_match_alias(struct s_client *UNUSED(cl), ECM_REQUEST *UNUSED(er)) { return false; } +static inline void cacheex_set_csp_lastnode(ECM_REQUEST *UNUSED(er)) { } +static inline void cacheex_free_csp_lastnodes(ECM_REQUEST *UNUSED(er)) { } +static inline void cacheex_set_cacheex_src(ECM_REQUEST *UNUSED(ecm), struct s_client *UNUSED(cl)) { } +static inline void cacheex_init_cacheex_src(ECM_REQUEST *UNUSED(ecm), ECM_REQUEST *UNUSED(er)) { } +static inline void checkcache_process_thread_start(void) { } +static inline void cacheex_push_out(struct s_client *UNUSED(cl), ECM_REQUEST *UNUSED(er)) { } +static inline bool cacheex_check_queue_length(struct s_client *UNUSED(cl)) { return 0; } +static inline int8_t cacheex_get_rdr_mode(struct s_reader *UNUSED(reader)) { return 0; } +static inline void cacheex_init_hitcache(void) { } +static inline void cacheex_free_hitcache(void) { } +static inline void cacheex_cleanup_hitcache(bool UNUSED(force)) { } +static inline void cacheex_update_hash(ECM_REQUEST *UNUSED(er)) { } +static inline void cacheex_mode1_delay(ECM_REQUEST *UNUSED(er)) { } +static inline void cacheex_timeout(ECM_REQUEST *UNUSED(er)) { } +#endif + +#endif diff --git a/module-camd33.c b/module-camd33.c new file mode 100644 index 0000000..48b0b4e --- /dev/null +++ b/module-camd33.c @@ -0,0 +1,262 @@ +#define MODULE_LOG_PREFIX "camd33" + +#include "globals.h" +#ifdef MODULE_CAMD33 +#include "oscam-aes.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-emm.h" +#include "oscam-net.h" +#include "oscam-string.h" + +#define REQ_SIZE 4 + +static int32_t camd33_send(uint8_t *buf, int32_t ml) +{ + int32_t l; + + if(!cur_client()->pfd) + { + return (-1); + } + + l = boundary(4, ml); + memset(buf + ml, 0, l - ml); + cs_log_dump_dbg(D_CLIENT, buf, l, "send %d bytes to client", l); + + if(cur_client()->crypted) + { + aes_encrypt_idx(cur_client()->aes_keys, buf, l); + } + + return (send(cur_client()->pfd, buf, l, 0)); +} + +static int32_t camd33_recv(struct s_client *client, uint8_t *buf, int32_t l) +{ + int32_t n; + + if(!client->pfd) + { + return (-1); + } + + if((n = cs_recv(client->pfd, buf, l, 0)) > 0) + { + client->last = time((time_t *)0); + if(client->crypted) + { + aes_encrypt_idx(cur_client()->aes_keys, buf, n); + } + } + cs_log_dump_dbg(D_CLIENT, buf, n, "received %d bytes from client", n); + + return (n); +} + +static void camd33_request_emm(void) +{ + uint8_t mbuf[20]; + struct s_reader *aureader = NULL, *rdr = NULL; + + // TODO: just take the first reader in list + LL_ITER itr = ll_iter_create(cur_client()->aureader_list); + while((rdr = ll_iter_next(&itr))) + { + aureader = rdr; + break; + } + + if(!aureader) + { + return; + } + + if(aureader->hexserial[0]) + { + cs_log("%s emm-request sent (reader=%s, caid=%04X, auprovid=%06X)", + username(cur_client()), aureader->label, aureader->caid, + aureader->auprovid ? aureader->auprovid : b2i(4, aureader->prid[0])); + + mbuf[0] = 0; + mbuf[1] = aureader->caid >> 8; + mbuf[2] = aureader->caid & 0xff; + + memcpy(mbuf + 3, aureader->hexserial, 4); + memcpy(mbuf + 7, &aureader->prid[0][1], 3); + memcpy(mbuf + 10, &aureader->prid[2][1], 3); + camd33_send(mbuf, 13); + } +} + +static void camd33_auth_client(uint8_t *camdbug) +{ + int32_t i, rc; + uint8_t *usr = NULL, *pwd = NULL; + struct s_auth *account; + uint8_t mbuf[1024]; + struct s_client *cl = cur_client(); + + cl->crypted = cfg.c33_crypted; + if(cl->crypted) + { + cl->crypted = !check_ip(cfg.c33_plain, cl->ip); + } + + if(cl->crypted) + { + if (!aes_set_key_alloc(&cl->aes_keys, (char *)cfg.c33_key)) + { + cs_disconnect_client(cl); + return; + } + } + + mbuf[0] = 0; + camd33_send(mbuf, 1); // send login-request + + for(rc = 0, camdbug[0] = 0, mbuf[0] = 1; (rc < 2) && (mbuf[0]); rc++) + { + i = process_input(mbuf, sizeof(mbuf), 1); + if((i > 0) && (!mbuf[0])) + { + usr = mbuf + 1; + pwd = usr + cs_strlen((char *)usr) + 2; + } + else + { + memcpy(camdbug + 1, mbuf, camdbug[0] = i); + } + } + + for(rc = -1, account = cfg.account; (usr) && (account) && (rc < 0); account = account->next) + { + if(streq((char *)usr, account->usr) && streq((char *)pwd, account->pwd)) + { + rc = cs_auth_client(cl, account, NULL); + } + } + + if(!rc) + { + camd33_request_emm(); + } + else + { + if(rc < 0) + { + cs_auth_client(cl, 0, usr ? "invalid account" : "no user given"); + } + cs_disconnect_client(cl); + } +} + +static void camd33_send_dcw(struct s_client *UNUSED(client), ECM_REQUEST *er) +{ + uint8_t mbuf[128]; + + mbuf[0] = 2; + memcpy(mbuf + 1, &er->msgid, 4); // get pin + memcpy(mbuf + 5, er->cw, 16); + camd33_send(mbuf, 21); + + if(!cfg.c33_passive) + { + camd33_request_emm(); + } +} + +static void camd33_process_ecm(uint8_t *buf, int32_t l) +{ + ECM_REQUEST *er; + + if(l < 7) + { + return; + } + + if(!(er = get_ecmtask())) + { + return; + } + + memcpy(&er->msgid, buf + 3, 4); // save pin + er->ecmlen = l - 7; + + if(er->ecmlen < 0 || er->ecmlen > MAX_ECM_SIZE) + { + NULLFREE(er); + return; + } + + er->caid = b2i(2, buf + 1); + memcpy(er->ecm , buf + 7, er->ecmlen); + get_cw(cur_client(), er); +} + +static void camd33_process_emm(uint8_t *buf, int32_t l) +{ + EMM_PACKET epg; + + if(l < 7) + { + return; + } + + memset(&epg, 0, sizeof(epg)); + epg.emmlen = l - 7; + + if(epg.emmlen < 3 || epg.emmlen > MAX_EMM_SIZE) + { + return; + } + + memcpy(epg.caid, buf + 1, 2); + memcpy(epg.hexserial, buf + 3, 4); + memcpy(epg.emm, buf + 7, epg.emmlen); + do_emm(cur_client(), &epg); +} + +static void *camd33_server(struct s_client *UNUSED(client), uint8_t *mbuf, int32_t n) +{ + switch(mbuf[0]) + { + case 2: + camd33_process_ecm(mbuf, n); + break; + + case 3: + camd33_process_emm(mbuf, n); + break; + + default: + cs_log_dbg(D_CLIENT, "unknown command!"); + } + + return NULL; +} + +static void camd33_server_init(struct s_client *UNUSED(client)) +{ + uint8_t camdbug[256]; + + camd33_auth_client(camdbug); +} + +void module_camd33(struct s_module *ph) +{ + cfg.c33_crypted = array_has_nonzero_byte(cfg.c33_key, sizeof(cfg.c33_key)); + ph->ptab.nports = 1; + ph->ptab.ports[0].s_port = cfg.c33_port; + ph->desc = "camd33"; + ph->type = MOD_CONN_TCP; + ph->large_ecm_support = 1; + ph->listenertype = LIS_CAMD33TCP; + IP_ASSIGN(ph->s_ip, cfg.c33_srvip); + ph->s_handler = camd33_server; + ph->s_init = camd33_server_init; + ph->recv = camd33_recv; + ph->send_dcw = camd33_send_dcw; + ph->num = R_CAMD33; +} +#endif diff --git a/module-camd35-cacheex.c b/module-camd35-cacheex.c new file mode 100644 index 0000000..8a85b94 --- /dev/null +++ b/module-camd35-cacheex.c @@ -0,0 +1,1771 @@ +#define MODULE_LOG_PREFIX "camd35" + +#include "globals.h" +#include "oscam-array.h" + +#if defined(CS_CACHEEX) && (defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP)) + +#include "module-cacheex.h" +#include "module-camd35.h" +#include "module-camd35-cacheex.h" +#include "oscam-cache.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-string.h" +#include "oscam-reader.h" +#ifdef CS_CACHEEX_AIO +#include "oscam-chk.h" +#include "oscam-config.h" +#endif + +uint8_t camd35_node_id[8]; + +#define CSP_HASH_SWAP(n) (((((uint32_t)(n) & 0xFF)) << 24) | \ + ((((uint32_t)(n) & 0xFF00)) << 8) | \ + ((((uint32_t)(n) & 0xFF0000)) >> 8) | \ + ((((uint32_t)(n) & 0xFF000000)) >> 24)) + +#ifdef CS_CACHEEX_AIO +void camd35_cacheex_feature_trigger_in(struct s_client *cl, uint8_t *buf) +{ + int32_t feature = 0; + uint16_t i = 20; + uint8_t filter_count; + uint8_t j, k, l, rc; + feature = buf[21] | (buf[20] << 8); + FTAB *lgonly_tab; + + // check client & cacheex-mode + if( + !check_client(cl) || + !( + (cl->typ == 'c' && cl->account->cacheex.mode > 0) || + (cl->typ == 'p' && cl->reader->cacheex.mode > 0) + ) + ) + { + return; + } + + switch(feature) + { + // set localgenerated only + case 1: + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + if(cfg.cacheex_lg_only_remote_settings || cl->account->cacheex.lg_only_remote_settings) + cl->account->cacheex.localgenerated_only = buf[24]; + else if(buf[24]) + cl->account->cacheex.localgenerated_only = buf[24]; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + if(cfg.cacheex_lg_only_remote_settings || cl->reader->cacheex.lg_only_remote_settings) + cl->reader->cacheex.localgenerated_only = buf[24]; + else if(buf[24]) + cl->reader->cacheex.localgenerated_only = buf[24]; + } + break; + // set localgenerated only caidtab + case 2: + filter_count = buf[i+4]; + i += 5; + + memset(&lgonly_tab, 0, sizeof(lgonly_tab)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + lgonly_tab = &cl->account->cacheex.lg_only_tab; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + lgonly_tab = &cl->reader->cacheex.lg_only_tab; + } + else + { + return; + } + + // remotesettings enabled - replace local settings + if(cfg.cacheex_lg_only_remote_settings || + ( + (cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.lg_only_remote_settings) + || (cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.lg_only_remote_settings) + ) + ) + { + ftab_clear(lgonly_tab); + + for(j = 0; j < filter_count; j++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + + d.caid = b2i(2, buf + i); + i += 2; + + d.nprids = 1; + d.prids[0] = NO_PROVID_VALUE; + + ftab_add(lgonly_tab, &d); + } + } + // remotesettings disabled - write additional remote-caids received + else + { + for(j = 0; j < filter_count; j++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + + d.caid = b2i(2, buf + i); + i += 2; + + d.nprids = 1; + d.prids[0] = NO_PROVID_VALUE; + + if(!chk_lg_only_cp(d.caid, d.prids[0], lgonly_tab)) + { + cs_log_dbg(D_CACHEEX, "%04X:%06X not found in local settings - adding them", d.caid, d.prids[0]); + + for(l = rc = 0; (!rc) && (l < lgonly_tab->nfilts); l++) + { + if(lgonly_tab->filts[l].caid == d.caid) + { + rc = 1; + + if(lgonly_tab->filts[l].nprids+1 <= CS_MAXPROV) + { + lgonly_tab->filts[l].prids[lgonly_tab->filts[l].nprids] = d.prids[0]; + lgonly_tab->filts[l].nprids++; + } + else + { + cs_log_dbg(D_CACHEEX, "error: cacheex_lg_only_tab -> max. number(%i) of providers reached", CS_MAXPROV); + } + } + } + if(!rc) + { + ftab_add(lgonly_tab, &d); + } + } + } + } + break; + // set cacheex_ecm_filter - extended + case 4: + filter_count = buf[i+4]; + i += 5; + + CECSPVALUETAB *filter; + memset(&filter, 0, sizeof(filter)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.allow_filter) + { + filter = &cl->account->cacheex.filter_caidtab; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.allow_filter) + { + filter = &cl->reader->cacheex.filter_caidtab; + } + else + { + return; + } + + cecspvaluetab_clear(filter); + + for(j = 0; j < filter_count; j++) + { + int32_t caid = -1, cmask = -1, provid = -1, srvid = -1; + CECSPVALUETAB_DATA d; + memset(&d, 0, sizeof(d)); + + caid = b2i(2, buf + i); + if(caid == 0xFFFF) caid = -1; + i += 2; + + cmask = b2i(2, buf + i); + if(cmask == 0xFFFF) cmask = -1; + i += 2; + + provid = b2i(3, buf + i); + if(provid == 0xFFFFFF) provid = -1; + i += 3; + + srvid = b2i(2, buf + i); + if(srvid == 0xFFFF) srvid = -1; + i += 2; + + if(caid > 0) + { + d.caid = caid; + d.cmask = cmask; + d.prid = provid; + d.srvid = srvid; + cecspvaluetab_add(filter, &d); + } + } + break; + // no push after + case 8: ; + CAIDVALUETAB *ctab; + memset(&ctab, 0, sizeof(ctab)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + ctab = &cl->account->cacheex.cacheex_nopushafter_tab; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + ctab = &cl->reader->cacheex.cacheex_nopushafter_tab; + } + else + { + return; + } + + filter_count = buf[i+4]; + i += 5; + + caidvaluetab_clear(ctab); + + for(j = 0; j < filter_count; j++) + { + uint16_t caid = 0, value = 0; + CAIDVALUETAB_DATA d; + memset(&d, 0, sizeof(d)); + + caid = b2i(2, buf + i); + if(caid == 0xFFFF) caid = -1; + i += 2; + + value = b2i(2, buf + i); + if(value == 0xFFFF) value = -1; + i += 2; + + if(caid > 0) + { + d.caid = caid; + d.value = value; + caidvaluetab_add(ctab, &d); + } + } + break; + // max hop + case 16: + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.allow_maxhop) + { + cl->account->cacheex.maxhop = buf[24]; + cl->account->cacheex.maxhop_lg = buf[25]; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.allow_maxhop) + { + cl->reader->cacheex.maxhop = buf[24]; + cl->reader->cacheex.maxhop_lg = buf[25]; + } + break; + // aio-version + case 32: ; + uint16_t payload_size = b2i(2, buf + i + 2); + if(cl->typ == 'c' && cl->account->cacheex.mode > 0) + { + char *ofs = (char *)buf + i + 4; + memset(cl->account->cacheex.aio_version, 0, sizeof(cl->account->cacheex.aio_version)); + if(payload_size > 0) + { + size_t str_len = strnlen(ofs, payload_size); + if(str_len >= sizeof(cl->account->cacheex.aio_version)) + str_len = sizeof(cl->account->cacheex.aio_version) - 1; + memcpy(cl->account->cacheex.aio_version, ofs, str_len); + // sanitize: remove non-printable characters + size_t x; + for(x = 0; x < str_len; x++) + { + if(cl->account->cacheex.aio_version[x] < 0x20 || cl->account->cacheex.aio_version[x] > 0x7E) + { + cl->account->cacheex.aio_version[x] = '\0'; + break; + } + } + } + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode > 0) + { + char *ofs = (char *)buf + i + 4; + memset(cl->reader->cacheex.aio_version, 0, sizeof(cl->reader->cacheex.aio_version)); + if(payload_size > 0) + { + size_t str_len = strnlen(ofs, payload_size); + if(str_len >= sizeof(cl->reader->cacheex.aio_version)) + str_len = sizeof(cl->reader->cacheex.aio_version) - 1; + memcpy(cl->reader->cacheex.aio_version, ofs, str_len); + // sanitize: remove non-printable characters + size_t x; + for(x = 0; x < str_len; x++) + { + if(cl->reader->cacheex.aio_version[x] < 0x20 || cl->reader->cacheex.aio_version[x] > 0x7E) + { + cl->reader->cacheex.aio_version[x] = '\0'; + break; + } + } + } + } + break; + // lg_only_tab caid:prov1[,provN][;caid:prov] + case 64: ; + memset(&lgonly_tab, 0, sizeof(lgonly_tab)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + lgonly_tab = &cl->account->cacheex.lg_only_tab; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + lgonly_tab = &cl->reader->cacheex.lg_only_tab; + } + else + { + return; + } + + filter_count = buf[i+4]; + i += 5; + + // remotesettings enabled - replace local settings + if(cfg.cacheex_lg_only_remote_settings || + ( + (cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.lg_only_remote_settings) + || (cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.lg_only_remote_settings) + || !lgonly_tab->nfilts + ) + ) + { + ftab_clear(lgonly_tab); + + for(j = 0; j < filter_count; j++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + + d.caid = b2i(2, buf + i); + i += 2; + + d.nprids = b2i(1, buf + i); + i += 1; + + for(k=0; k < d.nprids; k++) + { + d.prids[k] = b2i(3, buf + i); + i += 3; + } + ftab_add(lgonly_tab, &d); + + } + } + // remotesettings disabled - write additional remote-caid/provids received + else + { + for(j = 0; j < filter_count; j++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + + d.caid = b2i(2, buf + i); + i += 2; + + d.nprids = b2i(1, buf + i); + i += 1; + + for(k=0; k < d.nprids; k++) + { + d.prids[k] = b2i(3, buf + i); + i += 3; + + if(!chk_ident_filter(d.caid, d.prids[k], lgonly_tab)) + { + cs_log_dbg(D_CACHEEX, "%04X:%06X not found in local settings - adding them", d.caid, d.prids[k]); + + for(l = rc = 0; (!rc) && (l < lgonly_tab->nfilts); l++) + { + if(lgonly_tab->filts[l].caid == d.caid) + { + rc = 1; + + if(lgonly_tab->filts[l].nprids+1 <= CS_MAXPROV) + { + lgonly_tab->filts[l].prids[lgonly_tab->filts[l].nprids] = d.prids[k]; + lgonly_tab->filts[l].nprids++; + } + else + { + cs_log_dbg(D_CACHEEX, "error: cacheex_lg_only_tab -> max. number of providers reached"); + } + } + } + if(!rc) + { + ftab_add(lgonly_tab, &d); + } + } + } + } + } + break; + default: + return; + } +} + +void camd35_cacheex_feature_trigger(struct s_client *cl, int32_t feature, uint8_t mode) +{ + // size: 20 + (feature-bitfield & mask: 2) + payload-size: 2 + feature-payload :x + uint16_t size = 20 + 2 + 2; + int i = 0; + uint8_t j; + uint8_t payload[MAX_ECM_SIZE-size]; + memset(payload, 0, sizeof(payload)); + + // check client & cacheex-mode + if(!check_client(cl)) + { + return; + } + + switch(feature) + { + FTAB *lgonly_tab; + // set localgenerated only + case 1: + size += 1; + if(size < 32) + size = 32; + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + i2b_buf(2, 1, payload + i); + i += 2; + // set payload + if(mode == 2) + { + if(cl->reader->cacheex.localgenerated_only_in) + payload[i] = cl->reader->cacheex.localgenerated_only_in; + else + payload[i] = cfg.cacheex_localgenerated_only_in; + } + else if(mode == 3) + { + if(cl->account->cacheex.localgenerated_only_in) + payload[i] = cl->account->cacheex.localgenerated_only_in; + else + payload[i] = cfg.cacheex_localgenerated_only_in; + } + + break; + // set localgenerated only caidtab; cx-aio < 9.2.6-04 + case 2: ; + if(mode == 2) + { + lgonly_tab = &cl->reader->cacheex.lg_only_in_tab; + if(!lgonly_tab->nfilts) + lgonly_tab = &cfg.cacheex_lg_only_in_tab; + } + else if(mode == 3) + { + lgonly_tab = &cl->account->cacheex.lg_only_in_tab; + if(!lgonly_tab->nfilts) + lgonly_tab = &cfg.cacheex_lg_only_in_tab; + } + else + { + return; + } + + size += (lgonly_tab->nfilts * 2 + 1); + if(size < 32) + size = 32; + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + if((lgonly_tab->nfilts * 2 + 1) > (int)sizeof(payload)) + { + cs_log_dbg(D_CACHEEX, "ERROR: too much localgenerated only caidtab-entries (max. 255)"); + return; + } + i2b_buf(2, (lgonly_tab->nfilts * 2 + 1), payload + i); // n * caid + ctnum + i += 2; + // set payload + if(lgonly_tab->nfilts > 255) + { + cs_log_dbg(D_CACHEEX, "ERROR: too much localgenerated only caidtab-entries (max. 255)"); + return; + } + payload[i] = lgonly_tab->nfilts; + i += 1; + + for(j = 0; j < lgonly_tab->nfilts; j++) + { + FILTER *d = &lgonly_tab->filts[j]; + if(d->caid) + { + i2b_buf(2, d->caid, payload + i); + i += 2; + } + else + { + continue; + } + } + break; + // cacchex_ecm_filter extendend + case 4: ; + CECSPVALUETAB *filter; + if(mode == 2) + { + filter = &cl->reader->cacheex.filter_caidtab; + // if not set, use global settings + if(cl->reader->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) + filter = &cfg.cacheex_filter_caidtab; + // if aio, use global aio settings + if(cl->reader->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && cl->cacheex_aio_checked && (cl->reader->cacheex.feature_bitfield & 4)) + filter = &cfg.cacheex_filter_caidtab_aio; + } + else if(mode == 3) + { + + filter = &cl->account->cacheex.filter_caidtab; + // if not set, use global settings + if(cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) + filter = &cfg.cacheex_filter_caidtab; + if(cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && cl->cacheex_aio_checked && (cl->account->cacheex.feature_bitfield & 4)) + filter = &cfg.cacheex_filter_caidtab_aio; + } + else + { + return; + } + + size += (filter->cevnum * 9 + 1); + if(size < 32) + size = 32; + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + if((filter->cevnum * 9 + 1) > (int)sizeof(payload)) + { + cs_log_dbg(D_CACHEEX, "ERROR: to much cacheex_ecm_filter-entries (max. 63), only 15 default camd3-filters sent"); + return; + } + i2b_buf(2, (filter->cevnum * 9 + 1), payload + i); // n * (caid2,mask2,provid3,srvid2) + ctnum1 + i += 2; + // set payload + payload[i] = filter->cevnum; + i += 1; + + for(j = 0; j < filter->cevnum; j++) + { + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + if(d->caid) + { + i2b_buf(2, d->caid, payload + i); + i += 2; + } + if(d->cmask) + { + i2b_buf(2, d->cmask, payload + i); + } + i += 2; + + if(d->prid) + { + i2b_buf(3, d->prid, payload + i); + } + i += 3; + + if(d->srvid) + { + i2b_buf(2, d->srvid, payload + i); + } + i += 2; + } + + camd35_cacheex_send_push_filter(cl, 2); + break; + // no push after + case 8: ; + CAIDVALUETAB *ctab; + if(mode == 2) + { + ctab = &cl->reader->cacheex.cacheex_nopushafter_tab; + if(!ctab->cvnum) + ctab = &cfg.cacheex_nopushafter_tab; + } + else if(mode == 3) + { + ctab = &cl->account->cacheex.cacheex_nopushafter_tab; + if(!ctab->cvnum) + ctab = &cfg.cacheex_nopushafter_tab; + } + else + { + return; + } + + size += (ctab->cvnum * 4 + 1); + if(size < 32) + size = 32; + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + if((ctab->cvnum * 4 + 1) > (int)sizeof(payload)) + { + cs_log_dbg(D_CACHEEX, "ERROR: to much no push after caidtvalueab-entries (max. 255)"); + return; + } + i2b_buf(2, (ctab->cvnum * 4 + 1), payload + i); // n * (caid2+value2) + cvnum + i += 2; + // set payload + if(ctab->cvnum > 255) + { + cs_log_dbg(D_CACHEEX, "ERROR: to much no push after caidtvalueab-entries (max. 255)"); + return; + } + payload[i] = ctab->cvnum; + i += 1; + + for(j = 0; j < ctab->cvnum; j++) + { + CAIDVALUETAB_DATA *d = &ctab->cvdata[j]; + if(d->caid) + { + i2b_buf(2, d->caid, payload + i); + i += 2; + i2b_buf(2, d->value, payload + i); + i += 2; + } + else + { + continue; + } + } + break; + // maxhop + case 16: + size += 2; + if(size < 32) + size = 32; + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + i2b_buf(2, 2, payload + i); + i += 2; + // set payload + if(mode == 2) + { + if(cl->reader->cacheex.maxhop) + payload[i] = cl->reader->cacheex.maxhop; + else + payload[i] = 0; + i += 1; + + if(cl->reader->cacheex.maxhop_lg) + payload[i] = cl->reader->cacheex.maxhop_lg; + else + payload[i] = 0; + } + else if(mode == 3) + { + if(cl->account->cacheex.maxhop) + payload[i] = cl->account->cacheex.maxhop; + else + payload[i] = 0; + i += 1; + + if(cl->account->cacheex.maxhop_lg) + payload[i] = cl->account->cacheex.maxhop_lg; + else + payload[i] = 0; + } + break; + // aio-version + case 32: ; + size += CS_AIO_VERSION_LEN; + if(size < 32) + size = 32; + + uint8_t token[CS_AIO_VERSION_LEN]; + memset(token, 0, sizeof(token)); + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + i2b_buf(2, sizeof(token), payload + i); + i += 2; + // set payload + + snprintf((char *)token, sizeof(token), "%s", CS_AIO_VERSION); + uint8_t *ofs = payload + i; + memcpy(ofs, token, sizeof(token)); + break; + // lg_only_tab + case 64: ; + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + + if(mode == 2) + { + lgonly_tab = &cl->reader->cacheex.lg_only_in_tab; + if(!lgonly_tab->nfilts) + lgonly_tab = &cfg.cacheex_lg_only_in_tab; + } + else if(mode == 3) + { + lgonly_tab = &cl->account->cacheex.lg_only_in_tab; + if(!lgonly_tab->nfilts) + lgonly_tab = &cfg.cacheex_lg_only_in_tab; + } + else + { + return; + } + + char *cx_aio_ftab; + cx_aio_ftab = cxaio_ftab_to_buf(lgonly_tab); + if(cs_strlen(cx_aio_ftab) > 0 && cx_aio_ftab[0] != '\0') + { + size += cs_strlen(cx_aio_ftab) * sizeof(char); + + // payload-size + i2b_buf(2, cs_strlen(cx_aio_ftab), payload + i); + i += 2; + + // filter counter + payload[i] = lgonly_tab->nfilts; + i += 1; + + for(j=0; j> 8; + + uint8_t *ofs = buf + 20; + memcpy(ofs, payload, size - 20); + + camd35_send_without_timeout(cl, buf, size-20); // send adds +20 +} + +void camd35_cacheex_feature_request_save(struct s_client *cl, uint8_t *buf) +{ + int32_t field = b2i(2, (buf+20)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + cl->account->cacheex.feature_bitfield = field; + // flag 32 => aio-version + if(cl->account->cacheex.feature_bitfield & 32) + { + camd35_cacheex_feature_trigger(cl, 32, 2); + } + } + + if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + cl->reader->cacheex.feature_bitfield = field; + // flag 32 => aio-version + if(cl->reader->cacheex.feature_bitfield & 32) + { + camd35_cacheex_feature_trigger(cl, 32, 3); + } + } + + if(cl->typ == 'c' && cl->account->cacheex.mode == 3) + { + struct s_auth *acc = cl->account; + if(acc) + { + acc->cacheex.feature_bitfield = field; + // process feature-specific actions based on feature_bitfield received + + // flag 1 => set localgenerated only flag + if(acc->cacheex.feature_bitfield & 1) + { + camd35_cacheex_feature_trigger(cl, 1, 3); + } + // flag 2 => set localgenerated only caids flag + if(acc->cacheex.feature_bitfield & 2 && !(acc->cacheex.feature_bitfield & 64)) + { + camd35_cacheex_feature_trigger(cl, 2, 3); + } + // flag 4 => set cacheex_ecm_filter (extended) + if(acc->cacheex.feature_bitfield & 4) + { + camd35_cacheex_feature_trigger(cl, 4, 3); + } + // flag 8 => np push after caids + if(acc->cacheex.feature_bitfield & 8) + { + camd35_cacheex_feature_trigger(cl, 8, 3); + } + // flag 16 => maxhop + if(acc->cacheex.feature_bitfield & 16) + { + camd35_cacheex_feature_trigger(cl, 16, 3); + } + // flag 32 => aio-version + if(acc->cacheex.feature_bitfield & 32) + { + camd35_cacheex_feature_trigger(cl, 32, 3); + } + // flag 64 => lg_only_tab + if(acc->cacheex.feature_bitfield & 64) + { + camd35_cacheex_feature_trigger(cl, 64, 3); + } + } + else + { + cs_log_dbg(D_CACHEEX, "feature_bitfield save failed - cl, %s", username(cl)); + } + } + else if(cl->typ == 'p' && (cl->reader->cacheex.mode == 2 || cl->reader->cacheex.mode == 1)) + { + struct s_reader *rdr = cl->reader; + if(rdr) + { + rdr->cacheex.feature_bitfield = field; + // process feature-specific actions + + // flag 1 => set localgenerated_only; cause of rdr->cacheex.localgenerated_only_in is set + if(rdr->cacheex.feature_bitfield & 1) + { + camd35_cacheex_feature_trigger(cl, 1, 2); + } + + // flag 2 => set lg_only_tab; cause of rdr->cacheex.lg_only_in_tab is set + if(rdr->cacheex.feature_bitfield & 2 && !(rdr->cacheex.feature_bitfield & 64)) + { + camd35_cacheex_feature_trigger(cl, 2, 2); + } + + // flag 4 => set cacheex_ecm_filter (extended) + if(rdr->cacheex.feature_bitfield & 4) + { + camd35_cacheex_feature_trigger(cl, 4, 2); + } + + // flag 8 => no push after caids + if(rdr->cacheex.feature_bitfield & 8) + { + camd35_cacheex_feature_trigger(cl, 8, 2); + } + // flag 16 => maxhop + if(rdr->cacheex.feature_bitfield & 16) + { + camd35_cacheex_feature_trigger(cl, 16, 2); + } + // flag 32 => aio-version + if(rdr->cacheex.feature_bitfield & 32) + { + camd35_cacheex_feature_trigger(cl, 32, 2); + } + // flag 64 => lg_only_tab + if(rdr->cacheex.feature_bitfield & 64) + { + camd35_cacheex_feature_trigger(cl, 64, 2); + } + } + else + { + cs_log_dbg(D_CACHEEX, "feature_bitfield save failed - rdr, %s", username(cl)); + } + } +} + +void camd35_cacheex_feature_request(struct s_client *cl) +{ + int i = 20; + + uint8_t buf[32]; + memset(buf, 0, sizeof(buf)); + buf[0] = 0x40; + buf[1] = 12; + buf[2] = 0; + + i2b_buf(2, CACHEEX_FEATURES, buf + i); // set feature-list here + + camd35_send_without_timeout(cl, buf, 12); // send adds +20 +} + +void camd35_cacheex_feature_request_reply(struct s_client *cl, uint8_t *buf) +{ + camd35_cacheex_feature_request_save(cl, buf); + int i = 20; + + uint8_t rbuf[32]; + memset(rbuf, 0, sizeof(rbuf)); + rbuf[0] = 0x41; + rbuf[1] = 12; + rbuf[2] = 0; + + i2b_buf(2, CACHEEX_FEATURES, rbuf + i); + + camd35_send_without_timeout(cl, rbuf, 12); // send adds +20 +} +#endif + + +/** + * send push filter + */ +void camd35_cacheex_send_push_filter(struct s_client *cl, uint8_t mode) +{ + struct s_reader *rdr = cl->reader; + int i = 20, j; + CECSPVALUETAB *filter; + // maximum size: 20+255 + uint8_t buf[20+242]; + memset(buf, 0, sizeof(buf)); + buf[0] = 0x3c; + buf[1] = 0xf2; + + // mode==2 send filters from rdr + if(mode == 2 && rdr) + { + filter = &rdr->cacheex.filter_caidtab; +#ifdef CS_CACHEEX_AIO + // if not set, use global settings + if(rdr->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) + filter = &cfg.cacheex_filter_caidtab; +#endif + } + // mode==3 send filters from acc + else if(mode == 3 && cl->typ == 'c' && cl->account) + { + filter = &cl->account->cacheex.filter_caidtab; +#ifdef CS_CACHEEX_AIO + // if not set, use global settings + if(cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) + filter = &cfg.cacheex_filter_caidtab; +#endif + } + else { + return; + } + + i2b_buf(2, filter->cevnum, buf + i); + i += 2; + + int32_t max_filters = 15; + for(j=0; jcevnum > j){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + i2b_buf(4, d->caid, buf + i); + } + i += 4; + } + + for(j=0; jcevnum > j){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + i2b_buf(4, d->cmask, buf + i); + } + i += 4; + } + + for(j=0; jcevnum > j){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + i2b_buf(4, d->prid, buf + i); + } + i += 4; + } + + for(j=0; jcevnum > j){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + i2b_buf(4, d->srvid, buf + i); + } + i += 4; + } + + cs_log_dbg(D_CACHEEX, "cacheex: sending push filter request to %s", username(cl)); + camd35_send_without_timeout(cl, buf, 242); // send adds +20 +} + +/** + * store received push filter + */ +static void camd35_cacheex_push_filter(struct s_client *cl, uint8_t *buf, uint8_t mode) +{ + struct s_reader *rdr = cl->reader; + int i = 20, j; + int32_t caid, cmask, provid, srvid; + CECSPVALUETAB *filter; + + // mode==2 write filters to acc + if(mode == 2 && cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 2 + && cl->account->cacheex.allow_filter == 1) + { + filter = &cl->account->cacheex.filter_caidtab; + } + // mode==3 write filters to rdr + else if(mode == 3 && rdr && rdr->cacheex.allow_filter == 1) + { + filter = &rdr->cacheex.filter_caidtab; + } + else { + return; + } + + cecspvaluetab_clear(filter); + i += 2; + + int32_t max_filters = 15; + for(j=0; j 0){ + CECSPVALUETAB_DATA d; + memset(&d, 0, sizeof(d)); + d.caid = b2i(4, buf + i); + cecspvaluetab_add(filter, &d); + } + i += 4; + } + + for(j=0; jcevnum){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + d->cmask = cmask; + } + i += 4; + } + + for(j=0; jcevnum){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + d->prid = provid; + } + i += 4; + } + + for(j=0; jcevnum){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + d->srvid = srvid; + } + i += 4; + } + + cs_log_dbg(D_CACHEEX, "cacheex: received push filter request from %s", username(cl)); +} + +static int32_t camd35_cacheex_push_chk(struct s_client *cl, ECM_REQUEST *er) +{ + if( + ll_count(er->csp_lastnodes) >= cacheex_maxhop(cl) // check max 10 nodes to push +#ifdef CS_CACHEEX_AIO + && (!er->localgenerated || (er->localgenerated && (ll_count(er->csp_lastnodes) >= cacheex_maxhop_lg(cl)))) // check maxhop_lg if cw is lg-flagged +#endif + ) + { +#ifdef CS_CACHEEX_AIO + cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes(non-lg) or reached %d nodes(lg), no push", cacheex_maxhop(cl), cacheex_maxhop_lg(cl)); +#else + cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes, no push", cacheex_maxhop(cl)); +#endif + return 0; + } + + if(cl->reader) + { + if(!cl->reader->tcp_connected) + { + cs_log_dbg(D_CACHEEX, "cacheex: not connected %s -> no push", username(cl)); + return 0; + } + } + + //if(chk_is_null_nodeid(remote_node)){ + if(!cl->ncd_skey[8]) + { + cs_log_dbg(D_CACHEEX, "cacheex: NO peer_node_id got yet, skip!"); + return 0; + } + + uint8_t *remote_node = cl->ncd_skey; //it is sended by reader(mode 2) or client (mode 3) each 30s using keepalive msgs + + //search existing peer nodes: + LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0); + uint8_t *node; + while((node = ll_li_next(li))) + { + cs_log_dbg(D_CACHEEX, "cacheex: check node %" PRIu64 "X == %" PRIu64 "X ?", cacheex_node_id(node), cacheex_node_id(remote_node)); + if(memcmp(node, remote_node, 8) == 0) + { + break; + } + } + ll_li_destroy(li); + + // node found, so we got it from there, do not push: + if(node) + { + cs_log_dbg(D_CACHEEX, + "cacheex: node %" PRIu64 "X found in list => skip push!", cacheex_node_id(node)); + return 0; + } + + // check if cw is already pushed + if(check_is_pushed(er->cw_cache, cl)) + { return 0; } + + cs_log_dbg(D_CACHEEX, "cacheex: push ok %" PRIu64 "X to %" PRIu64 "X %s", cacheex_node_id(camd35_node_id), cacheex_node_id(remote_node), username(cl)); + + return 1; +} + +static int32_t camd35_cacheex_push_out(struct s_client *cl, struct ecm_request_t *er) +{ + int8_t rc = (er->rc < E_NOTFOUND) ? E_FOUND : er->rc; + if(rc != E_FOUND && rc != E_UNHANDLED) { return -1; } // Maybe later we could support other rcs + + // E_FOUND : we have the CW, + // E_UNHANDLED : incoming ECM request + + if(cl->reader) + { + if(!camd35_tcp_connect(cl)) + { + cs_log_dbg(D_CACHEEX, "cacheex: not connected %s -> no push", username(cl)); + return (-1); + } + } + + uint32_t size = sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw) + sizeof(uint8_t) + +#ifdef CS_CACHEEX_AIO + (ll_count(er->csp_lastnodes) + 1) * 8 + sizeof(uint8_t); +#else + (ll_count(er->csp_lastnodes) + 1) * 8; +#endif + uint8_t *buf; + if(!cs_malloc(&buf, size + 20)) // camd35_send() adds +20 + { return -1; } + + buf[0] = 0x3f; // New Command: Cache-push + buf[1] = size & 0xff; + buf[2] = size >> 8; + buf[3] = rc; + + i2b_buf(2, er->srvid, buf + 8); + i2b_buf(2, er->caid, buf + 10); + i2b_buf(4, er->prid, buf + 12); + // i2b_buf(2, er->idx, buf + 16); // Not relevant...? + + if(er->cwc_cycletime && er->cwc_next_cw_cycle < 2) + { + buf[18] = er->cwc_cycletime; // contains cwc stage3 cycletime + if(er->cwc_next_cw_cycle == 1) + { buf[18] = (buf[18] | 0x80); } // set bit 8 to high + + if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) + { cl->account->cwc_info++; } + else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) + { cl->cwc_info++; } + + cs_log_dbg(D_CWC, "CWC (CE) push to %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", username(cl), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); + } + + buf[19] = er->ecm[0] != 0x80 && er->ecm[0] != 0x81 ? 0 : er->ecm[0]; + + uint8_t *ofs = buf + 20; + + // write oscam ecmd5: + memcpy(ofs, er->ecmd5, sizeof(er->ecmd5)); // 16 + ofs += sizeof(er->ecmd5); + + // write csp hashcode: + i2b_buf(4, CSP_HASH_SWAP(er->csp_hash), ofs); + ofs += 4; + + // write cw: + memcpy(ofs, er->cw, sizeof(er->cw)); // 16 + ofs += sizeof(er->cw); + + // write node count: + *ofs = ll_count(er->csp_lastnodes) + 1; + ofs++; + + // write own node: + memcpy(ofs, camd35_node_id, 8); + ofs += 8; + + // write other nodes: + LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0); + uint8_t *node; + while((node = ll_li_next(li))) + { + memcpy(ofs, node, 8); + ofs += 8; + } + ll_li_destroy(li); + +#ifdef CS_CACHEEX_AIO + // add localgenerated cw-flag + if(er->localgenerated) + { + *ofs = 1; + } + else + { + *ofs = 0xFF; + } +#endif + int32_t res = camd35_send(cl, buf, size); + NULLFREE(buf); + return res; +} + +static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf) +{ + int8_t rc = buf[3]; + if(rc != E_FOUND && rc != E_UNHANDLED) // Maybe later we could support other rcs + { return; } + + ECM_REQUEST *er; + uint16_t size = buf[1] | (buf[2] << 8); + if(size < sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw)) + { + cs_log_dbg(D_CACHEEX, "cacheex: %s received old cache-push format! data ignored!", username(cl)); + return; + } + + if(!(er = get_ecmtask())) + { return; } + + er->srvid = b2i(2, buf + 8); + er->caid = b2i(2, buf + 10); + er->prid = b2i(4, buf + 12); + er->pid = b2i(2, buf + 16); + er->ecm[0] = buf[19]!=0x80 && buf[19]!=0x81 ? 0 : buf[19]; // odd/even byte, usefull to send it over CSP and to check cw for swapping + er->rc = rc; + + er->ecmlen = 0; + + if(buf[18]) + { + if(buf[18] & (0x01 << 7)) + { + er->cwc_cycletime = (buf[18] & 0x7F); // remove bit 8 to get cycletime + er->cwc_next_cw_cycle = 1; + } + else + { + er->cwc_cycletime = buf[18]; + er->cwc_next_cw_cycle = 0; + } + } + + uint8_t *ofs = buf + 20; + + // Read ecmd5 + memcpy(er->ecmd5, ofs, sizeof(er->ecmd5)); // 16 + ofs += sizeof(er->ecmd5); + + if(!check_cacheex_filter(cl, er)) + { + return; + } + +#ifdef CS_CACHEEX_AIO + // check incoming cache + if(check_client(cl) && cl->typ == 'p' && cl->reader && cl->reader->cacheex.mode == 2 + && ( (cl->reader->cacheex.filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cl->reader->cacheex.filter_caidtab)) // reader cacheex_ecm_filter not matching if set + || (cl->reader->cacheex.filter_caidtab.cevnum == 0 && (cl->reader->cacheex.feature_bitfield & 4) && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab_aio)) // global cacheex_ecm_filter_aio not matching if set + || (cl->reader->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab)) // global cacheex_ecm_filter not matching if set + ) + ) + { + cs_log_dbg(D_CACHEEX, "cacheex: received cache not matching cacheex_ecm_filter => pushing filter again"); + camd35_cacheex_send_push_filter(cl, 2); // get cache != cacheex_ecm_filter, send filter again - remote restarted + if(cl->reader->cacheex.feature_bitfield & 4) + camd35_cacheex_feature_trigger(cl, 4, 2); + free_push_in_ecm(er); + return; + } + + if(check_client(cl) && cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3 + && ( (cl->account->cacheex.filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cl->account->cacheex.filter_caidtab)) // account cacheex_ecm_filter not matching if set + || (cl->account->cacheex.filter_caidtab.cevnum == 0 && (cl->account->cacheex.feature_bitfield & 4) && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab_aio)) // global cacheex_ecm_filter_aio not matching if set + || (cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab)) // global cacheex_ecm_filter not matching if set + ) + ) + { + cs_log_dbg(D_CACHEEX, "cacheex: received cache not matching cacheex_ecm_filter => pushing filter again"); + camd35_cacheex_send_push_filter(cl, 3); // get cache != cacheex_ecm_filter, send filter again - remote restarted + if(cl->account->cacheex.feature_bitfield & 4) + camd35_cacheex_feature_trigger(cl, 4, 3); + free_push_in_ecm(er); + return; + } +#endif + + // Read csp_hash: + er->csp_hash = CSP_HASH_SWAP(b2i(4, ofs)); + ofs += 4; + + // Read cw: + memcpy(er->cw, ofs, sizeof(er->cw)); // 16 + ofs += sizeof(er->cw); + + // Check auf neues Format: + uint8_t *data; + if(size > (sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw))) + { + + // Read lastnodes: + uint8_t count = *ofs; + ofs++; + +#ifndef CS_CACHEEX_AIO + // check max nodes: + if(count > cacheex_maxhop(cl)) + { + cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", (int32_t)count, cacheex_maxhop(cl), username(cl)); + NULLFREE(er); + return; + } +#endif + cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes %s", (int32_t)count, username(cl)); + if (er){ + er->csp_lastnodes = ll_create("csp_lastnodes"); + } + while(count) + { + if(!cs_malloc(&data, 8)) + { break; } + memcpy(data, ofs, 8); + ofs += 8; + ll_append(er->csp_lastnodes, data); + count--; + cs_log_dbg(D_CACHEEX, "cacheex: received node %" PRIu64 "X %s", cacheex_node_id(data), username(cl)); + } + +#ifdef CS_CACHEEX_AIO + // check byte after nodelist for "localgenerated CW"-flag + if(b2i(1, ofs) == 1) + { + er->localgenerated = 1; + cs_log_dbg(D_CACHEEX, "cacheex: received ECM with localgenerated flag %04X@%06X:%04X %s", er->caid, er->prid, er->srvid, username(cl)); + + // check max nodes for lg flagged cw: + if(ll_count(er->csp_lastnodes) > cacheex_maxhop_lg(cl)) + { + cs_log_dbg(D_CACHEEX, "cacheex: received (lg) %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop_lg(cl), username(cl)); + free_push_in_ecm(er); + return; + } + } + // without localgenerated flag + else + { + // check max nodes: + if(ll_count(er->csp_lastnodes) > cacheex_maxhop(cl)) + { + cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop(cl), username(cl)); + free_push_in_ecm(er); + return; + } + + if( + (cl->typ == 'p' && cl->reader && cl->reader->cacheex.mode == 2 && !chk_srvid_localgenerated_only_exception(er) // cx1&2 + && ( + // !aio + (cl->cacheex_aio_checked && !cl->reader->cacheex.feature_bitfield + && ( + !cfg.cacheex_lg_only_in_aio_only && !cl->reader->cacheex.lg_only_in_aio_only + && (cfg.cacheex_localgenerated_only_in || cl->reader->cacheex.localgenerated_only_in || chk_lg_only(er, &cl->reader->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab)) + ) + ) + || + // aio + (cl->cacheex_aio_checked && cl->reader->cacheex.feature_bitfield + && ( + cfg.cacheex_localgenerated_only_in || cl->reader->cacheex.localgenerated_only_in || chk_lg_only(er, &cl->reader->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab) + ) + ) + ) + ) + || + (cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3 && !chk_srvid_localgenerated_only_exception(er) // cx3 + && ( + // !aio + (cl->cacheex_aio_checked && !cl->account->cacheex.feature_bitfield + && ( + !cfg.cacheex_lg_only_in_aio_only && !cl->account->cacheex.lg_only_in_aio_only + && (cfg.cacheex_localgenerated_only_in || cl->account->cacheex.localgenerated_only_in || chk_lg_only(er, &cl->account->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab)) + ) + ) + || + // aio + (cl->cacheex_aio_checked && cl->account->cacheex.feature_bitfield + && ( + cfg.cacheex_localgenerated_only_in || cl->account->cacheex.localgenerated_only_in || chk_lg_only(er, &cl->account->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab) + ) + ) + ) + ) + ) + { + cs_log_dbg(D_CACHEEX, "cacheex: drop ECM without localgenerated flag %04X@%06X:%04X %s", er->caid, er->prid, er->srvid, username(cl)); + free_push_in_ecm(er); + return; + } + } +#endif + } + else + { + cs_log_dbg(D_CACHEEX, "cacheex: received old cachex from %s", username(cl)); + er->csp_lastnodes = ll_create("csp_lastnodes"); + } + + // store remote node id if we got one. The remote node is the first node in the node list + data = ll_has_elements(er->csp_lastnodes); + if(data && !cl->ncd_skey[8]) // Ok, this is tricky, we use newcamd key storage for saving the remote node + { + memcpy(cl->cxnodeid_last, data, 8); + cl->cxnodeid_last[8] = 1; + cl->cxnodeid_changer_detected = 0; + memcpy(cl->ncd_skey, data, 8); + cl->ncd_skey[8] = 1; // Mark as valid node + } + cs_log_dbg(D_CACHEEX, "cacheex: received cacheex from remote node id %" PRIu64 "X", cacheex_node_id(cl->ncd_skey)); + + // for compatibility: add peer node if no node received (not working now, maybe later): + if(!ll_count(er->csp_lastnodes) && cl->ncd_skey[8]) + { + if(!cs_malloc(&data, 8)) + { +#ifdef CS_CACHEEX_AIO + free_push_in_ecm(er); +#endif + return; + } + memcpy(data, cl->ncd_skey, 8); + ll_append(er->csp_lastnodes, data); + cs_log_dbg(D_CACHEEX, "cacheex: added missing remote node id %" PRIu64 "X", cacheex_node_id(data)); + } + +#ifdef CS_CACHEEX_AIO + if (er->cwc_cycletime && er->cwc_next_cw_cycle < 2) + { + if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) + { cl->account->cwc_info++; } + else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) + { cl->cwc_info++; } + cs_log_dbg(D_CWC, "CWC (CE) received from %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", username(cl), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); + } +#endif + cacheex_add_to_cache(cl, er); +} + +void camd35_cacheex_recv_ce1_cwc_info(struct s_client *cl, uint8_t *buf, int32_t idx) +{ + if(!(buf[0] == 0x01 && buf[18] < 0xFF && buf[18] > 0x00)) // cwc info ; normal camd3 ecms send 0xFF but we need no cycletime of 255 ;) + return; + + ECM_REQUEST *er = NULL; + int32_t i; + + for(i = 0; i < cfg.max_pending; i++) + { + if (cl->ecmtask[i].idx == idx) + { + er = &cl->ecmtask[i]; + break; + } + } + + if(!er) + { return; } + + int8_t rc = buf[3]; + if(rc != E_FOUND) + { return; } + + if(buf[18]) + { + if(buf[18] & (0x01 << 7)) + { + er->cwc_cycletime = (buf[18] & 0x7F); // remove bit 8 to get cycletime + er->parent->cwc_cycletime = er->cwc_cycletime; + er->cwc_next_cw_cycle = 1; + er->parent->cwc_next_cw_cycle = er->cwc_next_cw_cycle; + } + else + { + er->cwc_cycletime = buf[18]; + er->parent->cwc_cycletime = er->cwc_cycletime; + er->cwc_next_cw_cycle = 0; + er->parent->cwc_next_cw_cycle = er->cwc_next_cw_cycle; + } + } + + if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) + { cl->account->cwc_info++; } + else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) + { cl->cwc_info++; } + + cs_log_dbg(D_CWC, "CWC (CE1) received from %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", username(cl), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); + +} + + +/** + * when a server client connects + */ +static void camd35_server_client_init(struct s_client *cl) +{ + if(!cl->init_done) + { + cl->cacheex_needfilter = 1; + + // inicjalizacja nodeid changer detection + cl->cxnodeid_changer_detected = 0; + memset(cl->cxnodeid_last, 0, sizeof(cl->cxnodeid_last)); + memset(cl->ncd_skey, 0, sizeof(cl->ncd_skey)); + } +} + +/** + * store received remote id + */ +static void camd35_cacheex_push_receive_remote_id(struct s_client *cl, uint8_t *buf) +{ + uint8_t new_nodeid[8]; + + memcpy(new_nodeid, buf + 20, 8); + + // jeśli pierwszy raz, zapisz baseline + if (!cl->cxnodeid_last[8]) + { + memcpy(cl->cxnodeid_last, new_nodeid, 8); + cl->cxnodeid_last[8] = 1; + } + + // zapisz aktualny nodeid do struktury OSCam + memcpy(cl->ncd_skey, new_nodeid, 8); + cl->ncd_skey[8] = 1; + + // sprawdź czy zmieniony + if (memcmp(new_nodeid, cl->cxnodeid_last, 8) != 0) + { + cs_log("CACHEEX WARNING: NodeID changer detected for %s: new=%" PRIu64 "X old=%" PRIu64 "X", + username(cl), + cacheex_node_id(new_nodeid), + cacheex_node_id(cl->cxnodeid_last)); + + cl->cxnodeid_changer_detected = 1; + + // zapamiętaj nowy jako baseline + memcpy(cl->cxnodeid_last, new_nodeid, 8); + cl->cxnodeid_last[8] = 1; + } +} + + + +void camd35_cacheex_init_dcw(struct s_client *client, ECM_REQUEST *er) +{ + uint8_t *buf = er->src_data; // get orig request + + if(((client->typ == 'c' && client->account && client->account->cacheex.mode) + || ((client->typ == 'p' || client->typ == 'r') && (client->reader && client->reader->cacheex.mode))) + && er->cwc_cycletime && er->cwc_next_cw_cycle < 2) // ce1 + { + buf[18] = er->cwc_cycletime; // contains cwc stage3 cycletime + if(er->cwc_next_cw_cycle == 1) + { buf[18] = (buf[18] | 0x80); } // set bit 8 to high + if(client->typ == 'c' && client->account && client->account->cacheex.mode) + { client->account->cwc_info++; } + else if((client->typ == 'p' || client->typ == 'r') && (client->reader && client->reader->cacheex.mode)) + { client->cwc_info++; } + cs_log_dbg(D_CWC, "CWC (CE1) push to %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", username(client), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); + buf[19] = er->ecm[0]; + } +} + +/** + * send own id + */ +void camd35_cacheex_push_send_own_id(struct s_client *cl, uint8_t *mbuf) +{ + uint8_t rbuf[32]; // minimal size + if(!cl->crypted) { return; } + cs_log_dbg(D_CACHEEX, "cacheex: received id request from node %" PRIu64 "X %s", cacheex_node_id(mbuf + 20), username(cl)); + memset(rbuf, 0, sizeof(rbuf)); + rbuf[0] = 0x3e; + rbuf[1] = 12; + rbuf[2] = 0; + memcpy(rbuf + 20, camd35_node_id, 8); + cs_log_dbg(D_CACHEEX, "cacheex: sending own id %" PRIu64 "X request %s", cacheex_node_id(camd35_node_id), username(cl)); + camd35_send(cl, rbuf, 12); // send adds +20 +} + +bool camd35_cacheex_server(struct s_client *client, uint8_t *mbuf) +{ + switch(mbuf[0]) + { + case 0x3c: // Cache-push filter request + if(client->account && client->account->cacheex.mode==2){ + camd35_cacheex_push_filter(client, mbuf, 2); + } + break; + case 0x3d: // Cache-push id request + camd35_cacheex_push_receive_remote_id(client, mbuf); // reader send request id with its nodeid, so we save it! + camd35_cacheex_push_send_own_id(client, mbuf); + if(client->cacheex_needfilter && client->account && client->account->cacheex.mode==3){ + camd35_cacheex_send_push_filter(client, 3); + client->cacheex_needfilter = 0; + } +#ifdef CS_CACHEEX_AIO + if(!client->cacheex_aio_checked && ((client->account && client->account->cacheex.mode > 0) || (client->reader && client->reader->cacheex.mode > 0))) + { + camd35_cacheex_feature_request(client); + client->cacheex_aio_checked = 1; + } +#endif + break; + case 0x3e: // Cache-push id answer + camd35_cacheex_push_receive_remote_id(client, mbuf); + break; + case 0x3f: // Cache-push + camd35_cacheex_push_in(client, mbuf); + break; +#ifdef CS_CACHEEX_AIO + case 0x40: // cacheex-features request + camd35_cacheex_feature_request_reply(client, mbuf); + break; + case 0x41: // cacheex-features answer + // camd35_cacheex_feature_request_save(client, mbuf); + break; + case 0x42: // cacheex-feature trigger in + camd35_cacheex_feature_trigger_in(client, mbuf); + break; +#endif + default: + return 0; // Not processed by cacheex + } + return 1; // Processed by cacheex +} + +bool camd35_cacheex_recv_chk(struct s_client *client, uint8_t *buf) +{ + struct s_reader *rdr = client->reader; + switch(buf[0]) + { + case 0x3c: // Cache-push filter request + if(rdr->cacheex.mode==3){ + camd35_cacheex_push_filter(client, buf, 3); + } + break; + case 0x3d: // Cache-push id request + camd35_cacheex_push_receive_remote_id(client, buf); // client send request id with its nodeid, so we save it! + camd35_cacheex_push_send_own_id(client, buf); + break; + case 0x3e: // Cache-push id answer + camd35_cacheex_push_receive_remote_id(client, buf); +#ifdef CS_CACHEEX_AIO + if(!client->cacheex_aio_checked && ((client->account && client->account->cacheex.mode > 0) || (client->reader && client->reader->cacheex.mode > 0))) + { + camd35_cacheex_feature_request(client); + client->cacheex_aio_checked = 1; + } +#endif + break; + case 0x3f: // cache-push + camd35_cacheex_push_in(client, buf); + break; +#ifdef CS_CACHEEX_AIO + case 0x40: // cacheex-features request + camd35_cacheex_feature_request_reply(client, buf); + break; + case 0x41: // cacheex-features answer + // camd35_cacheex_feature_request_save(client, buf); + break; + case 0x42: // cacheex-feature trigger in + camd35_cacheex_feature_trigger_in(client, buf); + break; +#endif + default: + return 0; // Not processed by cacheex + } + return 1; // Processed by cacheex +} + +/** + * request remote id + */ +void camd35_cacheex_push_request_remote_id(struct s_client *cl) +{ + uint8_t rbuf[32]; // minimal size + memset(rbuf, 0, sizeof(rbuf)); + rbuf[0] = 0x3d; + rbuf[1] = 12; + rbuf[2] = 0; + memcpy(rbuf + 20, camd35_node_id, 8); + cs_log_dbg(D_CACHEEX, "cacheex: sending id request to %s", username(cl)); + camd35_send(cl, rbuf, 12); // send adds +20 +} + +void camd35_cacheex_module_init(struct s_module *ph) +{ + ph->c_cache_push = camd35_cacheex_push_out; + ph->c_cache_push_chk = camd35_cacheex_push_chk; + ph->s_init = camd35_server_client_init; +} + +#endif diff --git a/module-camd35-cacheex.c.orig b/module-camd35-cacheex.c.orig new file mode 100644 index 0000000..e7e7b84 --- /dev/null +++ b/module-camd35-cacheex.c.orig @@ -0,0 +1,1754 @@ +#define MODULE_LOG_PREFIX "camd35" + +#include "globals.h" +#include "oscam-array.h" + +#if defined(CS_CACHEEX) && (defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP)) + +#include "module-cacheex.h" +#include "module-camd35.h" +#include "module-camd35-cacheex.h" +#include "oscam-cache.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-string.h" +#include "oscam-reader.h" +#ifdef CS_CACHEEX_AIO +#include "oscam-chk.h" +#include "oscam-config.h" +#endif + +uint8_t camd35_node_id[8]; + +#define CSP_HASH_SWAP(n) (((((uint32_t)(n) & 0xFF)) << 24) | \ + ((((uint32_t)(n) & 0xFF00)) << 8) | \ + ((((uint32_t)(n) & 0xFF0000)) >> 8) | \ + ((((uint32_t)(n) & 0xFF000000)) >> 24)) + +#ifdef CS_CACHEEX_AIO +void camd35_cacheex_feature_trigger_in(struct s_client *cl, uint8_t *buf) +{ + int32_t feature = 0; + uint16_t i = 20; + uint8_t filter_count; + uint8_t j, k, l, rc; + feature = buf[21] | (buf[20] << 8); + FTAB *lgonly_tab; + + // check client & cacheex-mode + if( + !check_client(cl) || + !( + (cl->typ == 'c' && cl->account->cacheex.mode > 0) || + (cl->typ == 'p' && cl->reader->cacheex.mode > 0) + ) + ) + { + return; + } + + switch(feature) + { + // set localgenerated only + case 1: + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + if(cfg.cacheex_lg_only_remote_settings || cl->account->cacheex.lg_only_remote_settings) + cl->account->cacheex.localgenerated_only = buf[24]; + else if(buf[24]) + cl->account->cacheex.localgenerated_only = buf[24]; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + if(cfg.cacheex_lg_only_remote_settings || cl->reader->cacheex.lg_only_remote_settings) + cl->reader->cacheex.localgenerated_only = buf[24]; + else if(buf[24]) + cl->reader->cacheex.localgenerated_only = buf[24]; + } + break; + // set localgenerated only caidtab + case 2: + filter_count = buf[i+4]; + i += 5; + + memset(&lgonly_tab, 0, sizeof(lgonly_tab)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + lgonly_tab = &cl->account->cacheex.lg_only_tab; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + lgonly_tab = &cl->reader->cacheex.lg_only_tab; + } + else + { + return; + } + + // remotesettings enabled - replace local settings + if(cfg.cacheex_lg_only_remote_settings || + ( + (cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.lg_only_remote_settings) + || (cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.lg_only_remote_settings) + ) + ) + { + ftab_clear(lgonly_tab); + + for(j = 0; j < filter_count; j++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + + d.caid = b2i(2, buf + i); + i += 2; + + d.nprids = 1; + d.prids[0] = NO_PROVID_VALUE; + + ftab_add(lgonly_tab, &d); + } + } + // remotesettings disabled - write additional remote-caids received + else + { + for(j = 0; j < filter_count; j++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + + d.caid = b2i(2, buf + i); + i += 2; + + d.nprids = 1; + d.prids[0] = NO_PROVID_VALUE; + + if(!chk_lg_only_cp(d.caid, d.prids[0], lgonly_tab)) + { + cs_log_dbg(D_CACHEEX, "%04X:%06X not found in local settings - adding them", d.caid, d.prids[0]); + + for(l = rc = 0; (!rc) && (l < lgonly_tab->nfilts); l++) + { + if(lgonly_tab->filts[l].caid == d.caid) + { + rc = 1; + + if(lgonly_tab->filts[l].nprids+1 <= CS_MAXPROV) + { + lgonly_tab->filts[l].prids[lgonly_tab->filts[l].nprids] = d.prids[0]; + lgonly_tab->filts[l].nprids++; + } + else + { + cs_log_dbg(D_CACHEEX, "error: cacheex_lg_only_tab -> max. number(%i) of providers reached", CS_MAXPROV); + } + } + } + if(!rc) + { + ftab_add(lgonly_tab, &d); + } + } + } + } + break; + // set cacheex_ecm_filter - extended + case 4: + filter_count = buf[i+4]; + i += 5; + + CECSPVALUETAB *filter; + memset(&filter, 0, sizeof(filter)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.allow_filter) + { + filter = &cl->account->cacheex.filter_caidtab; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.allow_filter) + { + filter = &cl->reader->cacheex.filter_caidtab; + } + else + { + return; + } + + cecspvaluetab_clear(filter); + + for(j = 0; j < filter_count; j++) + { + int32_t caid = -1, cmask = -1, provid = -1, srvid = -1; + CECSPVALUETAB_DATA d; + memset(&d, 0, sizeof(d)); + + caid = b2i(2, buf + i); + if(caid == 0xFFFF) caid = -1; + i += 2; + + cmask = b2i(2, buf + i); + if(cmask == 0xFFFF) cmask = -1; + i += 2; + + provid = b2i(3, buf + i); + if(provid == 0xFFFFFF) provid = -1; + i += 3; + + srvid = b2i(2, buf + i); + if(srvid == 0xFFFF) srvid = -1; + i += 2; + + if(caid > 0) + { + d.caid = caid; + d.cmask = cmask; + d.prid = provid; + d.srvid = srvid; + cecspvaluetab_add(filter, &d); + } + } + break; + // no push after + case 8: ; + CAIDVALUETAB *ctab; + memset(&ctab, 0, sizeof(ctab)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + ctab = &cl->account->cacheex.cacheex_nopushafter_tab; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + ctab = &cl->reader->cacheex.cacheex_nopushafter_tab; + } + else + { + return; + } + + filter_count = buf[i+4]; + i += 5; + + caidvaluetab_clear(ctab); + + for(j = 0; j < filter_count; j++) + { + uint16_t caid = 0, value = 0; + CAIDVALUETAB_DATA d; + memset(&d, 0, sizeof(d)); + + caid = b2i(2, buf + i); + if(caid == 0xFFFF) caid = -1; + i += 2; + + value = b2i(2, buf + i); + if(value == 0xFFFF) value = -1; + i += 2; + + if(caid > 0) + { + d.caid = caid; + d.value = value; + caidvaluetab_add(ctab, &d); + } + } + break; + // max hop + case 16: + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.allow_maxhop) + { + cl->account->cacheex.maxhop = buf[24]; + cl->account->cacheex.maxhop_lg = buf[25]; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.allow_maxhop) + { + cl->reader->cacheex.maxhop = buf[24]; + cl->reader->cacheex.maxhop_lg = buf[25]; + } + break; + // aio-version + case 32: ; + uint16_t payload_size = b2i(2, buf + i + 2); + if(cl->typ == 'c' && cl->account->cacheex.mode > 0) + { + char *ofs = (char *)buf + i + 4; + memset(cl->account->cacheex.aio_version, 0, sizeof(cl->account->cacheex.aio_version)); + if(payload_size > 0) + { + size_t str_len = strnlen(ofs, payload_size); + if(str_len >= sizeof(cl->account->cacheex.aio_version)) + str_len = sizeof(cl->account->cacheex.aio_version) - 1; + memcpy(cl->account->cacheex.aio_version, ofs, str_len); + // sanitize: remove non-printable characters + size_t x; + for(x = 0; x < str_len; x++) + { + if(cl->account->cacheex.aio_version[x] < 0x20 || cl->account->cacheex.aio_version[x] > 0x7E) + { + cl->account->cacheex.aio_version[x] = '\0'; + break; + } + } + } + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode > 0) + { + char *ofs = (char *)buf + i + 4; + memset(cl->reader->cacheex.aio_version, 0, sizeof(cl->reader->cacheex.aio_version)); + if(payload_size > 0) + { + size_t str_len = strnlen(ofs, payload_size); + if(str_len >= sizeof(cl->reader->cacheex.aio_version)) + str_len = sizeof(cl->reader->cacheex.aio_version) - 1; + memcpy(cl->reader->cacheex.aio_version, ofs, str_len); + // sanitize: remove non-printable characters + size_t x; + for(x = 0; x < str_len; x++) + { + if(cl->reader->cacheex.aio_version[x] < 0x20 || cl->reader->cacheex.aio_version[x] > 0x7E) + { + cl->reader->cacheex.aio_version[x] = '\0'; + break; + } + } + } + } + break; + // lg_only_tab caid:prov1[,provN][;caid:prov] + case 64: ; + memset(&lgonly_tab, 0, sizeof(lgonly_tab)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + lgonly_tab = &cl->account->cacheex.lg_only_tab; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + lgonly_tab = &cl->reader->cacheex.lg_only_tab; + } + else + { + return; + } + + filter_count = buf[i+4]; + i += 5; + + // remotesettings enabled - replace local settings + if(cfg.cacheex_lg_only_remote_settings || + ( + (cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.lg_only_remote_settings) + || (cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.lg_only_remote_settings) + || !lgonly_tab->nfilts + ) + ) + { + ftab_clear(lgonly_tab); + + for(j = 0; j < filter_count; j++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + + d.caid = b2i(2, buf + i); + i += 2; + + d.nprids = b2i(1, buf + i); + i += 1; + + for(k=0; k < d.nprids; k++) + { + d.prids[k] = b2i(3, buf + i); + i += 3; + } + ftab_add(lgonly_tab, &d); + + } + } + // remotesettings disabled - write additional remote-caid/provids received + else + { + for(j = 0; j < filter_count; j++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + + d.caid = b2i(2, buf + i); + i += 2; + + d.nprids = b2i(1, buf + i); + i += 1; + + for(k=0; k < d.nprids; k++) + { + d.prids[k] = b2i(3, buf + i); + i += 3; + + if(!chk_ident_filter(d.caid, d.prids[k], lgonly_tab)) + { + cs_log_dbg(D_CACHEEX, "%04X:%06X not found in local settings - adding them", d.caid, d.prids[k]); + + for(l = rc = 0; (!rc) && (l < lgonly_tab->nfilts); l++) + { + if(lgonly_tab->filts[l].caid == d.caid) + { + rc = 1; + + if(lgonly_tab->filts[l].nprids+1 <= CS_MAXPROV) + { + lgonly_tab->filts[l].prids[lgonly_tab->filts[l].nprids] = d.prids[k]; + lgonly_tab->filts[l].nprids++; + } + else + { + cs_log_dbg(D_CACHEEX, "error: cacheex_lg_only_tab -> max. number of providers reached"); + } + } + } + if(!rc) + { + ftab_add(lgonly_tab, &d); + } + } + } + } + } + break; + default: + return; + } +} + +void camd35_cacheex_feature_trigger(struct s_client *cl, int32_t feature, uint8_t mode) +{ + // size: 20 + (feature-bitfield & mask: 2) + payload-size: 2 + feature-payload :x + uint16_t size = 20 + 2 + 2; + int i = 0; + uint8_t j; + uint8_t payload[MAX_ECM_SIZE-size]; + memset(payload, 0, sizeof(payload)); + + // check client & cacheex-mode + if(!check_client(cl)) + { + return; + } + + switch(feature) + { + FTAB *lgonly_tab; + // set localgenerated only + case 1: + size += 1; + if(size < 32) + size = 32; + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + i2b_buf(2, 1, payload + i); + i += 2; + // set payload + if(mode == 2) + { + if(cl->reader->cacheex.localgenerated_only_in) + payload[i] = cl->reader->cacheex.localgenerated_only_in; + else + payload[i] = cfg.cacheex_localgenerated_only_in; + } + else if(mode == 3) + { + if(cl->account->cacheex.localgenerated_only_in) + payload[i] = cl->account->cacheex.localgenerated_only_in; + else + payload[i] = cfg.cacheex_localgenerated_only_in; + } + + break; + // set localgenerated only caidtab; cx-aio < 9.2.6-04 + case 2: ; + if(mode == 2) + { + lgonly_tab = &cl->reader->cacheex.lg_only_in_tab; + if(!lgonly_tab->nfilts) + lgonly_tab = &cfg.cacheex_lg_only_in_tab; + } + else if(mode == 3) + { + lgonly_tab = &cl->account->cacheex.lg_only_in_tab; + if(!lgonly_tab->nfilts) + lgonly_tab = &cfg.cacheex_lg_only_in_tab; + } + else + { + return; + } + + size += (lgonly_tab->nfilts * 2 + 1); + if(size < 32) + size = 32; + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + if((lgonly_tab->nfilts * 2 + 1) > (int)sizeof(payload)) + { + cs_log_dbg(D_CACHEEX, "ERROR: too much localgenerated only caidtab-entries (max. 255)"); + return; + } + i2b_buf(2, (lgonly_tab->nfilts * 2 + 1), payload + i); // n * caid + ctnum + i += 2; + // set payload + if(lgonly_tab->nfilts > 255) + { + cs_log_dbg(D_CACHEEX, "ERROR: too much localgenerated only caidtab-entries (max. 255)"); + return; + } + payload[i] = lgonly_tab->nfilts; + i += 1; + + for(j = 0; j < lgonly_tab->nfilts; j++) + { + FILTER *d = &lgonly_tab->filts[j]; + if(d->caid) + { + i2b_buf(2, d->caid, payload + i); + i += 2; + } + else + { + continue; + } + } + break; + // cacchex_ecm_filter extendend + case 4: ; + CECSPVALUETAB *filter; + if(mode == 2) + { + filter = &cl->reader->cacheex.filter_caidtab; + // if not set, use global settings + if(cl->reader->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) + filter = &cfg.cacheex_filter_caidtab; + // if aio, use global aio settings + if(cl->reader->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && cl->cacheex_aio_checked && (cl->reader->cacheex.feature_bitfield & 4)) + filter = &cfg.cacheex_filter_caidtab_aio; + } + else if(mode == 3) + { + + filter = &cl->account->cacheex.filter_caidtab; + // if not set, use global settings + if(cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) + filter = &cfg.cacheex_filter_caidtab; + if(cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && cl->cacheex_aio_checked && (cl->account->cacheex.feature_bitfield & 4)) + filter = &cfg.cacheex_filter_caidtab_aio; + } + else + { + return; + } + + size += (filter->cevnum * 9 + 1); + if(size < 32) + size = 32; + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + if((filter->cevnum * 9 + 1) > (int)sizeof(payload)) + { + cs_log_dbg(D_CACHEEX, "ERROR: to much cacheex_ecm_filter-entries (max. 63), only 15 default camd3-filters sent"); + return; + } + i2b_buf(2, (filter->cevnum * 9 + 1), payload + i); // n * (caid2,mask2,provid3,srvid2) + ctnum1 + i += 2; + // set payload + payload[i] = filter->cevnum; + i += 1; + + for(j = 0; j < filter->cevnum; j++) + { + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + if(d->caid) + { + i2b_buf(2, d->caid, payload + i); + i += 2; + } + if(d->cmask) + { + i2b_buf(2, d->cmask, payload + i); + } + i += 2; + + if(d->prid) + { + i2b_buf(3, d->prid, payload + i); + } + i += 3; + + if(d->srvid) + { + i2b_buf(2, d->srvid, payload + i); + } + i += 2; + } + + camd35_cacheex_send_push_filter(cl, 2); + break; + // no push after + case 8: ; + CAIDVALUETAB *ctab; + if(mode == 2) + { + ctab = &cl->reader->cacheex.cacheex_nopushafter_tab; + if(!ctab->cvnum) + ctab = &cfg.cacheex_nopushafter_tab; + } + else if(mode == 3) + { + ctab = &cl->account->cacheex.cacheex_nopushafter_tab; + if(!ctab->cvnum) + ctab = &cfg.cacheex_nopushafter_tab; + } + else + { + return; + } + + size += (ctab->cvnum * 4 + 1); + if(size < 32) + size = 32; + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + if((ctab->cvnum * 4 + 1) > (int)sizeof(payload)) + { + cs_log_dbg(D_CACHEEX, "ERROR: to much no push after caidtvalueab-entries (max. 255)"); + return; + } + i2b_buf(2, (ctab->cvnum * 4 + 1), payload + i); // n * (caid2+value2) + cvnum + i += 2; + // set payload + if(ctab->cvnum > 255) + { + cs_log_dbg(D_CACHEEX, "ERROR: to much no push after caidtvalueab-entries (max. 255)"); + return; + } + payload[i] = ctab->cvnum; + i += 1; + + for(j = 0; j < ctab->cvnum; j++) + { + CAIDVALUETAB_DATA *d = &ctab->cvdata[j]; + if(d->caid) + { + i2b_buf(2, d->caid, payload + i); + i += 2; + i2b_buf(2, d->value, payload + i); + i += 2; + } + else + { + continue; + } + } + break; + // maxhop + case 16: + size += 2; + if(size < 32) + size = 32; + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + i2b_buf(2, 2, payload + i); + i += 2; + // set payload + if(mode == 2) + { + if(cl->reader->cacheex.maxhop) + payload[i] = cl->reader->cacheex.maxhop; + else + payload[i] = 0; + i += 1; + + if(cl->reader->cacheex.maxhop_lg) + payload[i] = cl->reader->cacheex.maxhop_lg; + else + payload[i] = 0; + } + else if(mode == 3) + { + if(cl->account->cacheex.maxhop) + payload[i] = cl->account->cacheex.maxhop; + else + payload[i] = 0; + i += 1; + + if(cl->account->cacheex.maxhop_lg) + payload[i] = cl->account->cacheex.maxhop_lg; + else + payload[i] = 0; + } + break; + // aio-version + case 32: ; + size += CS_AIO_VERSION_LEN; + if(size < 32) + size = 32; + + uint8_t token[CS_AIO_VERSION_LEN]; + memset(token, 0, sizeof(token)); + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + i2b_buf(2, sizeof(token), payload + i); + i += 2; + // set payload + + snprintf((char *)token, sizeof(token), "%s", CS_AIO_VERSION); + uint8_t *ofs = payload + i; + memcpy(ofs, token, sizeof(token)); + break; + // lg_only_tab + case 64: ; + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + + if(mode == 2) + { + lgonly_tab = &cl->reader->cacheex.lg_only_in_tab; + if(!lgonly_tab->nfilts) + lgonly_tab = &cfg.cacheex_lg_only_in_tab; + } + else if(mode == 3) + { + lgonly_tab = &cl->account->cacheex.lg_only_in_tab; + if(!lgonly_tab->nfilts) + lgonly_tab = &cfg.cacheex_lg_only_in_tab; + } + else + { + return; + } + + char *cx_aio_ftab; + cx_aio_ftab = cxaio_ftab_to_buf(lgonly_tab); + if(cs_strlen(cx_aio_ftab) > 0 && cx_aio_ftab[0] != '\0') + { + size += cs_strlen(cx_aio_ftab) * sizeof(char); + + // payload-size + i2b_buf(2, cs_strlen(cx_aio_ftab), payload + i); + i += 2; + + // filter counter + payload[i] = lgonly_tab->nfilts; + i += 1; + + for(j=0; j> 8; + + uint8_t *ofs = buf + 20; + memcpy(ofs, payload, size - 20); + + camd35_send_without_timeout(cl, buf, size-20); // send adds +20 +} + +void camd35_cacheex_feature_request_save(struct s_client *cl, uint8_t *buf) +{ + int32_t field = b2i(2, (buf+20)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + cl->account->cacheex.feature_bitfield = field; + // flag 32 => aio-version + if(cl->account->cacheex.feature_bitfield & 32) + { + camd35_cacheex_feature_trigger(cl, 32, 2); + } + } + + if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + cl->reader->cacheex.feature_bitfield = field; + // flag 32 => aio-version + if(cl->reader->cacheex.feature_bitfield & 32) + { + camd35_cacheex_feature_trigger(cl, 32, 3); + } + } + + if(cl->typ == 'c' && cl->account->cacheex.mode == 3) + { + struct s_auth *acc = cl->account; + if(acc) + { + acc->cacheex.feature_bitfield = field; + // process feature-specific actions based on feature_bitfield received + + // flag 1 => set localgenerated only flag + if(acc->cacheex.feature_bitfield & 1) + { + camd35_cacheex_feature_trigger(cl, 1, 3); + } + // flag 2 => set localgenerated only caids flag + if(acc->cacheex.feature_bitfield & 2 && !(acc->cacheex.feature_bitfield & 64)) + { + camd35_cacheex_feature_trigger(cl, 2, 3); + } + // flag 4 => set cacheex_ecm_filter (extended) + if(acc->cacheex.feature_bitfield & 4) + { + camd35_cacheex_feature_trigger(cl, 4, 3); + } + // flag 8 => np push after caids + if(acc->cacheex.feature_bitfield & 8) + { + camd35_cacheex_feature_trigger(cl, 8, 3); + } + // flag 16 => maxhop + if(acc->cacheex.feature_bitfield & 16) + { + camd35_cacheex_feature_trigger(cl, 16, 3); + } + // flag 32 => aio-version + if(acc->cacheex.feature_bitfield & 32) + { + camd35_cacheex_feature_trigger(cl, 32, 3); + } + // flag 64 => lg_only_tab + if(acc->cacheex.feature_bitfield & 64) + { + camd35_cacheex_feature_trigger(cl, 64, 3); + } + } + else + { + cs_log_dbg(D_CACHEEX, "feature_bitfield save failed - cl, %s", username(cl)); + } + } + else if(cl->typ == 'p' && (cl->reader->cacheex.mode == 2 || cl->reader->cacheex.mode == 1)) + { + struct s_reader *rdr = cl->reader; + if(rdr) + { + rdr->cacheex.feature_bitfield = field; + // process feature-specific actions + + // flag 1 => set localgenerated_only; cause of rdr->cacheex.localgenerated_only_in is set + if(rdr->cacheex.feature_bitfield & 1) + { + camd35_cacheex_feature_trigger(cl, 1, 2); + } + + // flag 2 => set lg_only_tab; cause of rdr->cacheex.lg_only_in_tab is set + if(rdr->cacheex.feature_bitfield & 2 && !(rdr->cacheex.feature_bitfield & 64)) + { + camd35_cacheex_feature_trigger(cl, 2, 2); + } + + // flag 4 => set cacheex_ecm_filter (extended) + if(rdr->cacheex.feature_bitfield & 4) + { + camd35_cacheex_feature_trigger(cl, 4, 2); + } + + // flag 8 => no push after caids + if(rdr->cacheex.feature_bitfield & 8) + { + camd35_cacheex_feature_trigger(cl, 8, 2); + } + // flag 16 => maxhop + if(rdr->cacheex.feature_bitfield & 16) + { + camd35_cacheex_feature_trigger(cl, 16, 2); + } + // flag 32 => aio-version + if(rdr->cacheex.feature_bitfield & 32) + { + camd35_cacheex_feature_trigger(cl, 32, 2); + } + // flag 64 => lg_only_tab + if(rdr->cacheex.feature_bitfield & 64) + { + camd35_cacheex_feature_trigger(cl, 64, 2); + } + } + else + { + cs_log_dbg(D_CACHEEX, "feature_bitfield save failed - rdr, %s", username(cl)); + } + } +} + +void camd35_cacheex_feature_request(struct s_client *cl) +{ + int i = 20; + + uint8_t buf[32]; + memset(buf, 0, sizeof(buf)); + buf[0] = 0x40; + buf[1] = 12; + buf[2] = 0; + + i2b_buf(2, CACHEEX_FEATURES, buf + i); // set feature-list here + + camd35_send_without_timeout(cl, buf, 12); // send adds +20 +} + +void camd35_cacheex_feature_request_reply(struct s_client *cl, uint8_t *buf) +{ + camd35_cacheex_feature_request_save(cl, buf); + int i = 20; + + uint8_t rbuf[32]; + memset(rbuf, 0, sizeof(rbuf)); + rbuf[0] = 0x41; + rbuf[1] = 12; + rbuf[2] = 0; + + i2b_buf(2, CACHEEX_FEATURES, rbuf + i); + + camd35_send_without_timeout(cl, rbuf, 12); // send adds +20 +} +#endif + + +/** + * send push filter + */ +void camd35_cacheex_send_push_filter(struct s_client *cl, uint8_t mode) +{ + struct s_reader *rdr = cl->reader; + int i = 20, j; + CECSPVALUETAB *filter; + // maximum size: 20+255 + uint8_t buf[20+242]; + memset(buf, 0, sizeof(buf)); + buf[0] = 0x3c; + buf[1] = 0xf2; + + // mode==2 send filters from rdr + if(mode == 2 && rdr) + { + filter = &rdr->cacheex.filter_caidtab; +#ifdef CS_CACHEEX_AIO + // if not set, use global settings + if(rdr->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) + filter = &cfg.cacheex_filter_caidtab; +#endif + } + // mode==3 send filters from acc + else if(mode == 3 && cl->typ == 'c' && cl->account) + { + filter = &cl->account->cacheex.filter_caidtab; +#ifdef CS_CACHEEX_AIO + // if not set, use global settings + if(cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) + filter = &cfg.cacheex_filter_caidtab; +#endif + } + else { + return; + } + + i2b_buf(2, filter->cevnum, buf + i); + i += 2; + + int32_t max_filters = 15; + for(j=0; jcevnum > j){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + i2b_buf(4, d->caid, buf + i); + } + i += 4; + } + + for(j=0; jcevnum > j){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + i2b_buf(4, d->cmask, buf + i); + } + i += 4; + } + + for(j=0; jcevnum > j){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + i2b_buf(4, d->prid, buf + i); + } + i += 4; + } + + for(j=0; jcevnum > j){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + i2b_buf(4, d->srvid, buf + i); + } + i += 4; + } + + cs_log_dbg(D_CACHEEX, "cacheex: sending push filter request to %s", username(cl)); + camd35_send_without_timeout(cl, buf, 242); // send adds +20 +} + +/** + * store received push filter + */ +static void camd35_cacheex_push_filter(struct s_client *cl, uint8_t *buf, uint8_t mode) +{ + struct s_reader *rdr = cl->reader; + int i = 20, j; + int32_t caid, cmask, provid, srvid; + CECSPVALUETAB *filter; + + // mode==2 write filters to acc + if(mode == 2 && cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 2 + && cl->account->cacheex.allow_filter == 1) + { + filter = &cl->account->cacheex.filter_caidtab; + } + // mode==3 write filters to rdr + else if(mode == 3 && rdr && rdr->cacheex.allow_filter == 1) + { + filter = &rdr->cacheex.filter_caidtab; + } + else { + return; + } + + cecspvaluetab_clear(filter); + i += 2; + + int32_t max_filters = 15; + for(j=0; j 0){ + CECSPVALUETAB_DATA d; + memset(&d, 0, sizeof(d)); + d.caid = b2i(4, buf + i); + cecspvaluetab_add(filter, &d); + } + i += 4; + } + + for(j=0; jcevnum){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + d->cmask = cmask; + } + i += 4; + } + + for(j=0; jcevnum){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + d->prid = provid; + } + i += 4; + } + + for(j=0; jcevnum){ + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + d->srvid = srvid; + } + i += 4; + } + + cs_log_dbg(D_CACHEEX, "cacheex: received push filter request from %s", username(cl)); +} + +static int32_t camd35_cacheex_push_chk(struct s_client *cl, ECM_REQUEST *er) +{ + if( + ll_count(er->csp_lastnodes) >= cacheex_maxhop(cl) // check max 10 nodes to push +#ifdef CS_CACHEEX_AIO + && (!er->localgenerated || (er->localgenerated && (ll_count(er->csp_lastnodes) >= cacheex_maxhop_lg(cl)))) // check maxhop_lg if cw is lg-flagged +#endif + ) + { +#ifdef CS_CACHEEX_AIO + cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes(non-lg) or reached %d nodes(lg), no push", cacheex_maxhop(cl), cacheex_maxhop_lg(cl)); +#else + cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes, no push", cacheex_maxhop(cl)); +#endif + return 0; + } + + if(cl->reader) + { + if(!cl->reader->tcp_connected) + { + cs_log_dbg(D_CACHEEX, "cacheex: not connected %s -> no push", username(cl)); + return 0; + } + } + + //if(chk_is_null_nodeid(remote_node)){ + if(!cl->ncd_skey[8]) + { + cs_log_dbg(D_CACHEEX, "cacheex: NO peer_node_id got yet, skip!"); + return 0; + } + + uint8_t *remote_node = cl->ncd_skey; //it is sended by reader(mode 2) or client (mode 3) each 30s using keepalive msgs + + //search existing peer nodes: + LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0); + uint8_t *node; + while((node = ll_li_next(li))) + { + cs_log_dbg(D_CACHEEX, "cacheex: check node %" PRIu64 "X == %" PRIu64 "X ?", cacheex_node_id(node), cacheex_node_id(remote_node)); + if(memcmp(node, remote_node, 8) == 0) + { + break; + } + } + ll_li_destroy(li); + + // node found, so we got it from there, do not push: + if(node) + { + cs_log_dbg(D_CACHEEX, + "cacheex: node %" PRIu64 "X found in list => skip push!", cacheex_node_id(node)); + return 0; + } + + // check if cw is already pushed + if(check_is_pushed(er->cw_cache, cl)) + { return 0; } + + cs_log_dbg(D_CACHEEX, "cacheex: push ok %" PRIu64 "X to %" PRIu64 "X %s", cacheex_node_id(camd35_node_id), cacheex_node_id(remote_node), username(cl)); + + return 1; +} + +static int32_t camd35_cacheex_push_out(struct s_client *cl, struct ecm_request_t *er) +{ + int8_t rc = (er->rc < E_NOTFOUND) ? E_FOUND : er->rc; + if(rc != E_FOUND && rc != E_UNHANDLED) { return -1; } // Maybe later we could support other rcs + + // E_FOUND : we have the CW, + // E_UNHANDLED : incoming ECM request + + if(cl->reader) + { + if(!camd35_tcp_connect(cl)) + { + cs_log_dbg(D_CACHEEX, "cacheex: not connected %s -> no push", username(cl)); + return (-1); + } + } + + uint32_t size = sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw) + sizeof(uint8_t) + +#ifdef CS_CACHEEX_AIO + (ll_count(er->csp_lastnodes) + 1) * 8 + sizeof(uint8_t); +#else + (ll_count(er->csp_lastnodes) + 1) * 8; +#endif + uint8_t *buf; + if(!cs_malloc(&buf, size + 20)) // camd35_send() adds +20 + { return -1; } + + buf[0] = 0x3f; // New Command: Cache-push + buf[1] = size & 0xff; + buf[2] = size >> 8; + buf[3] = rc; + + i2b_buf(2, er->srvid, buf + 8); + i2b_buf(2, er->caid, buf + 10); + i2b_buf(4, er->prid, buf + 12); + // i2b_buf(2, er->idx, buf + 16); // Not relevant...? + + if(er->cwc_cycletime && er->cwc_next_cw_cycle < 2) + { + buf[18] = er->cwc_cycletime; // contains cwc stage3 cycletime + if(er->cwc_next_cw_cycle == 1) + { buf[18] = (buf[18] | 0x80); } // set bit 8 to high + + if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) + { cl->account->cwc_info++; } + else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) + { cl->cwc_info++; } + + cs_log_dbg(D_CWC, "CWC (CE) push to %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", username(cl), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); + } + + buf[19] = er->ecm[0] != 0x80 && er->ecm[0] != 0x81 ? 0 : er->ecm[0]; + + uint8_t *ofs = buf + 20; + + // write oscam ecmd5: + memcpy(ofs, er->ecmd5, sizeof(er->ecmd5)); // 16 + ofs += sizeof(er->ecmd5); + + // write csp hashcode: + i2b_buf(4, CSP_HASH_SWAP(er->csp_hash), ofs); + ofs += 4; + + // write cw: + memcpy(ofs, er->cw, sizeof(er->cw)); // 16 + ofs += sizeof(er->cw); + + // write node count: + *ofs = ll_count(er->csp_lastnodes) + 1; + ofs++; + + // write own node: + memcpy(ofs, camd35_node_id, 8); + ofs += 8; + + // write other nodes: + LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0); + uint8_t *node; + while((node = ll_li_next(li))) + { + memcpy(ofs, node, 8); + ofs += 8; + } + ll_li_destroy(li); + +#ifdef CS_CACHEEX_AIO + // add localgenerated cw-flag + if(er->localgenerated) + { + *ofs = 1; + } + else + { + *ofs = 0xFF; + } +#endif + int32_t res = camd35_send(cl, buf, size); + NULLFREE(buf); + return res; +} + +static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf) +{ + int8_t rc = buf[3]; + if(rc != E_FOUND && rc != E_UNHANDLED) // Maybe later we could support other rcs + { return; } + + ECM_REQUEST *er; + uint16_t size = buf[1] | (buf[2] << 8); + if(size < sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw)) + { + cs_log_dbg(D_CACHEEX, "cacheex: %s received old cache-push format! data ignored!", username(cl)); + return; + } + + if(!(er = get_ecmtask())) + { return; } + + er->srvid = b2i(2, buf + 8); + er->caid = b2i(2, buf + 10); + er->prid = b2i(4, buf + 12); + er->pid = b2i(2, buf + 16); + er->ecm[0] = buf[19]!=0x80 && buf[19]!=0x81 ? 0 : buf[19]; // odd/even byte, usefull to send it over CSP and to check cw for swapping + er->rc = rc; + + er->ecmlen = 0; + + if(buf[18]) + { + if(buf[18] & (0x01 << 7)) + { + er->cwc_cycletime = (buf[18] & 0x7F); // remove bit 8 to get cycletime + er->cwc_next_cw_cycle = 1; + } + else + { + er->cwc_cycletime = buf[18]; + er->cwc_next_cw_cycle = 0; + } + } + + uint8_t *ofs = buf + 20; + + // Read ecmd5 + memcpy(er->ecmd5, ofs, sizeof(er->ecmd5)); // 16 + ofs += sizeof(er->ecmd5); + + if(!check_cacheex_filter(cl, er)) + { + return; + } + +#ifdef CS_CACHEEX_AIO + // check incoming cache + if(check_client(cl) && cl->typ == 'p' && cl->reader && cl->reader->cacheex.mode == 2 + && ( (cl->reader->cacheex.filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cl->reader->cacheex.filter_caidtab)) // reader cacheex_ecm_filter not matching if set + || (cl->reader->cacheex.filter_caidtab.cevnum == 0 && (cl->reader->cacheex.feature_bitfield & 4) && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab_aio)) // global cacheex_ecm_filter_aio not matching if set + || (cl->reader->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab)) // global cacheex_ecm_filter not matching if set + ) + ) + { + cs_log_dbg(D_CACHEEX, "cacheex: received cache not matching cacheex_ecm_filter => pushing filter again"); + camd35_cacheex_send_push_filter(cl, 2); // get cache != cacheex_ecm_filter, send filter again - remote restarted + if(cl->reader->cacheex.feature_bitfield & 4) + camd35_cacheex_feature_trigger(cl, 4, 2); + free_push_in_ecm(er); + return; + } + + if(check_client(cl) && cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3 + && ( (cl->account->cacheex.filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cl->account->cacheex.filter_caidtab)) // account cacheex_ecm_filter not matching if set + || (cl->account->cacheex.filter_caidtab.cevnum == 0 && (cl->account->cacheex.feature_bitfield & 4) && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab_aio)) // global cacheex_ecm_filter_aio not matching if set + || (cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab)) // global cacheex_ecm_filter not matching if set + ) + ) + { + cs_log_dbg(D_CACHEEX, "cacheex: received cache not matching cacheex_ecm_filter => pushing filter again"); + camd35_cacheex_send_push_filter(cl, 3); // get cache != cacheex_ecm_filter, send filter again - remote restarted + if(cl->account->cacheex.feature_bitfield & 4) + camd35_cacheex_feature_trigger(cl, 4, 3); + free_push_in_ecm(er); + return; + } +#endif + + // Read csp_hash: + er->csp_hash = CSP_HASH_SWAP(b2i(4, ofs)); + ofs += 4; + + // Read cw: + memcpy(er->cw, ofs, sizeof(er->cw)); // 16 + ofs += sizeof(er->cw); + + // Check auf neues Format: + uint8_t *data; + if(size > (sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw))) + { + + // Read lastnodes: + uint8_t count = *ofs; + ofs++; + +#ifndef CS_CACHEEX_AIO + // check max nodes: + if(count > cacheex_maxhop(cl)) + { + cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", (int32_t)count, cacheex_maxhop(cl), username(cl)); + NULLFREE(er); + return; + } +#endif + cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes %s", (int32_t)count, username(cl)); + if (er){ + er->csp_lastnodes = ll_create("csp_lastnodes"); + } + while(count) + { + if(!cs_malloc(&data, 8)) + { break; } + memcpy(data, ofs, 8); + ofs += 8; + ll_append(er->csp_lastnodes, data); + count--; + cs_log_dbg(D_CACHEEX, "cacheex: received node %" PRIu64 "X %s", cacheex_node_id(data), username(cl)); + } + +#ifdef CS_CACHEEX_AIO + // check byte after nodelist for "localgenerated CW"-flag + if(b2i(1, ofs) == 1) + { + er->localgenerated = 1; + cs_log_dbg(D_CACHEEX, "cacheex: received ECM with localgenerated flag %04X@%06X:%04X %s", er->caid, er->prid, er->srvid, username(cl)); + + // check max nodes for lg flagged cw: + if(ll_count(er->csp_lastnodes) > cacheex_maxhop_lg(cl)) + { + cs_log_dbg(D_CACHEEX, "cacheex: received (lg) %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop_lg(cl), username(cl)); + free_push_in_ecm(er); + return; + } + } + // without localgenerated flag + else + { + // check max nodes: + if(ll_count(er->csp_lastnodes) > cacheex_maxhop(cl)) + { + cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop(cl), username(cl)); + free_push_in_ecm(er); + return; + } + + if( + (cl->typ == 'p' && cl->reader && cl->reader->cacheex.mode == 2 && !chk_srvid_localgenerated_only_exception(er) // cx1&2 + && ( + // !aio + (cl->cacheex_aio_checked && !cl->reader->cacheex.feature_bitfield + && ( + !cfg.cacheex_lg_only_in_aio_only && !cl->reader->cacheex.lg_only_in_aio_only + && (cfg.cacheex_localgenerated_only_in || cl->reader->cacheex.localgenerated_only_in || chk_lg_only(er, &cl->reader->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab)) + ) + ) + || + // aio + (cl->cacheex_aio_checked && cl->reader->cacheex.feature_bitfield + && ( + cfg.cacheex_localgenerated_only_in || cl->reader->cacheex.localgenerated_only_in || chk_lg_only(er, &cl->reader->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab) + ) + ) + ) + ) + || + (cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3 && !chk_srvid_localgenerated_only_exception(er) // cx3 + && ( + // !aio + (cl->cacheex_aio_checked && !cl->account->cacheex.feature_bitfield + && ( + !cfg.cacheex_lg_only_in_aio_only && !cl->account->cacheex.lg_only_in_aio_only + && (cfg.cacheex_localgenerated_only_in || cl->account->cacheex.localgenerated_only_in || chk_lg_only(er, &cl->account->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab)) + ) + ) + || + // aio + (cl->cacheex_aio_checked && cl->account->cacheex.feature_bitfield + && ( + cfg.cacheex_localgenerated_only_in || cl->account->cacheex.localgenerated_only_in || chk_lg_only(er, &cl->account->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab) + ) + ) + ) + ) + ) + { + cs_log_dbg(D_CACHEEX, "cacheex: drop ECM without localgenerated flag %04X@%06X:%04X %s", er->caid, er->prid, er->srvid, username(cl)); + free_push_in_ecm(er); + return; + } + } +#endif + } + else + { + cs_log_dbg(D_CACHEEX, "cacheex: received old cachex from %s", username(cl)); + er->csp_lastnodes = ll_create("csp_lastnodes"); + } + + // store remote node id if we got one. The remote node is the first node in the node list + data = ll_has_elements(er->csp_lastnodes); + if(data && !cl->ncd_skey[8]) // Ok, this is tricky, we use newcamd key storage for saving the remote node + { + memcpy(cl->cxnodeid_last, data, 8); + cl->cxnodeid_last[8] = 1; + cl->cxnodeid_changer_detected = 0; + memcpy(cl->ncd_skey, data, 8); + cl->ncd_skey[8] = 1; // Mark as valid node + } + cs_log_dbg(D_CACHEEX, "cacheex: received cacheex from remote node id %" PRIu64 "X", cacheex_node_id(cl->ncd_skey)); + + // for compatibility: add peer node if no node received (not working now, maybe later): + if(!ll_count(er->csp_lastnodes) && cl->ncd_skey[8]) + { + if(!cs_malloc(&data, 8)) + { +#ifdef CS_CACHEEX_AIO + free_push_in_ecm(er); +#endif + return; + } + memcpy(data, cl->ncd_skey, 8); + ll_append(er->csp_lastnodes, data); + cs_log_dbg(D_CACHEEX, "cacheex: added missing remote node id %" PRIu64 "X", cacheex_node_id(data)); + } + +#ifdef CS_CACHEEX_AIO + if (er->cwc_cycletime && er->cwc_next_cw_cycle < 2) + { + if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) + { cl->account->cwc_info++; } + else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) + { cl->cwc_info++; } + cs_log_dbg(D_CWC, "CWC (CE) received from %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", username(cl), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); + } +#endif + cacheex_add_to_cache(cl, er); +} + +void camd35_cacheex_recv_ce1_cwc_info(struct s_client *cl, uint8_t *buf, int32_t idx) +{ + if(!(buf[0] == 0x01 && buf[18] < 0xFF && buf[18] > 0x00)) // cwc info ; normal camd3 ecms send 0xFF but we need no cycletime of 255 ;) + return; + + ECM_REQUEST *er = NULL; + int32_t i; + + for(i = 0; i < cfg.max_pending; i++) + { + if (cl->ecmtask[i].idx == idx) + { + er = &cl->ecmtask[i]; + break; + } + } + + if(!er) + { return; } + + int8_t rc = buf[3]; + if(rc != E_FOUND) + { return; } + + if(buf[18]) + { + if(buf[18] & (0x01 << 7)) + { + er->cwc_cycletime = (buf[18] & 0x7F); // remove bit 8 to get cycletime + er->parent->cwc_cycletime = er->cwc_cycletime; + er->cwc_next_cw_cycle = 1; + er->parent->cwc_next_cw_cycle = er->cwc_next_cw_cycle; + } + else + { + er->cwc_cycletime = buf[18]; + er->parent->cwc_cycletime = er->cwc_cycletime; + er->cwc_next_cw_cycle = 0; + er->parent->cwc_next_cw_cycle = er->cwc_next_cw_cycle; + } + } + + if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) + { cl->account->cwc_info++; } + else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) + { cl->cwc_info++; } + + cs_log_dbg(D_CWC, "CWC (CE1) received from %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", username(cl), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); + +} + + +/** + * when a server client connects + */ +static void camd35_server_client_init(struct s_client *cl) +{ + if(!cl->init_done) + { + cl->cacheex_needfilter = 1; + } +} + +/** + * store received remote id + */ +static void camd35_cacheex_push_receive_remote_id(struct s_client *cl, uint8_t *buf) +{ + + memcpy(cl->ncd_skey, buf + 20, 8); + cl->ncd_skey[8] = 1; + if (!cl->cxnodeid_last[8]) + { + memcpy(cl->cxnodeid_last, buf + 20, 8); + cl->cxnodeid_last[8] = 1; + } + + if (cl->cxnodeid_last[8] && (memcmp(cl->ncd_skey, cl->cxnodeid_last, 8) != 0)) + { + cs_log_dbg(D_CACHEEX, "cacheex: received id answer from %s: %" PRIu64 "X [previous nodeid: %" PRIu64 "X ] nodeid changed!", username(cl), cacheex_node_id(cl->ncd_skey), cacheex_node_id(cl->cxnodeid_last)); + cl->cxnodeid_changer_detected = 1; + } + else + { + cl->cxnodeid_changer_detected = 0; + } +} + + +void camd35_cacheex_init_dcw(struct s_client *client, ECM_REQUEST *er) +{ + uint8_t *buf = er->src_data; // get orig request + + if(((client->typ == 'c' && client->account && client->account->cacheex.mode) + || ((client->typ == 'p' || client->typ == 'r') && (client->reader && client->reader->cacheex.mode))) + && er->cwc_cycletime && er->cwc_next_cw_cycle < 2) // ce1 + { + buf[18] = er->cwc_cycletime; // contains cwc stage3 cycletime + if(er->cwc_next_cw_cycle == 1) + { buf[18] = (buf[18] | 0x80); } // set bit 8 to high + if(client->typ == 'c' && client->account && client->account->cacheex.mode) + { client->account->cwc_info++; } + else if((client->typ == 'p' || client->typ == 'r') && (client->reader && client->reader->cacheex.mode)) + { client->cwc_info++; } + cs_log_dbg(D_CWC, "CWC (CE1) push to %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", username(client), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); + buf[19] = er->ecm[0]; + } +} + +/** + * send own id + */ +void camd35_cacheex_push_send_own_id(struct s_client *cl, uint8_t *mbuf) +{ + uint8_t rbuf[32]; // minimal size + if(!cl->crypted) { return; } + cs_log_dbg(D_CACHEEX, "cacheex: received id request from node %" PRIu64 "X %s", cacheex_node_id(mbuf + 20), username(cl)); + memset(rbuf, 0, sizeof(rbuf)); + rbuf[0] = 0x3e; + rbuf[1] = 12; + rbuf[2] = 0; + memcpy(rbuf + 20, camd35_node_id, 8); + cs_log_dbg(D_CACHEEX, "cacheex: sending own id %" PRIu64 "X request %s", cacheex_node_id(camd35_node_id), username(cl)); + camd35_send(cl, rbuf, 12); // send adds +20 +} + +bool camd35_cacheex_server(struct s_client *client, uint8_t *mbuf) +{ + switch(mbuf[0]) + { + case 0x3c: // Cache-push filter request + if(client->account && client->account->cacheex.mode==2){ + camd35_cacheex_push_filter(client, mbuf, 2); + } + break; + case 0x3d: // Cache-push id request + camd35_cacheex_push_receive_remote_id(client, mbuf); // reader send request id with its nodeid, so we save it! + camd35_cacheex_push_send_own_id(client, mbuf); + if(client->cacheex_needfilter && client->account && client->account->cacheex.mode==3){ + camd35_cacheex_send_push_filter(client, 3); + client->cacheex_needfilter = 0; + } +#ifdef CS_CACHEEX_AIO + if(!client->cacheex_aio_checked && ((client->account && client->account->cacheex.mode > 0) || (client->reader && client->reader->cacheex.mode > 0))) + { + camd35_cacheex_feature_request(client); + client->cacheex_aio_checked = 1; + } +#endif + break; + case 0x3e: // Cache-push id answer + camd35_cacheex_push_receive_remote_id(client, mbuf); + break; + case 0x3f: // Cache-push + camd35_cacheex_push_in(client, mbuf); + break; +#ifdef CS_CACHEEX_AIO + case 0x40: // cacheex-features request + camd35_cacheex_feature_request_reply(client, mbuf); + break; + case 0x41: // cacheex-features answer + // camd35_cacheex_feature_request_save(client, mbuf); + break; + case 0x42: // cacheex-feature trigger in + camd35_cacheex_feature_trigger_in(client, mbuf); + break; +#endif + default: + return 0; // Not processed by cacheex + } + return 1; // Processed by cacheex +} + +bool camd35_cacheex_recv_chk(struct s_client *client, uint8_t *buf) +{ + struct s_reader *rdr = client->reader; + switch(buf[0]) + { + case 0x3c: // Cache-push filter request + if(rdr->cacheex.mode==3){ + camd35_cacheex_push_filter(client, buf, 3); + } + break; + case 0x3d: // Cache-push id request + camd35_cacheex_push_receive_remote_id(client, buf); // client send request id with its nodeid, so we save it! + camd35_cacheex_push_send_own_id(client, buf); + break; + case 0x3e: // Cache-push id answer + camd35_cacheex_push_receive_remote_id(client, buf); +#ifdef CS_CACHEEX_AIO + if(!client->cacheex_aio_checked && ((client->account && client->account->cacheex.mode > 0) || (client->reader && client->reader->cacheex.mode > 0))) + { + camd35_cacheex_feature_request(client); + client->cacheex_aio_checked = 1; + } +#endif + break; + case 0x3f: // cache-push + camd35_cacheex_push_in(client, buf); + break; +#ifdef CS_CACHEEX_AIO + case 0x40: // cacheex-features request + camd35_cacheex_feature_request_reply(client, buf); + break; + case 0x41: // cacheex-features answer + // camd35_cacheex_feature_request_save(client, buf); + break; + case 0x42: // cacheex-feature trigger in + camd35_cacheex_feature_trigger_in(client, buf); + break; +#endif + default: + return 0; // Not processed by cacheex + } + return 1; // Processed by cacheex +} + +/** + * request remote id + */ +void camd35_cacheex_push_request_remote_id(struct s_client *cl) +{ + uint8_t rbuf[32]; // minimal size + memset(rbuf, 0, sizeof(rbuf)); + rbuf[0] = 0x3d; + rbuf[1] = 12; + rbuf[2] = 0; + memcpy(rbuf + 20, camd35_node_id, 8); + cs_log_dbg(D_CACHEEX, "cacheex: sending id request to %s", username(cl)); + camd35_send(cl, rbuf, 12); // send adds +20 +} + +void camd35_cacheex_module_init(struct s_module *ph) +{ + ph->c_cache_push = camd35_cacheex_push_out; + ph->c_cache_push_chk = camd35_cacheex_push_chk; + ph->s_init = camd35_server_client_init; +} + +#endif diff --git a/module-camd35-cacheex.h b/module-camd35-cacheex.h new file mode 100644 index 0000000..6d3873d --- /dev/null +++ b/module-camd35-cacheex.h @@ -0,0 +1,25 @@ +#ifndef MODULE_CAMD35_CACHEEX_H_ +#define MODULE_CAMD35_CACHEEX_H_ + +#ifdef CS_CACHEEX +void camd35_cacheex_init_dcw(struct s_client *client, ECM_REQUEST *er); +void camd35_cacheex_recv_ce1_cwc_info(struct s_client *cl, uint8_t *buf, int32_t idx); +void camd35_cacheex_push_request_remote_id(struct s_client *cl); +void camd35_cacheex_send_push_filter(struct s_client *cl, uint8_t mode); +bool camd35_cacheex_server(struct s_client *client, uint8_t *mbuf); +bool camd35_cacheex_recv_chk(struct s_client *client, uint8_t *buf); +void camd35_cacheex_module_init(struct s_module *ph); +#ifdef CS_CACHEEX_AIO +void camd35_cacheex_feature_request(struct s_client *cl); +#endif +#else +static inline void camd35_cacheex_init_dcw(struct s_client *UNUSED(client), ECM_REQUEST *UNUSED(er)) { } +static inline void camd35_cacheex_recv_ce1_cwc_info(struct s_client *UNUSED(cl), uint8_t *UNUSED(buf), int32_t UNUSED(idx)) { } +static inline void camd35_cacheex_push_request_remote_id(struct s_client *UNUSED(cl)) { } +static inline void camd35_cacheex_send_push_filter(struct s_client *UNUSED(cl), uint8_t UNUSED(mode)) { } +static inline bool camd35_cacheex_server(struct s_client *UNUSED(client), uint8_t *UNUSED(mbuf)) { return 0; } +static inline bool camd35_cacheex_recv_chk(struct s_client *UNUSED(client), uint8_t *UNUSED(buf)) { return 0; } +static inline void camd35_cacheex_module_init(struct s_module *UNUSED(ph)) { } +#endif + +#endif diff --git a/module-camd35.c b/module-camd35.c new file mode 100644 index 0000000..9cce812 --- /dev/null +++ b/module-camd35.c @@ -0,0 +1,1296 @@ +#define MODULE_LOG_PREFIX "camd35" + +#include "globals.h" +#if defined MODULE_CAMD35 || defined MODULE_CAMD35_TCP + +#include "cscrypt/md5.h" +#include "module-cacheex.h" +#include "module-camd35.h" +#include "module-camd35-cacheex.h" +#include "oscam-aes.h" +#include "oscam-chk.h" +#include "oscam-cache.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-emm.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-reader.h" + +//CMD00 - ECM (request) +//CMD01 - ECM (response) +//CMD02 - EMM (in clientmode - set EMM, in server mode - EMM data) - obsolete +//CMD03 - ECM (cascading request) +//CMD04 - ECM (cascading response) +//CMD05 - EMM (emm request) send cardata/cardinfo to client +//CMD06 - EMM (incomming EMM in server mode) +//CMD19 - EMM (incomming EMM in server mode) only seen with caid 0x1830 +//CMD08 - Stop sending requests to the server for current srvid, prvid, caid +//CMD44 - MPCS/OScam internal error notification +//CMD55 - connect_on_init/keepalive + +//CMD0x3c - CACHEEX Cache-push filter request +//CMD0x3d - CACHEEX Cache-push id request +//CMD0x3e - CACHEEX Cache-push id answer +//CMD0x3f - CACHEEX cache-push + +//used variable ncd_skey for storing remote node id: ncd_skey[0..7] : 8 +//bytes node id ncd_skey[8] : 1=valid node id received + +#define REQ_SIZE 20 + MAX_ECM_SIZE + 0x34 + +static int32_t __camd35_send(struct s_client *cl, uint8_t *buf, int32_t buflen, int answer_awaited) +{ + int32_t l, status; + uint8_t rbuf[4 + REQ_SIZE + 15]; + uint8_t *sbuf = rbuf + 4; + + if(!cl->udp_fd || !cl->crypted) + { + return -1; // exit if no fd or aes key not set + } + + // Fix ECM len > 255 + if(buflen <= 0) + { + buflen = (buf[0] == 0) ? (((buf[21] & 0x0F) << 8) | buf[22]) + 3 : buf[1]; + } + l = 20 + (((buf[0] == 3) || (buf[0] == 4)) ? 0x34 : 0) + buflen; + + memcpy(rbuf, cl->ucrc, 4); + memcpy(sbuf, buf, l); + memset(sbuf + l, 0xFF, 15); // set unused space to 0xFF for newer camd3's + i2b_buf(4, crc32(0L, sbuf + 20, buflen), sbuf + 4); + l = boundary(4, l); + + cs_log_dump_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, sbuf, l, "send %d bytes to %s", l, username(cl)); + + aes_encrypt_idx(cl->aes_keys, sbuf, l); + + if(cl->is_udp) + { + status = sendto(cl->udp_fd, rbuf, l + 4, 0, (struct sockaddr *)&cl->udp_sa, cl->udp_sa_len); + if(status == -1) + { + set_null_ip(&SIN_GET_ADDR(cl->udp_sa)); + } + } + else + { + status = send(cl->udp_fd, rbuf, l + 4, 0); + + if(cl->typ == 'p' && cl->reader) + { + if(status == -1) + { + network_tcp_connection_close(cl->reader, "can't send"); + } + } + else if(cl->typ == 'c') + { + if(status == -1) + { + cs_disconnect_client(cl); + } + } + } + + if(status != -1) + { + if(cl->reader && answer_awaited) + { + cl->reader->last_s = time(NULL); + } + + if(cl->reader && !answer_awaited) + { + cl->reader->last_s = cl->reader->last_g = time(NULL); + } + + cl->last = time(NULL); + } + + return status; +} + +int32_t camd35_send(struct s_client *cl, uint8_t *buf, int32_t buflen) +{ + // send command and set sending time because we await response + return __camd35_send(cl, buf, buflen, 1); +} + +int32_t camd35_send_without_timeout(struct s_client *cl, uint8_t *buf, int32_t buflen) +{ + // send command and do NOT set sending time because we DON'T await response + return __camd35_send(cl, buf, buflen, 0); +} + +static int32_t camd35_auth_client(struct s_client *cl, uint8_t *ucrc) +{ + int32_t rc = 1, no_delay = 1; + uint32_t crc; + struct s_auth *account; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + + if(cl->upwd[0]) + { + return memcmp(cl->ucrc, ucrc, 4) ? 1 : 0; + } + + cl->crypted = 1; + + crc = ((ucrc[0] << 24) | (ucrc[1] << 16) | (ucrc[2] << 8) | ucrc[3]) & 0xffffffffL; + + for(account = cfg.account; account && (!cl->upwd[0]); account = account->next) + { + if(crc == crc32(0L, MD5((uint8_t *)account->usr, cs_strlen(account->usr), md5tmp), MD5_DIGEST_LENGTH)) + { + rc = cs_auth_client(cl, account, NULL); + if(!rc) + { + memcpy(cl->ucrc, ucrc, 4); + cs_strncpy((char *)cl->upwd, account->pwd, sizeof(cl->upwd)); + if(!aes_set_key_alloc(&cl->aes_keys, (char *) MD5(cl->upwd, cs_strlen((char *)cl->upwd), md5tmp))) + { + return 1; + } + +#ifdef CS_CACHEEX + if(cl->account->cacheex.mode < 2) +#endif + if(!cl->is_udp && cl->tcp_nodelay == 0) + { + setsockopt(cl->udp_fd, IPPROTO_TCP, TCP_NODELAY, (void *)&no_delay, sizeof(no_delay)); + cl->tcp_nodelay = 1; + } + + return 0; + } + } + } + + return (rc); +} + +static int32_t camd35_recv(struct s_client *client, uint8_t *buf, int32_t l) +{ + int32_t rc, s, rs, n = 0, buflen = 0, len = 0; + + for(rc = rs = s = 0; !rc; s++) + { + switch(s) + { + case 0: + { + if(!client->udp_fd) + { + return -9; + } + + if(client->is_udp && client->typ == 'c') + { + rs = recv_from_udpipe(buf); + } + else + { + // read minimum packet size (4 byte ucrc + 32 byte data) + // to detect packet size (tcp only) + + //rs = cs_recv(client->udp_fd, buf, client->is_udp ? l : 36, 0); + + if(client->is_udp) + { + while (1) + { + rs = cs_recv(client->udp_fd, buf, l, 0); + if(rs < 0) + { + if(errno == EINTR) + { + continue; // try again in case of interrupt + } + + if(errno == EAGAIN) + { + continue; // EAGAIN needs select procedure again + } + + cs_log_dbg(client->typ == 'c' ? D_CLIENT : D_READER, "ERROR: %s (errno=%d %s)", + __func__, errno, strerror(errno)); + break; + } + else + { + break; + } + } + } + else + { + int32_t tot = 36, readed = 0; + rs = 0; + + do + { + readed = cs_recv(client->udp_fd, buf + rs, tot, 0); + if(readed < 0) + { + if(errno == EINTR) + { + continue; // try again in case of interrupt + } + + if(errno == EAGAIN) + { + continue; // EAGAIN needs select procedure again + } + + cs_log_dbg(client->typ == 'c' ? D_CLIENT : D_READER, "ERROR: %s (errno=%d %s)", + __func__, errno, strerror(errno)); + break; + } + + if(readed == 0) // nothing to read left! + { + rc = -5; + break; + } + + if(readed > 0) // received something, add it! + { + tot -= readed; + rs += readed; + } + } + while(tot != 0); + } + } + + if(rs < 36) + { + if(rc != -5) + { + rc = -1; + } + + goto out; + } + + break; + } + + case 1: + { + switch(camd35_auth_client(client, buf)) + { + case 0: +#ifdef CS_CACHEEX_AIO + if(!client->c35_extmode) + { + camd35_send_extmode(client, false); + client->c35_extmode = 1; + } +#endif + break; // ok + + case 1: + rc = -2; + break; // unknown user + + default: + rc = -9; + break; // error's from cs_auth() + } + + memmove(buf, buf + 4, rs -= 4); + break; + } + + case 2: + { + aes_decrypt(client->aes_keys, buf, rs); + + if(rs != boundary(4, rs)) + { + cs_log_dbg(client->typ == 'c' ? D_CLIENT : D_READER, + "WARNING: packet size has wrong decryption boundary"); + } + + n = (buf[0] == 3) ? 0x34 : 0; + + // Fix for ECM request size > 255 (use ecm length field) + if(buf[0] == 0) + { + buflen = (((buf[21] & 0x0F) << 8) | buf[22]) + 3; + } + else if( +#ifdef CS_CACHEEX_AIO + buf[0] == 0x40 || buf[0] == 0x41 || buf[0] == 0x42 || +#endif + buf[0] == 0x3D || buf[0] == 0x3E || buf[0] == 0x3F + ) // cacheex-push + { + buflen = buf[1] | (buf[2] << 8); + } + else + { + buflen = buf[1]; + } + + n = boundary(4, n + 20 + buflen); + + if(!(client->is_udp && client->typ == 'c') && (rs < n) && ((n - 32) > 0)) + { + //len = cs_recv(client->udp_fd, buf+32, n-32, 0); // read the rest of the packet + + int32_t tot = n - 32; + int32_t readed = 0; + len = 0; + + do + { + readed = cs_recv(client->udp_fd, buf + 32 + len, tot, 0); // read the rest of the packet + if(readed < 0) + { + if(errno == EINTR) + { + continue; // try again in case of interrupt + } + + if(errno == EAGAIN) + { + continue; // EAGAIN needs select procedure again + } + + cs_log_dbg(client->typ == 'c' ? D_CLIENT : D_READER, "ERROR: %s (errno=%d %s)", + __func__, errno, strerror(errno)); + break; + } + + if(readed == 0) // nothing to read left + { + break; + } + + if(readed > 0) // received something, add it + { + tot -= readed; + len += readed; + } + } + while(tot != 0); + + + if(len > 0) + { + rs += len; + aes_decrypt(client->aes_keys, buf + 32, len); + } + + if(len < 0) + { + rc = -1; + goto out; + } + } + + cs_log_dump_dbg(client->typ == 'c' ? D_CLIENT : D_READER, buf, rs, "received %d bytes from %s", + rs, remote_txt()); + + if(n < rs) + { + cs_log_dbg(client->typ == 'c' ? D_CLIENT : D_READER, "ignoring %d bytes of garbage", rs - n); + } + else if(n > rs) + { + rc = -3; + } + + break; + } + + case 3: + { + if(crc32(0L, buf + 20, buflen) != b2i(4, buf + 4)) + { + rc = -4; + } + + if(!rc) + { + rc = n; + } + + break; + } + } + } + +out: + if((rs > 0) && ((rc == -1) || (rc == -2))) + { + cs_log_dump_dbg(client->typ == 'c' ? D_CLIENT : D_READER, buf, rs, + "received %d bytes from %s (native)", rs, remote_txt()); + } + + if(rc >= 0) + { + client->last = time(NULL); // last client action is now + } + + switch(rc) + { + //case 0: + // break; + + case -1: + cs_log("packet is too small (received %d bytes, expected %d bytes)", rs, l); + break; + + case -2: + if(cs_auth_client(client, 0, "unknown user")) + { + cs_disconnect_client(client); + } + break; + + case -3: + cs_log("incomplete request!"); + break; + + case -4: + cs_log("checksum error (wrong password?)"); + break; + + case -5: + cs_log_dbg(client->typ == 'c' ? D_CLIENT : D_READER, "connection closed"); + break; + + //default: + // cs_log_dbg(D_TRACE, "camd35_recv returns rc=%d", rc); + // break; + } + + return (rc); +} + +/* + * server functions + */ + +static void camd35_request_emm(ECM_REQUEST *er) +{ + int32_t i; + time_t now; + uint8_t mbuf[1024]; + struct s_client *cl = cur_client(); + struct s_reader *aureader = NULL, *rdr = NULL; + + if(er->selected_reader && !er->selected_reader->audisabled + && ll_contains(cl->aureader_list, er->selected_reader)) + { + aureader = er->selected_reader; + } + + if(!aureader && cl->aureader_list) + { + LL_ITER itr = ll_iter_create(cl->aureader_list); + + while((rdr = ll_iter_next(&itr))) + { + if(emm_reader_match(rdr, er->caid, er->prid)) + { + aureader = rdr; + break; + } + } + } + + if(!aureader) + { + return; // TODO + } + + uint16_t au_caid = aureader->caid; + + // Bulcrypt has two caids and aureader->caid can't be used. + // Use ECM_REQUEST caid for AU. + if(!au_caid && caid_is_bulcrypt(er->caid)) + { + au_caid = er->caid; + } + + time(&now); + + if(!memcmp(cl->lastserial, aureader->hexserial, 8)) + { + if(llabs(now - cl->last) < 180) + { + return; + } + } + + memcpy(cl->lastserial, aureader->hexserial, 8); + cl->last = now; + + if(au_caid) + { + cl->disable_counter = 0; + + cs_log("%s emm-request sent (reader=%s, caid=%04X, auprovid=%06X)", username(cur_client()), + aureader->label, au_caid, aureader->auprovid ? aureader->auprovid : b2i(4, aureader->prid[0])); + } + else if(cl->disable_counter > 2) + { + return; + } + else + { + cl->disable_counter++; + } + + memset(mbuf, 0, sizeof(mbuf)); + mbuf[2] = mbuf[3] = 0xFF; // must not be zero + i2b_buf(2, er->srvid, mbuf + 8); + + // override request provid with auprovid if set in CMD05 + if(aureader->auprovid) + { + if(aureader->auprovid != er->prid) + { + i2b_buf(4, aureader->auprovid, mbuf + 12); + } + else + { + i2b_buf(4, er->prid, mbuf + 12); + } + } + else + { + i2b_buf(4, er->prid, mbuf + 12); + } + + i2b_buf(2, er->pid, mbuf + 16); + mbuf[0] = 5; + mbuf[1] = 111; + + if(au_caid) + { + mbuf[39] = 1; // no. caids + mbuf[20] = au_caid >> 8; // caid's (max 8) + mbuf[21] = au_caid & 0xFF; + memcpy(mbuf + 40, aureader->hexserial, 6); // serial now 6 bytes + mbuf[47] = aureader->nprov; + + for(i = 0; i < aureader->nprov; i++) + { + if(caid_is_betacrypt(au_caid) || caid_is_irdeto(au_caid)) + { + mbuf[48 + (i * 5)] = aureader->prid[i][0]; + memcpy(&mbuf[50 + (i * 5)], &aureader->prid[i][1], 3); + } + else + { + mbuf[48 + (i * 5)] = aureader->prid[i][2]; + mbuf[49 + (i * 5)] = aureader->prid[i][3]; + memcpy(&mbuf[50 + (i * 5)], &aureader->sa[i][0], 4); // for conax we need at least 4 bytes + } + } + + // we think client/server protocols should deliver + // all information, and only readers should discard EMM + mbuf[128] = (aureader->blockemm & EMM_GLOBAL && !(aureader->saveemm & EMM_GLOBAL)) ? 0 : 1; + mbuf[129] = (aureader->blockemm & EMM_SHARED && !(aureader->saveemm & EMM_SHARED)) ? 0 : 1; + mbuf[130] = (aureader->blockemm & EMM_UNIQUE && !(aureader->saveemm & EMM_UNIQUE)) ? 0 : 1; + mbuf[127] = (aureader->blockemm & EMM_UNKNOWN && !(aureader->saveemm & EMM_UNKNOWN)) ? 0 : 1; + } + else // disable emm + { + mbuf[20] = mbuf[39] = mbuf[40] = mbuf[47] = mbuf[49] = 1; + } + + memcpy(mbuf + 10, mbuf + 20, 2); + camd35_send(cl, mbuf, 0); // send with data-len 111 for camd3 > 3.890 + mbuf[1]++; + camd35_send(cl, mbuf, 0); // send with data-len 112 for camd3 < 3.890 +} + +static void camd35_send_dcw(struct s_client *client, ECM_REQUEST *er) +{ + uint8_t *buf; + buf = er->src_data; // get orig request + + if(!buf) + { + rdr_log(client->reader, "ERROR: src_data missing"); + return; + } + + if(er->rc == E_INVALID && !client->c35_suppresscmd08) // send normal CMD08 + { + buf[0] = 0x08; + buf[1] = 2; + memset(buf + 20, 0, buf[1]); + buf[22] = er->rc; // put rc in byte 22 - hopefully don't break legacy camd3 + } + else if(er->rc == E_STOPPED) // send sleep CMD08 + { + buf[0] = 0x08; + buf[1] = 2; + buf[20] = 0; + buf[21] = 0xFF; + + cs_log("%s stop request send", client->account->usr); + } + else + { + // Send CW + if((er->rc < E_NOTFOUND) || (er->rc == E_FAKE)) + { + if(buf[0] == 3) + { + memmove(buf + 20 + 16, buf + 20 + buf[1], 0x34); + } + +#ifdef CS_CACHEEX_AIO + if(er->localgenerated && client->c35_extmode > 1) + { + buf[0] += 0x51; // ecm response with lg-flag + } + else + { +#endif + buf[0]++; // ecm response (CMD01 or CMD04) +#ifdef CS_CACHEEX_AIO + } +#endif + buf[1] = 16; + camd35_cacheex_init_dcw(client, er); + memcpy(buf + 20, er->cw, buf[1]); + } + else + { + // Send old CMD44 to prevent cascading + // problems with older mpcs/oscam versions + buf[0] = 0x44; + buf[1] = 0; + } + } + + camd35_send(client, buf, 0); + camd35_request_emm(er); +} + +static void camd35_process_ecm(uint8_t *buf, int buflen) +{ + ECM_REQUEST *er; + + if(!buf || buflen < 23) + { + return; + } + + uint16_t ecmlen = SCT_LEN((&buf[20])); + + if(ecmlen > MAX_ECM_SIZE || ecmlen + 20 > buflen || ecmlen < 4) + { + return; + } + + if(!(er = get_ecmtask())) + { + return; + } + + er->ecmlen = ecmlen; + + if(!cs_malloc(&er->src_data, 0x34 + 20 + er->ecmlen)) + { + NULLFREE(er); + return; + } + + memcpy(er->src_data, buf, 0x34 + 20 + er->ecmlen); // save request + er->srvid = b2i(2, buf + 8); + er->caid = b2i(2, buf + 10); + er->prid = b2i(4, buf + 12); + //er->idx = b2i(2, buf + 16); // ecmtask idx (see camd35_recv_chk) + memcpy(er->ecm, buf + 20, er->ecmlen); + + get_cw(cur_client(), er); +} + +static void camd35_process_emm(uint8_t *buf, int buflen, int emmlen) +{ + EMM_PACKET epg; + + if(!buf || buflen < 20 || emmlen + 20 > buflen) + { + return; + } + + memset(&epg, 0, sizeof(epg)); + + epg.emmlen = emmlen; + if(epg.emmlen < 3 || epg.emmlen > MAX_EMM_SIZE) + { + return; + } + + memcpy(epg.caid, buf + 10, 2); + memcpy(epg.provid, buf + 12 , 4); + memcpy(epg.emm, buf + 20, epg.emmlen); + + do_emm(cur_client(), &epg); +} + +int32_t camd35_tcp_connect(struct s_client *cl) +{ + if(cl->is_udp) // check for udp client + { + if(!IP_ISSET(SIN_GET_ADDR(cl->udp_sa))) // check ip is set + { + if(!(hostResolve(cl->reader))) // no ip -> try to resolve ip of client + { + network_tcp_connection_close(cl->reader, "no ip"); + return 0; + } + } + } + + if(!cl->reader->tcp_connected) // client not connected + { + int32_t handle = 0; + handle = network_tcp_connection_open(cl->reader); // try to connect + if(handle < 0) // got no handle -> error! + { + cl->reader->last_s = 0; // set last send to zero + cl->reader->last_g = 0; // set last receive to zero + cl->last = 0; // set last client action to zero + + return 0; + } + + cl->reader->tcp_connected = 1; + cl->reader->card_status = CARD_INSERTED; + cl->reader->last_s = time(NULL); // reset last send + cl->reader->last_g = time(NULL); // reset last receive + cl->last = time(NULL); // reset last client action + cl->pfd = cl->udp_fd = handle; + } + + if(!cl->udp_fd) // Check if client has no handle -> error + { + return 0; + } + + // check if client reached timeout + if(cl->reader->tcp_rto && (cl->reader->last_s - cl->reader->last_g > cl->reader->tcp_rto)) + { + if(!cl->is_udp) // tcp on timeout disconnect reader + { + network_tcp_connection_close(cl->reader, "rto"); + return 0; + } + else //udp check to discover ip change on dynamic ip servers + { + IN_ADDR_T last_ip; + IP_ASSIGN(last_ip, cl->ip); + + if(!hostResolve(cl->reader)) + { + network_tcp_connection_close(cl->reader, "no ip"); + return 0; + } + + if(!IP_EQUAL(last_ip, cl->ip)) + { + network_tcp_connection_close(cl->reader, "ip change"); + return 0; + } + } + } + + return 1; // all ok +} + +/* + * client functions + */ + +static void camd35_send_keepalive(struct s_client *cl) +{ + if(cl->reader) + { + if(camd35_tcp_connect(cl)) + { + if(cacheex_get_rdr_mode(cl->reader) > 1) + { + camd35_cacheex_push_request_remote_id(cl); + return; + } + + uint8_t rbuf[32]; // minimal size + memset(rbuf, 0, sizeof(rbuf)); + + rbuf[0] = 55; + rbuf[1] = 1; + rbuf[2] = 0; + + camd35_send(cl, rbuf, 1); // send adds +20 + } + } +} + +static void camd35_send_keepalive_answer(struct s_client *cl) +{ + if(check_client(cl) && cl->account) + { + uint8_t rbuf[32]; // minimal size + memset(rbuf, 0, sizeof(rbuf)); + + rbuf[0] = 55; + rbuf[1] = 1; + rbuf[2] = 0; + + camd35_send(cl, rbuf, 1); // send adds +20 + } +} + +static int32_t camd35_client_init(struct s_client *cl) +{ + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + int32_t no_delay = 1; + + cs_strncpy((char *)cl->upwd, cl->reader->r_pwd, sizeof(cl->upwd)); + i2b_buf(4, crc32(0L, MD5((uint8_t *)cl->reader->r_usr, cs_strlen(cl->reader->r_usr), md5tmp), 16), cl->ucrc); + + if(!aes_set_key_alloc(&cl->aes_keys, (char *)MD5(cl->upwd, cs_strlen((char *)cl->upwd), md5tmp))) + { + return 1; + } + + cl->crypted = 1; + + rdr_log(cl->reader, "proxy %s:%d", cl->reader->device, cl->reader->r_port); + + if(!cl->is_udp && cacheex_get_rdr_mode(cl->reader) < 2) + { + setsockopt(cl->udp_fd, IPPROTO_TCP, TCP_NODELAY, (void *)&no_delay, sizeof(no_delay)); + } + + if(cl->reader->keepalive) + { + camd35_send_keepalive(cl); + } + + if(cacheex_get_rdr_mode(cl->reader) == 2 +#ifdef CS_CACHEEX_AIO + || cacheex_get_rdr_mode(cl->reader) == 1 +#endif + ) + { + camd35_cacheex_send_push_filter(cl, 2); +#ifdef CS_CACHEEX_AIO + camd35_cacheex_feature_request(cl); +#endif + } + +#ifdef CS_CACHEEX_AIO + if(!cl->c35_extmode) + { + camd35_send_extmode(cl, false); + cl->c35_extmode = 1; + } +#endif + + return 0; +} + +static void camd35_idle(void) +{ + struct s_client *cl = cur_client(); + + if(!cl->reader) + { + return; + } + + if(cl->reader->keepalive) + { + camd35_send_keepalive(cl); + } + else if(cl->reader->tcp_ito > 0) // only check if user added an inactivity timeout + { + // inactivity timeout check + time_t now; + int32_t time_diff; + + time(&now); + time_diff = llabs(now - cl->reader->last_s); + + if(time_diff > cl->reader->tcp_ito) + { + if(check_client(cl) && cl->reader->tcp_connected && cl->reader->ph.type == MOD_CONN_TCP) + { + rdr_log_dbg(cl->reader, D_READER, "inactive_timeout, close connection (fd=%d)", cl->pfd); + network_tcp_connection_close(cl->reader, "inactivity"); + } + else + { + cl->reader->last_s = now; + } + } + } +} + +static void *camd35_server(struct s_client *client, uint8_t *mbuf, int32_t n) +{ + if(!client || !mbuf) + { + return NULL; + } + + if(client->reader) + { + client->reader->last_g = time(NULL); // last receive is now + + if(mbuf[0] == 6 || mbuf[0] == 19) // check for emm command + { + // fixup: last send is now (if client is only + // sending emms, connection would be dropped!) + client->reader->last_s = time(NULL); + } + + rdr_log(client->reader, "SERVER last = %d, last_s = %d, last_g = %d", + (int) client->last, (int) client->reader->last_s, (int) client->reader->last_g); + } + + client->last = time(NULL); // last client action is now + + switch(mbuf[0]) + { + case 0: // ECM + case 3: // ECM (cascading) + camd35_process_ecm(mbuf, n); + break; + + case 6: // EMM + case 19: // EMM + if(n > 2) + { + camd35_process_emm(mbuf, n, mbuf[1]); + } + break; + + case 55: + camd35_send_keepalive_answer(client); // keepalive msg + break; +#ifdef CS_CACHEEX_AIO + case 0x43: + break; + case 0x50: + client->c35_extmode = 2; + break; +#endif + default: + if(!camd35_cacheex_server(client, mbuf)) + { + cs_log("unknown [cs357x/cs378x] command from %s! (%d) n=%d", username(client), mbuf[0], n); + } + } + + return NULL; +} + +static int32_t camd35_send_ecm(struct s_client *client, ECM_REQUEST *er) +{ + static const char *typtext[] = { "ok", "invalid", "sleeping" }; + + if(client->stopped) + { + if(er->srvid == client->lastsrvid && er->caid == client->lastcaid) + { + cs_log("%s is stopped - requested by server (%s)", client->reader->label, typtext[client->stopped]); + return -1; + } + else + { + client->stopped = 0; + } + } + + client->lastsrvid = er->srvid; + client->lastcaid = er->caid; + client->lastpid = er->pid; + + if(!camd35_tcp_connect(client)) + { + return -1; + } + + client->reader->card_status = CARD_INSERTED; // for udp + + uint8_t *buf; + if(!cs_malloc(&buf, 20 + er->ecmlen + 15)) + { + return -1; + } + + memset(buf, 0, 20); + memset(buf + 20, 0xFF, er->ecmlen + 15); + + buf[1] = er->ecmlen; + i2b_buf(2, er->srvid, buf + 8); + i2b_buf(2, er->caid, buf + 10); + i2b_buf(4, er->prid, buf + 12); + i2b_buf(2, er->idx, buf + 16); + buf[18] = 0xFF; + buf[19] = 0xFF; + memcpy(buf + 20, er->ecm, er->ecmlen); + + int32_t rc = (camd35_send(client, buf, 0) < 1) ? -1 : 0; + + NULLFREE(buf); + return rc; +} + +static int32_t camd35_send_emm(EMM_PACKET *ep) +{ + uint8_t *buf; + struct s_client *cl = cur_client(); + + if(!camd35_tcp_connect(cl)) + { + return 0; + } + + cl->reader->card_status = CARD_INSERTED; // for udp + + if(!cs_malloc(&buf, ep->emmlen + 20 + 15)) + { + return -1; + } + + memset(buf, 0, 20); + memset(buf + 20, 0xFF, ep->emmlen + 15); + + buf[0] = 0x06; + buf[1] = ep->emmlen; + memcpy(buf + 10, ep->caid, 2); + memcpy(buf + 12, ep->provid, 4); + memcpy(buf + 20, ep->emm, ep->emmlen); + + int32_t rc = (camd35_send_without_timeout(cl, buf, 0) < 1) ? 0 : 1; + + NULLFREE(buf); + return rc; +} + +static int32_t camd35_recv_chk(struct s_client *client, uint8_t *dcw, int32_t *rc, uint8_t *buf, int32_t rc2 __attribute__((unused))) +{ + uint16_t idx; + static const char *typtext[] = { "ok", "invalid", "sleeping" }; + struct s_reader *rdr = client->reader; + + rdr->last_g = time(NULL); // last receive is now + + // reading CMD05 Emm request and set serial + if(buf[0] == 0x05 && buf[1] == 111) + { + //cs_log("CMD05: %s", cs_hexdump(1, buf, buf[1], tmp, sizeof(tmp))); + + rdr->nprov = 0; // reset if number changes on reader change + rdr->nprov = buf[47]; + rdr->caid = b2i(2, buf + 20); + + int32_t i; + for(i = 0; i < rdr->nprov; i++) + { + if(caid_is_betacrypt(rdr->caid) || caid_is_irdeto(rdr->caid)) + { + rdr->prid[i][0] = buf[48 + (i * 5)]; + memcpy(&rdr->prid[i][1], &buf[50 + (i * 5)], 3); + } + else + { + rdr->prid[i][2] = buf[48 + (i * 5)]; + rdr->prid[i][3] = buf[49 + (i * 5)]; + memcpy(&rdr->sa[i][0], &buf[50 + (i * 5)], 4); + } + } + + memcpy(rdr->hexserial, buf + 40, 6); + rdr->hexserial[6] = 0; + rdr->hexserial[7] = 0; + + if(cfg.getblockemmauprovid) + { + rdr->blockemm = 0; + rdr->blockemm |= (buf[128] == 1) ? 0 : EMM_GLOBAL; + rdr->blockemm |= (buf[129] == 1) ? 0 : EMM_SHARED; + rdr->blockemm |= (buf[130] == 1) ? 0 : EMM_UNIQUE; + rdr->blockemm |= (buf[127] == 1) ? 0 : EMM_UNKNOWN; + rdr->auprovid = b2i(4, buf + 12); + } + + cs_log("%s CMD05 AU request for caid: %04X auprovid: %06X", + rdr->label, rdr->caid, rdr->auprovid); + } + + bool rc_invalid = 0; + + if(buf[0] == 0x08 && ((rdr->ph.type == MOD_CONN_TCP && !cfg.c35_tcp_suppresscmd08) + || (rdr->ph.type == MOD_CONN_UDP && !cfg.c35_udp_suppresscmd08))) + { + if(buf[21] == 0xFF) + { + client->stopped = 2; // server says sleep + rdr->card_status = NO_CARD; + } + else + { + if(config_enabled(WITH_LB) && cfg.lb_mode) + { + rc_invalid = 1; + } + else + { + client->stopped = 1; // server says invalid + rdr->card_status = CARD_FAILURE; + } + } + + cs_log("%s CMD08 (%02X - %d) stop request by server (%s)", + rdr->label, buf[21], buf[21], typtext[client->stopped]); + } + + if(camd35_cacheex_recv_chk(client, buf)) + { + return -1; + } + + if(buf[0] == 55) // keepalive answer + { + return -1; + } + +#ifdef CS_CACHEEX_AIO + if(buf[0] == 0x50) + { + client->c35_extmode = 2; + + if(buf[12] != 1) // answer + camd35_send_extmode(client, true); + return -1; + } +#endif + // CMD44: old reject command introduced in mpcs + // keeping this for backward compatibility + if((buf[0] != 1) && (buf[0] != 0x44) && (buf[0] != 0x08) +#ifdef CS_CACHEEX_AIO + && (buf[0] != 0x51) +#endif + ) + { + return -1; + } + + idx = b2i(2, buf + 16); + camd35_cacheex_recv_ce1_cwc_info(client, buf, idx); + + *rc = ((buf[0] != 0x44) && (buf[0] != 0x08)); + + if(rc_invalid) + { + *rc = 2; // INVALID sent by CMD08 + } +#ifdef CS_CACHEEX_AIO + if(buf[0] == 0x51 || buf[0] == 0x54) // lg-flag + { + *rc = 0x86; + client->c35_extmode = 2; + } +#endif + + memcpy(dcw, buf + 20, 16); + + return idx; +} + +#ifdef CS_CACHEEX_AIO +void camd35_send_extmode(struct s_client *cl, bool answer) +{ + uint8_t rbuf[32]; // minimal size + memset(rbuf, 0, sizeof(rbuf)); + + rbuf[0] = 0x50; + rbuf[1] = 1; + rbuf[2] = 0; + if(answer) + rbuf[12] = 1; + + if(cl->reader) + { + if(camd35_tcp_connect(cl)) + { + camd35_send(cl, rbuf, 1); // send adds +20 + } + } + else if(check_client(cl) && cl->account) + { + camd35_send(cl, rbuf, 1); // send adds +20 + } +} +#endif + +/* + * module definitions + */ +#ifdef MODULE_CAMD35 +void module_camd35(struct s_module *ph) +{ + ph->ptab.nports = 1; + ph->ptab.ports[0].s_port = cfg.c35_port; + + ph->desc = "cs357x"; + ph->type = MOD_CONN_UDP; + ph->large_ecm_support = 1; + ph->listenertype = LIS_CAMD35UDP; + IP_ASSIGN(ph->s_ip, cfg.c35_srvip); + ph->s_handler = camd35_server; + ph->recv = camd35_recv; + ph->send_dcw = camd35_send_dcw; + ph->c_init = camd35_client_init; + ph->c_recv_chk = camd35_recv_chk; + ph->c_send_ecm = camd35_send_ecm; + ph->c_send_emm = camd35_send_emm; + ph->c_idle = camd35_idle; + camd35_cacheex_module_init(ph); + ph->num = R_CAMD35; +} +#endif + +#ifdef MODULE_CAMD35_TCP +void module_camd35_tcp(struct s_module *ph) +{ + ph->desc = "cs378x"; + ph->type = MOD_CONN_TCP; + ph->large_ecm_support = 1; + ph->listenertype = LIS_CAMD35TCP; + ph->ptab = cfg.c35_tcp_ptab; + IP_ASSIGN(ph->s_ip, cfg.c35_tcp_srvip); + ph->s_handler = camd35_server; + ph->recv = camd35_recv; + ph->send_dcw = camd35_send_dcw; + ph->c_init = camd35_client_init; + ph->c_recv_chk = camd35_recv_chk; + ph->c_send_ecm = camd35_send_ecm; + ph->c_send_emm = camd35_send_emm; + ph->c_idle = camd35_idle; + camd35_cacheex_module_init(ph); + ph->num = R_CS378X; +} +#endif + +#endif diff --git a/module-camd35.h b/module-camd35.h new file mode 100644 index 0000000..676e872 --- /dev/null +++ b/module-camd35.h @@ -0,0 +1,11 @@ +#ifndef MODULE_CAMD35_H_ +#define MODULE_CAMD35_H_ + +int32_t camd35_send(struct s_client *cl, uint8_t *buf, int32_t buflen); +int32_t camd35_send_without_timeout(struct s_client *cl, uint8_t *buf, int32_t buflen); +int32_t camd35_tcp_connect(struct s_client *cl); +#ifdef CS_CACHEEX_AIO +void camd35_send_extmode(struct s_client *cl, bool answer); +#endif + +#endif diff --git a/module-cccam-cacheex.c b/module-cccam-cacheex.c new file mode 100644 index 0000000..039f464 --- /dev/null +++ b/module-cccam-cacheex.c @@ -0,0 +1,1574 @@ +#define MODULE_LOG_PREFIX "cccam" + +#include "globals.h" +#include "oscam-array.h" + +#if defined(CS_CACHEEX) && defined(MODULE_CCCAM) + +#include "module-cacheex.h" +#include "module-cccam-data.h" +#include "module-cccam-cacheex.h" +#include "oscam-cache.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-string.h" +#include "oscam-chk.h" +#include "oscam-reader.h" +#ifdef CS_CACHEEX_AIO +#include "oscam-chk.h" +#include "oscam-config.h" +#endif + +#define CSP_HASH_SWAP(n) (((((uint32_t)(n) & 0xFF)) << 24) | \ + ((((uint32_t)(n) & 0xFF00)) << 8) | \ + ((((uint32_t)(n) & 0xFF0000)) >> 8) | \ + ((((uint32_t)(n) & 0xFF000000)) >> 24)) + +extern int32_t cc_cli_connect(struct s_client *cl); +extern int32_t cc_cmd_send(struct s_client *cl, uint8_t *buf, int32_t len, cc_msg_type_t cmd); + +#ifdef CS_CACHEEX_AIO +void cc_cacheex_feature_trigger_in(struct s_client *cl, uint8_t *buf) +{ + int32_t feature = 0; + int i = 0; + uint8_t filter_count; + uint8_t j, k, l, rc; + feature = buf[1] | (buf[0] << 8); + FTAB *lgonly_tab; + + // check client & cacheex-mode + if( + !check_client(cl) || + !( + (cl->typ == 'c' && cl->account->cacheex.mode > 0) || + (cl->typ == 'p' && cl->reader->cacheex.mode > 0) + ) + ) + { + return; + } + + switch(feature) + { + // set localgenerated only + case 1: + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + if(cfg.cacheex_lg_only_remote_settings || cl->account->cacheex.lg_only_remote_settings) + cl->account->cacheex.localgenerated_only = buf[4]; + else if(buf[4]) + cl->account->cacheex.localgenerated_only = buf[4]; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + if(cfg.cacheex_lg_only_remote_settings || cl->reader->cacheex.lg_only_remote_settings) + cl->reader->cacheex.localgenerated_only = buf[4]; + else if(buf[4]) + cl->reader->cacheex.localgenerated_only = buf[4]; + } + break; + // set localgenerated only caidtab + case 2: + filter_count = buf[i+4]; + i += 5; + + memset(&lgonly_tab, 0, sizeof(lgonly_tab)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + lgonly_tab = &cl->account->cacheex.lg_only_tab; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + lgonly_tab = &cl->reader->cacheex.lg_only_tab; + } + else + { + return; + } + + // remotesettings enabled - replace local settings + if(cfg.cacheex_lg_only_remote_settings || + ( + (cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.lg_only_remote_settings) + || (cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.lg_only_remote_settings) + ) + ) + { + ftab_clear(lgonly_tab); + + for(j = 0; j < filter_count; j++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + + d.caid = b2i(2, buf + i); + i += 2; + + d.nprids = 1; + d.prids[0] = NO_PROVID_VALUE; + + ftab_add(lgonly_tab, &d); + } + } + // remotesettings disabled - write additional remote-caids received + else + { + for(j = 0; j < filter_count; j++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + + d.caid = b2i(2, buf + i); + i += 2; + + d.nprids = 1; + d.prids[0] = NO_PROVID_VALUE; + + if(!chk_lg_only_cp(d.caid, d.prids[0], lgonly_tab)) + { + cs_log_dbg(D_CACHEEX, "%04X:%06X not found in local settings - adding them", d.caid, d.prids[0]); + + for(l = rc = 0; (!rc) && (l < lgonly_tab->nfilts); l++) + { + if(lgonly_tab->filts[l].caid == d.caid) + { + rc = 1; + + if(lgonly_tab->filts[l].nprids+1 <= CS_MAXPROV) + { + lgonly_tab->filts[l].prids[lgonly_tab->filts[l].nprids] = d.prids[0]; + lgonly_tab->filts[l].nprids++; + } + else + { + cs_log_dbg(D_CACHEEX, "error: cacheex_lg_only_tab -> max. number(%i) of providers reached", CS_MAXPROV); + } + } + } + if(!rc) + { + ftab_add(lgonly_tab, &d); + } + } + } + } + break; + // set cacheex_ecm_filter - extended + case 4: + filter_count = buf[i+4]; + i += 5; + + CECSPVALUETAB *filter; + memset(&filter, 0, sizeof(filter)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.allow_filter) + { + filter = &cl->account->cacheex.filter_caidtab; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.allow_filter) + { + filter = &cl->reader->cacheex.filter_caidtab; + } + else + { + return; + } + + cecspvaluetab_clear(filter); + + for(j = 0; j < filter_count; j++) + { + int32_t caid = -1, cmask = -1, provid = -1, srvid = -1; + CECSPVALUETAB_DATA d; + memset(&d, 0, sizeof(d)); + + caid = b2i(2, buf + i); + if(caid == 0xFFFF) caid = -1; + i += 2; + + cmask = b2i(2, buf + i); + if(cmask == 0xFFFF) cmask = -1; + i += 2; + + provid = b2i(3, buf + i); + if(provid == 0xFFFFFF) provid = -1; + i += 3; + + srvid = b2i(2, buf + i); + if(srvid == 0xFFFF) srvid = -1; + i += 2; + + if(caid > 0) + { + d.caid = caid; + d.cmask = cmask; + d.prid = provid; + d.srvid = srvid; + cecspvaluetab_add(filter, &d); + } + } + break; + // no push after + case 8: ; + CAIDVALUETAB *ctab; + memset(&ctab, 0, sizeof(ctab)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + ctab = &cl->account->cacheex.cacheex_nopushafter_tab; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + ctab = &cl->reader->cacheex.cacheex_nopushafter_tab; + } + else + { + return; + } + + filter_count = buf[i+4]; + i += 5; + + caidvaluetab_clear(ctab); + + for(j = 0; j < filter_count; j++) + { + uint16_t caid = 0, value = 0; + CAIDVALUETAB_DATA d; + memset(&d, 0, sizeof(d)); + + caid = b2i(2, buf + i); + if(caid == 0xFFFF) caid = -1; + i += 2; + + value = b2i(2, buf + i); + if(value == 0xFFFF) value = -1; + i += 2; + + if(caid > 0) + { + d.caid = caid; + d.value = value; + caidvaluetab_add(ctab, &d); + } + } + break; + // max_hop + case 16: + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.allow_maxhop) + { + cl->account->cacheex.maxhop = buf[4]; + cl->account->cacheex.maxhop_lg = buf[5]; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.allow_maxhop) + { + cl->reader->cacheex.maxhop = buf[4]; + cl->reader->cacheex.maxhop_lg = buf[5]; + } + break; + // aio-version + case 32: ; + uint16_t payload_size = b2i(2, buf + i + 2); + if(cl->typ == 'c' && cl->account->cacheex.mode > 0) + { + char *ofs = (char *)buf + i + 4; + memset(cl->account->cacheex.aio_version, 0, sizeof(cl->account->cacheex.aio_version)); + if(payload_size > 0) + { + size_t str_len = strnlen(ofs, payload_size); + if(str_len >= sizeof(cl->account->cacheex.aio_version)) + str_len = sizeof(cl->account->cacheex.aio_version) - 1; + memcpy(cl->account->cacheex.aio_version, ofs, str_len); + // sanitize: remove non-printable characters + size_t x; + for(x = 0; x < str_len; x++) + { + if(cl->account->cacheex.aio_version[x] < 0x20 || cl->account->cacheex.aio_version[x] > 0x7E) + { + cl->account->cacheex.aio_version[x] = '\0'; + break; + } + } + } + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode > 0) + { + char *ofs = (char *)buf + i + 4; + memset(cl->reader->cacheex.aio_version, 0, sizeof(cl->reader->cacheex.aio_version)); + if(payload_size > 0) + { + size_t str_len = strnlen(ofs, payload_size); + if(str_len >= sizeof(cl->reader->cacheex.aio_version)) + str_len = sizeof(cl->reader->cacheex.aio_version) - 1; + memcpy(cl->reader->cacheex.aio_version, ofs, str_len); + // sanitize: remove non-printable characters + size_t x; + for(x = 0; x < str_len; x++) + { + if(cl->reader->cacheex.aio_version[x] < 0x20 || cl->reader->cacheex.aio_version[x] > 0x7E) + { + cl->reader->cacheex.aio_version[x] = '\0'; + break; + } + } + } + } + break; + // lg_only_tab caid:prov1[,provN][;caid:prov] + case 64: ; + memset(&lgonly_tab, 0, sizeof(lgonly_tab)); + + if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) + { + lgonly_tab = &cl->account->cacheex.lg_only_tab; + } + else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + lgonly_tab = &cl->reader->cacheex.lg_only_tab; + } + else + { + return; + } + + filter_count = buf[i+4]; + i += 5; + + // remotesettings enabled - replace local settings + if(cfg.cacheex_lg_only_remote_settings || + ( + (cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.lg_only_remote_settings) + || (cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.lg_only_remote_settings) + || !lgonly_tab->nfilts + ) + ) + { + ftab_clear(lgonly_tab); + + for(j = 0; j < filter_count; j++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + + d.caid = b2i(2, buf + i); + i += 2; + + d.nprids = b2i(1, buf + i); + i += 1; + + for(k=0; k < d.nprids; k++) + { + d.prids[k] = b2i(3, buf + i); + i += 3; + } + ftab_add(lgonly_tab, &d); + + } + } + // remotesettings disabled - write additional remote-caid/provids received + else + { + for(j = 0; j < filter_count; j++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + + d.caid = b2i(2, buf + i); + i += 2; + + d.nprids = b2i(1, buf + i); + i += 1; + + for(k=0; k < d.nprids; k++) + { + d.prids[k] = b2i(3, buf + i); + i += 3; + + if(!chk_ident_filter(d.caid, d.prids[k], lgonly_tab)) + { + cs_log_dbg(D_CACHEEX, "%04X:%06X not found in local settings - adding them", d.caid, d.prids[k]); + + for(l = rc = 0; (!rc) && (l < lgonly_tab->nfilts); l++) + { + if(lgonly_tab->filts[l].caid == d.caid) + { + rc = 1; + + if(lgonly_tab->filts[l].nprids+1 <= CS_MAXPROV) + { + lgonly_tab->filts[l].prids[lgonly_tab->filts[l].nprids] = d.prids[k]; + lgonly_tab->filts[l].nprids++; + } + else + { + cs_log_dbg(D_CACHEEX, "error: cacheex_lg_only_tab -> max. number of providers reached"); + } + } + } + if(!rc) + { + ftab_add(lgonly_tab, &d); + } + } + } + } + } + break; + default: + return; + } +} + +void cc_cacheex_feature_trigger(struct s_client *cl, int32_t feature, uint8_t mode) +{ + // size: (feature-bitfield & mask: 2) + payload-size: 2 + feature-payload :x + uint16_t size = 2 + 2; + int i = 0; + uint8_t j; + uint8_t payload[MAX_ECM_SIZE-size]; + memset(payload, 0, sizeof(payload)); + + // check client & cacheex-mode + if(!check_client(cl)) + { + return; + } + + switch(feature) + { + FTAB *lgonly_tab; + // set localgenerated only + case 1: + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + i2b_buf(2, 1, payload + i); + i += 2; + + size += 1; + + // set payload + if(mode == 2) + { + if(cl->reader->cacheex.localgenerated_only_in) + payload[i] = cl->reader->cacheex.localgenerated_only_in; + else + payload[i] = cfg.cacheex_localgenerated_only_in; + } + else if(mode == 3) + { + if(cl->account->cacheex.localgenerated_only_in) + payload[i] = cl->account->cacheex.localgenerated_only_in; + else + payload[i] = cfg.cacheex_localgenerated_only_in; + } + + break; + // set localgenerated only caidtab; cx-aio < 9.2.6-04 + case 2: ; + if(mode == 2) + { + lgonly_tab = &cl->reader->cacheex.lg_only_in_tab; + if(!lgonly_tab->nfilts) + lgonly_tab = &cfg.cacheex_lg_only_in_tab; + } + else if(mode == 3) + { + lgonly_tab = &cl->account->cacheex.lg_only_in_tab; + if(!lgonly_tab->nfilts) + lgonly_tab = &cfg.cacheex_lg_only_in_tab; + } + else + { + return; + } + + size += (lgonly_tab->nfilts * 2 + 1); + if(size < 32) + size = 32; + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + if((lgonly_tab->nfilts * 2 + 1) > (int)sizeof(payload)) + { + cs_log_dbg(D_CACHEEX, "ERROR: too much localgenerated only caidtab-entries (max. 255)"); + return; + } + i2b_buf(2, (lgonly_tab->nfilts * 2 + 1), payload + i); // n * caid + ctnum + i += 2; + // set payload + if(lgonly_tab->nfilts > 255) + { + cs_log_dbg(D_CACHEEX, "ERROR: too much localgenerated only caidtab-entries (max. 255)"); + return; + } + payload[i] = lgonly_tab->nfilts; + i += 1; + + for(j = 0; j < lgonly_tab->nfilts; j++) + { + FILTER *d = &lgonly_tab->filts[j]; + if(d->caid) + { + i2b_buf(2, d->caid, payload + i); + i += 2; + } + else + { + continue; + } + } + break; + // cacchex_ecm_filter extendend + case 4: ; + CECSPVALUETAB *filter; + if(mode == 2) + { + filter = &cl->reader->cacheex.filter_caidtab; + // if not set, use global settings + if(cl->reader->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) + filter = &cfg.cacheex_filter_caidtab; + // if aio, use global aio settings + if(cl->reader->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && cl->cacheex_aio_checked && (cl->reader->cacheex.feature_bitfield & 4)) + filter = &cfg.cacheex_filter_caidtab_aio; + } + else if(mode == 3) + { + filter = &cl->account->cacheex.filter_caidtab; + // if not set, use global settings + if(cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) + filter = &cfg.cacheex_filter_caidtab; + // if aio, use global aio settings + if(cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && cl->cacheex_aio_checked && (cl->account->cacheex.feature_bitfield & 4)) + filter = &cfg.cacheex_filter_caidtab_aio; + } + else + { + return; + } + + size += (filter->cevnum * 9 + 1); + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + if((filter->cevnum * 9 + 1) > (int)sizeof(payload)) + { + cs_log_dbg(D_CACHEEX, "ERROR: to much cacheex_ecm_filter-entries (max. 63), only 30 default cccam-filters sent"); + return; + } + i2b_buf(2, (filter->cevnum * 9 + 1), payload + i); // n * (caid2,mask2,provid3,srvid2) + ctnum1 + i += 2; + // set payload + payload[i] = filter->cevnum; + i += 1; + + for(j = 0; j < filter->cevnum; j++) + { + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + if(d->caid) + { + i2b_buf(2, d->caid, payload + i); + i += 2; + } + if(d->cmask) + { + i2b_buf(2, d->cmask, payload + i); + } + i += 2; + + if(d->prid) + { + i2b_buf(3, d->prid, payload + i); + } + i += 3; + + if(d->srvid) + { + i2b_buf(2, d->srvid, payload + i); + } + i += 2; + } + break; + // no push after + case 8: ; + CAIDVALUETAB *ctab; + if(mode == 2) + { + ctab = &cl->reader->cacheex.cacheex_nopushafter_tab; + if(!ctab->cvnum) + ctab = &cfg.cacheex_nopushafter_tab; + } + else if(mode == 3) + { + ctab = &cl->account->cacheex.cacheex_nopushafter_tab; + if(!ctab->cvnum) + ctab = &cfg.cacheex_nopushafter_tab; + } + else + { + return; + } + + size += (ctab->cvnum * 4 + 1); + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + if((ctab->cvnum * 4 + 1) > (int)sizeof(payload)) + { + cs_log_dbg(D_CACHEEX, "ERROR: to much no push after caidtvalueab-entries (max. 255)"); + return; + } + i2b_buf(2, (ctab->cvnum * 4 + 1), payload + i); // n * (caid2+value2) + cvnum + i += 2; + // set payload + if(ctab->cvnum > 255) + { + cs_log_dbg(D_CACHEEX, "ERROR: to much no push after caidtvalueab-entries (max. 255)"); + return; + } + payload[i] = ctab->cvnum; + i += 1; + + for(j = 0; j < ctab->cvnum; j++) + { + CAIDVALUETAB_DATA *d = &ctab->cvdata[j]; + if(d->caid) + { + i2b_buf(2, d->caid, payload + i); + i += 2; + i2b_buf(2, d->value, payload + i); + i += 2; + } + else + { + continue; + } + } + break; + // max hop + case 16: + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + i2b_buf(2, 2, payload + i); + i += 2; + + size += 2; + + // set payload + if(mode == 2) + { + if(cl->reader->cacheex.maxhop) + payload[i] = cl->reader->cacheex.maxhop; + else + payload[i] = 0; + i += 1; + + if(cl->reader->cacheex.maxhop_lg) + payload[i] = cl->reader->cacheex.maxhop_lg; + else + payload[i] = 0; + } + else if(mode == 3) + { + if(cl->account->cacheex.maxhop) + payload[i] = cl->account->cacheex.maxhop; + else + payload[i] = 0; + i += 1; + + if(cl->account->cacheex.maxhop_lg) + payload[i] = cl->account->cacheex.maxhop_lg; + else + payload[i] = 0; + } + break; + // aio-version + case 32: ; + uint8_t token[CS_AIO_VERSION_LEN]; + memset(token, 0, sizeof(token)); + + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + // payload-size + i2b_buf(2, sizeof(token), payload + i); + i += 2; + + size +=sizeof(token); + // set payload + + snprintf((char *)token, sizeof(token), "%s", CS_AIO_VERSION); + uint8_t *ofs = payload + i; + memcpy(ofs, token, sizeof(token)); + break; + // lg_only_tab + case 64: ; + // bitfield + i2b_buf(2, feature, payload + i); + i += 2; + + if(mode == 2) + { + lgonly_tab = &cl->reader->cacheex.lg_only_in_tab; + if(!lgonly_tab->nfilts) + lgonly_tab = &cfg.cacheex_lg_only_in_tab; + } + else if(mode == 3) + { + lgonly_tab = &cl->account->cacheex.lg_only_in_tab; + if(!lgonly_tab->nfilts) + lgonly_tab = &cfg.cacheex_lg_only_in_tab; + } + else + { + return; + } + + char *cx_aio_ftab; + cx_aio_ftab = cxaio_ftab_to_buf(lgonly_tab); + if(cs_strlen(cx_aio_ftab) > 0 && cx_aio_ftab[0] != '\0') + { + size += cs_strlen(cx_aio_ftab) * sizeof(char); + + // payload-size + i2b_buf(2, cs_strlen(cx_aio_ftab), payload + i); + i += 2; + + // filter counter + payload[i] = lgonly_tab->nfilts; + i += 1; + + for(j=0; jtyp == 'c' && (cl->account->cacheex.mode == 2 ||cl->account->cacheex.mode == 1)) + { + cl->account->cacheex.feature_bitfield = field; + // flag 32 => aio-version + if(cl->account->cacheex.feature_bitfield & 32) + { + cc_cacheex_feature_trigger(cl, 32, 2); + } + } + + if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) + { + cl->reader->cacheex.feature_bitfield = field; + // flag 32 => aio-version + if(cl->reader->cacheex.feature_bitfield & 32) + { + cc_cacheex_feature_trigger(cl, 32, 3); + } + } + + if(cl->typ == 'c' && cl->account->cacheex.mode == 3) + { + struct s_auth *acc = cl->account; + if(acc) + { + acc->cacheex.feature_bitfield = field; + // process feature-specific actions based on feature_bitfield received + + // flag 1 => set localgenerated only flag + if(acc->cacheex.feature_bitfield & 1) + { + cc_cacheex_feature_trigger(cl, 1, 3); + } + // flag 2 => set localgenerated only caids flag + if(acc->cacheex.feature_bitfield & 2 && !(acc->cacheex.feature_bitfield & 64)) + { + cc_cacheex_feature_trigger(cl, 2, 3); + } + // flag 4 => set cacheex_ecm_filter (extended) + if(acc->cacheex.feature_bitfield & 4) + { + cc_cacheex_feature_trigger(cl, 4, 3); + } + // flag 8 => np push after caids + if(acc->cacheex.feature_bitfield & 8) + { + cc_cacheex_feature_trigger(cl, 8, 3); + } + // flag 16 => maxhop + if(acc->cacheex.feature_bitfield & 16) + { + cc_cacheex_feature_trigger(cl, 16, 3); + } + // flag 32 => aio-version + if(acc->cacheex.feature_bitfield & 32) + { + cc_cacheex_feature_trigger(cl, 32, 3); + } + // flag 64 => lg_only_tab + if(acc->cacheex.feature_bitfield & 64) + { + cc_cacheex_feature_trigger(cl, 64, 3); + } + } + else + { + cs_log_dbg(D_CACHEEX, "feature_bitfield save failed - cl, %s", username(cl)); + } + } + else if(cl->typ == 'p' && (cl->reader->cacheex.mode == 2 || cl->reader->cacheex.mode == 1)) + { + struct s_reader *rdr = cl->reader; + if(rdr) + { + rdr->cacheex.feature_bitfield = field; + // process feature-specific actions + + // flag 1 => set localgenerated_only; cause of rdr->cacheex.localgenerated_only_in is set + if(rdr->cacheex.feature_bitfield & 1) + { + cc_cacheex_feature_trigger(cl, 1, 2); + } + + // flag 2 => set localgenerated_only_caidtab; cause of rdr->cacheex.localgenerated_only_in_caidtab is set + if(rdr->cacheex.feature_bitfield & 2 && !(rdr->cacheex.feature_bitfield & 64)) + { + cc_cacheex_feature_trigger(cl, 2, 2); + } + + // flag 4 => set cacchex_ecm_filter extendend + if(rdr->cacheex.feature_bitfield & 4) + { + cc_cacheex_feature_trigger(cl, 4, 2); + } + + // flag 8 => np push after caids + if(rdr->cacheex.feature_bitfield & 8) + { + cc_cacheex_feature_trigger(cl, 8, 2); + } + // flag 16 => maxhop + if(rdr->cacheex.feature_bitfield & 16) + { + cc_cacheex_feature_trigger(cl, 16, 2); + } + // flag 32 => aio-version + if(rdr->cacheex.feature_bitfield & 32) + { + cc_cacheex_feature_trigger(cl, 32, 2); + } + // flag 64 => lg_only_tab + if(rdr->cacheex.feature_bitfield & 64) + { + cc_cacheex_feature_trigger(cl, 64, 2); + } + } + else + { + cs_log_dbg(D_CACHEEX, "feature_bitfield save failed - rdr, %s", username(cl)); + } + } +} + +void cc_cacheex_feature_request_reply(struct s_client *cl) +{ + int32_t size = 2; + uint8_t rbuf[size]; + + i2b_buf(2, CACHEEX_FEATURES, rbuf); + cc_cmd_send(cl, rbuf, size, MSG_CACHE_FEATURE_EXCHANGE_REPLY); +} + +void cc_cacheex_feature_request(struct s_client *cl) +{ + int32_t size = 2; + uint8_t rbuf[2]; + i2b_buf(2, CACHEEX_FEATURES, rbuf); + cc_cmd_send(cl, rbuf, size, MSG_CACHE_FEATURE_EXCHANGE); +} +#endif + +void cc_cacheex_filter_out(struct s_client *cl) +{ + struct s_reader *rdr = (cl->typ == 'c') ? NULL : cl->reader; + int i = 0, j; + CECSPVALUETAB *filter; + int32_t size = 482; // minimal size, keep it <= 512 for max UDP packet size without fragmentation + uint8_t buf[482]; + memset(buf, 0, sizeof(buf)); + + if(rdr && (rdr->cacheex.mode == 2 +#ifdef CS_CACHEEX_AIO + || rdr->cacheex.mode == 1 +#endif + )) // mode == 2 send filters from rdr + { + filter = &rdr->cacheex.filter_caidtab; +#ifdef CS_CACHEEX_AIO + // if not set, use global settings + if(rdr->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) + filter = &cfg.cacheex_filter_caidtab; +#endif + } + else if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3) // mode == 3 send filters from acc + { + filter = &cl->account->cacheex.filter_caidtab; +#ifdef CS_CACHEEX_AIO + // if not set, use global settings + if(cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) + filter = &cfg.cacheex_filter_caidtab; +#endif + } + else + { + return; + } + + i2b_buf(2, filter->cevnum, buf + i); + i += 2; + + int32_t max_filters = 30; + for(j = 0; j < max_filters; j++) + { + if(filter->cevnum > j) + { + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + i2b_buf(4, d->caid, buf + i); + } + i += 4; + } + + for(j = 0; j < max_filters; j++) + { + if(filter->cevnum > j) + { + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + i2b_buf(4, d->cmask, buf + i); + } + i += 4; + } + + for(j = 0; j < max_filters; j++) + { + if(filter->cevnum > j) + { + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + i2b_buf(4, d->prid, buf + i); + } + i += 4; + } + + for(j = 0; j < max_filters; j++) + { + if(filter->cevnum > j) + { + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + i2b_buf(4, d->srvid, buf + i); + } + i += 4; + } + + cs_log_dbg(D_CACHEEX, "cacheex: sending push filter request to %s", username(cl)); + cc_cmd_send(cl, buf, size, MSG_CACHE_FILTER); +} + +void cc_cacheex_filter_in(struct s_client *cl, uint8_t *buf) +{ + struct s_reader *rdr = (cl->typ == 'c') ? NULL : cl->reader; + int i = 0, j; + int32_t caid, cmask, provid, srvid; + CECSPVALUETAB *filter; + + // mode == 2 write filters to acc + if(cl->typ == 'c' && cl->account && (cl->account->cacheex.mode == 2 +#ifdef CS_CACHEEX_AIO + || cl->account->cacheex.mode == 1 +#endif + ) && cl->account->cacheex.allow_filter == 1) + { + filter = &cl->account->cacheex.filter_caidtab; + } + else if(rdr && rdr->cacheex.mode == 3 && rdr->cacheex.allow_filter == 1) // mode == 3 write filters to rdr + { + filter = &rdr->cacheex.filter_caidtab; + } + else + { + return; + } + + cecspvaluetab_clear(filter); + i += 2; + + int32_t max_filters = 30; + for(j = 0; j < max_filters; j++) + { + caid = b2i(4, buf + i); + if(caid > 0) + { + CECSPVALUETAB_DATA d; + memset(&d, 0, sizeof(d)); + d.caid = b2i(4, buf + i); + cecspvaluetab_add(filter, &d); + } + i += 4; + } + + for(j = 0; j < max_filters; j++) + { + cmask = b2i(4, buf + i); + if(j < filter->cevnum) + { + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + d->cmask = cmask; + } + i += 4; + } + + for(j = 0; j < max_filters; j++) + { + provid = b2i(4, buf + i); + if(j < filter->cevnum) + { + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + d->prid = provid; + } + i += 4; + } + + for(j = 0; j < max_filters; j++) + { + srvid = b2i(4, buf + i); + if(j < filter->cevnum) + { + CECSPVALUETAB_DATA *d = &filter->cevdata[j]; + d->srvid = srvid; + } + i += 4; + } + + cs_log_dbg(D_CACHEEX, "cacheex: received push filter request from %s", username(cl)); +} + +static int32_t cc_cacheex_push_chk(struct s_client *cl, struct ecm_request_t *er) +{ + struct cc_data *cc = cl->cc; + if(chk_is_null_nodeid(cc->peer_node_id)) + { + cs_log_dbg(D_CACHEEX, "cacheex: NO peer_node_id got yet, skip!"); + return 0; + } + + if( + ll_count(er->csp_lastnodes) >= cacheex_maxhop(cl) // check max 10 nodes to push +#ifdef CS_CACHEEX_AIO + && (!er->localgenerated || (er->localgenerated && (ll_count(er->csp_lastnodes) >= cacheex_maxhop_lg(cl)))) // check maxhop_lg if cw is lg-flagged +#endif + ) + { +#ifdef CS_CACHEEX_AIO + cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes(non-lg) or reached %d nodes(lg), no push", cacheex_maxhop(cl), cacheex_maxhop_lg(cl)); +#else + cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes, no push", cacheex_maxhop(cl)); +#endif + return 0; + } + + uint8_t *remote_node = cc->peer_node_id; + + // search existing peer nodes + LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0); + uint8_t *node; + while((node = ll_li_next(li))) + { + cs_log_dbg(D_CACHEEX, "cacheex: check node %" PRIu64 "X == %" PRIu64 "X ?", + cacheex_node_id(node), cacheex_node_id(remote_node)); + + if(memcmp(node, remote_node, 8) == 0) + { + break; + } + } + ll_li_destroy(li); + + // node found, so we got it from there, do not push + if(node) + { + cs_log_dbg(D_CACHEEX, "cacheex: node %" PRIu64 "X found in list => skip push!", cacheex_node_id(node)); + return 0; + } + + if(!cl->cc) + { + if(cl->reader && !cl->reader->tcp_connected) + { + cc_cli_connect(cl); + } + } + + if(!cc || !cl->udp_fd) + { + cs_log_dbg(D_CACHEEX, "cacheex: not connected %s -> no push", username(cl)); + return 0; + } + + // check if cw is already pushed + if(check_is_pushed(er->cw_cache, cl)) + { + return 0; + } + + return 1; +} + +static int32_t cc_cacheex_push_out(struct s_client *cl, struct ecm_request_t *er) +{ + int8_t rc = (er->rc < E_NOTFOUND) ? E_FOUND : er->rc; + + if(rc != E_FOUND && rc != E_UNHANDLED) + { + return -1; // Maybe later we could support other rcs + } + + if(cl->reader) + { + if(!cl->reader->tcp_connected) + { + cc_cli_connect(cl); + } + } + + struct cc_data *cc = cl->cc; + if(!cc || !cl->udp_fd) + { + cs_log_dbg(D_CACHEEX, "cacheex: not connected %s -> no push", username(cl)); + return (-1); + } + + uint32_t size = sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw) + sizeof(uint8_t) + +#ifdef CS_CACHEEX_AIO + (ll_count(er->csp_lastnodes) + 1) * 8 + sizeof(uint8_t); +#else + (ll_count(er->csp_lastnodes) + 1) * 8; +#endif + + uint8_t *buf; + if(!cs_malloc(&buf, size + 20)) // camd35_send() adds +20 + { + return -1; + } + + // build ecm message + //buf[0] = er->caid >> 8; + //buf[1] = er->caid & 0xff; + //buf[2] = er->prid >> 24; + //buf[3] = er->prid >> 16; + //buf[4] = er->prid >> 8; + //buf[5] = er->prid & 0xff; + //buf[10] = er->srvid >> 8; + //buf[11] = er->srvid & 0xff; + buf[12] = (sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw)) & 0xff; + buf[13] = (sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw)) >> 8; + //buf[12] = 0; + //buf[13] = 0; + buf[14] = rc; + + i2b_buf(2, er->caid, buf + 0); + i2b_buf(4, er->prid, buf + 2); + i2b_buf(2, er->srvid, buf + 10); + + if(er->cwc_cycletime && er->cwc_next_cw_cycle < 2) + { + buf[18] = er->cwc_cycletime; // contains cwc stage3 cycletime + + if(er->cwc_next_cw_cycle == 1) + { + buf[18] = (buf[18] | 0x80); // set bit 8 to high + } + + if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) + { + cl->account->cwc_info++; + } + else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) + { + cl->cwc_info++; + } + + cs_log_dbg(D_CWC, "CWC (CE) push to %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", + username(cl), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); + } + + buf[19] = er->ecm[0] != 0x80 && er->ecm[0] != 0x81 ? 0 : er->ecm[0]; + + uint8_t *ofs = buf + 20; + + // write oscam ecmd5 + memcpy(ofs, er->ecmd5, sizeof(er->ecmd5)); // 16 + ofs += sizeof(er->ecmd5); + + // write csp hashcode + i2b_buf(4, CSP_HASH_SWAP(er->csp_hash), ofs); + ofs += 4; + + // write cw + memcpy(ofs, er->cw, sizeof(er->cw)); // 16 + ofs += sizeof(er->cw); + + // write node count + *ofs = ll_count(er->csp_lastnodes) + 1; + ofs++; + + // write own node + memcpy(ofs, cc->node_id, 8); + ofs += 8; + + // write other nodes + LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0); + uint8_t *node; + while((node = ll_li_next(li))) + { + memcpy(ofs, node, 8); + ofs += 8; + } + ll_li_destroy(li); + +#ifdef CS_CACHEEX_AIO + // add localgenerated cw-flag + if(er->localgenerated) + { + *ofs = 1; + } + else + { + *ofs = 0xFF; + } +#endif + + int32_t res = cc_cmd_send(cl, buf, size + 20, MSG_CACHE_PUSH); + if(res > 0) // cache-ex is pushing out, so no receive but last_g should be updated otherwise disconnect! + { + if(cl->reader) + { + cl->reader->last_s = cl->reader->last_g = time((time_t *)0); // correct + } + + if(cl) + { + cl->last = time(NULL); + } + } + + NULLFREE(buf); + return res; +} + +void cc_cacheex_push_in(struct s_client *cl, uint8_t *buf) +{ + struct cc_data *cc = cl->cc; + ECM_REQUEST *er; + + if(!cc) + { + return; + } + + if(cl->reader) + { + cl->reader->last_s = cl->reader->last_g = time((time_t *)0); + } + + if(cl) + { + cl->last = time(NULL); + } + + int8_t rc = buf[14]; + if(rc != E_FOUND && rc != E_UNHANDLED) // Maybe later we could support other rcs + { + return; + } + + uint16_t size = buf[12] | (buf[13] << 8); + if(size != sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw)) + { + cs_log_dbg(D_CACHEEX, "cacheex: %s received old cash-push format! data ignored!", username(cl)); + return; + } + + if(!(er = get_ecmtask())) + { + return; + } + + er->caid = b2i(2, buf + 0); + er->prid = b2i(4, buf + 2); + er->srvid = b2i(2, buf + 10); + er->ecm[0] = buf[19] != 0x80 && buf[19] != 0x81 ? 0 : buf[19]; // odd/even byte, usefull to send it over CSP and to check cw for swapping + er->rc = rc; + + er->ecmlen = 0; + + if(buf[18]) + { + if(buf[18] & (0x01 << 7)) + { + er->cwc_cycletime = (buf[18] & 0x7F); // remove bit 8 to get cycletime + er->cwc_next_cw_cycle = 1; + } + else + { + er->cwc_cycletime = buf[18]; + er->cwc_next_cw_cycle = 0; + } + } + +#ifndef CS_CACHEEX_AIO + if (er->cwc_cycletime && er->cwc_next_cw_cycle < 2) + { + if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) + { + cl->account->cwc_info++; + } + else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) + { + cl->cwc_info++; + } + + cs_log_dbg(D_CWC, "CWC (CE) received from %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", + username(cl), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); + } +#endif + + uint8_t *ofs = buf + 20; + + // Read ecmd5 + memcpy(er->ecmd5, ofs, sizeof(er->ecmd5)); // 16 + ofs += sizeof(er->ecmd5); + + if(!check_cacheex_filter(cl, er)) + { + return; + } + +#ifdef CS_CACHEEX_AIO + // check cacheex_ecm_filter + if(check_client(cl) && cl->typ == 'p' && cl->reader && cl->reader->cacheex.mode == 2 + && ( (cl->reader->cacheex.filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cl->reader->cacheex.filter_caidtab)) // reader cacheex_ecm_filter not matching if set + || (cl->reader->cacheex.filter_caidtab.cevnum == 0 && (cl->reader->cacheex.feature_bitfield & 4) && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab_aio)) // global cacheex_ecm_filter_aio not matching if set + || (cl->reader->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab)) // global cacheex_ecm_filter not matching if set + ) + ) + { + cs_log_dbg(D_CACHEEX, "cacheex: received cache not matching cacheex_ecm_filter => pushing filter again"); + cc_cacheex_filter_out(cl); // get cache != cacheex_ecm_filter, send filter again - remote restarted + if(cl->reader->cacheex.feature_bitfield & 4) + cc_cacheex_feature_trigger(cl, 4, 2); + free_push_in_ecm(er); + return; + } + + if(check_client(cl) && cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3 + && ( (cl->account->cacheex.filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cl->account->cacheex.filter_caidtab)) // account cacheex_ecm_filter not matching if set + || (cl->account->cacheex.filter_caidtab.cevnum == 0 && (cl->account->cacheex.feature_bitfield & 4) && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab_aio)) // global cacheex_ecm_filter_aio not matching if set + || (cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab)) // global cacheex_ecm_filter not matching if set + ) + ) + { + cs_log_dbg(D_CACHEEX, "cacheex: received cache not matching cacheex_ecm_filter => pushing filter again"); + cc_cacheex_filter_out(cl); // get cache != cacheex_ecm_filter, send filter again - remote restarted + if(cl->account->cacheex.feature_bitfield & 4) + cc_cacheex_feature_trigger(cl, 4, 3); + free_push_in_ecm(er); + return; + } +#endif + + // Read csp_hash + er->csp_hash = CSP_HASH_SWAP(b2i(4, ofs)); + ofs += 4; + + // Read cw + memcpy(er->cw, ofs, sizeof(er->cw)); // 16 + ofs += sizeof(er->cw); + + // Read lastnode count + uint8_t count = *ofs; + ofs++; + +#ifndef CS_CACHEEX_AIO + // check max nodes + if(count > cacheex_maxhop(cl)) + { + cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", + (int32_t)count, cacheex_maxhop(cl), username(cl)); + + NULLFREE(er); + return; + } +#endif + + cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes %s", (int32_t)count, username(cl)); + + // Read lastnodes + uint8_t *data; + if (er) + { + er->csp_lastnodes = ll_create("csp_lastnodes"); + } + + while(count) + { + if(!cs_malloc(&data, 8)) + { + break; + } + + memcpy(data, ofs, 8); + ofs += 8; + ll_append(er->csp_lastnodes, data); + count--; + + cs_log_dbg(D_CACHEEX, "cacheex: received node %" PRIu64 "X %s", cacheex_node_id(data), username(cl)); + } + +#ifdef CS_CACHEEX_AIO + if(b2i(1, ofs) == 1) + { + er->localgenerated = 1; + cs_log_dbg(D_CACHEEX, "cacheex: received ECM with localgenerated flag %04X@%06X:%04X %s", er->caid, er->prid, er->srvid, username(cl)); + + //check max nodes for lg flagged cw: + if(ll_count(er->csp_lastnodes) > cacheex_maxhop_lg(cl)) + { + cs_log_dbg(D_CACHEEX, "cacheex: received (lg) %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop_lg(cl), username(cl)); + free_push_in_ecm(er); + return; + } + } + // without localgenerated flag + else + { + //check max nodes: + if(ll_count(er->csp_lastnodes) > cacheex_maxhop(cl)) + { + cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop(cl), username(cl)); + free_push_in_ecm(er); + return; + } + + if( + (cl->typ == 'p' && cl->reader && cl->reader->cacheex.mode == 2 && !chk_srvid_localgenerated_only_exception(er) // cx2 + && ( + // !aio + (cl->cacheex_aio_checked && !cl->reader->cacheex.feature_bitfield + && ( + !cfg.cacheex_lg_only_in_aio_only && !cl->reader->cacheex.lg_only_in_aio_only + && (cfg.cacheex_localgenerated_only_in || cl->reader->cacheex.localgenerated_only_in || ((cl->reader->cacheex.feature_bitfield & 64) && (chk_lg_only(er, &cl->reader->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab))) || ( !(cl->reader->cacheex.feature_bitfield & 64) && (chk_ctab_ex(er->caid, &cl->reader->cacheex.localgenerated_only_in_caidtab) || chk_ctab_ex(er->caid, &cfg.cacheex_localgenerated_only_in_caidtab)))) + ) + ) + || + // aio + (cl->cacheex_aio_checked && cl->reader->cacheex.feature_bitfield + && ( + cfg.cacheex_localgenerated_only_in || cl->reader->cacheex.localgenerated_only_in || ((cl->reader->cacheex.feature_bitfield & 64) && (chk_lg_only(er, &cl->reader->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab))) || ( !(cl->reader->cacheex.feature_bitfield & 64) && (chk_ctab_ex(er->caid, &cl->reader->cacheex.localgenerated_only_in_caidtab) || chk_ctab_ex(er->caid, &cfg.cacheex_localgenerated_only_in_caidtab))) + ) + ) + ) + ) + || + (cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3 && !chk_srvid_localgenerated_only_exception(er) // cx3 + && ( + // !aio + (cl->cacheex_aio_checked && !cl->account->cacheex.feature_bitfield + && ( + !cfg.cacheex_lg_only_in_aio_only && !cl->account->cacheex.lg_only_in_aio_only + && (cfg.cacheex_localgenerated_only_in || cl->account->cacheex.localgenerated_only_in || ((cl->account->cacheex.feature_bitfield & 64) && (chk_lg_only(er, &cl->account->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab))) || ( !(cl->account->cacheex.feature_bitfield & 64) && (chk_ctab_ex(er->caid, &cl->account->cacheex.localgenerated_only_in_caidtab) || chk_ctab_ex(er->caid, &cfg.cacheex_localgenerated_only_in_caidtab)))) + ) + ) + || + // aio + (cl->cacheex_aio_checked && cl->account->cacheex.feature_bitfield + && ( + cfg.cacheex_localgenerated_only_in || cl->account->cacheex.localgenerated_only_in || ((cl->account->cacheex.feature_bitfield & 64) && (chk_lg_only(er, &cl->account->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab))) || ( !(cl->account->cacheex.feature_bitfield & 64) && (chk_ctab_ex(er->caid, &cl->account->cacheex.localgenerated_only_in_caidtab) || chk_ctab_ex(er->caid, &cfg.cacheex_localgenerated_only_in_caidtab))) + ) + ) + ) + ) + ) + { + cs_log_dbg(D_CACHEEX, "cacheex: drop ECM without localgenerated flag %04X@%06X:%04X %s", er->caid, er->prid, er->srvid, username(cl)); + free_push_in_ecm(er); + return; + } + } +#endif + + // for compatibility: add peer node if no node received + if(!ll_count(er->csp_lastnodes)) + { + if(!cs_malloc(&data, 8)) + { + return; + } + + memcpy(data, cc->peer_node_id, 8); + ll_append(er->csp_lastnodes, data); + cs_log_dbg(D_CACHEEX, "cacheex: added missing remote node id %" PRIu64 "X", cacheex_node_id(data)); + } + +#ifdef CS_CACHEEX_AIO + if (er->cwc_cycletime && er->cwc_next_cw_cycle < 2) + { + if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) + { + cl->account->cwc_info++; + } + else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) + { + cl->cwc_info++; + } + + cs_log_dbg(D_CWC, "CWC (CE) received from %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", + username(cl), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); + } +#endif + + cacheex_add_to_cache(cl, er); +} + +void cc_cacheex_module_init(struct s_module *ph) +{ + ph->c_cache_push = cc_cacheex_push_out; + ph->c_cache_push_chk = cc_cacheex_push_chk; +} + +#endif diff --git a/module-cccam-cacheex.h b/module-cccam-cacheex.h new file mode 100644 index 0000000..100e789 --- /dev/null +++ b/module-cccam-cacheex.h @@ -0,0 +1,22 @@ +#ifndef MODULE_CCCAM_CACHEEX_H_ +#define MODULE_CCCAM_CACHEEX_H_ + +#ifdef CS_CACHEEX +void cc_cacheex_filter_out(struct s_client *cl); +void cc_cacheex_filter_in(struct s_client *cl, uint8_t *buf); +void cc_cacheex_push_in(struct s_client *cl, uint8_t *buf); +void cc_cacheex_module_init(struct s_module *ph); +#ifdef CS_CACHEEX_AIO +void cc_cacheex_feature_request(struct s_client *cl); +void cc_cacheex_feature_request_reply(struct s_client *cl); +void cc_cacheex_feature_request_save(struct s_client *cl, uint8_t *buf); +void cc_cacheex_feature_trigger_in(struct s_client *cl, uint8_t *buf); +#endif +#else +static inline void cc_cacheex_filter_out(struct s_client *UNUSED(cl)) { } +static inline void cc_cacheex_filter_in(struct s_client *UNUSED(cl), uint8_t *UNUSED(buf)) { } +static inline void cc_cacheex_push_in(struct s_client *UNUSED(cl), uint8_t *UNUSED(buf)) { } +static inline void cc_cacheex_module_init(struct s_module *UNUSED(ph)) { } +#endif + +#endif diff --git a/module-cccam-data.h b/module-cccam-data.h new file mode 100644 index 0000000..cdcee74 --- /dev/null +++ b/module-cccam-data.h @@ -0,0 +1,255 @@ +/* + * Created on: 23.04.2010 + * Author: alno + */ +#ifndef MODULE_CCCAM_DATA_H_ +#define MODULE_CCCAM_DATA_H_ + +#include "cscrypt/rc6.h" +#include "cscrypt/idea.h" + +#define CAID_KEY 0x20 + +#define CC_MAXMSGSIZE 0x400 // by Project::Keynation: Buffer size is limited on "O" CCCam to 1024 bytes +#define CC_MAX_PROV 32 +#define SWAPC(X, Y) do { char p; p = *X; *X = *Y; *Y = p; } while(0) + +#if (defined(WIN32) || defined(__CYGWIN__)) && !defined(MSG_WAITALL) +#define MSG_WAITALL 0 +#endif + +#define MINIMIZE_NONE 0 +#define MINIMIZE_HOPS 1 +#define MINIMIZE_CAID 2 +#define MINIMIZE_TRANSPARENT 3 + +#define CCCAM_MODE_NOTINIT 0 +#define CCCAM_MODE_NORMAL 1 +#define CCCAM_MODE_SHUTDOWN 0xFF + +#define QUITERROR 1 + +#define MIN_RATING -25 +#define MAX_RATING 25 + +#define HOP_RATING 5 + +typedef enum +{ + DECRYPT, ENCRYPT +} cc_crypt_mode_t; + +typedef enum +{ + MSG_CLI_DATA = 0, + MSG_CW_ECM = 1, + MSG_EMM_ACK = 2, + MSG_CARD_REMOVED = 4, + MSG_CMD_05 = 5, + MSG_KEEPALIVE = 6, + MSG_NEW_CARD = 7, + MSG_SRV_DATA = 8, + MSG_CMD_0A = 0x0a, + MSG_CMD_0B = 0x0b, + MSG_CMD_0C = 0x0c, // CCCam 2.2.x fake client checks + MSG_CMD_0D = 0x0d, // " + MSG_CMD_0E = 0x0e, // " + MSG_NEW_CARD_SIDINFO = 0x0f, + MSG_SLEEPSEND = 0x80, // Sleepsend support + MSG_CACHE_PUSH = 0x81, // CacheEx Cache-Push In/Out + MSG_CACHE_FILTER = 0x82, // CacheEx Cache-Filter Request +#ifdef CS_CACHEEX_AIO + MSG_CACHE_FEATURE_EXCHANGE = 0x83, // CacheEx feature-exchange + MSG_CACHE_FEATURE_EXCHANGE_REPLY = 0x84, // CacheEx feature-exchange-reply + MSG_CACHE_FEATURE_TRIGGER = 0x85, // CacheEx feature-trigger + MSG_CW_ECM_LGF = 0x86, // oscam lg-flagged CW +#endif + MSG_CW_NOK1 = 0xfe, // Node no more available + MSG_CW_NOK2 = 0xff, // No decoding + MSG_NO_HEADER = 0xffff +} cc_msg_type_t; + +struct cc_crypt_block +{ + uint8_t keytable[256]; + uint8_t state; + uint8_t counter; + uint8_t sum; +}; + +struct cc_srvid +{ + uint16_t sid; + uint16_t chid; + uint8_t ecmlen; +}; + +struct cc_srvid_block +{ + uint16_t sid; + uint16_t chid; + uint8_t ecmlen; + time_t blocked_till; +}; + +struct cc_provider +{ + uint32_t prov; // provider + uint8_t sa[4]; // shared address +}; + +typedef enum +{ + CT_LOCALCARD = 1, + CT_CARD_BY_SERVICE_READER = 2, + CT_CARD_BY_SERVICE_USER = 3, + CT_CARD_BY_CAID1 = 4, + CT_CARD_BY_CAID2 = 5, + CT_CARD_BY_CAID3 = 6, + CT_REMOTECARD = 10 +} cc_card_type; + +struct cc_card +{ + uint32_t id; // cccam card (share) id - reader + uint32_t remote_id; + uint16_t caid; + uint8_t hop; + uint8_t reshare; + uint8_t hexserial[8]; // card serial (for au) + LLIST *providers; // providers (struct cc_provider) + LLIST *badsids; // sids that have failed to decode (struct cc_srvid_block) + LLIST *goodsids; // sids that could decoded (struct cc_srvid) + LLIST *remote_nodes; // remote note id, 8 bytes + struct s_reader *origin_reader; + uint32_t origin_id; + cc_card_type card_type; + struct s_sidtab *sidtab; // pointer to sidtab entry if card_type = CT_CARD_BY_SERVICE + uint64_t grp; + uint8_t rdr_reshare; + SIDTABBITS sidtabno; + time_t timeout; + uint8_t is_ext; + int8_t rating; +}; + +typedef enum +{ + MODE_UNKNOWN = 0, + MODE_PLAIN = 1, + MODE_AES = 2, + MODE_CC_CRYPT = 3, + MODE_RC4_CRYPT = 4, + MODE_LEN0 = 5, +} cc_cmd05_mode; + +typedef enum +{ + MODE_CMD_0x0C_NONE = 0, + MODE_CMD_0x0C_RC6 = 1, + MODE_CMD_0x0C_RC4 = 2, + MODE_CMD_0x0C_CC_CRYPT = 3, + MODE_CMD_0x0C_AES = 4, + MODE_CMD_0x0C_IDEA = 5, +} cc_cmd0c_mode; + +struct cc_extended_ecm_idx +{ + uint8_t send_idx; + uint16_t ecm_idx; + struct cc_card *card; + struct cc_srvid srvid; + uint8_t free_card; + struct timeb tps; + uint32_t cccam_id; +}; + +struct cc_data +{ + uint8_t g_flag; + char *prefix; + + struct cc_crypt_block block[2]; // crypto state blocks + + uint8_t node_id[8]; // client node id + uint8_t peer_node_id[8]; // server node id + uint8_t peer_version[8]; // server version + uint8_t dcw[16]; // control words + uint8_t cmd0b_aeskey[16]; + uint8_t cmd05_aeskey[16]; + struct cc_crypt_block cmd05_cryptkey; + + uint8_t is_oscam_cccam; + uint8_t cmd05_active; + int32_t cmd05_data_len; + uint8_t cmd05_data[256]; + cc_cmd05_mode cmd05_mode; + int32_t cmd05_offset; + + cc_cmd0c_mode cmd0c_mode; + struct cc_crypt_block cmd0c_cryptkey; + RC6KEY cmd0c_RC6_cryptkey; + AES_KEY cmd0c_AES_key; + IDEA_KEY_SCHEDULE cmd0c_IDEA_dkey; + + uint8_t receive_buffer[CC_MAXMSGSIZE]; + uint8_t send_buffer[CC_MAXMSGSIZE]; + + LLIST *cards; // cards list + + int32_t max_ecms; + int32_t ecm_counter; + int32_t card_added_count; + int32_t card_removed_count; + uint8_t just_logged_in; // true for checking NOK direct after login + uint8_t key_table; // key for CMD 0B + + LLIST *pending_emms; // pending emm list + + uint32_t recv_ecmtask; + + struct cc_card *last_emm_card; + int32_t server_ecm_pending; // initialized by server + uint16_t server_ecm_idx; + + CS_MUTEX_LOCK lockcmd; + int8_t ecm_busy; + CS_MUTEX_LOCK cards_busy; + struct timeb ecm_time; + uint8_t last_msg; + uint8_t cmd05NOK; + + char remote_version[7]; + char remote_build[7]; + char remote_oscam[200]; + + uint8_t cccam220; + uint32_t remote_build_nr; + uint8_t sleepsend; + + // Extended Mode for SPECIAL clients: + uint8_t extended_mode; + LLIST *extended_ecm_idx; + + // multics detection + int8_t multics_mode; + int8_t multics_version[2]; + + // stats: + int32_t num_hop1; + int32_t num_hop2; + int32_t num_hopx; + + int32_t num_reshare0; + int32_t num_reshare1; + int32_t num_reshare2; + int32_t num_resharex; + + char *nok_message; + +#ifdef CS_CACHEEX_AIO + uint8_t extended_lg_flagged_cws; +#endif +}; + +#endif diff --git a/module-cccam.c b/module-cccam.c new file mode 100644 index 0000000..0b66509 --- /dev/null +++ b/module-cccam.c @@ -0,0 +1,5140 @@ +#define MODULE_LOG_PREFIX "cccam" + +#include "globals.h" + +#ifdef MODULE_CCCAM + +#include "cscrypt/md5.h" +#include "cscrypt/sha1.h" +#include "module-cacheex.h" +#include "module-cccam.h" +#include "module-cccam-data.h" +#include "module-cccam-cacheex.h" +#include "module-cccshare.h" +#include "oscam-chk.h" +#include "oscam-cache.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-emm.h" +#include "oscam-failban.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" + +// Mode names for CMD_05 command +static const char *cmd05_mode_name[] = { "UNKNOWN", "PLAIN", "AES", "CC_CRYPT", "RC4", "LEN=0" }; + +// Mode names for CMD_0C command +static const char *cmd0c_mode_name[] = { "NONE", "RC6", "RC4", "CC_CRYPT", "AES", "IDEA" }; + +uint8_t cc_node_id[8]; + +int32_t cc_cli_connect(struct s_client *cl); +int32_t cc_send_pending_emms(struct s_client *cl); + +#define getprefix() (!cl ? "" : (!cl->cc ? "" : (((struct cc_data *)(cl->cc))->prefix))) + +void cc_init_crypt(struct cc_crypt_block *block, uint8_t *key, int32_t len) +{ + int32_t i = 0; + uint8_t j = 0; + + for(i = 0; i < 256; i++) + { + block->keytable[i] = i; + } + + for(i = 0; i < 256; i++) + { + j += key[i % len] + block->keytable[i]; + SWAPC(&block->keytable[i], &block->keytable[j]); + } + + block->state = *key; + block->counter = 0; + block->sum = 0; +} + +void cc_crypt(struct cc_crypt_block *block, uint8_t *data, int32_t len, cc_crypt_mode_t mode) +{ + int32_t i; + uint8_t z; + + for(i = 0; i < len; i++) + { + block->counter++; + block->sum += block->keytable[block->counter]; + SWAPC(&block->keytable[block->counter], &block->keytable[block->sum]); + + z = data[i]; + data[i] = z ^ block->keytable[(block->keytable[block->counter] + block->keytable[block->sum]) & 0xff]; + data[i] ^= block->state; + + if(!mode) + { + z = data[i]; + } + + block->state = block->state ^ z; + } +} + +void cc_rc4_crypt(struct cc_crypt_block *block, uint8_t *data, int32_t len, cc_crypt_mode_t mode) +{ + int32_t i; + uint8_t z; + + for(i = 0; i < len; i++) + { + block->counter++; + block->sum += block->keytable[block->counter]; + SWAPC(&block->keytable[block->counter], &block->keytable[block->sum]); + + z = data[i]; + data[i] = z ^ block->keytable[(block->keytable[block->counter] + block->keytable[block->sum]) & 0xff]; + + if(!mode) + { + z = data[i]; + } + + block->state = block->state ^ z; + } +} + +void cc_xor(uint8_t *buf) +{ + const char cccam[] = "CCcam"; + uint8_t i; + + for(i = 0; i < 8; i++) + { + buf[8 + i] = i * buf[i]; + + if(i <= 5) + { + buf[i] ^= cccam[i]; + } + } +} + +void cc_cw_crypt(struct s_client *cl, uint8_t *cws, uint32_t cardid) +{ + struct cc_data *cc = cl->cc; + uint8_t n = 0; + uint8_t i; + uint8_t tmp; + uint8_t *nod = NULL; + + if (!cs_malloc(&nod, 8)) + { + return; + } + + for (i=0; i<8; i++) + { + if (cl->typ != 'c') + { + nod[i] = cc->node_id[7-i]; + } + else + { + nod[i] = cc->peer_node_id[7-i]; + } + } + + for (i=0; i<16; i++) + { + if (i & 1) + { + if (i != 15) + { + n = (nod[i>>1]>>4) | (nod[(i>>1)+1]<<4); + } + else + { + n = nod[i>>1]>>4; + } + } + else + { + n = nod[i>>1]; + } + + n = n & 0xff; + tmp = cws[i] ^ n; + + if (i & 1) + { + tmp = ~tmp; + } + + cws[i] = ((cardid >> (2 * i)) ^ tmp) & 0xff; + } + + free(nod); +} + +/** swap endianness (int) */ +static void SwapLBi(uint8_t *buff, int32_t len) +{ +#if __BYTE_ORDER != __BIG_ENDIAN + return; +#endif + + int32_t i; + uint8_t swap[4]; + + for(i = 0; i < len / 4; i++) + { + memcpy(swap, buff, 4); + buff[0] = swap[3]; + buff[1] = swap[2]; + buff[2] = swap[1]; + buff[3] = swap[0]; + buff += 4; + } +} + +void cc_crypt_cmd0c(struct s_client *cl, uint8_t *buf, int32_t len) +{ + struct cc_data *cc = cl->cc; + uint8_t *out; + + if(!cs_malloc(&out, len)) + { + return; + } + + switch(cc->cmd0c_mode) + { + case MODE_CMD_0x0C_NONE: // none additional encryption + { + memcpy(out, buf, len); + break; + } + + case MODE_CMD_0x0C_RC6: // RC6 + { + // buf may be unaligned, + // so we use malloc() memory for the uint32_t* cast + uint8_t *tmp; + int32_t i; + + if(!cs_malloc(&tmp, len)) + { + return; + } + + memcpy(tmp, buf, len); + SwapLBi(tmp, len); + + for(i = 0; i < len / 16; i++) + { + rc6_block_decrypt((uint32_t *)(tmp + i * 16), (uint32_t *)(out + i * 16), 1, cc->cmd0c_RC6_cryptkey); + } + + SwapLBi(out, len); + NULLFREE(tmp); + break; + } + + case MODE_CMD_0x0C_RC4: // RC4 + { + cc_rc4_crypt(&cc->cmd0c_cryptkey, buf, len, ENCRYPT); + memcpy(out, buf, len); + break; + } + + case MODE_CMD_0x0C_CC_CRYPT: // cc_crypt + { + cc_crypt(&cc->cmd0c_cryptkey, buf, len, DECRYPT); + memcpy(out, buf, len); + break; + } + + case MODE_CMD_0x0C_AES: // AES + { + int32_t i; + for(i = 0; i < len / 16; i++) + { + AES_decrypt((uint8_t *)buf + i * 16, (uint8_t *)out + i * 16, &cc->cmd0c_AES_key); + } + break; + } + + case MODE_CMD_0x0C_IDEA: // IDEA + { + int32_t i = 0; + int32_t j; + + while(i < len) + { + idea_ecb_encrypt(buf + i, out + i, &cc->cmd0c_IDEA_dkey); + i += 8; + } + + i = 8; + while(i < len) + { + for(j = 0; j < 8; j++) + { + out[j + i] ^= buf[j + i - 8]; + } + i += 8; + } + + break; + } + } + memcpy(buf, out, len); + NULLFREE(out); +} + +void set_cmd0c_cryptkey(struct s_client *cl, uint8_t *key, uint8_t len) +{ + struct cc_data *cc = cl->cc; + uint8_t key_buf[32]; + + memset(&key_buf, 0, sizeof(key_buf)); + + if(len > 32) + { + len = 32; + } + + memcpy(key_buf, key, len); + + switch(cc->cmd0c_mode) + { + case MODE_CMD_0x0C_NONE: // NONE + { + break; + } + + case MODE_CMD_0x0C_RC6: // RC6 + { + rc6_key_setup(key_buf, 32, cc->cmd0c_RC6_cryptkey); + break; + } + + case MODE_CMD_0x0C_RC4: // RC4 + case MODE_CMD_0x0C_CC_CRYPT: // CC_CRYPT + { + cc_init_crypt(&cc->cmd0c_cryptkey, key_buf, 32); + break; + } + + case MODE_CMD_0x0C_AES: // AES + { + memset(&cc->cmd0c_AES_key, 0, sizeof(cc->cmd0c_AES_key)); + AES_set_decrypt_key((uint8_t *)key_buf, 256, &cc->cmd0c_AES_key); + break; + } + + case MODE_CMD_0x0C_IDEA: // IDEA + { + uint8_t key_buf_idea[16]; + memcpy(key_buf_idea, key_buf, 16); + IDEA_KEY_SCHEDULE ekey; + + idea_set_encrypt_key(key_buf_idea, &ekey); + idea_set_decrypt_key(&ekey, &cc->cmd0c_IDEA_dkey); + break; + } + } +} + +int32_t sid_eq(struct cc_srvid *srvid1, struct cc_srvid *srvid2) +{ + return (srvid1->sid == srvid2->sid && (srvid1->chid == srvid2->chid || !srvid1->chid || !srvid2->chid) + && (srvid1->ecmlen == srvid2->ecmlen || !srvid1->ecmlen || !srvid2->ecmlen)); +} + +int32_t sid_eq_nb(struct cc_srvid *srvid1, struct cc_srvid_block *srvid2) +{ + return sid_eq(srvid1, (struct cc_srvid *)srvid2); +} + +int32_t sid_eq_bb(struct cc_srvid_block *srvid1, struct cc_srvid_block *srvid2) +{ + return (srvid1->sid == srvid2->sid && (srvid1->chid == srvid2->chid || !srvid1->chid || !srvid2->chid) + && (srvid1->ecmlen == srvid2->ecmlen || !srvid1->ecmlen || !srvid2->ecmlen) + && (srvid1->blocked_till == srvid2->blocked_till || !srvid1->blocked_till || !srvid2->blocked_till)); +} + +struct cc_srvid_block *is_sid_blocked(struct cc_card *card, struct cc_srvid *srvid_blocked) +{ + LL_ITER it = ll_iter_create(card->badsids); + struct cc_srvid_block *srvid; + + while((srvid = ll_iter_next(&it))) + { + if(sid_eq_nb(srvid_blocked, srvid)) + { + break; + } + } + return srvid; +} + +uint32_t has_perm_blocked_sid(struct cc_card *card) +{ + LL_ITER it = ll_iter_create(card->badsids); + struct cc_srvid_block *srvid; + + while((srvid = ll_iter_next(&it))) + { + if(srvid->blocked_till == 0) + { + break; + } + } + return srvid != NULL; +} + +struct cc_srvid *is_good_sid(struct cc_card *card, struct cc_srvid *srvid_good) +{ + LL_ITER it = ll_iter_create(card->goodsids); + struct cc_srvid *srvid; + + while((srvid = ll_iter_next(&it))) + { + if(sid_eq(srvid, srvid_good)) + { + break; + } + } + return srvid; +} + +#define BLOCKING_SECONDS 10 + +void add_sid_block(struct cc_card *card, struct cc_srvid *srvid_blocked, bool temporary) +{ + if(is_sid_blocked(card, srvid_blocked)) + { + return; + } + + struct cc_srvid_block *srvid; + if(!cs_malloc(&srvid, sizeof(struct cc_srvid_block))) + { + return; + } + memcpy(srvid, srvid_blocked, sizeof(struct cc_srvid)); + + if(temporary) + { + srvid->blocked_till = time(NULL) + BLOCKING_SECONDS; + } + + ll_append(card->badsids, srvid); + cs_log_dbg(D_READER, "added sid block %04X(CHID %04X, length %d) for card %08x", + srvid_blocked->sid, srvid_blocked->chid, srvid_blocked->ecmlen, card->id); +} + +void remove_sid_block(struct cc_card *card, struct cc_srvid *srvid_blocked) +{ + LL_ITER it = ll_iter_create(card->badsids); + struct cc_srvid_block *srvid; + + while((srvid = ll_iter_next(&it))) + { + if(sid_eq_nb(srvid_blocked, srvid)) + { + ll_iter_remove_data(&it); + } + } + + cs_log_dbg(D_READER, "removed sid block %04X(CHID %04X, length %d) for card %08x", + srvid_blocked->sid, srvid_blocked->chid, srvid_blocked->ecmlen, card->id); +} + +void add_good_sid(struct cc_card *card, struct cc_srvid *srvid_good) +{ + if(is_good_sid(card, srvid_good)) + { + return; + } + + remove_sid_block(card, srvid_good); + struct cc_srvid *srvid; + + if(!cs_malloc(&srvid, sizeof(struct cc_srvid))) + { + return; + } + + memcpy(srvid, srvid_good, sizeof(struct cc_srvid)); + ll_append(card->goodsids, srvid); + + cs_log_dbg(D_READER, "added good sid %04X(%d) for card %08x", + srvid_good->sid, srvid_good->ecmlen, card->id); +} + +void remove_good_sid(struct cc_card *card, struct cc_srvid *srvid_good) +{ + LL_ITER it = ll_iter_create(card->goodsids); + struct cc_srvid *srvid; + + while((srvid = ll_iter_next(&it))) + { + if(sid_eq(srvid, srvid_good)) + { + ll_iter_remove_data(&it); + } + } + + cs_log_dbg(D_READER, "removed good sid %04X(%d) for card %08x", + srvid_good->sid, srvid_good->ecmlen, card->id); +} + +/** + * reader + * clears and frees values for reinit + */ +void cc_cli_close(struct s_client *cl, int32_t call_conclose) +{ + struct s_reader *rdr = cl->reader; + struct cc_data *cc = cl->cc; + + if(!rdr || !cc) + { + return; + } + + if(rdr) + { + rdr->tcp_connected = 0; + } + + if(rdr) + { + rdr->card_status = NO_CARD; + } + + if(rdr) + { + rdr->last_s = rdr->last_g = 0; + } + + if(cl) + { + cl->last = 0; + } + + if(call_conclose) // clears also pending ecms! + { + network_tcp_connection_close(rdr, "close"); + } + else + { + if(cl->udp_fd) + { + close(cl->udp_fd); + cl->udp_fd = 0; + cl->pfd = 0; + } + } + + cc->ecm_busy = 0; + cc->just_logged_in = 0; +} + +struct cc_extended_ecm_idx *add_extended_ecm_idx(struct s_client *cl, uint8_t send_idx, uint16_t ecm_idx, + struct cc_card *card, struct cc_srvid srvid, int8_t free_card) +{ + struct cc_data *cc = cl->cc; + struct cc_extended_ecm_idx *eei; + + if(!cs_malloc(&eei, sizeof(struct cc_extended_ecm_idx))) + { + return NULL; + } + + eei->send_idx = send_idx; + eei->ecm_idx = ecm_idx; + eei->card = card; + eei->cccam_id = card->id; + eei->srvid = srvid; + eei->free_card = free_card; + cs_ftime(&eei->tps); + ll_append(cc->extended_ecm_idx, eei); + //cs_log_dbg(D_TRACE, "%s add extended ecm-idx: %d:%d", getprefix(), send_idx, ecm_idx); + return eei; +} + +struct cc_extended_ecm_idx *get_extended_ecm_idx(struct s_client *cl, uint8_t send_idx, int32_t remove_item) +{ + struct cc_data *cc = cl->cc; + struct cc_extended_ecm_idx *eei; + LL_ITER it = ll_iter_create(cc->extended_ecm_idx); + + while((eei = ll_iter_next(&it))) + { + if(eei->send_idx == send_idx) + { + if(remove_item) + { + ll_iter_remove(&it); + } + + //cs_log_dbg(D_TRACE, "%s get by send-idx: %d FOUND: %d", getprefix(), send_idx, eei->ecm_idx); + return eei; + } + } + +#ifdef WITH_DEBUG + if(remove_item) + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "%s get by send-idx: %d NOT FOUND", getprefix(), send_idx); + } +#endif + + return NULL; +} + +struct cc_extended_ecm_idx *get_extended_ecm_idx_by_idx(struct s_client *cl, uint16_t ecm_idx, int32_t remove_item) +{ + struct cc_data *cc = cl->cc; + struct cc_extended_ecm_idx *eei; + LL_ITER it = ll_iter_create(cc->extended_ecm_idx); + + while((eei = ll_iter_next(&it))) + { + if(eei->ecm_idx == ecm_idx) + { + if(remove_item) + { + ll_iter_remove(&it); + } + + //cs_log_dbg(D_TRACE, "%s get by ecm-idx: %d FOUND: %d", getprefix(), ecm_idx, eei->send_idx); + return eei; + } + } + +#ifdef WITH_DEBUG + if(remove_item) + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "%s get by ecm-idx: %d NOT FOUND", getprefix(), ecm_idx); + } +#endif + + return NULL; +} + +void cc_reset_pending(struct s_client *cl, int32_t ecm_idx) +{ + int32_t i = 0; + + for(i = 0; i < cfg.max_pending; i++) + { + if(cl->ecmtask[i].idx == ecm_idx && cl->ecmtask[i].rc == E_ALREADY_SENT) + { + cl->ecmtask[i].rc = E_UNHANDLED; // Mark unused + } + } +} + +void free_extended_ecm_idx_by_card(struct s_client *cl, struct cc_card *card, int8_t null_only) +{ + struct cc_data *cc = cl->cc; + struct cc_extended_ecm_idx *eei; + LL_ITER it = ll_iter_create(cc->extended_ecm_idx); + + while((eei = ll_iter_next(&it))) + { + if(eei->card == card) + { + if(null_only) + { + cc_reset_pending(cl, eei->ecm_idx); + if(eei->free_card) + { + NULLFREE(eei->card); + } + ll_iter_remove_data(&it); + } + else + { + if(eei->free_card) + { + NULLFREE(eei->card); + } + eei->card = NULL; + } + } + } +} + +void free_extended_ecm_idx(struct cc_data *cc) +{ + struct cc_extended_ecm_idx *eei; + LL_ITER it = ll_iter_create(cc->extended_ecm_idx); + + while((eei = ll_iter_next(&it))) + { + if(eei->free_card) + { + NULLFREE(eei->card); + } + ll_iter_remove_data(&it); + } +} + +int32_t cc_recv_to(struct s_client *cl, uint8_t *buf, int32_t len) +{ + int32_t rc; + struct pollfd pfd; + + while(1) + { + pfd.fd = cl->udp_fd; + pfd.events = POLLIN | POLLPRI; + + rc = poll(&pfd, 1, cfg.cc_recv_timeout); + + if(rc < 0) + { + if(errno == EINTR) + { + continue; + } + + return -1; // error!! + } + + if(rc == 1) + { + if(pfd.revents & POLLHUP) + { + return -1; // hangup = error!! + } + else + { + break; + } + } + else + { + return -2; // timeout!! + } + } + return cs_recv(cl->udp_fd, buf, len, MSG_WAITALL); +} + +/** + * reader + * closes the connection and reopens it. + */ +static int8_t cc_cycle_connection(struct s_client *cl) +{ + if(!cl || cl->kill) + { + return 0; + } + + cs_log_dbg(D_TRACE, "%s unlocked-cycleconnection! timeout %d ms", getprefix(), cl->reader->cc_reconnect); + + cc_cli_close(cl, 0); + cs_sleepms(50); + cc_cli_connect(cl); + + return cl->reader->tcp_connected; +} + +/** + * reader + server: + * receive a message + */ +int32_t cc_msg_recv(struct s_client *cl, uint8_t *buf, int32_t maxlen) +{ + struct s_reader *rdr = (cl->typ == 'c') ? NULL : cl->reader; + + int32_t len; + struct cc_data *cc = cl->cc; + int32_t handle = cl->udp_fd; + + if(handle <= 0 || maxlen < 4) + { + return -1; + } + + if(!cl->cc) + { + return -1; + } + + cs_writelock(__func__, &cc->lockcmd); + + if(!cl->cc) + { + cs_writeunlock(__func__, &cc->lockcmd); + return -1; + } + + len = cs_recv(handle, buf, 4, MSG_WAITALL); + + if(len != 4) // invalid header length read + { + if(len <= 0) + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "%s disconnected by remote server", getprefix()); + } + else + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "%s invalid header length (expected 4, read %d)", + getprefix(), len); + } + + cs_writeunlock(__func__, &cc->lockcmd); + return -1; + } + + cc_crypt(&cc->block[DECRYPT], buf, 4, DECRYPT); + //cs_log_dump_dbg(D_CLIENT, buf, 4, "cccam: decrypted header:"); + + cc->g_flag = buf[0]; + + int32_t size = (buf[2] << 8) | buf[3]; + if(size) // check if any data is expected in msg + { + if(size > maxlen) + { + cs_writeunlock(__func__, &cc->lockcmd); + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "%s message too big (size=%d max=%d)", + getprefix(), size, maxlen); + return 0; + } + + len = cs_recv(handle, buf + 4, size, MSG_WAITALL); + + if(rdr && (buf[1] == MSG_CW_ECM +#ifdef CS_CACHEEX_AIO + || buf[1] == MSG_CW_ECM_LGF +#endif + )) + { + rdr->last_g = time(NULL); + } + + if(len != size) + { + cs_writeunlock(__func__, &cc->lockcmd); + + if(len <= 0) + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "%s disconnected by remote", getprefix()); + } + else + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "%s invalid message length read (expected %d, read %d)", + getprefix(), size, len); + } + return -1; + } + + cc_crypt(&cc->block[DECRYPT], buf + 4, len, DECRYPT); + len += 4; + } + + cs_writeunlock(__func__, &cc->lockcmd); + + //cs_log_dump_dbg(cl->typ=='c'?D_CLIENT:D_READER, buf, len, "cccam: full decrypted msg, len=%d:", len); + + return len; +} + +/** + * reader + server + * send a message + */ +int32_t cc_cmd_send(struct s_client *cl, uint8_t *buf, int32_t len, cc_msg_type_t cmd) +{ + if(!cl->udp_fd) // disconnected + { + return -1; + } + + struct s_reader *rdr = (cl->typ == 'c') ? NULL : cl->reader; + + int32_t n; + struct cc_data *cc = cl->cc; + + if(!cl->cc || cl->kill) + { + return -1; + } + + cs_writelock(__func__, &cc->lockcmd); + + if(!cl->cc || cl->kill) + { + cs_writeunlock(__func__, &cc->lockcmd); + return -1; + } + + uint8_t *netbuf; + if(!cs_malloc(&netbuf, len + 4)) + { + cs_writeunlock(__func__, &cc->lockcmd); + return -1; + } + + if(cmd == MSG_NO_HEADER) + { + memcpy(netbuf, buf, len); + } + else + { + // build command message + netbuf[0] = cc->g_flag; // flags? + netbuf[1] = cmd & 0xff; + netbuf[2] = len >> 8; + netbuf[3] = len & 0xff; + + if(buf) + { + memcpy(netbuf + 4, buf, len); + } + len += 4; + } + + cs_log_dump_dbg(D_CLIENT, netbuf, len, "cccam: send:"); + cc_crypt(&cc->block[ENCRYPT], netbuf, len, ENCRYPT); + + n = send(cl->udp_fd, netbuf, len, 0); + + cs_writeunlock(__func__, &cc->lockcmd); + + NULLFREE(netbuf); + + if(n != len) + { + if(rdr) + { + cc_cli_close(cl, 1); + } + else + { + cs_disconnect_client(cl); + } + n = -1; + } + + return n; +} + +#define CC_DEFAULT_VERSION 9 +#define CC_VERSIONS 10 +static char *version[CC_VERSIONS] = { "2.0.11", "2.1.1", "2.1.2", "2.1.3", "2.1.4", "2.2.0", "2.2.1", "2.3.0", "2.3.1", "2.3.2"}; +static char *build[CC_VERSIONS] = { "2892", "2971", "3094", "3165", "3191", "3290", "3316", "3367", "9d508a", "4000"}; +static char extcompat[CC_VERSIONS] = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; // Supporting new card format starting with 2.2.0 + +/** + * reader + server + * checks the cccam-version in the configuration + */ +void cc_check_version(char *cc_version, char *cc_build) +{ + int32_t i; + for(i = 0; i < CC_VERSIONS; i++) + { + if(!memcmp(cc_version, version[i], cs_strlen(version[i]))) + { + memcpy(cc_build, build[i], cs_strlen(build[i]) + 1); + cs_log_dbg(D_CLIENT, "cccam: auto build set for version: %s build: %s", cc_version, cc_build); + return; + } + } + + memcpy(cc_version, version[CC_DEFAULT_VERSION], cs_strlen(version[CC_DEFAULT_VERSION])); + memcpy(cc_build, build[CC_DEFAULT_VERSION], cs_strlen(build[CC_DEFAULT_VERSION])); + + cs_log_dbg(D_CLIENT, "cccam: auto version set: %s build: %s", cc_version, cc_build); + + return; +} + +int32_t check_cccam_compat(struct cc_data *cc) +{ + int32_t res = 0; + int32_t i = 0; + for(i = 0; i < CC_VERSIONS; i++) + { + if(!strcmp(cfg.cc_version, version[i])) + { + res += extcompat[i]; + break; + } + } + + if(!res) + { + return 0; + } + + for(i = 0; i < CC_VERSIONS; i++) + { + if(!strcmp(cc->remote_version, version[i])) + { + res += extcompat[i]; + break; + } + } + + return res == 2; +} + +/** + * reader + * sends own version information to the CCCam server + */ +int32_t cc_send_cli_data(struct s_client *cl) +{ + struct s_reader *rdr = cl->reader; + struct cc_data *cc = cl->cc; + const int32_t size = 20 + 8 + 6 + 26 + 4 + 28 + 1; + uint8_t buf[size]; + + cs_log_dbg(D_READER, "cccam: send client data"); + + memcpy(cc->node_id, cc_node_id, sizeof(cc_node_id)); + + memcpy(buf, rdr->r_usr, sizeof(rdr->r_usr)); + memcpy(buf + 20, cc->node_id, 8); + buf[28] = rdr->cc_want_emu; // <-- Client wants to have EMUs, 0 - NO; 1 - YES + memcpy(buf + 29, rdr->cc_version, sizeof(rdr->cc_version)); // cccam version (ascii) + memcpy(buf + 61, rdr->cc_build, sizeof(rdr->cc_build)); // build number (ascii) + + // multics seed already detected, now send multics 'WHO' for getting and confirming multics server + if(cc->multics_mode == 1) + { + memcpy(buf + 57, "W", 1); + memcpy(buf + 58, "H", 1); + memcpy(buf + 59, "O", 1); + } + + cs_log_dbg(D_READER, "%s sending own version: %s, build: %s", getprefix(), rdr->cc_version, rdr->cc_build); + + return cc_cmd_send(cl, buf, size, MSG_CLI_DATA); +} + +/** + * server + * sends version information to the client + */ +int32_t cc_send_srv_data(struct s_client *cl) +{ + struct cc_data *cc = cl->cc; + + cs_log_dbg(D_CLIENT, "cccam: send server data"); + + memcpy(cc->node_id, cc_node_id, sizeof(cc_node_id)); + + uint8_t buf[0x48]; + memset(buf, 0, 0x48); + + int32_t stealth = cl->account->cccstealth; + if(stealth == -1) + { + stealth = cfg.cc_stealth; + } + + if(stealth) + { + cc->node_id[7]++; + } + + memcpy(buf, cc->node_id, 8); + + char cc_build[7], tmp_dbg[17]; + memset(cc_build, 0, sizeof(cc_build)); + cc_check_version((char *) cfg.cc_version, cc_build); + memcpy(buf + 8, cfg.cc_version, sizeof(cfg.cc_version)); // cccam version (ascii) + memcpy(buf + 40, cc_build, sizeof(cc_build)); // build number (ascii) + + cs_log_dbg(D_CLIENT, "%s version: %s, build: %s nodeid: %s", getprefix(), + cfg.cc_version, cc_build, cs_hexdump(0, cc->peer_node_id, 8, tmp_dbg, sizeof(tmp_dbg))); + + return cc_cmd_send(cl, buf, 0x48, MSG_SRV_DATA); +} + +int32_t loop_check(uint8_t *myid, struct s_client *cl) +{ + if(!cl || !myid) + { + return 0; + } + + struct cc_data *cc = cl->cc; + if(!cc) + { + return 0; + } + + return !memcmp(myid, cc->peer_node_id, sizeof(cc->peer_node_id)); // same nodeid? ignore +} + +/** + * reader + * retrieves the next waiting ecm request + */ +int32_t cc_get_nxt_ecm(struct s_client *cl) +{ + struct cc_data *cc = cl->cc; + ECM_REQUEST *er, *ern = NULL; + int32_t n, i, pending = 0; + struct timeb t; + + cs_ftime(&t); + int32_t diff = (int32_t)cfg.ctimeout + 500; + + n = -1; + for(i = 0; i < cfg.max_pending; i++) + { + er = &cl->ecmtask[i]; + if((comp_timeb(&t, &er->tps) >= diff) && (er->rc >= E_NOCARD)) // drop timeouts + { + write_ecm_answer(cl->reader, er, E_TIMEOUT, 0, NULL, NULL, 0, NULL); + } + + else if(er->rc >= E_NOCARD && er->rc <= E_UNHANDLED) // stil active and waiting + { + pending++; + if(loop_check(cc->peer_node_id, er->client)) + { + cs_log_dbg(D_READER, "%s ecm loop detected! client %s (%8lX)", + getprefix(), er->client->account->usr, (unsigned long)er->client->thread); + write_ecm_answer(cl->reader, er, E_NOTFOUND, E2_CCCAM_LOOP, NULL, NULL, 0, NULL); + } + else + { + // search for the ecm with the lowest time, this should be the next to go + if(n < 0 || (ern->tps.time - er->tps.time < 0)) + { + // check for already pending: + if(cc && cc->extended_mode) + { + int32_t j, found; + ECM_REQUEST *erx; + + for(found = j = 0; j < cfg.max_pending; j++) + { + erx = &cl->ecmtask[j]; + if(i != j && erx->rc == E_ALREADY_SENT + && er->caid == erx->caid && er->ecmd5 == erx->ecmd5) + { + found = 1; + break; + } + } + + if(!found) + { + n = i; + ern = er; + } + } + else + { + n = i; + ern = er; + } + } + } + } + } + + cl->pending = pending; + return n; +} + +/** + * sends the secret cmd05 answer to the server + */ +int32_t send_cmd05_answer(struct s_client *cl) +{ + struct cc_data *cc = cl->cc; + if(!cc->cmd05_active || cc->ecm_busy) // exit if not in cmd05 or waiting for ECM answer + { + return 0; + } + + cc->cmd05_active--; + if(cc->cmd05_active) + { + return 0; + } + + uint8_t *data = cc->cmd05_data; + cc_cmd05_mode cmd05_mode = MODE_UNKNOWN; + + // by Project: Keynation + switch(cc->cmd05_data_len) + { + case 0: // payload 0, return with payload 0! + { + cc_cmd_send(cl, NULL, 0, MSG_CMD_05); + cmd05_mode = MODE_LEN0; + break; + } + + case 256: + { + cmd05_mode = cc->cmd05_mode; + switch(cmd05_mode) + { + case MODE_PLAIN: // send plain unencrypted back + { + cc_cmd_send(cl, data, 256, MSG_CMD_05); + break; + } + + case MODE_AES: // encrypt with received aes128 key + { + AES_KEY key; + uint8_t aeskey[16]; + uint8_t out[256]; + + memcpy(aeskey, cc->cmd05_aeskey, 16); + memset(&key, 0, sizeof(key)); + + AES_set_encrypt_key((uint8_t *) &aeskey, 128, &key); + int32_t i; + + for(i = 0; i < 256; i += 16) + { + AES_encrypt((uint8_t *)data + i, (uint8_t *) &out + i, &key); + } + + cc_cmd_send(cl, out, 256, MSG_CMD_05); + break; + } + + case MODE_CC_CRYPT: // encrypt with cc_crypt + { + cc_crypt(&cc->cmd05_cryptkey, data, 256, ENCRYPT); + cc_cmd_send(cl, data, 256, MSG_CMD_05); + break; + } + + case MODE_RC4_CRYPT: // special xor crypt + { + cc_rc4_crypt(&cc->cmd05_cryptkey, data, 256, DECRYPT); + cc_cmd_send(cl, data, 256, MSG_CMD_05); + break; + } + + default: + cmd05_mode = MODE_UNKNOWN; + } + break; + } + + default: + cmd05_mode = MODE_UNKNOWN; + } + + // unhandled types always needs cycle connection after 50 ECMs! + if(cmd05_mode == MODE_UNKNOWN) + { + cc_cmd_send(cl, NULL, 0, MSG_CMD_05); + if(!cc->max_ecms) // max_ecms already set? + { + cc->max_ecms = 50; + cc->ecm_counter = 0; + } + } + + cs_log_dbg(D_READER, "%s sending CMD_05 back! MODE: %s len=%d", + getprefix(), cmd05_mode_name[cmd05_mode], cc->cmd05_data_len); + + cc->cmd05NOK = 1; + return 1; +} + +int32_t get_UA_ofs(uint16_t caid) +{ + int32_t ofs = 0; + switch(caid >> 8) + { + case 0x05: // VIACCESS + case 0x0D: // CRYPTOWORKS + //ofs = 1; + //break; + case 0x4A: // TONGFANG + case 0x09: // VIDEOGUARD + case 0x0B: // CONAX + case 0x18: // NAGRA + case 0x01: // SECA + case 0x00: // SECAMANAGMENT + case 0x17: // BETACRYPT + case 0x06: // IRDETO + ofs = 2; + break; + } + + return ofs; +} + +int32_t UA_len(uint8_t *ua) +{ + int32_t i, len = 0; + + for(i = 0; i < 8; i++) + { + if(ua[i]) + { + len++; + } + } + return len; +} +void UA_left(uint8_t *in, uint8_t *out, int32_t ofs) +{ + memset(out, 0, 8); + memcpy(out, in + ofs, 8 - ofs); +} + +void UA_right(uint8_t *in, uint8_t *out, int32_t len) +{ + int32_t ofs = 0; + + while(len) + { + memcpy(out + ofs, in, len); + len--; + + if(out[len]) + { + break; + } + ofs++; + out[0] = 0; + } +} + +/** + * cccam uses UA right justified + **/ +void cc_UA_oscam2cccam(uint8_t *in, uint8_t *out, uint16_t caid) +{ + uint8_t tmp[8]; + memset(out, 0, 8); + memset(tmp, 0, 8); + + //switch(caid >> 8) + //{ + // case 0x17: //IRDETO/Betacrypt: + // //oscam: AA BB CC DD 00 00 00 00 + // //cccam: 00 00 00 00 DD AA BB CC + // out[4] = in[3]; //Hexbase + // out[5] = in[0]; + // out[6] = in[1]; + // out[7] = in[2]; + // return; + // + // //Place here your own adjustments! + //} + + if(caid_is_bulcrypt(caid)) + { + out[4] = in[0]; + out[5] = in[1]; + out[6] = in[2]; + out[7] = in[3]; + return; + } + + hexserial_to_newcamd(in, tmp + 2, caid); + UA_right(tmp, out, 8); +} + +/** + * oscam has a special format, depends on offset or type: + **/ +void cc_UA_cccam2oscam(uint8_t *in, uint8_t *out, uint16_t caid) +{ + uint8_t tmp[8]; + memset(out, 0, 8); + memset(tmp, 0, 8); + + //switch(caid >> 8) + //{ + // case 0x17: //IRDETO/Betacrypt: + // //cccam: 00 00 00 00 DD AA BB CC + // //oscam: AA BB CC DD 00 00 00 00 + // out[0] = in[5]; + // out[1] = in[6]; + // out[2] = in[7]; + // out[3] = in[4]; //Hexbase + // return; + + // //Place here your own adjustments! + //} + + if(caid_is_bulcrypt(caid)) + { + out[0] = in[4]; + out[1] = in[5]; + out[2] = in[6]; + out[3] = in[7]; + return; + } + + int32_t ofs = get_UA_ofs(caid); + UA_left(in, tmp, ofs); + newcamd_to_hexserial(tmp, out, caid); +} + +void cc_SA_oscam2cccam(uint8_t *in, uint8_t *out) +{ + memcpy(out, in, 4); +} + +void cc_SA_cccam2oscam(uint8_t *in, uint8_t *out) +{ + memcpy(out, in, 4); +} + +int32_t cc_UA_valid(uint8_t *ua) +{ + int32_t i; + + for(i = 0; i < 8; i++) + { + if(ua[i]) + { + return 1; + } + } + + return 0; +} + +/** + * Updates AU Data: UA (Unique ID / Hexserial) und SA (Shared ID - Provider) + */ +void set_au_data(struct s_client *cl, struct s_reader *rdr, struct cc_card *card, ECM_REQUEST *cur_er) +{ + if(rdr->audisabled || !cc_UA_valid(card->hexserial)) + { + return; + } + + struct cc_data *cc = cl->cc; + char tmp_dbg[17]; + cc->last_emm_card = card; + + cc_UA_cccam2oscam(card->hexserial, rdr->hexserial, rdr->caid); + + cs_log_dbg(D_EMM, "%s au info: caid %04X UA: %s", getprefix(), card->caid, + cs_hexdump(0, rdr->hexserial, 8, tmp_dbg, sizeof(tmp_dbg))); + + rdr->nprov = 0; + LL_ITER it2 = ll_iter_create(card->providers); + struct cc_provider *provider; + int32_t p = 0; + + while((provider = ll_iter_next(&it2))) + { + if(!cur_er || provider->prov == cur_er->prid || !provider->prov || !cur_er->prid) + { + rdr->prid[p][0] = provider->prov >> 24; + rdr->prid[p][1] = provider->prov >> 16; + rdr->prid[p][2] = provider->prov >> 8; + rdr->prid[p][3] = provider->prov & 0xFF; + cc_SA_cccam2oscam(provider->sa, rdr->sa[p]); + + cs_log_dbg(D_EMM, "%s au info: provider: %06X:%02X%02X%02X%02X", getprefix(), + provider->prov, provider->sa[0], provider->sa[1], provider->sa[2], provider->sa[3]); + + p++; + rdr->nprov = p; + if(p >= CS_MAXPROV) + { + break; + } + } + } + + if(!rdr->nprov) // No Providers? Add null-provider + { + memset(rdr->prid[0], 0, sizeof(rdr->prid[0])); + rdr->nprov = 1; + } + + rdr->caid = card->caid; + if(cur_er) + { + rdr->auprovid = cur_er->prid; + } +} + +int32_t same_first_node(struct cc_card *card1, struct cc_card *card2) +{ + uint8_t *node1 = ll_has_elements(card1->remote_nodes); + uint8_t *node2 = ll_has_elements(card2->remote_nodes); + + if(!node1 && !node2) + { + return 1; // both NULL, same! + } + + if(!node1 || !node2) + { + return 0; // one NULL, not same! + } + + return !memcmp(node1, node2, 8); // same? +} + +int32_t same_card2(struct cc_card *card1, struct cc_card *card2, int8_t compare_grp) +{ + return (card1->caid == card2->caid && + card1->card_type == card2->card_type && + card1->sidtab == card2->sidtab && + (!compare_grp || card1->grp == card2->grp) && + !memcmp(card1->hexserial, card2->hexserial, sizeof(card1->hexserial))); +} + +int32_t same_card(struct cc_card *card1, struct cc_card *card2) +{ + return (card1->remote_id == card2->remote_id && + same_card2(card1, card2, 1) && + same_first_node(card1, card2)); +} + +struct cc_card *get_matching_card(struct s_client *cl, ECM_REQUEST *cur_er, int8_t chk_only) +{ + struct cc_data *cc = cl->cc; + struct s_reader *rdr = cl->reader; + + if(cl->kill || !rdr || !cc) + { + return NULL; + } + + struct cc_srvid cur_srvid; + cur_srvid.sid = cur_er->srvid; + cur_srvid.chid = cur_er->chid; + cur_srvid.ecmlen = cur_er->ecmlen; + + int32_t best_rating = MIN_RATING - 1, rating; + + LL_ITER it = ll_iter_create(cc->cards); + struct cc_card *card = NULL, *ncard, *xcard = NULL; + + while((ncard = ll_iter_next(&it))) + { + int lb_match = 0; + if(config_enabled(WITH_LB)) + { + // accept beta card when beta-tunnel is on + lb_match = chk_only && cfg.lb_mode && cfg.lb_auto_betatunnel && + ((caid_is_nagra(cur_er->caid) && caid_is_betacrypt(ncard->caid) && cfg.lb_auto_betatunnel_mode <= 3) || + (caid_is_betacrypt(cur_er->caid) && caid_is_nagra(ncard->caid) && cfg.lb_auto_betatunnel_mode >= 1)); + } + + if((ncard->caid == cur_er->caid // caid matches + || (rdr->cc_want_emu && (ncard->caid == (cur_er->caid & 0xFF00)))) + || lb_match) // or system matches if caid ends with 00 (needed for wantemu) + { + int32_t goodSidCount = ll_count(ncard->goodsids); + int32_t badSidCount = ll_count(ncard->badsids); + struct cc_srvid *good_sid; + struct cc_srvid_block *blocked_sid; + + if(goodSidCount && !badSidCount) // only good sids -> check if sid is good + { + good_sid = is_good_sid(ncard, &cur_srvid); + if(!good_sid) + { + continue; + } + } + else if(!goodSidCount && badSidCount) // only bad sids -> check if sid is bad + { + blocked_sid = is_sid_blocked(ncard, &cur_srvid); + if(blocked_sid && (!chk_only || blocked_sid->blocked_till == 0)) + { + continue; + } + } + else if(goodSidCount && badSidCount) // bad and good sids -> check not blocked and good + { + blocked_sid = is_sid_blocked(ncard, &cur_srvid); + good_sid = is_good_sid(ncard, &cur_srvid); + + if(blocked_sid && (!chk_only || blocked_sid->blocked_till == 0)) + { + continue; + } + + if(!good_sid) + { + continue; + } + } + + if(!(rdr->cc_want_emu) && caid_is_nagra(ncard->caid) && (!xcard || ncard->hop < xcard->hop)) + { + xcard = ncard; // remember card (D+ / 1810 fix) if request has no provider, but card has + } + + rating = ncard->rating - ncard->hop * HOP_RATING; + rating = MIN(MAX(rating, MIN_RATING), MAX_RATING); + + if(!ll_count(ncard->providers)) // card has no providers: + { + if(rating > best_rating) + { + // ncard is closer + card = ncard; + best_rating = rating; // ncard has been matched + } + + } + else // card has providers + { + LL_ITER it2 = ll_iter_create(ncard->providers); + struct cc_provider *provider; + + while((provider = ll_iter_next(&it2))) + { + if(!cur_er->prid || (provider->prov == cur_er->prid)) // provid matches + { + if(rating > best_rating) + { + // ncard is closer + card = ncard; + best_rating = rating; // ncard has been matched + } + } + } + } + } + } + + if(!card) + { + card = xcard; // 18xx: if request has no provider and we have no card, we try this card + } + + return card; +} + +// reopen all blocked sids for this srvid +static void reopen_sids(struct cc_data *cc, int8_t ignore_time, ECM_REQUEST *cur_er, struct cc_srvid *cur_srvid) +{ + time_t utime = time(NULL); + struct cc_card *card; + LL_ITER it = ll_iter_create(cc->cards); + + while((card = ll_iter_next(&it))) + { + if(card->caid == cur_er->caid) // caid matches + { + LL_ITER it2 = ll_iter_create(card->badsids); + struct cc_srvid_block *srvid; + + while((srvid = ll_iter_next(&it2))) + { + if(srvid->blocked_till > 0 && sid_eq((struct cc_srvid *)srvid, cur_srvid)) + { + if(ignore_time || srvid->blocked_till <= utime) + { + ll_iter_remove_data(&it2); + } + } + } + } + } +} + +static int8_t cc_request_timeout(struct s_client *cl) +{ + struct s_reader *rdr = cl->reader; + struct cc_data *cc = cl->cc; + struct timeb timeout; + struct timeb cur_time; + + if(!cc || !cc->ecm_busy) + { + return 0; + } + + cs_ftime(&cur_time); + + timeout = cc->ecm_time; + int32_t tt = rdr->cc_reconnect; + + if(tt <= 0) + { + tt = DEFAULT_CC_RECONNECT; + } + + add_ms_to_timeb(&timeout, tt); + + return (comp_timeb(&cur_time, &timeout) >= 0); +} + +/** + * reader + * sends a ecm request to the connected CCCam Server + */ +int32_t cc_send_ecm(struct s_client *cl, ECM_REQUEST *er) +{ + struct s_reader *rdr = cl->reader; + + //cs_log_dbg(D_TRACE, "%s cc_send_ecm", getprefix()); + if(!rdr->tcp_connected) + { + cc_cli_connect(cl); + } + + int32_t n; + struct cc_data *cc = cl->cc; + struct cc_card *card = NULL; + LL_ITER it; + ECM_REQUEST *cur_er; + struct timeb cur_time; + cs_ftime(&cur_time); + + if(!cc || (cl->pfd < 1) || !rdr->tcp_connected) + { + if(er) + { + cs_log_dbg(D_READER, "%s server not init! ccinit=%d pfd=%d", rdr->label, cc ? 1 : 0, cl->pfd); + write_ecm_answer(rdr, er, E_NOTFOUND, E2_CCCAM_NOCARD, NULL, NULL, 0, NULL); + } + //cc_cli_close(cl); + return 0; + } + + if(rdr->tcp_connected != 2) + { + cs_log_dbg(D_READER, "%s Waiting for CARDS", getprefix()); + return 0; + } + + // No Card? Waiting for shares + if(!ll_has_elements(cc->cards)) + { + cs_log_dbg(D_READER, "%s NO CARDS!", getprefix()); + return 0; + } + + cc->just_logged_in = 0; + + if(!cc->extended_mode) + { + // Without extended mode, only one ecm at a time could be send + // this is a limitation of "O" CCCam + if(cc->ecm_busy > 0) // Unlock by NOK or ECM ACK + { + cs_log_dbg(D_READER, "%s ecm trylock: ecm busy, retrying later after msg-receive", getprefix()); + + if(!cc_request_timeout(cl)) + { + return 0; // pending send... + } + + if(!cc_cycle_connection(cl)) + { + return 0; + } + } + + cc->ecm_busy = 1; + cs_log_dbg(D_READER, "cccam: ecm trylock: got lock"); + } + + int32_t processed_ecms = 0; + + do + { + cc->ecm_time = cur_time; + + // Search next ECM to send + if((n = cc_get_nxt_ecm(cl)) < 0) + { + if(!cc->extended_mode) + { + cc->ecm_busy = 0; + } + + cs_log_dbg(D_READER, "%s no ecm pending!", getprefix()); + + if(!cc_send_pending_emms(cl)) + { + send_cmd05_answer(cl); + } + + return 0; // no queued ecms + } + + cur_er = &cl->ecmtask[n]; + cur_er->rc = E_ALREADY_SENT; // mark ECM as already send + cs_log_dbg(D_READER, "cccam: ecm-task %d", cur_er->idx); + + // sleepsend support + static const char *typtext[] = { "ok", "invalid", "sleeping" }; + + if(cc->sleepsend && cl->stopped) + { + if(cur_er->srvid == cl->lastsrvid && cur_er->caid == cl->lastcaid && cur_er->pid == cl->lastpid) + { + cs_log("%s is stopped - requested by server (%s)", cl->reader->label, typtext[cl->stopped]); + + if(!cc->extended_mode) + { + cc->ecm_busy = 0; + } + + write_ecm_answer(rdr, cur_er, E_STOPPED, 0, NULL, NULL, 0, NULL); + return 0; + } + else + { + cl->stopped = 0; + } + } + + cl->lastsrvid = cur_er->srvid; + cl->lastcaid = cur_er->caid; + cl->lastpid = cur_er->pid; + // sleepsend support end + + struct cc_srvid cur_srvid; + cur_srvid.sid = cur_er->srvid; + cur_srvid.chid = cur_er->chid; + cur_srvid.ecmlen = cur_er->ecmlen; + + cs_readlock(__func__, &cc->cards_busy); + + // forward_origin + if(cfg.cc_forward_origin_card && cur_er->origin_reader == rdr && cur_er->origin_card) + { + it = ll_iter_create(cc->cards); + struct cc_card *ncard; + + while((ncard = ll_iter_next(&it))) + { + if(ncard == cur_er->origin_card) // Search the origin card + { + card = ncard; // found it, use it! + break; + } + } + } + + if(!card) + { + reopen_sids(cc, 0, cur_er, &cur_srvid); + card = get_matching_card(cl, cur_er, 0); + } + + if(!card && has_srvid(rdr->client, cur_er)) + { + reopen_sids(cc, 1, cur_er, &cur_srvid); + card = get_matching_card(cl, cur_er, 0); + } + + if(card) + { + uint8_t *ecmbuf; + if(!cs_malloc(&ecmbuf, cur_er->ecmlen + 13)) + { + cs_readunlock(__func__, &cc->cards_busy); + break; + } + + // build ecm message + ecmbuf[0] = cur_er->caid >> 8; + ecmbuf[1] = cur_er->caid & 0xff; + ecmbuf[2] = cur_er->prid >> 24; + ecmbuf[3] = cur_er->prid >> 16; + ecmbuf[4] = cur_er->prid >> 8; + ecmbuf[5] = cur_er->prid & 0xff; + ecmbuf[6] = card->id >> 24; + ecmbuf[7] = card->id >> 16; + ecmbuf[8] = card->id >> 8; + ecmbuf[9] = card->id & 0xff; + ecmbuf[10] = cur_er->srvid >> 8; + ecmbuf[11] = cur_er->srvid & 0xff; + ecmbuf[12] = cur_er->ecmlen & 0xff; + memcpy(ecmbuf + 13, cur_er->ecm, cur_er->ecmlen); + + uint8_t send_idx = 1; + if(cc->extended_mode) + { + cc->server_ecm_idx++; + + if(cc->server_ecm_idx >= 256) + { + cc->server_ecm_idx = 1; + } + + cc->g_flag = cc->server_ecm_idx; // Flag is used as index! + send_idx = cc->g_flag; + } + + struct cc_extended_ecm_idx *eei = get_extended_ecm_idx(cl, send_idx, 0); + if(eei) + { + eei->ecm_idx = cur_er->idx; + eei->card = card; + eei->cccam_id = card->id; + eei->srvid = cur_srvid; + } + else + { + eei = add_extended_ecm_idx(cl, send_idx, cur_er->idx, card, cur_srvid, 0); + if(!eei) + { + NULLFREE(ecmbuf); + cs_readunlock(__func__, &cc->cards_busy); + break; + } + } + eei->tps = cur_er->tps; + + rdr->currenthops = card->hop; + rdr->card_status = CARD_INSERTED; + + cs_log_dbg( D_READER, "%s sending ecm for sid %04X(%d) to card %08x, hop %d, ecmtask %d", + getprefix(), cur_er->srvid, cur_er->ecmlen, card->id, card->hop, cur_er->idx); + + cl->reader->last_s = time(NULL); + cc_cmd_send(cl, ecmbuf, cur_er->ecmlen + 13, MSG_CW_ECM); // send ecm + + NULLFREE(ecmbuf); + + // For EMM + set_au_data(cl, rdr, card, cur_er); + cs_readunlock(__func__, &cc->cards_busy); + + processed_ecms++; + if(cc->extended_mode) + { + continue; // process next pending ecm! + } + + return 0; + } + else + { + // When connecting, it could happen than ecm requests come before all cards are received. + // So if the last Message was a MSG_NEW_CARD, this "card receiving" is not already done + // if this happens, we do not autoblock it and do not set rc status + // So fallback could resolve it + if(cc->last_msg != MSG_NEW_CARD && cc->last_msg != MSG_NEW_CARD_SIDINFO + && cc->last_msg != MSG_CARD_REMOVED && !cc->just_logged_in) + { + cs_log_dbg(D_READER, "%s no suitable card on server", getprefix()); + + write_ecm_answer(rdr, cur_er, E_NOTFOUND, E2_CCCAM_NOCARD, NULL, NULL, 0, NULL); + + //cur_er->rc = 1; + //cur_er->rcEx = 0; + //cs_sleepms(300); + rdr->last_s = rdr->last_g; + + reopen_sids(cc, 0, cur_er, &cur_srvid); + } + else + { + // We didn't find a card and the last message was MSG_CARD_REMOVED, + // so we wait for a new card and process die ecm later + cur_er->rc = E_WAITING; // mark as waiting + } + } + + cs_readunlock(__func__, &cc->cards_busy); + + // process next pending ecm! + } + while(cc->extended_mode || processed_ecms == 0); + + // Now mark all waiting as unprocessed + int8_t i; + for(i = 0; i < cfg.max_pending; i++) + { + er = &cl->ecmtask[i]; + if(er->rc == E_WAITING) + { + er->rc = E_UNHANDLED; + } + } + + if(!cc->extended_mode) + { + cc->ecm_busy = 0; + } + + return 0; +} + +/*int32_t cc_abort_user_ecms() +{ + int32_t n, i; + time_t t;//, tls; + struct cc_data *cc = rdr->cc; + + t = time((time_t *)0); + for(i = 1, n = 1; i < cfg.max_pending; i++) + { + if((t-cl->ecmtask[i].tps.time > ((cfg.ctimeout + 500) / 1000) + 1) && (cl->ecmtask[i].rc >= 10)) // drop timeouts + { + cl->ecmtask[i].rc=0; + } + + int32_t td = abs(comp_timeb(&ecmtask[i].tps, &cc->found->tps); + if(ecmtask[i].rc >= 10 && ecmtask[i].cidx == cc->found->cidx && &ecmtask[i] != cc->found) + { + cs_log("aborting idx:%d caid:%04x client:%d timedelta:%d",ecmtask[i].idx,ecmtask[i].caid,ecmtask[i].cidx,td); + ecmtask[i].rc = 0; + ecmtask[i].rcEx = 7; + write_ecm_answer(rdr, fd_c2m, &ecmtask[i], 0, NULL); + } + } + return n; +}*/ + +int32_t cc_send_pending_emms(struct s_client *cl) +{ + struct cc_data *cc = cl->cc; + if(!cc) + { + return 0; + } + + LL_ITER it = ll_iter_create(cc->pending_emms); + uint8_t *emmbuf; + int32_t size = 0; + + if((emmbuf = ll_iter_next(&it))) + { + if(!cc->extended_mode) + { + if(cc->ecm_busy > 0) // Unlock by NOK or ECM ACK + { + return 0; // send later with cc_send_ecm + } + cc->ecm_busy = 1; + } + + // Support for emmsize > 256 bytes + size = (emmbuf[11] | (emmbuf[2] << 8)) + 12; + emmbuf[2] = 0; + + cc->just_logged_in = 0; + cs_ftime(&cc->ecm_time); + + cs_log_dbg(D_EMM, "%s emm send for card %08X", getprefix(), b2i(4, emmbuf + 7)); + + cc_cmd_send(cl, emmbuf, size, MSG_EMM_ACK); // send emm + cl->last = time(NULL); + cl->reader->last_g = time(NULL); + cl->reader->last_s = time(NULL); + + ll_iter_remove_data(&it); + } + + return size; +} + +/** + * READER only: + * find card by hexserial + */ +struct cc_card *get_card_by_hexserial(struct s_client *cl, uint8_t *hexserial, uint16_t caid) +{ + struct cc_data *cc = cl->cc; + LL_ITER it = ll_iter_create(cc->cards); + struct cc_card *card; + + while((card = ll_iter_next(&it))) + { + if(card->caid == caid && memcmp(card->hexserial, hexserial, 8) == 0) // found it! + { + break; + } + } + + return card; +} + +/** + * EMM Procession + * Copied from http://85.17.209.13:6100/file/8ec3c0c5d257/systems/cardclient/cccam2.c + * ProcessEmm + */ +int32_t cc_send_emm(EMM_PACKET *ep) +{ + struct s_client *cl = cur_client(); + struct s_reader *rdr = cl->reader; + + if(!rdr->tcp_connected) + { + cc_cli_connect(cl); + } + + struct cc_data *cc = cl->cc; + + if(!cc || (cl->pfd < 1) || !rdr->tcp_connected) + { + cs_log_dbg(D_READER, "%s server not init! ccinit=%d pfd=%d", getprefix(), cc ? 1 : 0, cl->pfd); + return 0; + } + + if(rdr->audisabled) + { + cs_log_dbg(D_READER, "%s au is disabled", getprefix()); + return 0; + } + + uint16_t caid = b2i(2, ep->caid); + + // Last used card is first card of current_cards + cs_readlock(__func__, &cc->cards_busy); + + struct cc_card *emm_card = cc->last_emm_card; + + if(!emm_card) + { + uint8_t hs[8]; + char tmp_dbg[17]; + + cc_UA_oscam2cccam(ep->hexserial, hs, caid); + + cs_log_dbg(D_EMM, "%s au info: searching card for caid %04X oscam-UA: %s", + getprefix(), b2i(2, ep->caid), cs_hexdump(0, ep->hexserial, 8, tmp_dbg, sizeof(tmp_dbg))); + + cs_log_dbg(D_EMM, "%s au info: searching card for caid %04X cccam-UA: %s", + getprefix(), b2i(2, ep->caid), cs_hexdump(0, hs, 8, tmp_dbg, sizeof(tmp_dbg))); + + emm_card = get_card_by_hexserial(cl, hs, caid); + } + + if(!emm_card) // Card for emm not found! + { + cs_log_dbg(D_EMM, "%s emm for client %8lX not possible, no card found!", + getprefix(), (unsigned long)ep->client->thread); + + cs_readunlock(__func__, &cc->cards_busy); + return 0; + } + + cs_log_dbg(D_EMM, "%s emm received for client %8lX caid %04X for card %08X", + getprefix(), (unsigned long)ep->client->thread, caid, emm_card->id); + + int32_t size = ep->emmlen + 12; + uint8_t *emmbuf; + + if(!cs_malloc(&emmbuf, size)) + { + cs_readunlock(__func__, &cc->cards_busy); + return 0; + } + + // build ecm message + emmbuf[0] = ep->caid[0]; + emmbuf[1] = ep->caid[1]; + emmbuf[2] = ep->emmlen >> 8; // Support for emm len > 256 bytes + emmbuf[3] = ep->provid[0]; + emmbuf[4] = ep->provid[1]; + emmbuf[5] = ep->provid[2]; + emmbuf[6] = ep->provid[3]; + emmbuf[7] = emm_card->id >> 24; + emmbuf[8] = emm_card->id >> 16; + emmbuf[9] = emm_card->id >> 8; + emmbuf[10] = emm_card->id & 0xff; + emmbuf[11] = ep->emmlen & 0xff; + memcpy(emmbuf + 12, ep->emm, ep->emmlen); + + cs_readunlock(__func__, &cc->cards_busy); + + ll_append(cc->pending_emms, emmbuf); + cc_send_pending_emms(cl); + + return 1; +} + +void cc_free_card(struct cc_card *card) +{ + if(!card) + { + return; + } + + ll_destroy_data(&card->providers); + ll_destroy_data(&card->badsids); + ll_destroy_data(&card->goodsids); + ll_destroy_data(&card->remote_nodes); + + add_garbage(card); +} + +struct cc_card *cc_get_card_by_id(uint32_t card_id, LLIST *cards) +{ + if(!cards) + { + return NULL; + } + + LL_ITER it = ll_iter_create(cards); + struct cc_card *card; + + while((card = ll_iter_next(&it))) + { + if(card->id == card_id) + { + break; + } + } + + return card; +} + +void cc_free_cardlist(LLIST *card_list, int32_t destroy_list) +{ + if(card_list) + { + LL_ITER it = ll_iter_create(card_list); + struct cc_card *card; + + while((card = ll_iter_next_remove(&it))) + { + cc_free_card(card); + } + + if(destroy_list) + { + ll_destroy(&card_list); + } + } +} + +/** + * Clears and free the cc datas + */ +void cc_free(struct s_client *cl) +{ + struct cc_data *cc = cl->cc; + if(!cc) + { + return; + } + + cl->cc = NULL; + + cs_writelock(__func__, &cc->lockcmd); + + cs_log_dbg(D_TRACE, "exit cccam1/3"); + cc_free_cardlist(cc->cards, 1); + ll_destroy_data(&cc->pending_emms); + free_extended_ecm_idx(cc); + ll_destroy_data(&cc->extended_ecm_idx); + + cs_writeunlock(__func__, &cc->lockcmd); + + cs_log_dbg(D_TRACE, "exit cccam2/3"); + + add_garbage(cc->prefix); + add_garbage(cc); + + cs_log_dbg(D_TRACE, "exit cccam3/3"); +} + +int32_t is_null_dcw(uint8_t *dcw) +{ + int32_t i; + for(i = 0; i < 15; i++) + if(dcw[i]) + { return 0; } + return 1; +} + +int32_t check_extended_mode(struct s_client *cl, char *msg) +{ + // Extended mode: if PARTNER String is ending with [PARAM], extended mode is activated + // For future compatibilty the syntax should be compatible with + // [PARAM1,PARAM2...PARAMn] + // + // EXT: Extended ECM Mode: Multiple ECMs could be send and received + // ECMs are numbered, Flag (byte[0] is the index + // + // SID: Exchange of good sids/bad sids activated (like cccam 2.2.x) + // card exchange command MSG_NEW_CARD_SIDINFO instead MSG_NEW_CARD is used + // + // SLP: Sleepsend supported, like camd35 + // + + struct cc_data *cc = cl->cc; + char *saveptr1 = NULL; + int32_t has_param = 0; + char *p = strtok_r(msg, "[", &saveptr1); + + while(p) + { + p = strtok_r(NULL, ",]", &saveptr1); + if(p && strncmp(p, "EXT", 3) == 0) + { + cc->extended_mode = 1; + cs_log_dbg(D_CLIENT, "%s extended ECM mode", getprefix()); + has_param = 1; + } + else if(p && strncmp(p, "SID", 3) == 0) + { + cc->cccam220 = 1; + cs_log_dbg(D_CLIENT, "%s extra SID mode", getprefix()); + has_param = 1; + } + else if(p && strncmp(p, "SLP", 3) == 0) + { + cc->sleepsend = 1; + cs_log_dbg(D_CLIENT, "%s sleepsend", getprefix()); + has_param = 1; + } +#ifdef CS_CACHEEX_AIO + else if(p && strncmp(p, "LGF", 3) == 0) + { + cc->extended_lg_flagged_cws = 1; + cs_log_dbg(D_CLIENT, "%s lg-flagged CWs", getprefix()); + has_param = 1; + } +#endif + } + return has_param; +} + +void cc_idle(void) +{ + struct s_client *cl = cur_client(); + struct s_reader *rdr = cl->reader; + struct cc_data *cc = cl->cc; + + if(!cl->udp_fd) + { + cc_cli_close(cl, 0); + } + + if(rdr && !rdr->tcp_connected && (rdr->cc_keepalive || (rdr->tcp_ito == -1 && (rdr->last_s != 0 || rdr->last_g != 0)))) + { + cc_cli_connect(cl); + } + + if(!rdr || !rdr->tcp_connected || !cl || !cc) + { + return; + } + + time_t now = time(NULL); + + if(rdr->cc_keepalive) + { +#ifdef CS_CACHEEX_AIO + if(!cl->cacheex_aio_checked && ((cl->account && cl->account->cacheex.mode > 0) || (cl->reader && cl->reader->cacheex.mode > 0))) + { + cc_cacheex_feature_request(cl); + cl->cacheex_aio_checked = 1; + } +#endif + if(cc_cmd_send(cl, NULL, 0, MSG_KEEPALIVE) > 0) + { + cs_log_dbg(D_READER, "cccam: keepalive"); + + if(cl) + { + cl->last = now; + } + + if(cl->reader) + { + cl->reader->last_s = now; + cl->reader->last_g = now; + } + } + return; + } + else + { + //cs_log("last_s - now = %d, last_g - now = %d, tcp_ito=%d", + // abs(rdr->last_s - now), abs(rdr->last_g - now), rdr->tcp_ito); + + // check inactivity timeout + if(rdr->tcp_ito > 0) + { + // inactivity timeout is entered in seconds in webif! + if((llabs(rdr->last_s - now) > rdr->tcp_ito) && (llabs(rdr->last_g - now) > rdr->tcp_ito)) + { + rdr_log_dbg(rdr, D_READER, "inactive_timeout, close connection (fd=%d)", rdr->client->pfd); + network_tcp_connection_close(rdr, "inactivity"); + return; + } + } + + // check read timeout + int32_t rto = llabs(rdr->last_g - now); + //cs_log("last_g - now = %d, rto=%d", rto, rdr->tcp_rto); + + // this is also entered in seconds, actually its an receive timeout! + if(rto > (rdr->tcp_rto) && (rdr->last_g != 0 || rdr->last_s != 0) && rdr->last_s != rdr->last_g) + { + rdr_log_dbg(rdr, D_READER, "read timeout, close connection (fd=%d)", rdr->client->pfd); + network_tcp_connection_close(rdr, "rto"); + return; + } + } +} + +struct cc_card *read_card(uint8_t *buf, int32_t buflen, int32_t ext) +{ + struct cc_card *card; + int16_t nprov, nassign = 0, nreject = 0; + int32_t offset = 21; + + if(buflen < 21) + { + return NULL; + } + + if(!cs_malloc(&card, sizeof(struct cc_card))) + { + return NULL; + } + + card->providers = ll_create("providers"); + card->badsids = ll_create("badsids"); + card->goodsids = ll_create("goodsids"); + card->remote_nodes = ll_create("remote_nodes"); + card->id = b2i(4, buf); + card->remote_id = b2i(4, buf + 4); + card->caid = b2i(2, buf + 8); + card->hop = buf[10]; + card->reshare = buf[11]; + card->is_ext = ext; + card->card_type = CT_REMOTECARD; + memcpy(card->hexserial, buf + 12, 8); // HEXSERIAL!! + + //cs_log_dbg(D_CLIENT, "cccam: card %08x added, caid %04X, hop %d, key %s, count %d", card->id, card->caid, + // card->hop, cs_hexdump(0, card->hexserial, 8, tmp_dbg, sizeof(tmp_dbg)), ll_count(cc->cards)); + + nprov = buf[20]; + + if(ext) + { + if(buflen < 23) + { + cc_free_card(card); + return NULL; + } + + nassign = buf[21]; + nreject = buf[22]; + + offset += 2; + } + + if(buflen < (offset + (nprov * 7))) + { + cc_free_card(card); + return NULL; + } + + int16_t i; + for(i = 0; i < nprov; i++) // providers + { + struct cc_provider *prov; + if(!cs_malloc(&prov, sizeof(struct cc_provider))) + { + break; + } + + prov->prov = b2i(3, buf + offset); + if(prov->prov == 0xFFFFFF && caid_is_betacrypt(card->caid)) + { + prov->prov = i; + } + + memcpy(prov->sa, buf + offset + 3, 4); + //cs_log_dbg(D_CLIENT, " prov %d, %06x, sa %08x", i + 1, prov->prov, b2i(4, prov->sa)); + + ll_append(card->providers, prov); + offset += 7; + } + + if(ext) + { + if(buflen < (offset + (nassign * 2) + (nreject * 2))) + { + cc_free_card(card); + return NULL; + } + + for(i = 0; i < nassign; i++) + { + uint16_t sid = b2i(2, buf + offset); + //cs_log_dbg(D_CLIENT, " assigned sid = %04X, added to good sid list", sid); + + struct cc_srvid *srvid; + if(!cs_malloc(&srvid, sizeof(struct cc_srvid))) + { + break; + } + + srvid->sid = sid; + srvid->chid = 0; + srvid->ecmlen = 0; + ll_append(card->goodsids, srvid); + offset += 2; + } + + for(i = 0; i < nreject; i++) + { + uint16_t sid = b2i(2, buf + offset); + //cs_log_dbg(D_CLIENT, " rejected sid = %04X, added to sid block list", sid); + + struct cc_srvid_block *srvid; + if(!cs_malloc(&srvid, sizeof(struct cc_srvid_block))) + { + break; + } + + srvid->sid = sid; + srvid->chid = 0; + srvid->ecmlen = 0; + srvid->blocked_till = 0; + ll_append(card->badsids, srvid); + offset += 2; + } + } + + if(buflen < (offset + 1)) + { + return card; + } + + int16_t remote_count = buf[offset]; + offset++; + + if(buflen < (offset + (remote_count * 8))) + { + cc_free_card(card); + return NULL; + } + + for(i = 0; i < remote_count; i++) + { + uint8_t *remote_node; + if(!cs_malloc(&remote_node, 8)) + { + break; + } + + memcpy(remote_node, buf + offset, 8); + ll_append(card->remote_nodes, remote_node); + offset += 8; + } + + return card; +} + +void cc_card_removed(struct s_client *cl, uint32_t shareid) +{ + struct cc_data *cc = cl->cc; + struct cc_card *card; + LL_ITER it = ll_iter_create(cc->cards); + + while((card = ll_iter_next(&it))) + { + if(card->id == shareid) // && card->sub_id == b2i (3, buf + 9)) { + { + //cs_log_dbg(D_CLIENT, "cccam: card %08x removed, caid %04X, count %d", + // card->id, card->caid, ll_count(cc->cards)); + + ll_iter_remove(&it); + if(cc->last_emm_card == card) + { + cc->last_emm_card = NULL; + cs_log_dbg(D_READER, "%s current card %08x removed!", getprefix(), card->id); + } + + free_extended_ecm_idx_by_card(cl, card, 1); + + if(card->hop == 1) + { + cc->num_hop1--; + } + else if(card->hop == 2) + { + cc->num_hop2--; + } + else + { + cc->num_hopx--; + } + + if(card->reshare == 0) + { + cc->num_reshare0--; + } + else if(card->reshare == 1) + { + cc->num_reshare1--; + } + else if(card->reshare == 2) + { + cc->num_reshare2--; + } + else + { + cc->num_resharex--; + } + + cs_log_dbg(D_TRACE, "%s card removed: id %8X remoteid %8X caid %4X hop %d reshare %d originid %8X cardtype %d", + getprefix(), card->id, card->remote_id, card->caid, card->hop, card->reshare, card->origin_id, card->card_type); + + cc_free_card(card); + cc->card_removed_count++; + //break; + } + } +} + +void move_card_to_end(struct s_client *cl, struct cc_card *card_to_move) +{ + struct cc_data *cc = cl->cc; + LL_ITER it = ll_iter_create(cc->cards); + struct cc_card *card; + + while((card = ll_iter_next(&it))) + { + if(card == card_to_move) + { + ll_iter_remove(&it); + break; + } + } + + if(card) + { + cs_log_dbg(D_READER, "%s Moving card %08X to the end...", getprefix(), card_to_move->id); + free_extended_ecm_idx_by_card(cl, card, 0); + ll_append(cc->cards, card_to_move); + } +} + +/*void fix_dcw(uint8_t *dcw) +{ + int32_t i; + + for(i = 0; i < 16; i += 4) + { + dcw[i + 3] = (dcw[i] + dcw[i + 1] + dcw[i + 2]) & 0xFF; + } +}*/ + +void addParam(char *param, size_t param_sz, char *value) +{ + if (!param_sz) { + cs_log("ERROR! Sizeof param is zero!"); + return; + } + + if (param && value) { + if ((cs_strlen(param) + cs_strlen(value) + 1) < param_sz) { + if (cs_strlen(param) < 4) { + cs_strncat(param, value, param_sz); + } + else { + cs_strncat(param, ",", param_sz); + cs_strncat(param, value, param_sz); + } + } + else { + cs_log("ERROR! Buffer overflow in addParam!"); + } + } + else { + cs_log("ERROR! Booth param and value pointer NULL!"); + } +} + +static void chk_peer_node_for_oscam(struct cc_data *cc) +{ + if(!cc->is_oscam_cccam) // Allready discovered oscam-cccam + { + uint16_t sum = 0x1234; + uint16_t recv_sum = (cc->peer_node_id[6] << 8) | cc->peer_node_id[7]; + int32_t i; + + for(i = 0; i < 6; i++) + { + sum += cc->peer_node_id[i]; + } + + // Create special data to detect oscam-cccam + cc->is_oscam_cccam = sum == recv_sum; + } +} + +#ifdef MODULE_CCCSHARE +static void cc_s_idle(struct s_client *cl) +{ + cs_log_dbg(D_TRACE, "ccc idle %s", username(cl)); + if(cfg.cc_keep_connected) + { +#ifdef CS_CACHEEX_AIO + if(!cl->cacheex_aio_checked && ((cl->account && cl->account->cacheex.mode > 0) || (cl->reader && cl->reader->cacheex.mode > 0))) + { + cc_cacheex_feature_request(cl); + cl->cacheex_aio_checked = 1; + } +#endif + cc_cmd_send(cl, NULL, 0, MSG_KEEPALIVE); + cl->last = time(NULL); + } + else + { + cs_log_dbg(D_CLIENT, "%s keepalive after maxidle is reached", getprefix()); + cs_disconnect_client(cl); + } +} +#endif + +int32_t cc_parse_msg(struct s_client *cl, uint8_t *buf, int32_t l) +{ + struct s_reader *rdr = (cl->typ == 'c') ? NULL : cl->reader; + int32_t ret = buf[1]; + struct cc_data *cc = cl->cc; + char tmp_dbg[33]; + + if(!cc || cl->kill) + { + return -1; + } + + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "%s parse_msg=%d", getprefix(), buf[1]); + + uint8_t *data = buf + 4; + + if(l < 4) + { + return -1; + } + + memcpy(&cc->receive_buffer, data, l - 4); + cc->last_msg = buf[1]; + + switch(buf[1]) + { + case MSG_CLI_DATA: + { + cs_log_dbg(D_CLIENT, "cccam: client data ack"); + break; + } + + case MSG_SRV_DATA: + { + l -= 4; + cs_log_dbg(D_READER, "%s MSG_SRV_DATA (payload=%d, hex=%02X)", getprefix(), l, l); + data = cc->receive_buffer; + + if(l == 0x48) // 72 bytes: normal server data + { + cs_writelock(__func__, &cc->cards_busy); + + cc_free_cardlist(cc->cards, 0); + free_extended_ecm_idx(cc); + cc->last_emm_card = NULL; + cc->num_hop1 = 0; + cc->num_hop2 = 0; + cc->num_hopx = 0; + cc->num_reshare0 = 0; + cc->num_reshare1 = 0; + cc->num_reshare2 = 0; + cc->num_resharex = 0; + + memcpy(cc->peer_node_id, data, 8); + memcpy(cc->peer_version, data + 8, 8); + + memcpy(cc->cmd0b_aeskey, cc->peer_node_id, 8); + memcpy(cc->cmd0b_aeskey + 8, cc->peer_version, 8); + + cs_strncpy(cc->remote_version, (char *)data + 8, sizeof(cc->remote_version)); + cs_strncpy(cc->remote_build, (char *)data + 40, sizeof(cc->remote_build)); + cc->remote_build_nr = atoi(cc->remote_build); + + // multics server response + if(data[33] == 'M' && data[34] == 'C' && data[35] == 'S') + { + cc->multics_mode = 2; // multics server finaly confirmed. + cc->multics_version[0] = data[37]; + cc->multics_version[1] = data[38]; + cs_log_dbg(D_READER, "multics detected: %s!", getprefix()); + } + + cs_writeunlock(__func__, &cc->cards_busy); + + cs_log_dbg(D_READER, "%s remote server %s running v%s (%s)", getprefix(), cs_hexdump(0, + cc->peer_node_id, 8, tmp_dbg, sizeof(tmp_dbg)), cc->remote_version, cc->remote_build); + + chk_peer_node_for_oscam(cc); + // Trick: when discovered partner is an Oscam Client, then we send him our version string: + if(cc->is_oscam_cccam) + { + uint8_t token[256]; +#ifdef CS_CACHEEX_AIO + snprintf((char *)token, sizeof(token), "PARTNER: OSCam %s (%s) [EXT,SID,SLP,LGF]", +#else + snprintf((char *)token, sizeof(token), "PARTNER: OSCam %s (%s) [EXT,SID,SLP]", +#endif + CS_VERSION, CS_TARGET); + + cc_cmd_send(cl, token, cs_strlen((char *)token) + 1, MSG_CW_NOK1); + } + + cc->cmd05_mode = MODE_PLAIN; + // + // Keyoffset is payload-size: + // + } + else if(l >= 0x00 && l <= 0x0F) + { + cs_writelock(__func__, &cc->cards_busy); + cc->cmd05_offset = l; + cs_writeunlock(__func__, &cc->cards_busy); + // + // 16..43 bytes: RC4 encryption + // + } + else if((l >= 0x10 && l <= 0x1f) || (l >= 0x24 && l <= 0x2b)) + { + cs_writelock(__func__, &cc->cards_busy); + cc_init_crypt(&cc->cmd05_cryptkey, data, l); + cc->cmd05_mode = MODE_RC4_CRYPT; + cs_writeunlock(__func__, &cc->cards_busy); + // + // 32 bytes: set AES128 key for CMD_05, Key=16 bytes offset keyoffset + // + } + else if(l == 0x20) + { + cs_writelock(__func__, &cc->cards_busy); + memcpy(cc->cmd05_aeskey, data + cc->cmd05_offset, 16); + cc->cmd05_mode = MODE_AES; + cs_writeunlock(__func__, &cc->cards_busy); + // + // 33 bytes: xor-algo mit payload-bytes, offset keyoffset + // + } + else if(l == 0x21) + { + cs_writelock(__func__, &cc->cards_busy); + cc_init_crypt(&cc->cmd05_cryptkey, data + cc->cmd05_offset, l); + cc->cmd05_mode = MODE_CC_CRYPT; + cs_writeunlock(__func__, &cc->cards_busy); + // + // 34 bytes: cmd_05 plain back + // + } + else if(l == 0x22) + { + cs_writelock(__func__, &cc->cards_busy); + cc->cmd05_mode = MODE_PLAIN; + cs_writeunlock(__func__, &cc->cards_busy); + // + // 35 bytes: Unknown!! 2 256 byte keys exchange + // + } + else if(l == 0x23) + { + cs_writelock(__func__, &cc->cards_busy); + cc->cmd05_mode = MODE_UNKNOWN; + cs_writeunlock(__func__, &cc->cards_busy); + cc_cycle_connection(cl); + // + // 44 bytes: set aes128 key, Key=16 bytes [Offset=len(password)] + // + } + else if(l == 0x2c) + { + cs_writelock(__func__, &cc->cards_busy); + memcpy(cc->cmd05_aeskey, data + cs_strlen(rdr->r_pwd), 16); + cc->cmd05_mode = MODE_AES; + cs_writeunlock(__func__, &cc->cards_busy); + // + // 45 bytes: set aes128 key, Key=16 bytes [Offset=len(username)] + // + } + else if(l == 0x2d) + { + cs_writelock(__func__, &cc->cards_busy); + memcpy(cc->cmd05_aeskey, data + cs_strlen(rdr->r_usr), 16); + cc->cmd05_mode = MODE_AES; + cs_writeunlock(__func__, &cc->cards_busy); + // + //Unknown!! + // + } + else + { + cs_log_dbg(D_READER, "%s received improper MSG_SRV_DATA! No change to current mode, mode=%d", + getprefix(), cc->cmd05_mode); + break; + } + + cs_log_dbg(D_READER, "%s MSG_SRV_DATA MODE=%s, len=%d", getprefix(), cmd05_mode_name[cc->cmd05_mode], l); + break; + } + + case MSG_NEW_CARD_SIDINFO: + case MSG_NEW_CARD: + { + if(l < 16 || !rdr) + { + break; + } + + uint16_t caid = b2i(2, buf + 12); + + // filter caid == 0 and maxhop + if(!caid || buf[14] >= rdr->cc_maxhops + 1) + { + break; + } + + // filter mindown + if(buf[15] < rdr->cc_mindown) + { + break; + } + + // caid check + if(!chk_ctab(caid, &rdr->ctab)) + { + break; + } + + rdr->tcp_connected = 2; // we have card + rdr->card_status = CARD_INSERTED; + + cs_writelock(__func__, &cc->cards_busy); + struct cc_card *card = read_card(data, l - 4, buf[1] == MSG_NEW_CARD_SIDINFO); + + if(!card) + { + cs_writeunlock(__func__, &cc->cards_busy); + break; + } + + card->origin_reader = rdr; + card->origin_id = card->id; + card->grp = rdr->grp; + card->rdr_reshare = rdr->cc_reshare > -1 ? rdr->cc_reshare : cfg.cc_reshare; + + // Check if this card is from us + LL_ITER it = ll_iter_create(card->remote_nodes); + uint8_t *node_id; + + while((node_id = ll_iter_next(&it))) + { + if(memcmp(node_id, cc_node_id, sizeof(cc_node_id)) == 0) // this card is from us! + { + cs_log_dbg(D_READER, "filtered card because of recursive nodeid: id=%08X, caid=%04X", card->id, card->caid); + cc_free_card(card); + card = NULL; + break; + } + } + +#ifdef MODULE_CCCSHARE + // Check Ident filter + if(card) + { + if(!chk_ident(&rdr->ftab, card)) + { + cc_free_card(card); + card = NULL; + } + } +#endif + if(card) + { + // Check if we already have this card + it = ll_iter_create(cc->cards); + struct cc_card *old_card; + + while((old_card = ll_iter_next(&it))) + { + // We already have this card, delete it + if(old_card->id == card->id || same_card(old_card, card)) + { + cc_free_card(card); + card = old_card; + break; + } + } + + if(!old_card) + { + card->card_type = CT_REMOTECARD; + ll_append(cc->cards, card); + set_au_data(cl, rdr, card, NULL); + cc->card_added_count++; + card->hop++; + + if(card->hop == 1) + { + cc->num_hop1++; + } + else if(card->hop == 2) + { + cc->num_hop2++; + } + else + { + cc->num_hopx++; + } + + if(card->reshare == 0) + { + cc->num_reshare0++; + } + else if(card->reshare == 1) + { + cc->num_reshare1++; + } + else if(card->reshare == 2) + { + cc->num_reshare2++; + } + else + { + cc->num_resharex++; + } + + cs_log_dbg(D_TRACE, "%s card added: id %8X remoteid %8X caid %4X hop %d reshare %d originid %8X cardtype %d", + getprefix(), card->id, card->remote_id, card->caid, card->hop, card->reshare, card->origin_id, card->card_type); + } + } + + cs_writeunlock(__func__, &cc->cards_busy); + +#ifdef MODULE_CCCSHARE + cccam_refresh_share(); +#endif + break; + } + + case MSG_CARD_REMOVED: + { + if(l < 8) + { + break; + } + + cs_writelock(__func__, &cc->cards_busy); + cc_card_removed(cl, b2i(4, buf + 4)); + cs_writeunlock(__func__, &cc->cards_busy); + break; + } + + case MSG_SLEEPSEND: + { + // Server sends SLEEPSEND + if(l < 5) + { + break; + } + + if(!cfg.c35_suppresscmd08) + { + if(buf[4] == 0xFF) + { + cl->stopped = 2; // server says sleep + //rdr->card_status = NO_CARD; + } + else + { + if(config_enabled(WITH_LB) && !cfg.lb_mode) + { + cl->stopped = 1; // server says invalid + if(rdr) + rdr->card_status = CARD_FAILURE; + } + } + } + } /* fallthrough */ // NO BREAK!! NOK Handling needed! + + case MSG_CW_NOK1: + case MSG_CW_NOK2: + { + if(l < 2) + { + break; + } + + if(l > 5) + { + // Received NOK with payload + char *msg = (char *) buf + 4; + + // Check for PARTNER connection + if((l >= (4 + 8)) && strncmp(msg, "PARTNER:", 8) == 0) + { + // When Data starts with "PARTNER:" we have an Oscam-cccam-compatible client/server! + + cs_strncpy(cc->remote_oscam, msg + 9, sizeof(cc->remote_oscam)); + int32_t has_param = check_extended_mode(cl, msg); + + if(!cc->is_oscam_cccam) + { + cc->is_oscam_cccam = 1; + + // send params back. At the moment there is only "EXT" + char param[20]; + if(!has_param) + { + param[0] = 0; + } + else + { + cs_strncpy(param, " [", sizeof(param)); + + if(cc->extended_mode) + { + addParam(param, sizeof(param), "EXT"); + } + + if(cc->cccam220) + { + addParam(param, sizeof(param), "SID"); + } + + if(cc->sleepsend) + { + addParam(param, sizeof(param), "SLP"); + } + +#ifdef CS_CACHEEX_AIO + if(cc->extended_lg_flagged_cws) + { + addParam(param, sizeof(param), "LGF"); + } +#endif + if (!cs_strncat(param, "]", sizeof(param))) { + cs_log("BUG!!, Adding ']' didn't succed!"); + } + } + + uint8_t token[256]; + snprintf((char *)token, sizeof(token), "PARTNER: OSCam %s (%s)%s", + CS_VERSION, CS_TARGET, param); + + cc_cmd_send(cl, token, cs_strlen((char *)token) + 1, MSG_CW_NOK1); + } + } + else + { + size_t msg_size = l - 4; + char last_char = msg[msg_size - 1]; + + if(last_char == 0) // verify if the payload is a null terminated string + { + if(cs_realloc(&cc->nok_message, msg_size)) + { + memcpy(cc->nok_message, msg, msg_size); + } + } + else + { + NULLFREE(cc->nok_message); + } + } + + return ret; + } + + if(cl->typ == 'c') + { + return ret; + } + + // for reader only + + cc->recv_ecmtask = -1; + + if(cc->just_logged_in) // reader restart needed + { + return -1; + } + + cs_readlock(__func__, &cc->cards_busy); + + struct cc_extended_ecm_idx *eei = get_extended_ecm_idx(cl, cc->extended_mode ? cc->g_flag : 1, 1); + if(!eei) + { + cs_log_dbg(D_READER, "%s received extended ecm NOK id %d but not found!", getprefix(), cc->g_flag); + } + else + { + uint16_t ecm_idx = eei->ecm_idx; + cc->recv_ecmtask = ecm_idx; + struct cc_card *card = eei->card; + //uint32_t cccam_id = eei->cccam_id; + struct cc_srvid srvid = eei->srvid; + int8_t retry = 1; + struct timeb tpe; + cs_ftime(&tpe); + int64_t cwlastresptime = comp_timeb(&tpe, &eei->tps); + + add_garbage(eei); + + if(card) + { + if(buf[1] == MSG_CW_NOK1) // MSG_CW_NOK1: share no more available + { + cs_log_dbg(D_TRACE, "NOK1: share temporarily not available %d %04X ecm %d %d!", + card->id, card->caid, eei->send_idx, eei->ecm_idx); + + int j; + for(j = 0; j < cfg.max_pending; j++) + { + if(cl->ecmtask[j].idx == ecm_idx && cl->ecmtask[j].rc == E_ALREADY_SENT) + { + ECM_REQUEST *er = &cl->ecmtask[j]; + cl->pending--; + + write_ecm_answer(rdr, er, E_NOTFOUND, 0, NULL, NULL, 0, NULL); + break; + } + } + } + else if(cc->cmd05NOK) // else MSG_CW_NOK2: can't decode + { + move_card_to_end(cl, card); + if(cwlastresptime < 5000) + { + add_sid_block(card, &srvid, true); + } + else + { + if(card->rating <= MIN_RATING) + { + add_sid_block(card, &srvid, true); + } + else + { + card->rating--; + } + } + } + else if(cacheex_get_rdr_mode(rdr) != 1) + { + if(!is_good_sid(card, &srvid)) + { + move_card_to_end(cl, card); + if(cwlastresptime < 5000) + { + add_sid_block(card, &srvid, true); + } + else + { + if(card->rating <= MIN_RATING) + { + add_sid_block(card, &srvid, true); + } + else + { + card->rating--; + } + } + } + else + { + move_card_to_end(cl, card); + add_sid_block(card, &srvid, true); + } + + card->rating = MAX(card->rating, MIN_RATING); + + if(cfg.cc_forward_origin_card && card->origin_reader == rdr) + { + // this card is from us but it can't decode this ecm + // also origin card is only set on cccam clients + // so wie send back the nok to the client + cs_log_dbg(D_TRACE, "%s forward card: %s", getprefix(), (buf[1] == MSG_CW_NOK1) ? "NOK1" : "NOK2"); + retry = 0; + } + } + } + else + { + cs_log_dbg(D_READER, "%s NOK: NO CARD!", getprefix()); + // try next card... + } + + // A "NOK" in extended mode means, NOTHING found, + // regardless of the requested card. So do not retry + if(cc->extended_mode) + { + cl->pending--; + retry = 0; + } + + if(retry) + { + cc_reset_pending(cl, ecm_idx); + } + else + { + int32_t i = 0; + for(i = 0; i < cfg.max_pending; i++) + { + if(cl->ecmtask[i].idx == ecm_idx && cl->ecmtask[i].rc == E_ALREADY_SENT) + { + cs_log_dbg(D_TRACE, "%s ext NOK %s", getprefix(), (buf[1] == MSG_CW_NOK1) ? "NOK1" : "NOK2"); + ECM_REQUEST *er = &cl->ecmtask[i]; + cl->pending--; + + write_ecm_answer(rdr, er, E_NOTFOUND, 0, NULL, NULL, 0, NULL); + break; + } + } + } + } + cc->cmd05NOK = 0; + cs_readunlock(__func__, &cc->cards_busy); + + if(!cc->extended_mode) + { + cc->ecm_busy = 0; + } + + cc_send_ecm(cl, NULL); + break; + } + + case MSG_CACHE_PUSH: + { + if((l - 4) >= 18) + { + cc_cacheex_push_in(cl, data); + } + break; + } + + case MSG_CACHE_FILTER: + { + if((l - 4) >= 482) + { + cc_cacheex_filter_in(cl, data); + } + break; + } +#ifdef CS_CACHEEX_AIO + case MSG_CACHE_FEATURE_EXCHANGE: + { + if((l - 4) >= 2) + { + cc_cacheex_feature_request_reply(cl); + } + break; + } + + case MSG_CACHE_FEATURE_EXCHANGE_REPLY: + { + if((l - 4) >= 2) + { + cc_cacheex_feature_request_save(cl, data); + } + break; + } + + case MSG_CACHE_FEATURE_TRIGGER: + { + if((l - 4) >= 2) + { + cc_cacheex_feature_trigger_in(cl, data); + } + break; + } + + case MSG_CW_ECM_LGF: +#endif + case MSG_CW_ECM: + { + cc->just_logged_in = 0; + if(cl->typ == 'c') // SERVER: + { +#define CCMSG_HEADER_LEN 17 + ECM_REQUEST *er; + struct cc_card *server_card; + + if(l < CCMSG_HEADER_LEN) + { + break; + } + + if(!cs_malloc(&server_card, sizeof(struct cc_card))) + { + break; + } + + server_card->id = buf[10] << 24 | buf[11] << 16 | buf[12] << 8 | buf[13]; + server_card->caid = b2i(2, data); + + if((er = get_ecmtask()) && l > CCMSG_HEADER_LEN && MAX_ECM_SIZE > l - CCMSG_HEADER_LEN) + { + er->caid = b2i(2, buf + 4); + er->prid = b2i(4, buf + 6); + er->srvid = b2i(2, buf + 14); + er->ecmlen = l - CCMSG_HEADER_LEN; + memcpy(er->ecm, buf + CCMSG_HEADER_LEN, er->ecmlen); + cc->server_ecm_pending++; + er->idx = ++cc->server_ecm_idx; + +#ifdef MODULE_CCCSHARE + if(cfg.cc_forward_origin_card) // search my shares for this card: + { + cs_log_dbg(D_TRACE, "%s forward card: %04X:%04x search share %d", getprefix(), + er->caid, er->srvid, server_card->id); + + LLIST **sharelist = get_and_lock_sharelist(); + LL_ITER itr = ll_iter_create(get_cardlist(er->caid, sharelist)); + struct cc_card *card; + struct cc_card *rcard = NULL; + + while((card = ll_iter_next(&itr))) + { + if(card->id == server_card->id) // found it + { + break; + } + } + + cs_log_dbg(D_TRACE, "%s forward card: share %d found: %d", getprefix(), server_card->id, card ? 1 : 0); + + struct s_reader *ordr = NULL; + + if(card && card->origin_reader) // found own card, now search reader card + { + // Search reader in list, because it is maybe offline? + for(ordr = first_active_reader; ordr; ordr = ordr->next) + { + if(ordr == card->origin_reader) + { + break; + } + } + + if(!ordr) + { + cs_log_dbg(D_TRACE, "%s origin reader not found!", getprefix()); + } + else + { + cs_log_dbg(D_TRACE, "%s forward card: share %d origin reader %s origin id %d", + getprefix(), card->id, ordr->label, card->origin_id); + + struct s_client *cl2 = ordr->client; + if(card->origin_id && cl2 && cl2->cc) // only if we have a origin from a cccam reader + { + struct cc_data *rcc = cl2->cc; + + if(rcc) + { + itr = ll_iter_create(rcc->cards); + while((rcard = ll_iter_next(&itr))) + { + if(rcard->id == card->origin_id) // found it! + { + break; + } + } + } + } + else + { + rcard = card; + } + } + er->origin_reader = ordr; + } + + er->origin_card = rcard; + if(!rcard || !ordr) + { + cs_log_dbg(D_TRACE, "%s forward card: share %d not found!", getprefix(), server_card->id); + er->rc = E_NOTFOUND; + er->rcEx = E2_CCCAM_NOK1; // share not found! + } + else + { + cs_log_dbg(D_TRACE, "%s forward card: share %d forwarded to %s origin as id %d", + getprefix(), card->id, ordr->label, rcard->id); + } + unlock_sharelist(); + } +#endif + cs_log_dbg(D_CLIENT, "%s ECM request from client: caid %04x srvid %04x(%d) prid %06x", + getprefix(), er->caid, er->srvid, er->ecmlen, er->prid); + + struct cc_srvid srvid; + srvid.sid = er->srvid; + srvid.chid = er->chid; + srvid.ecmlen = er->ecmlen; + add_extended_ecm_idx(cl, cc->extended_mode ? cc->g_flag : 1, er->idx, server_card, srvid, 1); + + get_cw(cl, er); + + } + else + { + cs_log_dbg(D_CLIENT, "%s NO ECMTASK!!!! l=%d", getprefix(), l); + NULLFREE(server_card); + } + + } + else // READER + { + if(l < 20) + { + break; + } + + cs_readlock(__func__, &cc->cards_busy); + cc->recv_ecmtask = -1; + + struct cc_extended_ecm_idx *eei = get_extended_ecm_idx(cl, cc->extended_mode ? cc->g_flag : 1, 1); + if(!eei) + { + cs_log_dbg(D_READER, "%s received extended ecm id %d but not found!", getprefix(), cc->g_flag); + + if(!cc->extended_mode) + { + cc_cli_close(cl, 0); + } + } + else + { + uint16_t ecm_idx = eei->ecm_idx; + cc->recv_ecmtask = ecm_idx; + struct cc_card *card = eei->card; + uint32_t cccam_id = eei->cccam_id; + struct cc_srvid srvid = eei->srvid; + NULLFREE(eei); + + if(card) + { + if(!cc->extended_mode) + { + cc_cw_crypt(cl, buf + 4, card->id); + cc_crypt_cmd0c(cl, buf + 4, 16); + } + + memcpy(cc->dcw, buf + 4, 16); + //fix_dcw(cc->dcw); + + if(!cc->extended_mode) // additional crypto step + { + cc_crypt(&cc->block[DECRYPT], buf + 4, l - 4, ENCRYPT); + } + + if(is_null_dcw(cc->dcw)) + { + cs_log_dbg(D_READER, "%s null dcw received! sid=%04X(%d)", getprefix(), srvid.sid, srvid.ecmlen); + move_card_to_end(cl, card); + add_sid_block(card, &srvid, true); + // ecm retry + cc_reset_pending(cl, ecm_idx); + buf[1] = MSG_CW_NOK2; // So it's really handled like a nok! + } + else + { + cs_log_dbg(D_READER, "%s cws: %d %s", getprefix(), ecm_idx, + cs_hexdump(0, cc->dcw, 16, tmp_dbg, sizeof(tmp_dbg))); + + // check response time, if > fallbacktime, switch cards! + struct timeb tpe; + cs_ftime(&tpe); + int64_t cwlastresptime = comp_timeb(&tpe, &cc->ecm_time); + + if(cwlastresptime > get_fallbacktimeout(card->caid) && !cc->extended_mode) + { + cs_log_dbg(D_READER, "%s card %04X is too slow, moving to the end...", getprefix(), card->id); + move_card_to_end(cl, card); + + card->rating = MAX(card->rating - 1, MIN_RATING); + } + else + { + card->rating = MIN(card->rating + 1, MAX_RATING); + } + } + } + else + { + // Card removed... + cs_log_dbg(D_READER, "%s warning: ECM-CWS respond by CCCam server without current card!", getprefix()); + + if(!cc->extended_mode) + { + cc_cw_crypt(cl, buf + 4, cccam_id); + cc_crypt_cmd0c(cl, buf + 4, 16); + } + memcpy(cc->dcw, buf + 4, 16); + //fix_dcw(cc->dcw); + + if(!cc->extended_mode) // additional crypto step + { + cc_crypt(&cc->block[DECRYPT], buf + 4, l - 4, ENCRYPT); + } + + cs_log_dbg(D_READER, "%s cws: %d %s", getprefix(), ecm_idx, + cs_hexdump(0, cc->dcw, 16, tmp_dbg, sizeof(tmp_dbg))); + } + } + cs_readunlock(__func__, &cc->cards_busy); + + if(!cc->extended_mode) + { + cc->ecm_busy = 0; + } + + //cc_abort_user_ecms(); + cc_send_ecm(cl, NULL); + + if(cc->max_ecms) + { + cc->ecm_counter++; + } + } + break; + } + + case MSG_KEEPALIVE: + { +#ifdef CS_CACHEEX_AIO + if(!cl->cacheex_aio_checked && ((cl->account && cl->account->cacheex.mode > 0) || (cl->reader && cl->reader->cacheex.mode > 0))) + { + cc_cacheex_feature_request(cl); + cl->cacheex_aio_checked = 1; + } +#endif + if(cl) + { + cl->last = time(NULL); + } + + if(rdr && rdr->cc_keepalive) + { + rdr->last_g = time(NULL); + rdr->last_s = time(NULL); + rdr_log_dbg(rdr, D_READER, "%s: receive keepalive", __func__); + } + + cc->just_logged_in = 0; + break; + } + + case MSG_CMD_05: + { + if(cl->typ != 'c') + { + cc->just_logged_in = 0; + l = l - 4; // Header Length = 4 bytes + if(l < 0) + { + break; + } + + cs_log_dbg(D_READER, "%s MSG_CMD_05 recvd, payload length=%d mode=%d", + getprefix(), l, cc->cmd05_mode); + + cc->cmd05_active = 1; + cc->cmd05_data_len = l; + memcpy(&cc->cmd05_data, buf + 4, l); + + if(!cc->ecm_busy && ll_has_elements(cc->cards)) + { + send_cmd05_answer(cl); + } + } + break; + } + + case MSG_CMD_0B: + { + if(l < 20) + { + break; + } + + // by Project: Keynation + cs_log_dbg(D_READER, "%s MSG_CMD_0B received (payload=%d)!", getprefix(), l - 4); + + AES_KEY key; + uint8_t aeskey[16]; + uint8_t out[16]; + + memcpy(aeskey, cc->cmd0b_aeskey, 16); + memset(&key, 0, sizeof(key)); + + //cs_log_dump_dbg(D_READER, aeskey, 16, "%s CMD_0B AES key:", getprefix()); + //cs_log_dump_dbg(D_READER, data, 16, "%s CMD_0B received data:", getprefix()); + + AES_set_encrypt_key((uint8_t *)&aeskey, 128, &key); + AES_encrypt((uint8_t *)data, (uint8_t *)&out, &key); + + cs_log_dbg(D_TRACE, "%s sending CMD_0B! ", getprefix()); + //cs_log_dump_dbg(D_READER, out, 16, "%s CMD_0B out:", getprefix()); + cc_cmd_send(cl, out, 16, MSG_CMD_0B); + break; + } + + case MSG_CMD_0C: // New CCCAM 2.2.0 Server/Client fake check! + { + int32_t len = l - 4; + if(len < 0) + { + break; + } + + if(cl->typ == 'c') // Only im comming from "client" + { + cs_log_dbg(D_CLIENT, "%s MSG_CMD_0C received (payload=%d)!", getprefix(), len); + + uint8_t bytes[0x20]; + if(len < 0x20) // if less then 0x20 bytes, clear others + { + memset(data + len, 0, 0x20 - len); + } + + // change first 0x10 bytes to the second + memcpy(bytes, data + 0x10, 0x10); + memcpy(bytes + 0x10, data, 0x10); + + // xor data: + int32_t i; + for(i = 0; i < 0x20; i++) + { + bytes[i] ^= (data[i] & 0x7F); + } + + // key is now the 16bit hash of md5 + uint8_t md5hash[0x10]; + MD5(data, 0x20, md5hash); + memcpy(bytes, md5hash, 0x10); + + cs_log_dbg(D_CLIENT, "%s sending CMD_0C! ", getprefix()); + //cs_log_dump_dbg(D_CLIENT, bytes, 0x20, "%s CMD_0C out:", getprefix()); + cc_cmd_send(cl, bytes, 0x20, MSG_CMD_0C); + } + else // reader + { + // by Project: Keynation + Oscam team + cc_crypt_cmd0c(cl, data, len); + + uint8_t CMD_0x0C_Command = data[0]; + + switch(CMD_0x0C_Command) + { + case 0: // RC6 + { + cc->cmd0c_mode = MODE_CMD_0x0C_RC6; + break; + } + + case 1: // RC4 + { + cc->cmd0c_mode = MODE_CMD_0x0C_RC4; + break; + } + + case 2: // CC_CRYPT + { + cc->cmd0c_mode = MODE_CMD_0x0C_CC_CRYPT; + break; + } + + case 3: // AES + { + cc->cmd0c_mode = MODE_CMD_0x0C_AES; + break; + } + + case 4: // IDEA + { + cc->cmd0c_mode = MODE_CMD_0x0C_IDEA; + break; + } + + default: + { + cc->cmd0c_mode = MODE_CMD_0x0C_NONE; + } + } + + set_cmd0c_cryptkey(cl, data, len); + + cs_log_dbg(D_READER, "%s received MSG_CMD_0C from server! CMD_0x0C_CMD=%d, MODE=%s", + getprefix(), CMD_0x0C_Command, cmd0c_mode_name[cc->cmd0c_mode]); + } + break; + } + + case MSG_CMD_0D: // key update for the active cmd0x0c algo + { + int32_t len = l - 4; + if(len < 0) + { + break; + } + + if(cc->cmd0c_mode == MODE_CMD_0x0C_NONE) + { + break; + } + + cc_crypt_cmd0c(cl, data, len); + set_cmd0c_cryptkey(cl, data, len); + + cs_log_dbg(D_READER, "%s received MSG_CMD_0D from server! MODE=%s", + getprefix(), cmd0c_mode_name[cc->cmd0c_mode]); + break; + } + + case MSG_CMD_0E: + { + if(l < 2) + { + break; + } + + cs_log_dbg(D_READER, "cccam 2.2.x commands not implemented: 0x%02X", buf[1]); + + // Unkwon commands... need workout algo + if(cl->typ == 'c') // client connection + { + //switching to an older version and then disconnect... + cs_strncpy(cfg.cc_version, version[0], sizeof(cfg.cc_version)); + ret = -1; + } + else // reader connection + { + cs_strncpy(cl->reader->cc_version, version[0], sizeof(cl->reader->cc_version)); + cs_strncpy(cl->reader->cc_build, build[0], sizeof(cl->reader->cc_build)); + cc_cycle_connection(cl); + } + break; + } + + case MSG_EMM_ACK: + { + cc->just_logged_in = 0; + if(cl->typ == 'c') // EMM Request received + { + cc_cmd_send(cl, NULL, 0, MSG_EMM_ACK); // Send back ACK + + if(l < 16) + { + break; + } + + cs_log_dbg(D_EMM, "%s EMM Request received!", getprefix()); + + if(!ll_count(cl->aureader_list)) + { + cs_log_dbg( D_EMM, "%s EMM Request discarded because au is not assigned to an reader!", getprefix()); + return MSG_EMM_ACK; + } + + EMM_PACKET *emm; + if(!cs_malloc(&emm, sizeof(EMM_PACKET))) + { + break; + } + + emm->caid[0] = buf[4]; + emm->caid[1] = buf[5]; + emm->provid[0] = buf[7]; + emm->provid[1] = buf[8]; + emm->provid[2] = buf[9]; + emm->provid[3] = buf[10]; + //emm->hexserial[0] = buf[11]; + //emm->hexserial[1] = buf[12]; + //emm->hexserial[2] = buf[13]; + //emm->hexserial[3] = buf[14]; + + if(l <= 0xFF) + { + emm->emmlen = buf[15]; + } + else + { + emm->emmlen = MIN(l - 16, (int32_t)sizeof(emm->emm)); + } + + if(emm->emmlen < 0 || emm->emmlen > MAX_EMM_SIZE || emm->emmlen + 16 > l) + { + NULLFREE(emm); + break; + } + + memcpy(emm->emm, buf + 16, emm->emmlen); + //emm->type = UNKNOWN; + //emm->cidx = cs_idx; + do_emm(cl, emm); + NULLFREE(emm); + } + else // Our EMM Request Ack! + { + cs_log_dbg(D_EMM, "%s EMM ACK!", getprefix()); + if(!cc->extended_mode) + { + cc->ecm_busy = 0; + } + cc_send_ecm(cl, NULL); + } + break; + } + + default: + { + //cs_log_dump_dbg(D_CLIENT, buf, l, "%s unhandled msg: %d len=%d", getprefix(), buf[1], l); + break; + } + } + + if(cc->max_ecms && (cc->ecm_counter > cc->max_ecms)) + { + cs_log_dbg(D_READER, "%s max ecms (%d) reached, cycle connection!", getprefix(), cc->max_ecms); + cc_cycle_connection(cl); + } + return ret; +} + +/** + * Reader: write dcw to receive + */ +int32_t cc_recv_chk(struct s_client *cl, uint8_t *dcw, int32_t *rc, uint8_t *buf, int32_t UNUSED(n)) +{ + struct cc_data *cc = cl->cc; + + if(buf[1] == MSG_CW_ECM +#ifdef CS_CACHEEX_AIO + || buf[1] == MSG_CW_ECM_LGF +#endif + ) + { + memcpy(dcw, cc->dcw, 16); + //cs_log_dbg(D_CLIENT, "cccam: recv chk - MSG_CW %d - %s", cc->recv_ecmtask, + // cs_hexdump(0, dcw, 16, tmp_dbg, sizeof(tmp_dbg))); + *rc = 1; +#ifdef CS_CACHEEX_AIO + if(buf[1] == MSG_CW_ECM_LGF) + *rc = 0x86; +#endif + return (cc->recv_ecmtask); + } + else if((buf[1] == (MSG_CW_NOK1)) || (buf[1] == (MSG_CW_NOK2))) + { + *rc = 0; + //if(cc->is_oscam_cccam) + if(cfg.cc_forward_origin_card) + { + return (cc->recv_ecmtask); + } + else + { + return -1; + } + } + + return (-1); +} + +//int32_t is_softfail(int32_t rc) +//{ +// //see oscam.c send_dcw() for a full list +// switch(rc) +// { +// case 5: // 5 = timeout +// case 6: // 6 = sleeping +// case 7: // 7 = fake +// case 10: // 10 = no card +// case 11: // 11 = expdate +// case 12: // 12 = disabled +// case 13: // 13 = stopped +// case 14: // 100 = unhandled +// return 1; +// } +// return 0; +//} + +/** + * Server: send DCW to client + */ +void cc_send_dcw(struct s_client *cl, ECM_REQUEST *er) +{ + uint8_t buf[16]; + struct cc_data *cc = cl->cc; + + memset(buf, 0, sizeof(buf)); + + struct cc_extended_ecm_idx *eei = get_extended_ecm_idx_by_idx(cl, er->idx, 1); + + if(er->rc < E_NOTFOUND && eei) // found + { + memcpy(buf, er->cw, sizeof(buf)); + //fix_dcw(buf); + //cs_log_dbg(D_TRACE, "%s send cw: %s cpti: %d", getprefix(), + // cs_hexdump(0, buf, 16, tmp_dbg, sizeof(tmp_dbg)), er->cpti); + + if(!cc->extended_mode) + { + cc_cw_crypt(cl, buf, eei->cccam_id); + } + else + { + cc->g_flag = eei->send_idx; + } + +#ifdef CS_CACHEEX_AIO + // lg-flag + if(cc->extended_lg_flagged_cws && (er->localgenerated || (er->selected_reader && !is_network_reader(er->selected_reader)))) + { + cc_cmd_send(cl, buf, 16, MSG_CW_ECM_LGF); + } + else + { +#endif + cc_cmd_send(cl, buf, 16, MSG_CW_ECM); +#ifdef CS_CACHEEX_AIO + } +#endif + + if(!cc->extended_mode) + { + cc_crypt(&cc->block[ENCRYPT], buf, 16, ENCRYPT); // additional crypto step + } + } + else // NOT found + { + //cs_log_dbg(D_TRACE, "%s send cw: NOK cpti: %d", getprefix(), er->cpti); + + if(eei && cc->extended_mode) + { + cc->g_flag = eei->send_idx; + } + + int32_t nok, bufsize = 0; + if(cc->sleepsend && er->rc == E_STOPPED) + { + buf[0] = cl->c35_sleepsend; + bufsize = 1; + nok = MSG_SLEEPSEND; + } + else if(!eei || !eei->card) + { + nok = MSG_CW_NOK1; // share no more available + } + else + { + if(cfg.cc_forward_origin_card && er->origin_card == eei->card) + { + nok = (er->rcEx == E2_CCCAM_NOK1) ? MSG_CW_NOK1 : MSG_CW_NOK2; + } + else + { + nok = MSG_CW_NOK2; // can't decode + } + } + cc_cmd_send(cl, buf, bufsize, nok); + } + cc->server_ecm_pending--; + + if(eei) + { + NULLFREE(eei->card); + NULLFREE(eei); + } +} + +int32_t cc_recv(struct s_client *cl, uint8_t *buf, int32_t l) +{ + int32_t n; + struct s_reader *rdr = (cl->typ == 'c') ? NULL : cl->reader; + + if(buf == NULL || l <= 0) + { + return -1; + } + + n = cc_msg_recv(cl, buf, l); // recv and decrypt msg + //cs_log_dump_dbg(D_CLIENT, buf, n, "cccam: received %d bytes from %s", n, remote_txt()); + + if(n <= 0) + { + struct cc_data *cc = cl->cc; + if(cc && cc->nok_message) + { + cs_log_dbg(D_CLIENT, "%s connection closed by %s. n=%d, Reason: %s", + getprefix(), remote_txt(), n, cc->nok_message); + } + else + { + cs_log_dbg(D_CLIENT, "%s connection closed by %s, n=%d.", getprefix(), remote_txt(), n); + if(rdr) + { + cc_cli_close(cl, 1); + } + else + { + //cs_writelock(__func__, &cc->cards_busy); maybe uninitialized + cs_disconnect_client(cl); + //cs_writeunlock(__func__, &cc->cards_busy); + } + + cs_sleepms(150); + n = -1; + return n; + } + + n = -1; + } + else if(n < 4) + { + cs_log("%s packet is too small (%d bytes)", getprefix(), n); + n = -1; + } + else if(n > CC_MAXMSGSIZE) + { + cs_log("%s packet is too big (%d bytes, max: %d)", getprefix(), n, CC_MAXMSGSIZE); + n = -1; + } + else + { + // parse it and write it back, if we have received something of value + n = cc_parse_msg(cl, buf, n); + if(n == MSG_CW_ECM || n == MSG_EMM_ACK +#ifdef CS_CACHEEX_AIO + || n == MSG_CW_ECM_LGF +#endif + ) + { + cl->last = time(NULL); // last client action is now + if(rdr) + { + rdr->last_g = time(NULL); // last reader receive is now + } + } + } + + if(n == -1) + { + if(cl->typ != 'c') + { + cc_cli_close(cl, 1); + } + } + + return n; +} + +void cc_init_locks(struct cc_data *cc) +{ + cs_lock_create(__func__, &cc->lockcmd, "lockcmd", 5000); + cs_lock_create(__func__, &cc->cards_busy, "cards_busy", 10000); +} + +#ifdef MODULE_CCCSHARE +/** + * Starting readers to get cards + **/ +int32_t cc_srv_wakeup_readers(struct s_client *cl) +{ + int32_t wakeup = 0; + struct s_reader *rdr; + struct s_client *client; + + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + if(rdr->typ != R_CCCAM) + { + continue; + } + + if(rdr->tcp_connected == 2) + { + continue; + } + + if(!(rdr->grp & cl->grp)) + { + continue; + } + + // if reader has keepalive but is NOT connected, + // reader can't connect. so don't ask him + if(rdr->cc_keepalive) + { + continue; + } + + // reader is in shutdown + if((client = rdr->client) == NULL || (client->cc) == NULL || client->kill) + { + continue; + } + + // reader cannot be waked up currently because its blocked + if(is_connect_blocked(rdr)) + { + continue; + } + + // This wakeups the reader: + add_job(rdr->client, ACTION_READER_CARDINFO, NULL, 0); + wakeup++; + } + + return wakeup; +} + +int32_t cc_srv_connect(struct s_client *cl) +{ + int32_t i, ccversion_pos, ccbuild_pos; + int32_t no_delay = 1; + uint8_t data[16]; + char usr[21], pwd[65], tmp_dbg[17]; + struct s_auth *account; + struct cc_data *cc; + + if(!cs_malloc(&cc, sizeof(struct cc_data))) + { + return -1; + } + + memset(usr, 0, sizeof(usr)); + memset(pwd, 0, sizeof(pwd)); + + // init internals data struct + cl->cc = cc; + cc->extended_ecm_idx = ll_create("extended_ecm_idx"); + + cc_init_locks(cc); + uint8_t *buf = cc->send_buffer; + + cc->server_ecm_pending = 0; + cc->extended_mode = 0; + cc->ecm_busy = 0; + + int32_t keep_alive = 1; + setsockopt(cl->udp_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keep_alive, sizeof(keep_alive)); + + // Create checksum for "O" cccam + get_random_bytes(data, 12); + for(i = 0; i < 4; i++) + { + data[12 + i] = (data[i] + data[4 + i] + data[8 + i]) & 0xff; + } + + cs_log_dbg(D_TRACE, "send ccc checksum"); + + send(cl->udp_fd, data, 16, 0); + + cc_xor(data); // XOR init bytes with 'CCcam' + + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, data, 16); + SHA1_Final(buf, &ctx); + + cc_init_crypt(&cc->block[ENCRYPT], buf, 20); + cc_crypt(&cc->block[ENCRYPT], data, 16, DECRYPT); + cc_init_crypt(&cc->block[DECRYPT], data, 16); + cc_crypt(&cc->block[DECRYPT], buf, 20, DECRYPT); + + cs_log_dbg(D_TRACE, "receive ccc checksum"); + + if(cc_recv_to(cl, buf, 20) == 20) + { + //cs_log_dump_dbg(D_CLIENT, buf, 20, "cccam: recv:"); + cc_crypt(&cc->block[DECRYPT], buf, 20, DECRYPT); + //cs_log_dump_dbg(D_CLIENT, buf, 20, "cccam: hash:"); + } + else + { + return -1; + } + + // receive username + memset(buf, 0, CC_MAXMSGSIZE); + i = cc_recv_to(cl, buf, 20); + if(i < 0) // errors during receive! + { + return -1; + } + + if(i == 20) + { + cc_crypt(&cc->block[DECRYPT], buf, 20, DECRYPT); + cs_strncpy(usr, (char *)buf, sizeof(usr)); + + // test for non printable characters + for(i = 0; i < 20; i++) + { + if(usr[i] > 0 && usr[i] < 0x20) // found non printable char + { + cs_log("illegal username received"); + return -3; + } + } + //cs_log_dump_dbg(D_CLIENT, buf, 20, "cccam: username '%s':", usr); + } + else + { + cs_add_violation(cl, NULL); + return -2; + } + cs_log_dbg(D_TRACE, "ccc username received %s", usr); + + cl->crypted = 1; + + // CCCam only supports len=20 usr/pass. So we could have + // more than one user that matches the first 20 chars. + + // receive password-CCCam encrypted Hash: + i = cc_recv_to(cl, buf, 6); + + if(i < 0) // errors during receive! + { + return -1; + } + + if(i != 6) // received invalid password length + { + cs_add_violation(cl, usr); + return -2; + } + + cs_log_dbg(D_TRACE, "ccc passwdhash received %s", usr); + + account = cfg.account; + struct cc_crypt_block *save_block; + if(!cs_malloc(&save_block, sizeof(struct cc_crypt_block))) + { + return -1; + } + + memcpy(save_block, cc->block, sizeof(struct cc_crypt_block)); + int32_t found = 0; + + while(1) + { + while(account) + { + if(strncmp(usr, account->usr, 20) == 0) + { + memset(pwd, 0, sizeof(pwd)); + cs_strncpy(pwd, account->pwd, sizeof(pwd)); + found = 1; + break; + } + account = account->next; + } + + if(!account) + { + break; + } + + // receive passwd / 'CCcam' + memcpy(cc->block, save_block, sizeof(struct cc_crypt_block)); + cc_crypt(&cc->block[DECRYPT], (uint8_t *) pwd, cs_strlen(pwd), ENCRYPT); + cc_crypt(&cc->block[DECRYPT], buf, 6, DECRYPT); + + // illegal buf-bytes could kill the logger! + //cs_log_dump_dbg(D_CLIENT, buf, 6, "cccam: pwd check '%s':", buf); + + if(memcmp(buf, "CCcam\0", 6) == 0) // Password Hash OK! + { + break; // account is set + } + + account = account->next; + } + NULLFREE(save_block); + + // cs_auth_client returns 0 if account is valid/active/accessible + if(cs_auth_client(cl, account, NULL)) + { + if(!found) + { + cs_log("account '%s' not found!", usr); + } + else + { + cs_log("password for '%s' invalid!", usr); + } + + cs_add_violation(cl, usr); + return -2; + } + + if(cl->dup) + { + cs_log("account '%s' duplicate login, disconnect!", usr); + return -3; + } + + if(cl->disabled) + { + cs_log("account '%s' disabled, blocking+disconnect!", usr); + cs_add_violation(cl, usr); + return -2; + } + + if(account->cccmaxhops < -1) + { + cs_log("account '%s' has cccmaxhops < -1, cccam can't handle this, disconnect!", usr); + return -3; + } + + cs_log_dbg(D_TRACE, "ccc user authenticated %s", usr); + + if(account->cccmaxhops == -1) + { + cs_log("account '%s' has cccmaxhops = -1: user will not see any card!", usr); + } + + if(!cs_malloc(&cc->prefix, cs_strlen(cl->account->usr) + 20)) + { + return -1; + } + snprintf(cc->prefix, cs_strlen(cl->account->usr) + 20, "cccam(s) %s:", cl->account->usr); + +#ifdef CS_CACHEEX + if(cl->account->cacheex.mode < 2) +#endif + if(cl->tcp_nodelay == 0) + { + setsockopt(cl->udp_fd, IPPROTO_TCP, TCP_NODELAY, (void *)&no_delay, sizeof(no_delay)); + cl->tcp_nodelay = 1; + } + + // Starting readers to get cards + cc_srv_wakeup_readers(cl); + + // send passwd ack + memset(buf, 0, 20); + memcpy(buf, "CCcam\0", 6); + cs_log_dump_dbg(D_CLIENT, buf, 20, "cccam: send ack:"); + cc_crypt(&cc->block[ENCRYPT], buf, 20, ENCRYPT); + send(cl->pfd, buf, 20, 0); + + // recv cli data + memset(buf, 0, CC_MAXMSGSIZE); + i = cc_msg_recv(cl, buf, CC_MAXMSGSIZE - 1); + + if(i < 0) + { + return -1; + } + cs_log_dump_dbg(D_CLIENT, buf, i, "cccam: cli data:"); + + if(i < 66) + { + cs_log_dbg(D_CLIENT, "cccam: cli data too small"); + return -1; + } + + memcpy(cc->peer_node_id, buf + 24, 8); + //chk_peer_node_for_oscam(cc); + + ccversion_pos = 33; + while(ccversion_pos + 1 < i && ccversion_pos < 33 + 5 && buf[ccversion_pos] == 0) + { + ccversion_pos++; + } + + ccbuild_pos = 65; + while(ccbuild_pos + 1 < i && ccbuild_pos < 65 + 5 && buf[ccbuild_pos] == 0) + { + ccbuild_pos++; + } + + cs_strncpy(cc->remote_version, (char *)buf + ccversion_pos, sizeof(cc->remote_version)); + cs_strncpy(cc->remote_build, (char *)buf + ccbuild_pos, sizeof(cc->remote_build)); + + cs_log_dbg(D_CLIENT, "%s client '%s' (%s) running v%s (%s)", getprefix(), buf + 4, + cs_hexdump(0, cc->peer_node_id, 8, tmp_dbg, sizeof(tmp_dbg)), cc->remote_version, cc->remote_build); + + // send cli data ack + cc_cmd_send(cl, NULL, 0, MSG_CLI_DATA); + + cs_log_dbg(D_TRACE, "ccc send srv_data %s", usr); + if(cc_send_srv_data(cl) < 0) + { + return -1; + } + + cc->cccam220 = check_cccam_compat(cc); + cc->just_logged_in = 1; + + // Wait for Partner detection (NOK1 with data) before reporting cards + // When Partner is detected, cccam220=1 is set. then we can report extended card data + i = process_input(buf, CC_MAXMSGSIZE, 1); + + if(i <= 0 && i != -9) // disconnected + { + return 0; + } + + if(cc->cccam220) + { + cs_log_dbg(D_CLIENT, "%s extended sid mode activated", getprefix()); + } + else + { + cs_log_dbg(D_CLIENT, "%s 2.1.x compatibility mode", getprefix()); + } + + cs_log_dbg(D_TRACE, "ccc send cards %s", usr); + + if(!cc_srv_report_cards(cl)) + { + return -1; + } + cs_ftime(&cc->ecm_time); + + // some clients, e.g. mgcamd, do not support keepalive. So if not answered, keep + // connection check for client timeout. If timeout occurs try to send keepalive + cs_log_dbg(D_TRACE, "ccc connected and waiting for data %s", usr); + return 0; +} + +void cc_srv_init2(struct s_client *cl) +{ + if(!cl->init_done && !cl->kill) + { + if(IP_ISSET(cl->ip)) + { + cs_log_dbg(D_CLIENT, "cccam: new connection from %s", cs_inet_ntoa(cl->ip)); + } + + cl->pfd = cl->udp_fd; + int32_t ret; + if((ret = cc_srv_connect(cl)) < 0) + { + if(errno != 0) + { + cs_log_dbg(D_CLIENT, "cccam: failed errno: %d (%s)", errno, strerror(errno)); + } + else + { + cs_log_dbg(D_CLIENT, "cccam: failed ret: %d", ret); + } + cs_disconnect_client(cl); + } + else + { + cl->init_done = 1; + cc_cacheex_filter_out(cl); +#ifdef CS_CACHEEX_AIO + if((cl->account && cl->account->cacheex.mode > 0) || (cl->reader && cl->reader->cacheex.mode > 0)) + cc_cacheex_feature_request(cl); +#endif + } + } + return; +} + +void *cc_srv_init(struct s_client *cl, uint8_t *UNUSED(mbuf), int32_t UNUSED(len)) +{ + cc_srv_init2(cl); + return NULL; +} +#endif + +int32_t cc_cli_connect(struct s_client *cl) +{ + struct s_reader *rdr = cl->reader; + struct cc_data *cc = cl->cc; + rdr->card_status = CARD_FAILURE; + cl->stopped = 0; + + if(!cc) + { + // init internals data struct + if(!cs_malloc(&cc, sizeof(struct cc_data))) + { + return -1; + } + + cc_init_locks(cc); + cc->cards = ll_create("cards"); + cl->cc = cc; + cc->pending_emms = ll_create("pending_emms"); + cc->extended_ecm_idx = ll_create("extended_ecm_idx"); + } + else + { + cc_free_cardlist(cc->cards, 0); + free_extended_ecm_idx(cc); + } + + if(!cc->prefix) + { + if(!cs_malloc(&cc->prefix, cs_strlen(cl->reader->label) + 20)) + { + return -1; + } + } + snprintf(cc->prefix, cs_strlen(cl->reader->label) + 20, "cccam(r) %s:", cl->reader->label); + + int32_t handle, n; + uint8_t data[20]; + uint8_t hash[SHA_DIGEST_LENGTH]; + uint8_t *buf = cc->send_buffer; + char pwd[65]; + + // check cred config + if(rdr->device[0] == 0 || rdr->r_pwd[0] == 0 || rdr->r_usr[0] == 0 || rdr->r_port == 0) + { + cs_log("%s configuration error!", rdr->label); + return -5; + } + + // connect + handle = network_tcp_connection_open(rdr); + if(handle <= 0) + { + cs_log_dbg(D_READER, "%s network connect error!", rdr->label); + return -1; + } + + if(errno == EISCONN) + { + cc_cli_close(cl, 0); + block_connect(rdr); + return -1; + } + + int32_t no_delay = 1; + if(cacheex_get_rdr_mode(rdr) < 2) + { + setsockopt(cl->udp_fd, IPPROTO_TCP, TCP_NODELAY, (void *)&no_delay, sizeof(no_delay)); + } + + // get init seed + if((n = cc_recv_to(cl, data, 16)) != 16) + { + if(n <= 0) + { + cs_log("init error from reader %s", rdr->label); + } + else + { + cs_log("%s server returned %d instead of 16 bytes as init seed (errno=%d %s)", + rdr->label, n, errno, strerror(errno)); + } + + cc_cli_close(cl, 0); + block_connect(rdr); + return -2; + } + + cc->ecm_counter = 0; + cc->max_ecms = 0; + cc->cmd05_mode = MODE_UNKNOWN; + cc->cmd05_offset = 0; + cc->cmd05_active = 0; + cc->cmd05_data_len = 0; + cc->extended_mode = 0; + cc->last_emm_card = NULL; + cc->num_hop1 = 0; + cc->num_hop2 = 0; + cc->num_hopx = 0; + cc->num_reshare0 = 0; + cc->num_reshare1 = 0; + cc->num_reshare2 = 0; + cc->num_resharex = 0; + memset(&cc->cmd05_data, 0, sizeof(cc->cmd05_data)); + memset(&cc->receive_buffer, 0, sizeof(cc->receive_buffer)); + NULLFREE(cc->nok_message); + cc->cmd0c_mode = MODE_CMD_0x0C_NONE; + + cs_log_dump_dbg(D_CLIENT, data, 16, "cccam: server init seed:"); + + uint16_t sum = 0x1234; + uint16_t recv_sum = (data[14] << 8) | data[15]; + int32_t i; + + for(i = 0; i < 14; i++) + { + sum += data[i]; + } + + // create special data to detect oscam-cccam + cc->is_oscam_cccam = sum == recv_sum; + + // detect multics seed + uint8_t a = (data[0] ^ 'M') + data[1] + data[2]; + uint8_t b = data[4] + (data[5] ^ 'C') + data[6]; + uint8_t c = data[8] + data[9] + (data[10] ^ 'S'); + + if((a == data[3]) && (b == data[7]) && (c == data[11])) + { + cc->multics_mode = 1; // detected multics seed + cs_log_dbg(D_READER, "multics seed detected: %s", rdr->label); + } + + cc_xor(data); // XOR init bytes with 'CCcam' + + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, data, 16); + SHA1_Final(hash, &ctx); + + cs_log_dump_dbg(D_CLIENT, hash, sizeof(hash), "cccam: sha1 hash:"); + + // initialisate crypto states + cc_init_crypt(&cc->block[DECRYPT], hash, 20); + cc_crypt(&cc->block[DECRYPT], data, 16, DECRYPT); + cc_init_crypt(&cc->block[ENCRYPT], data, 16); + cc_crypt(&cc->block[ENCRYPT], hash, 20, DECRYPT); + + cc_cmd_send(cl, hash, 20, MSG_NO_HEADER); // send crypted hash to server + + memset(buf, 0, CC_MAXMSGSIZE); + memcpy(buf, rdr->r_usr, cs_strlen(rdr->r_usr)); + cs_log_dump_dbg(D_CLIENT, buf, 20, "cccam: username '%s':", buf); + cc_cmd_send(cl, buf, 20, MSG_NO_HEADER); // send usr '0' padded -> 20 bytes + + memset(buf, 0, CC_MAXMSGSIZE); + memset(pwd, 0, sizeof(pwd)); + + //cs_log_dbg(D_CLIENT, "cccam: 'CCcam' xor"); + memcpy(buf, "CCcam", 5); + cs_strncpy(pwd, rdr->r_pwd, sizeof(pwd)); + cc_crypt(&cc->block[ENCRYPT], (uint8_t *)pwd, cs_strlen(pwd), ENCRYPT); + cc_cmd_send(cl, buf, 6, MSG_NO_HEADER); // send 'CCcam' xor w/ pwd + + if((cc_recv_to(cl, data, 20)) != 20) + { + cs_log("%s login failed, usr/pwd invalid", getprefix()); + cc_cli_close(cl, 0); + block_connect(rdr); + return -2; + } + + cc_crypt(&cc->block[DECRYPT], data, 20, DECRYPT); + cs_log_dump_dbg(D_CLIENT, data, 20, "cccam: login data"); + + if(memcmp(data, buf, 5)) // check server response + { + cs_log("%s login failed, usr/pwd invalid", getprefix()); + cc_cli_close(cl, 0); + block_connect(rdr); + return -2; + } + else + { + cs_log_dbg(D_READER, "%s login succeeded", getprefix()); + } + + cs_log_dbg(D_READER, "cccam: last_s=%" PRId64 ", last_g=%" PRId64, (int64_t)rdr->last_s, (int64_t)rdr->last_g); + + cl->pfd = cl->udp_fd; + cs_log_dbg(D_READER, "cccam: pfd=%d", cl->pfd); + + if(cc_send_cli_data(cl) <= 0) + { + cs_log("%s login failed, could not send client data", getprefix()); + cc_cli_close(cl, 0); + block_connect(rdr); + return -3; + } + + if(rdr->ftab.filts) + { + rdr->caid = rdr->ftab.filts[0].caid; + rdr->nprov = rdr->ftab.filts[0].nprids; + + for(n = 0; n < rdr->nprov; n++) + { + rdr->prid[n][0] = rdr->ftab.filts[0].prids[n] >> 24; + rdr->prid[n][1] = rdr->ftab.filts[0].prids[n] >> 16; + rdr->prid[n][2] = rdr->ftab.filts[0].prids[n] >> 8; + rdr->prid[n][3] = rdr->ftab.filts[0].prids[n] & 0xff; + } + } + + rdr->card_status = CARD_NEED_INIT; + rdr->last_g = rdr->last_s = time((time_t *) 0); + rdr->tcp_connected = 1; + + cc->just_logged_in = 1; + cl->crypted = 1; + cc->ecm_busy = 0; + +#ifdef CS_CACHEEX_AIO + if(cacheex_get_rdr_mode(rdr) > 0) + { +#endif + + cc_cacheex_filter_out(cl); + +#ifdef CS_CACHEEX_AIO + cc_cacheex_feature_request(cl); + } +#endif + + return 0; +} + +int32_t cc_cli_init_int(struct s_client *cl) +{ + struct s_reader *rdr = cl->reader; + + if(rdr->tcp_connected) + { + return 1; + } + + if(rdr->tcp_ito < 15 && rdr->tcp_ito !=-1) + { + rdr->tcp_ito = 30; + } + + if(rdr->cc_maxhops < 0) + { + rdr->cc_maxhops = DEFAULT_CC_MAXHOPS; + } + + if(rdr->tcp_rto < 1) // timeout to 30s + { + rdr->tcp_rto = 30; + } + + cs_log_dbg(D_READER, "cccam: inactivity timeout: %d seconds, receive timeout: %d seconds", + rdr->tcp_ito, rdr->tcp_rto); + + cc_check_version(rdr->cc_version, rdr->cc_build); + + cs_log_dbg(D_READER, "proxy reader: %s (%s:%d) cccam v%s build %s, maxhops: %d", + rdr->label, rdr->device, rdr->r_port, rdr->cc_version, rdr->cc_build, rdr->cc_maxhops); + + return 0; +} + +int32_t cc_cli_init(struct s_client *cl) +{ + struct s_reader *reader = cl->reader; + int32_t res = cc_cli_init_int(cl); // Create socket + + if(res == 0 && reader && (reader->cc_keepalive || !cl->cc) && !reader->tcp_connected) + { + cc_cli_connect(cl); // connect to remote server + + //while(!reader->tcp_connected && reader->cc_keepalive && cfg.reader_restart_seconds > 0) + //{ + // if((cc && cc->mode == CCCAM_MODE_SHUTDOWN)) + // { + // return -1; + // } + // + // if(!reader->tcp_connected) + // { + // cc_cli_close(cl, 0); + // res = cc_cli_init_int(cl); + // if(res) + // { + // return res; + // } + // } + // + // cs_log_dbg(D_READER, "%s restarting reader in %d seconds", reader->label, cfg.reader_restart_seconds); + // cs_sleepms(cfg.reader_restart_seconds*1000); + // cs_log_dbg(D_READER, "%s restarting reader...", reader->label); + // cc_cli_connect(cl); + //} + } + return res; +} + +/** + * return 1 if we are able to send requests: + */ +int32_t cc_available(struct s_reader *rdr, int32_t checktype, ECM_REQUEST *er) +{ + if(!rdr || !rdr->client) + { + return 0; + } + + struct s_client *cl = rdr->client; + if(!cl) + { + return 0; + } + + struct cc_data *cc = cl->cc; + if(er && cc && rdr->tcp_connected) + { + struct cc_card *card = get_matching_card(cl, er, 1); + if(!card) + { + return 0; + } + } + //cs_log_dbg(D_TRACE, "checking reader %s availibility", rdr->label); + + if(!cc || rdr->tcp_connected != 2) + { + // Two cases: + // 1. Keepalive ON but not connected: Do NOT send requests, + // because we can't connect - problem of full running pipes + // 2. Keepalive OFF but not connected: Send requests to connect + // pipe won't run full, because we are reading from pipe to + // get the ecm request + + if(rdr->cc_keepalive) + { + return 0; + } + } + + //if(er && er->ecmlen > 255 && cc && !cc->extended_mode && (cc->remote_build_nr < 3367)) + //{ + // return 0; // remote does not support large ecms! + //} + + if(checktype == AVAIL_CHECK_LOADBALANCE && cc && cc->ecm_busy) + { + if(cc_request_timeout(cl)) + { + cc_cycle_connection(cl); + } + + if(!rdr->tcp_connected || cc->ecm_busy) + { + cs_log_dbg(D_TRACE, "checking reader %s availibility=0 (unavail)", rdr->label); + return 0; // We are processing EMMs/ECMs + } + } + + return 1; +} + +/** + * + **/ +void cc_card_info(void) +{ + struct s_client *cl = cur_client(); + struct s_reader *rdr = cl->reader; + + if(rdr && !rdr->tcp_connected) + { + cc_cli_connect(cl); + } +} + +void cc_cleanup(struct s_client *cl) +{ + if(cl->typ != 'c') + { + cc_cli_close(cl, 1); // we need to close open fd's + } + cc_free(cl); +} + +void cc_update_nodeid(void) +{ + if(array_has_nonzero_byte(cfg.cc_fixed_nodeid, sizeof(cfg.cc_fixed_nodeid))) + { + memcpy(cc_node_id, cfg.cc_fixed_nodeid, 8); + return; + } + + // Partner Detection + uint16_t sum = 0x1234; // This is our checksum + int32_t i; + get_random_bytes(cc_node_id, 4); + + for(i = 0; i < 4; i++) + { + sum += cc_node_id[i]; + } + + // Partner ID + cc_node_id[4] = 0x10; // (Oscam 0x10, vPlugServer 0x11, Hadu 0x12, ...) + sum += cc_node_id[4]; + + // generate checksum for Partner ID: + cc_node_id[5] = 0xAA; + + for(i = 0; i < 5; i++) + { + cc_node_id[5] ^= cc_node_id[i]; + } + sum += cc_node_id[5]; + + cc_node_id[6] = sum >> 8; + cc_node_id[7] = sum & 0xff; + + memcpy(cfg.cc_fixed_nodeid, cc_node_id, 8); +} + +bool cccam_forward_origin_card(ECM_REQUEST *er) +{ + if(cfg.cc_forward_origin_card && er->origin_card) + { + struct cc_card *card = er->origin_card; + struct s_ecm_answer *eab = NULL; + struct s_ecm_answer *ea; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + ea->status &= ~(READER_ACTIVE | READER_FALLBACK); + if(card->origin_reader == ea->reader) + { + eab = ea; + } + } + + if(eab) + { + cs_log_dbg(D_LB, "loadbalancer: forward card: forced by card %d to reader %s", + card->id, eab->reader->label); + + eab->status |= READER_ACTIVE; + return true; + } + } + + return false; +} + +bool cccam_snprintf_cards_stat(struct s_client *cl, char *emmtext, size_t emmtext_sz) +{ + struct cc_data *rcc = cl->cc; + if(rcc) + { + LLIST *cards = rcc->cards; + if(cards) + { + int32_t ncards = ll_count(cards); + int32_t locals = rcc->num_hop1; + snprintf(emmtext, emmtext_sz, " %3d/%3d card%s", locals, ncards, ncards > 1 ? "s " : " "); + return true; + } + } + return false; +} + +bool cccam_client_extended_mode(struct s_client *cl) +{ + return cl && cl->cc && ((struct cc_data *)cl->cc)->extended_mode; +} + +bool cccam_client_multics_mode(struct s_client *cl) +{ + return cl && cl->cc && ((struct cc_data *)cl->cc)->multics_mode == 2; +} + +static int32_t compare_cards_by_hop(struct cc_card **pcard1, struct cc_card **pcard2) +{ + struct cc_card *card1 = (*pcard1), *card2 = (*pcard2); + + int32_t res = card1->hop - card2->hop; + if(res) { return res; } + res = card1->caid - card2->caid; + if(res) { return res; } + res = card1->reshare - card2->reshare; + if(res) { return res; } + res = card1->id - card2->id; + return res; +} + +static int32_t compare_cards_by_hop_r(struct cc_card **pcard1, struct cc_card **pcard2) +{ + return -compare_cards_by_hop(pcard1, pcard2); +} + +struct cc_card **get_sorted_card_copy(LLIST *cards, int32_t reverse, int32_t *size) +{ + if(reverse) + { return (struct cc_card **)ll_sort(cards, compare_cards_by_hop_r, size); } + else + { return (struct cc_card **)ll_sort(cards, compare_cards_by_hop, size); } +} + +#ifndef MODULE_CCCSHARE +/* Stub functions when CCCSHARE is disabled */ +LLIST **get_and_lock_sharelist(void) { return NULL; } +void unlock_sharelist(void) { } +void cccam_init_share(void) { } +#endif + +void module_cccam(struct s_module *ph) +{ + ph->desc = "cccam"; + ph->type = MOD_CONN_TCP; + ph->large_ecm_support = 1; + ph->listenertype = LIS_CCCAM; + ph->num = R_CCCAM; + ph->recv = cc_recv; + ph->cleanup = cc_cleanup; + ph->bufsize = 2048; + ph->c_init = cc_cli_init; + ph->c_idle = cc_idle; + ph->c_recv_chk = cc_recv_chk; + ph->c_send_ecm = cc_send_ecm; + ph->c_send_emm = cc_send_emm; +#ifdef MODULE_CCCSHARE + IP_ASSIGN(ph->s_ip, cfg.cc_srvip); + ph->s_handler = cc_srv_init; + ph->s_init = cc_srv_init2; + ph->s_idle = cc_s_idle; + ph->send_dcw = cc_send_dcw; +#endif + ph->c_available = cc_available; + ph->c_card_info = cc_card_info; + cc_cacheex_module_init(ph); + cc_update_nodeid(); + +#ifdef MODULE_CCCSHARE + int32_t i; + for(i = 0; i < CS_MAXPORTS; i++) + { + if(!cfg.cc_port[i]) + { + break; + } + ph->ptab.ports[i].s_port = cfg.cc_port[i]; + ph->ptab.nports++; + } + + if(cfg.cc_port[0]) + { + cccam_init_share(); + } +#endif +} +#endif diff --git a/module-cccam.h b/module-cccam.h new file mode 100644 index 0000000..5124fbe --- /dev/null +++ b/module-cccam.h @@ -0,0 +1,50 @@ +#ifndef MODULE_CCCAM_H_ +#define MODULE_CCCAM_H_ + +// In this file put functions that are called outside of module-cccam.c and module-cccshare.c + +void cc_update_nodeid(void); + +void cc_UA_cccam2oscam(uint8_t *in, uint8_t *out, uint16_t caid); + +int32_t cc_UA_valid(uint8_t *ua); + +void refresh_shares(void); +LLIST **get_and_lock_sharelist(void); +void unlock_sharelist(void); + +struct cc_card **get_sorted_card_copy(LLIST *cards, int32_t reverse, int32_t *size); + +void cccam_init_share(void); + +#if defined(MODULE_CCCSHARE) +void cccam_done_share(void); +#else +static inline void cccam_done_share(void) { } +#endif + +#if defined(MODULE_CCCAM) +bool cccam_forward_origin_card(ECM_REQUEST *er); +bool cccam_snprintf_cards_stat(struct s_client *cl, char *emmtext, size_t emmtext_sz); +bool cccam_client_extended_mode(struct s_client *cl); +bool cccam_client_multics_mode(struct s_client *cl); +#else +static inline bool cccam_forward_origin_card(ECM_REQUEST *UNUSED(er)) +{ + return false; +} +static inline bool cccam_snprintf_cards_stat(struct s_client *UNUSED(cl), char *UNUSED(emmtext), size_t UNUSED(emmtext_sz)) +{ + return false; +} +static inline bool cccam_client_extended_mode(struct s_client *UNUSED(cl)) +{ + return false; +} +static inline bool cccam_client_multics_mode(struct s_client *UNUSED(cl)) +{ + return false; +} +#endif + +#endif diff --git a/module-cccshare.c b/module-cccshare.c new file mode 100644 index 0000000..10306ca --- /dev/null +++ b/module-cccshare.c @@ -0,0 +1,1728 @@ +#define MODULE_LOG_PREFIX "cccam" + +#include "globals.h" + +#if defined(MODULE_CCCAM) && defined(MODULE_CCCSHARE) + +#include "module-cccam.h" +#include "module-cccam-data.h" +#include "module-cccshare.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-lock.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" + +extern uint32_t cfg_sidtab_generation; + +static uint32_t cc_share_id = 0x64; +static LLIST *reported_carddatas_list[CAID_KEY]; +static CS_MUTEX_LOCK cc_shares_lock; + +static int32_t card_added_count; +static int32_t card_removed_count; +static int32_t card_dup_count; +static pthread_t share_updater_thread; +static bool share_updater_thread_active; +static bool share_updater_refresh; + +int32_t card_valid_for_client(struct s_client *cl, struct cc_card *card); +int32_t flt = 0; + +LLIST *get_cardlist(uint16_t caid, LLIST **list) +{ + caid = (caid >> 8) % CAID_KEY; + if(!list[caid]) + { list[caid] = ll_create("card_list"); } + return list[caid]; +} + +LLIST **get_and_lock_sharelist(void) +{ + cs_readlock(__func__, &cc_shares_lock); + return reported_carddatas_list; +} + +void unlock_sharelist(void) +{ + cs_readunlock(__func__, &cc_shares_lock); +} + +void add_good_sids(struct s_sidtab *ptr, struct cc_card *card) +{ + int32_t l; + for(l = 0; l < ptr->num_srvid; l++) + { + struct cc_srvid *srvid; + if(!cs_malloc(&srvid, sizeof(struct cc_srvid))) + { return; } + srvid->sid = ptr->srvid[l]; + srvid->chid = 0; + srvid->ecmlen = 0; // 0=undefined, also not used with "O" CCcam + + if(!ll_contains_data(card->goodsids, srvid, sizeof(struct cc_srvid))) + { ll_append(card->goodsids, srvid); } + else { NULLFREE(srvid);} + } +} + +void add_bad_sids(struct s_sidtab *ptr, struct cc_card *card) +{ + int32_t l; + for(l = 0; l < ptr->num_srvid; l++) + { + struct cc_srvid_block *srvid; + if(!cs_malloc(&srvid, sizeof(struct cc_srvid_block))) + { return; } + srvid->sid = ptr->srvid[l]; + srvid->chid = 0; + srvid->ecmlen = 0; // 0=undefined, also not used with "O" CCcam + srvid->blocked_till = 0; + + if(!ll_contains_data(card->badsids, srvid, sizeof(struct cc_srvid_block))) + { ll_append(card->badsids, srvid); } + else { NULLFREE(srvid); } + } +} + +void add_good_bad_sids_by_rdr(struct s_reader *rdr, struct cc_card *card) +{ + struct s_sidtab *ptr; + int32_t n, i; + for(n = 0, ptr = cfg.sidtab; ptr; ptr = ptr->next, n++) + { + if(rdr->sidtabs.ok & ((SIDTABBITS)1 << n)) + { + for(i = 0; i < ptr->num_caid; i++) + { + if(ptr->caid[i] == card->caid) + { add_good_sids(ptr, card); } + } + } + else if(rdr->sidtabs.no & ((SIDTABBITS)1 << n)) + { + for(i = 0; i < ptr->num_caid; i++) + { + if(ptr->caid[i] == card->caid) + { add_bad_sids(ptr, card); } + } + } + } +} + +int32_t can_use_ext(struct cc_card *card) +{ + if(card->card_type == CT_REMOTECARD) + { return card->is_ext; } + + if(card->sidtab) + { return (card->sidtab->num_srvid > 0); } + else + { return ll_count(card->goodsids) || ll_count(card->badsids); } + return 0; +} + +int32_t write_card(struct cc_data *cc, uint8_t *buf, struct cc_card *card, int32_t add_own, int32_t ext, int32_t au_allowed, struct s_client *cl) +{ + memset(buf, 0, CC_MAXMSGSIZE); + buf[0] = card->id >> 24; + buf[1] = card->id >> 16; + buf[2] = card->id >> 8; + buf[3] = card->id & 0xff; + buf[4] = card->remote_id >> 24; + buf[5] = card->remote_id >> 16; + buf[6] = card->remote_id >> 8; + buf[7] = card->remote_id & 0xFF; + buf[8] = card->caid >> 8; + buf[9] = card->caid & 0xff; + buf[10] = card->hop; + buf[11] = card->reshare; + if(au_allowed) + { memcpy(buf + 12, card->hexserial, 8); } + + // with cccam 2.2.0 we have assigned and rejected sids: + int32_t ofs = ext ? 23 : 21; + + // write providers: + LL_ITER it = ll_iter_create(card->providers); + struct cc_provider *prov; + while((prov = ll_iter_next(&it))) + { + uint32_t prid = prov->prov; + buf[ofs + 0] = prid >> 16; + buf[ofs + 1] = prid >> 8; + buf[ofs + 2] = prid & 0xFF; + if(au_allowed) + { memcpy(buf + ofs + 3, prov->sa, 4); } + buf[20]++; + ofs += 7; + } + + // write sids only if cccam 2.2.x: + if(ext) + { + if(card->sidtab) + { + // good sids: + struct s_sidtab *ptr = card->sidtab; + int32_t l; + for(l = 0; l < ptr->num_srvid; l++) + { + buf[ofs + 0] = ptr->srvid[l] >> 8; + buf[ofs + 1] = ptr->srvid[l] & 0xFF; + ofs += 2; + buf[21]++; // nassign + if(buf[21] >= 240) + { break; } + } + + // bad sids: + int32_t n; + for(n = 0, ptr = cfg.sidtab; ptr; ptr = ptr->next, n++) + { + if((cl->sidtabs.no & ((SIDTABBITS)1 << n)) || (card->sidtabno & ((SIDTABBITS)1 << n))) + { + int32_t m; + int32_t ok_caid = 0; + for(m = 0; m < ptr->num_caid; m++) // search bad sids for this caid: + { + if(ptr->caid[m] == card->caid) + { + ok_caid = 1; + break; + } + } + if(ok_caid) + { + for(l = 0; l < ptr->num_srvid; l++) + { + buf[ofs + 0] = ptr->srvid[l] >> 8; + buf[ofs + 1] = ptr->srvid[l] & 0xFF; + ofs += 2; + buf[22]++; // nreject + if(buf[22] >= 240) + { break; } + } + } + } + if(buf[22] >= 240) + { break; } + } + + } + else + { + // assigned sids: + it = ll_iter_create(card->goodsids); + struct cc_srvid *srvid; + struct cc_srvid_block *srvidblock; + while((srvid = ll_iter_next(&it))) + { + buf[ofs + 0] = srvid->sid >> 8; + buf[ofs + 1] = srvid->sid & 0xFF; + ofs += 2; + buf[21]++; // nassign + if(buf[21] >= 200) + { break; } + } + + // reject sids: + it = ll_iter_create(card->badsids); + while((srvidblock = ll_iter_next(&it))) + { + if(srvidblock->blocked_till > 0) + { + continue; + } + buf[ofs + 0] = srvidblock->sid >> 8; + buf[ofs + 1] = srvidblock->sid & 0xFF; + ofs += 2; + buf[22]++; // nreject + if(buf[22] >= 200) + { break; } + } + } + } + + // write remote nodes + int32_t nremote_ofs = ofs; + ofs++; + it = ll_iter_create(card->remote_nodes); + uint8_t *remote_node; + while((remote_node = ll_iter_next(&it))) + { + memcpy(buf + ofs, remote_node, 8); + ofs += 8; + buf[nremote_ofs]++; + } + if(add_own) + { + memcpy(buf + ofs, cc->node_id, 8); + ofs += 8; + buf[nremote_ofs]++; + } + return ofs; +} + +static int32_t is_client_au_allowed(struct cc_card *card, struct s_client *cl) +{ + if(!card || !card->origin_reader) + { + return 0; + } + + if(!cl || !cl->aureader_list || !ll_count(cl->aureader_list)) + { + return 0; + } + + struct s_reader *rdr = NULL; + LL_ITER itr = ll_iter_create(cl->aureader_list); + while((rdr = ll_iter_next(&itr))) + { + if(rdr == card->origin_reader) + { + return 1; + } + } + + return 0; +} + +static int32_t send_card_to_client(struct cc_card *card, struct s_client *cl) +{ + uint8_t buf[CC_MAXMSGSIZE]; + + if(!card_valid_for_client(cl, card)) + { return 0; } + + int8_t usr_reshare = cl->account->cccreshare; + if(usr_reshare == -1) + { usr_reshare = cfg.cc_reshare; } + + int8_t ignorereshare = cl->account->cccignorereshare; + if(ignorereshare == -1) + { ignorereshare = cfg.cc_ignore_reshare; } + + int8_t reader_reshare = card->origin_reader ? card->rdr_reshare : usr_reshare; + if(reader_reshare == -1) + { reader_reshare = cfg.cc_reshare; } + + int8_t reshare = (reader_reshare < usr_reshare) ? reader_reshare : usr_reshare; + int8_t new_reshare; + if(card->card_type == CT_CARD_BY_SERVICE_USER) + { + new_reshare = usr_reshare; + } + else if(ignorereshare) + { + new_reshare = reshare; + } + else + { + new_reshare = card->reshare; + if(card->card_type == CT_REMOTECARD) + { new_reshare--; } + if(new_reshare > reshare) + { new_reshare = reshare; } + } + if(new_reshare < 0) + { return 0; } + + if(!card->id) + { card->id = cc_share_id++; } + + struct cc_data *cc = cl->cc; + int32_t is_ext = cc->cccam220 && can_use_ext(card); + int32_t len = write_card(cc, buf, card, 1, is_ext, is_client_au_allowed(card, cl), cl); + //buf[10] = card->hop-1; + buf[11] = new_reshare; + + struct s_clientmsg *clientmsg; + if(cs_malloc(&clientmsg, sizeof(struct s_clientmsg))) + { + memcpy(clientmsg->msg, buf, len); + clientmsg->len = len; + clientmsg->cmd = is_ext ? MSG_NEW_CARD_SIDINFO : MSG_NEW_CARD; + add_job(cl, ACTION_CLIENT_SEND_MSG, clientmsg, sizeof(struct s_clientmsg)); + } + return 1; +} + +int32_t hide_card_to_client(struct cc_card *card, struct s_client *cl) +{ + if(!card || !card->id) + { return 0; } + + uint8_t buf[4]; + buf[0] = card->id >> 24; + buf[1] = card->id >> 16; + buf[2] = card->id >> 8; + buf[3] = card->id & 0xFF; + + struct s_clientmsg *clientmsg; + struct cc_data *cc = cl->cc; + if(cc && (cl->typ == 'c') && !cl->kill && (get_module(cl)->num == R_CCCAM)) //CCCam-Client! + { + if(card_valid_for_client(cl, card)) + { + if(cs_malloc(&clientmsg, sizeof(struct s_clientmsg))) + { + memcpy(clientmsg->msg, buf, sizeof(buf)); + clientmsg->len = sizeof(buf); + clientmsg->cmd = MSG_CARD_REMOVED; + add_job(cl, ACTION_CLIENT_SEND_MSG, clientmsg, sizeof(struct s_clientmsg)); + return 1; + } + } + } + return 0; +} + +int32_t unhide_card_to_client(struct cc_card *card, struct s_client *cl) +{ + return send_card_to_client(card, cl); +} + +int32_t hidecards_card_valid_for_client(struct s_client *cl, struct cc_card *card) +{ + return card_valid_for_client(cl, card); +} + +int32_t send_card_to_all_clients(struct cc_card *card) +{ + int32_t count = 0; + struct s_client *cl; + cs_readlock(__func__, &clientlist_lock); + for(cl = first_client; cl; cl = cl->next) + { + if(cl->cc && cl->typ == 'c' && !cl->kill && get_module(cl)->num == R_CCCAM) // CCCam-Client! + { + count += send_card_to_client(card, cl); + } + } + cs_readunlock(__func__, &clientlist_lock); + return count; +} + +void send_remove_card_to_clients(struct cc_card *card) +{ + if(!card || !card->id) + { return; } + + uint8_t buf[4]; + buf[0] = card->id >> 24; + buf[1] = card->id >> 16; + buf[2] = card->id >> 8; + buf[3] = card->id & 0xFF; + + struct s_client *cl; + struct s_clientmsg *clientmsg; + cs_readlock(__func__, &clientlist_lock); + for(cl = first_client; cl; cl = cl->next) + { + struct cc_data *cc = cl->cc; + if(cc && cl->typ == 'c' && !cl->kill && get_module(cl)->num == R_CCCAM) // CCCam-Client! + { + if(card_valid_for_client(cl, card)) + { + if(cs_malloc(&clientmsg, sizeof(struct s_clientmsg))) + { + memcpy(clientmsg->msg, buf, sizeof(buf)); + clientmsg->len = sizeof(buf); + clientmsg->cmd = MSG_CARD_REMOVED; + add_job(cl, ACTION_CLIENT_SEND_MSG, clientmsg, sizeof(struct s_clientmsg)); + } + } + } + } + cs_readunlock(__func__, &clientlist_lock); +} + + +/** + * if idents defined on an cccam reader, the cards caid+provider are checked. + * return 1 a) if no ident defined b) card is in identlist + * 0 if card is not in identlist + * + * a card is in the identlist, if the cards caid is matching and mininum a provider is matching + **/ +int32_t chk_ident(FTAB *ftab, struct cc_card *card) +{ + + int32_t j, k; + int32_t res = 1; + + if(ftab && ftab->filts) + { + for(j = 0; j < ftab->nfilts; j++) + { + if(ftab->filts[j].caid) + { + res = 0; + if(ftab->filts[j].caid == card->caid) // caid matches! + { + + int32_t nprids = ftab->filts[j].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + + LL_ITER it = ll_iter_create(card->providers); + struct cc_provider *prov; + + while((prov = ll_iter_next(&it))) + { + for(k = 0; k < nprids; k++) + { + uint32_t prid = ftab->filts[j].prids[k]; + if(prid == prov->prov) // Provider matches + { + return 1; + } + } + } + } + } + } + } + return res; +} + +int32_t cc_clear_reported_carddata(LLIST *reported_carddatas, LLIST *except, int32_t send_removed) +{ + int32_t i = 0; + LL_ITER it = ll_iter_create(reported_carddatas); + struct cc_card *card; + while((card = ll_iter_next(&it))) + { + struct cc_card *card2 = NULL; + if(except) + { + LL_ITER it2 = ll_iter_create(except); + while((card2 = ll_iter_next(&it2))) + { + if(card == card2) + { break; } + } + } + + if(!card2 && ll_iter_remove(&it)) // check result of ll_iter_remove, because another thread could removed it + { + if(send_removed) + { + cs_log_dbg(D_TRACE, "s-card removed: id %8X remoteid %8X caid %4X hop %d reshare %d originid %8X cardtype %d", + card->id, card->remote_id, card->caid, card->hop, card->reshare, card->origin_id, card->card_type); + + send_remove_card_to_clients(card); + } + cc_free_card(card); + i++; + } + } + return i; +} + +int32_t cc_free_reported_carddata(LLIST *reported_carddatas, LLIST *except, int32_t send_removed) +{ + int32_t i = 0; + if(reported_carddatas) + { + i = cc_clear_reported_carddata(reported_carddatas, except, send_removed); + ll_destroy(&reported_carddatas); + } + return i; +} + +int32_t card_valid_for_client(struct s_client *cl, struct cc_card *card) +{ + + // Check group: + if(card->grp && !(card->grp & cl->grp)) + { return 0; } + + // Check idents: + if(!chk_ident(&cl->ftab, card)) + { return 0; } + + // Check caids: + if(!chk_ctab(card->caid, &cl->ctab)) + { return 0; } + + // Check reshare + if(card->card_type == CT_REMOTECARD) + { + int8_t ignorereshare = cl->account->cccignorereshare; + if(ignorereshare == -1) { ignorereshare = cfg.cc_ignore_reshare; } + if(!ignorereshare && !card->reshare) + { return 0; } + } + + // Check account maxhops: + if(cl->account->cccmaxhops < card->hop) + { return 0; } + + // Check remote node id, if card is from there, ignore it! + LL_ITER it = ll_iter_create(card->remote_nodes); + uint8_t *node; + struct cc_data *cc = cl->cc; + while((node = ll_iter_next(&it))) + { + if(!memcmp(node, cc->peer_node_id, 8)) + { + return 0; + } + } + + // Check Services: + if(ll_count(card->providers)) + { + it = ll_iter_create(card->providers); + struct cc_provider *prov; + int8_t found = 0; + while((prov = ll_iter_next(&it))) + { + uint32_t prid = prov->prov; + if(chk_srvid_by_caid_prov(cl, card->caid, prid)) + { + found = 1; + break; + } + } + if(!found) { return 0; } + } + else + { + if(!chk_srvid_by_caid_prov(cl, card->caid, 0)) + { return 0; } + } + + // Check Card created by Service: + if(card->sidtab) + { + struct s_sidtab *ptr; + int32_t j; + int32_t ok = !cl->sidtabs.ok && !cl->sidtabs.no; // default valid if no positive services and no negative services + if(!ok) + { + if(!cl->sidtabs.ok) // no positive services, so ok by default if no negative found + { ok = 1; } + + for(j = 0, ptr = cfg.sidtab; ptr; ptr = ptr->next, j++) + { + if(ptr == card->sidtab) + { + if(cl->sidtabs.no & ((SIDTABBITS)1 << j)) + { return 0; } + if(cl->sidtabs.ok & ((SIDTABBITS)1 << j)) + { ok = 1; } + break; + } + } + } + if(!ok) + { return 0; } + } + + return 1; +} + +uint32_t get_reader_prid(struct s_reader *rdr, int32_t j) +{ + return b2i(3, &rdr->prid[j][1]); +} +//uint32_t get_reader_prid(struct s_reader *rdr, int32_t j) { +// uint32_t prid; +// if (!is_cascading_reader(rdr)) { // Real cardreaders have 4-byte Providers +// prid = b2i(4, &rdr->prid[j][0]); +// //prid = (rdr->prid[j][0] << 24) | (rdr->prid[j][1] << 16) +// // | (rdr->prid[j][2] << 8) | (rdr->prid[j][3] & 0xFF); +// } else { // Cascading/Network-reader 3-bytes Providers +// prid = b2i(3, &rdr->prid[j][0]); +// //prid = (rdr->prid[j][0] << 16) | (rdr->prid[j][1] << 8) +// // | (rdr->prid[j][2] & 0xFF); +// +// } +// return prid; +//} + +void copy_good_sids(LLIST *dst, LLIST *src) +{ + LL_ITER it_src = ll_iter_create(src); + LL_ITER it_dst = ll_iter_create(dst); + struct cc_srvid *srvid_src; + struct cc_srvid *srvid_dst; + while((srvid_src = ll_iter_next(&it_src))) + { + ll_iter_reset(&it_dst); + while((srvid_dst = ll_iter_next(&it_dst))) + { + if(sid_eq(srvid_src, srvid_dst)) + { break; } + } + if(!srvid_dst) + { + if(!cs_malloc(&srvid_dst, sizeof(struct cc_srvid))) + { break; } + memcpy(srvid_dst, srvid_src, sizeof(struct cc_srvid)); + ll_iter_insert(&it_dst, srvid_dst); + } + } +} + +void copy_bad_sids(LLIST *dst, LLIST *src) +{ + LL_ITER it_src = ll_iter_create(src); + LL_ITER it_dst = ll_iter_create(dst); + struct cc_srvid_block *srvid_src; + struct cc_srvid_block *srvid_dst; + while((srvid_src = ll_iter_next(&it_src))) + { + ll_iter_reset(&it_dst); + while((srvid_dst = ll_iter_next(&it_dst))) + { + if(sid_eq_bb(srvid_src, srvid_dst)) + { break; } + } + if(!srvid_dst) + { + if(!cs_malloc(&srvid_dst, sizeof(struct cc_srvid_block))) + { break; } + memcpy(srvid_dst, srvid_src, sizeof(struct cc_srvid_block)); + ll_iter_insert(&it_dst, srvid_dst); + } + } +} + +int32_t add_card_providers(struct cc_card *dest_card, struct cc_card *card, int32_t copy_remote_nodes) +{ + int32_t modified = 0; + + // 1. Copy nonexisting providers, ignore double: + struct cc_provider *prov_info; + LL_ITER it_src = ll_iter_create(card->providers); + LL_ITER it_dst = ll_iter_create(dest_card->providers); + + struct cc_provider *provider; + while((provider = ll_iter_next(&it_src))) + { + ll_iter_reset(&it_dst); + while((prov_info = ll_iter_next(&it_dst))) + { + if(prov_info->prov == provider->prov) + { break; } + } + if(!prov_info) + { + struct cc_provider *prov_new; + if(!cs_malloc(&prov_new, sizeof(struct cc_provider))) + { break; } + memcpy(prov_new, provider, sizeof(struct cc_provider)); + ll_iter_insert(&it_dst, prov_new); + modified = 1; + } + } + + if(copy_remote_nodes) + { + // 2. Copy nonexisting remote_nodes, ignoring existing: + it_src = ll_iter_create(card->remote_nodes); + it_dst = ll_iter_create(dest_card->remote_nodes); + uint8_t *remote_node; + uint8_t *remote_node2; + while((remote_node = ll_iter_next(&it_src))) + { + ll_iter_reset(&it_dst); + while((remote_node2 = ll_iter_next(&it_dst))) + { + if(memcmp(remote_node, remote_node2, 8) == 0) + { break; } + } + if(!remote_node2) + { + uint8_t *remote_node_new; + if(!cs_malloc(&remote_node_new, 8)) + { break; } + memcpy(remote_node_new, remote_node, 8); + ll_iter_insert(&it_dst, remote_node_new); + modified = 1; + } + } + } + return modified; +} + +#define TIMEOUT_SECONDS 3600 + +void set_card_timeout(struct cc_card *card) +{ + card->timeout = time(NULL) + TIMEOUT_SECONDS + ((rand() & 0xff) - 128) * 2; +} + +struct cc_card *create_card(struct cc_card *card) +{ + struct cc_card *card2; + if(!cs_malloc(&card2, sizeof(struct cc_card))) + { return NULL; } + if(card) + { memcpy(card2, card, sizeof(struct cc_card)); } + else + { memset(card2, 0, sizeof(struct cc_card)); } + card2->providers = ll_create("providers"); + card2->badsids = ll_create("badsids"); + card2->goodsids = ll_create("goodsids"); + card2->remote_nodes = ll_create("remote_nodes"); + + if(card) + { + copy_good_sids(card2->goodsids, card->goodsids); + copy_bad_sids(card2->badsids, card->badsids); + card2->id = 0; + } + else + { set_card_timeout(card2); } + + return card2; +} + +struct cc_card *create_card2(struct s_reader *rdr, int32_t j, uint16_t caid, uint8_t reshare) +{ + + struct cc_card *card = create_card(NULL); + if(!card) + { return NULL; } + card->remote_id = (rdr ? (rdr->cc_id << 16) : 0x7F7F8000) | j; + card->caid = caid; + card->reshare = reshare; + card->origin_reader = rdr; + if(rdr) + { + card->grp = rdr->grp; + card->rdr_reshare = rdr->cc_reshare > -1 ? rdr->cc_reshare : cfg.cc_reshare; //copy reshare because reader could go offline + card->sidtabno = rdr->sidtabs.no; + card->hop = rdr->cc_hop; + } + else { card->rdr_reshare = reshare; } + return card; +} + +/** + * num_same_providers checks if card1 has exactly the same providers as card2 + * returns same provider count + **/ +int32_t num_same_providers(struct cc_card *card1, struct cc_card *card2) +{ + + int32_t found = 0; + + LL_ITER it1 = ll_iter_create(card1->providers); + LL_ITER it2 = ll_iter_create(card2->providers); + + struct cc_provider *prov1, *prov2; + + while((prov1 = ll_iter_next(&it1))) + { + + ll_iter_reset(&it2); + while((prov2 = ll_iter_next(&it2))) + { + if(prov1->prov == prov2->prov) + { + found++; + break; + } + + } + } + return found; +} + +/** + * equal_providers checks if card1 has exactly the same providers as card2 + * returns 1=equal 0=different + **/ +int32_t equal_providers(struct cc_card *card1, struct cc_card *card2) +{ + + if(ll_count(card1->providers) != ll_count(card2->providers)) + { return 0; } + if(ll_count(card1->providers) == 0) + { return 1; } + + LL_ITER it1 = ll_iter_create(card1->providers); + LL_ITER it2 = ll_iter_create(card2->providers); + + struct cc_provider *prov1, *prov2; + + while((prov1 = ll_iter_next(&it1))) + { + + ll_iter_reset(&it2); + while((prov2 = ll_iter_next(&it2))) + { + if(prov1->prov == prov2->prov) + { + break; + } + + } + if(!prov2) { break; } + } + return (prov1 == NULL); +} + +int32_t is_au_card(struct cc_card *card) +{ + if(card && card->origin_reader) + { return !card->origin_reader->audisabled && cc_UA_valid(card->hexserial); } + return 0; +} + +void merge_sids(struct cc_card *carddst, struct cc_card *cardsrc) +{ + LL_ITER it; + struct cc_srvid *srvid; + struct cc_srvid_block *srvidb; + + int32_t goodSidCountSrc = ll_count(cardsrc->goodsids); + int32_t goodSidCountDst = ll_count(carddst->goodsids); + + if(goodSidCountDst == 0) + { + // remove sid blocks good+notbad from src + it = ll_iter_create(cardsrc->goodsids); + while((srvid = ll_iter_next(&it))) + { + if(!is_sid_blocked(cardsrc, srvid)) + { remove_sid_block(carddst, srvid); } + } + } + else + { + if(goodSidCountSrc == 0) + { + // del goods from dst + ll_clear(carddst->goodsids); + + // del bads from dst + ll_clear(carddst->badsids); + + // add bads from src + it = ll_iter_create(cardsrc->badsids); + while((srvidb = ll_iter_next(&it))) + { + { add_sid_block(carddst, (struct cc_srvid*)srvidb, false); } + } + } + else + { + // add good sid good+notbad from src + it = ll_iter_create(cardsrc->goodsids); + while((srvid = ll_iter_next(&it))) + { + if(!is_sid_blocked(cardsrc, srvid)) + { add_good_sid(carddst, srvid); } + } + } + } + +} + +/** + * Adds a new card to a cardlist. + */ +int32_t add_card_to_serverlist(LLIST *cardlist, struct cc_card *card, int8_t free_card) +{ + + int32_t modified = 0; + if(!card) + { return modified; } + + LL_ITER it = ll_iter_create(cardlist); + struct cc_card *card2; + + // Minimize all, transmit just CAID, merge providers: + if(cfg.cc_minimize_cards == MINIMIZE_CAID && !cfg.cc_forward_origin_card) + { + while((card2 = ll_iter_next(&it))) + { + // compare caid, hexserial, cardtype and sidtab (if any): + if(same_card2(card, card2, 0)) + { + // Merge cards only if resulting providercount is smaller than CS_MAXPROV + int32_t nsame, ndiff, nnew; + + nsame = num_same_providers(card, card2); // count same cards + ndiff = ll_count(card->providers) - nsame; // cound different cards, this cound will be added + nnew = ndiff + ll_count(card2->providers); // new card count after add. because its limited to CS_MAXPROV, dont add it + + if(nnew <= CS_MAXPROV) + { break; } + } + } + + if(!card2) // Not found->add it: + { + if(free_card) // Use this card + { + free_card = 0; + ll_iter_insert(&it, card); + } + else + { + card2 = create_card(card); // Copy card + if(!card2) + { return modified; } + card2->hop = 0; + ll_iter_insert(&it, card2); + add_card_providers(card2, card, 1); // copy providers to new card. Copy remote nodes to new card + } + modified = 1; + + } + else // found, merge providers: + { + card_dup_count++; + card2->grp |= card->grp; // add group to the card + add_card_providers(card2, card, 0); // merge all providers + ll_clear_data(card2->remote_nodes); // clear remote nodes + merge_sids(card2, card); + } + } + + // Removed duplicate cards, keeping card with lower hop: + else if(cfg.cc_minimize_cards == MINIMIZE_HOPS && !cfg.cc_forward_origin_card) + { + while((card2 = ll_iter_next(&it))) + { + // compare caid, hexserial, cardtype, sidtab (if any), providers: + if(same_card2(card, card2, 0) && equal_providers(card, card2)) + { + break; + } + } + + if(card2 && card2->hop > card->hop) // hop is smaller, drop old card + { + ll_iter_remove(&it); + cc_free_card(card2); + card2 = NULL; + card_dup_count++; + } + + if(!card2) // Not found->add it: + { + if(free_card) // use this card + { + free_card = 0; + ll_iter_insert(&it, card); + } + else + { + card2 = create_card(card); // copy card + if(!card2) + { return modified; } + ll_iter_insert(&it, card2); + add_card_providers(card2, card, 1); // copy providers to new card. Copy remote nodes to new card + } + modified = 1; + } + else // found, merge cards (providers are same!) + { + card_dup_count++; + card2->grp |= card->grp; //add group to the card + add_card_providers(card2, card, 0); + merge_sids(card2, card); + } + + } + // like cccam: + else // just remove duplicate cards (same ids) + { + while((card2 = ll_iter_next(&it))) + { + // compare remote_id, first_node, caid, hexserial, cardtype, sidtab (if any), providers: + if(same_card(card, card2)) + { break; } + } + + if(card2 && card2->hop > card->hop) // same card, if hop greater drop card + { + ll_iter_remove(&it); + cc_free_card(card2); + card2 = NULL; + card_dup_count++; + } + if(!card2) // Not found, add it: + { + if(free_card) + { + free_card = 0; + ll_iter_insert(&it, card); + } + else + { + card2 = create_card(card); + if(!card2) + { return modified; } + ll_iter_insert(&it, card2); + add_card_providers(card2, card, 1); + } + modified = 1; + } + else // Found, everything is same (including providers) + { + card_dup_count++; + } + } + + if(free_card) + { cc_free_card(card); } + + return modified; +} + +/** + * returns true if timeout-time is reached + * only local cards needs to be renewed after 1h. "O" CCCam throws away cards older than 1,2h + **/ +int32_t card_timed_out(struct cc_card *card) +{ + //!=CT_REMOTECARD = LOCALCARD (or virtual cards by caid/ident/service) + //timeout is set in future, so if current time is bigger, timeout is reached + int32_t res = (card->card_type != CT_REMOTECARD) && (card->timeout < time(NULL)); //local card is older than 1h? + if(res) + { cs_log_dbg(D_TRACE, "card %08X timed out! refresh forced", card->id ? card->id : card->origin_id); } + return res; +} + +/** + * returns true if card1 is already reported. + * "reported" means, we already have this card1 in our sharelist. + * if the card1 is already reported, we throw it away, because we build a new sharelist + * so after finding all reported cards, we have a list of reported cards, which aren't used anymore + **/ +int32_t find_reported_card(struct cc_card *card1) +{ + LL_ITER it = ll_iter_create(get_cardlist(card1->caid, reported_carddatas_list)); + struct cc_card *card2; + while((card2 = ll_iter_next(&it))) + { + if(same_card(card1, card2) && !card_timed_out(card2)) + { + card1->id = card2->id; //Set old id !! + card1->timeout = card2->timeout; + cc_free_card(card2); + ll_iter_remove(&it); + return 1; //Old card and new card are equal! + } + } + return 0; //Card not found +} + +/** +* Server: +* Adds a cccam-carddata buffer to the list of reported carddatas +*/ +void cc_add_reported_carddata(LLIST *reported_carddatas, struct cc_card *card) +{ + ll_append(reported_carddatas, card); +} + +/** + * adds the card to the list of the new reported carddatas - this is the new sharelist + * if this card is not already reported, we send them to the clients + * if this card is already reported, find_reported_card throws the "origin" card away + * so the "old" sharelist is reduced + **/ +void report_card(struct cc_card *card, LLIST *new_reported_carddatas, LLIST *new_cards) +{ + if(!find_reported_card(card)) //Add new card: + { + + cs_log_dbg(D_TRACE, "s-card added: id %8X remoteid %8X caid %4X hop %d reshare %d originid %8X cardtype %d", + card->id, card->remote_id, card->caid, card->hop, card->reshare, card->origin_id, card->card_type); + + ll_append(new_cards, card); + + card_added_count++; + } + cc_add_reported_carddata(new_reported_carddatas, card); +} + + +/** + * Server: + * Reports all caid/providers to the connected clients + * returns 1=ok, 0=error + * cfg.cc_reshare_services =0 CCCAM reader reshares only received cards + defined reader services + * =1 CCCAM reader reshares received cards + defined services + * =2 CCCAM reader reshares only defined reader-services as virtual cards + * =3 CCCAM reader reshares only defined user-services as virtual cards + * =4 CCCAM reader reshares only received cards + */ +void update_card_list(void) +{ + int32_t i, j, k, l, card_count = 0; + + LLIST *server_cards[CAID_KEY]; + LLIST *new_reported_carddatas[CAID_KEY]; + + LL_ITER it, it2; + struct cc_card *card; + + memset(server_cards, 0, sizeof(server_cards)); + memset(new_reported_carddatas, 0, sizeof(new_reported_carddatas)); + + card_added_count = 0; + card_removed_count = 0; + card_dup_count = 0; + + //User-Services: + if(cfg.cc_reshare_services == 3 && cfg.sidtab) + { + struct s_sidtab *ptr; + for(j = 0, ptr = cfg.sidtab; ptr; ptr = ptr->next, j++) + { + for(k = 0; k < ptr->num_caid; k++) + { + card = create_card2(NULL, (j << 8) | k, ptr->caid[k], cfg.cc_reshare); + if(!card) + { return; } + card->card_type = CT_CARD_BY_SERVICE_USER; + card->sidtab = ptr; + for(l = 0; l < ptr->num_provid; l++) + { + struct cc_provider *prov; + if(!cs_malloc(&prov, sizeof(struct cc_provider))) + { return; } + prov->prov = ptr->provid[l]; + ll_append(card->providers, prov); + } + + add_card_to_serverlist(get_cardlist(card->caid, server_cards), card, 1); + } + flt = 1; + } + } + else + { + struct s_reader *rdr; + int32_t r = 0; + + cs_readlock(__func__, &readerlist_lock); + + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + //Generate a uniq reader id: + if(!rdr->cc_id) + { + rdr->cc_id = ++r; + struct s_reader *rdr2; + for(rdr2 = first_active_reader; rdr2; rdr2 = rdr2->next) + { + if(rdr2 != rdr && rdr2->cc_id == rdr->cc_id) + { + rdr2 = first_active_reader; + rdr->cc_id = ++r; + } + } + } + + flt = 0; + + int8_t reshare = rdr->cc_reshare > -1 ? rdr->cc_reshare : cfg.cc_reshare; + + //Reader-Services: + if((cfg.cc_reshare_services == 1 || cfg.cc_reshare_services == 2 || (!rdr->caid && rdr->typ != R_CCCAM && cfg.cc_reshare_services != 4)) && + cfg.sidtab && (rdr->sidtabs.no || rdr->sidtabs.ok)) + { + struct s_sidtab *ptr; + for(j = 0, ptr = cfg.sidtab; ptr; ptr = ptr->next, j++) + { + if(!(rdr->sidtabs.no & ((SIDTABBITS)1 << j)) && (rdr->sidtabs.ok & ((SIDTABBITS)1 << j))) + { + for(k = 0; k < ptr->num_caid; k++) + { + card = create_card2(rdr, (j << 8) | k, ptr->caid[k], reshare); + if(!card) + { + cs_readunlock(__func__, &readerlist_lock); + return; + } + card->card_type = CT_CARD_BY_SERVICE_READER; + card->sidtab = ptr; + for(l = 0; l < ptr->num_provid; l++) + { + struct cc_provider *prov; + if(!cs_malloc(&prov, sizeof(struct cc_provider))) + { + cs_readunlock(__func__, &readerlist_lock); + return; + } + prov->prov = ptr->provid[l]; + ll_append(card->providers, prov); + } + + if(chk_ident(&rdr->ftab, card) && chk_ctab(card->caid, &rdr->ctab)) + { + if(!rdr->audisabled) + { cc_UA_oscam2cccam(rdr->hexserial, card->hexserial, card->caid); } + + add_card_to_serverlist(get_cardlist(card->caid, server_cards), card, 1); + flt = 1; + } + else + { cc_free_card(card); } + } + } + } + } + + //Filts by Hardware readers: + if((rdr->typ != R_CCCAM) && rdr->ftab.filts && !flt) + { + for(j = 0; j < rdr->ftab.nfilts; j++) + { + uint16_t caid = rdr->ftab.filts[j].caid; + if(caid) + { + card = create_card2(rdr, j, caid, reshare); + if(!card) + { + cs_readunlock(__func__, &readerlist_lock); + return; + } + card->card_type = CT_LOCALCARD; + + //Setting UA: (Unique Address): + if(!rdr->audisabled) + { cc_UA_oscam2cccam(rdr->hexserial, card->hexserial, caid); } + //cs_log("Ident CCcam card report caid: %04X readr %s subid: %06X", rdr->ftab.filts[j].caid, rdr->label, rdr->cc_id); + for(k = 0; k < rdr->ftab.filts[j].nprids; k++) + { + struct cc_provider *prov; + if(!cs_malloc(&prov, sizeof(struct cc_provider))) + { + cs_readunlock(__func__, &readerlist_lock); + return; + } + prov->prov = rdr->ftab.filts[j].prids[k]; + + //cs_log("Ident CCcam card report provider: %02X%02X%02X", buf[21 + (k*7)]<<16, buf[22 + (k*7)], buf[23 + (k*7)]); + if(!rdr->audisabled) + { + for(l = 0; l < rdr->nprov; l++) + { + uint32_t rprid = get_reader_prid(rdr, l); + if(rprid == prov->prov) + { cc_SA_oscam2cccam(&rdr->sa[l][0], prov->sa); } + } + } + + ll_append(card->providers, prov); + } + + add_good_bad_sids_by_rdr(rdr, card); + add_card_to_serverlist(get_cardlist(caid, server_cards), card, 1); + flt = 1; + } + } + } + + if((rdr->typ != R_CCCAM) && !rdr->caid && !flt) + { + for(j = 0; j < rdr->ctab.ctnum; j++) + { + CAIDTAB_DATA *d = &rdr->ctab.ctdata[j]; + //cs_log("CAID map CCcam card report caid: %04X cmap: %04X", d->caid, d->cmap); + uint16_t lcaid = d->caid; + + if(!lcaid || (lcaid == 0xFFFF)) + { lcaid = d->cmap; } + + if(lcaid && (lcaid != 0xFFFF)) + { + card = create_card2(rdr, j, lcaid, reshare); + if(!card) + { + cs_readunlock(__func__, &readerlist_lock); + return; + } + card->card_type = CT_CARD_BY_CAID1; + if(!rdr->audisabled) + { cc_UA_oscam2cccam(rdr->hexserial, card->hexserial, lcaid); } + + add_good_bad_sids_by_rdr(rdr, card); + add_card_to_serverlist(get_cardlist(lcaid, server_cards), card, 1); + flt = 1; + } + } + } + + if((rdr->typ != R_CCCAM) && rdr->ctab.ctnum && !flt) + { + //cs_log("tcp_connected: %d card_status: %d ", rdr->tcp_connected, rdr->card_status); + int32_t c; + if(rdr->tcp_connected || rdr->card_status == CARD_INSERTED) + { + for(c = 0; c < rdr->ctab.ctnum; c++) + { + CAIDTAB_DATA *d = &rdr->ctab.ctdata[c]; + uint16_t caid = d->caid; + if(!caid) { break; } + + card = create_card2(rdr, c, caid, reshare); + if(!card) + { break; } + card->card_type = CT_CARD_BY_CAID2; + + if(!rdr->audisabled) + { cc_UA_oscam2cccam(rdr->hexserial, card->hexserial, caid); } + for(j = 0; j < rdr->nprov; j++) + { + uint32_t prid = get_reader_prid(rdr, j); + struct cc_provider *prov; + if(!cs_malloc(&prov, sizeof(struct cc_provider))) + { + cs_readunlock(__func__, &readerlist_lock); + return; + } + prov->prov = prid; + //cs_log("Ident CCcam card report provider: %02X%02X%02X", buf[21 + (k*7)]<<16, buf[22 + (k*7)], buf[23 + (k*7)]); + if(!rdr->audisabled) + { + //Setting SA (Shared Addresses): + cc_SA_oscam2cccam(rdr->sa[j], prov->sa); + } + ll_append(card->providers, prov); + //cs_log("Main CCcam card report provider: %02X%02X%02X%02X", buf[21+(j*7)], buf[22+(j*7)], buf[23+(j*7)], buf[24+(j*7)]); + } + add_good_bad_sids_by_rdr(rdr, card); + add_card_to_serverlist(get_cardlist(caid, server_cards), card, 1); + flt = 1; + } + } + } + + + if((rdr->typ != R_CCCAM) && rdr->caid && !flt) + { + //cs_log("tcp_connected: %d card_status: %d ", rdr->tcp_connected, rdr->card_status); + if(rdr->tcp_connected || rdr->card_status == CARD_INSERTED) + { + uint16_t caid = rdr->caid; + card = create_card2(rdr, 1, caid, reshare); + if(!card) + { + cs_readunlock(__func__, &readerlist_lock); + return; + } + card->card_type = CT_CARD_BY_CAID3; + + if(!rdr->audisabled) + { cc_UA_oscam2cccam(rdr->hexserial, card->hexserial, caid); } + for(j = 0; j < rdr->nprov; j++) + { + uint32_t prid = get_reader_prid(rdr, j); + struct cc_provider *prov; + if(!cs_malloc(&prov, sizeof(struct cc_provider))) + { + cs_readunlock(__func__, &readerlist_lock); + return; + } + prov->prov = prid; + //cs_log("Ident CCcam card report provider: %02X%02X%02X", buf[21 + (k*7)]<<16, buf[22 + (k*7)], buf[23 + (k*7)]); + if(!rdr->audisabled) + { + //Setting SA (Shared Addresses): + cc_SA_oscam2cccam(rdr->sa[j], prov->sa); + } + ll_append(card->providers, prov); + //cs_log("Main CCcam card report provider: %02X%02X%02X%02X", buf[21+(j*7)], buf[22+(j*7)], buf[23+(j*7)], buf[24+(j*7)]); + } + add_good_bad_sids_by_rdr(rdr, card); + add_card_to_serverlist(get_cardlist(caid, server_cards), card, 1); + } + } + + if(rdr->typ == R_CCCAM && rdr->tcp_connected && + (cfg.cc_reshare_services < 2 || cfg.cc_reshare_services == 4) && rdr->card_status != CARD_FAILURE) + { + + cs_log_dbg(D_TRACE, "asking reader %s for cards...", rdr->label); + + struct s_client *rc = rdr->client; + struct cc_data *rcc = rc ? rc->cc : NULL; + + int32_t count = 0; + if(rcc && rcc->cards && !rc->kill) + { + cs_readlock(__func__, &rcc->cards_busy); + + it = ll_iter_create(rcc->cards); + while((card = ll_iter_next(&it))) + { + if(chk_ctab(card->caid, &rdr->ctab)) + { + int32_t dont_ignore = ll_count(card->providers) ? 0 : 1; + + it2 = ll_iter_create(card->providers); + struct cc_provider *prov; + while((prov = ll_iter_next(&it2))) + { + uint32_t prid = prov->prov; + if(chk_srvid_by_caid_prov(rc, card->caid, prid)) + { + dont_ignore = 1; + break; + } + } + + if(dont_ignore) //Filtered by service + { + add_card_to_serverlist(get_cardlist(card->caid, server_cards), card, 0); + count++; + } + } + } + cs_readunlock(__func__, &rcc->cards_busy); + } + else + { cs_log_dbg(D_TRACE, "reader %s not active!", rdr->label); } + cs_log_dbg(D_TRACE, "got %d cards from %s", count, rdr->label); + } + } + cs_readunlock(__func__, &readerlist_lock); + } + + LLIST *new_cards = ll_create("new_cards"); //List of new (added) cards + + cs_writelock(__func__, &cc_shares_lock); + + //report reshare cards: + //cs_log_dbg(D_TRACE, "%s reporting %d cards", getprefix(), ll_count(server_cards)); + for(i = 0; i < CAID_KEY; i++) + { + if(server_cards[i]) + { + it = ll_iter_create(server_cards[i]); + + //we compare every card of our new list (server_cards) with the last list. + while((card = ll_iter_next(&it))) + { + //cs_log_dbg(D_TRACE, "%s card %d caid %04X hop %d", getprefix(), card->id, card->caid, card->hop); + + if(!new_reported_carddatas[i]) + { new_reported_carddatas[i] = ll_create("new_cardlist"); } + report_card(card, new_reported_carddatas[i], new_cards); + ll_iter_remove(&it); + } + cc_free_cardlist(server_cards[i], 1); + } + + //remove unsed, remaining cards: + card_removed_count += cc_free_reported_carddata(reported_carddatas_list[i], new_reported_carddatas[i], 1); + reported_carddatas_list[i] = new_reported_carddatas[i]; + card_count += ll_count(reported_carddatas_list[i]); + //cs_log_dbg(D_TRACE, "CARDS FOR INDEX %d=%d", i, ll_count(reported_carddatas[i])); + } + + //now send new cards. Always remove first, then add new: + it = ll_iter_create(new_cards); + while((card = ll_iter_next(&it))) + { + send_card_to_all_clients(card); + } + ll_destroy(&new_cards); + + + cs_writeunlock(__func__, &cc_shares_lock); + + cs_log_dbg(D_TRACE, "reported/updated +%d/-%d/dup %d of %d cards to sharelist", + card_added_count, card_removed_count, card_dup_count, card_count); +} + +int32_t cc_srv_report_cards(struct s_client *cl) +{ + + struct cc_card *card; + int32_t i, count = 0; + LL_ITER it; + cs_readlock(__func__, &cc_shares_lock); + for(i = 0; i < CAID_KEY; i++) + { + if(reported_carddatas_list[i]) + { + it = ll_iter_create(reported_carddatas_list[i]); + while(cl->cc && !cl->kill && (card = ll_iter_next(&it))) + { + count += send_card_to_client(card, cl); + } + } + } + cs_readunlock(__func__, &cc_shares_lock); + cs_log_dbg(D_TRACE, "reported %d cards for %s", count, username(cl)); + + return cl->cc && !cl->kill; +} + +void refresh_shares(void) +{ + update_card_list(); +} + +#define DEFAULT_INTERVAL 30 + +void share_updater(void) +{ + int32_t i = DEFAULT_INTERVAL + cfg.waitforcards_extra_delay / 1000; + uint32_t last_check = 0; + uint32_t last_check_rdroptions = 0; + uint32_t cur_check_rdroptions = 0; + uint32_t last_card_check = 0; + uint32_t last_sidtab_generation = 0; + uint32_t card_count = 0; + while(share_updater_thread_active) + { + const uint32_t sleep_step = 500; + uint32_t sleep_time; + uint32_t slept; + if(i > 0 && card_count < 100) //fast refresh only if we have less cards + { + cs_log_dbg(D_TRACE, "share-updater mode=initfast t=1s i=%d", i); + sleep_time = 1000; + i--; + } + else if(i > 0) + { + cs_log_dbg(D_TRACE, "share-updater mode=initslow t=6s i=%d", i); + sleep_time = 6000; //1s later than garbage collector because this list uses much space + i -= 6; + } + else + { + if(cfg.cc_update_interval <= 10) + { cfg.cc_update_interval = DEFAULT_UPDATEINTERVAL; } + cs_log_dbg(D_TRACE, "share-updater mode=interval t=%ds", cfg.cc_update_interval); + sleep_time = cfg.cc_update_interval * 1000; + } + for(slept = 0; slept < sleep_time; slept += sleep_step) + { + if(!share_updater_thread_active || share_updater_refresh) + { + share_updater_refresh = 0; + break; + } + cs_sleepms(sleep_step); + } + if(!share_updater_thread_active) + { break; } + + cs_log_dbg(D_TRACE, "share-updater check"); + + uint32_t cur_check = 0; + uint32_t cur_card_check = 0; + int8_t rdroptionchange = 0; + card_count = 0; + struct s_reader *rdr; + struct cc_data *cc; + + cs_readlock(__func__, &readerlist_lock); + + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + struct s_client *cl = rdr->client; + if(cl && (cc = cl->cc)) //check cccam-cardlist: + { + cur_card_check += cc->card_added_count; + cur_card_check += cc->card_removed_count; + card_count += ll_count(cc->cards); + } + cur_check = crc32(cur_check, (uint8_t *)&rdr->tcp_connected, sizeof(rdr->tcp_connected)); + cur_check = crc32(cur_check, (uint8_t *)&rdr->card_status, sizeof(rdr->card_status)); + + //Check hexserial/UA changes only on lokal readers: + if(!is_network_reader(rdr)) + { + cur_check = crc32(cur_check, (uint8_t *)&rdr->hexserial, 8); //check hexserial + cur_check = crc32(cur_check, (uint8_t *)&rdr->prid, rdr->nprov * sizeof(rdr->prid[0])); //check providers + cur_check = crc32(cur_check, (uint8_t *)&rdr->sa, rdr->nprov * sizeof(rdr->sa[0])); //check provider-SA + } + + if(rdr->changes_since_shareupdate) + { + rdr->changes_since_shareupdate = 0; + rdroptionchange = 1; + } + } + if(rdroptionchange) + { + cur_check_rdroptions = 0; + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + cur_check_rdroptions = crc32(cur_check_rdroptions, (uint8_t *)&rdr->ftab, sizeof(FTAB)); //check reader + cur_check_rdroptions = crc32(cur_check_rdroptions, (uint8_t *)&rdr->ctab, sizeof(CAIDTAB)); //check caidtab + cur_check_rdroptions = crc32(cur_check_rdroptions, (uint8_t *)&rdr->fchid, sizeof(FTAB)); //check chids + cur_check_rdroptions = crc32(cur_check_rdroptions, (uint8_t *)&rdr->sidtabs.ok, sizeof(rdr->sidtabs.ok)); //check assigned ok services + cur_check_rdroptions = crc32(cur_check_rdroptions, (uint8_t *)&rdr->sidtabs.no, sizeof(rdr->sidtabs.no)); //check assigned no services + } + } + + cs_readunlock(__func__, &readerlist_lock); + + //update cardlist if reader config has changed, also set interval to 1s / 30times + if(cur_check != last_check || last_sidtab_generation != cfg_sidtab_generation || last_check_rdroptions != cur_check_rdroptions) + { + last_sidtab_generation = cfg_sidtab_generation; + i = DEFAULT_INTERVAL; + cs_log_dbg(D_TRACE, "share-update [1] %u %u", cur_check, last_check); + refresh_shares(); + last_check = cur_check; + last_card_check = cur_card_check; + } + //update cardlist if cccam cards has changed: + else if(cur_card_check != last_card_check) + { + cs_log_dbg(D_TRACE, "share-update [2] %u %u", cur_card_check, last_card_check); + refresh_shares(); + last_card_check = cur_card_check; + } + last_check_rdroptions = cur_check_rdroptions; + } + for(i = 0; i < CAID_KEY; i++) + { cc_free_reported_carddata(reported_carddatas_list[i], NULL, 0); } +} + +void cccam_init_share(void) +{ + memset(reported_carddatas_list, 0, sizeof(reported_carddatas_list)); + cs_lock_create(__func__, &cc_shares_lock, "cc_shares_lock", 200000); + + share_updater_thread = 0; + share_updater_thread_active = 1; + share_updater_refresh = 0; + + pthread_t temp; + int32_t ret = start_thread("share updater", (void *)&share_updater, NULL, &temp, 1, 1); + if(!ret) + { + share_updater_thread = temp; + } +} + +void cccam_done_share(void) +{ + if(share_updater_thread) + { + share_updater_thread_active = 0; + share_updater_thread = 0; + } +} + +void cccam_refresh_share(void) +{ + share_updater_refresh = 1; +} +#endif diff --git a/module-cccshare.h b/module-cccshare.h new file mode 100644 index 0000000..9752361 --- /dev/null +++ b/module-cccshare.h @@ -0,0 +1,44 @@ +/* +* module-cccshare.h +* +* Created on: 26.02.2011 +* Author: schlocke +*/ +#ifndef MODULE_CCCSHARE_H_ +#define MODULE_CCCSHARE_H_ + +// In this file put functions that are shared between module-cccam.c and module-cccshare.c + +int32_t chk_ident(FTAB *ftab, struct cc_card *card); +int32_t cc_srv_report_cards(struct s_client *cl); +LLIST *get_cardlist(uint16_t caid, LLIST **list); + +void cc_free_card(struct cc_card *card); +void cc_free_cardlist(LLIST *card_list, int32_t destroy_list); +int32_t cc_cmd_send(struct s_client *cl, uint8_t *buf, int32_t len, cc_msg_type_t cmd); +int32_t sid_eq(struct cc_srvid *srvid1, struct cc_srvid *srvid2); +int32_t sid_eq_nb(struct cc_srvid *srvid1, struct cc_srvid_block *srvid2); +int32_t sid_eq_bb(struct cc_srvid_block *srvid1, struct cc_srvid_block *srvid2); +int32_t same_card(struct cc_card *card1, struct cc_card *card2); +int32_t same_card2(struct cc_card *card1, struct cc_card *card2, int8_t compare_grp); +void cc_UA_oscam2cccam(uint8_t *in, uint8_t *out, uint16_t caid); +void cc_SA_oscam2cccam(uint8_t *in, uint8_t *out); +void set_card_timeout(struct cc_card *card); + +struct cc_srvid *is_good_sid(struct cc_card *card, struct cc_srvid *srvid_good); +struct cc_srvid_block *is_sid_blocked(struct cc_card *card, struct cc_srvid *srvid_blocked); + +void add_good_sid(struct cc_card *card, struct cc_srvid *srvid_good); +void remove_good_sid(struct cc_card *card, struct cc_srvid *srvid_good); +void add_sid_block(struct cc_card *card, struct cc_srvid *srvid_blocked, bool temporary); +void remove_sid_block(struct cc_card *card, struct cc_srvid *srvid_blocked); + +void merge_sids(struct cc_card *carddst, struct cc_card *cardsrc); + +void cccam_refresh_share(void); + +int32_t hide_card_to_client(struct cc_card *card, struct s_client *cl); +int32_t unhide_card_to_client(struct cc_card *card, struct s_client *cl); +int32_t hidecards_card_valid_for_client(struct s_client *cl, struct cc_card *card); + +#endif diff --git a/module-constcw.c b/module-constcw.c new file mode 100644 index 0000000..01370c7 --- /dev/null +++ b/module-constcw.c @@ -0,0 +1,170 @@ +#define MODULE_LOG_PREFIX "constcw" + +//FIXME Not checked on threadsafety yet; after checking please remove this line +#include "globals.h" +#ifdef MODULE_CONSTCW +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-net.h" +#include "oscam-string.h" + +static int32_t pserver; + +int32_t constcw_file_available(void) +{ + FILE *fp; + + fp = fopen(cur_client()->reader->device, "r"); + if(!fp) + { + cs_log("ERROR: Can't open %s (errno=%d %s)", cur_client()->reader->device, errno, strerror(errno)); + return (0); + } + fclose(fp); + return (1); +} + +int32_t constcw_analyse_file(uint16_t c_caid, uint32_t c_prid, uint16_t c_sid, uint16_t c_pmtpid, uint32_t c_vpid, uint16_t c_ecmpid, uint8_t *dcw) +{ + //CAID:PROVIDER:SID:PMTPID:ECMPID:VPID:XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX + FILE *fp; + char token[512]; + uint32_t caid, provid, sid, vpid, pmtpid, ecmpid; + int32_t cw[16]; + + fp = fopen(cur_client()->reader->device, "r"); + if(!fp) + { + cs_log("ERROR: Can't open %s (errno=%d %s)", cur_client()->reader->device, errno, strerror(errno)); + return (0); + } + + cs_log("Searching CW for CAID %04X PROVID %06X SRVID %04X ECMPID %04X PMTPID %04X VPID %04X", c_caid, c_prid, c_sid, c_ecmpid, c_pmtpid, c_vpid); + + while(fgets(token, sizeof(token), fp)) + { + if(token[0] == '#') { continue; } + vpid = 0; + int ret = sscanf(token, "%4x:%6x:%4x:%4x:%4x::%2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", &caid, &provid, &sid, &pmtpid, &ecmpid, + &cw[0], &cw[1], &cw[2], &cw[3], &cw[4], &cw[5], &cw[6], &cw[7], + &cw[8], &cw[9], &cw[10], &cw[11], &cw[12], &cw[13], &cw[14], &cw[15]); + + if(ret != 21){ + ret = sscanf(token, "%4x:%6x:%4x:%4x:%4x:%4x:%2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", &caid, &provid, &sid, &pmtpid, &ecmpid, &vpid, + &cw[0], &cw[1], &cw[2], &cw[3], &cw[4], &cw[5], &cw[6], &cw[7], + &cw[8], &cw[9], &cw[10], &cw[11], &cw[12], &cw[13], &cw[14], &cw[15]); + if(ret != 22) continue; + } + + //cs_log("Line found: %s", token); + if(c_caid == caid && c_sid == sid && (!provid || provid == c_prid) && (!pmtpid || !c_pmtpid || pmtpid == c_pmtpid) && (!vpid || !c_vpid || vpid == c_vpid) + && (!ecmpid || !c_ecmpid || ecmpid == c_ecmpid)) + { + fclose(fp); + int8_t i; + for(i = 0; i < 16; ++i) + { dcw[i] = (uint8_t) cw[i]; } + cs_log("Entry found: %04X@%06X:%04X:%04X:%04X:%04X:%s", caid, provid, sid, pmtpid, ecmpid, vpid, cs_hexdump(1, dcw, 16, token, sizeof(token))); + return 1; + } + } + + fclose(fp); + return 0; +} +//************************************************************************************************************************ +//* client/server common functions +//************************************************************************************************************************ +static int32_t constcw_recv(struct s_client *client, uint8_t *buf, int32_t l) +{ + int32_t ret; + + if(!client->udp_fd) { return (-9); } + ret = read(client->udp_fd, buf, l); + if(ret < 1) { return (-1); } + client->last = time(NULL); + return (ret); +} + +//************************************************************************************************************************ +//* client functions +//************************************************************************************************************************ +int32_t constcw_client_init(struct s_client *client) +{ + int32_t fdp[2]; + + client->pfd = 0; + if(socketpair(PF_LOCAL, SOCK_STREAM, 0, fdp)) + { + cs_log("ERROR: Socket creation failed: %s", strerror(errno)); + return 1; + } + client->udp_fd = fdp[0]; + pserver = fdp[1]; + + memset((char *) &client->udp_sa, 0, sizeof(client->udp_sa)); + SIN_GET_FAMILY(client->udp_sa) = AF_INET; + + // Oscam has no reader.au in s_reader like ki's mpcs ;) + // reader[ridx].au = 0; + // cs_log("local reader: %s (file: %s) constant cw au=0", reader[ridx].label, reader[ridx].device); + cs_log("Local reader: %s (file: %s)", client->reader->label, client->reader->device); + + client->pfd = client->udp_fd; + + if(constcw_file_available()) + { + client->reader->tcp_connected = 2; + client->reader->card_status = CARD_INSERTED; + } + + return (0); +} + +static int32_t constcw_send_ecm(struct s_client *client, ECM_REQUEST *er) +{ + time_t t; + struct s_reader *rdr = client->reader; + uint8_t cw[16]; + + t = time(NULL); + // Check if DCW exist in the files + //cs_log("Searching ConstCW for ECM: %04X@%06X:%04X (%d)", er->caid, er->prid, er->srvid, er->l); + + if(constcw_analyse_file(er->caid, er->prid, er->srvid, er->pmtpid, er->vpid, er->pid, cw) == 0) + { + write_ecm_answer(rdr, er, E_NOTFOUND, (E1_READER << 4 | E2_SID), NULL, NULL, 0, NULL); + } + else + { + write_ecm_answer(rdr, er, E_FOUND, 0, cw, NULL, 0, NULL); + } + + client->last = t; + rdr->last_g = t; + return (0); +} + +static int32_t constcw_recv_chk(struct s_client *UNUSED(client), uint8_t *UNUSED(dcw), int32_t *rc, uint8_t *UNUSED(buf), int32_t UNUSED(n)) +{ + //dcw = dcw; + //n = n; + //buf = buf; + + *rc = 0; + return (-1); +} + +void module_constcw(struct s_module *ph) +{ + ph->desc = "constcw"; + ph->type = MOD_NO_CONN; + ph->listenertype = LIS_CONSTCW; + ph->recv = constcw_recv; + + ph->c_init = constcw_client_init; + ph->c_recv_chk = constcw_recv_chk; + ph->c_send_ecm = constcw_send_ecm; + ph->num = R_CONSTCW; +} +#endif diff --git a/module-csp.c b/module-csp.c new file mode 100644 index 0000000..468b835 --- /dev/null +++ b/module-csp.c @@ -0,0 +1,278 @@ +#define MODULE_LOG_PREFIX "csp" + +/* + * module-csp.c + * + * Created on: 20.12.2011 + * Author: Corsair + */ + +#include "globals.h" + +#ifdef CS_CACHEEX + +#include "module-cacheex.h" +#include "oscam-cache.h" +#include "oscam-ecm.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-time.h" + +#define TYPE_REQUEST 1 +#define TYPE_REPLY 2 +#define TYPE_PINGREQ 3 +#define TYPE_PINGRPL 4 +#define TYPE_RESENDREQ 5 + +#define FAKE_ONID 0xFFFF +#define FAKE_TAG 0x80 + +#define PING_INTVL 4 + +static void *csp_server(struct s_client *client __attribute__((unused)), uint8_t *mbuf __attribute__((unused)), int32_t n __attribute__((unused))) +{ + return NULL; +} + +static int32_t csp_send_ping(struct s_client *cl, uint32_t now) +{ + uint8_t buf[13] = {0}; + + buf[0] = TYPE_PINGREQ; + i2b_buf(4, now, buf + 1); + i2b_buf(4, cfg.csp_port, buf + 9); + + int32_t status = sendto(cl->udp_fd, buf, sizeof(buf), 0, (struct sockaddr *) &cl->udp_sa, cl->udp_sa_len); + + cl->lastecm = time((time_t *) 0); // use this to indicate last ping sent for now + return status; +} + +static int32_t csp_cache_push_out(struct s_client *cl, struct ecm_request_t *er) +{ + int8_t rc = (er->rc < E_NOTFOUND) ? E_FOUND : er->rc; + uint8_t size = 0, type; + + switch(rc) + { + case E_FOUND: // we have the cw + size = 29; + type = TYPE_REPLY; + break; + case E_UNHANDLED: // request pending - not yet used? + size = 12; + type = TYPE_REQUEST; + break; + default: + return -1; + + } + + uint8_t *buf; + if(!cs_malloc(&buf, size)) { return -1; } + + uint16_t onid = er->onid; + if(onid == 0) { onid = FAKE_ONID; } + uint8_t tag = er->ecm[0]; + if(tag != 0x80 && tag != 0x81) { tag = FAKE_TAG; } + + buf[0] = type; + buf[1] = tag; + i2b_buf(2, er->srvid, buf + 2); + i2b_buf(2, onid, buf + 4); + i2b_buf(2, er->caid, buf + 6); + i2b_buf(4, er->csp_hash, buf + 8); + + if(rc == E_FOUND) + { + buf[12] = tag; + memcpy(buf + 13, er->cw, sizeof(er->cw)); + } + + struct timeb tpe; + cs_ftime(&tpe); + + if(tpe.time - cl->lastecm > PING_INTVL) { csp_send_ping(cl, 1000 * tpe.time + tpe.millitm); } + + cs_log_dump_dbg(D_TRACE, buf, size, "pushing cache update to csp onid=%04X caid=%04X srvid=%04X hash=%08X (tag: %02X)", onid, er->caid, er->srvid, er->csp_hash, tag); + + /* + struct SOCKADDR peer_sa = {0}; + SIN_GET_FAMILY(peer_sa) = SIN_GET_FAMILY(cl->udp_sa); + cs_inet_addr("127.0.0.1", &SIN_GET_ADDR(peer_sa)); + SIN_GET_PORT(peer_sa) = htons(12346); + int32_t status = sendto(cl->udp_fd, buf, size, 0, (struct sockaddr *)&peer_sa, sizeof(peer_sa)); + */ + + int32_t status = sendto(cl->udp_fd, buf, size, 0, (struct sockaddr *) &cl->udp_sa, cl->udp_sa_len); + NULLFREE(buf); + return status; +} + +static uint8_t parse_request(struct ecm_request_t *er, uint8_t *buf) +{ + uint8_t commandTag = buf[0]; // first ecm byte indicating odd or even (0x80 or 0x81) + uint16_t srvid = b2i(2, buf + 1); + uint16_t onid = b2i(2, buf + 3); + uint16_t caid = b2i(2, buf + 5); + uint32_t hash = b2i(4, buf + 7); + + er->caid = caid; + er->onid = onid; + er->srvid = srvid; + er->csp_hash = hash; + er->ecm[0] = commandTag; + er->from_csp = 1; + + return commandTag; +} + +static int32_t csp_recv(struct s_client *client, uint8_t *buf, int32_t l) +{ + int32_t rs = 0; + if(!client->udp_fd) { return (-9); } + if(client->is_udp && client->typ == 'c') + { + rs = recv_from_udpipe(buf); // whats this? + } + else + { + rs = cs_recv(client->udp_fd, buf, client->is_udp ? l : 36, 0); + } + //cs_log_dump_dbg(D_TRACE, buf, rs, "received %d bytes from csp", rs); + + uint8_t type = buf[0]; // TYPE + + switch(type) + { + + case TYPE_REPLY: // request hash + reply received: + if(rs >= 29) + { + ECM_REQUEST *er = get_ecmtask(); + if(!er) { return -1; } + + uint8_t commandTag = parse_request(er, buf + 1); + uint8_t rplTag = buf[12]; + + er->rc = E_FOUND; + + if(chk_csp_ctab(er, &cfg.csp.filter_caidtab)) + { + memcpy(er->cw, buf + 13, sizeof(er->cw)); + uint8_t orgname[32] = {0}; + if(rs >= 31) + { + // origin connector name included + uint16_t namelen = (buf[29] << 8) | buf[30]; + if(namelen > sizeof(orgname)) { namelen = sizeof(orgname); } + memcpy(orgname, buf + 31, namelen); + } + cs_log_dump_dbg(D_TRACE, er->cw, sizeof(er->cw), "received cw from csp onid=%04X caid=%04X srvid=%04X hash=%08X (org connector: %s, tags: %02X/%02X)", er->onid, er->caid, er->srvid, er->csp_hash, orgname, commandTag, rplTag); + cacheex_add_to_cache_from_csp(client, er); + } + else { NULLFREE(er); } + } + break; + + case TYPE_REQUEST: // pending request notification hash received + if(rs == 12) // ignore requests for arbitration (csp "pre-requests", size 20) + { + ECM_REQUEST *er = get_ecmtask(); + if(!er) { return -1; } + + uint8_t commandTag = parse_request(er, buf + 1); + + er->rc = E_UNHANDLED; + + if(chk_csp_ctab(er, &cfg.csp.filter_caidtab) && cfg.csp.allow_request) + { + cs_log_dump_dbg(D_TRACE, buf, l, "received ecm request from csp onid=%04X caid=%04X srvid=%04X hash=%08X (tag: %02X)", er->onid, er->caid, er->srvid, er->csp_hash, commandTag); + cacheex_add_to_cache_from_csp(client, er); + } + else { NULLFREE(er); } + } + break; + + case TYPE_PINGREQ: + if(rs >= 13) + { + client->last = time((time_t *) 0); + uint32_t port = b2i(4, buf + 9); + SIN_GET_PORT(client->udp_sa) = htons(port); + + uint8_t pingrpl[9]; + pingrpl[0] = TYPE_PINGRPL; + memcpy(pingrpl + 1, buf + 1, 8); + int32_t status = sendto(client->udp_fd, pingrpl, sizeof(pingrpl), 0, (struct sockaddr *) &client->udp_sa, client->udp_sa_len); + cs_log_dbg(D_TRACE, "received ping from cache peer: %s:%d (replied: %d)", cs_inet_ntoa(SIN_GET_ADDR(client->udp_sa)), port, status); + } + break; + + case TYPE_PINGRPL: + if(rs >= 9) + { + struct timeb tpe; + cs_ftime(&tpe); + uint32_t ping = b2i(4, buf + 1); + uint32_t now = tpe.time * 1000 + tpe.millitm; + cs_log_dbg(D_TRACE, "received ping reply from cache peer: %s:%d (%d ms)", cs_inet_ntoa(SIN_GET_ADDR(client->udp_sa)), ntohs(SIN_GET_PORT(client->udp_sa)), now - ping); + client->cwcacheexping = now - ping; + } + break; + + case TYPE_RESENDREQ: // sent as a result of delay alert in a remote cache + if(rs >= 16) + { + uint32_t port = b2i(4, buf + 1); + ECM_REQUEST *er = get_ecmtask(); + if(!er) { return -1; } + + parse_request(er, buf + 5); + + ECM_REQUEST *result = check_cache(er, client); + + if(result) + { + + er->rc = E_FOUND; + er->rcEx = 0; + memcpy(er->cw, result->cw, 16); + er->grp |= result->grp; + NULLFREE(result); + + int32_t status = csp_cache_push_out(client, er); + cs_log_dbg(D_TRACE, "received resend request from cache peer: %s:%d (replied: %d)", cs_inet_ntoa(SIN_GET_ADDR(client->udp_sa)), port, status); + } + else + { + cs_log_dbg(D_TRACE, "received resend request from cache peer: %s:%d (not found)", cs_inet_ntoa(SIN_GET_ADDR(client->udp_sa)), port); + } + NULLFREE(er); + } + break; + + default: + cs_log_dbg(D_TRACE, "unknown csp cache message received: %d", type); + } + + return rs; +} + +void module_csp(struct s_module *ph) +{ + ph->ptab.nports = 1; + ph->ptab.ports[0].s_port = cfg.csp_port; + + ph->desc = "csp"; + ph->type = MOD_CONN_UDP; + ph->large_ecm_support = 1; + ph->listenertype = LIS_CSPUDP; + IP_ASSIGN(ph->s_ip, cfg.csp_srvip); + ph->s_handler = csp_server; + ph->recv = csp_recv; + ph->c_cache_push = csp_cache_push_out; + ph->num = R_CSP; +} + +#endif diff --git a/module-cw-cycle-check.c b/module-cw-cycle-check.c new file mode 100644 index 0000000..9afb237 --- /dev/null +++ b/module-cw-cycle-check.c @@ -0,0 +1,879 @@ +#define MODULE_LOG_PREFIX "cwccheck" + +#include "globals.h" +#ifdef CW_CYCLE_CHECK + +#include "module-cw-cycle-check.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-lock.h" +#include "oscam-string.h" +#include "oscam-cache.h" + +struct s_cwc_md5 +{ + uint8_t md5[CS_ECMSTORESIZE]; + uint32_t csp_hash; + uint8_t cw[16]; +}; + +struct s_cw_cycle_check +{ + uint8_t cw[16]; + time_t time; + time_t locktime; // lock in learning + uint16_t caid; + uint16_t sid; + uint16_t chid; + uint32_t provid; + int16_t ecmlen; + int8_t stage; + int32_t cycletime; + int32_t dyncycletime; + int8_t nextcyclecw; + struct s_cwc_md5 ecm_md5[15]; // max 15 old ecm md5 /csp-hashs + int8_t cwc_hist_entry; + uint8_t old; + int8_t stage4_repeat; + struct s_cw_cycle_check *prev; + struct s_cw_cycle_check *next; +}; + +extern CS_MUTEX_LOCK cwcycle_lock; + +static struct s_cw_cycle_check *cw_cc_list; +static int32_t cw_cc_list_size; +static time_t last_cwcyclecleaning; + +/* + * Check for CW CYCLE + */ + +static uint8_t chk_is_pos_fallback(ECM_REQUEST *er, char *reader) +{ + struct s_ecm_answer *ea; + struct s_reader *fbrdr; + char fb_reader[64]; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if(ea->reader) + { + fbrdr = ea->reader; + snprintf(fb_reader, sizeof(fb_reader), "%s", ea->reader->label); + if(!strcmp(reader, fb_reader) && chk_is_fixed_fallback(fbrdr, er)) + { + cs_log("cyclecheck [check Fixed FB] %s is set as fixed fallback", reader); + return 1; + } + } + } + return 0; +} + +static inline uint8_t checkECMD5CW(uint8_t *ecmd5_cw) +{ + int8_t i; + for(i = 0; i < CS_ECMSTORESIZE; i++) + if(ecmd5_cw[i]) { return 1; } + return 0; +} + +/* + * countCWpart is to prevent like this + * D41A1A08B01DAD7A 0F1D0A36AF9777BD found -> ok + * E9151917B01DAD7A 0F1D0A36AF9777BD found last -> worng (freeze), but for cwc is ok + * 7730F59C6653A55E D3822A7F133D3C8C cwc bad -> but cw is right, cwc out of step + */ +static uint8_t countCWpart(ECM_REQUEST *er, struct s_cw_cycle_check *cwc) +{ + uint8_t eo = cwc->nextcyclecw ? 0 : 8; + int8_t i, ret = 0; + +#ifdef WITH_DEBUG + if(cs_dblevel & D_CWC) + { + char cwc_cw[9 * 3]; + char er_cw[9 * 3]; + cs_hexdump(0, cwc->cw + eo, 8, cwc_cw, sizeof(cwc_cw)); + cs_hexdump(0, er->cw + eo, 8, er_cw, sizeof(er_cw)); + cs_log_dbg(D_CWC, "cyclecheck [countCWpart] er-cw %s", er_cw); + cs_log_dbg(D_CWC, "cyclecheck [countCWpart] cw-cw %s", cwc_cw); + } +#endif + + for(i = 0; i < 8; i++) + { + if(cwc->cw[i + eo] == er->cw[i + eo]) + { + ret++; + } + } + + + if(ret > cfg.cwcycle_sensitive) + { + cs_log("cyclecheck [countCWpart] new cw is to like old one (unused part), sensitive %d, same bytes %d", cfg.cwcycle_sensitive, ret); + } + return ret; +} + +static uint8_t checkvalidCW(ECM_REQUEST *er) +{ + uint8_t ret = 1; + + // Skip check for BISS1 - cw could be indeed zero + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + if(chk_is_null_CW(er->cw) && !caid_is_biss(er->caid)) + { er->rc = E_NOTFOUND; } + + if(er->rc == E_NOTFOUND) + { return 0; } // wrong leave the check + + if(checkCWpart(er->cw, 0) && checkCWpart(er->cw, 1)) + { return 1; } // cw1 and cw2 is filled -> we can check for cwc + + if((!checkCWpart(er->cw, 0) || !checkCWpart(er->cw, 1)) && caid_is_videoguard(er->caid)) + { + cs_log("CAID: %04X uses obviously half cycle cw's : NO need to check it with CWC! Remove CAID: %04X from CWC Config!", er->caid, er->caid); + ret = 0; // cw1 or cw2 is null + } + + return ret; +} + +void cleanupcwcycle(void) +{ + time_t now = time(NULL); + if(last_cwcyclecleaning + 120 > now) // only clean once every 2min + { return; } + + last_cwcyclecleaning = now; + int32_t count = 0, kct = cfg.keepcycletime * 60 + 30; // if keepcycletime is set, wait more before deleting + struct s_cw_cycle_check *prv = NULL, *currentnode = NULL, *temp = NULL; + + bool bcleanup = false; + + // write lock + cs_writelock(__func__, &cwcycle_lock); + for(currentnode = cw_cc_list, prv = NULL; currentnode; prv = currentnode, currentnode = currentnode->next, count++) // First Remove old Entries + { + if((now - currentnode->time) <= kct) // delete Entry which old to hold list small + { + continue; + } + cs_log_dbg(D_CWC, "cyclecheck [Cleanup] diff: %" PRId64 " kct: %i", (int64_t)(now - currentnode->time), kct); + if(prv != NULL) + { + prv->next = NULL; + } + else + { + cw_cc_list = NULL; + } + bcleanup = true; + break; // we need only once, all follow to old + } + cs_writeunlock(__func__, &cwcycle_lock); + while(currentnode != NULL) + { + temp = currentnode->next; + if(!currentnode->old) + { cw_cc_list_size--; } + NULLFREE(currentnode); + currentnode = temp; + } + if(bcleanup) + { cs_log_dbg(D_CWC, "cyclecheck [Cleanup] list new size: %d (realsize: %d)", cw_cc_list_size, count); } +} + +static int32_t checkcwcycle_int(ECM_REQUEST *er, char *er_ecmf , char *user, uint8_t *cw , char *reader, uint8_t cycletime_fr, uint8_t next_cw_cycle_fr) +{ + + int8_t i, ret = 6; // ret = 6 no checked + int8_t cycleok = -1; + time_t now = er->tps.time; //time(NULL); + uint8_t need_new_entry = 1, upd_entry = 1; + char cwstr[17 * 3]; // cw to check + + char cwc_ecmf[ECM_FMT_LEN]; + char cwc_cw[17 * 3]; +#ifdef WITH_DEBUG + char cwc_md5[17 * 3]; + char cwc_csp[5 * 3]; +#endif + int8_t n = 1, m = 1, k; + int32_t mcl = cfg.maxcyclelist; + struct s_cw_cycle_check *currentnode = NULL, *cwc = NULL; + + /*for(list = cw_cc_list; list; list = list->next) { // List all Entries in Log for DEBUG + cs_log_dbg(D_CWC, "cyclecheck: [LIST] %04X@%06X:%04X OLD: %i Time: %ld DifftoNow: %ld Stage: %i cw: %s", list->caid, list->provid, list->sid, list->old, list->time, now - list->time, list->stage, cs_hexdump(0, list->cw, 16, cwstr, sizeof(cwstr))); + + }*/ + + if(!checkvalidCW(er)) + { return 3; } //cwc ign + + //read lock + cs_readlock(__func__, &cwcycle_lock); + bool readlocked = true; + for(currentnode = cw_cc_list; currentnode; currentnode = currentnode->next) + { + if(currentnode->caid != er->caid || currentnode->provid != er->prid || currentnode->sid != er->srvid || currentnode->chid != er->chid) + { + continue; + } + if(er->ecmlen != 0 && currentnode->ecmlen != 0) + { + if(currentnode->ecmlen != er->ecmlen) + { + cs_log_dbg(D_CWC, "cyclecheck [other ECM LEN] -> don't check"); + continue; + } + + } + need_new_entry = 0; // we got a entry for caid/prov/sid so we dont need new one + +#ifdef WITH_DEBUG + if(cs_dblevel & D_CWC) + { + cs_hexdump(0, cw, 16, cwstr, sizeof(cwstr)); //checked cw for log + } +#endif + if(cs_malloc(&cwc, sizeof(struct s_cw_cycle_check))) + { + memcpy(cwc, currentnode, sizeof(struct s_cw_cycle_check)); //copy current to new + + if(!currentnode->old) + { + currentnode->old = 1; //need later to counting + cw_cc_list_size--; + } + //now we have all data and can leave read lock + cs_readunlock(__func__, &cwcycle_lock); + readlocked = false; +#ifdef WITH_DEBUG + if(cs_dblevel & D_CWC) + { + cs_hexdump(0, cwc->ecm_md5[cwc->cwc_hist_entry].md5, 16, cwc_md5, sizeof(cwc_md5)); + cs_hexdump(0, (void *)&cwc->ecm_md5[cwc->cwc_hist_entry].csp_hash, 4, cwc_csp, sizeof(cwc_csp)); + cs_hexdump(0, cwc->cw, 16, cwc_cw, sizeof(cwc_cw)); + ecmfmt(cwc_ecmf, ECM_FMT_LEN, cwc->caid, 0, cwc->provid, cwc->chid, 0, cwc->sid, cwc->ecmlen, cwc_md5, cwc_csp, cwc_cw, 0, 0, NULL, NULL); + } +#endif + +// Cycletime over Cacheex + if (cfg.cwcycle_usecwcfromce) + { + if(cycletime_fr > 0 && next_cw_cycle_fr < 2) + { + cs_log_dbg(D_CWC, "cyclecheck [Use Info in Request] Client: %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", user, cycletime_fr, next_cw_cycle_fr, er->caid, er->prid, er->srvid); + cwc->stage = 3; + cwc->cycletime = cycletime_fr; + cwc->nextcyclecw = next_cw_cycle_fr; + ret = 8; + if(memcmp(cwc->cw, cw, 16) == 0) //check if the store cw the same like the current + { + cs_log_dbg(D_CWC, "cyclecheck [Dump Stored CW] Client: %s EA: %s CW: %s Time: %" PRId64, user, cwc_ecmf, cwc_cw, (int64_t)cwc->time); + cs_log_dbg(D_CWC, "cyclecheck [Dump CheckedCW] Client: %s EA: %s CW: %s Time: %" PRId64 " Timediff: %" PRId64, user, er_ecmf, cwstr, (int64_t)now, (int64_t)now - cwc->time); + if(now - cwc->time >= cwc->cycletime - cwc->dyncycletime) + { + cs_log_dbg(D_CWC, "cyclecheck [Same CW but much too late] Client: %s EA: %s CW: %s Time: %" PRId64 " Timediff: %" PRId64, user, er_ecmf, cwstr, (int64_t)now, (int64_t)now - cwc->time); + ret = cfg.cwcycle_dropold ? 2 : 4; + } + else + { + ret = 4; // Return 4 same CW + } + upd_entry = 0; + } + break; + } + } +// + if(cwc->stage == 3 && cwc->nextcyclecw < 2 && now - cwc->time < cwc->cycletime * 2 - cwc->dyncycletime - 1) // Check for Cycle no need to check Entries others like stage 3 + { + /*for (k=0; k<15; k++) { // debug md5 + cs_log_dbg(D_CWC, "cyclecheck [checksumlist[%i]]: ecm_md5: %s csp-hash: %d Entry: %i", k, cs_hexdump(0, cwc->ecm_md5[k].md5, 16, ecm_md5, sizeof(ecm_md5)), cwc->ecm_md5[k].csp_hash, cwc->cwc_hist_entry); + } */ + + // first we check if the store cw the same like the current + if(memcmp(cwc->cw, cw, 16) == 0) + { + cs_log_dbg(D_CWC, "cyclecheck [Dump Stored CW] Client: %s EA: %s CW: %s Time: %" PRId64, user, cwc_ecmf, cwc_cw, (int64_t)cwc->time); + cs_log_dbg(D_CWC, "cyclecheck [Dump CheckedCW] Client: %s EA: %s CW: %s Time: %" PRId64 " Timediff: %" PRId64, user, er_ecmf, cwstr, (int64_t)now, (int64_t)now - cwc->time); + if(now - cwc->time >= cwc->cycletime - cwc->dyncycletime) + { + cs_log_dbg(D_CWC, "cyclecheck [Same CW but much too late] Client: %s EA: %s CW: %s Time: %" PRId64 " Timediff: %" PRId64, user, er_ecmf, cwstr, (int64_t)now, (int64_t)now - cwc->time); + ret = cfg.cwcycle_dropold ? 2 : 4; + } + else + { + ret = 4; // Return 4 same CW + } + upd_entry = 0; + break; + } + + if(cwc->nextcyclecw == 0) //CW0 must Cycle + { + for(i = 0; i < 8; i++) + { + if(cwc->cw[i] == cw[i]) + { + cycleok = 0; //means CW0 Cycle OK + } + else + { + cycleok = -1; + break; + } + } + } + else if(cwc->nextcyclecw == 1) //CW1 must Cycle + { + for(i = 0; i < 8; i++) + { + if(cwc->cw[i + 8] == cw[i + 8]) + { + cycleok = 1; //means CW1 Cycle OK + } + else + { + cycleok = -1; + break; + } + } + } + + if(cycleok >= 0 && cfg.cwcycle_sensitive && countCWpart(er, cwc) >= cfg.cwcycle_sensitive) //2,3,4, 0 = off + { + cycleok = -2; + } + + if(cycleok >= 0) + { + ret = 0; // return Code 0 Cycle OK + if(cycleok == 0) + { + cwc->nextcyclecw = 1; + er->cwc_next_cw_cycle = 1; + if(cwc->cycletime < 128 && (!(cwc->caid == 0x0100 && cwc->provid == 0x00006A))) // make sure cycletime is lower dez 128 because share over cacheex buf[18] bit 8 is used for cwc_next_cw_cycle + { er->cwc_cycletime = cwc->cycletime; } + cs_log_dbg(D_CWC, "cyclecheck [Valid CW 0 Cycle] Client: %s EA: %s Timediff: %" PRId64 " Stage: %i Cycletime: %i dyncycletime: %i nextCycleCW = CW%i from Reader: %s", user, er_ecmf, (int64_t)now - cwc->time, cwc->stage, cwc->cycletime, cwc->dyncycletime, cwc->nextcyclecw, reader); + } + else if(cycleok == 1) + { + cwc->nextcyclecw = 0; + er->cwc_next_cw_cycle = 0; + if(cwc->cycletime < 128 && (!(cwc->caid == 0x0100 && cwc->provid == 0x00006A))) // make sure cycletime is lower dez 128 because share over cacheex buf[18] bit 8 is used for cwc_next_cw_cycle + { er->cwc_cycletime = cwc->cycletime; } + cs_log_dbg(D_CWC, "cyclecheck [Valid CW 1 Cycle] Client: %s EA: %s Timediff: %" PRId64 " Stage: %i Cycletime: %i dyncycletime: %i nextCycleCW = CW%i from Reader: %s", user, er_ecmf, (int64_t)now - cwc->time, cwc->stage, cwc->cycletime, cwc->dyncycletime, cwc->nextcyclecw, reader); + } + cs_log_dbg(D_CWC, "cyclecheck [Dump Stored CW] Client: %s EA: %s CW: %s Time: %" PRId64, user, cwc_ecmf, cwc_cw, (int64_t)cwc->time); + cs_log_dbg(D_CWC, "cyclecheck [Dump CheckedCW] Client: %s EA: %s CW: %s Time: %" PRId64 " Timediff: %" PRId64, user, er_ecmf, cwstr, (int64_t)now, (int64_t)now - cwc->time); + } + else + { + + for(k = 0; k < 15; k++) // check for old ECMs + { +#ifdef CS_CACHEEX + if((checkECMD5CW(er->ecmd5) && checkECMD5CW(cwc->ecm_md5[k].md5) && !(memcmp(er->ecmd5, cwc->ecm_md5[k].md5, sizeof(er->ecmd5)))) || (er->csp_hash && cwc->ecm_md5[k].csp_hash && er->csp_hash == cwc->ecm_md5[k].csp_hash)) +#else + if((memcmp(er->ecmd5, cwc->ecm_md5[k].md5, sizeof(er->ecmd5))) == 0) +#endif + { + cs_log_dbg(D_CWC, "cyclecheck [OLD] [CheckedECM] Client: %s EA: %s", user, er_ecmf); +#ifdef WITH_DEBUG + if(cs_dblevel & D_CWC) + { + cs_hexdump(0, cwc->ecm_md5[k].md5, 16, cwc_md5, sizeof(cwc_md5)); + cs_hexdump(0, (void *)&cwc->ecm_md5[k].csp_hash, 4, cwc_csp, sizeof(cwc_csp)); + cs_log_dbg(D_CWC, "cyclecheck [OLD] [Stored ECM] Client: %s EA: %s.%s", user, cwc_md5, cwc_csp); + } +#endif + if(!cfg.cwcycle_dropold && !memcmp(cwc->ecm_md5[k].cw, cw, 16)) + { ret = 4; } + else + { ret = 2; } // old ER + upd_entry = 0; + break; + } + } + if(!upd_entry) { break; } + if(cycleok == -2) + { cs_log_dbg(D_CWC, "cyclecheck [ATTENTION!! NON Valid CW] Client: %s EA: %s Timediff: %" PRId64 " Stage: %i Cycletime: %i dyncycletime: %i nextCycleCW = CW%i from Reader: %s", user, er_ecmf, (int64_t)now - cwc->time, cwc->stage, cwc->cycletime, cwc->dyncycletime, cwc->nextcyclecw, reader); } + else + { cs_log_dbg(D_CWC, "cyclecheck [ATTENTION!! NON Valid CW Cycle] NO CW Cycle detected! Client: %s EA: %s Timediff: %" PRId64 " Stage: %i Cycletime: %i dyncycletime: %i nextCycleCW = CW%i from Reader: %s", user, er_ecmf, (int64_t)now - cwc->time, cwc->stage, cwc->cycletime, cwc->dyncycletime, cwc->nextcyclecw, reader); } + cs_log_dbg(D_CWC, "cyclecheck [Dump Stored CW] Client: %s EA: %s CW: %s Time: %" PRId64, user, cwc_ecmf, cwc_cw, (int64_t)cwc->time); + cs_log_dbg(D_CWC, "cyclecheck [Dump CheckedCW] Client: %s EA: %s CW: %s Time: %" PRId64 " Timediff: %" PRId64, user, er_ecmf, cwstr, (int64_t)now, (int64_t)now - cwc->time); + ret = 1; // bad cycle + upd_entry = 0; + if(cfg.cwcycle_allowbadfromffb) + { + if(chk_is_pos_fallback(er, reader)) + { + ret = 5; + cwc->stage = 4; + upd_entry = 1; + cwc->nextcyclecw = 2; + break; + } + } + break; + } + } + else + { + if(cwc->stage == 3) + { + if(cfg.keepcycletime > 0 && now - cwc->time < cfg.keepcycletime * 60) // we are in keepcycletime window + { + cwc->stage++; // go to stage 4 + cs_log_dbg(D_CWC, "cyclecheck [Set Stage 4] for Entry: %s Cycletime: %i -> Entry too old but in keepcycletime window - no cycletime learning - only check which CW must cycle", cwc_ecmf, cwc->cycletime); + } + else + { + cwc->stage--; // go one stage back, we are not in keepcycletime window + cs_log_dbg(D_CWC, "cyclecheck [Back to Stage 2] for Entry: %s Cycletime: %i -> new cycletime learning", cwc_ecmf, cwc->cycletime); + } + memset(cwc->cw, 0, sizeof(cwc->cw)); //fake cw for stage 2/4 + ret = 3; + cwc->nextcyclecw = 2; + } + } + if(upd_entry) // learning stages + { + if(now > cwc->locktime) + { + int16_t diff = now - cwc->time - cwc->cycletime; + if(cwc->stage <= 0) // stage 0 is passed; we update the cw's and time and store cycletime + { + // if(cwc->cycletime == now - cwc->time) // if we got a stable cycletime we go to stage 1 + if(diff > -2 && diff < 2) // if we got a stable cycletime we go to stage 1 + { + cwc->cycletime = now - cwc->time; + cs_log_dbg(D_CWC, "cyclecheck [Set Stage 1] %s Cycletime: %i Lockdiff: %" PRId64, cwc_ecmf, cwc->cycletime, (int64_t)now - cwc->locktime); + cwc->stage++; // increase stage + } + else + { + cs_log_dbg(D_CWC, "cyclecheck [Stay on Stage 0] %s Cycletime: %i -> no constant CW-Change-Time", cwc_ecmf, cwc->cycletime); + } + + } + else if(cwc->stage == 1) // stage 1 is passed; we update the cw's and time and store cycletime + { + // if(cwc->cycletime == now - cwc->time) // if we got a stable cycletime we go to stage 2 + if(diff > -2 && diff < 2) // if we got a stable cycletime we go to stage 2 + { + cwc->cycletime = now - cwc->time; + cs_log_dbg(D_CWC, "cyclecheck [Set Stage 2] %s Cycletime: %i Lockdiff: %" PRId64, cwc_ecmf, cwc->cycletime, (int64_t)now - cwc->locktime); + cwc->stage++; // increase stage + } + else + { + cs_log_dbg(D_CWC, "cyclecheck [Back to Stage 0] for Entry %s Cycletime: %i -> no constant CW-Change-Time", cwc_ecmf, cwc->cycletime); + cwc->stage--; + } + } + else if(cwc->stage == 2) // stage 2 is passed; we update the cw's and compare cycletime + { + // if(cwc->cycletime == now - cwc->time && cwc->cycletime > 0) // if we got a stable cycletime we go to stage 3 + if(diff > -2 && diff < 2 && cwc->cycletime > 0) // if we got a stable cycletime we go to stage 3 + { + cwc->cycletime = now - cwc->time; + n = memcmp(cwc->cw, cw, 8); + m = memcmp(cwc->cw + 8, cw + 8, 8); + if(n == 0) + { + cwc->nextcyclecw = 1; + } + if(m == 0) + { + cwc->nextcyclecw = 0; + } + if(n == m || !checkECMD5CW(cw)) { cwc->nextcyclecw = 2; } //be sure only one cw part cycle and is valid + if(cwc->nextcyclecw < 2) + { + cs_log_dbg(D_CWC, "cyclecheck [Set Stage 3] %s Cycletime: %i Lockdiff: %" PRId64 " nextCycleCW = CW%i", cwc_ecmf, cwc->cycletime, (int64_t)now - cwc->locktime, cwc->nextcyclecw); + cs_log_dbg(D_CWC, "cyclecheck [Set Cycletime %i] for Entry: %s -> now we can check CW's", cwc->cycletime, cwc_ecmf); + cwc->stage = 3; // increase stage + } + else + { + cs_log_dbg(D_CWC, "cyclecheck [Back to Stage 1] for Entry %s Cycletime: %i -> no CW-Cycle in Learning Stage", cwc_ecmf, cwc->cycletime); // if a server asked only every twice ECM we got a stable cycletime*2 ->but thats wrong + cwc->stage = 1; + } + + } + else + { + + cs_log_dbg(D_CWC, "cyclecheck [Back to Stage 1] for Entry %s Cycletime: %i -> no constant CW-Change-Time", cwc_ecmf, cwc->cycletime); + cwc->stage = 1; + } + } + else if(cwc->stage == 4) // we got a early learned cycletime.. use this cycletime and check only which cw cycle + { + n = memcmp(cwc->cw, cw, 8); + m = memcmp(cwc->cw + 8, cw + 8, 8); + if(n == 0) + { + cwc->nextcyclecw = 1; + } + if(m == 0) + { + cwc->nextcyclecw = 0; + } + if(n == m || !checkECMD5CW(cw)) { cwc->nextcyclecw = 2; } //be sure only one cw part cycle and is valid + if(cwc->nextcyclecw < 2) + { + cs_log_dbg(D_CWC, "cyclecheck [Back to Stage 3] %s Cycletime: %i Lockdiff: %" PRId64 " nextCycleCW = CW%i", cwc_ecmf, cwc->cycletime, (int64_t)now - cwc->locktime, cwc->nextcyclecw); + cs_log_dbg(D_CWC, "cyclecheck [Set old Cycletime %i] for Entry: %s -> now we can check CW's", cwc->cycletime, cwc_ecmf); + cwc->stage = 3; // go back to stage 3 + } + else + { + cs_log_dbg(D_CWC, "cyclecheck [Stay on Stage %d] for Entry %s Cycletime: %i no cycle detect!", cwc->stage, cwc_ecmf, cwc->cycletime); + if (cwc->stage4_repeat > 12) + { + cwc->stage = 1; + cs_log_dbg(D_CWC, "cyclecheck [Back to Stage 1] too much cyclefailure, maybe cycletime not correct %s Cycletime: %i Lockdiff: %" PRId64 " nextCycleCW = CW%i", cwc_ecmf, cwc->cycletime, (int64_t)now - cwc->locktime, cwc->nextcyclecw); + } + } + cwc->stage4_repeat++; + ret = ret == 3 ? 3 : 7; // IGN for first stage4 otherwise LEARN + } + if(cwc->stage == 3) + { + cwc->locktime = 0; + cwc->stage4_repeat = 0; + } + else + { + if(cwc->stage < 3) { cwc->cycletime = now - cwc->time; } + cwc->locktime = now + (get_fallbacktimeout(cwc->caid) / 1000); + } + } + else if(cwc->stage != 3) + { + cs_log_dbg(D_CWC, "cyclecheck [Ignore this EA] for LearningStages because of locktime EA: %s Lockdiff: %" PRId64, cwc_ecmf, (int64_t)now - cwc->locktime); + upd_entry = 0; + } + + if(cwc->stage == 3) // we stay in Stage 3 so we update only time and cw + { + if(now - cwc->time > cwc->cycletime) + { + cwc->dyncycletime = now - cwc->time - cwc->cycletime; + } + else + { + cwc->dyncycletime = 0; + } + } + } + } + else + { + upd_entry = 0; + cwc = NULL; + } + break; + } + + if (readlocked) + { + cs_readunlock(__func__, &cwcycle_lock); + } + + if(need_new_entry) + { + cs_readunlock(__func__, &cwcycle_lock); + if(cw_cc_list_size <= mcl) //only add when we have space + { + struct s_cw_cycle_check *new = NULL; + if(cs_malloc(&new, sizeof(struct s_cw_cycle_check))) // store cw on top in cyclelist + { + memcpy(new->cw, cw, sizeof(new->cw)); + // csp cache got no ecm and no md5 hash + memcpy(new->ecm_md5[0].md5, er->ecmd5, sizeof(er->ecmd5)); +#ifdef CS_CACHEEX + new->ecm_md5[0].csp_hash = er->csp_hash; // we got no ecm_md5 so CSP-Hash could be necessary +#else + new->ecm_md5[0].csp_hash = 0; //fake CSP-Hash we got a ecm_md5 so CSP-Hash is not necessary +#endif + memcpy(new->ecm_md5[0].cw, cw, sizeof(new->cw)); + new->ecmlen = er->ecmlen; + new->cwc_hist_entry = 0; + new->caid = er->caid; + new->provid = er->prid; + new->sid = er->srvid; + new->chid = er->chid; + new->time = now; + new->locktime = now + (get_fallbacktimeout(er->caid) / 1000); + new->dyncycletime = 0; // to react of share timings +// cycletime over Cacheex + new->stage = (cfg.cwcycle_usecwcfromce && cycletime_fr > 0 && next_cw_cycle_fr < 2) ? 3 : 0; + new->cycletime = (cfg.cwcycle_usecwcfromce && cycletime_fr > 0 && next_cw_cycle_fr < 2) ? cycletime_fr : 99; + new->nextcyclecw = (cfg.cwcycle_usecwcfromce && cycletime_fr > 0 && next_cw_cycle_fr < 2) ? next_cw_cycle_fr : 2; //2=we dont know which next cw Cycle; 0= next cw Cycle CW0; 1= next cw Cycle CW1; + ret = (cycletime_fr > 0 && next_cw_cycle_fr < 2) ? 8 : 6; +// + new->prev = new->next = NULL; + new->old = 0; + new->stage4_repeat = 0; + //write lock + cs_writelock(__func__, &cwcycle_lock); + if(cw_cc_list) // the new entry on top + { + cw_cc_list->prev = new; + new->next = cw_cc_list; + } + cw_cc_list = new; + cw_cc_list_size++; + //write unlock / + cs_writeunlock(__func__, &cwcycle_lock); + + cs_log_dbg(D_CWC, "cyclecheck [Store New Entry] %s Time: %" PRId64 " Stage: %i Cycletime: %i Locktime: %" PRId64, er_ecmf, (int64_t)new->time, new->stage, new->cycletime, (int64_t)new->locktime); + } + } + else + { + cs_log("cyclecheck [Store New Entry] Max List arrived -> dont store new Entry list_size: %i, mcl: %i", cw_cc_list_size, mcl); + } + } + else if(upd_entry && cwc) + { + cwc->prev = cwc->next = NULL; + cwc->old = 0; + memcpy(cwc->cw, cw, sizeof(cwc->cw)); + cwc->time = now; + cwc->cwc_hist_entry++; + if(cwc->cwc_hist_entry > 14) //ringbuffer for md5 + { + cwc->cwc_hist_entry = 0; + } + // csp cache got no ecm and no md5 hash + memcpy(cwc->ecm_md5[cwc->cwc_hist_entry].md5, er->ecmd5, sizeof(cwc->ecm_md5[0].md5)); +#ifdef CS_CACHEEX + cwc->ecm_md5[cwc->cwc_hist_entry].csp_hash = er->csp_hash; +#else + cwc->ecm_md5[cwc->cwc_hist_entry].csp_hash = 0; //fake CSP-Hash for logging +#endif + memcpy(cwc->ecm_md5[cwc->cwc_hist_entry].cw, cw, sizeof(cwc->cw)); + cwc->ecmlen = er->ecmlen; + //write lock / + cs_writelock(__func__, &cwcycle_lock); + if(cw_cc_list) // the clone entry on top + { + cw_cc_list->prev = cwc; + cwc->next = cw_cc_list; + } + cw_cc_list = cwc; + cw_cc_list_size++; + //write unlock / + cs_writeunlock(__func__, &cwcycle_lock); + cs_log_dbg(D_CWC, "cyclecheck [Update Entry and add on top] %s Time: %" PRId64 " Stage: %i Cycletime: %i", er_ecmf, (int64_t)cwc->time, cwc->stage, cwc->cycletime); + } + else if(cwc) + { + NULLFREE(cwc); + } + return ret; +} + +static void count_ok(struct s_client *client) +{ + if(client) + { + client->cwcycledchecked++; + client->cwcycledok++; + } + if(client && client->account) + { + client->account->cwcycledchecked++; + client->account->cwcycledok++; + } +} + +static void count_nok(struct s_client *client) +{ + if(client) + { + client->cwcycledchecked++; + client->cwcyclednok++; + } + if(client && client->account) + { + client->account->cwcycledchecked++; + client->account->cwcyclednok++; + } +} + +static void count_ign(struct s_client *client) +{ + if(client) + { + client->cwcycledchecked++; + client->cwcycledign++; + } + if(client && client->account) + { + client->account->cwcycledchecked++; + client->account->cwcycledign++; + } +} + +uint8_t checkcwcycle(struct s_client *client, ECM_REQUEST *er, struct s_reader *reader, uint8_t *cw, int8_t rc, uint8_t cycletime_fr, uint8_t next_cw_cycle_fr) +{ + + if(!cfg.cwcycle_check_enable) + { return 3; } + if(client && client->account && client->account->cwc_disable) + { return 3; } + // if (!(rc == E_FOUND) && !(rc == E_CACHEEX)) + if(rc >= E_NOTFOUND) + { return 2; } + if(!cw || !er) + { return 2; } + if(!(chk_ctab_ex(er->caid, &cfg.cwcycle_check_caidtab))) // dont check caid not in list + { return 1; } // no match leave the check + if(is_halfCW_er(er)) + { return 1; } // half cw cycle, checks are done in ecm-handler + + memcpy(er->cw, cw, 16); + char er_ecmf[ECM_FMT_LEN]; + format_ecm(er, er_ecmf, ECM_FMT_LEN); + + char c_reader[64]; + char user[64]; + + if(!streq(username(client), "NULL")) + { snprintf(user, sizeof(user), "%s", username(client)); } + else + { snprintf(user, sizeof(user), "---"); } + + if(reader) + { snprintf(c_reader, sizeof(c_reader), "%s", reader->label); } + else + { snprintf(c_reader, sizeof(c_reader), "cache"); } + + + cs_log_dbg(D_CWC | D_TRACE, "cyclecheck EA: %s rc: %i reader: %s", er_ecmf, rc, c_reader); + + switch(checkcwcycle_int(er, er_ecmf, user, cw, c_reader, cycletime_fr, next_cw_cycle_fr)) + { + + case 0: // CWCYCLE OK + count_ok(client); + snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc OK"); + break; + + case 1: // CWCYCLE NOK + count_nok(client); + snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc NOK"); + if(cfg.onbadcycle > 0) // ignore ECM Request + { +#ifdef CS_CACHEEX_AIO + if(!er->localgenerated) + { +#endif + cs_log("cyclecheck [Bad CW Cycle] for: %s %s from: %s -> drop cw (ECM Answer)", user, er_ecmf, c_reader); //D_CWC| D_TRACE + return 0; +#ifdef CS_CACHEEX_AIO + } + else + { + cs_log("cyclecheck [Bad CW Cycle] for: %s %s from: %s -> lg-flagged CW -> do nothing", user, er_ecmf, c_reader); //D_CWC| D_TRACE + break; + } +#endif + + } + else // only logging + { + cs_log("cyclecheck [Bad CW Cycle] for: %s %s from: %s -> do nothing", user, er_ecmf, c_reader);//D_CWC| D_TRACE + break; + } + + case 2: // ER to OLD +#ifdef CS_CACHEEX_AIO + if(!er->localgenerated) + { +#endif + count_nok(client); + snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc NOK(old)"); + cs_log("cyclecheck [Bad CW Cycle] for: %s %s from: %s -> ECM Answer is too OLD -> drop cw (ECM Answer)", user, er_ecmf, c_reader);//D_CWC| D_TRACE + return 0; +#ifdef CS_CACHEEX_AIO + } + else + { + cs_log("cyclecheck [Bad CW Cycle] for: %s %s from: %s -> ECM Answer is too OLD -> lg-flagged CW -> do nothing", user, er_ecmf, c_reader); //D_CWC| D_TRACE + break; + } +#endif + + case 3: // CycleCheck ignored (stage 3 to stage 4) + count_ign(client); + snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc IGN"); + break; + + case 4: // same CW + cs_log_dbg(D_CWC, "cyclecheck [Same CW] for: %s %s -> same CW detected from: %s -> do nothing ", user, er_ecmf, c_reader); + break; + + case 5: //answer from fixed Fallbackreader with Bad Cycle + count_nok(client); + snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc NOK but IGN (fixed FB)"); + cs_log("cyclecheck [Bad CW Cycle] for: %s %s from: %s -> But Ignored because of answer from Fixed Fallback Reader", user, er_ecmf, c_reader); + break; + + case 6: // not checked ( learning Stages Cycletime and CWCycle Stage < 3) + case 7: // not checked ( learning Stages only CWCycle Stage 4) + snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc LEARN"); + break; + + case 8: // use Cyclecheck from CE Source + count_ok(client); + snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc OK(CE)"); + break; + + case 9: // CWCYCLE NOK without counting + snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc NOK"); + if(cfg.onbadcycle > 0) // ignore ECM Request + { +#ifdef CS_CACHEEX_AIO + if(!er->localgenerated) + { +#endif + cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> drop cw (ECM Answer)", user, er_ecmf, c_reader); + return 0; +#ifdef CS_CACHEEX_AIO + } + else + { + cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> lg-flagged CW -> do nothing", user, er_ecmf, c_reader); //D_CWC| D_TRACE + break; + } +#endif + } + else // only logging + { + cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> do nothing", user, er_ecmf, c_reader); + break; + } + + } + return 1; +} + + +/* + * + */ + +#endif diff --git a/module-cw-cycle-check.h b/module-cw-cycle-check.h new file mode 100644 index 0000000..8534e80 --- /dev/null +++ b/module-cw-cycle-check.h @@ -0,0 +1,12 @@ +#ifndef MODULE_CW_CYCLE_CHECK_H_ +#define MODULE_CW_CYCLE_CHECK_H_ + +uint8_t checkcwcycle(struct s_client *client, ECM_REQUEST *er, struct s_reader *reader, uint8_t *cw, int8_t rc, uint8_t cycletime_fr, uint8_t next_cw_cycle_fr); + +#ifdef CW_CYCLE_CHECK +void cleanupcwcycle(void); +#else +static inline void cleanupcwcycle(void) { } +#endif + +#endif diff --git a/module-dvbapi-azbox.c b/module-dvbapi-azbox.c new file mode 100644 index 0000000..321f19e --- /dev/null +++ b/module-dvbapi-azbox.c @@ -0,0 +1,354 @@ +#define MODULE_LOG_PREFIX "dvbazbox" + +#include "globals.h" + +#if defined(HAVE_DVBAPI) && defined(WITH_AZBOX) + +#include "extapi/openxcas/openxcas_api.h" +#include "extapi/openxcas/openxcas_message.h" + +#include "module-dvbapi.h" +#include "module-dvbapi-azbox.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-time.h" + +// These variables are declared in module-dvbapi.c +extern void *dvbapi_client; +extern DEMUXTYPE demux[MAX_DEMUX]; + +// These are used in module-dvbapi.c +int32_t openxcas_provid; +uint16_t openxcas_sid, openxcas_caid, openxcas_ecm_pid; + +static uint8_t openxcas_cw[16]; +static int32_t openxcas_seq, openxcas_filter_idx, openxcas_stream_id, openxcas_cipher_idx, openxcas_busy = 0; +static uint16_t openxcas_video_pid, openxcas_audio_pid, openxcas_data_pid; + +static void azbox_openxcas_ecm_callback(int32_t stream_id, uint32_t UNUSED(seq), int32_t cipher_index, uint32_t UNUSED(caid), uint8_t *ecm_data, int32_t l, uint16_t pid) +{ + cs_log_dbg(D_DVBAPI, "ecm callback received"); + + openxcas_stream_id = stream_id; + //openxcas_seq = seq; + //openxcas_caid = caid; + openxcas_ecm_pid = pid; + openxcas_busy = 1; + + if(l < 0 || l > MAX_ECM_SIZE) + { return; } + + ECM_REQUEST *er; + if(!(er = get_ecmtask())) + { return; } + + er->srvid = openxcas_sid; + er->caid = openxcas_caid; + er->pid = openxcas_ecm_pid; + er->prid = openxcas_provid; + + er->ecmlen = l; + memcpy(er->ecm, ecm_data, er->ecmlen); + + request_cw(dvbapi_client, er, 0, 0); + + //openxcas_stop_filter(openxcas_stream_id, OPENXCAS_FILTER_ECM); + //openxcas_remove_filter(openxcas_stream_id, OPENXCAS_FILTER_ECM); + + openxcas_cipher_idx = cipher_index; + + struct timeb tp; + cs_ftime(&tp); + tp.time += 500; +} + + +static void azbox_openxcas_ex_callback(int32_t stream_id, uint32_t seq, int32_t idx, uint32_t pid, uint8_t *ecm_data, int32_t l) +{ + cs_log_dbg(D_DVBAPI, "ex callback received"); + + openxcas_stream_id = stream_id; + openxcas_ecm_pid = pid; + openxcas_cipher_idx = idx; // is this really cipher_idx? + + if(l < 0 || l > MAX_ECM_SIZE) + { return; } + + ECM_REQUEST *er; + if(!(er = get_ecmtask())) + { return; } + + er->srvid = openxcas_sid; + er->caid = openxcas_caid; + er->pid = openxcas_ecm_pid; + er->prid = openxcas_provid; + + er->ecmlen = l; + memcpy(er->ecm, ecm_data, er->ecmlen); + + request_cw(dvbapi_client, er, 0, 0); + + if(openxcas_stop_filter_ex(stream_id, seq, openxcas_filter_idx) < 0) + { cs_log("unable to stop ex filter"); } + else + { cs_log_dbg(D_DVBAPI, "ex filter stopped"); } + + + + uint8_t mask[12]; + uint8_t comp[12]; + memset(&mask, 0x00, sizeof(mask)); + memset(&comp, 0x00, sizeof(comp)); + + mask[0] = 0xff; + comp[0] = ecm_data[0] ^ 1; + + if((openxcas_filter_idx = openxcas_start_filter_ex(stream_id, seq, openxcas_ecm_pid, mask, comp, (void *)azbox_openxcas_ex_callback)) < 0) + { cs_log("unable to start ex filter"); } + else + { cs_log_dbg(D_DVBAPI, "ex filter started, pid = %x", openxcas_ecm_pid); } +} + +static void *azbox_main_thread(void *cli) +{ + struct s_client *client = (struct s_client *) cli; + client->thread = pthread_self(); + SAFE_SETSPECIFIC(getclient, cli); + dvbapi_client = cli; + + struct s_auth *account; + int32_t ok = 0; + for(account = cfg.account; account; account = account->next) + { + if((ok = is_dvbapi_usr(account->usr))) + { break; } + } + cs_auth_client(client, ok ? account : (struct s_auth *)(-1), "dvbapi"); + + dvbapi_read_priority(); + + openxcas_msg_t msg; + int32_t ret; + while((ret = openxcas_get_message(&msg, 0)) >= 0) + { + cs_sleepms(10); + + if(ret) + { + openxcas_stream_id = msg.stream_id; + openxcas_seq = msg.sequence; + struct stOpenXCAS_Data data; + + switch(msg.cmd) + { + case OPENXCAS_SELECT_CHANNEL: + cs_log_dbg(D_DVBAPI, "OPENXCAS_SELECT_CHANNEL"); + + // parse channel info + struct stOpenXCASChannel chan; + memcpy(&chan, msg.buf, msg.buf_len); + + cs_log("channel change: sid = %x, vpid = %x. apid = %x", chan.service_id, chan.v_pid, chan.a_pid); + + openxcas_video_pid = chan.v_pid; + openxcas_audio_pid = chan.a_pid; + openxcas_data_pid = chan.d_pid; + break; + case OPENXCAS_START_PMT_ECM: + cs_log_dbg(D_DVBAPI, "OPENXCAS_START_PMT_ECM"); + + // parse pmt + uint8_t *dest; + if(!cs_malloc(&dest, msg.buf_len + 7 - 12 - 4)) + { break; } + + memcpy(dest, "\x03\xFF\xFF\x00\x00\x13\x00", 7); + + dest[1] = msg.buf[3]; + dest[2] = msg.buf[4]; + dest[5] = msg.buf[11] + 1; + + memcpy(dest + 7, msg.buf + 12, msg.buf_len - 12 - 4); + + dvbapi_parse_capmt(dest, 7 + msg.buf_len - 12 - 4, -1, NULL, 0, 0); + NULLFREE(dest); + + uint8_t mask[12]; + uint8_t comp[12]; + memset(&mask, 0x00, sizeof(mask)); + memset(&comp, 0x00, sizeof(comp)); + + mask[0] = 0xfe; + comp[0] = 0x80; + + if((ret = openxcas_add_filter(msg.stream_id, OPENXCAS_FILTER_ECM, 0, 0xffff, openxcas_ecm_pid, mask, comp, (void *)azbox_openxcas_ecm_callback)) < 0) + { cs_log("unable to add ecm filter"); } + else + { cs_log_dbg(D_DVBAPI, "ecm filter added, pid = %x, caid = %x", openxcas_ecm_pid, 0); } + + if(openxcas_start_filter(msg.stream_id, msg.sequence, OPENXCAS_FILTER_ECM) < 0) + { cs_log("unable to start ecm filter"); } + else + { cs_log_dbg(D_DVBAPI, "ecm filter started"); } + + if(!openxcas_create_cipher_ex(msg.stream_id, openxcas_seq, 0, openxcas_ecm_pid, openxcas_video_pid, 0xffff, openxcas_audio_pid, 0xffff, 0xffff, 0xffff)) + { cs_log("failed to create cipher ex"); } + else + { cs_log_dbg(D_DVBAPI, "cipher created"); } + break; + case OPENXCAS_STOP_PMT_ECM: + cs_log_dbg(D_DVBAPI, "OPENXCAS_STOP_PMT_ECM"); + openxcas_stop_filter(msg.stream_id, OPENXCAS_FILTER_ECM); + openxcas_remove_filter(msg.stream_id, OPENXCAS_FILTER_ECM); + openxcas_stop_filter_ex(msg.stream_id, msg.sequence, openxcas_filter_idx); + openxcas_destory_cipher_ex(msg.stream_id, msg.sequence); + memset(&demux, 0, sizeof(demux)); + break; + case OPENXCAS_ECM_CALLBACK: + cs_log_dbg(D_DVBAPI, "OPENXCAS_ECM_CALLBACK"); + memcpy(&data, msg.buf, msg.buf_len); + if(!openxcas_busy) + { openxcas_filter_callback(msg.stream_id, msg.sequence, OPENXCAS_FILTER_ECM, &data); } + break; + case OPENXCAS_PID_FILTER_CALLBACK: + cs_log_dbg(D_DVBAPI, "OPENXCAS_PID_FILTER_CALLBACK"); + memcpy(&data, msg.buf, msg.buf_len); + openxcas_filter_callback_ex(msg.stream_id, msg.sequence, &data); + break; + case OPENXCAS_QUIT: + cs_log_dbg(D_DVBAPI, "OPENXCAS_QUIT"); + openxcas_close(); + cs_log("exited"); + return NULL; + break; + case OPENXCAS_UKNOWN_MSG: + default: + cs_log_dbg(D_DVBAPI, "OPENXCAS_UKNOWN_MSG (%d)", msg.cmd); + //cs_log_dump_dbg(D_DVBAPI, &msg, sizeof(msg), "msg dump:"); + break; + } + } + } + cs_log("invalid message"); + return NULL; +} + +void azbox_send_dcw(struct s_client *client, ECM_REQUEST *er) +{ + struct s_dvbapi_priority *delayentry = dvbapi_check_prio_match(0, demux[0].pidindex, 'd'); + uint32_t delay = 0; + + cs_log_dbg(D_DVBAPI, "send_dcw"); + + if(delayentry) + { + if(delayentry->delay < 1000) + { + delay = delayentry->delay; + cs_log_dbg(D_DVBAPI, "specific delay: write cw %d ms after ecmrequest", delay); + } + } + else if (cfg.dvbapi_delayer > 0) + { + delay = cfg.dvbapi_delayer; + cs_log_dbg(D_DVBAPI, "generic delay: write cw %d ms after ecmrequest", delay); + } + + delayer(er, delay); + + if(cfg.dvbapi_ecminfo_file != 0) + { + dvbapi_write_ecminfo_file(client, er, demux[0].last_cw[0][0], demux[0].last_cw[0][1], 8); + } + + openxcas_busy = 0; + + int32_t i; + for(i = 0; i < MAX_DEMUX; i++) + { + if(er->rc >= E_NOTFOUND) + { + cs_log_dbg(D_DVBAPI, "cw not found"); + + if(demux[i].pidindex == -1) + { dvbapi_try_next_caid(i, 0, 0); } + + openxcas_stop_filter(openxcas_stream_id, OPENXCAS_FILTER_ECM); + openxcas_remove_filter(openxcas_stream_id, OPENXCAS_FILTER_ECM); + + uint8_t mask[12]; + uint8_t comp[12]; + memset(&mask, 0x00, sizeof(mask)); + memset(&comp, 0x00, sizeof(comp)); + + mask[0] = 0xfe; + comp[0] = 0x80; + + if(openxcas_add_filter(openxcas_stream_id, OPENXCAS_FILTER_ECM, 0, 0xffff, openxcas_ecm_pid, mask, comp, (void *)azbox_openxcas_ecm_callback) < 0) + { + cs_log("unable to add ecm filter (0)"); + if(openxcas_add_filter(openxcas_stream_id, OPENXCAS_FILTER_ECM, openxcas_caid, 0xffff, openxcas_ecm_pid, mask, comp, (void *)azbox_openxcas_ecm_callback) < 0) + { cs_log("unable to add ecm filter (%04x)", openxcas_caid); } + else + { cs_log_dbg(D_DVBAPI, "ecm filter added, pid = %x, caid = %x", openxcas_ecm_pid, openxcas_caid); } + } + else + { cs_log_dbg(D_DVBAPI, "ecm filter added, pid = %x, caid = %x", openxcas_ecm_pid, 0); } + + if(openxcas_start_filter(openxcas_stream_id, openxcas_seq, OPENXCAS_FILTER_ECM) < 0) + { cs_log("unable to start ecm filter"); } + else + { cs_log_dbg(D_DVBAPI, "ecm filter started"); } + + return; + } + } + + uint8_t nullcw[8]; + memset(nullcw, 0, 8); + + int32_t n; + for(n = 0; n < 2; n++) + { + // Skip check for BISS1 - cw could be indeed zero + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + if(memcmp(er->cw + (n * 8), demux[0].last_cw[0][n], 8) && (memcmp(er->cw + (n * 8), nullcw, 8) != 0 || caid_is_biss(er->caid))) + { + memcpy(demux[0].last_cw[0][n], er->cw + (n * 8), 8); + memcpy(openxcas_cw + (n * 8), er->cw + (n * 8), 8); + } + } + + if(openxcas_set_key(openxcas_stream_id, openxcas_seq, 0, openxcas_cipher_idx, openxcas_cw, openxcas_cw + 8) != 1) + { cs_log("set cw failed"); } + else + { cs_log_dump_dbg(D_DVBAPI, openxcas_cw, 16, "write cws to descrambler"); } +} + +#ifdef WITH_CARDREADER +#define __openxcas_open openxcas_open_with_smartcard +#else +#define __openxcas_open openxcas_open +#endif + +void azbox_init(void) +{ + openxcas_debug_message_onoff(1); // debug + if(__openxcas_open("oscamCAS") < 0) + { cs_log("could not init"); } +} + +void azbox_close(void) +{ + if(openxcas_close() < 0) + { cs_log("could not close"); } +} + +void *azbox_handler(struct s_client *cl, uint8_t *mbuf, int32_t module_idx) +{ + return dvbapi_start_handler(cl, mbuf, module_idx, azbox_main_thread); +} + +#endif diff --git a/module-dvbapi-azbox.h b/module-dvbapi-azbox.h new file mode 100644 index 0000000..40e9133 --- /dev/null +++ b/module-dvbapi-azbox.h @@ -0,0 +1,15 @@ +#ifndef _MODULE_AZBOX_H_ +#define _MODULE_AZBOX_H_ + +void azbox_send_dcw(struct s_client *client, ECM_REQUEST *er); +void *azbox_handler(struct s_client *cl, uint8_t *mbuf, int32_t module_idx); + +#if defined(HAVE_DVBAPI) && defined(WITH_AZBOX) +void azbox_init(void); +void azbox_close(void); +#else +static inline void azbox_init(void) { } +static inline void azbox_close(void) { } +#endif + +#endif diff --git a/module-dvbapi-chancache.c b/module-dvbapi-chancache.c new file mode 100644 index 0000000..c69dd50 --- /dev/null +++ b/module-dvbapi-chancache.c @@ -0,0 +1,212 @@ +#define MODULE_LOG_PREFIX "dvbapi" + +#include "globals.h" + +#ifdef HAVE_DVBAPI + +#include "oscam-config.h" +#include "oscam-ecm.h" +#include "oscam-string.h" +#include "module-dvbapi.h" +#include "module-dvbapi-chancache.h" + +extern DEMUXTYPE demux[MAX_DEMUX]; + +static LLIST *channel_cache; + +void dvbapi_save_channel_cache(void) +{ + if(boxtype_is("dbox2")) return; // dont save channelcache on these boxes, they lack resources and will crash! + + if (USE_OPENXCAS) // Why? + return; + + char fname[256]; + int32_t result = 0; + get_config_filename(fname, sizeof(fname), "oscam.ccache"); + FILE *file = fopen(fname, "w"); + + if(!file) + { + cs_log("dvbapi channelcache can't write to file %s", fname); + return; + } + + LL_ITER it = ll_iter_create(channel_cache); + struct s_channel_cache *c; + while((c = ll_iter_next(&it))) + { + result = fprintf(file, "%04X,%06X,%04X,%04X,%06X\n", c->caid, c->prid, c->srvid, c->pid, c->chid); + if(result < 0) + { + fclose(file); + result = remove(fname); + if(!result) + { + cs_log("error writing cache -> cache file removed!"); + } + else + { + cs_log("error writing cache -> cache file could not be removed either!"); + } + return; + } + } + + fclose(file); + cs_log("dvbapi channelcache saved to %s", fname); +} + +void dvbapi_load_channel_cache(void) +{ + if(boxtype_is("dbox2")) return; // dont load channelcache on these boxes, they lack resources and will crash! + + if (USE_OPENXCAS) // Why? + return; + + char fname[256]; + char line[1024]; + FILE *file; + struct s_channel_cache *c; + + get_config_filename(fname, sizeof(fname), "oscam.ccache"); + file = fopen(fname, "r"); + if(!file) + { + cs_log_dbg(D_TRACE, "dvbapi channelcache can't read from file %s", fname); + return; + } + + int32_t i = 1; + int32_t valid = 0; + char *ptr, *saveptr1 = NULL; + char *split[6]; + + memset(line, 0, sizeof(line)); + while(fgets(line, sizeof(line), file)) + { + if(!line[0] || line[0] == '#' || line[0] == ';') + { continue; } + + for(i = 0, ptr = strtok_r(line, ",", &saveptr1); ptr && i < 6 ; ptr = strtok_r(NULL, ",", &saveptr1), i++) + { + split[i] = ptr; + } + + valid = (i == 5); + if(valid) + { + if(!cs_malloc(&c, sizeof(struct s_channel_cache))) + { continue; } + c->caid = a2i(split[0], 4); + c->prid = a2i(split[1], 6); + c->srvid = a2i(split[2], 4); + c->pid = a2i(split[3], 4); + c->chid = a2i(split[4], 6); + + if(valid && c->caid != 0) + { + if(!channel_cache) + { + channel_cache = ll_create("channel cache"); + } + + ll_append(channel_cache, c); + } + else + { + NULLFREE(c); + } + } + } + fclose(file); + cs_log("dvbapi channelcache loaded from %s", fname); +} + +struct s_channel_cache *dvbapi_find_channel_cache(int32_t demux_id, int32_t pidindex, int8_t caid_and_prid_only) +{ + struct s_ecmpid *p = &demux[demux_id].ECMpids[pidindex]; + struct s_channel_cache *c; + LL_ITER it; + + if(!channel_cache) + { channel_cache = ll_create("channel cache"); } + + it = ll_iter_create(channel_cache); + while((c = ll_iter_next(&it))) + { + + if(caid_and_prid_only) + { + if(p->CAID == c->caid && (p->PROVID == c->prid || p->PROVID == 0)) // PROVID ==0 some provider no provid in PMT table + { return c; } + } + else + { + if(demux[demux_id].program_number == c->srvid + && p->CAID == c->caid + && p->ECM_PID == c->pid + && (p->PROVID == c->prid || p->PROVID == 0)) // PROVID ==0 some provider no provid in PMT table + { + +#ifdef WITH_DEBUG + char buf[ECM_FMT_LEN]; + ecmfmt(buf, ECM_FMT_LEN, c->caid, 0, c->prid, c->chid, c->pid, c->srvid, 0, 0, 0, 0, 0, 0, NULL, NULL); + cs_log_dbg(D_DVBAPI, "Demuxer %d found in channel cache: %s", demux_id, buf); +#endif + return c; + } + } + } + return NULL; +} + +int32_t dvbapi_edit_channel_cache(int32_t demux_id, int32_t pidindex, uint8_t add) +{ + struct s_ecmpid *p = &demux[demux_id].ECMpids[pidindex]; + struct s_channel_cache *c; + LL_ITER it; + int32_t count = 0; + + if(!channel_cache) + { channel_cache = ll_create("channel cache"); } + + it = ll_iter_create(channel_cache); + while((c = ll_iter_next(&it))) + { + if(demux[demux_id].program_number == c->srvid + && p->CAID == c->caid + && p->ECM_PID == c->pid + && (p->PROVID == c->prid || p->PROVID == 0)) + { + if(add && p->CHID == c->chid) + { + return 0; //already added + } + ll_iter_remove_data(&it); + count++; + } + } + + if(add) + { + if(!cs_malloc(&c, sizeof(struct s_channel_cache))) + { return count; } + c->srvid = demux[demux_id].program_number; + c->caid = p->CAID; + c->pid = p->ECM_PID; + c->prid = p->PROVID; + c->chid = p->CHID; + ll_append(channel_cache, c); +#ifdef WITH_DEBUG + char buf[ECM_FMT_LEN]; + ecmfmt(buf, ECM_FMT_LEN, c->caid, 0, c->prid, c->chid, c->pid, c->srvid, 0, 0, 0, 0, 0, 0, NULL, NULL); + cs_log_dbg(D_DVBAPI, "Demuxer %d added to channel cache: %s", demux_id, buf); +#endif + count++; + } + + return count; +} + +#endif diff --git a/module-dvbapi-chancache.h b/module-dvbapi-chancache.h new file mode 100644 index 0000000..b5e43a5 --- /dev/null +++ b/module-dvbapi-chancache.h @@ -0,0 +1,24 @@ +#ifndef MODULE_DVBAPI_CHANCACHE_H_ +#define MODULE_DVBAPI_CHANCACHE_H_ + +#ifdef HAVE_DVBAPI + +struct s_channel_cache +{ + uint16_t caid; + uint32_t prid; + uint16_t srvid; + uint16_t pid; + uint32_t chid; +}; + +void dvbapi_save_channel_cache(void); +void dvbapi_load_channel_cache(void); +struct s_channel_cache *dvbapi_find_channel_cache(int32_t demux_id, int32_t pidindex, int8_t caid_and_prid_only); +int32_t dvbapi_edit_channel_cache(int32_t demux_id, int32_t pidindex, uint8_t add); + +#else +static inline void dvbapi_save_channel_cache(void) { } +#endif + +#endif diff --git a/module-dvbapi-coolapi-legacy.c b/module-dvbapi-coolapi-legacy.c new file mode 100644 index 0000000..a04589e --- /dev/null +++ b/module-dvbapi-coolapi-legacy.c @@ -0,0 +1,701 @@ +/* Reversed from libcoolstream.so, this comes without any warranty */ + +#define MODULE_LOG_PREFIX "dvbcool" + +#include "globals.h" + +#if defined(HAVE_DVBAPI) && defined(WITH_COOLAPI) + +#include "extapi/coolapi.h" + +#include "module-dvbapi.h" +#include "module-dvbapi-coolapi.h" +#include "oscam-string.h" + +#define MAX_COOL_DMX 4 + +typedef struct s_cool_filter +{ + int32_t fd; + int32_t channel; + int32_t pid; + uint8_t filter16[16]; + uint8_t mask16[16]; +} S_COOL_FILTER; + +typedef struct s_cool_chanhandle +{ + int32_t pid; + void *buffer1; + void *buffer2; + void *channel; + int32_t demux_index; +} S_COOL_CHANHANDLE; + +struct cool_dmx +{ + int32_t opened; + int32_t fd; + uint8_t buffer[4096]; + void *buffer1; + void *buffer2; + void *channel; + void *filter; + void *device; + int32_t pid; + pthread_mutex_t mutex; + int32_t demux_index; + int32_t demux_id; + int32_t filter_num; + int32_t type; +}; +typedef struct cool_dmx dmx_t; + +typedef struct +{ + int32_t type; + uint32_t size; + int32_t unknown1; + int16_t unknown2; + int32_t unknown3; + int32_t unknown[4]; +} buffer_open_arg_t; + +typedef struct +{ + int32_t type; + int32_t unknown[2]; +} channel_open_arg_t; + +typedef struct +{ + uint32_t number; + int32_t unknown1; + int32_t unknown2; + int32_t unknown3; + int32_t unknown4; + int32_t unknown5; + int32_t unknown6; + int32_t unknown[6]; +} device_open_arg_t; + +typedef struct +{ + uint32_t length; + uint8_t filter[18]; //strange: initialization with max 18 possible but length limited to 12 + uint8_t mask[18]; + uint8_t nmask[18]; + int8_t fvernum; + int8_t crcchange; + int8_t keeprbytes; + int32_t mode; +} filter_set_t; + + +typedef enum +{ + CONTINUOUS_ACQUIRE = 0, + ONE_SHOT_ACQUIRE, + TOGGLE_ACQUIRE +} DATA_ACQUIRE_MODE; + +typedef enum +{ + DATA_ACQUIRED = 1, + CHANNEL_TIMEOUT, + CRC_ERROR, + BUF_OVERFLOW, + PES_ERROR, + COPY_DONE, + CHANNEL_INFO +} DATA_ACQUIRE_STATUS; + +typedef struct +{ + uint32_t PTSLow; + uint32_t PTSHi; +} DMX_PTS; + +typedef struct +{ + void *channel; + DATA_ACQUIRE_STATUS type; + DMX_PTS ptssnapshop; + DATA_ACQUIRE_MODE mode; + void *buf; + uint32_t len; + uint32_t num; + void *filters[64]; + void *tags[64]; +} dmx_callback_data_t; + +/* These functions are implemented in libnxp */ +extern int32_t cnxt_cbuf_open(void **handle, buffer_open_arg_t *arg, void *, void *); +extern int32_t cnxt_dmx_open(void **device, device_open_arg_t *arg, void *, void *); +extern int32_t cnxt_dmx_channel_open(void *device, void **channel, channel_open_arg_t *arg, void *callback, void *); +extern int32_t cnxt_dmx_set_filter(void *handle, filter_set_t *arg, void *); +extern int32_t cnxt_dmx_channel_suspend(void *handle, int32_t enable); + +/* Local coolapi functions */ +static void coolapi_open(void); +static int32_t coolapi_read(dmx_t *dmx, dmx_callback_data_t *data); + +static int8_t dmx_opened; +int8_t cool_kal_opened = 0; + +static void *dmx_device[MAX_COOL_DMX]; +static dmx_t cdemuxes[MAX_COOL_DMX][MAX_FILTER]; + +extern void *dvbapi_client; + +static LLIST *ll_cool_filter = NULL; +static LLIST *ll_cool_chanhandle = NULL; + +#define COOLDEMUX_FD(device, num) (('O' << 24) | ('S' << 16) | (device << 8) | num) +#define COOLDEMUX_DMX_DEV(fd) (((fd) >> 8) & 0xFF) + +static dmx_t *find_demux(int32_t fd, int32_t dmx_dev_num) +{ + if(dmx_dev_num < 0 || dmx_dev_num >= MAX_COOL_DMX) + { + cs_log("Invalid demux %d", dmx_dev_num); + return NULL; + } + + int32_t i, idx; + + idx = dmx_dev_num; + if(fd == 0) + { + for(i = 0; i < MAX_FILTER; i++) + { + if(!cdemuxes[idx][i].opened) + { + cdemuxes[idx][i].fd = COOLDEMUX_FD(dmx_dev_num, i); + cs_log_dbg(D_DVBAPI, "opening new fd: %08x", cdemuxes[idx][i].fd); + cdemuxes[idx][i].demux_index = dmx_dev_num; + return &cdemuxes[idx][i]; + } + } + cs_log_dbg(D_DVBAPI, "ERROR: no free demux found"); + return NULL; + } + + idx = COOLDEMUX_DMX_DEV(fd); + for(i = 0; i < MAX_FILTER; i++) + { + if(cdemuxes[idx][i].fd == fd) + { return &cdemuxes[idx][i]; } + } + + cs_log_dbg(D_DVBAPI, "ERROR: CANT FIND Demux %08x", fd); + + return NULL; +} + +static void coolapi_read_data(dmx_t *dmx, dmx_callback_data_t *data) +{ + if(!dmx) + { + cs_log_dbg(D_DVBAPI, "handle is NULL!"); + return; + } + + int32_t ret; + + SAFE_SETSPECIFIC(getclient, dvbapi_client); + SAFE_MUTEX_LOCK(&dmx->mutex); + memset(dmx->buffer, 0, 4096); + ret = coolapi_read(dmx, data); + SAFE_MUTEX_UNLOCK(&dmx->mutex); + if(ret > -1) + { dvbapi_process_input(dmx->demux_id, dmx->filter_num, dmx->buffer, data->len, 0); } +} + +static void dmx_callback(void *UNUSED(unk), dmx_t *dmx, int32_t type, dmx_callback_data_t *data) +{ + if(!dmx) + { + cs_log_dbg(D_DVBAPI, "wrong dmx pointer !!!"); + return; + } + + if(data != NULL) + { + switch(type) + { + case 0xE: + if(data->type == 1 && data->len > 0) + { + coolapi_read_data(dmx, data); + } + else + { cs_log_dbg(D_DVBAPI, "unknown callback data %d len %d", data->type, data->len); } + break; + default: + break; + + } + } +} + +int32_t coolapi_set_filter(int32_t fd, int32_t num, int32_t pid, uint8_t *flt, uint8_t *mask, int32_t type) +{ + dmx_t *dmx = find_demux(fd, 0); + if(!dmx) + { + cs_log_dbg(D_DVBAPI, "dmx is NULL!"); + return -1; + } + + int32_t result, channel_found = 0; + + void *channel = NULL; + + if(ll_count(ll_cool_chanhandle) > 0) + { + LL_ITER itr = ll_iter_create(ll_cool_chanhandle); + S_COOL_CHANHANDLE *handle_item; + while((handle_item = ll_iter_next(&itr))) + { + if(handle_item->demux_index == dmx->demux_index && handle_item->pid == pid) + { + channel = handle_item->channel; + channel_found = 1; + break; + } + } + } + + if(!channel) + { + buffer_open_arg_t bufarg; + int32_t uBufferSize = 8256; + memset(&bufarg, 0, sizeof(bufarg)); + + bufarg.type = 3; + bufarg.size = uBufferSize; + bufarg.unknown3 = (uBufferSize * 7) / 8; + + result = cnxt_cbuf_open(&dmx->buffer1, &bufarg, NULL, NULL); + coolapi_check_error("cnxt_cbuf_open", result); + + bufarg.type = 0; + + result = cnxt_cbuf_open(&dmx->buffer2, &bufarg, NULL, NULL); + coolapi_check_error("cnxt_cbuf_open", result); + + channel_open_arg_t chanarg; + memset(&chanarg, 0, sizeof(channel_open_arg_t)); + chanarg.type = 4; + + result = cnxt_dmx_channel_open(dmx->device, &dmx->channel, &chanarg, dmx_callback, dmx); + coolapi_check_error("cnxt_dmx_channel_open", result); + + result = cnxt_dmx_set_channel_buffer(dmx->channel, 0, dmx->buffer1); + coolapi_check_error("cnxt_dmx_set_channel_buffer", result); + + result = cnxt_dmx_channel_attach(dmx->channel, 0xB, 0, dmx->buffer2); + coolapi_check_error("cnxt_dmx_channel_attach", result); + + result = cnxt_cbuf_attach(dmx->buffer2, 2, dmx->channel); + coolapi_check_error("cnxt_cbuf_attach", result); + + result = cnxt_dmx_set_channel_pid(dmx->channel, pid); + coolapi_check_error("cnxt_dmx_set_channel_pid", result); + + result = cnxt_cbuf_flush(dmx->buffer1, 0); + coolapi_check_error("cnxt_cbuf_flush", result); + result = cnxt_cbuf_flush(dmx->buffer2, 0); + coolapi_check_error("cnxt_cbuf_flush", result); + + S_COOL_CHANHANDLE *handle_item; + if(cs_malloc(&handle_item, sizeof(S_COOL_CHANHANDLE))) + { + handle_item->pid = pid; + handle_item->channel = dmx->channel; + handle_item->buffer1 = dmx->buffer1; + handle_item->buffer2 = dmx->buffer2; + handle_item->demux_index = dmx->demux_index; + ll_append(ll_cool_chanhandle, handle_item); + } + cs_log_dbg(D_DVBAPI, "opened new channel %x", (int32_t) dmx->channel); + } + else + { + channel_found = 1; + dmx->channel = channel; + dmx->buffer1 = NULL; + dmx->buffer2 = NULL; + } + + cs_log_dbg(D_DVBAPI, "setting new filter fd=%08x demux=%d channel=%x num=%d pid=%04x flt=%x mask=%x", fd, dmx->demux_index, (int32_t) dmx->channel, num, pid, flt[0], mask[0]); + + SAFE_MUTEX_LOCK(&dmx->mutex); + + filter_set_t filter; + dmx->filter_num = num; + dmx->pid = pid; + dmx->type = type; + + memset(&filter, 0, sizeof(filter)); + filter.length = 12; + memcpy(filter.filter, flt, 16); + memcpy(filter.mask, mask, 16); + + result = cnxt_dmx_open_filter(dmx->device, &dmx->filter); + coolapi_check_error("cnxt_dmx_open_filter", result); + + result = cnxt_dmx_set_filter(dmx->filter, &filter, NULL); + coolapi_check_error("cnxt_dmx_set_filter", result); + + result = cnxt_dmx_channel_suspend(dmx->channel, 1); + coolapi_check_error("cnxt_dmx_channel_suspend", result); + result = cnxt_dmx_channel_attach_filter(dmx->channel, dmx->filter); + coolapi_check_error("cnxt_dmx_channel_attach_filter", result); + result = cnxt_dmx_channel_suspend(dmx->channel, 0); + coolapi_check_error("cnxt_dmx_channel_suspend", result); + + if(channel_found) + { + result = cnxt_dmx_channel_ctrl(dmx->channel, 0, 0); + coolapi_check_error("cnxt_dmx_channel_ctrl", result); + } + + result = cnxt_dmx_channel_ctrl(dmx->channel, 2, 0); + coolapi_check_error("cnxt_dmx_channel_ctrl", result); + + SAFE_MUTEX_UNLOCK(&dmx->mutex); + + S_COOL_FILTER *filter_item; + if(cs_malloc(&filter_item, sizeof(S_COOL_FILTER))) + { + // fill filter item + filter_item->fd = fd; + filter_item->pid = pid; + filter_item->channel = (int32_t) dmx->channel; + memcpy(filter_item->filter16, flt, 16); + memcpy(filter_item->mask16, mask, 16); + + //add filter item + ll_append(ll_cool_filter, filter_item); + } + return 0; +} + +int32_t coolapi_remove_filter(int32_t fd, int32_t num) +{ + dmx_t *dmx = find_demux(fd, 0); + if(!dmx) + { + cs_log_dbg(D_DVBAPI, "dmx is NULL!"); + return -1; + } + + if(dmx->pid <= 0) + { return -1; } + + int32_t result, filter_on_channel = 0; + + cs_log_dbg(D_DVBAPI, "removing filter fd=%08x num=%d pid=%04x on channel=%x", fd, num, dmx->pid, (int32_t) dmx->channel); + + SAFE_MUTEX_LOCK(&dmx->mutex); + + if(dmx->filter) + { + result = cnxt_dmx_channel_suspend(dmx->channel, 1); + coolapi_check_error("cnxt_dmx_channel_suspend", result); + result = cnxt_dmx_channel_detach_filter(dmx->channel, dmx->filter); + coolapi_check_error("cnxt_dmx_channel_detach_filter", result); + result = cnxt_dmx_channel_suspend(dmx->channel, 0); + coolapi_check_error("cnxt_dmx_channel_suspend", result); + result = cnxt_dmx_close_filter(dmx->filter); + coolapi_check_error("cnxt_dmx_close_filter", result); + dmx->filter = NULL; + result = cnxt_dmx_channel_ctrl(dmx->channel, 0, 0); + coolapi_check_error("cnxt_dmx_channel_ctrl", result); + } + + LL_ITER itr = ll_iter_create(ll_cool_filter); + S_COOL_FILTER *filter_item; + while((filter_item = ll_iter_next(&itr))) + { + if(filter_item->channel == (int32_t) dmx->channel) + { filter_on_channel++; } + if(filter_item->fd == fd) + { + ll_iter_remove_data(&itr); + filter_on_channel--; + } + } + + if(!filter_on_channel) + { + cs_log_dbg(D_DVBAPI, "closing channel %x", (int32_t) dmx->channel); + + itr = ll_iter_create(ll_cool_chanhandle); + S_COOL_CHANHANDLE *handle_item; + while((handle_item = ll_iter_next(&itr))) + { + if(handle_item->demux_index == dmx->demux_index && handle_item->pid == dmx->pid) + { + dmx->buffer1 = handle_item->buffer1; + dmx->buffer2 = handle_item->buffer2; + ll_iter_remove_data(&itr); + break; + } + } + + if(!dmx->buffer1 || !dmx->buffer2) + { cs_log_dbg(D_DVBAPI, "WARNING: buffer handle not found!"); } + + result = cnxt_dmx_channel_ctrl(dmx->channel, 0, 0); + coolapi_check_error("cnxt_dmx_channel_ctrl", result); + + result = cnxt_dmx_set_channel_pid(dmx->channel, 0x1FFF); + coolapi_check_error("cnxt_dmx_set_channel_pid", result); + + result = cnxt_cbuf_flush(dmx->buffer1, 0); + coolapi_check_error("cnxt_cbuf_flush", result); + + result = cnxt_cbuf_flush(dmx->buffer2, 0); + coolapi_check_error("cnxt_cbuf_flush", result); + + result = cnxt_cbuf_detach(dmx->buffer2, 2, dmx->channel); + coolapi_check_error("cnxt_cbuf_detach", result); + result = cnxt_dmx_channel_detach(dmx->channel, 0xB, 0, dmx->buffer1); + coolapi_check_error("cnxt_dmx_channel_detach", result); + + result = cnxt_dmx_channel_close(dmx->channel); + coolapi_check_error("cnxt_dmx_channel_close", result); + + result = cnxt_cbuf_close(dmx->buffer2); + coolapi_check_error("cnxt_cbuf_close", result); + + result = cnxt_cbuf_close(dmx->buffer1); + coolapi_check_error("cnxt_cbuf_close", result); + } + + if(filter_on_channel) + { + result = cnxt_dmx_channel_ctrl(dmx->channel, 2, 0); + coolapi_check_error("cnxt_dmx_channel_ctrl", result); + } + + SAFE_MUTEX_UNLOCK(&dmx->mutex); + + dmx->pid = -1; + return 0; +} + +int32_t coolapi_open_device(int32_t demux_index, int32_t demux_id) +{ + dmx_t *dmx; + + coolapi_open(); + + dmx = find_demux(0, demux_index); + if(!dmx) + { + cs_log("no free demux found"); + return 0; + } + + if(!ll_cool_filter) + { ll_cool_filter = ll_create("ll_cool_filter"); } + + if(!ll_cool_chanhandle) + { ll_cool_chanhandle = ll_create("ll_cool_chanhandle"); } + + dmx->demux_index = demux_index; + dmx->demux_id = demux_id; + dmx->pid = -1; + + dmx->device = dmx_device[demux_index]; + dmx->opened = 1; + + pthread_mutexattr_t attr; + SAFE_MUTEXATTR_INIT(&attr); + SAFE_MUTEXATTR_SETTYPE(&attr, PTHREAD_MUTEX_ERRORCHECK_NP); + SAFE_MUTEX_INIT(&dmx->mutex, &attr); + + return dmx->fd; +} + +int32_t coolapi_close_device(int32_t fd) +{ + dmx_t *dmx = find_demux(fd, 0); + if(!dmx) + { + cs_log_dbg(D_DVBAPI, "dmx is NULL!"); + return -1; + } + + cs_log_dbg(D_DVBAPI, "closing fd=%08x", fd); + dmx->opened = 0; + pthread_mutex_destroy(&dmx->mutex); + + memset(dmx, 0, sizeof(dmx_t)); + return 0; +} + +/* write cw to all demuxes in mask with passed index */ +int32_t coolapi_write_cw(int32_t mask, uint16_t *STREAMpids, int32_t count, ca_descr_t *ca_descr) +{ + int32_t i, idx = ca_descr->index; + int32_t result; + void *channel; + + cs_log_dbg(D_DVBAPI, "cw%d: mask %d index %d pid count %d", ca_descr->parity, mask, idx, count); + for(i = 0; i < count; i++) + { + int32_t pid = STREAMpids[i]; + int32_t j; + for(j = 0; j < MAX_COOL_DMX; j++) + { + if(mask & (1 << j)) + { + result = cnxt_dmx_get_channel_from_pid(dmx_device[j], pid, &channel); + if(result == 0) + { + cs_log_dbg(D_DVBAPI, "Found demux %d channel %x for pid %04x", j, (int32_t) channel, pid); + result = cnxt_dmx_set_channel_key(channel, 0, ca_descr->parity, ca_descr->cw, 8); + coolapi_check_error("cnxt_dmx_set_channel_key", result); + if(result != 0) + { + cs_log("set_channel_key failed for demux %d pid %04x", j, pid); + } + } + } + } + } + return 0; +} + +static int32_t coolapi_read(dmx_t *dmx, dmx_callback_data_t *data) +{ + if(!dmx) + { + cs_log_dbg(D_DVBAPI, "dmx is NULL!"); + return -1; + } + + int32_t result; + uint32_t done = 0, toread, len = data->len; + uint8_t *buff = &dmx->buffer[0]; + uint32_t bytes_used = 0; + + //cs_log_dbg(D_DVBAPI, "dmx channel %x pid %x len %d", (int) dmx->channel, dmx->pid, len); + + result = cnxt_cbuf_get_used(data->buf, &bytes_used); + coolapi_check_error("cnxt_cbuf_get_used", result); + if(bytes_used == 0) + { return -1; } + + result = cnxt_cbuf_read_data(data->buf, buff, 3, &done); + coolapi_check_error("cnxt_cbuf_read_data", result); + + if(done != 3) + { return -1; } + + toread = ((buff[1] << 8) | buff[2]) & 0xFFF; + if((toread + 3) > len) + { return -1; } + result = cnxt_cbuf_read_data(data->buf, buff + 3, toread, &done); + coolapi_check_error("cnxt_cbuf_read_data", result); + if(done != toread) + { return -1; } + done += 3; + + //cs_log_dbg(D_DVBAPI, "bytes read %d\n", done); + + return 0; +} + +void coolapi_open_all(void) +{ + cnxt_kal_initialize(); + cnxt_drv_init(); + cnxt_smc_init(NULL); + cool_kal_opened = 1; +} + +static void coolapi_open(void) +{ + int32_t result = 0; + device_open_arg_t devarg; + + if(!dmx_opened) + { + int32_t i; + + cs_log_dbg(D_DVBAPI, "Open Coolstream DMX API"); + cnxt_cbuf_init(NULL); + cnxt_dmx_init(NULL); + + memset(&devarg, 0, sizeof(device_open_arg_t)); + + devarg.unknown1 = 1; + devarg.unknown3 = 3; + devarg.unknown6 = 1; + for(i = 0; i < MAX_COOL_DMX; i++) + { + devarg.number = i; + result = cnxt_dmx_open(&dmx_device[i], &devarg, NULL, NULL); + coolapi_check_error("cnxt_dmx_open", result); + } + dmx_opened = 1; + } +} + +void coolapi_close_all(void) +{ + if(dmx_opened) + { + int32_t result; + int32_t i, j; + + for(i = 0; i < MAX_COOL_DMX; i++) + { + for(j = 0; j < MAX_FILTER; j++) + { + if(cdemuxes[i][j].fd > 0) + { + coolapi_remove_filter(cdemuxes[i][j].fd, cdemuxes[i][j].filter_num); + coolapi_close_device(cdemuxes[i][j].fd); + } + } + } + for(i = 0; i < MAX_COOL_DMX; i++) + { + result = cnxt_dmx_close(dmx_device[i]); + coolapi_check_error("cnxt_dmx_close", result); + dmx_device[i] = NULL; + } + } + cool_kal_opened = 0; + cnxt_kal_terminate(); + cnxt_drv_term(); +} +#elif defined(HAVE_DVBAPI) && defined(WITH_SU980) +#include "extapi/coolapi.h" +void cnxt_css_drv_init(void); +void cnxt_css_drv_term(void); +void cnxt_smc_term(void); + +int32_t cool_kal_opened = 0; +void coolapi_open_all(void) +{ + cnxt_kal_initialize(); + cnxt_css_drv_init(); + cnxt_smc_init(0); + cool_kal_opened = 1; +} + +void coolapi_close_all(void) +{ + cnxt_kal_terminate(); + cool_kal_opened = 0; +} +#endif + diff --git a/module-dvbapi-coolapi.c b/module-dvbapi-coolapi.c new file mode 100644 index 0000000..9f6ea5a --- /dev/null +++ b/module-dvbapi-coolapi.c @@ -0,0 +1,952 @@ +/* Reversed from libcoolstream.so, this comes without any warranty */ + +#define MODULE_LOG_PREFIX "dvbcool" + +#include "globals.h" + +#if defined(HAVE_DVBAPI) && (defined(WITH_SU980) || defined(WITH_COOLAPI2) || defined(WITH_COOLAPI)) +#include "extapi/coolapi.h" + +#include "module-dvbapi.h" +#include "module-dvbapi-coolapi.h" +#include "oscam-string.h" + + +#define MAX_COOL_DMX 4 + +//kronos-Plattform (Coolsterem ZEE) +//#define MAX_COOL_DMX 3 + + +#define DMX_MAX_FILTERS_PER_CHAN 16 +#define DMX_MAX_CHANNELS_PER_DMX 192 +//#define MAX_COOL_DMX_FILTERS 128 + +struct s_cool_chanhandle; + +typedef struct s_cool_filter +{ + int32_t fd; + struct s_cool_chanhandle *chanhandle; + void *filter; + int32_t filter_num; + uint8_t filter16[16]; + uint8_t mask16[16]; +} S_COOL_FILTER; + +typedef struct s_cool_chanhandle +{ + int32_t pid; + void *buffer1; // filter Cbuf 1 + void *buffer2; // filter Cbuf 2 + void *channel; + int32_t demux_index; + struct s_cool_dmxhandle *dmx_handle; + uint32_t allocated_filters; +} S_COOL_CHANHANDLE; + +typedef struct s_cool_dmxhandle +{ + void *handle; + uint32_t allocated_channels; +} S_COOL_DMXHANDLE; + +struct cool_dmx +{ + int32_t pid; + int32_t opened; + int32_t fd; + pthread_mutex_t mutex; + int32_t filter_num; + int32_t demux_id; + int32_t type; +}; +typedef struct cool_dmx dmx_t; + +typedef struct +{ + int32_t type; + uint32_t size; +#ifdef HAVE_COOLAPI2 + uint32_t ptsbufsize; +#endif + int32_t unknown1; + int16_t unknown2; + int32_t hwm; + int32_t lwm; + int32_t unknown5; + int32_t unknown6; + int32_t poolid; +#ifdef HAVE_COOLAPI2 + uint32_t unit; +#endif +} buffer_open_arg_t; + +typedef struct +{ + int32_t type; + int32_t unknown[2]; +} channel_open_arg_t; + +// Nevis : 13 +// Apollo: 13 +typedef struct +{ + uint32_t number; + int32_t unknown1; // channel source + int32_t unknown2; // mcard cap + int32_t unknown3; // descrambler type + int32_t unknown4; // support legacy NDS + int32_t unknown5; + int32_t unknown6; + int32_t unknown[6]; +} device_open_arg_t; + +typedef struct +{ + uint32_t length; + uint8_t filter[18]; //strange: initialization with max 18 possible but length limited to 12 + uint8_t mask[18]; + uint8_t nmask[18]; + int8_t fvernum; + int8_t crcchange; + int8_t keeprbytes; + int32_t mode; +} filter_set_t; + + +typedef enum +{ + CONTINUOUS_ACQUIRE = 0, + ONE_SHOT_ACQUIRE, + TOGGLE_ACQUIRE +} DATA_ACQUIRE_MODE; + +typedef enum +{ + DATA_ACQUIRED = 1, + CHANNEL_TIMEOUT, + CRC_ERROR, + BUF_OVERFLOW, + PES_ERROR, + COPY_DONE, + CHANNEL_INFO +} DATA_ACQUIRE_STATUS; + +typedef struct +{ + uint32_t PTSLow; + uint32_t PTSHi; +} DMX_PTS; + +typedef struct +{ + void *channel; + DATA_ACQUIRE_STATUS type; + // + DMX_PTS ptssnapshot; + void *buf; + uint32_t start; + // + DATA_ACQUIRE_MODE mode; + uint32_t len; + uint16_t num; + void *filters[DMX_MAX_FILTERS_PER_CHAN]; + void *tags[DMX_MAX_FILTERS_PER_CHAN]; +} dmx_callback_data_t; + +/* These functions are implemented in libnxp */ +extern int32_t cnxt_cbuf_open(void **handle, buffer_open_arg_t *arg, void *, void *); +extern int32_t cnxt_dmx_open(void **device, device_open_arg_t *arg, void *, void *); +extern int32_t cnxt_dmx_channel_open(void *device, void **channel, channel_open_arg_t *arg, void *callback, void *); +extern int32_t cnxt_dmx_set_filter(void *handle, filter_set_t *arg, void *); +extern int32_t cnxt_dmx_channel_suspend(void *handle, int32_t enable); + +/* Local coolapi functions */ +static int32_t coolapi_read(dmx_t *dmx, dmx_callback_data_t *dataa, uint8_t *buffer); + +static int8_t dmx_opened; +int32_t cool_kal_opened = 0; + +static S_COOL_DMXHANDLE dmx_handles[MAX_COOL_DMX]; +static dmx_t cdemuxes[MAX_COOL_DMX][MAX_FILTER]; +static pthread_mutex_t demux_lock = PTHREAD_MUTEX_INITIALIZER; +extern void *dvbapi_client; + +static LLIST *ll_cool_filter = NULL; +static LLIST *ll_cool_chanhandle = NULL; + +#define COOLDEMUX_FD(device, num) (('O' << 24) | ('S' << 16) | (device << 8) | num) +#define COOLDEMUX_DMX_DEV(fd) (((fd) >> 8) & 0xFF) +#define COOLDEMUX_FLT_IDX(fd) (((fd) >> 0) & 0xFF) +#define COOLDEMUX_IS_VALID_FD(fd) ((((fd) & 0xFF000000) == ('O' << 24)) && \ + (((fd) & 0x00FF0000) == ('S' << 16))) + +#define COOLDEMUX_DATA_RECEIVED 1 +#define COOLDEMUX_CHANNEL_TIMEOUT 2 +#define COOLDEMUX_CRC_ERROR 3 +#define COOLDEMUX_BUFF_OVERFLOW 4 + +static dmx_t *find_demux(int32_t fd, int32_t dmx_dev_num) +{ + if(dmx_dev_num < 0 || dmx_dev_num >= MAX_COOL_DMX) + { + cs_log("Invalid demux %d", dmx_dev_num); + return NULL; + } + + dmx_t *dmx; + int32_t i; + + if(fd == 0) // DEMUX FILTER ALLOCATE + { + for(i = 0; i < MAX_FILTER; i++) + { + dmx = &cdemuxes[dmx_dev_num][i]; + if(!dmx->opened) + { + dmx->fd = COOLDEMUX_FD(dmx_dev_num, i); + cs_log_dbg(D_DVBAPI, "opening new fd: %08x", dmx->fd); + return dmx; + } + } + cs_log_dbg(D_DVBAPI, "ERROR: no free demux found"); + return NULL; + } + + if (!COOLDEMUX_IS_VALID_FD(fd)) + { + cs_log_dbg(D_DVBAPI, "ERROR: invalid FD"); + return NULL; + } + + dmx_dev_num = COOLDEMUX_DMX_DEV(fd); + for(i = 0; i < MAX_FILTER; i++) + { + dmx = &cdemuxes[dmx_dev_num][i]; + if(dmx->fd == fd) + { + return dmx; + } + } + + cs_log_dbg(D_DVBAPI, "ERROR: CANT FIND Demux %08x", fd); + + return NULL; +} + +int32_t coolapi_get_filter_num(int32_t fd) +{ + if (!COOLDEMUX_IS_VALID_FD(fd)) + { + cs_log_dbg(D_DVBAPI, "ERROR: invalid FD"); + return -1; + } + + return cdemuxes[COOLDEMUX_DMX_DEV(fd)][COOLDEMUX_FLT_IDX(fd)].filter_num; +} + +static S_COOL_CHANHANDLE *find_chanhandle(int32_t demux_index, int32_t pid) +{ + // Find matching channel, if it exists. + if(ll_count(ll_cool_chanhandle) > 0) + { + LL_ITER itr = ll_iter_create(ll_cool_chanhandle); + S_COOL_CHANHANDLE *handle_item; + + while((handle_item = ll_iter_next(&itr))) + { + if(handle_item->demux_index == demux_index && handle_item->pid == pid) + { + return handle_item; + } + } + } + + return NULL; +} + +static int32_t remove_chanhandle(S_COOL_CHANHANDLE *handle) +{ + // Find matching channel, if it exists. + if(ll_count(ll_cool_chanhandle) > 0) + { + LL_ITER itr = ll_iter_create(ll_cool_chanhandle); + S_COOL_CHANHANDLE *handle_item; + + while((handle_item = ll_iter_next(&itr))) + { + if(handle_item == handle) + { + ll_iter_remove_data(&itr); + return 0; + } + } + } + + return -1; +} + +static S_COOL_FILTER *find_filter_by_chanhandle(S_COOL_CHANHANDLE *chanhandle, int32_t filter_num) +{ + // Find matching channel, if it exists. + if(ll_count(ll_cool_filter) > 0) + { + LL_ITER itr = ll_iter_create(ll_cool_filter); + S_COOL_FILTER *filter_item; + + while((filter_item = ll_iter_next(&itr))) + { + if(filter_item->chanhandle == chanhandle && filter_item->filter_num == filter_num) + { + return filter_item; + } + } + } + + return NULL; +} + +static S_COOL_FILTER *find_filter_by_channel(void *channel, int32_t filter_num) +{ + // Find matching channel, if it exists. + if(ll_count(ll_cool_filter) > 0) + { + LL_ITER itr = ll_iter_create(ll_cool_filter); + S_COOL_FILTER *filter_item; + + while((filter_item = ll_iter_next(&itr))) + { + if(filter_item->chanhandle && + filter_item->chanhandle->channel == channel && + filter_item->filter_num == filter_num) + { + return filter_item; + } + } + } + + return NULL; +} + +static int32_t remove_filter(S_COOL_FILTER *filter_handle) +{ + if(ll_count(ll_cool_filter) > 0) + { + LL_ITER itr = ll_iter_create(ll_cool_filter); + S_COOL_FILTER *filter_item; + + while((filter_item = ll_iter_next(&itr))) + { + if(filter_item == filter_handle) + { + ll_iter_remove_data(&itr); + return 0; + } + } + } + + return -1; +} + +static void coolapi_read_data(dmx_t *dmx, dmx_callback_data_t *data) +{ + if(!dmx) + { + cs_log_dbg(D_DVBAPI, "handle is NULL!"); + return; + } + + int32_t ret; + uint8_t buffer[4096]; + + SAFE_SETSPECIFIC(getclient, dvbapi_client); + SAFE_MUTEX_LOCK(&dmx->mutex); + memset(buffer, 0, sizeof(buffer)); + ret = coolapi_read(dmx, data, buffer); + SAFE_MUTEX_UNLOCK(&dmx->mutex); + + if(ret > -1) { + uint16_t filters = data->num; + uint16_t flt; + + for (flt = 0; flt < filters; flt++) { + uint32_t n = (uint32_t)data->tags[flt]; + S_COOL_FILTER *filter = find_filter_by_channel(data->channel, n); + + if (!filter || data->filters[flt] != filter->filter) + { + cs_log_dbg(D_DVBAPI, "filter not found in notification!!!!"); + continue; + } + + dvbapi_process_input(dmx->demux_id, n, buffer, data->len, 0); + } + } +} + +static void dmx_callback(void *channel, dmx_t *dmx, int32_t type, dmx_callback_data_t *data) +{ + if(!dmx) + { + cs_log_dbg(D_DVBAPI, "wrong dmx pointer !!!"); + return; + } + + if(data == NULL) + return; + + if (channel != data->channel) + return; + + switch(type) + { +#ifdef WITH_COOLAPI2 + case 0x11: +#else + case 0x0E: +#endif + if(data->type == COOLDEMUX_DATA_RECEIVED && data->len > 0) { + coolapi_read_data(dmx, data); + } else if(data->type == COOLDEMUX_CRC_ERROR && data->len > 0) { + cs_log_dbg(D_DVBAPI, "CRC error !!!"); + cnxt_cbuf_removed_data(data->buf, data->len); + } else if(data->type == COOLDEMUX_BUFF_OVERFLOW) { + cs_log_dbg(D_DVBAPI, "OVERFLOW !!!"); + } else { + cs_log_dbg(D_DVBAPI, "unknown callback data %d len %d", data->type, data->len); + } + break; + default: + break; + } +} + +int32_t coolapi_set_filter(int32_t fd, int32_t num, int32_t pid, uint8_t *flt, uint8_t *mask, int32_t type) +{ + dmx_t *dmx = find_demux(fd, 0); + if(!dmx) + { + cs_log_dbg(D_DVBAPI, "dmx is NULL!"); + return -1; + } + + int32_t result, channel_found; + SAFE_MUTEX_LOCK(&dmx->mutex); + + // Find matching channel, if it exists. + S_COOL_CHANHANDLE *handle_item = find_chanhandle(COOLDEMUX_DMX_DEV(fd), pid); + if(!handle_item) + { + // No channel was found, allocate one + buffer_open_arg_t bufarg; + int32_t uBufferSize = 8192 + 64; + /* Mark that we did not find any open channel on this PID */ + channel_found = 0; + + if(!cs_malloc(&handle_item, sizeof(S_COOL_CHANHANDLE))) + { + return -1; + } + + memset(&bufarg, 0, sizeof(bufarg)); + +#ifdef HAVE_COOLAPI2 + bufarg.poolid = 5 +#endif + bufarg.type = 3; + bufarg.size = uBufferSize; + bufarg.hwm = (uBufferSize * 7) / 8; + + result = cnxt_cbuf_open(&handle_item->buffer1, &bufarg, NULL, NULL); + coolapi_check_error("cnxt_cbuf_open", result); + bufarg.type = 0; + +#ifdef HAVE_COOLAPI2 + bufarg.poolid = 0 +#endif + result = cnxt_cbuf_open(&handle_item->buffer2, &bufarg, NULL, NULL); + coolapi_check_error("cnxt_cbuf_open", result); + + channel_open_arg_t chanarg; + memset(&chanarg, 0, sizeof(channel_open_arg_t)); + + chanarg.type = 4; + result = cnxt_dmx_channel_open(dmx_handles[COOLDEMUX_DMX_DEV(fd)].handle, &handle_item->channel, &chanarg, dmx_callback, dmx); + coolapi_check_error("cnxt_dmx_channel_open", result); + + result = cnxt_dmx_set_channel_buffer(handle_item->channel, 0, handle_item->buffer1); + coolapi_check_error("cnxt_dmx_set_channel_buffer", result); + + result = cnxt_dmx_channel_attach(handle_item->channel, 0xB, 0, handle_item->buffer2); + coolapi_check_error("cnxt_dmx_channel_attach", result); + + result = cnxt_cbuf_attach(handle_item->buffer2, 2, handle_item->channel); + coolapi_check_error("cnxt_cbuf_attach", result); + + result = cnxt_dmx_set_channel_pid(handle_item->channel, pid); + coolapi_check_error("cnxt_dmx_set_channel_pid", result); + + result = cnxt_cbuf_flush(handle_item->buffer1, 0); + coolapi_check_error("cnxt_cbuf_flush", result); + result = cnxt_cbuf_flush(handle_item->buffer2, 0); + coolapi_check_error("cnxt_cbuf_flush", result); + + handle_item->pid = pid; + handle_item->dmx_handle = &dmx_handles[COOLDEMUX_DMX_DEV(fd)]; + dmx_handles[COOLDEMUX_DMX_DEV(fd)].allocated_channels++; + ll_append(ll_cool_chanhandle, handle_item); + + cs_log_dbg(D_DVBAPI, "opened new channel %x", (int32_t) handle_item->channel);; + } + else + { + channel_found = 1; + } + + cs_log_dbg(D_DVBAPI, "setting new filter fd=%08x demux=%d channel=%x num=%d pid=%04x flt=%x mask=%x", fd, COOLDEMUX_DMX_DEV(fd), (int32_t) handle_item->channel, num, pid, flt[0], mask[0]); + void *filter_handle = NULL; + filter_set_t filterset; + int32_t has_filter = 0; + + S_COOL_FILTER *filter_item = find_filter_by_chanhandle(handle_item, num); + if (filter_item && type == dmx->type && pid == dmx->pid && + (memcmp(flt, filter_item->filter16, 16) || memcmp(mask, filter_item->mask16, 16))) + { + cs_log_dbg(D_DVBAPI, "setting new filter fd=%08x demux=%d channel=%x num=%d pid=%04x flt=%x mask=%x, filter exists.. modifying", fd, COOLDEMUX_DMX_DEV(fd), (int32_t) handle_item->channel, num, pid, flt[0], mask[0]); + filter_handle = filter_item->filter; + + has_filter = 1; + + memcpy(filter_item->filter16, flt, 16); + memcpy(filter_item->mask16, mask, 16); + } + else + { + dmx->pid = pid; + dmx->type = type; + dmx->filter_num = num; + result = cnxt_dmx_open_filter(dmx_handles[COOLDEMUX_DMX_DEV(fd)].handle, &filter_handle); + coolapi_check_error("cnxt_dmx_open_filter", result); + + if(!cs_malloc(&filter_item, sizeof(S_COOL_FILTER))) + { + SAFE_MUTEX_UNLOCK(&dmx->mutex); + return -1; + } + + // fill filter item + filter_item->fd = fd; + filter_item->filter = filter_handle; + filter_item->filter_num = num; + filter_item->chanhandle = handle_item; + memcpy(filter_item->filter16, flt, 16); + memcpy(filter_item->mask16, mask, 16); + + //add filter item + ll_append(ll_cool_filter, filter_item); + // increase allocated filters + handle_item->allocated_filters++; + } + + if (has_filter) + { + result = cnxt_dmx_channel_suspend(handle_item->channel, 1); + coolapi_check_error("cnxt_dmx_channel_suspend", result); + result = cnxt_dmx_channel_detach_filter(handle_item->channel, filter_handle); + coolapi_check_error("cnxt_dmx_channel_detach_filter", result); + } + + memset(&filterset, 0, sizeof(filterset)); + filterset.length = 12; + memcpy(filterset.filter, flt, 16); + memcpy(filterset.mask, mask, 16); + + result = cnxt_dmx_set_filter(filter_handle, &filterset, (void *)num); + coolapi_check_error("cnxt_dmx_set_filter", result); + + result = cnxt_dmx_channel_attach_filter(handle_item->channel, filter_handle); + coolapi_check_error("cnxt_dmx_channel_attach_filter", result); + + if (has_filter) + { + result = cnxt_dmx_channel_suspend(handle_item->channel, 0); + coolapi_check_error("cnxt_dmx_channel_suspend", result); + } + + if(!channel_found) + { + // Start channel + result = cnxt_dmx_channel_ctrl(handle_item->channel, 2, 0); + coolapi_check_error("cnxt_dmx_channel_ctrl", result); + } + + SAFE_MUTEX_UNLOCK(&dmx->mutex); + + return 0; +} + +int32_t coolapi_remove_filter(int32_t fd, int32_t num) +{ + void * channel = NULL; + void * filter = NULL; + + dmx_t *dmx = find_demux(fd, 0); + if(!dmx) + { + cs_log_dbg(D_DVBAPI, "dmx is NULL!"); + return -1; + } + + if(dmx->pid <= 0) + { return -1; } + + int32_t result; + + SAFE_MUTEX_LOCK(&dmx->mutex); + + // Find matching channel, if it exists. + S_COOL_CHANHANDLE *handle_item = find_chanhandle(COOLDEMUX_DMX_DEV(fd), dmx->pid); + if (!handle_item) + { + SAFE_MUTEX_UNLOCK(&dmx->mutex); + cs_log_dbg(D_DVBAPI, "removing filter fd=%08x num=%d pid=%04xcfailed, channel does not exist.", fd, num, dmx->pid); + return -1; + } + + channel = handle_item->channel; + cs_log_dbg(D_DVBAPI, "removing filter fd=%08x num=%d pid=%04x on channel=%p", fd, num, dmx->pid, channel); + + + S_COOL_FILTER *filter_item = find_filter_by_chanhandle(handle_item, num); + if(filter_item) + { + result = cnxt_dmx_channel_suspend(channel, 1); + coolapi_check_error("cnxt_dmx_channel_suspend", result); + result = cnxt_dmx_channel_detach_filter(channel, filter_item->filter); + coolapi_check_error("cnxt_dmx_channel_detach_filter", result); +#if 0 + result = cnxt_dmx_close_filter(filter_item->filter); + coolapi_check_error("cnxt_dmx_close_filter", result); +#endif + filter = filter_item->filter; + remove_filter(filter_item); + handle_item->allocated_filters--; + } + else + { + SAFE_MUTEX_UNLOCK(&dmx->mutex); + cs_log_dbg(D_DVBAPI, "removing filter fd=%08x num=%d pid=%04x on channel=%x failed, channel does not exist.", fd, num, dmx->pid, (int32_t) handle_item->channel); + return -1; + } + + if (!handle_item->allocated_filters) + { + result = cnxt_dmx_channel_ctrl(channel, 0, 0); + coolapi_check_error("cnxt_dmx_channel_ctrl", result); + cs_log_dbg(D_DVBAPI, "closing channel %x", (int32_t) channel); + + result = cnxt_dmx_set_channel_pid(channel, 0x1FFF); + coolapi_check_error("cnxt_dmx_set_channel_pid", result); + + result = cnxt_cbuf_flush(handle_item->buffer1, 0); + coolapi_check_error("cnxt_cbuf_flush", result); + + result = cnxt_cbuf_flush(handle_item->buffer2, 0); + coolapi_check_error("cnxt_cbuf_flush", result); + + result = cnxt_cbuf_detach(handle_item->buffer2, 2, channel); + coolapi_check_error("cnxt_cbuf_detach", result); + + result = cnxt_dmx_channel_detach(channel, 0xB, 0, handle_item->buffer1); + coolapi_check_error("cnxt_dmx_channel_detach", result); + +#if 0 + result = cnxt_dmx_channel_close(channel); + coolapi_check_error("cnxt_dmx_channel_close", result); +#endif + + result = cnxt_cbuf_close(handle_item->buffer2); + coolapi_check_error("cnxt_cbuf_close", result); + + result = cnxt_cbuf_close(handle_item->buffer1); + coolapi_check_error("cnxt_cbuf_close", result); + handle_item->channel = NULL; + handle_item->buffer1 = NULL; + handle_item->buffer2 = NULL; + remove_chanhandle(handle_item); + dmx_handles[COOLDEMUX_DMX_DEV(fd)].allocated_channels--; + dmx->pid = -1; + } else { + result = cnxt_dmx_channel_suspend(channel, 0); + coolapi_check_error("cnxt_dmx_channel_suspend", result); + channel = NULL; + } + + SAFE_MUTEX_UNLOCK(&dmx->mutex); + if (filter) { + result = cnxt_dmx_close_filter(filter); + coolapi_check_error("cnxt_dmx_close_filter", result); + } + + // COOLAPI2 - We don't want to close Channel on no ECM Filters (Makes AU / EMMs work) + if(dmx->type != TYPE_ECM) + { return 0; } + + if (channel) { + result = cnxt_dmx_channel_close(channel); + coolapi_check_error("cnxt_dmx_channel_close", result); + } + + return 0; +} + +int32_t coolapi_open_device(int32_t demux_index, int32_t demux_id) +{ + dmx_t *dmx; + + SAFE_MUTEX_LOCK(&demux_lock); + + dmx = find_demux(0, demux_index); + if(!dmx) + { + SAFE_MUTEX_UNLOCK(&demux_lock); + cs_log("no free demux found"); + return 0; + } + + if(!ll_cool_filter) + { ll_cool_filter = ll_create("ll_cool_filter"); } + + if(!ll_cool_chanhandle) + { ll_cool_chanhandle = ll_create("ll_cool_chanhandle"); } + + dmx->demux_id = demux_id; + dmx->pid = -1; + + //dmx->device = dmx_handles[demux_index].handle; + dmx->opened = 1; + + pthread_mutexattr_t attr; + SAFE_MUTEXATTR_INIT(&attr); + SAFE_MUTEXATTR_SETTYPE(&attr, PTHREAD_MUTEX_ERRORCHECK_NP); + SAFE_MUTEX_INIT(&dmx->mutex, &attr); + + SAFE_MUTEX_UNLOCK(&demux_lock); + + return dmx->fd; +} + +int32_t coolapi_close_device(int32_t fd) +{ + dmx_t *dmx = find_demux(fd, 0); + if(!dmx) + { + cs_log_dbg(D_DVBAPI, "dmx is NULL!"); + SAFE_MUTEX_UNLOCK(&demux_lock); + return -1; + } + + cs_log_dbg(D_DVBAPI, "closing fd=%08x", fd); + dmx->opened = 0; + pthread_mutex_destroy(&dmx->mutex); + + memset(dmx, 0, sizeof(dmx_t)); + return 0; +} + +/* write cw to all demuxes in mask with passed index */ +int32_t coolapi_write_cw(int32_t mask, uint16_t *STREAMpids, int32_t count, ca_descr_t *ca_descr) +{ + int32_t i; + uint32_t idx = ca_descr->index; + int32_t result; + void *channel; + + cs_log_dbg(D_DVBAPI, "cw%d: mask %d index %d pid count %d", ca_descr->parity, mask, idx, count); + for(i = 0; i < count; i++) + { + int32_t pid = STREAMpids[i]; + int32_t j; + for(j = 0; j < MAX_COOL_DMX; j++) + { + if(mask & (1 << j)) + { + result = cnxt_dmx_get_channel_from_pid(dmx_handles[j].handle, pid, &channel); + if(result == 0) + { + cs_log_dbg(D_DVBAPI, "Found demux %d channel %x for pid %04x", j, (int32_t) channel, pid); + result = cnxt_dmx_set_channel_key(channel, 0, ca_descr->parity, ca_descr->cw, 8); + coolapi_check_error("cnxt_dmx_set_channel_key", result); + if(result != 0) + { + cs_log("set_channel_key failed for demux %d pid %04x", j, pid); + } + } + } + } + } + return 0; +} + +static int32_t coolapi_read(dmx_t *dmx, dmx_callback_data_t *data, uint8_t *buffer) +{ + if(!dmx) + { + cs_log_dbg(D_DVBAPI, "dmx is NULL!"); + return -1; + } + + int32_t result; + uint32_t done = 0, toread, len = data->len; + uint32_t bytes_used = 0; + + //cs_log_dbg(D_DVBAPI, "dmx channel %x pid %x len %d", (int) dmx->channel, dmx->pid, len); + + result = cnxt_cbuf_get_used(data->buf, &bytes_used); + coolapi_check_error("cnxt_cbuf_get_used", result); + if(bytes_used == 0) + { return -1; } + + result = cnxt_cbuf_read_data(data->buf, buffer, 3, &done); + coolapi_check_error("cnxt_cbuf_read_data", result); + + if(done != 3) + { return -1; } + + toread = ((buffer[1] << 8) | buffer[2]) & 0xFFF; + if((toread + 3) > len) + { return -1; } + result = cnxt_cbuf_read_data(data->buf, buffer + 3, toread, &done); + coolapi_check_error("cnxt_cbuf_read_data", result); + if(done != toread) + { return -1; } + done += 3; + + //cs_log_dbg(D_DVBAPI, "bytes read %d\n", done); + + return 0; +} + +static void coolapi_dmx_open(void) +{ + int32_t result = 0; + device_open_arg_t devarg; + + if(!dmx_opened) + { + int32_t i; + + cs_log_dbg(D_DVBAPI, "Open Coolstream DMX API"); + + memset(&devarg, 0, sizeof(device_open_arg_t)); + + devarg.unknown1 = 1; + devarg.unknown3 = 3; + devarg.unknown6 = 1; + for(i = 0; i < MAX_COOL_DMX; i++) + { + devarg.number = i; + result = cnxt_dmx_open(&dmx_handles[i].handle, &devarg, NULL, NULL); + coolapi_check_error("cnxt_dmx_open", result); + } + dmx_opened = 1; + } +} + +static void coolapi_dmx_close(void) +{ + if(dmx_opened) + { + int32_t result; + int32_t i; + + for(i = 0; i < MAX_COOL_DMX; i++) + { + result = cnxt_dmx_close(dmx_handles[i].handle); + coolapi_check_error("cnxt_dmx_close", result); + dmx_handles[i].handle = NULL; + } + dmx_opened = 0; + } +} + +static void coolapi_start_api(void); +static void coolapi_stop_api(void); + +void coolapi_open_all(void) +{ + SAFE_MUTEX_LOCK(&demux_lock); + + coolapi_start_api(); + cool_kal_opened = 1; + coolapi_dmx_open(); + + SAFE_MUTEX_UNLOCK(&demux_lock); +} + +void coolapi_close_all(void) +{ + SAFE_MUTEX_LOCK(&demux_lock); + + if(!dmx_opened) { + SAFE_MUTEX_UNLOCK(&demux_lock); + return; + } + + int32_t i, j; + + for(i = 0; i < MAX_COOL_DMX; i++) + { + for(j = 0; j < MAX_FILTER; j++) + { + if(cdemuxes[i][j].fd > 0) + { + coolapi_remove_filter(cdemuxes[i][j].fd, cdemuxes[i][j].filter_num); + coolapi_close_device(cdemuxes[i][j].fd); + } + } + } + + coolapi_dmx_close(); + coolapi_stop_api(); + cool_kal_opened = 0; + + SAFE_MUTEX_UNLOCK(&demux_lock); +} +#endif + +#if defined(HAVE_DVBAPI) && (defined(WITH_SU980) || defined(WITH_COOLAPI2)) +#include "extapi/coolapi.h" +extern void cnxt_css_drv_init(void); +extern void cnxt_css_drv_term(void); +extern void cnxt_smc_term(void); + +static void coolapi_start_api(void) +{ + cnxt_kal_initialize(); + cnxt_css_drv_init(); + cnxt_cbuf_init(NULL); + cnxt_dmx_init(NULL); + cnxt_smc_init(NULL); +} + +static void coolapi_stop_api(void) +{ + cnxt_css_drv_term(); + cnxt_kal_terminate(); +} +#elif defined(HAVE_DVBAPI) && defined(WITH_COOLAPI) +static void coolapi_start_api(void) +{ + cnxt_kal_initialize(); + cnxt_drv_init(); + cnxt_smc_init(NULL); +} + +static void coolapi_stop_api(void) +{ + cnxt_kal_terminate(); + cnxt_drv_term(); +} +#endif diff --git a/module-dvbapi-coolapi.h b/module-dvbapi-coolapi.h new file mode 100644 index 0000000..391e662 --- /dev/null +++ b/module-dvbapi-coolapi.h @@ -0,0 +1,11 @@ +#ifndef _MODULE_COOLAPI_H_ +#define _MODULE_COOLAPI_H_ + +int32_t coolapi_set_filter(int32_t fd, int32_t num, int32_t pid, uint8_t *flt, uint8_t *mask, int32_t type); +int32_t coolapi_remove_filter(int32_t fd, int32_t num); +int32_t coolapi_open_device(int32_t demux_index, int32_t demux_id); +int32_t coolapi_close_device(int32_t fd); +int32_t coolapi_write_cw(int32_t mask, uint16_t *STREAMpids, int32_t count, ca_descr_t *ca_descr); +int32_t coolapi_get_filter_num(int32_t fd); + +#endif diff --git a/module-dvbapi-mca.c b/module-dvbapi-mca.c new file mode 100644 index 0000000..cc399e4 --- /dev/null +++ b/module-dvbapi-mca.c @@ -0,0 +1,624 @@ +#define MODULE_LOG_PREFIX "dvbmca" + +/** + * dvbapi-support for Matrix Cam Air + * + * The code is based partially on module-dvbapi-azbox. + * the (closed source) oscam that comes with the MCA is + * apparently based on svn-revision 5124-5242. + * DEMUXMATRIX is essentially the old DEMUXTYPE which + * has changed since then. + * + * We may be able to implement add/remove-filter + * by adding/removing them from DEMUXMATRIX + * and reexecute mca_write_flt + * In some cases the MCA will send ECMs for multiple PIDS + * So it is apparently able to handle multiple filters + * + * @author dirtyharry123 + */ + +#include "globals.h" + +#if defined(HAVE_DVBAPI) && defined(WITH_MCA) + +#include "extapi/openxcas/openxcas_message.h" + +#include "module-dvbapi.h" +#include "module-dvbapi-mca.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-time.h" + +int8_t dummy(void) +{ + return 0; +} +#define openxcas_start_filter_ex(...) dummy() +#define openxcas_stop_filter_ex(...) dummy() +#define openxcas_get_message mca_get_message +#define azbox_openxcas_ex_callback mca_ex_callback +#define openxcas_stop_filter(...) do { } while(0) +#define openxcas_remove_filter(...) do { } while(0) +#define openxcas_destory_cipher_ex(...) do { } while(0) + +// These variables are declared in module-dvbapi.c +extern void *dvbapi_client; +extern DEMUXTYPE demux[MAX_DEMUX]; + +// These are used in module-dvbapi.c +int32_t openxcas_provid; +uint16_t openxcas_sid, openxcas_caid, openxcas_ecm_pid; + +static uint8_t openxcas_cw[16]; +static int32_t openxcas_seq, openxcas_filter_idx, openxcas_stream_id, openxcas_cipher_idx, openxcas_busy = 0; +static uint16_t openxcas_video_pid, openxcas_audio_pid, openxcas_data_pid; +static uint8_t found[MAX_DEMUX]; + +static int fd_mdvbi = -1; +static int fd_mdesc = -1; +static int fd_mflt = -1; + +#define MCA_DVBI "/tmp/mdvbi" +#define MCA_DESC "/tmp/mdesc" +#define MCA_FLT "/tmp/mflt" + +enum eOPENXCAS_FILTER_TYPE +{ + OPENXCAS_FILTER_UNKNOWN = 0, + OPENXCAS_FILTER_ECM, + OPENXCAS_FILTER_EMM, +}; + +#define ECM_PIDS_MATRIX 20 +#define MAX_FILTER_MATRIX 10 + +struct s_ecmpids_matrix +{ + uint16_t CAID; + uint32_t PROVID; + uint16_t ECM_PID; + uint16_t EMM_PID; + int32_t irdeto_maxindex; + int32_t irdeto_curindex; + int32_t irdeto_cycle; + int32_t checked; + int32_t status; + uint8_t table; + int32_t index; + uint32_t streams; +}; + +typedef struct filter_s_matrix +{ + uint32_t fd; //FilterHandle + int32_t pidindex; + int32_t pid; + uint16_t type; + int32_t count; +} FILTERTYPE_MATRIX; + +struct s_emmpids_matrix +{ + uint16_t CAID; + uint32_t PROVID; + uint16_t PID; + uint8_t type; +}; + +typedef struct demux_s_matrix +{ + int32_t demux_index; + FILTERTYPE_MATRIX demux_fd[MAX_FILTER_MATRIX]; + int32_t ca_mask; + int32_t adapter_index; + int32_t socket_fd; + int32_t ECMpidcount; + struct s_ecmpids_matrix ECMpids[ECM_PIDS_MATRIX]; + int32_t EMMpidcount; + struct s_emmpids_matrix EMMpids[ECM_PIDS_MATRIX]; + int32_t STREAMpidcount; + uint16_t STREAMpids[ECM_PIDS_MATRIX]; + int32_t pidindex; + int32_t curindex; + int32_t tries; + int32_t max_status; + uint16_t program_number; + uint8_t lastcw[2][8]; + int32_t emm_filter; + uint8_t hexserial[8]; + struct s_reader *rdr; + char pmt_file[30]; + int32_t pmt_time; +} DEMUXMATRIX; + +static int mca_open(void) +{ + if((fd_mdvbi = open(MCA_DVBI, O_RDONLY)) < 0) + { + cs_log("can't open \"%s\" (err=%d %s)", MCA_DVBI, errno, strerror(errno)); + return -1; + } + if((fd_mdesc = open(MCA_DESC, O_WRONLY)) < 0) + { + cs_log("can't open \"%s\" (err=%d %s)", MCA_DESC, errno, strerror(errno)); + return -1; + } + if((fd_mflt = open(MCA_FLT, O_WRONLY)) < 0) + { + cs_log("can't open \"%s\" (err=%d %s)", MCA_FLT, errno, strerror(errno)); + return -1; + } + return 0; +} + +static int mca_exit(void) +{ + if((fd_mdvbi = close(fd_mdvbi)) < 0) + { + cs_log("can't close \"%s\" (err=%d %s)", MCA_DVBI, errno, strerror(errno)); + return -1; + } + if((fd_mdvbi = close(fd_mdesc)) < 0) + { + cs_log("can't close \"%s\" (err=%d %s)", MCA_DESC, errno, strerror(errno)); + return -1; + } + if((fd_mdvbi = close(fd_mflt)) < 0) + { + cs_log("can't close \"%s\" (err=%d %s)", MCA_FLT, errno, strerror(errno)); + return -1; + } + return 0; +} + +void mca_init(void) +{ + if(mca_open() < 0) + { + cs_log("could not init"); + } +} + +void mca_close(void) +{ + if(mca_exit() < 0) + { + cs_log("could not close"); + } +} + +static int mca_get_message(openxcas_msg_t *message, int timeout) +{ + int rval = -1; + struct pollfd mdvbi_poll_fd; + mdvbi_poll_fd.fd = fd_mdvbi; + mdvbi_poll_fd.events = POLLIN | POLLPRI; + rval = poll(&mdvbi_poll_fd, 1, timeout == 0 ? -1 : timeout); + if((rval >= 1) && (mdvbi_poll_fd.revents & (POLLIN | POLLPRI))) + { + rval = read(fd_mdvbi, message, 568); + } + else { rval = -1; } + return rval; +} + +static int mca_write_flt(DEMUXMATRIX *demux_matrix, int timeout) +{ + int rval = -1; + struct pollfd mflt_poll_fd; + mflt_poll_fd.fd = fd_mflt; + mflt_poll_fd.events = POLLOUT; + rval = poll(&mflt_poll_fd, 1, timeout); + if((rval >= 1) && (mflt_poll_fd.revents & POLLOUT)) + { + rval = write(fd_mflt, demux_matrix, sizeof(DEMUXMATRIX)); + } + else { rval = -1; } + return rval; +} + +static int mca_set_key(uint8_t *mca_cw) +{ + int rval = -1; + struct pollfd mdesc_poll_fd; + mdesc_poll_fd.fd = fd_mdesc; + mdesc_poll_fd.events = POLLOUT; + rval = poll(&mdesc_poll_fd, 1, 0); + if((rval >= 1) && (mdesc_poll_fd.revents & POLLOUT)) + { + rval = write(fd_mdesc, mca_cw, 16); + } + else { rval = -1; } + return rval; +} + +static int mca_capmt_remove_duplicates(uint8_t *capmt, int len) +{ + int i, newlen = len; + uint16_t descriptor_length = 0; + uint32_t program_info_length = ((capmt[4] & 0x0F) << 8) | capmt[5]; + for(i = 7; i < len; i += descriptor_length + 2) + { + descriptor_length = capmt[i + 1]; + if(capmt[i] != 0x09) { continue; } + if(!memcmp(&(capmt[i]), &(capmt[i + descriptor_length + 2]), descriptor_length + 2)) + { + memmove(&(capmt[i + descriptor_length + 2]), &(capmt[i + (2 * (descriptor_length + 2))]), newlen - (descriptor_length + 2)); + newlen -= descriptor_length + 2; + } + } + program_info_length -= (len - newlen); + capmt[4] = (uint8_t)((capmt[4] & 0xF0) | ((program_info_length & 0xF00) >> 8)); + capmt[5] = (uint8_t)(program_info_length & 0x0FF); + return newlen; +} + +static void mca_demux_convert(DEMUXTYPE *demux_orig, DEMUXMATRIX *demux_matrix) +{ + int i = 0; + memset(demux_matrix, 0, sizeof(DEMUXMATRIX)); + demux_matrix->demux_index = (int32_t)demux_orig->demux_index; + for(i = 0; i < MAX_FILTER_MATRIX; ++i) + { + demux_matrix->demux_fd[i].fd = (uint32_t) demux_orig->demux_fd[i].fd; + demux_matrix->demux_fd[i].pidindex = (int32_t) demux_orig->demux_fd[i].pidindex; + demux_matrix->demux_fd[i].pid = (int32_t) demux_orig->demux_fd[i].pid; + demux_matrix->demux_fd[i].type = (uint16_t) demux_orig->demux_fd[i].type; + demux_matrix->demux_fd[i].count = (int32_t) demux_orig->demux_fd[i].count; + } + demux_matrix->ca_mask = (int32_t)demux_orig->ca_mask; + demux_matrix->adapter_index = (int32_t)demux_orig->adapter_index; + demux_matrix->socket_fd = (int32_t)demux_orig->socket_fd; + demux_matrix->ECMpidcount = (int32_t)demux_orig->ECMpidcount; + for(i = 0; i < demux_matrix->ECMpidcount; ++i) + { + demux_matrix->ECMpids[i].CAID = (uint16_t)demux_orig->ECMpids[i].CAID; + demux_matrix->ECMpids[i].PROVID = (uint32_t)demux_orig->ECMpids[i].PROVID; + demux_matrix->ECMpids[i].ECM_PID = (uint16_t)demux_orig->ECMpids[i].ECM_PID; + demux_matrix->ECMpids[i].EMM_PID = (uint16_t)demux_orig->ECMpids[i].EMM_PID; + demux_matrix->ECMpids[i].irdeto_maxindex = (int32_t)demux_orig->ECMpids[i].irdeto_maxindex; + demux_matrix->ECMpids[i].irdeto_curindex = (int32_t)demux_orig->ECMpids[i].irdeto_curindex; + demux_matrix->ECMpids[i].irdeto_cycle = (int32_t)demux_orig->ECMpids[i].irdeto_cycle; + demux_matrix->ECMpids[i].checked = (int32_t)demux_orig->ECMpids[i].checked; + demux_matrix->ECMpids[i].status = (int32_t)demux_orig->ECMpids[i].status; + demux_matrix->ECMpids[i].table = (uint8_t)demux_orig->ECMpids[i].table; + demux_matrix->ECMpids[i].streams = (uint32_t)demux_orig->ECMpids[i].streams; + } + demux_matrix->STREAMpidcount = (int32_t)demux->STREAMpidcount; + memcpy(&demux_matrix->STREAMpids, &demux_orig->STREAMpids, demux_matrix->STREAMpidcount * sizeof(uint16_t)); + demux_matrix->pidindex = (int32_t)demux_orig->pidindex; + demux_matrix->curindex = (int32_t)demux_orig->curindex; + demux_matrix->max_status = (int32_t)demux_orig->max_status; + demux_matrix->program_number = (uint16_t)demux_orig->program_number; + memcpy(&demux_matrix->lastcw[0], &demux_orig->last_cw[0][0], 8 * sizeof(uint8_t)); + memcpy(&demux_matrix->lastcw[1], &demux_orig->last_cw[0][1], 8 * sizeof(uint8_t)); + demux_matrix->emm_filter = (int32_t)demux_orig->emm_filter; + memcpy(&demux_matrix->hexserial, &demux_orig->hexserial, 8 * sizeof(uint8_t)); + demux_matrix->rdr = (struct s_reader *)demux_orig->rdr; + memcpy(&demux_matrix->pmt_file, &demux_orig->pmt_file, 30); + demux_matrix->pmt_time = (int32_t)demux_orig->pmt_time; +} + +static void mca_ecm_callback(int32_t stream_id, uint32_t UNUSED(seq), int32_t cipher_index, uint32_t caid, uint8_t *ecm_data, int32_t l, uint16_t pid) +{ + cs_log_dbg(D_DVBAPI, "ecm callback received"); + + openxcas_stream_id = stream_id; + //openxcas_seq = seq; + //openxcas_caid = caid; + openxcas_ecm_pid = pid; + openxcas_busy = 1; + //char tmp[1024]; + + //As soon as we have received a valid CW we lock onto that CAID, otherwise we will have freezers. + if(openxcas_caid && caid && openxcas_caid != caid) + { + cs_log("ignoring caid: %04X, waiting for %04X", caid, openxcas_caid); + openxcas_busy = 0; + return; + } + + if(l < 0 || l > MAX_ECM_SIZE) + { return; } + + ECM_REQUEST *er; + if(!(er = get_ecmtask())) + { return; } + + er->srvid = openxcas_sid; + er->caid = openxcas_caid; + er->pid = openxcas_ecm_pid; + er->prid = openxcas_provid; + + er->ecmlen = l; + memcpy(er->ecm, ecm_data, er->ecmlen); + + request_cw(dvbapi_client, er, 0, 0); + + openxcas_stop_filter(openxcas_stream_id, OPENXCAS_FILTER_ECM); + openxcas_remove_filter(openxcas_stream_id, OPENXCAS_FILTER_ECM); + + openxcas_cipher_idx = cipher_index; + + struct timeb tp; + cs_ftime(&tp); + tp.time += 500; +} + + +static void mca_ex_callback(int32_t stream_id, uint32_t UNUSED(seq), int32_t idx, uint32_t pid, uint8_t *ecm_data, int32_t l) +{ + cs_log_dbg(D_DVBAPI, "ex callback received"); + + openxcas_stream_id = stream_id; + openxcas_ecm_pid = pid; + openxcas_cipher_idx = idx; // is this really cipher_idx? + + if(l < 0 || l > MAX_ECM_SIZE) + { return; } + + ECM_REQUEST *er; + if(!(er = get_ecmtask())) + { return; } + + er->srvid = openxcas_sid; + er->caid = openxcas_caid; + er->pid = openxcas_ecm_pid; + er->prid = openxcas_provid; + + er->ecmlen = l; + memcpy(er->ecm, ecm_data, er->ecmlen); + + request_cw(dvbapi_client, er, 0, 0); + + if(openxcas_stop_filter_ex(stream_id, seq, openxcas_filter_idx) < 0) + { cs_log("unable to stop ex filter"); } + else + { cs_log_dbg(D_DVBAPI, "ex filter stopped"); } + + uint8_t mask[12]; + uint8_t comp[12]; + memset(&mask, 0x00, sizeof(mask)); + memset(&comp, 0x00, sizeof(comp)); + + mask[0] = 0xff; + comp[0] = ecm_data[0] ^ 1; + + if((openxcas_filter_idx = openxcas_start_filter_ex(stream_id, seq, openxcas_ecm_pid, mask, comp, (void *)azbox_openxcas_ex_callback)) < 0) + { cs_log("unable to start ex filter"); } + else + { cs_log_dbg(D_DVBAPI, "ex filter started, pid = %x", openxcas_ecm_pid); } +} + +static void *mca_main_thread(void *cli) +{ + struct s_client *client = (struct s_client *) cli; + client->thread = pthread_self(); + SAFE_SETSPECIFIC(getclient, cli); + dvbapi_client = cli; + + struct s_auth *account; + int32_t ok = 0; + for(account = cfg.account; account; account = account->next) + { + if((ok = is_dvbapi_usr(account->usr))) + { break; } + } + cs_auth_client(client, ok ? account : (struct s_auth *)(-1), "dvbapi"); + + dvbapi_read_priority(); + + openxcas_msg_t msg; + int32_t ret; + while((ret = openxcas_get_message(&msg, 0)) >= 0) + { + cs_sleepms(10); + + if(ret) + { + openxcas_stream_id = msg.stream_id; + openxcas_seq = msg.sequence; + struct stOpenXCAS_Data data; + + switch(msg.cmd) + { + case OPENXCAS_SELECT_CHANNEL: + cs_log_dbg(D_DVBAPI, "OPENXCAS_SELECT_CHANNEL"); + + // parse channel info + struct stOpenXCASChannel chan; + memcpy(&chan, msg.buf, msg.buf_len); + + cs_log("channel change: sid = %x, vpid = %x. apid = %x", chan.service_id, chan.v_pid, chan.a_pid); + + openxcas_video_pid = chan.v_pid; + openxcas_audio_pid = chan.a_pid; + openxcas_data_pid = chan.d_pid; + break; + case OPENXCAS_START_PMT_ECM: + //FIXME: Apparently this is what the original MCA-oscam does + cs_log_dbg(D_DVBAPI, "OPENXCAS_STOP_PMT_ECM"); + memset(&demux, 0, sizeof(demux)); + memset(&found, 0, sizeof(found)); + + cs_log_dbg(D_DVBAPI, "OPENXCAS_START_PMT_ECM"); + + // parse pmt + cs_log_dump_dbg(D_DVBAPI, msg.buf + 2, msg.buf_len - 2, "capmt:"); + // For some reason the mca sometimes sends duplicate ECMpids, + // we remove them here so dvbapi will not try them twice. + int new_len = mca_capmt_remove_duplicates(msg.buf + 2, msg.buf_len - 2); + if(new_len < msg.buf_len - 2) + { cs_log_dump_dbg(D_DVBAPI, msg.buf + 2, new_len, "capmt (duplicates removed):"); } + int demux_id = dvbapi_parse_capmt(msg.buf + 2, new_len, -1, NULL, 0, 0); + + uint8_t mask[12]; + uint8_t comp[12]; + memset(&mask, 0x00, sizeof(mask)); + memset(&comp, 0x00, sizeof(comp)); + + mask[0] = 0xfe; + comp[0] = 0x80; + + if(demux_id < 0) + { + cs_log("could not parse pmt"); + break; + } + + //if ((ret = openxcas_add_filter(msg.stream_id, OPENXCAS_FILTER_ECM, 0, 0xffff, openxcas_ecm_pid, mask, comp, (void *)mca_ecm_callback)) < 0) + DEMUXMATRIX demux_matrix; + mca_demux_convert(&demux[demux_id], &demux_matrix); + if((ret = mca_write_flt(&demux_matrix, 0)) < 0) + { cs_log("unable to add ecm filter"); } + else + { + cs_log_dbg(D_DVBAPI, "ecm filter added, pid = %x, caid = %x", openxcas_ecm_pid, 0); + cs_log_dbg(D_DVBAPI, "ecm filter started"); + } + + //if (!openxcas_create_cipher_ex(msg.stream_id, openxcas_seq, 0, openxcas_ecm_pid, openxcas_video_pid, 0xffff, openxcas_audio_pid, 0xffff, 0xffff, 0xffff)) + // cs_log("failed to create cipher ex"); + //else + cs_log_dbg(D_DVBAPI, "cipher created"); + break; + case OPENXCAS_STOP_PMT_ECM: + cs_log_dbg(D_DVBAPI, "OPENXCAS_STOP_PMT_ECM"); + openxcas_stop_filter(msg.stream_id, OPENXCAS_FILTER_ECM); + openxcas_remove_filter(msg.stream_id, OPENXCAS_FILTER_ECM); + openxcas_stop_filter_ex(msg.stream_id, msg.sequence, openxcas_filter_idx); + openxcas_destory_cipher_ex(msg.stream_id, msg.sequence); + memset(&demux, 0, sizeof(demux)); + memset(&found, 0, sizeof(found)); + break; + case OPENXCAS_ECM_CALLBACK: + cs_log_dbg(D_DVBAPI, "OPENXCAS_ECM_CALLBACK"); + memcpy(&data, msg.buf, msg.buf_len); + if(!openxcas_busy) + //openxcas_filter_callback(msg.stream_id, msg.sequence, OPENXCAS_FILTER_ECM, &data); + { mca_ecm_callback(msg.stream_id, msg.sequence, data.cipher_index, data.ca_system_id, (uint8_t *)&data.buf, data.len, data.pid); } + break; + case OPENXCAS_PID_FILTER_CALLBACK: + cs_log_dbg(D_DVBAPI, "OPENXCAS_PID_FILTER_CALLBACK"); + memcpy(&data, msg.buf, msg.buf_len); + //openxcas_filter_callback_ex(msg.stream_id, msg.sequence, (struct stOpenXCAS_Data *)msg.buf); + mca_ex_callback(msg.stream_id, msg.sequence, data.cipher_index, data.pid, (uint8_t *)&data.buf, data.len); + break; + case OPENXCAS_QUIT: + cs_log_dbg(D_DVBAPI, "OPENXCAS_QUIT"); + mca_exit(); + cs_log("exited"); + return NULL; + break; + case OPENXCAS_UKNOWN_MSG: + default: + cs_log_dbg(D_DVBAPI, "OPENXCAS_UKNOWN_MSG (%d)", msg.cmd); + //cs_log_dump_dbg(D_DVBAPI, &msg, sizeof(msg), "msg dump:"); + break; + } + } + } + cs_log("invalid message"); + return NULL; +} + +void mca_send_dcw(struct s_client *client, ECM_REQUEST *er) +{ + struct s_dvbapi_priority *delayentry = dvbapi_check_prio_match(0, demux[0].pidindex, 'd'); + uint32_t delay = 0; + + cs_log_dbg(D_DVBAPI, "send_dcw"); + + if(delayentry) + { + if(delayentry->delay < 1000) + { + delay = delayentry->delay; + cs_log_dbg(D_DVBAPI, "specific delay: write cw %d ms after ecmrequest", delay); + } + } + else if (cfg.dvbapi_delayer > 0) + { + delay = cfg.dvbapi_delayer; + cs_log_dbg(D_DVBAPI, "generic delay: write cw %d ms after ecmrequest", delay); + } + + delayer(er, delay); + + if(cfg.dvbapi_ecminfo_file != 0) + { + dvbapi_write_ecminfo_file(client, er, demux[0].last_cw[0][0], demux[0].last_cw[0][1], 8); + } + + openxcas_busy = 0; + + int32_t i; + for(i = 0; i < MAX_DEMUX; i++) + { + + if(er->rc >= E_NOTFOUND && !found[i]) + { + cs_log_dbg(D_DVBAPI, "cw not found"); + + if(demux[i].pidindex == -1) + { dvbapi_try_next_caid(i, 0, 0); } + + openxcas_stop_filter(openxcas_stream_id, OPENXCAS_FILTER_ECM); + openxcas_remove_filter(openxcas_stream_id, OPENXCAS_FILTER_ECM); + + uint8_t mask[12]; + uint8_t comp[12]; + memset(&mask, 0x00, sizeof(mask)); + memset(&comp, 0x00, sizeof(comp)); + + mask[0] = 0xfe; + comp[0] = 0x80; + + DEMUXMATRIX demux_matrix; + mca_demux_convert(&demux[0], &demux_matrix); + if(mca_write_flt(&demux_matrix, 0) < 0) + { cs_log("unable to add ecm filter (0)"); } + else + { + cs_log_dbg(D_DVBAPI, "ecm filter added, pid = %x, caid = %x", openxcas_ecm_pid, 0); + cs_log_dbg(D_DVBAPI, "ecm filter started"); + } + + return; + } + else + { + found[i] = 1; + } + } + + uint8_t nullcw[8]; + memset(nullcw, 0, 8); + + int32_t n; + for(n = 0; n < 2; n++) + { + // Skip check for BISS1 - cw could be indeed zero + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + if((memcmp(er->cw + (n * 8), demux[0].last_cw[0][0], 8) && memcmp(er->cw + (n * 8), demux[0].last_cw[0][1], 8)) + && (memcmp(er->cw + (n * 8), nullcw, 8) != 0 || caid_is_biss(er->caid))) + { + memcpy(demux[0].last_cw[0][n], er->cw + (n * 8), 8); + memcpy(openxcas_cw + (n * 8), er->cw + (n * 8), 8); + if(mca_set_key(openxcas_cw) < 0) + { cs_log("set cw failed"); } + else + { cs_log_dump_dbg(D_DVBAPI, openxcas_cw, 16, "write cws to descrambler"); } + } + } +} + +void *mca_handler(struct s_client *cl, uint8_t *mbuf, int32_t module_idx) +{ + return dvbapi_start_handler(cl, mbuf, module_idx, mca_main_thread); +} + +#endif diff --git a/module-dvbapi-mca.h b/module-dvbapi-mca.h new file mode 100644 index 0000000..d5e820c --- /dev/null +++ b/module-dvbapi-mca.h @@ -0,0 +1,15 @@ +#ifndef _MODULE_MCA_H_ +#define _MODULE_MCA_H_ + +void mca_send_dcw(struct s_client *client, ECM_REQUEST *er); +void *mca_handler(struct s_client *cl, uint8_t *mbuf, int32_t module_idx); + +#if defined(HAVE_DVBAPI) && defined(WITH_MCA) +void mca_init(void); +void mca_close(void); +#else +static inline void mca_init(void) { } +static inline void mca_close(void) { } +#endif + +#endif diff --git a/module-dvbapi-stapi.c b/module-dvbapi-stapi.c new file mode 100644 index 0000000..2264bfc --- /dev/null +++ b/module-dvbapi-stapi.c @@ -0,0 +1,769 @@ +#define MODULE_LOG_PREFIX "dvbstapi" + +#include "globals.h" + +#if defined(HAVE_DVBAPI) && defined(WITH_STAPI) + +#include "module-dvbapi.h" +#include "module-dvbapi-stapi.h" +#include "oscam-client.h" +#include "oscam-files.h" +#include "oscam-string.h" +#include "oscam-time.h" + +extern int32_t exit_oscam; + +struct STDEVICE +{ + char name[20]; + uint32_t SessionHandle; + uint32_t SignalHandle; + pthread_t thread; + struct filter_s demux_fd[MAX_DEMUX][MAX_FILTER]; +}; + +struct read_thread_param +{ + int32_t id; + struct s_client *cli; +}; + +#define BUFFLEN 1024 +#define PROCDIR "/proc/stpti4_core/" + +/* These functions are in liboscam_stapi.a */ +extern uint32_t oscam_stapi_Capability(char *name); +extern char *oscam_stapi_LibVersion(void); +extern uint32_t oscam_stapi_Open(char *name, uint32_t *sessionhandle); +extern uint32_t oscam_stapi_SignalAllocate(uint32_t sessionhandle, uint32_t *signalhandle); +extern uint32_t oscam_stapi_FilterAllocate(uint32_t sessionhandle, uint32_t *filterhandle); +extern uint32_t oscam_stapi_SlotInit(uint32_t sessionhandle, uint32_t signalhandle, uint32_t *bufferhandle, uint32_t *slothandle, uint16_t pid); +extern uint32_t oscam_stapi_FilterSet(uint32_t filterhandle, uint8_t *filt, uint8_t *mask); +extern uint32_t oscam_stapi_FilterAssociate(uint32_t filterhandle, uint32_t slothandle); +extern uint32_t oscam_stapi_SlotDeallocate(uint32_t slothandle); +extern uint32_t oscam_stapi_BufferDeallocate(uint32_t bufferhandle); +extern uint32_t oscam_stapi_FilterDeallocate(uint32_t filterhandle); +extern uint32_t oscam_stapi_Close(uint32_t sessionhandle); +extern uint32_t oscam_stapi_CheckVersion(void); +extern uint32_t oscam_stapi_DescramblerAssociate(uint32_t deschandle, uint32_t slot); +extern uint32_t oscam_stapi_DescramblerDisassociate(uint32_t deschandle, uint32_t slot); +extern uint32_t oscam_stapi_DescramblerAllocate(uint32_t sessionhandle, uint32_t *deschandle); +extern uint32_t oscam_stapi_DescramblerDeallocate(uint32_t deschandle); +extern uint32_t oscam_stapi_DescramblerSet(uint32_t deschandle, int32_t parity, uint8_t *cw); +extern uint32_t oscam_stapi_SignalWaitBuffer(uint32_t signalhandle, uint32_t *qbuffer, int32_t timeout); +extern uint32_t oscam_stapi_BufferReadSection(uint32_t bufferhandle, uint32_t *filterlist, int32_t maxfilter, uint32_t *filtercount, int32_t *crc, uint8_t *buf, int32_t bufsize, uint32_t *size); +extern uint32_t oscam_stapi_SignalAbort(uint32_t signalhandle); +extern uint32_t oscam_stapi_PidQuery(char *name, uint16_t pid); +extern uint32_t oscam_stapi_BufferFlush(uint32_t bufferhandle); +extern uint32_t oscam_stapi_SlotClearPid(uint32_t slot); + +// Local functions +static void *stapi_read_thread(void *); +static int32_t stapi_do_set_filter(int32_t demux_id, FILTERTYPE *filter, uint16_t *pids, int32_t pidcount, uint8_t *filt, uint8_t *mask, int32_t dev_id); +static int32_t stapi_do_remove_filter(int32_t demux_id, FILTERTYPE *filter, int32_t dev_id); + +// These variables are declared in module-dvbapi.c +extern int32_t disable_pmt_files; +extern struct s_dvbapi_priority *dvbapi_priority; +extern DEMUXTYPE demux[MAX_DEMUX]; + +static int32_t stapi_on; +static pthread_mutex_t filter_lock; +static struct STDEVICE dev_list[PTINUM]; + +static void stapi_off(void) +{ + int32_t i; + + SAFE_MUTEX_LOCK(&filter_lock); + + cs_log("stapi shutdown"); + + disable_pmt_files = 1; + stapi_on = 0; + for(i = 0; i < MAX_DEMUX; i++) + { + dvbapi_stop_descrambling(i, 0); + } + + for(i = 0; i < PTINUM; i++) + { + if(dev_list[i].SessionHandle > 0) + { + if(dev_list[i].SignalHandle > 0) + { + oscam_stapi_SignalAbort(dev_list[i].SignalHandle); + } + pthread_cancel(dev_list[i].thread); + } + } + + SAFE_MUTEX_UNLOCK(&filter_lock); + sleep(2); + return; +} + +int32_t stapi_open(void) +{ + uint32_t ErrorCode; + + struct dirent **entries; + struct stat buf; + int32_t i = 0, n; + char pfad[512]; + stapi_on = 1; + int32_t stapi_priority = 0; + + memset(dev_list, 0, sizeof(struct STDEVICE)*PTINUM); + memset(pfad, 0, sizeof(pfad)); + + if(dvbapi_priority) + { + struct s_dvbapi_priority *p; + for(p = dvbapi_priority; p != NULL; p = p->next) + { + if(p->type == 's') + { + stapi_priority = 1; + break; + } + } + } + + if(!stapi_priority) + { + cs_log("WARNING: no PTI devices defined, stapi disabled"); + return 0; + } + + oscam_stapi_CheckVersion(); + + n = scandir(PROCDIR, &entries, NULL, NULL); + if (n==-1) + { + cs_log("scandir failed (errno=%d %s)", errno, strerror(errno)); + return 0; + } + while(n--) + { + snprintf(pfad, sizeof(pfad), "%s%s", PROCDIR, entries[n]->d_name); + if(stat(pfad, &buf) != 0) + { + free(entries[n]); + continue; + } + + if(!(buf.st_mode & S_IFDIR && strncmp(entries[n]->d_name, ".", 1) != 0)) + { + free(entries[n]); + continue; + } + + int32_t do_open = 0; + struct s_dvbapi_priority *p; + + for(p = dvbapi_priority; p != NULL; p = p->next) + { + if(p->type != 's') { continue; } + if(strcmp(entries[n]->d_name, p->devname) == 0) + { + do_open = 1; + break; + } + } + + if(!do_open) + { + cs_log("PTI: %s skipped", entries[n]->d_name); + free(entries[n]); + continue; + } + + ErrorCode = oscam_stapi_Open(entries[n]->d_name, &dev_list[i].SessionHandle); + if(ErrorCode != 0) + { + cs_log("STPTI_Open ErrorCode: %d", ErrorCode); + free(entries[n]); + continue; + } + + //debug + //oscam_stapi_Capability(entries[n]->d_name); + + cs_strncpy(dev_list[i].name, entries[n]->d_name, sizeof(dev_list[i].name)); + cs_log("PTI: %s open %d", entries[n]->d_name, i); + free(entries[n]); + + ErrorCode = oscam_stapi_SignalAllocate(dev_list[i].SessionHandle, &dev_list[i].SignalHandle); + if(ErrorCode != 0) + { cs_log("SignalAllocate: ErrorCode: %d SignalHandle: %x", ErrorCode, dev_list[i].SignalHandle); } + + i++; + if(i >= PTINUM) { break; } + } + free(entries); + + if(i == 0) { return 0; } + + SAFE_MUTEX_INIT(&filter_lock, NULL); + + for(i = 0; i < PTINUM; i++) + { + if(dev_list[i].SessionHandle == 0) + { continue; } + + struct read_thread_param *para; + if(!cs_malloc(¶, sizeof(struct read_thread_param))) + { return 0; } + para->id = i; + para->cli = cur_client(); + + int32_t ret = start_thread("stapi read", stapi_read_thread, (void *)para, &dev_list[i].thread, 1, 0); + if(ret) + { + return 0; + } + } + + atexit(stapi_off); + + cs_log("liboscam_stapi v.%s initialized", oscam_stapi_LibVersion()); + return 1; +} + +int32_t stapi_activate_section_filter(int32_t fd, uint8_t *filter, uint8_t *mask) +{ + int n = 0, ret = 852049; + while(n < 3 && ret == 852049) + { + ret = oscam_stapi_FilterSet(fd, filter, mask); + if(ret) + { + cs_log_dbg(D_DVBAPI, "Error: oscam_stapi_FilterSet; %d", ret); + cs_sleepms(50); + n++; + } + } + + if(ret) + { + cs_log("Error: stapi_activate_section_filter: %d", ret); + ret = -1; + } + return ret; +} + +int32_t stapi_set_filter(int32_t demux_id, uint16_t pid, uint8_t *filter, uint8_t *mask, int32_t num, char *pmtfile) +{ + int32_t i; + int32_t ret = -1; + char dest[1024]; + uint16_t pids[1] = { pid }; + struct s_dvbapi_priority *p; + + if(!pmtfile) + { + cs_log_dbg(D_DVBAPI, "No valid pmtfile!"); + return -1; + } + + cs_log_dbg(D_DVBAPI, "pmt file %s demux_id %d", pmtfile, demux_id); + + for(p = dvbapi_priority; p != NULL; p = p->next) + { + if(p->type != 's') { continue; } // stapi rule? + if(strcmp(pmtfile, p->pmtfile) != 0) { continue; } // same file? + + for(i = 0; i < PTINUM; i++) + { + if(strcmp(dev_list[i].name, p->devname) == 0 && p->disablefilter == 0) // check device name and if filtering is enabled! + { + cs_log_dbg(D_DVBAPI, "set stapi filter on %s for pid %04X", dev_list[i].name, pids[0]); + ret = stapi_do_set_filter(demux_id, &dev_list[i].demux_fd[demux_id][num], pids, 1, filter, mask, i); + if(ret > 0) // success + { + cs_log_dbg(D_DVBAPI, "%s filter %d set (pid %04X)", dev_list[i].name, num, pid); + return ret; // return filternumber + } + else // failure + { + cs_log_dbg(D_DVBAPI, "Error setting new filter for pid %04X on %s!", pid, dev_list[i].name); + return -1; // set return to error + } + } + } + } + + if(p == NULL) + { + cs_log_dbg(D_DVBAPI, "No matching S: line in oscam.dvbapi for pmtfile %s -> stop descrambling!", pmtfile); + snprintf(dest, sizeof(dest), "%s%s", TMPDIR, demux[demux_id].pmt_file); + unlink(dest); // remove obsolete pmt file + dvbapi_stop_descrambling(demux_id, 0); + } + return ret; +} + +int32_t stapi_remove_filter(int32_t demux_id, int32_t num, char *pmtfile) +{ + int32_t i, ret = 0; + struct s_dvbapi_priority *p; + + if(!pmtfile) { return 0; } + + for(p = dvbapi_priority; p != NULL; p = p->next) + { + if(p->type != 's') { continue; } + if(strcmp(pmtfile, p->pmtfile) != 0) + { continue; } + + for(i = 0; i < PTINUM; i++) + { + if(strcmp(dev_list[i].name, p->devname) == 0 && p->disablefilter == 0) + { + ret = stapi_do_remove_filter(demux_id, &dev_list[i].demux_fd[demux_id][num], i); + } + } + } + if(ret == 1) + { + cs_log_dbg(D_DVBAPI, "filter %d removed", num); + } + else + { + cs_log_dbg(D_DVBAPI, "Error: filter %d was not removed!", num); + } + return ret; +} + +static uint32_t check_slot(int32_t dev_id, uint32_t checkslot, FILTERTYPE *skipfilter) +{ + int32_t d, f, l; + for(d = 0; d < MAX_DEMUX; d++) + { + for(f = 0; f < MAX_FILTER; f++) + { + if(skipfilter && &dev_list[dev_id].demux_fd[d][f] == skipfilter) + { continue; } + for(l = 0; l < dev_list[dev_id].demux_fd[d][f].NumSlots; l++) + { + if(checkslot == dev_list[dev_id].demux_fd[d][f].SlotHandle[l]) + { + return dev_list[dev_id].demux_fd[d][f].BufferHandle[l]; + } + } + } + } + return 0; +} + + +static int32_t stapi_do_set_filter(int32_t demux_id, FILTERTYPE *filter, uint16_t *pids, int32_t pidcount, uint8_t *filt, uint8_t *mask, int32_t dev_id) +{ + uint32_t FilterAssociateError = 0; + int32_t k, ret = 0; + + filter->fd = 0; + filter->BufferHandle[0] = 0; + filter->SlotHandle[0] = 0; + + if(dev_list[dev_id].SessionHandle == 0) { return 0; } + + uint32_t FilterAllocateError = oscam_stapi_FilterAllocate(dev_list[dev_id].SessionHandle, &filter->fd); + + if(FilterAllocateError != 0) + { + cs_log("FilterAllocate problem"); + filter->fd = 0; + return 0; + } + + for(k = 0; k < pidcount; k++) + { + uint16_t pid = pids[k]; + + uint32_t QuerySlot = oscam_stapi_PidQuery(dev_list[dev_id].name, pid); + int32_t SlotInit = 1; + + if(QuerySlot != 0) + { + uint32_t checkslot = check_slot(dev_id, QuerySlot, NULL); + if(checkslot > 0) + { + filter->SlotHandle[k] = QuerySlot; + filter->BufferHandle[k] = checkslot; + SlotInit = 0; + } + else + { + cs_log("overtake: clear pid: %d", oscam_stapi_SlotClearPid(QuerySlot)); + SlotInit = 1; + } + } + + if(SlotInit == 1) + { + ret = oscam_stapi_SlotInit(dev_list[dev_id].SessionHandle, dev_list[dev_id].SignalHandle, &filter->BufferHandle[k], &filter->SlotHandle[k], pid); + } + + FilterAssociateError = oscam_stapi_FilterAssociate(filter->fd, filter->SlotHandle[k]); + filter->NumSlots++; + } + + uint32_t FilterSetError = oscam_stapi_FilterSet(filter->fd, filt, mask); + + if(ret || FilterAllocateError || FilterAssociateError || FilterSetError) + { + cs_log("set_filter: dev: %d FAl: %d FAs: %d FS: %d", + dev_id, FilterAllocateError, FilterAssociateError, FilterSetError); + stapi_do_remove_filter(demux_id, filter, dev_id); + return 0; + } + else + { + return filter->fd; // return fd of filter + } +} + +static int32_t stapi_do_remove_filter(int32_t UNUSED(demux_id), FILTERTYPE *filter, int32_t dev_id) +{ + if(filter->fd == 0) { return 0; } + + uint32_t BufferDeallocateError = 0, SlotDeallocateError = 0; + + if(dev_list[dev_id].SessionHandle == 0) { return 0; } + + int32_t k; + for(k = 0; k < filter->NumSlots; k++) + { + uint32_t checkslot = check_slot(dev_id, filter->SlotHandle[k], filter); + + if(checkslot == 0) + { + BufferDeallocateError = oscam_stapi_BufferDeallocate(filter->BufferHandle[k]); + SlotDeallocateError = oscam_stapi_SlotDeallocate(filter->SlotHandle[k]); + } + } + uint32_t FilterDeallocateError = oscam_stapi_FilterDeallocate(filter->fd); + + memset(filter, 0, sizeof(FILTERTYPE)); + + if(BufferDeallocateError || SlotDeallocateError || FilterDeallocateError) + { + cs_log("remove_filter: dev: %d BD: %d SD: %d FDe: %d", + dev_id, BufferDeallocateError, SlotDeallocateError, FilterDeallocateError); + return 0; + } + else + { + return 1; + } +} + +static void stapi_cleanup_thread(void *dev) +{ + int32_t dev_index = (int)dev; + + int32_t ErrorCode; + ErrorCode = oscam_stapi_Close(dev_list[dev_index].SessionHandle); + + cs_log("liboscam_stapi: PTI %s closed - %d\n", dev_list[dev_index].name, ErrorCode); + dev_list[dev_index].SessionHandle = 0; +} + +static void *stapi_read_thread(void *sparam) +{ + int32_t dev_index, ErrorCode, i, j, CRCValid; + uint32_t QueryBufferHandle = 0, DataSize = 0; + uint8_t buf[BUFFLEN]; + + struct read_thread_param *para = sparam; + dev_index = para->id; + + SAFE_SETSPECIFIC(getclient, para->cli); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + pthread_cleanup_push(stapi_cleanup_thread, (void *) dev_index); + + int32_t error_count = 0; + + while(!exit_oscam) + { + QueryBufferHandle = 0; + ErrorCode = oscam_stapi_SignalWaitBuffer(dev_list[dev_index].SignalHandle, &QueryBufferHandle, 1000); + + switch(ErrorCode) + { + case 0: // NO_ERROR: + break; + case 852042: // ERROR_SIGNAL_ABORTED + cs_log("Caught abort signal"); + pthread_exit(NULL); + break; + case 11: // ERROR_TIMEOUT: + //cs_log("timeout %d", dev_index); + //TODO: if pidindex == -1 try next + continue; + break; + default: + if(QueryBufferHandle != 0) + { + cs_log("SignalWaitBuffer error: %d", ErrorCode); + oscam_stapi_BufferFlush(QueryBufferHandle); + continue; + } + cs_log("SignalWaitBuffer: index %d ErrorCode: %d - QueryBuffer: %x", dev_index, ErrorCode, QueryBufferHandle); + error_count++; + if(error_count > 10) + { + cs_log("Too many errors in reader thread %d, quitting.", dev_index); + pthread_exit(NULL); + } + continue; + break; + } + + uint32_t NumFilterMatches = 0; + int32_t demux_id = 0, filter_num = 0; + DataSize = 0; + uint32_t k; + + uint32_t MatchedFilterList[10]; + ErrorCode = oscam_stapi_BufferReadSection(QueryBufferHandle, MatchedFilterList, 10, &NumFilterMatches, &CRCValid, buf, BUFFLEN, &DataSize); + + if(ErrorCode != 0) + { + cs_log("BufferRead: index: %d ErrorCode: %d", dev_index, ErrorCode); + cs_sleepms(1000); + continue; + } + + if(DataSize <= 0) + { continue; } + + SAFE_MUTEX_LOCK(&filter_lock); // don't use cs_lock() here; multiple threads using same s_client struct + for(k = 0; k < NumFilterMatches; k++) + { + for(i = 0; i < MAX_DEMUX; i++) + { + for(j = 0; j < MAX_FILTER; j++) + { + if(dev_list[dev_index].demux_fd[i][j].fd == MatchedFilterList[k]) + { + demux_id = i; + filter_num = j; + + dvbapi_process_input(demux_id, filter_num, buf, DataSize, 0); + } + } + } + } + SAFE_MUTEX_UNLOCK(&filter_lock); + } + + pthread_cleanup_pop(0); + + return NULL; +} + +#define ASSOCIATE 1 +#define DISASSOCIATE 0 + +#define DE_START 0 +#define DE_STOP 1 + +static void stapi_DescramblerAssociate(int32_t demux_id, uint16_t pid, int32_t mode, int32_t n) +{ + uint32_t Slot = 0; + int32_t ErrorCode = 0; + + if(dev_list[n].SessionHandle == 0) { return; } + + Slot = oscam_stapi_PidQuery(dev_list[n].name, pid); + if(!Slot) { return; } + + if(demux[demux_id].DescramblerHandle[n] == 0) { return; } + + if(mode == ASSOCIATE) + { + int32_t k; + for(k = 0; k < SLOTNUM; k++) + { + if(demux[demux_id].slot_assc[n][k] == Slot) + { + return; + } + } + + ErrorCode = oscam_stapi_DescramblerAssociate(demux[demux_id].DescramblerHandle[n], Slot); + cs_log_dbg(D_DVBAPI, "set pid %04x on %s", pid, dev_list[n].name); + + if(ErrorCode != 0) + { cs_log("DescramblerAssociate %d", ErrorCode); } + + for(k = 0; k < SLOTNUM; k++) + { + if(demux[demux_id].slot_assc[n][k] == 0) + { + demux[demux_id].slot_assc[n][k] = Slot; + break; + } + } + } + else + { + ErrorCode = oscam_stapi_DescramblerDisassociate(demux[demux_id].DescramblerHandle[n], Slot); + if(ErrorCode != 0) + { cs_log_dbg(D_DVBAPI, "DescramblerDisassociate %d", ErrorCode); } + + cs_log_dbg(D_DVBAPI, "unset pid %04x on %s", pid, dev_list[n].name); + + int32_t k; + for(k = 0; k < SLOTNUM; k++) + { + if(demux[demux_id].slot_assc[n][k] == Slot) + { + demux[demux_id].slot_assc[n][k] = 0; + return; + } + } + } + + return; +} + +static void stapi_startdescrambler(int32_t demux_id, int32_t dev_index, int32_t mode) +{ + int32_t ErrorCode; + + if(mode == DE_START && demux[demux_id].DescramblerHandle[dev_index] == 0) + { + uint32_t DescramblerHandle = 0; + ErrorCode = oscam_stapi_DescramblerAllocate(dev_list[dev_index].SessionHandle, &DescramblerHandle); + if(ErrorCode != 0) + { + cs_log("DescramblerAllocate: ErrorCode: %d SignalHandle: %x", ErrorCode, dev_list[dev_index].SignalHandle); + return; + } + + demux[demux_id].DescramblerHandle[dev_index] = DescramblerHandle; + } + + if(mode == DE_STOP && demux[demux_id].DescramblerHandle[dev_index] > 0) + { + ErrorCode = oscam_stapi_DescramblerDeallocate(demux[demux_id].DescramblerHandle[dev_index]); + + if(ErrorCode != 0) + { cs_log("DescramblerDeallocate: ErrorCode: %d", ErrorCode); } + + demux[demux_id].DescramblerHandle[dev_index] = 0; + } + + return; +} + +int32_t stapi_set_pid(int32_t demux_id, int32_t UNUSED(num), uint32_t idx, uint16_t UNUSED(pid), char *UNUSED(pmtfile)) +{ + int32_t n; + + if(idx == INDEX_INVALID) + { + for(n = 0; n < PTINUM; n++) + { + if(demux[demux_id].DescramblerHandle[n] == 0) { continue; } + + cs_log_dbg(D_DVBAPI, "stop descrambling PTI: %s", dev_list[n].name); + stapi_startdescrambler(demux_id, n, DE_STOP); + memset(demux[demux_id].slot_assc[n], 0, sizeof(demux[demux_id].slot_assc[n])); + } + } + + return 1; +} + +int32_t stapi_write_cw(int32_t demux_id, uint8_t *cw, uint16_t *STREAMpids, int32_t STREAMpidcount, char *pmtfile) +{ + int32_t ErrorCode, l, n, k; + uint8_t nullcw[8]; + memset(nullcw, 0, 8); + char *text[] = { "even", "odd" }; + + if(!pmtfile) { return 0; } + + for(n = 0; n < PTINUM; n++) + { + if(dev_list[n].SessionHandle == 0) { continue; } + if(demux[demux_id].DescramblerHandle[n] == 0) + { + struct s_dvbapi_priority *p; + + for(p = dvbapi_priority; p != NULL; p = p->next) + { + if(p->type != 's') { continue; } + if(strcmp(pmtfile, p->pmtfile) != 0) + { continue; } + + if(strcmp(dev_list[n].name, p->devname) == 0) + { + cs_log_dbg(D_DVBAPI, "start descrambling PTI: %s", dev_list[n].name); + stapi_startdescrambler(demux_id, n, DE_START); + } + } + } + + if(demux[demux_id].DescramblerHandle[n] == 0) { continue; } + + for(k = 0; k < STREAMpidcount; k++) + { + stapi_DescramblerAssociate(demux_id, STREAMpids[k], ASSOCIATE, n); + } + } + + int32_t pidnum = demux[demux_id].pidindex; // get current pidindex used for descrambling + uint32_t idx = demux[demux_id].ECMpids[pidnum].index[0]; + + if(idx == INDEX_INVALID) // if no indexer for this pid get one! + { + idx = dvbapi_get_desc_index(demux_id, pidnum, 0); + cs_log_dbg(D_DVBAPI, "Demuxer %d PID: %d CAID: %04X ECMPID: %04X is using index %d", demux_id, pidnum, + demux[demux_id].ECMpids[pidnum].CAID, demux[demux_id].ECMpids[pidnum].ECM_PID, idx); + } + + for(l = 0; l < 2; l++) + { + // Skip check for BISS1 - cw could be indeed zero + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + if(memcmp(cw + (l * 8), demux[demux_id].last_cw[0][l], 8) != 0 + && (memcmp(cw + (l * 8), nullcw, 8) != 0 || caid_is_biss(demux[demux_id].ECMpids[pidnum].CAID))) + { + for(n = 0; n < PTINUM; n++) + { + if(demux[demux_id].DescramblerHandle[n] == 0) { continue; } + + ErrorCode = oscam_stapi_DescramblerSet(demux[demux_id].DescramblerHandle[n], l, cw + (l * 8)); + if(ErrorCode != 0) + { cs_log("DescramblerSet: ErrorCode: %d", ErrorCode); } + + memcpy(demux[demux_id].last_cw[0][l], cw + (l * 8), 8); + cs_log_dbg(D_DVBAPI, "write cw %s index: %d %s", text[l], demux_id, dev_list[n].name); + } + } + } + + return 1; +} + +// Needed for compatability with liboscam_stapi.a +#undef cs_log +void cs_log(const char *fmt, ...) +{ + va_list params; + char log_txt[512]; + + va_start(params, fmt); + vsnprintf(log_txt, sizeof(log_txt), fmt, params); + va_end(params); + + cs_log_txt(MODULE_LOG_PREFIX, "%s", log_txt); +} + +#endif diff --git a/module-dvbapi-stapi.h b/module-dvbapi-stapi.h new file mode 100644 index 0000000..395c8a0 --- /dev/null +++ b/module-dvbapi-stapi.h @@ -0,0 +1,26 @@ +#ifndef MODULE_DVBAPI_STAPI_H_ +#define MODULE_DVBAPI_STAPI_H_ + +int32_t stapi_open(void); +int32_t stapi_set_filter(int32_t demux_id, uint16_t pid, uint8_t *filter, uint8_t *mask, int32_t num, char *pmtfile); +int32_t stapi_remove_filter(int32_t demux_id, int32_t num, char *pmtfile); +int32_t stapi_set_pid(int32_t demux_id, int32_t num, uint32_t idx, uint16_t pid, char *pmtfile); +int32_t stapi_write_cw(int32_t demux_id, uint8_t *cw, uint16_t *, int32_t, char *pmtfile); +int32_t stapi_activate_section_filter(int32_t fd, uint8_t *filter, uint8_t *mask); + +#ifdef WITH_STAPI5 + +struct STDEVICE +{ + char name[20]; + uint32_t SessionHandle; + uint32_t SignalHandle; + pthread_t thread; + struct filter_s demux_fd[MAX_DEMUX][MAX_FILTER]; +}; + +extern struct STDEVICE dev_list[PTINUM]; + +#endif + +#endif diff --git a/module-dvbapi-stapi5.c b/module-dvbapi-stapi5.c new file mode 100644 index 0000000..76f0a6f --- /dev/null +++ b/module-dvbapi-stapi5.c @@ -0,0 +1,749 @@ +#define MODULE_LOG_PREFIX "dvbstapi" + +#include "globals.h" + +#if defined(HAVE_DVBAPI) && defined(WITH_STAPI5) + +#include "module-dvbapi.h" +#include "module-dvbapi-stapi.h" +#include "oscam-client.h" +#include "oscam-files.h" +#include "oscam-string.h" +#include "oscam-time.h" + +extern int32_t exit_oscam; + +#define MAX_STREAMPIDS MAX_DEMUX + 1 + +struct tTkdDescInfo +{ + int STREAMPIDs[MAX_STREAMPIDS]; + uint32_t key_hndl; + uint32_t iv_hndl; + uint32_t path_hndl; +}; + +struct read_thread_param +{ + int32_t id; + struct s_client *cli; +}; + +#define BUFFLEN 1024 +#define PROCDIR "/proc/STAPI/stpti/" +#define MAX_DESCRAMBLER 16 +#define TKD_MAX_NUMBER 1 + +/* These functions are in liboscam_stapi5.a */ +extern char *oscam_stapi5_LibVersion(void); +extern uint32_t oscam_stapi5_Open(char *name, uint32_t *sessionhandle); +extern uint32_t oscam_stapi5_SignalAllocate(uint32_t sessionhandle, uint32_t *signalhandle); +extern uint32_t oscam_stapi5_FilterAllocate(uint32_t sessionhandle, uint32_t *filterhandle); +extern uint32_t oscam_stapi5_SlotInit(uint32_t sessionhandle, uint32_t signalhandle, uint32_t *bufferhandle, uint32_t *slothandle, uint16_t pid); +extern uint32_t oscam_stapi5_FilterSet(uint32_t filterhandle, uint8_t *filt, uint8_t *mask); +extern uint32_t oscam_stapi5_FilterAssociate(uint32_t filterhandle, uint32_t slothandle); +extern uint32_t oscam_stapi5_SlotDeallocate(uint32_t slothandle); +extern uint32_t oscam_stapi5_BufferDeallocate(uint32_t bufferhandle); +extern uint32_t oscam_stapi5_FilterDeallocate(uint32_t filterhandle, uint32_t bufferhandle, uint32_t slothandle); +extern uint32_t oscam_stapi5_Close(uint32_t sessionhandle); +extern const char *oscam_stapi5_GetRevision(void); +extern uint32_t oscam_stapi5_SignalWaitBuffer(uint32_t signalhandle, uint32_t *qbuffer, int32_t timeout); +extern uint32_t oscam_stapi5_SignalDisassociateBuffer(uint32_t signalhandle, uint32_t bufferhandle); +extern uint32_t oscam_stapi5_BufferReadSection(uint32_t bufferhandle, uint32_t *filterlist, int32_t maxfilter, uint32_t *filtercount, int32_t *crc, uint8_t *buf, int32_t bufsize, uint32_t *size); +extern uint32_t oscam_stapi5_SignalAbort(uint32_t signalhandle); +extern uint32_t oscam_stapi5_PidQuery(char *name, uint16_t pid); +extern uint32_t oscam_stapi5_BufferFlush(uint32_t bufferhandle); +extern uint32_t oscam_stapi5_SlotClearPid(uint32_t slot); +extern uint32_t oscam_stapi5_SlotUnlink(uint32_t slot); + +extern const char *oscam_sttkd_GetRevision(void); +extern uint32_t oscam_sttkd_Open(char *name, uint32_t *sessionhandle); +extern uint32_t oscam_sttkd_Close(uint32_t tkdhandle); +extern uint32_t oscam_sttkd_Allocate(uint32_t tkdhandle, uint8_t cp, uint32_t *pathhandle, uint32_t *keyhandle); +extern uint32_t oscam_sttkd_Associate(char *name, uint32_t pathhandle, uint16_t Pid); +extern uint32_t oscam_sttkd_Deallocate(uint32_t pathhandle, uint32_t Keyhandle); +extern uint32_t oscam_sttkd_Disassociate(char *name, uint16_t pid); +extern uint32_t oscam_sttkd_KeyWrite(uint32_t keyhandle, uint8_t pol, const uint8_t *cw); + +// Local functions +static void *stapi_read_thread(void *); +static int32_t stapi_do_set_filter(int32_t demux_id, FILTERTYPE *filter, uint16_t *pids, int32_t pidcount, uint8_t *filt, uint8_t *mask, int32_t dev_id); +static int32_t stapi_do_remove_filter(int32_t demux_id, FILTERTYPE *filter, int32_t dev_id); + +// These variables are declared in module-dvbapi.c +extern int32_t disable_pmt_files; +extern struct s_dvbapi_priority *dvbapi_priority; +extern DEMUXTYPE demux[MAX_DEMUX]; + +static int32_t stapi_on; +static pthread_mutex_t filter_lock; +struct STDEVICE dev_list[PTINUM]; + +struct tTkdDescInfo tkd_desc_info[MAX_DESCRAMBLER]; + +static char TKD_DeviceName[TKD_MAX_NUMBER][16]; +static uint32_t TKDHandle[TKD_MAX_NUMBER]; + + +static void stapi_off(void) +{ + int32_t i; + uint32_t ErrorCode; + + SAFE_MUTEX_LOCK(&filter_lock); + + cs_log("stapi shutdown"); + + disable_pmt_files = 1; + stapi_on = 0; + for(i = 0; i < MAX_DESCRAMBLER; i++) + { + dvbapi_stop_descrambling(i, 0); + + if (tkd_desc_info[i].path_hndl != 0) + { + ErrorCode = oscam_sttkd_Deallocate(tkd_desc_info[i].path_hndl, tkd_desc_info[i].key_hndl); + if (ErrorCode != 0) + { cs_log("oscam_sttkd_Deallocate faild! ErrorCode: %d", ErrorCode); } + } + } + + uint8_t TKD_InstanceID = 0; + for(TKD_InstanceID = 0; TKD_InstanceID < TKD_MAX_NUMBER; TKD_InstanceID++) + { + ErrorCode = oscam_sttkd_Close(TKDHandle[TKD_InstanceID]); + if(ErrorCode != 0) + { cs_log("oscam_sttkd_Close: ErrorCode: %d TKDHandle: 0x%08X", ErrorCode, TKDHandle[TKD_InstanceID]); } + } + + for(i = 0; i < PTINUM; i++) + { + if(dev_list[i].SessionHandle > 0) + { + if(dev_list[i].SignalHandle > 0) + { + oscam_stapi5_SignalAbort(dev_list[i].SignalHandle); + } + pthread_cancel(dev_list[i].thread); + } + } + + SAFE_MUTEX_UNLOCK(&filter_lock); + sleep(2); + return; +} + +int32_t stapi_open(void) +{ + uint32_t ErrorCode; + struct dirent **entries; + struct stat buf; + int32_t i = 0, n; + char pfad[512]; + stapi_on = 1; + int32_t stapi_priority = 0; + + memset(dev_list, 0, sizeof(struct STDEVICE)*PTINUM); + memset(pfad, 0, sizeof(pfad)); + + if(dvbapi_priority) + { + struct s_dvbapi_priority *p; + for(p = dvbapi_priority; p != NULL; p = p->next) + { + if(p->type == 's') + { + stapi_priority = 1; + break; + } + } + } + + if(!stapi_priority) + { + cs_log("WARNING: no PTI devices defined, stapi disabled"); + return 0; + } + + oscam_stapi5_GetRevision(); + oscam_sttkd_GetRevision(); + + n = scandir(PROCDIR, &entries, NULL, NULL); + if (n==-1) + { + cs_log("scandir failed (errno=%d %s)", errno, strerror(errno)); + return 0; + } + while(n--) + { + snprintf(pfad, sizeof(pfad), "%s%s", PROCDIR, entries[n]->d_name); + if(stat(pfad, &buf) != 0) + { + free(entries[n]); + continue; + } + + if(!(buf.st_mode & S_IFDIR && strncmp(entries[n]->d_name, ".", 1) != 0)) + { + free(entries[n]); + continue; + } + + int32_t do_open = 0; + struct s_dvbapi_priority *p; + + for(p = dvbapi_priority; p != NULL; p = p->next) + { + if(p->type != 's') { continue; } + if(strcmp(entries[n]->d_name, p->devname) == 0) + { + do_open = 1; + break; + } + } + + if(!do_open) + { + cs_log("PTI: %s skipped", entries[n]->d_name); + free(entries[n]); + continue; + } + + ErrorCode = oscam_stapi5_Open(entries[n]->d_name, &dev_list[i].SessionHandle); + if(ErrorCode != 0) + { + cs_log("STPTI_Open ErrorCode: %d", ErrorCode); + free(entries[n]); + continue; + } + + //debug + //oscam_stapi_Capability(entries[n]->d_name); + + cs_strncpy(dev_list[i].name, entries[n]->d_name, sizeof(dev_list[i].name)); + cs_log("PTI: %s open %d", entries[n]->d_name, i); + free(entries[n]); + + ErrorCode = oscam_stapi5_SignalAllocate(dev_list[i].SessionHandle, &dev_list[i].SignalHandle); + if(ErrorCode != 0) + { cs_log("SignalAllocate: ErrorCode: %d SignalHandle: %x", ErrorCode, dev_list[i].SignalHandle); } + + i++; + if(i >= PTINUM) { break; } + } + free(entries); + + if(i == 0) { return 0; } + + uint8_t TKD_InstanceID = 0; + memset(&tkd_desc_info, 0, sizeof(tkd_desc_info[0]) * MAX_DESCRAMBLER); + + for(TKD_InstanceID = 0; TKD_InstanceID < TKD_MAX_NUMBER; TKD_InstanceID++) + { + /* Generate the device name dynamically based upon the Instance ID */ + snprintf(TKD_DeviceName[TKD_InstanceID], sizeof(TKD_DeviceName), "TKD_%02d", TKD_InstanceID); + + ErrorCode = oscam_sttkd_Open(TKD_DeviceName[TKD_InstanceID], &TKDHandle[TKD_InstanceID]); + if(ErrorCode != 0) + cs_log("oscam_sttkd_Open: DeviceName: %s, TKDHandle: 0x%08X, ErrorCode: %d", TKD_DeviceName[TKD_InstanceID], TKDHandle[TKD_InstanceID], ErrorCode); + } + + SAFE_MUTEX_INIT(&filter_lock, NULL); + + for(i = 0; i < PTINUM; i++) + { + if(dev_list[i].SessionHandle == 0) + { continue; } + + struct read_thread_param *para; + if(!cs_malloc(¶, sizeof(struct read_thread_param))) + { return 0; } + para->id = i; + para->cli = cur_client(); + + int32_t ret = start_thread("stapi read", stapi_read_thread, (void *)para, &dev_list[i].thread, 1, 0); + if(ret) + { + return 0; + } + } + + atexit(stapi_off); + + cs_log("liboscam_stapi5 v.%s initialized", oscam_stapi5_LibVersion()); + return 1; +} + +int32_t stapi_activate_section_filter(int32_t fd, uint8_t *filter, uint8_t *mask) +{ + uint32_t ErrorCode; + + ErrorCode = oscam_stapi5_FilterSet(fd, filter, mask); + if(ErrorCode != 0) + { + cs_log_dbg(D_DVBAPI, "Error: oscam_stapi5_FilterSet; %d", ErrorCode); + return -1; + } + + return ErrorCode; +} + +int32_t stapi_set_filter(int32_t demux_id, uint16_t pid, uint8_t *filter, uint8_t *mask, int32_t num, char *pmtfile) +{ + int32_t i; + int32_t ret = -1; + char dest[1024]; + uint16_t pids[1] = { pid }; + struct s_dvbapi_priority *p; + + if(!pmtfile) + { + cs_log_dbg(D_DVBAPI, "No valid pmtfile!"); + return -1; + } + + cs_log_dbg(D_DVBAPI, "pmt file %s demux_id %d", pmtfile, demux_id); + + for(p = dvbapi_priority; p != NULL; p = p->next) + { + if(p->type != 's') { continue; } // stapi rule? + if(strcmp(pmtfile, p->pmtfile) != 0) { continue; } // same file? + + for(i = 0; i < PTINUM; i++) + { + if(strcmp(dev_list[i].name, p->devname) == 0 && p->disablefilter == 0) // check device name and if filtering is enabled! + { + cs_log_dbg(D_DVBAPI, "set stapi filter on %s for pid %04X", dev_list[i].name, pids[0]); + ret = stapi_do_set_filter(demux_id, &dev_list[i].demux_fd[demux_id][num], pids, 1, filter, mask, i); + if(ret > 0) // success + { + demux[demux_id].dev_index = i; + cs_log_dbg(D_DVBAPI, "%s filter %d set (pid %04X)", dev_list[i].name, num, pid); + return ret; // return filternumber + } + else // failure + { + cs_log_dbg(D_DVBAPI, "Error setting new filter for pid %04X on %s!", pid, dev_list[i].name); + return -1; // set return to error + } + } + } + } + + if(p == NULL) + { + cs_log_dbg(D_DVBAPI, "No matching S: line in oscam.dvbapi for pmtfile %s -> stop descrambling!", pmtfile); + snprintf(dest, sizeof(dest), "%s%s", TMPDIR, demux[demux_id].pmt_file); + unlink(dest); // remove obsolete pmt file + dvbapi_stop_descrambling(demux_id, 0); + } + return ret; +} + +int32_t stapi_remove_filter(int32_t demux_id, int32_t num, char *pmtfile) +{ + int32_t i, ret = 0; + struct s_dvbapi_priority *p; + + if(!pmtfile) { return 0; } + + for(p = dvbapi_priority; p != NULL; p = p->next) + { + if(p->type != 's') { continue; } + if(strcmp(pmtfile, p->pmtfile) != 0) + { continue; } + + for(i = 0; i < PTINUM; i++) + { + if(strcmp(dev_list[i].name, p->devname) == 0 && p->disablefilter == 0) + { + ret = stapi_do_remove_filter(demux_id, &dev_list[i].demux_fd[demux_id][num], i); + } + } + } + if(ret == 1) + { + cs_log_dbg(D_DVBAPI, "filter %d removed", num); + } + else + { + cs_log_dbg(D_DVBAPI, "Error: filter %d was not removed!", num); + } + return ret; +} + +static uint32_t check_slot(int32_t dev_id, uint32_t checkslot, FILTERTYPE *skipfilter) +{ + int32_t d, f, l; + for(d = 0; d < MAX_DEMUX; d++) + { + for(f = 0; f < MAX_FILTER; f++) + { + if(skipfilter && &dev_list[dev_id].demux_fd[d][f] == skipfilter) + { continue; } + for(l = 0; l < dev_list[dev_id].demux_fd[d][f].NumSlots; l++) + { + if(checkslot == dev_list[dev_id].demux_fd[d][f].SlotHandle[l]) + { + return dev_list[dev_id].demux_fd[d][f].BufferHandle[l]; + } + } + } + } + return 0; +} + + +static int32_t stapi_do_set_filter(int32_t demux_id, FILTERTYPE *filter, uint16_t *pids, int32_t pidcount, uint8_t *filt, uint8_t *mask, int32_t dev_id) +{ + uint32_t FilterAssociateError = 0; + int32_t k, ret = 0; + + filter->fd = 0; + filter->BufferHandle[0] = 0; + filter->SlotHandle[0] = 0; + + if(dev_list[dev_id].SessionHandle == 0) { return 0; } + + uint32_t FilterAllocateError = oscam_stapi5_FilterAllocate(dev_list[dev_id].SessionHandle, &filter->fd); + + if(FilterAllocateError != 0) + { + cs_log("FilterAllocate problem"); + filter->fd = 0; + return 0; + } + + uint32_t FilterSetError = oscam_stapi5_FilterSet(filter->fd, filt, mask); + + for(k = 0; k < pidcount; k++) + { + uint16_t pid = pids[k]; + + uint32_t QuerySlot = oscam_stapi5_PidQuery(dev_list[dev_id].name, pid); + int32_t SlotInit = 1; + + if(QuerySlot != 0) + { + uint32_t checkslot = check_slot(dev_id, QuerySlot, NULL); + if(checkslot > 0) + { + filter->SlotHandle[k] = QuerySlot; + filter->BufferHandle[k] = checkslot; + SlotInit = 0; + } + else + { + cs_log("overtake: clear pid, errorcode: %d", oscam_stapi5_SlotClearPid(QuerySlot)); + SlotInit = 1; + } + } + + if(SlotInit == 1) + { + ret = oscam_stapi5_SlotInit(dev_list[dev_id].SessionHandle, dev_list[dev_id].SignalHandle, &filter->BufferHandle[k], &filter->SlotHandle[k], pid); + } + + FilterAssociateError = oscam_stapi5_FilterAssociate(filter->fd, filter->SlotHandle[k]); + filter->NumSlots++; + } + + if(ret || FilterAllocateError || FilterAssociateError || FilterSetError) + { + cs_log("set_filter: dev: %d FAl: %d FAs: %d FS: %d", + dev_id, FilterAllocateError, FilterAssociateError, FilterSetError); + stapi_do_remove_filter(demux_id, filter, dev_id); + return 0; + } + else + { + return filter->fd; // return fd of filter + } +} + +static int32_t stapi_do_remove_filter(int32_t UNUSED(demux_id), FILTERTYPE *filter, int32_t dev_id) +{ + if(filter->fd == 0) { return 0; } + + uint32_t BufferDeallocateError = 0, SlotDeallocateError = 0, FilterDeallocateError = 0; + + if(dev_list[dev_id].SessionHandle == 0) { return 0; } + + int32_t k; + for(k = 0; k < filter->NumSlots; k++) + { + uint32_t checkslot = check_slot(dev_id, filter->SlotHandle[k], filter); + + if(checkslot == 0) + { + FilterDeallocateError = oscam_stapi5_FilterDeallocate(filter->fd, filter->BufferHandle[k], filter->SlotHandle[k]); + + oscam_stapi5_SlotClearPid(filter->SlotHandle[k]); + oscam_stapi5_SlotUnlink(filter->SlotHandle[k]); + oscam_stapi5_SignalDisassociateBuffer(dev_list[dev_id].SignalHandle, filter->BufferHandle[k]); + + BufferDeallocateError = oscam_stapi5_BufferDeallocate(filter->BufferHandle[k]); + SlotDeallocateError = oscam_stapi5_SlotDeallocate(filter->SlotHandle[k]); + + } + } + + memset(filter, 0, sizeof(FILTERTYPE)); + + if(BufferDeallocateError || SlotDeallocateError || FilterDeallocateError) + { + cs_log("remove_filter: dev: %d BD: %d SD: %d FDe: %d", + dev_id, BufferDeallocateError, SlotDeallocateError, FilterDeallocateError); + return 0; + } + else + { + return 1; + } +} + +static void stapi_cleanup_thread(void *dev) +{ + int32_t dev_index = (int)dev; + + int32_t ErrorCode; + ErrorCode = oscam_stapi5_Close(dev_list[dev_index].SessionHandle); + + cs_log("liboscam_stapi5: PTI %s closed - %d\n", dev_list[dev_index].name, ErrorCode); + dev_list[dev_index].SessionHandle = 0; +} + +static void *stapi_read_thread(void *sparam) +{ + int32_t dev_index, ErrorCode, i, j, CRCValid; + uint32_t QueryBufferHandle = 0, DataSize = 0; + uint8_t buf[BUFFLEN]; + + struct read_thread_param *para = sparam; + dev_index = para->id; + + SAFE_SETSPECIFIC(getclient, para->cli); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + pthread_cleanup_push(stapi_cleanup_thread, (void *) dev_index); + + int32_t error_count = 0; + + while(!exit_oscam) + { + QueryBufferHandle = 0; + ErrorCode = oscam_stapi5_SignalWaitBuffer(dev_list[dev_index].SignalHandle, &QueryBufferHandle, 1000); + + switch(ErrorCode) + { + case 0: // NO_ERROR: + break; + case 852042: // ERROR_SIGNAL_ABORTED + cs_log("Caught abort signal"); + pthread_exit(NULL); + break; + case 11: // ERROR_TIMEOUT: + //cs_log("timeout %d", dev_index); + //TODO: if pidindex == -1 try next + continue; + break; + default: + if(QueryBufferHandle != 0) + { + cs_log("SignalWaitBuffer error: %d", ErrorCode); + oscam_stapi5_BufferFlush(QueryBufferHandle); + continue; + } + cs_log("SignalWaitBuffer: index %d ErrorCode: %d - QueryBuffer: %x", dev_index, ErrorCode, QueryBufferHandle); + error_count++; + if(error_count > 10) + { + cs_log("Too many errors in reader thread %d, quitting.", dev_index); + pthread_exit(NULL); + } + continue; + break; + } + + uint32_t NumFilterMatches = 0; + int32_t demux_id = 0, filter_num = 0; + DataSize = 0; + uint32_t k; + + uint32_t MatchedFilterList[10]; + ErrorCode = oscam_stapi5_BufferReadSection(QueryBufferHandle, MatchedFilterList, 10, &NumFilterMatches, &CRCValid, buf, BUFFLEN, &DataSize); + + if(ErrorCode != 0) + { + cs_log("BufferRead: index: %d ErrorCode: %d", dev_index, ErrorCode); + cs_sleepms(1000); + continue; + } + + if(DataSize <= 0) + { continue; } + + SAFE_MUTEX_LOCK(&filter_lock); // don't use cs_lock() here; multiple threads using same s_client struct + for(k = 0; k < NumFilterMatches; k++) + { + for(i = 0; i < MAX_DEMUX; i++) + { + for(j = 0; j < MAX_FILTER; j++) + { + if(dev_list[dev_index].demux_fd[i][j].fd == MatchedFilterList[k]) + { + demux_id = i; + filter_num = j; + + dvbapi_process_input(demux_id, filter_num, buf, DataSize, 0); + } + } + } + } + SAFE_MUTEX_UNLOCK(&filter_lock); + } + + pthread_cleanup_pop(0); + + return NULL; +} + +int32_t stapi_init_descrambler(int32_t dev_index) +{ + int32_t ErrorCode; + + if(dev_index >= MAX_DESCRAMBLER) + { + cs_log("TKD MAX_DESCRAMBLER reached!"); + return 0; + } + + ErrorCode = oscam_sttkd_Allocate(TKDHandle[0], 0, &tkd_desc_info[dev_index].path_hndl, &tkd_desc_info[dev_index].key_hndl); + if (ErrorCode != 0) + { + cs_log("oscam_sttkd_Allocate faild! ErrorCode: %d", ErrorCode); + return 0; + } + + return 1; +} + +int32_t stapi_set_pid(int32_t demux_id, int32_t UNUSED(num), uint32_t idx, uint16_t pid, char *UNUSED(pmtfile)) +{ + if(idx == INDEX_INVALID) + { + if (tkd_desc_info[demux[demux_id].dev_index].path_hndl != 0) + { + cs_log_dbg(D_DVBAPI, "stop descrambling of PID %d on %s", pid, dev_list[demux[demux_id].dev_index].name); + uint32_t ErrorCode = oscam_sttkd_Disassociate(dev_list[demux[demux_id].dev_index].name, pid); + if (ErrorCode != 0) + cs_log("oscam_sttkd_Disassociate faild! ErrorCode: %d", ErrorCode); + + int i; + for (i = 0; i < MAX_STREAMPIDS; i++) + { + if (tkd_desc_info[demux[demux_id].dev_index].STREAMPIDs[i] == pid) + { + tkd_desc_info[demux[demux_id].dev_index].STREAMPIDs[i] = 0; + break; + } + } + + } + } + + return 1; +} + +int32_t stapi_write_cw(int32_t demux_id, uint8_t *cw, uint16_t *STREAMpids, int32_t STREAMpidcount, char *UNUSED(pmtfile)) +{ + int32_t ErrorCode, l, x; + uint8_t nullcw[8]; + memset(nullcw, 0, 8); + char *text[] = { "even", "odd" }; + + if(dev_list[demux[demux_id].dev_index].SessionHandle == 0) { return 0; } + + // check if descrambler is started for this dev_index + if(tkd_desc_info[demux[demux_id].dev_index].path_hndl == 0) + { + if(!stapi_init_descrambler(demux[demux_id].dev_index)) + { + cs_log_dbg(D_DVBAPI, "stapi_write_cw , faild to added descrambler for %s", dev_list[demux[demux_id].dev_index].name); + return 0; + } + } + + // descrambler started, check each pid + for (x = 0; x < STREAMpidcount; x++) + { + int pid_associated = -1; + + // search STREAMpids if path got associated + for (l = 0; l < MAX_STREAMPIDS; l++) + { + if (tkd_desc_info[demux[demux_id].dev_index].STREAMPIDs[l] == STREAMpids[x]) + { + pid_associated = l; + break; + } + } + + // if not associated add the pid + if(pid_associated < 0) + { + ErrorCode = oscam_sttkd_Associate(dev_list[demux[demux_id].dev_index].name, tkd_desc_info[demux[demux_id].dev_index].path_hndl, STREAMpids[x]); + if (ErrorCode != 0) + { + cs_log("stapi_write_cw , oscam_sttkd_Associate faild for pid %04X! ErrorCode: %d", STREAMpids[x], ErrorCode); + return 0; + } + + // add the pid to the next free index + for (l = 0; l < MAX_STREAMPIDS; l++) + { + if (tkd_desc_info[demux[demux_id].dev_index].STREAMPIDs[l] == 0) + { + tkd_desc_info[demux[demux_id].dev_index].STREAMPIDs[l] = STREAMpids[x]; + pid_associated = l; + break; + } + } + + if (pid_associated < 0) + { + cs_log("stapi_write_cw , faild to associate pid %04X, maximum number of %d pids reached", STREAMpids[x], MAX_STREAMPIDS); + return 0; + } + } + } + + //@theparasol: please verify this block is in the right place + int32_t pidnum = demux[demux_id].pidindex; // get current pidindex used for descrambling + uint32_t idx = demux[demux_id].ECMpids[pidnum].index[0]; + + if(idx == INDEX_INVALID) // if no indexer for this pid get one! + { + idx = dvbapi_get_desc_index(demux_id, pidnum, 0); + cs_log_dbg(D_DVBAPI, "Demuxer %d PID: %d CAID: %04X ECMPID: %04X is using index %d", demux_id, pidnum, + demux[demux_id].ECMpids[pidnum].CAID, demux[demux_id].ECMpids[pidnum].ECM_PID, idx); + } + // + + for(l = 0; l < 2; l++) + { + // Skip check for BISS1 - cw could be indeed zero + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + if(memcmp(cw + (l * 8), demux[demux_id].last_cw[0][l], 8) != 0 + && (memcmp(cw + (l * 8), nullcw, 8) != 0 || caid_is_biss(demux[demux_id].ECMpids[pidnum].CAID))) + { + ErrorCode = oscam_sttkd_KeyWrite(tkd_desc_info[demux[demux_id].dev_index].key_hndl, l, cw + (l * 8)); + + memcpy(demux[demux_id].last_cw[0][l], cw + (l * 8), 8); + cs_log_dbg(D_DVBAPI, "write cw %s index: %d on %s", text[l], demux_id, dev_list[demux[demux_id].dev_index].name); + } + } + + return 1; +} + +#endif diff --git a/module-dvbapi.c b/module-dvbapi.c new file mode 100644 index 0000000..f02f323 --- /dev/null +++ b/module-dvbapi.c @@ -0,0 +1,9412 @@ +#define MODULE_LOG_PREFIX "dvbapi" + +#include "globals.h" + +#ifdef HAVE_DVBAPI + +#include "module-dvbapi.h" +#include "module-cacheex.h" +#include "module-dvbapi-azbox.h" +#include "module-dvbapi-mca.h" +#include "module-dvbapi-coolapi.h" +#include "module-dvbapi-stapi.h" +#include "module-dvbapi-chancache.h" +#include "module-stat.h" +#include "module-streamrelay.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-config.h" +#include "oscam-ecm.h" +#include "oscam-emm.h" +#include "oscam-files.h" +#include "oscam-net.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" +#include "reader-irdeto.h" +#include "cscrypt/md5.h" +extern int32_t exit_oscam; + +static int32_t dvbapi_listenport_active = 0; + +#if defined (__CYGWIN__) +#define F_NOTIFY 0 +#define F_SETSIG 0 +#define DN_MODIFY 0 +#define DN_CREATE 0 +#define DN_DELETE 0 +#define DN_MULTISHOT 0 +#endif + +static const char *get_stream_type_txt(uint8_t stream_type) +{ + switch(stream_type) + { + case 0x00: return "invalid"; + case 0x01: return "MPEG-1 video"; + case 0x02: return "MPEG-2 video"; // MPEG-1 (constrained parameter) or MPEG-2 video + case 0x03: return "MPEG-1 audio"; // MP1, MP2, MP3 + case 0x04: return "MPEG-2 audio"; // MP1, MP2, MP3 + case 0x05: return "MPEG-2 private sections"; + case 0x06: return "MPEG-2 PES private data"; // AC-3, Enhanced AC-3, AC-4, DTS(-HD) audio, subtitles, etc (DVB) (depends on descriptor) + case 0x07: return "MHEG data"; + case 0x08: return "DSM-CC data"; + case 0x09: return "MPEG-2 over ATM data"; + case 0x0A: return "DSM-CC data"; + case 0x0B: return "DSM-CC data"; + case 0x0C: return "DSM-CC data"; + case 0x0D: return "DSM-CC data"; + case 0x0E: return "MPEG-2 auxiliary data"; + case 0x0F: return "MPEG-2 audio"; // AAC + case 0x10: return "MPEG-4 video"; + case 0x11: return "MPEG-4 audio"; // AAC, HE AAC and AAC v2 + case 0x12: return "MPEG-4 PES data"; + case 0x13: return "MPEG-4 data"; + case 0x14: return "DSM-CC data"; + case 0x15: return "MPEG-7 MetaData"; + case 0x16: return "MPEG-7 MetaData"; + case 0x17: return "MPEG-7 MetaData"; + case 0x18: return "MPEG-7 MetaData"; + case 0x19: return "MPEG-7 MetaData"; + case 0x1A: return "MPEG-2 IPMP data"; + case 0x1B: return "AVC video"; + case 0x1C: return "MPEG-4 audio"; // DST, ALS, SLS + case 0x24: return "HEVC video"; + case 0x25: return "HEVC subset video"; + case 0x2D: return "MPEG-H 3D audio"; // main stream + case 0x2E: return "MPEG-H 3D audio"; // auxiliary stream + case 0x42: return "Chinese video"; + case 0x7F: return "IPMP data"; + case 0x81: return "AC-3 audio (ATSC)"; // with descriptor tag 0x81 + case 0x86: return "SCTE 35 data"; + case 0x87: return "enhanced AC-3 audio (ATSC)"; + //case 0x88: return "DTS-HD audio (ATSC 2.0)"; // fixme: has ATSC 2.0 ever been used? + //case 0x??: return "AC-4 audio (ATSC 3.0)"; // fixme: add the actual value when it gets published + //case 0x??: return "MPEG-H 3D audio (ATSC 3.0)"; // fixme: add the actual value when it gets published + case 0xD1: return "BBC Dirac video"; + case 0xEA: return "VC-1 video"; + default: return "user private"; + } +} + +static const char *get_descriptor_tag_txt(uint8_t descriptor_tag) +{ + switch(descriptor_tag) + { + // Valid in all MPEG contexts: + case 0x00: return "reserved"; + case 0x01: return "forbidden"; + case 0x02: return "video stream"; + case 0x03: return "audio stream"; + case 0x04: return "hierarchy"; + case 0x05: return "registration"; + case 0x06: return "data stream alignment"; + case 0x07: return "target background grid"; + case 0x08: return "video window"; + case 0x09: return "CA"; + case 0x0A: return "ISO 639 language"; + case 0x0B: return "system clock"; + case 0x0C: return "multiplex buffer utilization"; + case 0x0D: return "copyright"; + case 0x0E: return "maximum bitrate"; + case 0x0F: return "private data indicator"; + case 0x10: return "smoothing buffer"; + case 0x11: return "STD"; + case 0x12: return "IBP"; + case 0x13: return "DSM-CC carousel identifier"; + case 0x14: return "DSM-CC association tag"; + case 0x15: return "DSM-CC deferred association tags"; + case 0x17: return "DSM-CC NPT reference"; + case 0x18: return "DSM-CC NPT endpoint"; + case 0x19: return "DSM-CC stream mode"; + case 0x1A: return "DSM-CC stream event"; + case 0x1B: return "MPEG-4 video"; + case 0x1C: return "MPEG-4 audio"; + case 0x1D: return "IOD"; + case 0x1E: return "SL"; + case 0x1F: return "FMC"; + case 0x20: return "External ES id"; + case 0x21: return "MuxCode"; + case 0x22: return "FmxBufferSize"; + case 0x23: return "MultiplexBuffer"; + case 0x24: return "Content labeling"; + case 0x25: return "Metadata association"; + case 0x26: return "Metadata"; + case 0x27: return "Metadata STD"; + case 0x28: return "AVC video"; + case 0x29: return "MPEG-2 IPMP"; + case 0x2A: return "AVC timing and HRD"; + case 0x2B: return "MPEG-2 AAC Audio"; + case 0x2C: return "FlexMuxTiming"; + case 0x2D: return "MPEG-4 Text"; + case 0x2E: return "MPEG-4 Audio Extension"; + case 0x2F: return "Auxiliary Video Stream"; + case 0x30: return "SVC Extension"; + case 0x31: return "MVC Extension"; + case 0x32: return "J2K Video"; + case 0x33: return "MVC Operation Point"; + case 0x34: return "MPEG-2 Stereoscopic Video Format"; + case 0x35: return "Stereoscopic Program Info"; + case 0x36: return "Stereoscopic Video Info"; + case 0x37: return "Transport Profile"; + case 0x38: return "HEVC Video"; + case 0x3F: return "MPEG-2 Extension"; + // Valid in DVB context: + case 0x45: return "VBI data"; + case 0x46: return "VBI teletext"; + case 0x51: return "mosaic"; + case 0x52: return "stream identifier"; + case 0x56: return "teletext"; + case 0x59: return "subtitling"; // with stream type 0x06 + case 0x5F: return "private data specifier"; + case 0x60: return "service move"; + case 0x65: return "scrambling"; + case 0x66: return "data broadcast id"; + case 0x6A: return "AC-3"; // with stream type 0x06 + case 0x6B: return "ancillary data"; + case 0x6F: return "application signalling"; + case 0x70: return "adaptation field data"; + case 0x74: return "related content"; + case 0x78: return "ECM repetition rate"; + case 0x7A: return "enhanced AC-3"; // with stream type 0x06 + case 0x7B: return "DTS"; // with stream type 0x06 + case 0x7C: return "AAC"; // with stream type 0x06 + case 0x7D: return "XAIT location"; + case 0x7F: return "DVB extension"; + // Valid in ATSC context: + case 0x81: return "AC-3"; // with stream type 0x81 + case 0xCC: return "enhanced AC-3"; // with stream type 0x87 + default: return "user private"; + } +} + +static const char *get_extension_descriptor_txt(uint8_t extension_tag) +{ + switch(extension_tag) + { + case 0x02: return "CP"; + case 0x06: return "supplementary audio"; + case 0x0E: return "DTS-HD"; // with stream type 0x06 + case 0x0F: return "DTS Neural"; // with stream type 0x06 + case 0x11: return "T2MI"; + case 0x13: return "URI linkage"; + case 0x15: return "AC-4"; // with stream type 0x06 + case 0x18: return "protection message"; + case 0x19: return "audio preselection"; + case 0x20: return "TTML subtitling"; // with stream type 0x06 + default: return "Undefined"; + } +} + +void flush_read_fd(int32_t demux_id, int32_t num, int fd) +{ + if(!dvbapi_listenport_active && cfg.dvbapi_boxtype != BOXTYPE_PC_NODMX) + { + cs_log_dbg(D_DVBAPI,"Demuxer %d flushing stale input data of filter %d (fd:%d)", demux_id, num + 1, fd); + + fd_set rd; + struct timeval t; + char buff[100]; + + t.tv_sec = 0; + t.tv_usec = 0; + + FD_ZERO(&rd); + FD_SET(fd, &rd); + + while(select(fd + 1, &rd, NULL, NULL, &t) > 0) + { + if(read(fd, buff, 100)) { ; } + } + } +} + +static int dvbapi_ioctl(int fd, uint32_t request, ...) +{ + int ret = 0; + va_list args; + va_start(args, request); + if(!(cfg.dvbapi_boxtype == BOXTYPE_SAMYGO)) + { + void *param = va_arg(args, void *); + ret = ioctl(fd, request, param); + } + else + { + switch(request) + { + case DMX_SET_FILTER: + { + struct dmx_sct_filter_params *sFP = va_arg(args, struct dmx_sct_filter_params *); + //fix filter for samygo + //note: we only have 14 available filter bytes (instead of 16) on samygo + memmove(&sFP->filter.filter[3], &sFP->filter.filter[1], 13); + memset(&sFP->filter.filter[1], 0, 2); + memmove(&sFP->filter.mask[3], &sFP->filter.mask[1], 13); + memset(&sFP->filter.mask[1], 0, 2); + // prepare packet + uint8_t packet[sizeof(request) + sizeof(struct dmx_sct_filter_params)]; + memcpy(&packet, &request, sizeof(request)); + memcpy(&packet[sizeof(request)], sFP, sizeof(struct dmx_sct_filter_params)); + ret = send(fd, packet, sizeof(packet), 0); + break; + } + + case DMX_SET_FILTER1: + { + cs_log("error: samygo does not support DMX_SET_FILTER1"); + ret = -1; + break; + } + + case DMX_STOP: + { + ret = send(fd, &request, sizeof(request), 0); + if(!(ret == 1)){ ret = 1; } + break; + } + + case CA_GET_DESCR_INFO: + { + ca_descr_info_t *ca_descr_info = va_arg(args, ca_descr_info_t *); + // preparing packet + uint8_t packet[sizeof(request) + sizeof(ca_descr_info_t)]; + memcpy(&packet[0], &request, sizeof(request)); + memcpy(&packet[sizeof(request)], ca_descr_info, sizeof(ca_descr_info_t)); + // sending data to UDP + ret = send(fd, &packet[0], sizeof(packet), 0); + break; + } + + case CA_SET_DESCR: + { + ca_descr_t *ca_descr = va_arg(args, ca_descr_t *); + // preparing packet + uint8_t packet[sizeof(request) + sizeof(ca_descr_t)]; + memcpy(&packet[0], &request, sizeof(request)); + memcpy(&packet[sizeof(request)], ca_descr, sizeof(ca_descr_t)); + // sending data to UDP + ret = send(fd, &packet[0], sizeof(packet), 0); + break; + } + + case CA_SET_PID: + { + ca_pid_t *ca_pid2 = va_arg(args, ca_pid_t *); + // preparing packet + uint8_t packet[sizeof(request) + sizeof(ca_pid_t)]; + memcpy(&packet[0], &request, sizeof(request)); + memcpy(&packet[sizeof(request)], ca_pid2, sizeof(ca_pid_t)); + // sending data to UDP + ret = send(fd, &packet[0], sizeof(packet), 0); + break; + } + + case CA_SET_DESCR_MODE: + { + cs_log("error: samygo does not support CA_SET_DESCR_MODE"); + ret = -1; + break; + } + + case CA_SET_DESCR_DATA: + { + cs_log("error: samygo does not support CA_SET_DESCR_DATA"); + ret = -1; + break; + } + } + + if(ret > 0) // send() may return larger than 1 + { + ret = 1; + } + } +#if defined(__powerpc__) + // Old dm500 boxes (ppc old) are using broken kernel, se we need some fixups + switch (request) + { + case DMX_STOP: + case CA_SET_DESCR: + case CA_SET_PID: + ret = 1; + } +#endif + // FIXME: Workaround for su980 bug + // See: https://board.streamboard.tv/?postid=533940 + if(boxtype_is("su980")) + { + ret = 1; + } + va_end(args); + return ret; +} + +// tunemm_caid_map +#define FROM_TO 0 +#define TO_FROM 1 + +int32_t pausecam = 0, disable_pmt_files = 0; + +DEMUXTYPE demux[MAX_DEMUX]; +struct s_dvbapi_priority *dvbapi_priority; +struct s_client *dvbapi_client; + +const char *boxdesc[] = { "none", "dreambox", "duckbox", "ufs910", "dbox2", "ipbox", "ipbox-pmt", + "dm7000", "qboxhd", "coolstream", "neumo", "pc", "pc-nodmx", "samygo" }; + +// when updating devices[BOX_COUNT] make sure to update these index defines +#define BOX_INDEX_QBOXHD 0 +#define BOX_INDEX_DREAMBOX_DVBAPI3 1 +#define BOX_INDEX_COOLSTREAM 6 + +static const struct box_devices devices[BOX_COUNT] = +{ + /* QboxHD (dvb-api-3)*/ { "/tmp/virtual_adapter/", "ca%d", "demux%d", "/tmp/camd.socket", DVBAPI_3 }, + /* dreambox (dvb-api-3)*/ { "/dev/dvb/adapter%d/", "ca%d", "demux%d", "/tmp/camd.socket", DVBAPI_3 }, + /* wetek (dvb-api-3)*/ { "/dev/dvb%d.", "ca%d", "demux%d", "/tmp/camd.socket", DVBAPI_3 }, + /* dreambox (dvb-api-1)*/ { "/dev/dvb/card%d/", "ca%d", "demux%d", "/tmp/camd.socket", DVBAPI_1 }, + /* neumo (dvb-api-1)*/ { "/dev/", "demuxapi", "demuxapi", "/tmp/camd.socket", DVBAPI_1 }, +#ifdef WITH_STAPI5 + /* sh4 (stapi5)*/ { "/dev/stapi/", "stpti5_ioctl", "stpti5_ioctl", "/tmp/camd.socket", STAPI }, +#else + /* sh4 (stapi)*/ { "/dev/stapi/", "stpti4_ioctl", "stpti4_ioctl", "/tmp/camd.socket", STAPI }, +#endif + /* coolstream*/ { "/dev/cnxt/", "null", "null", "/tmp/camd.socket", COOLAPI }, +}; + +static int32_t selected_box = -1; +static int32_t selected_api = -1; +static int32_t maxfilter = MAX_FILTER; +static int32_t dir_fd = -1; +static uint16_t last_client_proto_version = 0; +static char *last_client_name = NULL; +static uint32_t ca_descramblers_total = 0; // total number of available descramblers in box +static uint32_t ca_descramblers_used = 0; // total number of used descramblers during decoding +static int32_t ca_fd[CA_MAX]; // holds fd handle of all ca devices (0 not in use) +static LLIST *ll_activestreampids; // list of all enabled streampids on ca devices +static int32_t assoc_fd[MAX_ASSOC_FD]; // list of all client sockets +static uint8_t ca_soft_csa[MAX_DEMUX] = {0}; + +bool is_dvbapi_usr(char *usr) +{ + return streq(cfg.dvbapi_usr, usr); +} + +struct s_emm_filter +{ + int32_t demux_id; + uint8_t filter[32]; + uint16_t caid; + uint32_t provid; + uint16_t pid; + uint32_t num; + struct timeb time_started; +}; + +static LLIST *ll_emm_active_filter; +static LLIST *ll_emm_inactive_filter; +static LLIST *ll_emm_pending_filter; + +int32_t add_emmfilter_to_list(int32_t demux_id, uint8_t *filter, uint16_t caid, uint32_t provid, uint16_t emmpid, int32_t num, bool enable) +{ + if(!ll_emm_active_filter) + { + ll_emm_active_filter = ll_create("ll_emm_active_filter"); + } + + if(!ll_emm_inactive_filter) + { + ll_emm_inactive_filter = ll_create("ll_emm_inactive_filter"); + } + + if(!ll_emm_pending_filter) + { + ll_emm_pending_filter = ll_create("ll_emm_pending_filter"); + } + + struct s_emm_filter *filter_item; + if(!cs_malloc(&filter_item, sizeof(struct s_emm_filter))) + { + return 0; + } + + filter_item->demux_id = demux_id; + memcpy(filter_item->filter, filter, 32); + filter_item->caid = caid; + filter_item->provid = provid; + filter_item->pid = emmpid; + filter_item->num = num; + + if(enable) + { + cs_ftime(&filter_item->time_started); + } + else + { + memset(&filter_item->time_started, 0, sizeof(filter_item->time_started)); + } + + if(num > 0) + { + ll_append(ll_emm_active_filter, filter_item); + cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d added to active emmfilters (CAID %04X PROVID %06X EMMPID %04X)", + filter_item->demux_id, filter_item->num, filter_item->caid, filter_item->provid, filter_item->pid); + } + else if(num < 0) + { + ll_append(ll_emm_pending_filter, filter_item); + cs_log_dbg(D_DVBAPI, "Demuxer %d Filter added to pending emmfilters (CAID %04X PROVID %06X EMMPID %04X)", + filter_item->demux_id, filter_item->caid, filter_item->provid, filter_item->pid); + } + else + { + ll_append(ll_emm_inactive_filter, filter_item); + cs_log_dbg(D_DVBAPI, "Demuxer %d Filter added to inactive emmfilters (CAID %04X PROVID %06X EMMPID %04X)", + filter_item->demux_id, filter_item->caid, filter_item->provid, filter_item->pid); + } + return 1; +} + +int32_t is_emmfilter_in_list_internal(LLIST *ll, uint8_t *filter, uint16_t emmpid, uint32_t provid, uint16_t caid) +{ + struct s_emm_filter *filter_item; + LL_ITER itr; + if(ll_count(ll) > 0) + { + itr = ll_iter_create(ll); + while((filter_item = ll_iter_next(&itr)) != NULL) + { + if(!memcmp(filter_item->filter, filter, 32) && (filter_item->pid == emmpid) + && (filter_item->provid == provid) && (filter_item->caid == caid)) + { + return 1; + } + } + } + return 0; +} + +int32_t is_emmfilter_in_list(uint8_t *filter, uint16_t emmpid, uint32_t provid, uint16_t caid) +{ + if(!ll_emm_active_filter) + { + ll_emm_active_filter = ll_create("ll_emm_active_filter"); + } + + if(!ll_emm_inactive_filter) + { + ll_emm_inactive_filter = ll_create("ll_emm_inactive_filter"); + } + + if(!ll_emm_pending_filter) + { + ll_emm_pending_filter = ll_create("ll_emm_pending_filter"); + } + + if(is_emmfilter_in_list_internal(ll_emm_active_filter, filter, emmpid, provid, caid) + || is_emmfilter_in_list_internal(ll_emm_inactive_filter, filter, emmpid, provid, caid) + || is_emmfilter_in_list_internal(ll_emm_pending_filter, filter, emmpid, provid, caid)) + { + return 1; + } + return 0; +} + +struct s_emm_filter *get_emmfilter_by_filternum_internal(LLIST *ll, int32_t demux_id, uint32_t num) +{ + struct s_emm_filter *filter; + LL_ITER itr; + if(ll_count(ll) > 0) + { + itr = ll_iter_create(ll); + while((filter = ll_iter_next(&itr))) + { + if(filter->demux_id == demux_id && filter->num == num) + { + return filter; + } + } + } + return NULL; +} + +struct s_emm_filter *get_emmfilter_by_filternum(int32_t demux_id, uint32_t num) +{ + if(!ll_emm_active_filter) + { + ll_emm_active_filter = ll_create("ll_emm_active_filter"); + } + + if(!ll_emm_inactive_filter) + { + ll_emm_inactive_filter = ll_create("ll_emm_inactive_filter"); + } + + if(!ll_emm_pending_filter) + { + ll_emm_pending_filter = ll_create("ll_emm_pending_filter"); + } + + struct s_emm_filter *emm_filter = NULL; + + emm_filter = get_emmfilter_by_filternum_internal(ll_emm_active_filter, demux_id, num); + if(emm_filter) + { + return emm_filter; + } + + emm_filter = get_emmfilter_by_filternum_internal(ll_emm_inactive_filter, demux_id, num); + if(emm_filter) + { + return emm_filter; + } + + emm_filter = get_emmfilter_by_filternum_internal(ll_emm_pending_filter, demux_id, num); + if(emm_filter) + { + return emm_filter; + } + + return NULL; +} + +int8_t remove_emmfilter_from_list_internal(LLIST *ll, int32_t demux_id, uint16_t caid, uint32_t provid, uint16_t pid, uint32_t num) +{ + struct s_emm_filter *filter; + LL_ITER itr; + + if(ll_count(ll) > 0) + { + itr = ll_iter_create(ll); + while((filter = ll_iter_next(&itr))) + { + if(filter->demux_id == demux_id && filter->caid == caid && filter->provid == provid && filter->pid == pid && filter->num == num) + { + ll_iter_remove_data(&itr); + return 1; + } + } + } + return 0; +} + +void remove_emmfilter_from_list(int32_t demux_id, uint16_t caid, uint32_t provid, uint16_t pid, uint32_t num) +{ + if((ll_emm_active_filter && remove_emmfilter_from_list_internal(ll_emm_active_filter, demux_id, caid, provid, pid, num)) + || (ll_emm_inactive_filter && remove_emmfilter_from_list_internal(ll_emm_inactive_filter, demux_id, caid, provid, pid, num)) + || (ll_emm_pending_filter && remove_emmfilter_from_list_internal(ll_emm_pending_filter, demux_id, caid, provid, pid, num))) + { + return; + } +} + +void dvbapi_net_add_str(uint8_t *packet, int *size, const char *str) +{ + uint8_t *str_len = &packet[*size]; // string length + *size += 1; + *str_len = snprintf((char *) &packet[*size], DVBAPI_MAX_PACKET_SIZE - *size, "%s", str); + *size += *str_len; +} + +int32_t dvbapi_net_send(uint32_t request, int32_t socket_fd, uint32_t msgid, int32_t demux_id, + uint32_t filter_number, uint8_t *data, struct s_client *client, ECM_REQUEST *er, uint16_t client_proto_version) +{ + uint8_t packet[DVBAPI_MAX_PACKET_SIZE]; // maximum possible packet size + int32_t size = 0; + uint32_t u32; + + // not connected? + if(socket_fd <= 0) + { + return 0; + } + + // preparing packet - header + // in old protocol client expect this first byte as adapter index, + // changed in the new protocol to be always after request type (opcode) + if(client_proto_version <= 0) + { + packet[size++] = demux[demux_id].adapter_index; // adapter index - 1 byte + } + else if(client_proto_version >= 3) + { + packet[size++] = 0xa5; // message start + u32 = htonl(msgid); + memcpy(&packet[size], &u32, 4); + size += 4; + } + + // type of request + u32 = request; + if(client_proto_version >= 1) + { + u32 = htonl(u32); + } + memcpy(&packet[size], &u32, 4); + size += 4; + + // preparing packet - adapter index for proto >= 1 + if((request != DVBAPI_SERVER_INFO) && client_proto_version >= 1) + { + packet[size++] = demux[demux_id].adapter_index; // adapter index - 1 byte + } + + // struct with data + switch(request) + { + case DVBAPI_SERVER_INFO: + { + int16_t proto_version = htons(DVBAPI_PROTOCOL_VERSION); // our protocol version + char capabilities[128]; + memset(capabilities, 0, sizeof(capabilities)); + memcpy(&packet[size], &proto_version, 2); + size += 2; + uint8_t *info_len = &packet[size]; // info string length + size += 1; +#ifdef WITH_EXTENDED_CW + if(cfg.dvbapi_extended_cw_api == 1) + { + cs_strncat(capabilities, ",e1mk", sizeof(capabilities)); // extended cw, key follows mode - supports CSA, DES, AES128 + } + + if(cfg.dvbapi_extended_cw_api == 2) + { + cs_strncat(capabilities, ",e2", sizeof(capabilities)); // usage of DES algo signalled through PID index - CSA and DES only + } +#endif + *info_len = snprintf((char *) &packet[size], sizeof(packet) - size, "OSCam %s (%s); %s", + CS_VERSION, CS_TARGET, capabilities + 1); + + size += *info_len; + break; + } + + case DVBAPI_ECM_INFO: + { + if(er->rc >= E_NOTFOUND) + { + return 0; + } + + int8_t hops = 0; + + uint16_t sid = htons(er->srvid); // service ID (program number) + memcpy(&packet[size], &sid, 2); + size += 2; + + uint16_t caid = htons(er->caid); // CAID + memcpy(&packet[size], &caid, 2); + size += 2; + + uint16_t pid = htons(er->pid); // PID + memcpy(&packet[size], &pid, 2); + size += 2; + + uint32_t prid = htonl(er->prid); // Provider ID + memcpy(&packet[size], &prid, 4); + size += 4; + + uint32_t ecmtime = htonl(client->cwlastresptime); // ECM time + memcpy(&packet[size], &ecmtime, 4); + size += 4; + + dvbapi_net_add_str(packet, &size, get_cardsystem_desc_by_caid(er->caid)); // cardsystem name + + switch (er->rc) + { + case E_FOUND: + if(er->selected_reader) + { + dvbapi_net_add_str(packet, &size, er->selected_reader->label); // reader + if(is_network_reader(er->selected_reader)) + { + dvbapi_net_add_str(packet, &size, er->selected_reader->device); // from + } + else + { + dvbapi_net_add_str(packet, &size, "local"); // from + } + dvbapi_net_add_str(packet, &size, reader_get_type_desc(er->selected_reader, 1)); // protocol + hops = er->selected_reader->currenthops; + } + break; + + case E_CACHE1: + dvbapi_net_add_str(packet, &size, "Cache"); // reader + dvbapi_net_add_str(packet, &size, "cache1"); // from + dvbapi_net_add_str(packet, &size, "none"); // protocol + break; + + case E_CACHE2: + dvbapi_net_add_str(packet, &size, "Cache"); // reader + dvbapi_net_add_str(packet, &size, "cache2"); // from + dvbapi_net_add_str(packet, &size, "none"); // protocol + break; + + case E_CACHEEX: + dvbapi_net_add_str(packet, &size, "Cache"); // reader + dvbapi_net_add_str(packet, &size, "cache3"); // from + dvbapi_net_add_str(packet, &size, "none"); // protocol + break; + } + packet[size++] = hops; // hops + break; + } + + case DVBAPI_CA_GET_DESCR_INFO: + { + int sct_cadescr_info_size = sizeof(ca_descr_info_t); + if(client_proto_version >= 1) + { + ca_descr_info_t *cadescr_info = (ca_descr_info_t *) data; + cadescr_info->num = htonl(cadescr_info->num); + cadescr_info->type = htonl(cadescr_info->type); + } + memcpy(&packet[size], data, sct_cadescr_info_size); + size += sct_cadescr_info_size; + break; + } + + case DVBAPI_CA_SET_DESCR: + { + int sct_cadescr_size = sizeof(ca_descr_t); + if(client_proto_version >= 1) + { + ca_descr_t *cadesc = (ca_descr_t *) data; + cadesc->index = htonl(cadesc->index); + cadesc->parity = htonl(cadesc->parity); + } + memcpy(&packet[size], data, sct_cadescr_size); + size += sct_cadescr_size; + break; + } + + case DVBAPI_CA_SET_PID: + { + int sct_capid_size = sizeof(ca_pid_t); + if(client_proto_version >= 1) + { + ca_pid_t *capid = (ca_pid_t *) data; + capid->pid = htonl(capid->pid); + capid->index = htonl(capid->index); + } + memcpy(&packet[size], data, sct_capid_size); + size += sct_capid_size; + break; + } + + case DVBAPI_CA_SET_DESCR_MODE: + { + int sct_cadescr_mode_size = sizeof(ca_descr_mode_t); + if(client_proto_version >= 1) + { + ca_descr_mode_t *cadesc_mode = (ca_descr_mode_t *) data; + cadesc_mode->index = htonl(cadesc_mode->index); + cadesc_mode->algo = htonl(cadesc_mode->algo); + cadesc_mode->cipher_mode = htonl(cadesc_mode->cipher_mode); + } + memcpy(&packet[size], data, sct_cadescr_mode_size); + size += sct_cadescr_mode_size; + break; + } + + case DVBAPI_CA_SET_DESCR_DATA: + { + ca_descr_data_t *cadesc_data = (ca_descr_data_t *) data; + uint32_t length = cadesc_data->length; + if(client_proto_version >= 1) + { + cadesc_data->index = htonl(cadesc_data->index); + cadesc_data->parity = htonl(cadesc_data->parity); + cadesc_data->data_type = htonl(cadesc_data->data_type); + cadesc_data->length = htonl(cadesc_data->length); + } + memcpy(&packet[size], &cadesc_data->index, 4); + memcpy(&packet[size + 4], &cadesc_data->parity, 4); + memcpy(&packet[size + 8], &cadesc_data->data_type, 4); + memcpy(&packet[size + 12], &cadesc_data->length, 4); + memcpy(&packet[size + 16], cadesc_data->data, length); + size += 16 + length; + break; + } + + case DVBAPI_DMX_SET_FILTER: + case DVBAPI_DMX_STOP: + { + int32_t sct_filter_size = sizeof(struct dmx_sct_filter_params); + packet[size++] = demux_id; // demux id - 1 byte + packet[size++] = filter_number; // filter number - 1 byte + if(data) // filter data when starting + { + if(client_proto_version >= 1) + { + struct dmx_sct_filter_params *fp = (struct dmx_sct_filter_params *)data; + // adding all dmx_sct_filter_params structure fields + // one by one to avoid padding problems + uint16_t pid = htons(fp->pid); + memcpy(&packet[size], &pid, 2); + size += 2; + memcpy(&packet[size], fp->filter.filter, 16); + size += 16; + memcpy(&packet[size], fp->filter.mask, 16); + size += 16; + memcpy(&packet[size], fp->filter.mode, 16); + size += 16; + uint32_t timeout = htonl(fp->timeout); + memcpy(&packet[size], &timeout, 4); + size += 4; + uint32_t flags = htonl(fp->flags); + memcpy(&packet[size], &flags, 4); + size += 4; + } + else + { + memcpy(&packet[size], data, sct_filter_size); // dmx_sct_filter_params struct + size += sct_filter_size; + } + } + else // pid when stopping + { + if(client_proto_version >= 1) + { + uint16_t pid = htons(demux[demux_id].demux_fd[filter_number].pid); + memcpy(&packet[size], &pid, 2); + size += 2; + } + else + { + uint16_t pid = demux[demux_id].demux_fd[filter_number].pid; + packet[size++] = pid >> 8; + packet[size++] = pid & 0xff; + } + } + break; + } + + default: // unknown request + { + cs_log("ERROR: dvbapi_net_send: invalid request"); + return 0; + } + } + // sending + cs_log_dump_dbg(D_DVBAPI, packet, size, "Sending packet to dvbapi client (fd=%d):", socket_fd); + send(socket_fd, &packet, size, MSG_DONTWAIT); + // always returning success as the client could close socket + return 0; +} + +int32_t dvbapi_set_filter(int32_t demux_id, int32_t api, uint16_t pid, uint16_t caid, uint32_t provid, + uint8_t *filt, uint8_t *mask, int32_t timeout, int32_t pidindex, int32_t type, int8_t add_to_emm_list) +{ + int32_t ret = -1, n = -1, i, filterfd = -1; + + for(i = 0; i < maxfilter && demux[demux_id].demux_fd[i].fd > 0; i++) { ; } + + if(i >= maxfilter) + { + cs_log_dbg(D_DVBAPI, "no free filter"); + return -1; + } + n = i; + + if(USE_OPENXCAS) + { + if(type == TYPE_ECM) + { + openxcas_set_caid(demux[demux_id].ECMpids[pidindex].CAID); + openxcas_set_ecm_pid(pid); + } + + demux[demux_id].demux_fd[n].fd = DUMMY_FD; + demux[demux_id].demux_fd[n].pidindex = pidindex; + demux[demux_id].demux_fd[n].pid = pid; + demux[demux_id].demux_fd[n].caid = caid; + demux[demux_id].demux_fd[n].provid = provid; + demux[demux_id].demux_fd[n].type = type; + memcpy(demux[demux_id].demux_fd[n].filter, filt, 16); // copy filter to check later on if receiver delivered accordingly + memcpy(demux[demux_id].demux_fd[n].mask, mask, 16); // copy mask to check later on if receiver delivered accordingly + return 1; + } + + switch(api) + { + case DVBAPI_3: + { + if(dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) + { + ret = filterfd = DUMMY_FD; + } + else + { + ret = filterfd = dvbapi_open_device(0, demux[demux_id].demux_index, demux[demux_id].adapter_index); + } + + if(ret < 0) + { + return ret; // return if device cant be opened! + } + + struct dmx_sct_filter_params sFP2; + memset(&sFP2, 0, sizeof(sFP2)); + sFP2.pid = pid; + sFP2.timeout = timeout; + sFP2.flags = DMX_IMMEDIATE_START; + + if(cfg.dvbapi_boxtype == BOXTYPE_NEUMO) + { + //DeepThought: on dgs/cubestation and neumo images, perhaps others + //the following code is needed to descramble + sFP2.filter.filter[0] = filt[0]; + sFP2.filter.mask[0] = mask[0]; + sFP2.filter.filter[1] = 0; + sFP2.filter.mask[1] = 0; + sFP2.filter.filter[2] = 0; + sFP2.filter.mask[2] = 0; + memcpy(sFP2.filter.filter + 3, filt + 1, 16 - 3); + memcpy(sFP2.filter.mask + 3, mask + 1, 16 - 3); + + //DeepThought: in the drivers of the dgs/cubestation and neumo images, + //dvbapi 1 and 3 are somehow mixed. In the kernel drivers, the DMX_SET_FILTER + //ioctl expects to receive a dmx_sct_filter_params structure (DVBAPI 3) but + //due to a bug its sets the "positive mask" wrongly (they should be all 0). + //On the other hand, the DMX_SET_FILTER1 ioctl also uses the dmx_sct_filter_params + //structure, which is incorrect (it should be dmxSctFilterParams). + //The only way to get it right is to call DMX_SET_FILTER1 with the argument + //expected by DMX_SET_FILTER. Otherwise, the timeout parameter is not passed correctly. + ret = dvbapi_ioctl(filterfd, DMX_SET_FILTER1, &sFP2); + } + else + { + memcpy(sFP2.filter.filter, filt, 16); + memcpy(sFP2.filter.mask, mask, 16); + if(dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) + { + ret = dvbapi_net_send(DVBAPI_DMX_SET_FILTER, + demux[demux_id].socket_fd, + 0, + demux_id, + n, + (uint8_t *) &sFP2, + NULL, + NULL, + demux[demux_id].client_proto_version); + } + else + { + ret = dvbapi_ioctl(filterfd, DMX_SET_FILTER, &sFP2); + } + } + break; + } + + case DVBAPI_1: + { + ret = filterfd = dvbapi_open_device(0, demux[demux_id].demux_index, demux[demux_id].adapter_index); + if(ret < 0) { return ret; } // return if device cant be opened! + + struct dmxSctFilterParams sFP1; + memset(&sFP1, 0, sizeof(sFP1)); + sFP1.pid = pid; + sFP1.timeout = timeout; + sFP1.flags = DMX_IMMEDIATE_START; + memcpy(sFP1.filter.filter, filt, 16); + memcpy(sFP1.filter.mask, mask, 16); + ret = dvbapi_ioctl(filterfd, DMX_SET_FILTER1, &sFP1); + break; + } + +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + case STAPI: + { + ret = filterfd = stapi_set_filter(demux_id, pid, filt, mask, n, demux[demux_id].pmt_file); + if(ret <= 0) + { + ret = -1; // error setting filter! + } + break; + } +#endif + +#if defined WITH_COOLAPI || defined WITH_COOLAPI2 + case COOLAPI: + { + ret = filterfd = coolapi_open_device(demux[demux_id].demux_index, demux_id); + if(ret > 0) + { + ret = coolapi_set_filter(filterfd, n, pid, filt, mask, type); + } + else + { + ret = -1; // fail + } + break; + } +#endif + + default: + break; + } + + if(ret != -1) // filter set successful + { + // only register if filter was set successful + demux[demux_id].demux_fd[n].fd = filterfd; + demux[demux_id].demux_fd[n].pidindex = pidindex; + demux[demux_id].demux_fd[n].pid = pid; + demux[demux_id].demux_fd[n].caid = caid; + demux[demux_id].demux_fd[n].provid = provid; + demux[demux_id].demux_fd[n].type = type; + + // copy filter and mask to check later on if receiver delivered accordingly + memcpy(demux[demux_id].demux_fd[n].filter, filt, 16); + memcpy(demux[demux_id].demux_fd[n].mask, mask, 16); + + cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d started successfully (caid %04X provid %06X pid %04X)", + demux_id, n + 1, caid, provid, pid); + + if(type == TYPE_EMM && add_to_emm_list) + { + add_emmfilter_to_list(demux_id, filt, caid, provid, pid, n + 1, true); + } + } + else + { + cs_log("ERROR: Could not start demux filter (api: %d errno=%d %s)", selected_api, errno, strerror(errno)); + } + return ret; +} + +/* + * Get number of available descramblers from box. On success, it stores result on + * global variable "ca_descramblers_total". On failure, it uses a safe default value. + */ +static int32_t dvbapi_get_descrambler_info(void) +{ + // In enigma2 all ca devices are listed under adapter0. In addition we only + // need to ask one ca device to get the total number of descramblers. In + // PC installations, there are no ca devices, so we use a predefined value. + + if(cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) + { + ca_descramblers_total = INDEX_MAX_NET; + return 1; // nothing else to do for PCs + } + + int32_t fd = 0, ca_offset = 0; + char device_path[128], device_path2[128]; + struct ca_descr_info descr_info; + memset(&descr_info, 0, sizeof(descr_info)); + + // Use a safe default in case we fail to get the exact number + ca_descramblers_total = INDEX_MAX_LOCAL; + + if(cfg.dvbapi_boxtype == BOXTYPE_DUCKBOX || + cfg.dvbapi_boxtype == BOXTYPE_DBOX2 || + cfg.dvbapi_boxtype == BOXTYPE_UFS910) + { + ca_offset = 1; + } + + // Ask device for exact number of ca descramblers + snprintf(device_path2, sizeof(device_path2), devices[selected_box].ca_device, ca_offset); + snprintf(device_path, sizeof(device_path), devices[selected_box].path, 0); + + if (!cs_strncat(device_path, device_path2, sizeof(device_path))) + return 0; + + if((fd = open(device_path, O_RDWR | O_NONBLOCK)) < 0) + { + cs_log("ERROR: Can't open device %s (errno=%d %s)", device_path, errno, strerror(errno)); + return 0; + } + + if(dvbapi_ioctl(fd, CA_GET_DESCR_INFO, &descr_info) < 0) + { + cs_log_dbg(D_DVBAPI,"ERROR: ioctl(CA_GET_DESCR_INFO): %s", strerror(errno)); + } + + if(close(fd) < 0) + { + cs_log("ERROR: Can't close device %s (errno=%d %s)", device_path, errno, strerror(errno)); + } + + // We got a valid response from device (count the ECD type only) + if(descr_info.num > 0 && (descr_info.type & 1)) + { + ca_descramblers_total = descr_info.num; + cs_log("Detected %s device, total available descramblers: %d", device_path, ca_descramblers_total); + return 1; + } + + return 0; +} + +static int32_t dvbapi_detect_api(void) +{ +#if defined WITH_COOLAPI || defined WITH_COOLAPI2 + selected_api = COOLAPI; + selected_box = BOX_INDEX_COOLSTREAM; + disable_pmt_files = 1; + dvbapi_listenport_active = 0; + cs_log("Detected Coolstream API"); + return 1; +#else + if(cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX || cfg.dvbapi_boxtype == BOXTYPE_PC) + { + selected_api = DVBAPI_3; + selected_box = BOX_INDEX_DREAMBOX_DVBAPI3; + dvbapi_listenport_active = cfg.dvbapi_listenport; + if(dvbapi_listenport_active) + { + cs_log("Using TCP listen socket, API forced to DVBAPIv3 (%d), userconfig boxtype: %d", + selected_api, cfg.dvbapi_boxtype); + } + else + { + cs_log("Using %s listen socket, API forced to DVBAPIv3 (%d), userconfig boxtype: %d", + devices[selected_box].cam_socket_path, selected_api, cfg.dvbapi_boxtype); + } + return 1; + } + else if(cfg.dvbapi_boxtype == BOXTYPE_SAMYGO) + { + selected_api = DVBAPI_3; + selected_box = BOX_INDEX_QBOXHD; + dvbapi_listenport_active = 0; + disable_pmt_files = 1; + cs_log("Using SamyGO dvbapi v0.1"); + return 1; + } + else + { + dvbapi_listenport_active = 0; + } + + int32_t i = 0, n = 0, devnum = -1, dmx_fd = 0, filtercount = 0; + char device_path[128], device_path2[128]; + static LLIST *ll_max_fd; + ll_max_fd = ll_create("ll_max_fd"); + LL_ITER itr; + + struct s_open_fd + { + uint32_t fd; + }; + struct s_open_fd *open_fd; + + while(i < BOX_COUNT) + { + do + { + snprintf(device_path2, sizeof(device_path2), devices[i].demux_device, 0); + snprintf(device_path, sizeof(device_path), devices[i].path, n); + + filtercount = 0; + + if (cs_strncat(device_path, device_path2, sizeof(device_path))) + { + while((dmx_fd = open(device_path, O_RDWR | O_NONBLOCK)) > 0 && filtercount < MAX_FILTER) + { + filtercount++; + if(!cs_malloc(&open_fd, sizeof(struct s_open_fd))) + { + close(dmx_fd); + break; + } + open_fd->fd = dmx_fd; + ll_append(ll_max_fd, open_fd); + } + } + + if(filtercount > 0) + { + itr = ll_iter_create(ll_max_fd); + while((open_fd = ll_iter_next(&itr))) + { + dmx_fd = open_fd->fd; + do + { + ; + } + while(close(dmx_fd) < 0); + ll_iter_remove_data(&itr); + } + devnum = i; + selected_api = devices[devnum].api; + selected_box = devnum; + + if(cfg.dvbapi_boxtype == BOXTYPE_NEUMO) + { + selected_api = DVBAPI_3; //DeepThought + } + +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + if(selected_api == STAPI && stapi_open() == 0) + { + cs_log("ERROR: stapi: setting up stapi failed."); + return 0; + } +#endif + maxfilter = filtercount; + cs_log("Detected %s Api: %d, userconfig boxtype: %d maximum number of filters is %d (oscam limit is %d)", + device_path, selected_api, cfg.dvbapi_boxtype, filtercount, MAX_FILTER); + +#ifdef MODULE_STREAMRELAY + // Log enabled demuxer fix + if(cfg.dvbapi_demuxer_fix) + { + cs_log("Demuxer fix enabled, try fixing stream relay audio/video sync..."); + } +#endif + } + + // try at least 8 adapters + if((strchr(devices[i].path, '%') != NULL) && (n < 8)) + { + n++; + } + else + { + n = 0; i++; + } + + } while(n != 0); // n is set to 0 again if 8 adapters are tried! + + if(devnum != -1) + { + break; // check if box detected + } + } + + ll_destroy(&ll_max_fd); + if(devnum == -1) + { + return 0; + } +#endif + + return 1; +} + +static int32_t dvbapi_read_device(int32_t dmx_fd, uint8_t *buf, uint32_t length) +{ + int32_t readed; + uint32_t count = 0; + struct pollfd pfd[1]; + + pfd[0].fd = dmx_fd; + pfd[0].events = (POLLIN | POLLPRI); + + while(count < length) + { + if(poll(pfd, 1, 0)) // fd ready for reading? + { + if(pfd[0].revents & (POLLIN | POLLPRI)) // is there data to read? + { + readed = read(dmx_fd, &buf[count], length-count); + if(readed < 0) // error occured while reading + { + if(errno == EINTR || errno == EAGAIN) // try again in case of interrupt + { + continue; + } + + cs_log("ERROR: Read error on fd %d (errno=%d %s)", dmx_fd, errno, strerror(errno)); + return (errno == EOVERFLOW ? 0 : -1); + } + + if(readed > 0) // successful read + { + count += readed; + } + + if(readed == 0 && count > 0) // nothing to read left + { + break; + } + } + else + { + return -1; // other events than pollin/pri means bad news -> abort! + } + } + else + { + break; + } + } + cs_log_dump_dbg(D_TRACE, buf, count, "Received:"); + return count; +} + +int32_t dvbapi_open_device(int32_t type, int32_t num, int32_t adapter) +{ + int32_t dmx_fd, ret; + int32_t ca_offset = 0; + char device_path[128], device_path2[128]; + + if(dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) + { + return DUMMY_FD; + } + + if(type == 0) + { + snprintf(device_path2, sizeof(device_path2), devices[selected_box].demux_device, num); + snprintf(device_path, sizeof(device_path), devices[selected_box].path, adapter); + + if (!cs_strncat(device_path, device_path2, sizeof(device_path))) + return -1; + + } + else + { + if(cfg.dvbapi_boxtype == BOXTYPE_DUCKBOX + || cfg.dvbapi_boxtype == BOXTYPE_DBOX2 + || cfg.dvbapi_boxtype == BOXTYPE_UFS910) + { + ca_offset = 1; + } + + if(cfg.dvbapi_boxtype == BOXTYPE_QBOXHD + || cfg.dvbapi_boxtype == BOXTYPE_PC + || cfg.dvbapi_boxtype == BOXTYPE_SAMYGO) + { + num = 0; + } + + snprintf(device_path2, sizeof(device_path2), devices[selected_box].ca_device, num + ca_offset); + snprintf(device_path, sizeof(device_path), devices[selected_box].path, adapter); + + if (!cs_strncat(device_path, device_path2, sizeof(device_path))) + return -1; + + } + + if(cfg.dvbapi_boxtype == BOXTYPE_SAMYGO) + { + if(type == 0) + { + struct sockaddr_un saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sun_family = AF_UNIX; + cs_strncpy(saddr.sun_path, device_path, sizeof(saddr.sun_path)); + dmx_fd = socket(AF_UNIX, SOCK_STREAM, 0); + ret = connect(dmx_fd, (struct sockaddr *)&saddr, sizeof(saddr)); + if(ret < 0) + { + close(dmx_fd); + } + } + else if(type == 1) + { + int32_t udp_port = 9000; + struct sockaddr_in saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(udp_port + adapter); + saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); + dmx_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + set_nonblock(dmx_fd, true); + ret = connect(dmx_fd, (struct sockaddr *) &saddr, sizeof(saddr)); + if(ret < 0) + { + close(dmx_fd); + } + cs_log_dbg(D_DVBAPI, "NET DEVICE open (port = %d) fd %d", udp_port + adapter, dmx_fd); + } + else + { + ret = -1; + } + } + else + { + dmx_fd = ret = open(device_path, O_RDWR | O_NONBLOCK); + } + + if(ret < 0) + { + cs_log("ERROR: Can't open device %s (errno=%d %s)", device_path, errno, strerror(errno)); + return -1; + } + cs_log_dbg(D_DVBAPI, "Open device %s (fd %d)", device_path, dmx_fd); + return dmx_fd; +} + +uint16_t tunemm_caid_map(uint8_t direct, uint16_t caid, uint16_t srvid) +{ + int32_t i; + struct s_client *cl = cur_client(); + TUNTAB *ttab = &cl->ttab; + + if(!ttab->ttnum) + { + return caid; + } + + if(direct) + { + for(i = 0; i < ttab->ttnum; i++) + { + if(caid == ttab->ttdata[i].bt_caidto && + (srvid == ttab->ttdata[i].bt_srvid || ttab->ttdata[i].bt_srvid == 0xFFFF || !ttab->ttdata[i].bt_srvid)) + { + return ttab->ttdata[i].bt_caidfrom; + } + } + } + else + { + for(i = 0; i < ttab->ttnum; i++) + { + if(caid == ttab->ttdata[i].bt_caidfrom && + (srvid == ttab->ttdata[i].bt_srvid || ttab->ttdata[i].bt_srvid == 0xFFFF || !ttab->ttdata[i].bt_srvid)) + { + return ttab->ttdata[i].bt_caidto; + } + } + } + return caid; +} + +int32_t dvbapi_stop_filter(int32_t demux_id, int32_t type, uint32_t msgid) +{ +#if defined(WITH_COOLAPI) || defined(WITH_COOLAPI2) + // We prevented PAT and PMT from starting, so lets don't close them either. + if(type == TYPE_PAT || type == TYPE_PMT) + { + return 1; + } +#endif + + int32_t g, error = 0; + + // just stop them all, we don't want to risk leaving + // any stale filter running due to lowering of maxfilters + for(g = 0; g < MAX_FILTER; g++) + { + if(demux[demux_id].demux_fd[g].type == type) + { + if(dvbapi_stop_filternum(demux_id, g, msgid) == -1) + { + error = 1; + } + } + } + return !error; // on error return 0, all ok 1 +} + +int32_t dvbapi_stop_filternum(int32_t demux_id, int32_t num, uint32_t msgid) +{ + int32_t retfilter = -1, retfd = -1, fd = demux[demux_id].demux_fd[num].fd, try = 0; + + if(USE_OPENXCAS) + { + demux[demux_id].demux_fd[num].type = 0; + demux[demux_id].demux_fd[num].fd = 0; + return 1; // all ok! + } + + if(fd > 0) + { + do + { + errno = 0; + if(try) + { + cs_sleepms(50); + } + try++; + + cs_log_dbg(D_DVBAPI, "Demuxer %d stop filter %d try %d (fd: %d api: %d, caid: %04X, provid: %06X, %spid: %04X)", + demux_id, + num + 1, + try, + fd, + selected_api, + demux[demux_id].demux_fd[num].caid, + demux[demux_id].demux_fd[num].provid, + demux[demux_id].demux_fd[num].type == TYPE_ECM ? "ecm" : "emm", + demux[demux_id].demux_fd[num].pid); + + switch(selected_api) + { + case DVBAPI_3: + { + if(dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) + { + retfilter = dvbapi_net_send(DVBAPI_DMX_STOP, + demux[demux_id].socket_fd, + msgid, + demux_id, + num, + NULL, + NULL, + NULL, + demux[demux_id].client_proto_version); + } + else + { + retfilter = dvbapi_ioctl(fd, DMX_STOP, NULL); + } + break; + } + + case DVBAPI_1: + { + retfilter = dvbapi_ioctl(fd, DMX_STOP, NULL); + break; + } + +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + case STAPI: + { + retfilter = stapi_remove_filter(demux_id, num, demux[demux_id].pmt_file); + if(retfilter != 1) // stapi returns 0 for error, 1 for all ok + { + retfilter = -1; + } + break; + } +#endif +#if defined WITH_COOLAPI || defined WITH_COOLAPI2 + case COOLAPI: + { + retfilter = coolapi_remove_filter(fd, num); + if(retfilter >= 0) + { + retfd = coolapi_close_device(fd); + } + break; + } +#endif + default: + break; + } + + if(errno == 9) // no error on bad file descriptor + { + retfilter = 0; + } + } while(retfilter < 0 && try < 10); + +#if !defined WITH_COOLAPI && !defined WITH_COOLAPI2 // no fd close for coolapi and stapi, all others do close fd! + try = 0; + do + { + if(try) + { + errno = 0; + cs_sleepms(50); + } + try++; + + // on bad filterfd dont try to close! + if(!dvbapi_listenport_active && cfg.dvbapi_boxtype != BOXTYPE_PC_NODMX && errno != 9) + { + if(selected_api == STAPI) + { + retfd = 0; // stapi closes its own filter fd! + } + else + { + // flush filter input buffer in attempt to avoid overflow receivers internal buffer + flush_read_fd(demux_id, num, fd); + retfd = close(fd); + if(errno == 9) + { + retfd = 0; // no error on bad file descriptor + } + } + } + else + { + retfd = 0; + } + + } while(retfd < 0 && try < 10); +#endif + } + else // fd <=0 + { + return 1; // filter was already killed! + } + + if(retfilter < 0) // error on remove filter + { + cs_log("ERROR: Demuxer %d could not stop Filter %d (fd:%d api:%d errno=%d %s)", + demux_id, num + 1, fd, selected_api, errno, strerror(errno)); + return retfilter; + } + + if(retfd < 0) // error on close filter fd + { + cs_log("ERROR: Demuxer %d could not close fd of Filter %d (fd=%d api:%d errno=%d %s)", + demux_id, num + 1, fd, selected_api, errno, strerror(errno)); + return retfd; + } + + // code below runs only if nothing has gone wrong + if(demux[demux_id].demux_fd[num].type == TYPE_ECM) // ecm filter stopped: reset index! + { + int32_t oldpid = demux[demux_id].demux_fd[num].pidindex; + int32_t curpid = demux[demux_id].pidindex; + + // workaround: below dont run on stapi since it handles it own pids... + // stapi need to be better integrated in oscam dvbapi. + if(selected_api != STAPI) + { + int32_t z; + for(z = 0; z < MAX_STREAM_INDICES; z++) + { + uint32_t idx = demux[demux_id].ECMpids[oldpid].index[z]; + demux[demux_id].ECMpids[oldpid].index[z] = INDEX_INVALID; + + if(idx != INDEX_INVALID) // if in use + { + int32_t i; + for(i = 0; i < demux[demux_id].STREAMpidcount; i++) + { + int8_t match = 0; + + // check streams of old disabled ecmpid + if(!demux[demux_id].ECMpids[oldpid].streams + || ((demux[demux_id].ECMpids[oldpid].streams & (1 << i)) == (uint) (1 << i))) + { + // check if new ecmpid is using same streams + if(curpid != -1 && (!demux[demux_id].ECMpids[curpid].streams + || ((demux[demux_id].ECMpids[curpid].streams & (1 << i)) == (uint) (1 << i)))) + { + continue; // found same stream on old and new ecmpid -> skip! (and leave it enabled!) + } + int32_t pidtobestopped = demux[demux_id].STREAMpids[i]; + int32_t j, k, otherdemuxpid; + uint32_t otherdemuxidx; + + for(j = 0; j < MAX_DEMUX; j++) // check other demuxers for same streampid with same index + { + if(demux[j].program_number == 0 || demux_id == j || demux[j].ca_mask != demux[demux_id].ca_mask) + { + continue; + // skip empty demuxers + // skip same demuxer + // skip streampid running on other ca device + } + + otherdemuxpid = demux[j].pidindex; + if(otherdemuxpid == -1) + { + continue; // Other demuxer not descrambling yet + } + + int32_t y; + for(y = 0; y < MAX_STREAM_INDICES; y++) + { + otherdemuxidx = demux[j].ECMpids[otherdemuxpid].index[y]; + if(otherdemuxidx == INDEX_INVALID || otherdemuxidx != idx) + { + continue; // Other demuxer has no index yet, or index is different + } + + for(k = 0; k < demux[j].STREAMpidcount; k++) + { + if(!demux[j].ECMpids[otherdemuxpid].streams + || ((demux[j].ECMpids[otherdemuxpid].streams & (1 << k)) == (uint) (1 << k))) + { + if(demux[j].STREAMpids[k] == pidtobestopped) + { + continue; // found same streampid enabled with same index + // on one or more other demuxers -> skip! (and leave it enabled!) + } + } + match = 1; // matching stream found + } + } + } + + if(!match) + { + for(j = 0; j < CA_MAX; j++) + { + if(((demux[demux_id].ca_mask & (1 << j)) == (uint32_t) (1 << j))) + { + remove_streampid_from_list(j, pidtobestopped, idx); + break; + } + } + } + } + } + } + } + } + } + + if(demux[demux_id].demux_fd[num].type == TYPE_EMM) // If emm type remove from emm filterlist + { + remove_emmfilter_from_list(demux_id, + demux[demux_id].demux_fd[num].caid, + demux[demux_id].demux_fd[num].provid, + demux[demux_id].demux_fd[num].pid, + num + 1); + } + demux[demux_id].demux_fd[num].type = 0; + demux[demux_id].demux_fd[num].fd = 0; + return 1; // all ok! +} + +void dvbapi_start_filter(int32_t demux_id, int32_t pidindex, uint16_t pid, uint16_t caid, + uint32_t provid, uint8_t table, uint8_t mask, int32_t timeout, int32_t type) +{ + int32_t i; + for(i = 0; i < maxfilter; i++) // check if filter is present + { + if(demux[demux_id].demux_fd[i].fd > 0 && + demux[demux_id].demux_fd[i].pid == pid && + demux[demux_id].demux_fd[i].type == type && + demux[demux_id].demux_fd[i].filter[0] == table && + demux[demux_id].demux_fd[i].mask[0] == mask) + { + return; + } + } + + uint8_t filter[32]; + memset(filter, 0, 32); + filter[0] = table; + filter[16] = mask; + + cs_log_dbg(D_DVBAPI, "Demuxer %d try to start new filter for caid: %04X, provid: %06X, pid: %04X", + demux_id, caid, provid, pid); + + dvbapi_set_filter(demux_id, selected_api, pid, caid, provid, filter, filter + 16, timeout, pidindex, type, 0); +} + +void dvbapi_start_sdt_filter(int32_t demux_id) +{ + dvbapi_start_filter(demux_id, demux[demux_id].pidindex, 0x0011, 0x001, 0x01, 0x42, 0xFF, 0, TYPE_SDT); + demux[demux_id].sdt_filter = 0; +} + +void dvbapi_start_pat_filter(int32_t demux_id) +{ +#if defined(WITH_COOLAPI) || defined(WITH_COOLAPI2) + // PAT-Filter breaks API and OSCAM for Coolstream. + // Don't use it + return; +#endif + dvbapi_start_filter(demux_id, demux[demux_id].pidindex, 0x0000, 0x001, 0x01, 0x00, 0xFF, 0, TYPE_PAT); +} + +void dvbapi_start_pmt_filter(int32_t demux_id) +{ +#if defined(WITH_COOLAPI) || defined(WITH_COOLAPI2) + // PMT-Filter breaks API and OSCAM for Coolstream. + // Don't use it + return; +#endif + uint8_t filter[16], mask[16]; + memset(filter, 0, 16); + memset(mask, 0, 16); + + filter[0] = 0x02; + i2b_buf(2, demux[demux_id].program_number, filter + 1); // add srvid to filter since the pid can deliver pmt for multiple srvid + mask[0] = 0xFF; + mask[1] = 0xFF; + mask[2] = 0xFF; + + dvbapi_set_filter(demux_id, selected_api, demux[demux_id].pmtpid, 0x001, 0x01, filter, mask, 0, 0, TYPE_PMT, 0); +} + +void dvbapi_start_cat_filter(int32_t demux_id) +{ + dvbapi_start_filter(demux_id, demux[demux_id].pidindex, 0x0001, 0x001, 0x01, 0x01, 0xFF, 0, TYPE_CAT); + demux[demux_id].emm_filter = 0; +} + +void dvbapi_start_emm_filter(int32_t demux_id) +{ + unsigned int j; + if(!demux[demux_id].EMMpidcount) + { + return; + } + + struct s_csystem_emm_filter *dmx_filter = NULL; + unsigned int filter_count = 0; + uint16_t caid, ncaid; + uint32_t provid; + struct s_reader *rdr = NULL; + struct s_client *cl = cur_client(); + + if(!cl || !cl->aureader_list) + { + return; + } + + LL_ITER itr = ll_iter_create(cl->aureader_list); + while((rdr = ll_iter_next(&itr))) + { + if(!(rdr->grp & cl->grp)) + { + continue; + } + + if(rdr->audisabled || !rdr->enable || (!is_network_reader(rdr) && rdr->card_status != CARD_INSERTED)) + { + continue; + } + + const struct s_cardsystem *csystem; + uint16_t c, match; + cs_log_dbg(D_DVBAPI, "Demuxer %d matching reader %s against available emmpids -> START!", demux_id, rdr->label); + + for(c = 0; c < demux[demux_id].EMMpidcount; c++) + { + caid = ncaid = demux[demux_id].EMMpids[c].CAID; + if(!caid) continue; + + if(chk_is_betatunnel_caid(caid) == 2) + { + ncaid = tunemm_caid_map(FROM_TO, caid, demux[demux_id].program_number); + } + provid = demux[demux_id].EMMpids[c].PROVID; + + if(caid == ncaid) + { + match = emm_reader_match(rdr, caid, provid); + } + else + { + match = emm_reader_match(rdr, ncaid, provid); + } + + if(match) + { + if(rdr->typ == R_EMU) + { + csystem = rdr->csystem; + } + else + { + csystem = get_cardsystem_by_caid(caid); + } + if(csystem) + { + if(caid != ncaid) + { + csystem = get_cardsystem_by_caid(ncaid); + if(csystem && csystem->get_tunemm_filter) + { + csystem->get_tunemm_filter(rdr, &dmx_filter, &filter_count); + cs_log_dbg(D_DVBAPI, "Demuxer %d setting emm filter for betatunnel: %04X -> %04X", + demux_id, ncaid, caid); + } + else + { + cs_log_dbg(D_DVBAPI, "Demuxer %d cardsystem for emm filter for caid %04X of reader %s not found", + demux_id, ncaid, rdr->label); + continue; + } + } + else if(csystem->get_emm_filter) + { + if(rdr->typ == R_EMU) + { + csystem->get_emm_filter_adv(rdr, &dmx_filter, &filter_count, caid, provid, demux[demux_id].program_number, + demux[demux_id].tsid, demux[demux_id].onid, demux[demux_id].ens); + } + else + { + csystem->get_emm_filter(rdr, &dmx_filter, &filter_count); + } + } + } + else + { + cs_log_dbg(D_DVBAPI, "Demuxer %d cardsystem for emm filter for caid %04X of reader %s not found", + demux_id, caid, rdr->label); + continue; + } + + for(j = 0; j < filter_count ; j++) + { + if(dmx_filter[j].enabled == 0) + { + continue; + } + + uint8_t filter[32]; + memset(filter, 0, sizeof(filter)); // reset filter + uint32_t usefilterbytes = 16; // default use all filters + memcpy(filter, dmx_filter[j].filter, usefilterbytes); + memcpy(filter + 16, dmx_filter[j].mask, usefilterbytes); + int32_t emmtype = dmx_filter[j].type; + + if(filter[0] && (((1 << (filter[0] % 0x80)) & rdr->b_nano) && !((1 << (filter[0] % 0x80)) & rdr->s_nano))) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d reader %s emmfilter %d/%d blocked by userconfig -> SKIP!", + demux_id, rdr->label, j + 1, filter_count); + continue; + } + + if((rdr->blockemm & emmtype) && !(((1 << (filter[0] % 0x80)) & rdr->s_nano) || (rdr->saveemm & emmtype))) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d reader %s emmfilter %d/%d blocked by userconfig -> SKIP!", + demux_id, rdr->label, j + 1, filter_count); + continue; + } + + if(demux[demux_id].EMMpids[c].type & emmtype) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d reader %s emmfilter %d/%d type match -> ENABLE!", + demux_id, rdr->label, j + 1, filter_count); + check_add_emmpid(demux_id, filter, c, emmtype); + } + else + { + cs_log_dbg(D_DVBAPI, "Demuxer %d reader %s emmfilter %d/%d type mismatch -> SKIP!", + demux_id, rdr->label, j + 1, filter_count); + } + } + // dmx_filter not use below this point; + NULLFREE(dmx_filter); + filter_count = 0; + } + } + cs_log_dbg(D_DVBAPI, "Demuxer %d matching reader %s against available emmpids -> DONE!", demux_id, rdr->label); + } + + if(demux[demux_id].emm_filter == -1) // first run -1 + { + demux[demux_id].emm_filter = 0; + } + cs_log_dbg(D_DVBAPI, "Demuxer %d handles %i emm filters", demux_id, demux[demux_id].emm_filter); +} + +void dvbapi_add_ecmpid_int(int32_t demux_id, uint16_t caid, uint16_t ecmpid, uint32_t provid, uint32_t cadata, char *txt) +{ + int32_t n, added = 0; + int32_t stream = demux[demux_id].STREAMpidcount - 1; + + for(n = 0; n < demux[demux_id].ECMpidcount; n++) + { + if(demux[demux_id].ECMpids[n].CAID == caid && demux[demux_id].ECMpids[n].ECM_PID == ecmpid + && (!provid || (provid && demux[demux_id].ECMpids[n].PROVID == provid))) + { + added = 1; + if(stream > -1) + { + if(!demux[demux_id].ECMpids[n].streams) + { + // we already got this caid/ecmpid as global, no need to add the single stream + cs_log_dbg(D_DVBAPI, "Demuxer %d skipped stream CAID: %04X ECM_PID: %04X PROVID: %06X (Same as ECMPID %d)", + demux_id, caid, ecmpid, provid, n); + continue; + } + demux[demux_id].ECMpids[n].streams |= (1 << stream); + cs_log("Demuxer %d added stream to ecmpid %d CAID: %04X ECM_PID: %04X PROVID: %06X", + demux_id, n, caid, ecmpid, provid); + } + } + } + + if(added == 1) + { + return; + } + + if(demux[demux_id].ECMpidcount >= MAX_ECM_PIDS) + { + cs_log("We reached maximum ECMpids: unable to add to demuxer %d ecmpid %d CAID: %04X ECM_PID: %04X PROVID: %06X %s", + demux_id, demux[demux_id].ECMpidcount, caid, ecmpid, provid, txt); + return; + } + + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].ECM_PID = ecmpid; + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].CAID = caid; + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].PROVID = provid; + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].CHID = 0x10000; // reset CHID + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].checked = 0; + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].status = 0; + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].tries = 0xFE; + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].streams = 0; // reset streams! + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].irdeto_curindex = 0xFE; // reset + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].irdeto_maxindex = 0; // reset + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].irdeto_cycle = 0xFE; // reset + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].table = 0; + demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].cadata = cadata; + + cs_log("Demuxer %d ecmpid %d CAID: %04X ECM_PID: %04X PROVID: %06X %s", + demux_id, demux[demux_id].ECMpidcount, caid, ecmpid, provid, txt); + + // marker to fetch emms early irdeto needs them! + if(caid_is_irdeto(caid) || (caid_is_dre(caid) && (provid == 0x11 || provid == 0xFE))) + { + demux[demux_id].emmstart.time = 1; + } + demux[demux_id].ECMpidcount++; +} + +void dvbapi_add_ecmpid(int32_t demux_id, uint16_t caid, uint16_t ecmpid, uint32_t provid, uint32_t cadata, char *txt) +{ + dvbapi_add_ecmpid_int(demux_id, caid, ecmpid, provid, cadata, txt); + struct s_dvbapi_priority *joinentry; + + for(joinentry = dvbapi_priority; joinentry != NULL; joinentry = joinentry->next) + { + if((joinentry->type != 'j') + || (joinentry->caid && joinentry->caid != caid) + || (joinentry->provid && joinentry->provid != provid) + || (joinentry->ecmpid && joinentry->ecmpid != ecmpid) + || (joinentry->srvid && joinentry->srvid != demux[demux_id].program_number)) + { + continue; + } + + cs_log_dbg(D_DVBAPI, "Join ecmpid %04X@%06X:%04X to %04X@%06X:%04X", + caid, provid, ecmpid, joinentry->mapcaid, joinentry->mapprovid, joinentry->mapecmpid); + dvbapi_add_ecmpid_int(demux_id, joinentry->mapcaid, joinentry->mapecmpid, joinentry->mapprovid, 0, txt); + } +} + +void dvbapi_add_emmpid(int32_t demux_id, uint16_t caid, uint16_t emmpid, uint32_t provid, uint32_t cadata, uint8_t type) +{ + char typetext[40]; + char cadatatext[40]; + cs_strncpy(typetext, ":", sizeof(typetext)); + + if(type & 0x01) { cs_strncat(typetext, "UNIQUE:", sizeof(typetext)); } + if(type & 0x02) { cs_strncat(typetext, "SHARED:", sizeof(typetext)); } + if(type & 0x04) { cs_strncat(typetext, "GLOBAL:", sizeof(typetext)); } + if(type & 0xF8) { cs_strncat(typetext, "UNKNOWN:", sizeof(typetext)); } + + if(cadata > 0) + { + snprintf(cadatatext, 40, " CA DATA %X ", cadata); + } + else + { + cadatatext[0] = '\t'; cadatatext[1] = '\0'; + } + + if(caid == 0x4AE1 && provid == 0x11 && cadata == 0) + { + return; + } + + uint16_t i; + for(i = 0; i < demux[demux_id].EMMpidcount; i++) + { + if(demux[demux_id].EMMpids[i].PID == emmpid && demux[demux_id].EMMpids[i].CAID == caid && + demux[demux_id].EMMpids[i].PROVID == provid && demux[demux_id].EMMpids[i].cadata == cadata) + { + if(!(demux[demux_id].EMMpids[i].type & type)) + { + demux[demux_id].EMMpids[i].type |= type; // register this emm kind to this emmpid + cs_log_dbg(D_DVBAPI, "Added to existing emmpid %d additional emmtype %s", + demux[demux_id].EMMpidcount - 1, typetext); + } + return; + } + } + + if(i < MAX_EMM_PIDS) + { + demux[demux_id].EMMpids[demux[demux_id].EMMpidcount].PID = emmpid; + demux[demux_id].EMMpids[demux[demux_id].EMMpidcount].CAID = caid; + demux[demux_id].EMMpids[demux[demux_id].EMMpidcount].PROVID = provid; + demux[demux_id].EMMpids[demux[demux_id].EMMpidcount].type = type; + demux[demux_id].EMMpids[demux[demux_id].EMMpidcount++].cadata = cadata; + cs_log_dbg(D_DVBAPI, "Added new emmpid %d CAID: %04X EMM_PID: %04X PROVID: %06X%sTYPE %s", + demux[demux_id].EMMpidcount - 1, caid, emmpid, provid, cadatatext, typetext); + } + else + { + cs_log_dbg(D_DVBAPI, "We reached max emmpids: unable to add new emmpid %d CAID: %04X EMM_PID: %04X PROVID: %06X%sTYPE %s", + demux[demux_id].EMMpidcount - 1, caid, emmpid, provid, cadatatext, typetext); + } +} + +static void dvbapi_parse_cat_ca_descriptor(int32_t demux_id, const uint8_t *buffer, uint8_t descriptor_length) +{ + uint16_t i, ca_system_id, ca_pid; + uint32_t ca_provider = 0, ca_data = 0; + + if(descriptor_length < 4) + { + return; // CA descriptor has a minimum length of 4 bytes + } + + ca_system_id = b2i(2, buffer); + ca_pid = b2i(2, buffer + 2) & 0x1FFF; + + if(ca_system_id == 0x0000 || ca_pid == 0x1FFF) + { + return; // This is not a valid CAID or EMM pid + } + + switch(ca_system_id >> 8) + { + case 0x01: + { + dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, 0, 0, EMM_UNIQUE | EMM_GLOBAL); + + for(i = 5; i < descriptor_length; i += 4) + { + ca_pid = b2i(2, buffer + i) & 0x1FFF; + ca_provider = b2i(2, buffer + i + 2); + + dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, ca_provider, 0, EMM_SHARED); + } + break; + } + + case 0x05: + { + for(i = 4; i < descriptor_length; i += 2 + buffer[i + 1]) + { + if(buffer[i] == 0x14) + { + ca_provider = (b2i(3, buffer + i + 2) & 0xFFFFF0); // viaccess fixup: don't care about last digit + dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, ca_provider, 0, EMM_UNIQUE | EMM_SHARED | EMM_GLOBAL); + } + } + break; + } + + case 0x18: + { + if(descriptor_length == 0x07 || descriptor_length == 0x0B) + { + for(i = 5; i < 5 + buffer[4]; i += 2) + { + ca_provider = b2i(2, buffer + i); + dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, ca_provider, 0, EMM_UNIQUE | EMM_SHARED | EMM_GLOBAL); + } + } + else + { + dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, ca_provider, 0, EMM_UNIQUE | EMM_SHARED | EMM_GLOBAL); + } + break; + } + + case 0x27: + case 0x4A: + { + if(caid_is_bulcrypt(ca_system_id)) + { + dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, 0, 0, EMM_UNIQUE | EMM_SHARED | EMM_GLOBAL); + break; + } + + ca_provider = buffer[4]; + + if(buffer[4] == 0xFE) + { + dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, ca_provider, 0x102, EMM_GLOBAL); + } + else + { + if(descriptor_length == 0x0A) + { + ca_data = b2i(4, buffer + 6); + } + + dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, ca_provider, ca_data, EMM_UNIQUE | EMM_SHARED | EMM_GLOBAL); + } + break; + } + + default: + dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, 0, 0, EMM_UNIQUE | EMM_SHARED | EMM_GLOBAL); + break; + } +} + +static void dvbapi_parse_cat(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint32_t msgid) +{ +#if defined WITH_COOLAPI || defined WITH_COOLAPI2 + // driver sometimes reports error if too many emm filter + // but adding more ecm filter is no problem + // ... so ifdef here instead of limiting MAX_FILTER + demux[demux_id].max_emm_filter = 14; +#else + if(cfg.dvbapi_requestmode == 1) + { + uint16_t ecm_filter_needed = 0, n; + for(n = 0; n < demux[demux_id].ECMpidcount; n++) + { + if(demux[demux_id].ECMpids[n].status > -1) + { + ecm_filter_needed++; + } + } + + if(maxfilter - ecm_filter_needed <= 0) + { + demux[demux_id].max_emm_filter = 0; + } + else + { + demux[demux_id].max_emm_filter = maxfilter - ecm_filter_needed; + } + } + else + { + demux[demux_id].max_emm_filter = maxfilter - 1; + } +#endif + + uint16_t i; + uint8_t descriptor_tag, descriptor_length; + + dvbapi_stop_filter(demux_id, TYPE_CAT, msgid); + + for(i = 8; i + 1 < length; i += 2 + descriptor_length) + { + descriptor_tag = buffer[i]; + descriptor_length = buffer[i + 1]; + + if(descriptor_tag == 0x09) // There should be only CA descriptors here + { + dvbapi_parse_cat_ca_descriptor(demux_id, buffer + i + 2, descriptor_length); + } + } +} + +static pthread_mutex_t lockindex = PTHREAD_MUTEX_INITIALIZER; + +uint32_t dvbapi_get_desc_index(int32_t demux_id, int32_t pid, int32_t stream_id) +{ + int32_t i, j, k, fail = 1; + uint32_t idx = 0; + + if(cfg.dvbapi_boxtype == BOXTYPE_NEUMO) + { + sscanf(demux[demux_id].pmt_file, "pmt%3d.tmp", &idx); + return idx; + } + + SAFE_MUTEX_LOCK(&lockindex); // to avoid race when readers become responsive! + while(fail && idx <= INDEX_MAX) + { + fail = 0; + for(i = 0; i < MAX_DEMUX && !fail && idx < INDEX_MAX; i++) + { + if(demux[i].program_number == 0) + { + continue; // skip empty demuxers + } + + if(demux[i].ca_mask != demux[demux_id].ca_mask + && (!(cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX))) + { + continue; // skip demuxer using other ca device + } + + for(j = 0; j < demux[i].ECMpidcount && !fail; j++) // search for new unique index + { + for(k = 0; k < MAX_STREAM_INDICES; k++) + { + if(demux[i].ECMpids[j].index[k] == idx) + { + fail = 1; + idx++; + } + } + } + } + } + + if(selected_api == DVBAPI_3 || selected_api == DVBAPI_1) + { + // make sure we haven't run out of descramblers + if(ca_descramblers_used < ca_descramblers_total) + { + ca_descramblers_used++; // increase number of descramblers used + } + else + { + idx = INDEX_INVALID; // we don't have any descramblers left + } + } + else // old style check for STAPI, COOLAPI, etc + { + if(idx > ca_descramblers_total) + { + idx = INDEX_INVALID; // we don't have any descramblers left + } + } + + demux[demux_id].ECMpids[pid].index[stream_id] = idx; + SAFE_MUTEX_UNLOCK(&lockindex); // and release it! + + return idx; +} + +void dvbapi_set_pid(int32_t demux_id, int32_t num, uint32_t idx, bool enable, bool use_des, uint32_t msgid) +{ + int32_t i, currentfd; + uint16_t streampid = demux[demux_id].STREAMpids[num]; + uint32_t newidx = 0, curidx; + ca_pid_t ca_pid2; + + if(demux[demux_id].pidindex == -1 && enable) + { + return; // no current pid on enable? --> exit + } + + switch(selected_api) + { +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + case STAPI: + if(!enable) idx = INDEX_INVALID; + stapi_set_pid(demux_id, num, idx, streampid, demux[demux_id].pmt_file); // only used to disable pids!!! + break; +#endif + +#if defined WITH_COOLAPI || defined WITH_COOLAPI2 + case COOLAPI: + break; +#endif + + default: + for(i = 0; i < CA_MAX; i++) + { + newidx = INDEX_INVALID; + curidx = idx; + + if(((demux[demux_id].ca_mask & (1 << i)) == (uint32_t) (1 << i))) + { + uint32_t action = 0; + if(enable) + { + action = update_streampid_list(i, streampid, curidx, use_des); + } + + if(!enable) + { + action = remove_streampid_from_list(i, streampid, curidx); + } + + if(action != NO_STREAMPID_LISTED && action != INVALID_STREAMPID_INDEX && + action != FOUND_STREAMPID_INDEX && action != ADDED_STREAMPID_INDEX && + action != REMOVED_STREAMPID_INDEX) + { + // removed last index of this streampid on ca? -> disable this pid with -1 on this ca + if((action == REMOVED_STREAMPID_LASTINDEX || action == FIRST_STREAMPID_INDEX) + && (is_ca_used(i, streampid) == INDEX_INVALID)) + { + curidx = DVBAPI_INDEX_DISABLE; + } + + // removed index of streampid that is used to decode on ca -> get a fresh one + if(action == REMOVED_DECODING_STREAMPID_INDEX || action == FIRST_STREAMPID_INDEX) + { + newidx = is_ca_used(i, streampid); // get an active index for this pid and enable it on ca device + curidx = DVBAPI_INDEX_DISABLE; + } + + while(curidx != INDEX_INVALID || newidx != INDEX_INVALID) + { + memset(&ca_pid2, 0, sizeof(ca_pid2)); + ca_pid2.pid = streampid; + if(curidx != INDEX_INVALID) + { + (curidx == DVBAPI_INDEX_DISABLE) ? (ca_pid2.index = -1) : (ca_pid2.index = curidx); + cs_log_dbg(D_DVBAPI, "Demuxer %d %s stream %d pid=0x%04x index=%d on ca%d", + demux_id, + ((enable && curidx != DVBAPI_INDEX_DISABLE) ? "enable" : "disable"), + num + 1, + ca_pid2.pid, + ca_pid2.index, + i); + curidx = INDEX_INVALID; // flag this index as handled + } + else if(newidx != INDEX_INVALID) + { + (newidx == DVBAPI_INDEX_DISABLE) ? (ca_pid2.index = -1) : (ca_pid2.index = newidx); + cs_log_dbg(D_DVBAPI, "Demuxer %d %s stream %d pid=0x%04x by index=%d on ca%d", + demux_id, + ((enable && action == FIRST_STREAMPID_INDEX) ? "enable" : "takeover"), + num + 1, + ca_pid2.pid, + ca_pid2.index, + i); + newidx = INDEX_INVALID; // flag this takeover / new index as handled + } +#ifdef WITH_EXTENDED_CW + if(use_des && cfg.dvbapi_extended_cw_api == 2 && ca_pid2.index != -1) + { + ca_pid2.index |= 0x100; // flag DES algo through pid index + } +#endif + if(cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) + { + dvbapi_net_send(DVBAPI_CA_SET_PID, demux[demux_id].socket_fd, + msgid, + demux_id, + -1 /*unused*/, + (uint8_t *) &ca_pid2, + NULL, + NULL, + demux[demux_id].client_proto_version); + } + else + { + if(ca_soft_csa[demux_id]) + { + continue; + } + + currentfd = ca_fd[i]; + if(currentfd <= 0) + { + currentfd = dvbapi_open_device(1, i, demux[demux_id].adapter_index); + ca_fd[i] = currentfd; // save fd of this ca + } + + if(currentfd > 0) + { + if(dvbapi_ioctl(currentfd, CA_SET_PID, &ca_pid2) == -1) + { + cs_log_dbg(D_TRACE | D_DVBAPI,"CA_SET_PID ioctl error (errno=%d %s)", errno, strerror(errno)); + remove_streampid_from_list(i, ca_pid2.pid, INDEX_DISABLE_ALL); + } + + uint32_t result = is_ca_used(i, 0); // check if in use by any pid + if(result == INDEX_INVALID) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d close now unused CA%d device", demux_id, i); + int32_t ret = close(currentfd); + if(ret < 0) + { + cs_log("ERROR: Could not close demuxer fd (errno=%d %s)", errno, strerror(errno)); + } + ca_fd[i] = 0; + } + } + } + } + } + } + } + break; + } + return; +} + +void dvbapi_stop_all_descrambling(uint32_t msgid) +{ + int32_t j; + + for(j = 0; j < MAX_DEMUX; j++) + { + if(demux[j].program_number == 0) + { + continue; + } + + dvbapi_stop_descrambling(j, msgid); + } +} + +void dvbapi_stop_all_cat_emm_sdt_filtering(uint32_t msgid) +{ + int32_t j; + + for(j = 0; j < MAX_DEMUX; j++) + { + if(demux[j].program_number == 0) + { + continue; + } + + dvbapi_stop_filter(j, TYPE_EMM, msgid); + dvbapi_stop_filter(j, TYPE_SDT, msgid); + dvbapi_stop_filter(j, TYPE_CAT, msgid); + demux[j].emm_filter = -1; + } +} + +void dvbapi_stop_descrambling(int32_t demux_id, uint32_t msgid) +{ + int32_t i, j, z; + if(demux[demux_id].program_number == 0) { return; } + char channame[CS_SERVICENAME_SIZE]; + + i = demux[demux_id].pidindex; + if(i < 0) + { + i = 0; + } + + demux[demux_id].pidindex = -1; // no ecmpid is to be descrambling since we start stop descrambling! + + get_servicename(dvbapi_client, demux[demux_id].program_number, + demux[demux_id].ECMpidcount > 0 ? demux[demux_id].ECMpids[i].PROVID : NO_PROVID_VALUE, + demux[demux_id].ECMpidcount > 0 ? demux[demux_id].ECMpids[i].CAID : NO_CAID_VALUE, + channame, sizeof(channame)); + + cs_log("Demuxer %d stopped descrambling for program %04X (%s)", + demux_id, demux[demux_id].program_number, channame); + + dvbapi_stop_filter(demux_id, TYPE_EMM, msgid); + dvbapi_stop_filter(demux_id, TYPE_SDT, msgid); + dvbapi_stop_filter(demux_id, TYPE_PAT, msgid); + dvbapi_stop_filter(demux_id, TYPE_PMT, msgid); + dvbapi_stop_filter(demux_id, TYPE_CAT, msgid); + + for(i = 0; i < demux[demux_id].ECMpidcount && demux[demux_id].ECMpidcount > 0; i++) + { + for(j = 0; j < MAX_STREAM_INDICES; j++) + { + if(demux[demux_id].ECMpids[i].index[j] == INDEX_INVALID) + { + continue; + } + + // disable streams! + for(z = 0; z < demux[demux_id].STREAMpidcount; z++) + { + dvbapi_set_pid(demux_id, z, demux[demux_id].ECMpids[i].index[j], false, false, msgid); // disable streampid + } + demux[demux_id].ECMpids[i].index[j] = INDEX_INVALID; + } + } + dvbapi_stop_filter(demux_id, TYPE_ECM, msgid); + + pthread_mutex_destroy(&demux[demux_id].answerlock); + memset(&demux[demux_id], 0 , sizeof(DEMUXTYPE)); + + SAFE_MUTEX_INIT(&demux[demux_id].answerlock, NULL); + for(i = 0; i < MAX_ECM_PIDS; i++) + { + for(j = 0; j < MAX_STREAM_INDICES; j++) + { + demux[demux_id].ECMpids[i].index[j] = INDEX_INVALID; + } + } + demux[demux_id].pidindex = -1; + demux[demux_id].curindex = -1; + + if(!dvbapi_listenport_active && cfg.dvbapi_boxtype != BOXTYPE_PC_NODMX) + { + unlink(ECMINFO_FILE); + } + return; +} + +int32_t dvbapi_start_descrambling(int32_t demux_id, int32_t pid, int8_t checked, uint32_t msgid) +{ + int32_t started = 0; // in case ecmfilter started = 1 + int32_t fake_ecm = 0; + ECM_REQUEST *er; + struct s_reader *rdr; + + if(!(er = get_ecmtask())) + { + return started; + } + demux[demux_id].ECMpids[pid].checked = checked + 1; // mark this pid as checked! + + struct s_dvbapi_priority *p; + for(p = dvbapi_priority; p != NULL ; p = p->next) + { + if((p->type != 'p') + || (p->caid && p->caid != demux[demux_id].ECMpids[pid].CAID) + || (p->provid && p->provid != demux[demux_id].ECMpids[pid].PROVID) + || (p->ecmpid && p->ecmpid != demux[demux_id].ECMpids[pid].ECM_PID) + || (p->srvid && p->srvid != demux[demux_id].program_number) + || (p->pidx && p->pidx-1 != pid) + || (p->cadata && p->cadata != demux[demux_id].ECMpids[pid].cadata)) + { + continue; + } + + // if found chid and first run apply chid filter, on forced pids always apply! + if(p->type == 'p' && p->chid < 0x10000 && (demux[demux_id].ECMpids[pid].checked == 1 || (p && p->force))) + { + if(demux[demux_id].ECMpids[pid].CHID < 0x10000) // channelcache delivered chid + { + er->chid = demux[demux_id].ECMpids[pid].CHID; + } + else + { + er->chid = p->chid; // no channelcache or no chid in use, so use prio chid + demux[demux_id].ECMpids[pid].CHID = p->chid; + } + //cs_log("********* CHID %04X **************", demux[demux_id].ECMpids[pid].CHID); + break; // we only accept one! + } + else + { + if(demux[demux_id].ECMpids[pid].CHID < 0x10000) // channelcache delivered chid + { + er->chid = demux[demux_id].ECMpids[pid].CHID; + } + else // no channelcache or no chid in use + { + er->chid = 0; + demux[demux_id].ECMpids[pid].CHID = 0x10000; + } + } + } + + er->srvid = demux[demux_id].program_number; + er->caid = demux[demux_id].ECMpids[pid].CAID; + er->pid = demux[demux_id].ECMpids[pid].ECM_PID; + er->prid = demux[demux_id].ECMpids[pid].PROVID; + er->vpid = demux[demux_id].ECMpids[pid].VPID; + er->pmtpid = demux[demux_id].pmtpid; + er->onid = demux[demux_id].onid; + er->tsid = demux[demux_id].tsid; + er->ens = demux[demux_id].ens; + er->msgid = msgid; + +#ifdef WITH_STAPI5 + cs_strncpy(er->dev_name, dev_list[demux[demux_id].dev_index].name, sizeof(dev_list[demux[demux_id].dev_index].name)); +#endif + + for(rdr = first_active_reader; rdr != NULL ; rdr = rdr->next) + { + int8_t match = matching_reader(er, rdr); // check for matching reader + + if(p && p->force) + { + match = 1; // forced pid always started! + } + + if(!match) // if this reader does not match, check betatunnel for it + { + match = lb_check_auto_betatunnel(er, rdr); + } + + if(!match && chk_is_betatunnel_caid(er->caid)) // these caids might be tunneled invisible by peers + { + match = 1; // so make it a match to try it! + } + + // check if cache-ex is matching + if(config_enabled(CS_CACHEEX) && (!match && (cacheex_is_match_alias(dvbapi_client, er)))) + { + match = 1; // so make it a match to try it! + } + + // BISS1 and BISS2 mode 1/E or FAKE caid + // ecm pid is fake, so send out one fake ecm request + // special treatment: if we asked the cw first without starting a filter, + // the cw request will be killed due to no ecmfilter started + if(caid_is_fake(demux[demux_id].ECMpids[pid].CAID) || caid_is_biss_fixed(demux[demux_id].ECMpids[pid].CAID)) + { + int32_t j, n; + er->ecmlen = 7; + er->ecm[0] = 0x80; // to pass the cache check it must be 0x80 or 0x81 + er->ecm[1] = 0x00; + er->ecm[2] = 0x04; + i2b_buf(2, er->srvid, er->ecm + 3); + i2b_buf(2, er->pmtpid, er->ecm + 5); + + for(j = 0, n = 7; j < demux[demux_id].STREAMpidcount; j++, n += 2) + { + i2b_buf(2, demux[demux_id].STREAMpids[j], er->ecm + n); + er->ecm[2] += 2; + er->ecmlen += 2; + } + + er->ens &= 0x0FFFFFFF; // clear top 4 bits (in case of DVB-T/C or garbage), prepare for flagging + er->ens |= 0xA0000000; // flag to emu: this is the namespace, not a pid + + i2b_buf(2, er->tsid, er->ecm + er->ecmlen); // place tsid after the last stream pid + i2b_buf(2, er->onid, er->ecm + er->ecmlen + 2); // place onid right after tsid + i2b_buf(4, er->ens, er->ecm + er->ecmlen + 4); // place namespace at the end of the ecm + + er->ecm[2] += 8; + er->ecmlen += 8; + + cs_log("Demuxer %d trying to descramble PID %d CAID %04X PROVID %06X ECMPID %04X ANY CHID PMTPID %04X VPID %04X", + demux_id, + pid, + demux[demux_id].ECMpids[pid].CAID, + demux[demux_id].ECMpids[pid].PROVID, + demux[demux_id].ECMpids[pid].ECM_PID, + demux[demux_id].pmtpid, + demux[demux_id].ECMpids[pid].VPID); + + demux[demux_id].curindex = pid; // set current pid to the fresh started one + dvbapi_start_filter(demux_id, + pid, + demux[demux_id].ECMpids[pid].ECM_PID, + demux[demux_id].ECMpids[pid].CAID, + demux[demux_id].ECMpids[pid].PROVID, + 0x80, + 0xF0, + 3000, + TYPE_ECM); + + started = 1; + request_cw(dvbapi_client, er, demux_id, 0); // do not register ecm since this try! + fake_ecm = 1; + break; // we started an ecmfilter so stop looking for next matching reader! + } + + if(match) // if matching reader found check for irdeto cas if local irdeto card check if it received emms in last 60 minutes + { + if(caid_is_irdeto(er->caid)) // irdeto cas init irdeto_curindex to wait for first index (00) + { + if(demux[demux_id].ECMpids[pid].irdeto_curindex == 0xFE) + { + demux[demux_id].ECMpids[pid].irdeto_curindex = 0x00; + } + } + + if(p && p->chid < 0x10000) // do we prio a certain chid? + { + cs_log("Demuxer %d trying to descramble PID %d CAID %04X PROVID %06X ECMPID %04X CHID %04X PMTPID %04X VPID %04X", + demux_id, pid, + demux[demux_id].ECMpids[pid].CAID, + demux[demux_id].ECMpids[pid].PROVID, + demux[demux_id].ECMpids[pid].ECM_PID, + demux[demux_id].ECMpids[pid].CHID, + demux[demux_id].pmtpid, + demux[demux_id].ECMpids[pid].VPID); + } + else + { + cs_log("Demuxer %d trying to descramble PID %d CAID %04X PROVID %06X ECMPID %04X ANY CHID PMTPID %04X VPID %04X", + demux_id, pid, + demux[demux_id].ECMpids[pid].CAID, + demux[demux_id].ECMpids[pid].PROVID, + demux[demux_id].ECMpids[pid].ECM_PID, + demux[demux_id].pmtpid, + demux[demux_id].ECMpids[pid].VPID); + } + + demux[demux_id].curindex = pid; // set current pid to the fresh started one + dvbapi_start_filter(demux_id, + pid, + demux[demux_id].ECMpids[pid].ECM_PID, + demux[demux_id].ECMpids[pid].CAID, + demux[demux_id].ECMpids[pid].PROVID, + 0x80, + 0xF0, + 3000, + TYPE_ECM); + + started = 1; + break; // we started an ecmfilter so stop looking for next matching reader! + } + } + + if(demux[demux_id].curindex != pid) + { + cs_log("Demuxer %d impossible to descramble PID %d CAID %04X PROVID %06X ECMPID %04X PMTPID %04X (NO MATCHING READER)", + demux_id, pid, + demux[demux_id].ECMpids[pid].CAID, + demux[demux_id].ECMpids[pid].PROVID, + demux[demux_id].ECMpids[pid].ECM_PID, + demux[demux_id].pmtpid); + + demux[demux_id].ECMpids[pid].checked = 4; // flag this pid as checked + demux[demux_id].ECMpids[pid].status = -1; // flag this pid as unusable + dvbapi_edit_channel_cache(demux_id, pid, 0); // remove this pid from channelcache + } + + if(!fake_ecm) + { + NULLFREE(er); + } + return started; +} + +struct s_dvbapi_priority *dvbapi_check_prio_match_emmpid(int32_t demux_id, uint16_t caid, uint32_t provid, char type) +{ + struct s_dvbapi_priority *p; + int32_t i; + uint16_t ecm_pid = 0; + + for(i = 0; i < demux[demux_id].ECMpidcount; i++) + { + if((demux[demux_id].ECMpids[i].CAID == caid) && (demux[demux_id].ECMpids[i].PROVID == provid)) + { + ecm_pid = demux[demux_id].ECMpids[i].ECM_PID; + break; + } + } + + if(!ecm_pid) + { + return NULL; + } + + for(p = dvbapi_priority; p != NULL; p = p->next) + { + if(p->type != type + || (p->caid && p->caid != caid) + || (p->provid && p->provid != provid) + || (p->ecmpid && p->ecmpid != ecm_pid) + || (p->srvid && p->srvid != demux[demux_id].program_number) + || (p->pidx && p->pidx-1 !=i) + || (p->type == 'i' && (p->chid < 0x10000))) + { + continue; + } + + return p; + } + return NULL; +} + +struct s_dvbapi_priority *dvbapi_check_prio_match(int32_t demux_id, int32_t pidindex, char type) +{ + if(!dvbapi_priority) + { + return NULL; + } + + struct s_dvbapi_priority *p; + struct s_ecmpid *ecmpid = &demux[demux_id].ECMpids[pidindex]; + + for(p = dvbapi_priority; p != NULL; p = p->next) + { + if(p->type != type + || (p->caid && p->caid != ecmpid->CAID) + || (p->provid && p->provid != ecmpid->PROVID) + || (p->ecmpid && p->ecmpid != ecmpid->ECM_PID) + || (p->srvid && p->srvid != demux[demux_id].program_number) + || (p->pidx && p->pidx-1 != pidindex) + || (p->chid < 0x10000 && p->chid != ecmpid->CHID)) + { + continue; + } + + return p; + } + return NULL; +} + +void dvbapi_process_emm(int32_t demux_id, int32_t filter_num, uint8_t *buffer, uint32_t len) +{ + EMM_PACKET epg; + + struct s_emm_filter *filter = get_emmfilter_by_filternum(demux_id, filter_num + 1); // 0 is used for pending emmfilters, so everything increase 1 + if(!filter) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d no filter matches -> SKIP!", demux_id, filter_num + 1); + return; + } + + uint32_t provider = filter->provid; + uint16_t caid = filter->caid; + struct s_dvbapi_priority *mapentry = dvbapi_check_prio_match_emmpid(filter->demux_id, filter->caid, filter->provid, 'm'); + if(mapentry) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d mapping EMM from %04X@%06X to %04X@%06X", + demux_id, caid, provider, mapentry->mapcaid, mapentry->mapprovid); + + caid = mapentry->mapcaid; + provider = mapentry->mapprovid; + } + + memset(&epg, 0, sizeof(epg)); + i2b_buf(2, caid, epg.caid); + i2b_buf(4, provider, epg.provid); + epg.emmlen = len > sizeof(epg.emm) ? sizeof(epg.emm) : len; + memcpy(epg.emm, buffer, epg.emmlen); + + if(config_enabled(READER_IRDETO) && chk_is_betatunnel_caid(caid) == 2) + { + uint16_t ncaid = tunemm_caid_map(FROM_TO, caid, demux[demux_id].program_number); + if(caid != ncaid) + { + irdeto_add_emm_header(&epg); + i2b_buf(2, ncaid, epg.caid); + } + } + do_emm(dvbapi_client, &epg); +} + +void dvbapi_read_priority(void) +{ + FILE *fp; + char token[128], str1[128]; + char type; + int32_t i, ret, count = 0; + const char *cs_prio = "oscam.dvbapi"; + + fp = fopen(get_config_filename(token, sizeof(token), cs_prio), "r"); + if(!fp) + { + cs_log_dbg(D_DVBAPI, "ERROR: Can't open priority file %s", token); + return; + } + + if(dvbapi_priority) + { + cs_log_dbg(D_DVBAPI, "reread priority file %s", cs_prio); + struct s_dvbapi_priority *o, *p; + for(p = dvbapi_priority; p != NULL; p = o) + { + o = p->next; + NULLFREE(p); + } + dvbapi_priority = NULL; + } + + while(fgets(token, sizeof(token), fp)) + { + // Ignore comments and empty lines + if(token[0] == '#' || token[0] == '/' || token[0] == '\n' || token[0] == '\r' || token[0] == '\0' || cs_strlen(token) > 100) + { + continue; + } + memset(str1, 0, 128); + + for(i = 0; i < (int)cs_strlen(token) && token[i] == ' '; i++) { ; } + + if(i == (int)cs_strlen(token) - 1) // empty line or all spaces + { + continue; + } + + for(i = 0; i < (int)cs_strlen(token); i++) + { + if(token[i] == '@') + { + token[i] = ':'; + } + } + + for(i = 0; i < (int)cs_strlen(token); i++) + { + if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':') // if "::" or " :" + { + memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1); // insert extra position + token[i + 1] = '0'; // and fill it with NULL + } + + if(token[i] == '#' || token[i] == '/') + { + token[i] = '\0'; + break; + } + } + + type = 0; +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + uint32_t disablefilter = 0; + ret = sscanf(trim(token), "%c: %63s %63s %d", &type, str1, str1 + 64, &disablefilter); +#else + ret = sscanf(trim(token), "%c: %63s %63s", &type, str1, str1 + 64); +#endif + type = tolower((uint8_t)type); + + if(ret < 1 || (type != 'p' && type != 'i' && type != 'm' && type != 'd' && + type != 's' && type != 'l' && type != 'j' && type != 'a' && type != 'x')) + { + //fprintf(stderr, "Warning: line containing %s in %s not recognized, ignoring line\n", token, cs_prio); + // fprintf would issue the warning to the command line, which is more consistent with other config warnings + // however it takes OSCam a long time (>4 seconds) to reach this part of the program, so the warnings are + // reaching tty rather late which leads to confusion. So send the warnings to log file instead + cs_log_dbg(D_DVBAPI, "WARN: line containing %s in %s not recognized, ignoring...", token, cs_prio); + continue; + } + + struct s_dvbapi_priority *entry; + if(!cs_malloc(&entry, sizeof(struct s_dvbapi_priority))) + { + ret = fclose(fp); + if(ret < 0) + { + cs_log("ERROR: Could not close oscam.dvbapi fd (errno=%d %s)", errno, strerror(errno)); + } + return; + } + + entry->type = type; + entry->next = NULL; + count++; + +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + if(type == 's') + { + cs_strncpy(entry->devname, str1, sizeof(entry->devname)); + cs_strncpy(entry->pmtfile, str1 + 64, sizeof(entry->pmtfile)); + entry->disablefilter = disablefilter; + cs_log_dbg(D_DVBAPI, "stapi prio: ret=%d | %c: %s %s | disable %d", + ret, type, entry->devname, entry->pmtfile, disablefilter); + + if(!dvbapi_priority) + { + dvbapi_priority = entry; + } + else + { + struct s_dvbapi_priority *p; + for(p = dvbapi_priority; p->next != NULL; p = p->next) { ; } + p->next = entry; + } + continue; + } +#endif + char c_srvid[34]; + c_srvid[0] = '\0'; + uint32_t caid = 0, provid = 0, srvid = 0, ecmpid = 0, cadata = 0;; + uint32_t chid = 0x10000; //chid=0 is a valid chid + + ret = sscanf(str1, "%4x:%6x:%33[^:]:%4x:%4x:%8x"SCNx16, &caid, &provid, c_srvid, &ecmpid, &chid, &cadata); + if(ret < 1) + { + cs_log("Error in oscam.dvbapi: ret=%d | %c: %04X %06X %s %04X %04X", + ret, type, caid, provid, c_srvid, ecmpid, chid); + continue; // skip this entry! + } + else + { + cs_log_dbg(D_DVBAPI, "Parsing rule: ret=%d | %c: %04X %06X %s %04X %04X %04X", + ret, type, caid, provid, c_srvid, ecmpid, chid, cadata); + } + + entry->caid = caid; + entry->provid = provid; + entry->ecmpid = ecmpid; + entry->chid = chid; + entry->cadata = cadata; + uint32_t delay = 0, force = 0, mapcaid = 0, mapprovid = 0, mapecmpid = 0, pidx = 0; + + switch(type) + { + case 'i': + ret = sscanf(str1 + 64, "%1d", &pidx); + entry->pidx = pidx + 1; + if(ret < 1) entry->pidx = 0; + break; + + case 'd': + sscanf(str1 + 64, "%4d", &delay); + entry->delay = delay; + break; + + case 'l': + entry->delay = dyn_word_atob(str1 + 64); + if(entry->delay == -1) { entry->delay = 0; } + break; + + case 'p': + ret = sscanf(str1 + 64, "%1d:%1d", &force, &pidx); + entry->force = force; + entry->pidx = pidx + 1; + if(ret < 2) entry->pidx = 0; + break; + + case 'm': + sscanf(str1 + 64, "%4x:%6x", &mapcaid, &mapprovid); + if(!mapcaid) { mapcaid = 0xFFFF; } + entry->mapcaid = mapcaid; + entry->mapprovid = mapprovid; + break; + + case 'a': + case 'j': + sscanf(str1 + 64, "%4x:%6x:%4x", &mapcaid, &mapprovid, &mapecmpid); + if(!mapcaid) { mapcaid = 0xFFFF; } + entry->mapcaid = mapcaid; + entry->mapprovid = mapprovid; + entry->mapecmpid = mapecmpid; + break; + } + + if(c_srvid[0] == '=') + { + struct s_srvid *this; + for(i = 0; i < 16; i++) + { + for(this = cfg.srvid[i]; this != NULL; this = this->next) + { + if(this->name && strcmp(this->name, c_srvid + 1) == 0) + { + struct s_dvbapi_priority *entry2; + if(!cs_malloc(&entry2, sizeof(struct s_dvbapi_priority))) + { + continue; + } + + memcpy(entry2, entry, sizeof(struct s_dvbapi_priority)); + entry2->srvid = this->srvid; + cs_log_dbg(D_DVBAPI, "prio srvid: ret=%d | %c: %04X %06X %04X %04X %04X -> map %04X %06X %04X | prio %d | delay %d", + ret, + entry2->type, + entry2->caid, + entry2->provid, + entry2->srvid, + entry2->ecmpid, + entry2->chid, + entry2->mapcaid, + entry2->mapprovid, + entry2->mapecmpid, + entry2->force, + entry2->delay); + + if(!dvbapi_priority) + { + dvbapi_priority = entry2; + } + else + { + struct s_dvbapi_priority *p; + for(p = dvbapi_priority; p->next != NULL; p = p->next) { ; } + p->next = entry2; + } + } + } + } + NULLFREE(entry); + continue; + } + else + { + sscanf(c_srvid, "%4x", &srvid); + entry->srvid = srvid; + } + cs_log_dbg(D_DVBAPI, "prio: ret=%d | %c: %04X %06X %04X %04X %04X -> map %04X %06X %04X | prio %d | delay %d", + ret, + entry->type, + entry->caid, + entry->provid, + entry->srvid, + entry->ecmpid, + entry->chid, + entry->mapcaid, + entry->mapprovid, + entry->mapecmpid, + entry->force, + entry->delay); + + if(!dvbapi_priority) + { + dvbapi_priority = entry; + } + else + { + struct s_dvbapi_priority *p; + for(p = dvbapi_priority; p->next != NULL; p = p->next) { ; } + p->next = entry; + } + } + cs_log_dbg(D_DVBAPI, "Read %d entries from %s", count, cs_prio); + + ret = fclose(fp); + if(ret < 0) + { + cs_log("ERROR: Could not close oscam.dvbapi fd (errno=%d %s)", errno, strerror(errno)); + } + return; +} + +void dvbapi_resort_ecmpids(int32_t demux_id) +{ + int32_t n, cache = 0, matching_done = 0, found = -1, match_reader_count = 0, total_reader = 0; + uint16_t btun_caid = 0; + struct timeb start,end; + cs_ftime(&start); + + for(n = 0; n < demux[demux_id].ECMpidcount; n++) + { + demux[demux_id].ECMpids[n].status = 0; + demux[demux_id].ECMpids[n].checked = 0; + demux[demux_id].ECMpids[n].irdeto_curindex = 0xFE; + demux[demux_id].ECMpids[n].irdeto_maxindex = 0; + demux[demux_id].ECMpids[n].irdeto_cycle = 0xFE; + demux[demux_id].ECMpids[n].tries = 0xFE; + demux[demux_id].ECMpids[n].table = 0; + } + + demux[demux_id].max_status = 0; + demux[demux_id].curindex = -1; + demux[demux_id].pidindex = -1; + + struct s_reader *rdr; + int32_t p_order = demux[demux_id].ECMpidcount + 1; + struct s_dvbapi_priority *prio; + + // handle prio order in oscam.dvbapi + ignore all chids + for(rdr = first_active_reader; rdr ; rdr = rdr->next) + { + total_reader++; // only need to calculate once! + } + + ECM_REQUEST *er; + if(!cs_malloc(&er, sizeof(ECM_REQUEST))) + { + return; + } + + for(prio = dvbapi_priority; prio != NULL; prio = prio->next) + { + if(prio->type != 'p' && prio->type != 'i') + { + continue; + } + + for(n = 0; n < demux[demux_id].ECMpidcount; n++) + { + if(demux[demux_id].ECMpids[n].status == -1) continue; // skip ignores! + + er->caid = er->ocaid = demux[demux_id].ECMpids[n].CAID; + er->prid = demux[demux_id].ECMpids[n].PROVID; + er->pid = demux[demux_id].ECMpids[n].ECM_PID; + er->srvid = demux[demux_id].program_number; + er->client = cur_client(); + btun_caid = chk_on_btun(SRVID_MASK, er->client, er); + + if(prio->type == 'p' && btun_caid) + { + er->caid = btun_caid; + } + + if((prio->caid && (prio->caid != er->caid && prio->caid != er->ocaid)) + || (prio->provid && prio->provid != er->prid) + || (prio->srvid && prio->srvid != er->srvid) + || (prio->ecmpid && prio->ecmpid != er->pid) + || (prio->pidx && prio->pidx - 1 != n)) + { + continue; + } + + if(prio->type == 'p') // check for prio + { + if(prio->cadata != 0 && prio->cadata != demux[demux_id].ECMpids[n].cadata) + { + continue; + } + + if(prio->chid < 0x10000) + { + demux[demux_id].ECMpids[n].CHID = prio->chid; + } + + if(prio->force) + { + int32_t j; + for(j = 0; j < demux[demux_id].ECMpidcount; j++) + { + demux[demux_id].ECMpids[j].status = -1; + } + + demux[demux_id].ECMpids[n].status = 1; + demux[demux_id].ECMpids[n].checked = 0; + demux[demux_id].max_status = 1; + demux[demux_id].max_emm_filter = maxfilter - 1; + + cs_log_dbg(D_DVBAPI, "Demuxer %d prio forced%s ecmpid %d %04X@%06X:%04X:%04X (file)", + demux_id, + ((prio->caid == er->caid && prio->caid != er->ocaid) ? " betatunneled" : ""), + n, + demux[demux_id].ECMpids[n].CAID, + demux[demux_id].ECMpids[n].PROVID, + demux[demux_id].ECMpids[n].ECM_PID, + (uint16_t) prio->chid); + NULLFREE(er); + return; // go start descrambling since its forced by user! + } + else + { + if(!demux[demux_id].ECMpids[n].status) // only accept first matching prio from oscam.dvbapi + { + demux[demux_id].ECMpids[n].status = total_reader + p_order--; + matching_done = 1; + cs_log_dbg(D_DVBAPI, "Demuxer %d prio%s ecmpid %d %04X@%06X:%04X:%04X weight: %d (file)", + demux_id, + ((prio->caid == er->caid && prio->caid != er->ocaid) ? " betatunneled" : ""), + n, + demux[demux_id].ECMpids[n].CAID, + demux[demux_id].ECMpids[n].PROVID, + demux[demux_id].ECMpids[n].ECM_PID, + (uint16_t) prio->chid, + demux[demux_id].ECMpids[n].status); + } + continue; // evaluate next ecmpid + } + } + + // check for ignore all chids + if(prio->type == 'i' && prio->chid == 0x10000 && demux[demux_id].ECMpids[n].status == 0) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d ignore ecmpid %d %04X@%06X:%04X all chids (file)", + demux_id, + n, + demux[demux_id].ECMpids[n].CAID, + demux[demux_id].ECMpids[n].PROVID, + demux[demux_id].ECMpids[n].ECM_PID); + demux[demux_id].ECMpids[n].status = -1; + continue; // evaluate next ecmpid + } + } + } + + for(n = 0; n < demux[demux_id].ECMpidcount; n++) + { + if(demux[demux_id].ECMpids[n].status == -1) + { + continue; // skip ignores! + } + + int32_t nr; + SIDTAB *sidtab; + + er->caid = er->ocaid = demux[demux_id].ECMpids[n].CAID; + er->prid = demux[demux_id].ECMpids[n].PROVID; + er->pid = demux[demux_id].ECMpids[n].ECM_PID; + er->srvid = demux[demux_id].program_number; + er->client = cur_client(); + + btun_caid = chk_on_btun(SRVID_MASK, er->client, er); + if(btun_caid) + { + er->caid = btun_caid; + } + + match_reader_count = 0; + + for(rdr = first_active_reader; rdr ; rdr = rdr->next) + { + if(matching_reader(er, rdr)) + { + match_reader_count++; + } + } + + if(match_reader_count == 0) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d ignore ecmpid %d %04X@%06X:%04X:%04X (no matching reader)", + demux_id, + n, + demux[demux_id].ECMpids[n].CAID, + demux[demux_id].ECMpids[n].PROVID, + demux[demux_id].ECMpids[n].ECM_PID, + demux[demux_id].ECMpids[n].CHID); + demux[demux_id].ECMpids[n].status = -1; + continue; // evaluate next ecmpid + } + else // ecmpids with no matching readers are disabled and matching sidtabbits have now highest status + { + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) + { + if((cfg.dvbapi_sidtabs.no & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { + demux[demux_id].ECMpids[n].status = -1; //ignore + cs_log_dbg(D_DVBAPI, "Demuxer %d ignore ecmpid %d %04X@%06X:%04X (service %s pos %d)", + demux_id, + n, + demux[demux_id].ECMpids[n].CAID, + demux[demux_id].ECMpids[n].PROVID, + demux[demux_id].ECMpids[n].ECM_PID, + sidtab->label, + nr); + continue; // evaluate next ecmpid + } + if((cfg.dvbapi_sidtabs.ok & ((SIDTABBITS) 1 << nr)) && (chk_srvid_match(er, sidtab))) + { + demux[demux_id].ECMpids[n].status++; // priority + cs_log_dbg(D_DVBAPI, "Demuxer %d prio ecmpid %d %04X@%06X:%04X weight: %d (service %s pos %d)", + demux_id, + n, + demux[demux_id].ECMpids[n].CAID, + demux[demux_id].ECMpids[n].PROVID, + demux[demux_id].ECMpids[n].ECM_PID, + demux[demux_id].ECMpids[n].status, + sidtab->label, + nr); + } + } + } + } + } + + // ecmpid with highest prio from oscam.dvbapi has now highest status + // check all ecmpids and get the highest amount cache-ex and local readers + int32_t max_local_matching_reader = 0, max_cacheex_reader = 0; + + for(n = 0; n < demux[demux_id].ECMpidcount; n++) + { + int32_t count_matching_cacheex_reader = 0, count_matching_local_reader = 0; + if(demux[demux_id].ECMpids[n].status == -1) + { + continue; // skip ignores! + } + + er->caid = er->ocaid = demux[demux_id].ECMpids[n].CAID; + er->prid = demux[demux_id].ECMpids[n].PROVID; + er->pid = demux[demux_id].ECMpids[n].ECM_PID; + er->srvid = demux[demux_id].program_number; + er->client = cur_client(); + + btun_caid = chk_on_btun(SRVID_MASK, er->client, er); + if(btun_caid) + { + er->caid = btun_caid; + } + + for(rdr = first_active_reader; rdr ; rdr = rdr->next) + { + if(matching_reader(er, rdr)) + { + if(cacheex_reader(rdr)) + { + count_matching_cacheex_reader++; + } + else if(is_localreader(rdr, er)) + { + count_matching_local_reader++; + } + } + } + + if(max_local_matching_reader < count_matching_local_reader) + { + max_local_matching_reader = count_matching_local_reader; + } + + if(max_cacheex_reader < count_matching_cacheex_reader) + { + max_cacheex_reader = count_matching_cacheex_reader; + } + } + + if(max_local_matching_reader != 0 || max_cacheex_reader != 0) + { + p_order = demux[demux_id].ECMpidcount * 2; + for(n = 0; n < demux[demux_id].ECMpidcount; n++) + { + int32_t count_matching_cacheex_reader = 0, count_matching_local_reader = 0; + int32_t localprio = 1, cacheexprio = 1; + + if(demux[demux_id].ECMpids[n].status == -1) + { + continue; // skip ignores! + } + + if(cfg.preferlocalcards == 2) // ecmpids with local reader get highest prio + { + localprio = max_cacheex_reader + p_order + 1; + } + else if(cfg.preferlocalcards == 1) // ecmpids with cacheex reader get highest prio + { + cacheexprio = max_local_matching_reader + p_order + 1; + } + + er->caid = er->ocaid = demux[demux_id].ECMpids[n].CAID; + er->prid = demux[demux_id].ECMpids[n].PROVID; + er->pid = demux[demux_id].ECMpids[n].ECM_PID; + er->srvid = demux[demux_id].program_number; + er->client = cur_client(); + + btun_caid = chk_on_btun(SRVID_MASK, er->client, er); + if(btun_caid) + { + er->caid = btun_caid; + } + + int32_t oldstatus = demux[demux_id].ECMpids[n].status; + int32_t anyreader = 0; + + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + if(matching_reader(er, rdr)) + { + if(cfg.preferlocalcards == 0) + { + if(!matching_done) + { + demux[demux_id].ECMpids[n].status++; + } + anyreader++; + continue; + } + + if(cacheex_reader(rdr)) + { + demux[demux_id].ECMpids[n].status += cacheexprio; + count_matching_cacheex_reader++; + cacheexprio = 1; + } + + if(is_localreader(rdr, er)) + { + demux[demux_id].ECMpids[n].status += localprio; + count_matching_local_reader++; + localprio = 1; + } + } + } + + if(oldstatus != demux[demux_id].ECMpids[n].status) + { + if(anyreader) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d prio ecmpid %d %04X@%06X:%04X:%04X weight: %d (%d readers)", + demux_id, + n, + demux[demux_id].ECMpids[n].CAID, + demux[demux_id].ECMpids[n].PROVID, + demux[demux_id].ECMpids[n].ECM_PID, + demux[demux_id].ECMpids[n].CHID, + demux[demux_id].ECMpids[n].status, + anyreader); + } + else + { + cs_log_dbg(D_DVBAPI, "Demuxer %d prio ecmpid %d %04X@%06X:%04X:%04X weight: %d (%d local and %d cacheex readers)", + demux_id, + n, + demux[demux_id].ECMpids[n].CAID, + demux[demux_id].ECMpids[n].PROVID, + demux[demux_id].ECMpids[n].ECM_PID, + demux[demux_id].ECMpids[n].CHID, + demux[demux_id].ECMpids[n].status, + count_matching_local_reader, + count_matching_cacheex_reader); + } + } + } + } + + struct s_channel_cache *c = NULL; + + for(n = 0; n < demux[demux_id].ECMpidcount && matching_done == 0; n++) + { + if(demux[demux_id].ECMpids[n].status == -1) + { + continue; // skip ignores! + } + + c = dvbapi_find_channel_cache(demux_id, n, 0); // find exact channel match + if(c != NULL) + { + found = n; + cache = 2; // found cache entry with higher priority + demux[demux_id].ECMpids[n].status++; // prioritize CAIDs which already decoded same caid:provid:srvid + + if(c->chid < 0x10000) + { + demux[demux_id].ECMpids[n].CHID = c->chid; // if chid registered in cache -> use it! + } + cs_log_dbg(D_DVBAPI, "Demuxer %d prio ecmpid %d %04X@%06X:%04X weight: %d (found caid/provid/srvid in cache)", + demux_id, + n, + demux[demux_id].ECMpids[n].CAID, + demux[demux_id].ECMpids[n].PROVID, + demux[demux_id].ECMpids[n].ECM_PID, + demux[demux_id].ECMpids[n].status); + break; + } + } + + if(found == -1) + { + // prioritize CAIDs which already decoded same caid:provid + for(n = 0; n < demux[demux_id].ECMpidcount && matching_done == 0; n++) + { + if(demux[demux_id].ECMpids[n].status == -1) continue; // skip ignores! + + c = dvbapi_find_channel_cache(demux_id, n, 1); + if(c != NULL) + { + cache = 1; //found cache entry + demux[demux_id].ECMpids[n].status++; + cs_log_dbg(D_DVBAPI, "Demuxer %d prio ecmpid %d %04X@%06X:%04X weight: %d (found caid/provid in cache)", + demux_id, + n, + demux[demux_id].ECMpids[n].CAID, + demux[demux_id].ECMpids[n].PROVID, + demux[demux_id].ECMpids[n].ECM_PID, + demux[demux_id].ECMpids[n].status); + } + } + } + + int32_t max_status = 0; + int32_t highest_priopid = -1; + + for(n = 0; n < demux[demux_id].ECMpidcount; n++) + { + if(demux[demux_id].ECMpids[n].status == -1) + { + continue; // skip ignores! + } + + if(demux[demux_id].ECMpids[n].status > max_status) // find highest prio pid + { + max_status = demux[demux_id].ECMpids[n].status; + highest_priopid = n; + } + + if(!USE_OPENXCAS) // openxcas doesnt use prio and non-prio run: all are equal! + { + if(demux[demux_id].ECMpids[n].status == 0) + { + demux[demux_id].ECMpids[n].checked = 2; // set pids with no status to no prio run + } + } + } + + demux[demux_id].max_status = max_status; // register maxstatus + + // Found entry in channelcache that is valid and has exact match on srvid + if(highest_priopid != -1 && found == highest_priopid && cache == 2) + { + for(n = 0; n < demux[demux_id].ECMpidcount; n++) + { + if(demux[demux_id].ECMpids[n].status == -1) + { + continue; // skip ignores! + } + + if(n != found) + { + // disable non matching pid + demux[demux_id].ECMpids[n].status = -1; + } + else + { + demux[demux_id].ECMpids[n].status = 1; + } + } + demux[demux_id].max_emm_filter = maxfilter - 1; + demux[demux_id].max_status = 1; + cs_log("Demuxer %d found channel in cache and matching prio -> start descrambling ecmpid %d ", demux_id, found); + } + + NULLFREE(er); + cs_ftime(&end); + int64_t gone = comp_timeb(&end, &start); + cs_log_dbg(D_DVBAPI, "Demuxer %d sorting the ecmpids took %"PRId64" ms", demux_id, gone); + return; +} + +static void dvbapi_priority_read_entry_add(int32_t demux_id, uint16_t video_pid) +{ + struct s_dvbapi_priority *add_entry; + + for(add_entry = dvbapi_priority; add_entry != NULL; add_entry = add_entry->next) + { + // ECM pid is misused to hold PMT pid in case of 'A' rule. + // Some receivers don't forward the PMT pid, so use the video pid instead + if(add_entry->type != 'a' || add_entry->srvid != demux[demux_id].program_number + || (add_entry->ecmpid && demux[demux_id].pmtpid && add_entry->ecmpid != demux[demux_id].pmtpid) + || (add_entry->ecmpid && !demux[demux_id].pmtpid && add_entry->ecmpid != video_pid)) + { + continue; + } + + dvbapi_add_ecmpid(demux_id, add_entry->mapcaid, add_entry->mapecmpid, add_entry->mapprovid, 0, "(fake ecm pid)"); + + cs_log_dbg(D_DVBAPI, "Demuxer %d added fake ecm pid %04X@%06X:%04X for program %04X", demux_id, + add_entry->mapcaid, add_entry->mapprovid, add_entry->mapecmpid, demux[demux_id].program_number); + break; + } +} + +static void dvbapi_priority_read_entry_map(int32_t demux_id) +{ + int32_t j; + struct s_dvbapi_priority *map_entry; + + for(j = 0; j < demux[demux_id].ECMpidcount; j++) + { + map_entry = dvbapi_check_prio_match(demux_id, j, 'm'); + if(map_entry) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d mapping ecm pid %d from %04X@%06X to %04X@%06X", + demux_id, j, demux[demux_id].ECMpids[j].CAID, demux[demux_id].ECMpids[j].PROVID, + map_entry->mapcaid, map_entry->mapprovid); + + demux[demux_id].ECMpids[j].CAID = map_entry->mapcaid; + demux[demux_id].ECMpids[j].PROVID = map_entry->mapprovid; + } + } +} + +static void dvbapi_priority_read_entry_extra(int32_t demux_id) +{ + struct s_dvbapi_priority *extra_entry; + int32_t j, k, l, m, extra_demux_id; + + for(extra_entry = dvbapi_priority; extra_entry != NULL; extra_entry = extra_entry->next) + { + if(extra_entry->type != 'x') + { + continue; + } + + for(j = 0; j <= demux[demux_id].ECMpidcount; ++j) + { + if((extra_entry->caid && extra_entry->caid != demux[demux_id].ECMpids[j].CAID) + || (extra_entry->provid && extra_entry->provid != demux[demux_id].ECMpids[j].PROVID) + || (extra_entry->ecmpid && extra_entry->ecmpid != demux[demux_id].ECMpids[j].ECM_PID) + || (extra_entry->srvid && extra_entry->srvid != demux[demux_id].program_number)) + { + continue; + } + + cs_log("Mapping ecm pid %04X@%06X:%04X:%04X to extra demuxer", + extra_entry->caid, extra_entry->provid, extra_entry->ecmpid, extra_entry->srvid); + + for(extra_demux_id = 0; extra_demux_id < MAX_DEMUX; extra_demux_id++) + { + if(demux[extra_demux_id].program_number != 0) + { + continue; // Skip occupied demuxers + } + } + + if(extra_demux_id >= MAX_DEMUX) + { + cs_log("There is no free demuxer for extra streams"); + continue; + } + + demux[extra_demux_id].ECMpids[0] = demux[demux_id].ECMpids[j]; + demux[extra_demux_id].ECMpidcount = 1; + demux[extra_demux_id].STREAMpidcount = 0; + demux[extra_demux_id].program_number = demux[demux_id].program_number; + demux[extra_demux_id].pmtpid = demux[demux_id].pmtpid; + demux[extra_demux_id].demux_index = demux[demux_id].demux_index; + demux[extra_demux_id].adapter_index = demux[demux_id].adapter_index; + demux[extra_demux_id].ca_mask = demux[demux_id].ca_mask; + demux[extra_demux_id].socket_fd = demux[demux_id].socket_fd; + demux[extra_demux_id].stop_descrambling = false; + demux[extra_demux_id].rdr = NULL; + demux[extra_demux_id].curindex = -1; + + // Add streams to extra demux + for(k = 0; k < demux[demux_id].STREAMpidcount; ++k) + { + if(!demux[demux_id].ECMpids[j].streams || (demux[demux_id].ECMpids[j].streams & (1 << k))) + { + demux[extra_demux_id].ECMpids[0].streams |= (1 << demux[extra_demux_id].STREAMpidcount); + demux[extra_demux_id].STREAMpids[demux[extra_demux_id].STREAMpidcount] = demux[demux_id].STREAMpids[k]; + demux[extra_demux_id].STREAMpidsType[demux[extra_demux_id].STREAMpidcount] = demux[demux_id].STREAMpidsType[k]; + ++demux[extra_demux_id].STREAMpidcount; + + // Shift stream associations in normal demux because we will remove the stream entirely + for(l = 0; l < demux[demux_id].ECMpidcount; ++l) + { + for(m = k; m < demux[demux_id].STREAMpidcount - 1; ++m) + { + if(demux[demux_id].ECMpids[l].streams & (1 << (m + 1))) + { + demux[demux_id].ECMpids[l].streams |= (1 << m); + } + else + { + demux[demux_id].ECMpids[l].streams &= ~(1 << m); + } + } + } + + // Remove stream association from normal demux device + for(l = k; l < demux[demux_id].STREAMpidcount - 1; ++l) + { + demux[demux_id].STREAMpids[l] = demux[demux_id].STREAMpids[l + 1]; + demux[demux_id].STREAMpidsType[l] = demux[demux_id].STREAMpidsType[l + 1]; + } + + --demux[demux_id].STREAMpidcount; + --k; + } + } + + // Remove ecm pid from normal demuxer + for(k = j; k < demux[demux_id].ECMpidcount; ++k) + { + demux[demux_id].ECMpids[k] = demux[demux_id].ECMpids[k + 1]; + } + + --demux[demux_id].ECMpidcount; + --j; + + if(demux[extra_demux_id].STREAMpidcount <= 0) + { + cs_log("Found no streams for extra demuxer. Not starting additional decoding on it."); + + demux[extra_demux_id].program_number = 0; + demux[extra_demux_id].stop_descrambling = true; + } + + if(demux[demux_id].STREAMpidcount < 1) + { + cs_log("Found no streams for normal demuxer. Not starting additional decoding on it."); + } + } + } +} + +static void dvbapi_parse_pmt_ca_descriptor(int32_t demux_id, const uint8_t *buffer, uint8_t descriptor_length) +{ + uint16_t i, ca_system_id, ca_pid; + uint32_t ca_provider = 0, ca_data = 0; + char txt[40]; // room for PBM: 8 byte pbm and DATE: date + memset(txt, 0x00, sizeof(txt)); + + if(descriptor_length < 4) + { + return; // CA descriptor has a minimum length of 4 bytes + } + + ca_system_id = b2i(2, buffer); + ca_pid = b2i(2, buffer + 2) & 0x1FFF; + + if(ca_system_id == 0x0000 || (!caid_is_biss_fixed(ca_system_id) && !caid_is_fake(ca_system_id) && ca_pid == 0x1FFF)) + { + return; // This is not a valid CAID or ECM pid + } + + if(caid_is_seca(ca_system_id)) + { + for(i = 2; i < descriptor_length; i += 15) + { + ca_pid = b2i(2, buffer + i) & 0x1FFF; + ca_provider = b2i(2, buffer + i + 2); + + int8_t year = buffer[i + 13] >> 1; + int8_t month = (((buffer[i + 13] & 0x01) << 3) | (buffer[i + 14] >> 5)); + int8_t day = buffer[i + 14] & 0x1F; + + snprintf(txt, sizeof(txt), "PBM: "); + cs_hexdump(0, buffer + i + 5, 8, txt + 5, (2 * 8) + 1); // hexdump 8 byte pbm + snprintf(txt + 20, sizeof(txt) - 20, " DATE: %d-%d-%d", day, month, year + 1990); + + dvbapi_add_ecmpid(demux_id, ca_system_id, ca_pid, ca_provider, 0, txt); + } + } + else + { + if(caid_is_viaccess(ca_system_id) && descriptor_length == 0x0F && buffer[10] == 0x14) + { + ca_provider = b2i(3, buffer + 12) & 0xFFFFF0; + } + else if(caid_is_nagra(ca_system_id) && descriptor_length == 0x07) + { + ca_provider = b2i(2, buffer + 5); + } + else if((ca_system_id >> 8 == 0x4A || ca_system_id == 0x2710) && descriptor_length > 0x04) + { + ca_provider = buffer[4]; + + if(caid_is_dre(ca_system_id) && descriptor_length == 0x0A) + { + ca_data = b2i(4, buffer + 6); + snprintf(txt, 40, "CA DATA: %X", ca_data); + } + } + + dvbapi_add_ecmpid(demux_id, ca_system_id, ca_pid, ca_provider, ca_data, txt); + } +} + +static void dvbapi_parse_pmt_descriptors(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint8_t *type) +{ + uint16_t i, j; + uint8_t descriptor_tag, descriptor_length; + + for(i = 0; i + 1 < length; i += 2 + descriptor_length) + { + descriptor_tag = buffer[i]; + descriptor_length = buffer[i + 1]; + + cs_log_dbg(D_DVBAPI, "Demuxer %d found %s descriptor (tag: %02X length: %02X)", + demux_id, get_descriptor_tag_txt(descriptor_tag), descriptor_tag, descriptor_length); + + switch(descriptor_tag) + { + case 0x05: // Registration descriptor + { + // "HDMV" format identifier is removed + // OSCam does not need to know about Blu-ray + const char format_identifiers_audio[10][5] = + { + "AC-3", "BSSD", "dmat", "DRA1", "DTS1", + "DTS2", "DTS3", "EAC3", "mlpa", "Opus", + }; + + for(j = 0; j < 10; j++) + { + if(memcmp(buffer + i + 2, format_identifiers_audio[j], 4) == 0) + { + *type = STREAM_AUDIO; + break; + } + } + break; + } + + case 0x09: // CA descriptor + { + dvbapi_parse_pmt_ca_descriptor(demux_id, buffer + i + 2, descriptor_length); + break; + } + + case 0x59: // Subtitling descriptor (DVB) + { + *type = STREAM_SUBTITLE; + break; + } + + case 0x6A: // AC-3 descriptor (DVB) + case 0x7A: // Enhanced AC-3 descriptor (DVB) + case 0x7B: // DTS descriptor (DVB) + case 0x7C: // AAC descriptor (DVB) + case 0x81: // AC-3 descriptor (ATSC) + case 0xCC: // Enhanced AC-3 descriptor (ATSC) + { + *type = STREAM_AUDIO; + break; + } + + case 0x7F: // Extension descriptor (DVB) + { + uint8_t extension_descriptor_tag = buffer[i + 2]; + + cs_log_dbg(D_DVBAPI, "Demuxer %d found %s descriptor (extension tag: %02X)", + demux_id, get_extension_descriptor_txt(extension_descriptor_tag), extension_descriptor_tag); + + switch(extension_descriptor_tag) + { + case 0x0E: // DTS-HD descriptor (DVB) + case 0x0F: // DTS Neural descriptor (DVB) + case 0x15: // AC-4 descriptor (DVB) + *type = STREAM_AUDIO; + break; + + case 0x20: // TTML subtitling descriptor (DVB) + *type = STREAM_SUBTITLE; + break; + + default: + *type = STREAM_UNDEFINED; + break; + } + break; + } + + default: + break; + } + } +} + +void request_cw(struct s_client *client, ECM_REQUEST *er, int32_t demux_id, uint8_t delayed_ecm_check) +{ + if(!er) { return; } + + int32_t filternum = dvbapi_set_section_filter(demux_id, er, -1); // set ecm filter to odd -> even and visaversa + if(filternum < 0) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d not requesting cw -> ecm filter was killed!", demux_id); + NULLFREE(er); + return; + } + + if(!delayed_ecm_check) // no delayed ecm check for this filter + { + memset(demux[demux_id].demux_fd[filternum].lastecmd5, 0, CS_ECMSTORESIZE); // no ecm delay check: zero it! + } + else + { + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + MD5(er->ecm, er->ecmlen, md5tmp); + + if(!memcmp(demux[demux_id].demux_fd[filternum].prevecmd5, md5tmp, CS_ECMSTORESIZE)) + { + if(demux[demux_id].demux_fd[filternum].prevresult < E_NOTFOUND) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d not requesting same ecm again! -> SKIP!", demux_id); + NULLFREE(er); + return; + } + else + { + cs_log_dbg(D_DVBAPI, "Demuxer %d requesting same ecm again (previous result was not found!)", demux_id); + } + } + else if(!memcmp(demux[demux_id].demux_fd[filternum].lastecmd5, md5tmp, CS_ECMSTORESIZE)) + { + if(demux[demux_id].demux_fd[filternum].lastresult < E_NOTFOUND) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d not requesting same ecm again! -> SKIP!", demux_id); + NULLFREE(er); + return; + } + else + { + cs_log_dbg(D_DVBAPI, "Demuxer %d requesting same ecm again (previous result was not found!)", demux_id); + } + } + + memcpy(demux[demux_id].demux_fd[filternum].prevecmd5, demux[demux_id].demux_fd[filternum].lastecmd5, CS_ECMSTORESIZE); + demux[demux_id].demux_fd[filternum].prevresult = demux[demux_id].demux_fd[filternum].lastresult; + memcpy(demux[demux_id].demux_fd[filternum].lastecmd5, md5tmp, CS_ECMSTORESIZE); + demux[demux_id].demux_fd[filternum].lastresult = 0xFF; + } + + er->adapter_index = demux[demux_id].adapter_index; + get_cw(client, er); + +#ifdef WITH_DEBUG + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_log_dbg(D_DVBAPI, "Demuxer %d request controlword for ecm %s", demux_id, buf); +#endif +} + +void dvbapi_try_next_caid(int32_t demux_id, int8_t checked, uint32_t msgid) +{ + int32_t n, j, found = -1, started = 0; + int32_t status = demux[demux_id].max_status; + + for(j = status; j >= 0; j--) // largest status first! + { + for(n = 0; n < demux[demux_id].ECMpidcount; n++) + { + //cs_log_dbg(D_DVBAPI,"Demuxer %d PID %d checked = %d status = %d (searching for pid with status = %d)", + // demux_id, n, demux[demux_id].ECMpids[n].checked, demux[demux_id].ECMpids[n].status, j); + + if(demux[demux_id].ECMpids[n].checked == checked && demux[demux_id].ECMpids[n].status == j) + { + found = n; + openxcas_set_provid(demux[demux_id].ECMpids[found].PROVID); + openxcas_set_caid(demux[demux_id].ECMpids[found].CAID); + openxcas_set_ecm_pid(demux[demux_id].ECMpids[found].ECM_PID); + + // fixup for cas that need emm first! + if(caid_is_irdeto(demux[demux_id].ECMpids[found].CAID) || (caid_is_dre(demux[demux_id].ECMpids[found].CAID) + && ((demux[demux_id].ECMpids[found].PROVID == 0x11 || demux[demux_id].ECMpids[found].PROVID == 0xFE)))) + { + demux[demux_id].emmstart.time = 0; + } + + started = dvbapi_start_descrambling(demux_id, found, checked, msgid); + if(cfg.dvbapi_requestmode == 0 && started == 1) + { + return; // in requestmode 0 we only start 1 ecm request at the time + } + } + } + } + + if(found == -1 && demux[demux_id].pidindex == -1) + { + cs_log("Demuxer %d no suitable readers found that can be used for decoding!", demux_id); + return; + } +} + +static void dvbapi_parse_pmt_program_info(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint8_t *ca_pmt_cmd_id) +{ + uint16_t i, offset = 0; + uint8_t descriptor_tag, descriptor_length; + + if(ca_pmt_cmd_id != NULL) // We are on CA PMT parsing + { + *ca_pmt_cmd_id = buffer[0]; + offset = 1; + } + + for(i = offset; i + 1 < length; i += 2 + descriptor_length) + { + descriptor_tag = buffer[i]; + descriptor_length = buffer[i + 1]; + + if(descriptor_tag == 0x09) // We only care about CA descriptors at program level + { + dvbapi_parse_pmt_ca_descriptor(demux_id, buffer + i + 2, descriptor_length); + } + } +} + +static void dvbapi_parse_pmt_es_info(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint8_t *ca_pmt_cmd_id, uint16_t *video_pid) +{ + uint16_t i, elementary_pid, es_info_length, offset = 0; + uint8_t stream_type, type; + + for(i = 0; i + 4 < length; i += 5 + es_info_length) + { + if(demux[demux_id].STREAMpidcount >= MAX_STREAM_PIDS) + { + cs_log("Demuxer %d reached maximum number of elementary streams", demux_id); + break; + } + + type = STREAM_UNDEFINED; + stream_type = buffer[i]; + elementary_pid = b2i(2, buffer + i + 1) & 0x1FFF; + es_info_length = b2i(2, buffer + i + 3) & 0x0FFF; + + cs_log_dbg(D_DVBAPI,"Demuxer %d found %s stream (type: %02X pid: %04X)", + demux_id, get_stream_type_txt(stream_type), stream_type, elementary_pid); + + if(es_info_length != 0 && es_info_length < length) + { + if(ca_pmt_cmd_id != NULL) // We are on CA PMT parsing + { + // Only enigma2, Spark and VDR follow the CA PMT specification ("ca_pmt_cmd_id" + // shall be present in the ES info loop). For the first two, checking for boxtype + // "dreambox" is sufficient, but for VDR this is not enough, because it shares + // the same boxtype with tvheadend. So, for every other box (including VDR and + // tvheadend), we stick to the old style check based on the value (descriptors + // with tag 0x00 or 0x01 are not allowed, so this works), while for enigma2 we + // do a proper check, because the "ca_pmt_cmd_id" can also take greater values. + if(cfg.dvbapi_boxtype == BOXTYPE_DREAMBOX) + { + *ca_pmt_cmd_id = buffer[i + 5]; // It should be identical for all ES and the same as in program info + offset = 1; + } + else + { + offset = (buffer[i + 5] <= 0x01) ? 1 : 0; + } + } + + // Parse descriptors at ES level + dvbapi_parse_pmt_descriptors(demux_id, buffer + i + 5 + offset, es_info_length, &type); + } + + // Get basic stream type (video, audio, subtitle) for each ES pid + switch(stream_type) + { + case 0x01: + case 0x02: + case 0x10: + case 0x1B: + case 0x20: + case 0x24: + case 0x25: + case 0x42: + case 0xD1: + case 0xEA: + if(*video_pid == 0) + { + *video_pid = elementary_pid; + } + demux[demux_id].STREAMpidsType[demux[demux_id].STREAMpidcount] = STREAM_VIDEO; + break; + + case 0x03: + case 0x04: + case 0x0F: + case 0x11: + case 0x1C: + case 0x2D: + case 0x2E: + case 0x81: + demux[demux_id].STREAMpidsType[demux[demux_id].STREAMpidcount] = STREAM_AUDIO; + break; + + case 0x06: + //case 0x81: some ATSC AC-3 streams do not contain the AC-3 descriptor! + case 0x87: + // Set the type based on the descriptors for these stream types + demux[demux_id].STREAMpidsType[demux[demux_id].STREAMpidcount] = type; + break; + + default: + demux[demux_id].STREAMpidsType[demux[demux_id].STREAMpidcount] = STREAM_UNDEFINED; + break; + } + + demux[demux_id].STREAMpids[demux[demux_id].STREAMpidcount] = elementary_pid; + demux[demux_id].STREAMpidcount++; + } +} + +static void dvbapi_parse_pmt_info(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint16_t pcr_pid, uint8_t *ca_pmt_cmd_id) +{ + uint16_t i, program_info_length, video_pid = 0; + + // Cleanout demuxer from possible stale info + // (reset ECM pids and streams) + for(i = 0; i < demux[demux_id].ECMpidcount; i++) + { + demux[demux_id].ECMpids[i].streams = 0; + } + + demux[demux_id].ECMpidcount = 0; + demux[demux_id].STREAMpidcount = 0; + + // Parse program info + // In case of CA PMT, read the ca_pmt_cmd_id as well + program_info_length = b2i(2, buffer) & 0x0FFF; + if(program_info_length != 0 && program_info_length < length) + { + dvbapi_parse_pmt_program_info(demux_id, buffer + 2, program_info_length, ca_pmt_cmd_id); + } + + // Parse elementary stream info + // In case of CA PMT, read the ca_pmt_cmd_id for each stream as well + dvbapi_parse_pmt_es_info(demux_id, buffer + 2 + program_info_length, length - 2 - program_info_length, ca_pmt_cmd_id, &video_pid); + + cs_log("Demuxer %d found %d ECM pids and %d STREAM pids in %sPMT", demux_id, + demux[demux_id].ECMpidcount, demux[demux_id].STREAMpidcount, ca_pmt_cmd_id != NULL ? "CA " : ""); + + // Various retarded boxes misuse the "ca_pmt_cmd_id" value, + // usually by setting it to zero. If we are on CA PMT parsing, + // make sure we pass a value we can work with later on. + if(ca_pmt_cmd_id != NULL) + { + *ca_pmt_cmd_id = (*ca_pmt_cmd_id < CA_PMT_CMD_OK_DESCRAMBLING) ? CA_PMT_CMD_OK_DESCRAMBLING : *ca_pmt_cmd_id; + } + + // If no elementary streams are available, set the PMT pid as the + // first stream (PMT cannot be encrypted, like it was mentioned + // in the old comment, so not sure why this is needed...) + if(demux[demux_id].STREAMpidcount == 0) + { + demux[demux_id].STREAMpids[0] = demux[demux_id].pmtpid; + demux[demux_id].STREAMpidsType[0] = STREAM_VIDEO; + demux[demux_id].STREAMpidcount++; + video_pid = demux[demux_id].pmtpid; + } + + // Fix for channels not listing the video pid inside the PMT: use the pcr pid instead + if(video_pid == 0 && pcr_pid != 0x1FFF) + { + demux[demux_id].STREAMpids[demux[demux_id].STREAMpidcount] = pcr_pid; + demux[demux_id].STREAMpidsType[demux[demux_id].STREAMpidcount] = STREAM_VIDEO; + demux[demux_id].STREAMpidcount++; + video_pid = pcr_pid; + + cs_log("Demuxer %d found no video pid. Using the PCR pid %04X instead", demux_id, pcr_pid); + } + + // Register found video pid on all ECM pids of this demuxer + for(i = 0; i < demux[demux_id].ECMpidcount; i++) + { + demux[demux_id].ECMpids[i].VPID = video_pid; + } + + // Search for dvbapi priority entries for this program + if(dvbapi_priority != NULL) + { + dvbapi_priority_read_entry_add(demux_id, video_pid); + dvbapi_priority_read_entry_map(demux_id); + dvbapi_priority_read_entry_extra(demux_id); + } +} + +typedef struct demux_parameters +{ + uint8_t demux_index; + uint8_t adapter_index; + uint32_t ca_mask; + uint16_t program_number; + uint16_t pmtpid; + uint16_t onid; + uint16_t tsid; + uint32_t ens; +} demux_parameters_t; + +static void get_demux_parameters(const uint8_t *buffer, demux_parameters_t *parameters) +{ + parameters->ca_mask = 1; + parameters->demux_index = 0; + parameters->adapter_index = 0; + parameters->pmtpid = 0; + parameters->program_number = b2i(2, buffer + 1); + + uint16_t program_info_length = b2i(2, buffer + 4) & 0x0FFF; + uint16_t pos = 7; // 4 + 2 (program_info_length) + 1 (ca_pmt_cmd_id) + + while(pos + 1 < 5 + program_info_length) + { + uint8_t descriptor_tag = buffer[pos]; + uint8_t descriptor_length = buffer[pos + 1]; + + switch(descriptor_tag) + { + case CA: + { + break; + } + + case ENIGMA_NAMESPACE: + { + if(descriptor_length == 0x08) + { + parameters->ens = b2i(4, buffer + pos + 2); + parameters->tsid = b2i(2, buffer + pos + 6); + parameters->onid = b2i(2, buffer + pos + 8); + } + break; + } + + case DEMUX_CA_MASK_ADAPTER: + { + if(descriptor_length == 0x02 && (cfg.dvbapi_boxtype == BOXTYPE_PC || + cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX || cfg.dvbapi_boxtype == BOXTYPE_SAMYGO)) + { + parameters->demux_index = buffer[pos + 2]; // Usually 0, but not always + parameters->adapter_index = buffer[pos + 3]; // Can be 0, 1, 2, ... + parameters->ca_mask = (1 << parameters->adapter_index); // use adapter_index as ca_mask + } + else if(descriptor_length == 0x03 && cfg.dvbapi_boxtype == BOXTYPE_QBOXHD) + { + // ca_mask = buffer[pos + 2]; // with STONE 1.0.4 always 0x01 + parameters->demux_index = buffer[pos + 3]; // with STONE 1.0.4 always 0x00 + parameters->adapter_index = buffer[pos + 4]; // with STONE 1.0.4 adapter index can be 0, 1, 2 + parameters->ca_mask = (1 << parameters->adapter_index); // use adapter_index as ca_mask + } + else if(descriptor_length == 0x02) // enigma2 + { + parameters->ca_mask = buffer[pos + 2]; + uint8_t demux_tmp = buffer[pos + 3]; + + if(demux_tmp >= 8 && parameters->ca_mask == 0) // openpli based images + { + parameters->ca_mask = 1 << demux_tmp; + } + + if(demux_tmp == 0xFF) // tryfix prismcube (0xFF -> "demux-1" = error!) + { + demux_tmp = 0; + } + parameters->demux_index = demux_tmp; + } + break; + } + + case ADAPTER_DEVICE: + { + if(descriptor_length == 0x01) + { + parameters->adapter_index = buffer[pos + 2]; + } + break; + } + + case PMT_PID: + { + if(descriptor_length == 0x02) + { + parameters->pmtpid = b2i(2, buffer + pos + 2); + } + break; + } + + case SERVICE_TYPE_MASK: + break; + + case DEMUX_DEVICE: + { + if(descriptor_length == 0x01) + { + parameters->demux_index = buffer[pos + 2]; + parameters->ca_mask = 1 << parameters->demux_index; + } + break; + } + + case CA_DEVICE: + { + if(descriptor_length == 0x01) + { + parameters->ca_mask = 1 << buffer[pos + 2]; + } + break; + } + + default: + { + cs_log_dbg(D_DVBAPI, "Skipped unsupported or CA PMT irrelevant descriptor (tag: %02X length: %02X)", descriptor_tag, descriptor_length); + break; + } + } + + pos += 2 + descriptor_length; + } +} + +static void dvbapi_capmt_notify(struct demux_s *dmx) +{ + struct s_client *cl; + for(cl = first_client->next; cl; cl = cl->next) + { + if((cl->typ == 'p' || cl->typ == 'r') && cl->reader && cl->reader->ph.c_capmt) + { + struct demux_s *curdemux; + if(cs_malloc(&curdemux, sizeof(struct demux_s))) + { + memcpy(curdemux, dmx, sizeof(struct demux_s)); + add_job(cl, ACTION_READER_CAPMT_NOTIFY, curdemux, sizeof(struct demux_s)); + } + } + } +} + +static void dvbapi_prepare_descrambling(int32_t demux_id, uint32_t msgid) +{ + bool is_powervu = false, start_emm = true; + char service_name[CS_SERVICENAME_SIZE]; + + // The CA PMT should have given us enough info to determine if descrambling + // is possible. Parsing the (real) PMT is not necessary, unless we have a + // PowerVu encrypted channel or (for some weird reason) no stream pids at all. + // Actually, when no streams are available, we set the PMT pid as the 1st + // stream pid, so we have to check against that. Finally, if the PMT pid is + // not included in the CA PMT, we start the PAT filter instead. + +#ifdef WITH_EXTENDED_CW + uint8_t i; + for(i = 0; i < demux[demux_id].ECMpidcount; i++) + { + if(caid_is_powervu(demux[demux_id].ECMpids[i].CAID)) + { + is_powervu = true; + break; + } + } +#endif + + if(demux[demux_id].pmtpid == 0) + { + dvbapi_start_pat_filter(demux_id); + } + else if(demux[demux_id].STREAMpids[0] == demux[demux_id].pmtpid || is_powervu) + { + dvbapi_start_pmt_filter(demux_id); + } + + if(demux[demux_id].running) + { + disable_unused_streampids(demux_id); // disable all streampids not in use anymore + } + + if(!demux[demux_id].running && demux[demux_id].ECMpidcount != 0) // only start demuxer if it wasn't running + { + // remove all non important filtering + // (there are images with limited amount of filters available!) + dvbapi_stop_all_cat_emm_sdt_filtering(msgid); + + get_servicename(dvbapi_client, demux[demux_id].program_number, demux[demux_id].ECMpids[0].PROVID, + demux[demux_id].ECMpids[0].CAID, service_name, sizeof(service_name)); + + cs_log_dbg(D_DVBAPI, "Demuxer %d started descrambling for program %04X (%s) (fd: %d)", + demux_id, demux[demux_id].program_number, service_name, demux[demux_id].socket_fd); + + demux[demux_id].running = true; // mark channel as running + openxcas_set_sid(demux[demux_id].program_number); + demux[demux_id].decodingtries = -1; + dvbapi_resort_ecmpids(demux_id); + dvbapi_try_next_caid(demux_id, 0, msgid); + cs_sleepms(1); + } + else if(demux[demux_id].ECMpidcount == 0) // FTA: do logging and part of ecm handler + { + get_servicename(dvbapi_client, demux[demux_id].program_number, NO_PROVID_VALUE, NO_CAID_VALUE, + service_name, sizeof(service_name)); + + cs_log_dbg(D_DVBAPI, "Demuxer %d no descrambling needed for FTA program %04X (%s) (fd: %d)", + demux_id, demux[demux_id].program_number, service_name, demux[demux_id].socket_fd); + + demux[demux_id].running = false; // reset running flag + demux[demux_id].pidindex = -1; // reset ecmpid used for descrambling + dvbapi_stop_filter(demux_id, TYPE_ECM, msgid); + + if(cfg.usrfileflag) // add to user log previous channel + time on channel + { + cs_statistics(dvbapi_client); + } + + dvbapi_client->last_srvid = demux[demux_id].program_number; // set new channel srvid + dvbapi_client->last_caid = NO_CAID_VALUE; // FTA channels have no caid! + dvbapi_client->last_provid = NO_PROVID_VALUE; // FTA channels have no provid! + dvbapi_client->lastswitch = dvbapi_client->last = time((time_t *)0); // reset idle-Time & last switch + } + +#if defined(WITH_COOLAPI) || defined(WITH_COOLAPI2) + // Don't start and Stop EMM Filters over and over again if we are on FTA + if(dvbapi_client->last_caid == NO_CAID_VALUE) + { + start_emm = false; + } +#endif + + // only do emm setup if au enabled and not running! + if(cfg.dvbapi_au > 0 && demux[demux_id].EMMpidcount == 0 && start_emm == true) + { + demux[demux_id].emm_filter = -1; // to register first run emmfilter start + + if(demux[demux_id].emmstart.time == 1) // irdeto fetch emm cat direct! + { + // trick to let emm fetching start after 30 seconds to speed up zapping + cs_ftime(&demux[demux_id].emmstart); + + dvbapi_start_cat_filter(demux_id); + } + else + { + cs_ftime(&demux[demux_id].emmstart); // for all other caids delayed start! + } + } +} + +int32_t dvbapi_parse_capmt(const uint8_t *buffer, uint32_t length, int32_t connfd, char *pmtfile, uint16_t client_proto_version, uint32_t msgid) +{ + int32_t i, demux_id = -1; + uint8_t ca_pmt_list_management, ca_pmt_cmd_id; + bool is_update = false; + demux_parameters_t parameters; + memset(¶meters, 0, sizeof(parameters)); + +#if defined WITH_COOLAPI || defined WITH_COOLAPI2 + ca_pmt_list_management = CA_PMT_LIST_ONLY; +#else + ca_pmt_list_management = buffer[0]; +#endif + + // We received a new list of CA PMT objects. + // Mark all demuxers to stop descrambling, but do not actually stop any of them, + // until we verify the new list does not contain any previously selected program. + if(ca_pmt_list_management == CA_PMT_LIST_FIRST || ca_pmt_list_management == CA_PMT_LIST_ONLY) + { + for(i = 0; i < MAX_DEMUX; i++) + { + // Skip empty demuxers, demuxers belonging to different + // CA PMT connections or handled by different PMT files. + if(demux[i].program_number == 0 || demux[i].socket_fd != connfd || + (demux[i].socket_fd == -1 && pmtfile && strcmp(demux[i].pmt_file, pmtfile) != 0)) + { + continue; + } + + demux[i].stop_descrambling = true; // Mark for deletion if not used again by following CA PMT objects + + cs_log_dbg(D_DVBAPI, "Demuxer %d marked to stop descrambling for program %04X (fd: %d)", + i, demux[i].program_number, connfd); + } + } + + // Read private descriptors inside the CA PMT message + // in order to get adapter, demux, ca, pmt pid and more. + get_demux_parameters(buffer, ¶meters); + + cs_log_dbg(D_DVBAPI, "Received CA PMT list management %d for program %04X (pmt pid: %04X adapter: %d demux: %d camask: %d)", + ca_pmt_list_management, parameters.program_number, parameters.pmtpid, parameters.adapter_index, + parameters.demux_index, parameters.ca_mask); + + // Search current demuxers for having the same program + // as the one we received in this CA PMT object. + for(i = 0; i < MAX_DEMUX; i++) + { + if(demux[i].program_number == 0) + { + continue; + } + + if(cfg.dvbapi_boxtype == BOXTYPE_IPBOX_PMT) + { + parameters.demux_index = i; // fixup for ipbox + } + + bool full_check = true, matched = false; + + if(config_enabled(WITH_COOLAPI) || config_enabled(WITH_COOLAPI2) || cfg.dvbapi_boxtype == BOXTYPE_SAMYGO) + { + full_check = false; + } + + if(full_check) + { + matched = (connfd > 0 && demux[i].socket_fd == connfd) && demux[i].program_number == parameters.program_number; + } + else + { + matched = connfd > 0 && demux[i].program_number == parameters.program_number; + } + + if(matched) + { + if(full_check) + { + // In PMT mode 6, when zapping between channels with the same program number and PMT pid + // (sometimes the case with satellite feeds), as all hardware parameters being the same + // (adapter, demux, ca_mask, connfd), the new program is considered an existing one (matched). + // The only reliable way to determine whether we actually have a new program is to compare + // the enigma namespace, tsid and onid as well. + if(demux[i].demux_index != parameters.demux_index || demux[i].ca_mask != parameters.ca_mask || + demux[i].adapter_index != parameters.adapter_index || demux[i].pmtpid != parameters.pmtpid || + demux[i].ens != parameters.ens || demux[i].tsid != parameters.tsid || demux[i].onid != parameters.onid) + { + continue; + } + } + + // A program update is normally signaled by either a list management: + // 1. UPDATE for an existing program + // 2. ADD for an existing program (which according to the specifications should be treated as an UPDATE) + // 3. ONLY for an existing program (with broken clients, in pmt modes other than 6) + if(ca_pmt_list_management == CA_PMT_LIST_UPDATE || ca_pmt_list_management == CA_PMT_LIST_ADD + || (cfg.dvbapi_pmtmode != 6 && ca_pmt_list_management == CA_PMT_LIST_ONLY)) + { + is_update = true; + cs_log("Demuxer %d received CA PMT update for program %04X", i, parameters.program_number); + } + + cs_log("Demuxer %d continues processing for program %04X", i, demux[i].program_number); + openxcas_set_sid(parameters.program_number); + demux[i].stop_descrambling = false; // don't stop current demuxer! + demux_id = i; + break; // no need to explore other demuxers since we have a match! + } + } + + // We are currently processing the last object of the CA PMT list. + // We should now stop descrambling all programs not included in this list. + if(ca_pmt_list_management != CA_PMT_LIST_FIRST && ca_pmt_list_management != CA_PMT_LIST_MORE) + { + for(i = 0; i < MAX_DEMUX; i++) + { + if(demux[i].program_number == 0) + { + continue; + } + + if(demux[i].stop_descrambling) + { + dvbapi_stop_descrambling(i, msgid); + } + } + } + + // We continue reading the CA PMT object only when we get + // a new program (demux_id == -1) or an updated program. + if(!(demux_id == -1 || is_update)) + { + return demux_id; + } + + // We received a CA PMT object for a new program. + // Let's find an empty demuxer for it. + if(demux_id == -1) + { + if(ca_pmt_list_management == CA_PMT_LIST_UPDATE) + { + cs_log("ERROR: Received CA PMT list update for unknown program"); + return -1; + } + + for(demux_id = 0; demux_id < MAX_DEMUX; demux_id++) + { + if(demux[demux_id].program_number != 0) + { + continue; // Skip occupied demuxers + } + + // Probably this box doesn't send any private descriptor in the + // CA PMT, so we have to improvise before saving to the demuxer. + if(cfg.dvbapi_boxtype == BOXTYPE_IPBOX_PMT) + { + parameters.ca_mask = demux_id + 1; + parameters.demux_index = demux_id; + } + + demux[demux_id].demux_index = parameters.demux_index; + demux[demux_id].adapter_index = parameters.adapter_index; + demux[demux_id].ca_mask = parameters.ca_mask; + demux[demux_id].socket_fd = connfd; + demux[demux_id].client_proto_version = client_proto_version; + demux[demux_id].program_number = parameters.program_number; + demux[demux_id].pmtpid = parameters.pmtpid; + demux[demux_id].ens = parameters.ens; + demux[demux_id].tsid = parameters.tsid; + demux[demux_id].onid = parameters.onid; + demux[demux_id].stop_descrambling = false; + demux[demux_id].running = false; + demux[demux_id].sdt_filter = -1; + demux[demux_id].rdr = NULL; + demux[demux_id].msgid = msgid; + + if(pmtfile) + { + cs_strncpy(demux[demux_id].pmt_file, pmtfile, sizeof(demux[demux_id].pmt_file)); + } + + break; + } + + if(demux_id >= MAX_DEMUX) + { + cs_log("There is no free demuxer for the new program! Aborting..."); + return -1; + } + } + + // We continue parsing the CA PMT info for new or updated programs. + // For updated programs, we just delete all previous stream pids and + // ECM pids and start parsing the fresh data. + dvbapi_parse_pmt_info(demux_id, buffer + 4, length - 4, 0x1FFF, &ca_pmt_cmd_id); + + // Finally, evaluate what response the host requires from OSCam. + // This allows multiple CA applications to run at the host simultaneously. + // "OK query" will be implemented at a later stage, when support is first + // added in enigma2. + switch(ca_pmt_cmd_id) + { + case CA_PMT_CMD_OK_DESCRAMBLING: + { + dvbapi_capmt_notify(&demux[demux_id]); + dvbapi_prepare_descrambling(demux_id, msgid); + return demux_id; + } + + case CA_PMT_CMD_QUERY: + { + cs_log("Received unsupported CA PMT command ID 'query' for program %04X", demux[demux_id].program_number); + dvbapi_stop_descrambling(demux_id, msgid); // Clear all data from this demuxer + return -1; + } + + case CA_PMT_CMD_NOT_SELECTED: + { + cs_log("Program %04X is not selected for descrambling", demux[demux_id].program_number); + dvbapi_stop_descrambling(demux_id, msgid); // Clear all data from this demuxer + return -1; + } + + default: + { + cs_log("Received unknown or unsupported CA PMT command ID %02X from host", ca_pmt_cmd_id); + dvbapi_stop_descrambling(demux_id, msgid); + return -1; + } + } +} + +static void dvbapi_parse_pmt(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint32_t msgid) +{ + uint16_t program_number = b2i(2, buffer + 3); + uint16_t pcr_pid = b2i(2, buffer + 8) & 0x1FFF; + + if(program_number != demux[demux_id].program_number) + { + cs_log("Demuxer %d received PMT for undefined program %04X", demux_id, program_number); + return; + } + + dvbapi_stop_filter(demux_id, TYPE_PMT, msgid); + dvbapi_parse_pmt_info(demux_id, buffer + 10, length - 10 - 4, pcr_pid, NULL); // last 4 bytes are the CRC-32 +} + +static void dvbapi_create_srvid_line(int32_t demux_id, char *buffer, uint32_t buflen) +{ + int32_t i, j, pos = 0; + uint16_t caid_done[32], cur_caid; + uint8_t caid_done_count = 0, skip_caid; + + if(demux[demux_id].ECMpidcount == 0) + { + snprintf(buffer, buflen, "%04X@%06X", NO_CAID_VALUE, NO_PROVID_VALUE); + return; + } + + for(i = 0; i < demux[demux_id].ECMpidcount && i < 32; i++) + { + skip_caid = 0; + for(j = 0; j < caid_done_count; j++) + { + if(caid_done[j] == demux[demux_id].ECMpids[i].CAID) + { + skip_caid = 1; + break; + } + } + + if(skip_caid) + { + continue; + } + + cur_caid = demux[demux_id].ECMpids[i].CAID; + pos += snprintf(buffer + pos, buflen - pos, "%s%04X", caid_done_count > 0 ? "," : "", cur_caid == 0 ? NO_CAID_VALUE : cur_caid); + + for(j = i; j < demux[demux_id].ECMpidcount; j++) + { + if(demux[demux_id].ECMpids[j].PROVID == 0) + { + continue; + } + + if(cur_caid == demux[demux_id].ECMpids[j].CAID) + { + pos += snprintf(buffer + pos, buflen - pos, "@%06X", demux[demux_id].ECMpids[j].PROVID); + } + } + + caid_done[caid_done_count] = demux[demux_id].ECMpids[i].CAID; + caid_done_count++; + } +} + +static void dvbapi_write_sdt_info(int32_t demux_id, const char *provider_name, const char* service_name, const char *service_type) +{ + int8_t did_save_srvid = 0; + int32_t provid, caid, pidindex; + char tmp[256], srvid_line[1024]; + FILE *fpsave = NULL; + + pidindex = demux[demux_id].pidindex; + if(pidindex != -1) + { + caid = demux[demux_id].ECMpids[pidindex].CAID; + provid = demux[demux_id].ECMpids[pidindex].PROVID; + } + else + { + if(demux[demux_id].ECMpidcount == 0 || demux[demux_id].ECMpids[0].CAID == 0) + { + caid = NO_CAID_VALUE; + provid = NO_PROVID_VALUE; + } + else + { + caid = demux[demux_id].ECMpids[0].CAID; + provid = demux[demux_id].ECMpids[0].PROVID; + } + } + + if(cs_strlen(provider_name) && caid != NO_CAID_VALUE) + { + get_providername_or_null(provid, caid, tmp, sizeof(tmp)); + + if(tmp[0] == '\0') + { + get_config_filename(tmp, sizeof(tmp), "oscam.provid"); + + if((fpsave = fopen(tmp, "a"))) + { + fprintf(fpsave, "\n%04X@%06X|%s|", caid, provid, provider_name); + fclose(fpsave); + init_provid(); + } + } + } + + if(cs_strlen(service_name)) + { + get_servicename_or_null(cur_client(), demux[demux_id].program_number, provid, caid, tmp, sizeof(tmp)); + + if(tmp[0] == '\0') + { + get_config_filename(tmp, sizeof(tmp), "oscam.srvid2"); + + if(!access(tmp, F_OK) && (fpsave = fopen(tmp, "a"))) + { + if((caid != NO_CAID_VALUE) || (cfg.dvbapi_read_sdt > 1)) + { + dvbapi_create_srvid_line(demux_id, srvid_line, sizeof(srvid_line)); + + if(cfg.dvbapi_write_sdt_prov) + { + fprintf(fpsave, "\n%04X:%s|%s|%s||%s", demux[demux_id].program_number, srvid_line, service_name, service_type, provider_name); + } + else + { + fprintf(fpsave, "\n%04X:%s|%s|%s", demux[demux_id].program_number, srvid_line, service_name, service_type); + } + + did_save_srvid = 1; + } + } + else + { + get_config_filename(tmp, sizeof(tmp), "oscam.srvid"); + + if((fpsave = fopen(tmp, "a"))) + { + if((caid != NO_CAID_VALUE) || (cfg.dvbapi_read_sdt > 1)) + { + dvbapi_create_srvid_line(demux_id, srvid_line, sizeof(srvid_line)); + + if(cfg.dvbapi_write_sdt_prov) + { + fprintf(fpsave, "\n%s:%04X|%s|%s|%s", srvid_line, demux[demux_id].program_number, provider_name, service_name, service_type); + } + else + { + fprintf(fpsave, "\n%s:%04X||%s|%s", srvid_line, demux[demux_id].program_number, service_name, service_type); + } + + did_save_srvid = 1; + } + } + } + + if(fpsave) + { + fclose(fpsave); + } + + if(did_save_srvid) + { + init_srvid(); + } + } + } +} + +static uint32_t dvbapi_extract_sdt_string(char *buf, uint32_t buflen, const uint8_t *source, uint8_t sourcelen) +{ + uint32_t i, j, offset = 0; + int8_t iso_mode = -1; + char *tmpbuf; + const uint8_t *ptr_in; + uint8_t *ptr_out; + size_t in_bytes, out_bytes; + + if(sourcelen == 0) + { + buf[0] = '\0'; + return 1; + } + + if(!cs_malloc(&tmpbuf, buflen)) + { + return 0; + } + + if(sourcelen > buflen - 1) + { + sourcelen = buflen - 1; + } + + if(sourcelen > 0 && source[0] < 0x20) + { + if(source[0] >= 0x01 && source[0] <= 0x0B && source[0] != 0x08) // ISO/IEC 8859 + { + offset = 1; + iso_mode = 4 + source[0]; + } + else if(source[0] == 0x10) // Dynamically selected part of ISO/IEC 8859 + { + if(source[1] == 0x00 && source[2] >= 0x01 && source[2] <= 0x0F && source[2] != 0x0C) + { + offset = 3; + iso_mode = source[2]; + } + } + else if(source[0] == 0x11) // ISO/IEC 10646 + { + offset = 1; + iso_mode = -2; + } + // missing: 0x12 KSX1001-2004 (Korean Character Set) + // missing: 0x13 GB-2312-1980 (Simplified Chinese Character Set) + // missing: 0x14 Big5 subset of ISO/IEC 10646 (Traditional Chinese) + else if(source[0] == 0x15) // UTF-8 encoding of ISO/IEC 10646 + { + offset = 1; + iso_mode = -3; + } + // missing: 0x1F Described by encoding_type_id + else + { + NULLFREE(tmpbuf); + return 0; + } + } + + if(offset >= sourcelen) + { + NULLFREE(tmpbuf); + return 0; + } + + if(iso_mode >= -1) + { + for(i = 0, j = 0; i < sourcelen - offset; i++) + { + if(source[offset + i] >= 0x80 && source[offset + i] <= 0x9F) + { + continue; + } + + tmpbuf[j] = source[offset + i]; + j++; + } + + tmpbuf[j] = '\0'; + } + + ptr_in = (const uint8_t *)tmpbuf; + in_bytes = cs_strlen(tmpbuf); + ptr_out = (uint8_t *)buf; + out_bytes = buflen; + +#ifdef READ_SDT_CHARSETS + if(iso_mode >= -1) + { + memset(buf, 0, buflen); + cs_log_dbg(D_DVBAPI, "sdt-info dbg: iso_mode: %d offset: %u", iso_mode, offset); + cs_log_dump_dbg(D_DVBAPI, (uint8_t *)tmpbuf, in_bytes, "sdt-info dbg: raw string:"); + + if(iso_mode == -1) + { + if(ISO6937toUTF8(&ptr_in, &in_bytes, &ptr_out, &out_bytes) == (size_t)(-1)) + { + cs_log_dbg(D_DVBAPI, "sdt-info error: ISO6937toUTF8 failed"); + NULLFREE(tmpbuf); + return 0; + } + } + else + { + if(ISO8859toUTF8(iso_mode, &ptr_in, &in_bytes, &ptr_out, &out_bytes) == (size_t)(-1)) + { + cs_log_dbg(D_DVBAPI, "sdt-info error: ISO8859toUTF8 failed"); + NULLFREE(tmpbuf); + return 0; + } + } + } +#else + if(iso_mode >= -1) + { + cs_strncpy(buf, tmpbuf, buflen); + cs_log_dbg(D_DVBAPI, "sdt-info warning: your build of oscam does not support iso-to-utf8 conversion, special chars may be corrupted!"); + } +#endif + else if(iso_mode == -2) + { + memset(buf, 0, buflen); + cs_log_dbg(D_DVBAPI, "sdt-info dbg: iso_mode: %d offset: %u", iso_mode, offset); + + if(UnicodetoUTF8(&ptr_in, &in_bytes, &ptr_out, &out_bytes) == (size_t)(-1)) + { + cs_log_dbg(D_DVBAPI, "sdt-info error: UnicodetoUTF8 failed"); + NULLFREE(tmpbuf); + return 0; + } + } + else if(iso_mode == -3) // No conversion, already in UTF-8 + { + memcpy(buf, source + offset, sourcelen - offset); + buf[sourcelen - offset] = '\0'; + cs_log_dbg(D_DVBAPI, "sdt-info dbg: iso_mode: -3 offset: %u", offset); + } + + cs_log_dump_dbg(D_DVBAPI, (uint8_t *)buf, cs_strlen(buf), "sdt-info dbg: encoded string:"); + NULLFREE(tmpbuf); + return 1; +} + +static const char *dvbapi_get_service_type(uint8_t service_type) +{ + switch(service_type) + { + case 0x01: + case 0x0B: + case 0x11: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + return "TV"; + + case 0x02: + case 0x07: + case 0x0A: + return "Radio"; + + case 0x03: + return "Teletext"; + + case 0x0C: + return "Data"; + + default: + return "unknown"; + } +} + +static void dvbapi_parse_service_descriptor(int32_t demux_id, const uint8_t *buffer, uint8_t descriptor_length) +{ + uint8_t service_provider_name_length, service_name_length; + char service_provider_name[64], service_name[64]; + const char *service_type; + + if(descriptor_length < 3) + { + return; // Service descriptor has a minimum length of 3 bytes + } + + service_type = dvbapi_get_service_type(buffer[0]); + + service_provider_name_length = buffer[1]; + if(2 + service_provider_name_length + 1 > descriptor_length) + { + return; + } + + service_name_length = buffer[2 + service_provider_name_length]; + if(2 + service_provider_name_length + 1 + service_name_length > descriptor_length) + { + return; + } + + if(!dvbapi_extract_sdt_string(service_provider_name, sizeof(service_provider_name), buffer + 2, service_provider_name_length) || + !dvbapi_extract_sdt_string(service_name, sizeof(service_name), buffer + 2 + service_provider_name_length + 1, service_name_length)) + { + return; + } + + cs_log_dbg(D_DVBAPI, "Demuxer %d got service info (provider: %s - name: %s - type: %s)", + demux_id, service_provider_name, service_name, service_type); + + dvbapi_write_sdt_info(demux_id, service_provider_name, service_name, service_type); +} + +static void dvbapi_parse_sdt(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint32_t msgid) +{ + uint8_t descriptor_tag, descriptor_length; + uint16_t service_id, descriptors_loop_length, i, j; + + if(buffer[0] != 0x42) // SDT sections with table_id value 0x42 describe the actual TS + { + return; + } + + // Get the tsid and onid (in enigma2 STBs we have + // already received them in the CA PMT message) + demux[demux_id].tsid = b2i(2, buffer + 3); + demux[demux_id].onid = b2i(2, buffer + 8); + + for(i = 11; i + 5 < length; i += 5 + descriptors_loop_length) + { + service_id = b2i(2, buffer + i); + descriptors_loop_length = b2i(2, buffer + i + 3) & 0x0FFF; + + if(service_id != demux[demux_id].program_number) + { + continue; + } + + for(j = 0; j + 1 < descriptors_loop_length; j += 2 + descriptor_length) + { + descriptor_tag = buffer[i + 5 + j]; + descriptor_length = buffer[i + 5 + j + 1]; + + if(descriptor_tag == 0x48) + { + dvbapi_parse_service_descriptor(demux_id, buffer + i + 5 + j + 2, descriptor_length); + } + } + + dvbapi_stop_filter(demux_id, TYPE_SDT, msgid); + break; + } +} + +static void dvbapi_parse_pat(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint32_t msgid) +{ + uint16_t i, srvid; + + dvbapi_stop_filter(demux_id, TYPE_PAT, msgid); + + for(i = 8; i + 7 < length; i += 4) + { + srvid = b2i(2, buffer + i); + if(srvid == 0) + { + continue; + } + + if(demux[demux_id].program_number == srvid) + { + demux[demux_id].pmtpid = b2i(2, buffer + i + 2) & 0x1FFF; + dvbapi_start_pmt_filter(demux_id); + break; + } + } +} + +int32_t dvbapi_init_listenfd(void) +{ + int32_t clilen, listenfd; + struct sockaddr_un servaddr; + + memset(&servaddr, 0, sizeof(struct sockaddr_un)); + servaddr.sun_family = AF_UNIX; + cs_strncpy(servaddr.sun_path, devices[selected_box].cam_socket_path, sizeof(servaddr.sun_path)); + clilen = sizeof(servaddr.sun_family) + cs_strlen(servaddr.sun_path); + + if(((unlink(devices[selected_box].cam_socket_path) < 0) && (errno != ENOENT)) + || ((listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + || (bind(listenfd, (struct sockaddr *)&servaddr, clilen) < 0) + || (listen(listenfd, 5) < 0)) + { + return 0; + } + + // change the access right on the camd.socket + // this will allow oscam to run as root if needed + // and still allow non root client to connect to the socket + chmod(devices[selected_box].cam_socket_path, S_IRWXU | S_IRWXG | S_IRWXO); + return listenfd; +} + +int32_t dvbapi_net_init_listenfd(void) +{ + int32_t listenfd; + struct SOCKADDR servaddr; + + memset(&servaddr, 0, sizeof(servaddr)); + SIN_GET_FAMILY(servaddr) = DEFAULT_AF; + SIN_GET_ADDR(servaddr) = cfg.dvbapi_srvip; + SIN_GET_PORT(servaddr) = htons((uint16_t)dvbapi_listenport_active); + + if((listenfd = socket(DEFAULT_AF, SOCK_STREAM, 0)) < 0) + { + return 0; + } + int32_t opt = 0; + +#ifdef IPV6SUPPORT +// azbox toolchain do not have this define +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 26 +#endif + // set the server socket option to listen on IPv4 and IPv6 simultaneously + setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&opt, sizeof(opt)); +#endif + + opt = 1; + setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt)); + set_so_reuseport(listenfd); + + if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) + { + return 0; + } + + if(listen(listenfd, 5) < 0) + { + return 0; + } + return listenfd; +} + +static pthread_mutex_t event_handler_lock = PTHREAD_MUTEX_INITIALIZER; + +void event_handler(int32_t UNUSED(signal)) +{ + struct stat pmt_info; + char dest[1024]; + struct dirent **entries; + int32_t i, n; + int32_t pmt_fd = -1; + uint8_t mbuf[2048]; // dirty fix: larger buffer needed for CA PMT mode 6 with many parallel channels to decode + + if(dvbapi_client != cur_client()) + { + return; + } + + SAFE_MUTEX_LOCK(&event_handler_lock); + if(cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX || cfg.dvbapi_boxtype == BOXTYPE_SAMYGO) + { + pausecam = 0; + } + else + { + int32_t standby_fd = open(STANDBY_FILE, O_RDONLY); + pausecam = (standby_fd > 0) ? 1 : 0; + if(standby_fd > 0) + { + int32_t ret = close(standby_fd); + if(ret < 0) + { + cs_log("ERROR: Could not close standby fd (errno=%d %s)", errno, strerror(errno)); + } + } + } + + if(cfg.dvbapi_boxtype == BOXTYPE_IPBOX || cfg.dvbapi_pmtmode == 1) + { + SAFE_MUTEX_UNLOCK(&event_handler_lock); + return; + } + + for(i = 0; i < MAX_DEMUX; i++) + { + if(demux[i].pmt_file[0] != 0) + { + snprintf(dest, sizeof(dest), "%s%s", TMPDIR, demux[i].pmt_file); + pmt_fd = open(dest, O_RDONLY); + if(pmt_fd > 0) + { + if(fstat(pmt_fd, &pmt_info) != 0) + { + int32_t ret = close(pmt_fd); + if(ret < 0) + { + cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno)); + } + continue; + } + + if((time_t)pmt_info.st_mtime != demux[i].pmt_time) + { + dvbapi_stop_descrambling(i, 0); + } + + int32_t ret = close(pmt_fd); + if(ret < 0) + { + cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno)); + } + continue; + } + else + { + cs_log("Demuxer %d Unable to open PMT file %s -> stop descrambling!", i, dest); + dvbapi_stop_descrambling(i, 0); + } + } + } + + if(disable_pmt_files) + { + SAFE_MUTEX_UNLOCK(&event_handler_lock); + return; + } + + n = scandir(TMPDIR, &entries, NULL, NULL); + if (n==-1) + { + cs_log_dbg(D_DVBAPI, "scandir failed (errno=%d %s)", errno, strerror(errno)); + SAFE_MUTEX_UNLOCK(&event_handler_lock); + return; + } + while(n--) + { + if(cs_strlen(entries[n]->d_name) < 7) + { + free(entries[n]); + continue; + } + + if(strncmp(entries[n]->d_name, "pmt", 3) != 0 || strncmp(entries[n]->d_name + cs_strlen(entries[n]->d_name) - 4, ".tmp", 4) != 0) + { + free(entries[n]); + continue; + } + +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + struct s_dvbapi_priority *p; + for(p = dvbapi_priority; p != NULL; p = p->next) // stapi: check if there is a device connected to this pmt file! + { + if(p->type != 's') { continue; } // stapi rule? + if(strcmp(entries[n]->d_name, p->pmtfile) != 0) { continue; } // same file? + break; // found match! + } + + if(p == NULL) + { + cs_log_dbg(D_DVBAPI, "No matching S: line in oscam.dvbapi for pmtfile %s -> skip!", entries[n]->d_name); + free(entries[n]); + continue; + } +#endif + + if (!cs_strlen(TMPDIR)) + { + cs_log_dbg(D_DVBAPI, "BUG! cs_strlen(TMPDIR)!!!\n"); + free(entries[n]); + continue; + } + + if (!cs_strlen(entries[n]->d_name)) + { + cs_log_dbg(D_DVBAPI, "BUG! cs_strlen(entries[n]->d_name)!!!\n"); + free(entries[n]); + continue; + } + + if((cs_strlen(entries[n]->d_name) + cs_strlen(TMPDIR) - 1) > sizeof(dest)) + { + cs_log_dbg(D_DVBAPI, "BUG! Sum of the (d_name + TMPDIR) = %u > sizeof(dest) !!!\n", (unsigned int)(cs_strlen(entries[n]->d_name) + cs_strlen(TMPDIR) - 1)); + free(entries[n]); + continue; + } + else + { + memcpy(dest, TMPDIR, cs_strlen(TMPDIR)); + memcpy(dest + cs_strlen(TMPDIR), entries[n]->d_name, cs_strlen(entries[n]->d_name)); + dest[cs_strlen(TMPDIR) + cs_strlen(entries[n]->d_name)] = '\0'; + pmt_fd = open(dest, O_RDONLY); + } + + if(pmt_fd < 0) + { + free(entries[n]); + continue; + } + + if(fstat(pmt_fd, &pmt_info) != 0) + { + int32_t ret = close(pmt_fd); + if(ret < 0) + { + cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno)); + } + free(entries[n]); + continue; + } + + int32_t found = 0; + + for(i = 0; i < MAX_DEMUX; i++) + { + if(strcmp(demux[i].pmt_file, entries[n]->d_name) == 0) + { + if((time_t)pmt_info.st_mtime == demux[i].pmt_time) + { + found = 1; + break; + } + } + } + + if(found) + { + int32_t ret = close(pmt_fd); + if(ret < 0) + { + cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno)); + } + free(entries[n]); + continue; + } + cs_log_dbg(D_DVBAPI, "found pmt file %s", dest); + cs_sleepms(100); + + uint32_t len = read(pmt_fd, mbuf, sizeof(mbuf)); + int32_t ret = close(pmt_fd); + + if(ret < 0) + { + cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno)); + } + + if(len < 1) + { + cs_log_dbg(D_DVBAPI, "pmt file %s have invalid len!", dest); + free(entries[n]); + continue; + } + + int32_t demux_id; +#ifdef QBOXHD + uint32_t j1, j2; + + // QboxHD pmt.tmp is the full capmt written as a string of hex values + // pmt.tmp must be longer than 3 bytes (6 hex chars) and even length + if((len < 6) || ((len % 2) != 0) || ((len / 2) > sizeof(dest))) + { + cs_log_dbg(D_DVBAPI, "error parsing QboxHD pmt.tmp, incorrect length"); + free(entries[n]); + continue; + } + + for(j2 = 0, j1 = 0; j2 < len; j2 += 2, j1++) + { + unsigned int tmp; + if(sscanf((char *)mbuf + j2, "%02X", &tmp) != 1) + { + cs_log_dbg(D_DVBAPI, "error parsing QboxHD pmt.tmp, data not valid in position %d", j2); + SAFE_MUTEX_UNLOCK(&event_handler_lock); + return; + } + else + { + memcpy(dest + j1, &tmp, 4); + } + } + + cs_log_dump_dbg(D_DVBAPI, (uint8_t *)dest, len / 2, "QboxHD pmt.tmp:"); + demux_id = dvbapi_parse_capmt((uint8_t *)dest + 4, (len / 2) - 4, -1, entries[n]->d_name, 0, 0); +#else + if(len > sizeof(dest)) + { + cs_log_dbg(D_DVBAPI, "event_handler() dest buffer is to small for pmt data!"); + free(entries[n]); + continue; + } + + if(len < 16) + { + cs_log_dbg(D_DVBAPI, "event_handler() received pmt is too small! (%d < 16 bytes!)", len); + free(entries[n]); + continue; + } + + cs_log_dump_dbg(D_DVBAPI, mbuf, len, "PMT file:"); // Original PMT file + + // Do some tidying on the PMT file to make it compatible with the CA PMT parser + dest[0] = CA_PMT_LIST_ONLY; + memcpy(dest + 1, mbuf + 3, 2); // program_number + uint16_t pmt_program_info_length = b2i(2, mbuf + 10) & 0x0FFF; + i2b_buf(2, pmt_program_info_length + 1, (uint8_t *)dest + 4); + dest[6] = CA_PMT_CMD_OK_DESCRAMBLING; + memcpy(dest + 7, mbuf + 12, len - 12 - 4); + + cs_log_dump_dbg(D_DVBAPI, (uint8_t *)dest, 7 + len - 12 - 4, "CA PMT:"); // Actual CA PMT message + demux_id = dvbapi_parse_capmt((uint8_t *)dest, 7 + len - 12 - 4, -1, entries[n]->d_name, 0, 0); +#endif + + if(demux_id >= 0) + { + cs_strncpy(demux[demux_id].pmt_file, entries[n]->d_name, sizeof(demux[demux_id].pmt_file)); + demux[demux_id].pmt_time = (time_t)pmt_info.st_mtime; + } + free(entries[n]); + + if(cfg.dvbapi_pmtmode == 3) + { + disable_pmt_files = 1; + break; + } + } + free(entries); + SAFE_MUTEX_UNLOCK(&event_handler_lock); +} + +void *dvbapi_event_thread(void *cli) +{ + struct s_client *client = (struct s_client *) cli; + SAFE_SETSPECIFIC(getclient, client); + set_thread_name(__func__); + + while(!exit_oscam) + { + cs_sleepms(750); + event_handler(0); + } + return NULL; +} + +void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer, int32_t len, uint32_t msgid) +{ + struct s_ecmpid *curpid = NULL; + int32_t pid = demux[demux_id].demux_fd[filter_num].pidindex; + uint16_t filtertype = demux[demux_id].demux_fd[filter_num].type; + uint16_t sctlen = SCT_LEN(buffer); + + if(sctlen < 4) + { + cs_log_dbg(D_DVBAPI, "Received filter data with invalid section length!"); + return; + } + + if(len < sctlen) + { + cs_log_dbg(D_DVBAPI, "Received filter data with total length 0x%03X but section length is 0x%03X -> invalid length!", len, sctlen); + return; + } + + if(demux_id < 0 || demux_id >= MAX_DEMUX) + { + cs_log("dvbapi_process_input(): error - received invalid demux_id (%d)", demux_id); + return; + } + + if(filter_num < 0 || filter_num >= MAX_FILTER) + { + cs_log("dvbapi_process_input(): error - received invalid filter_num (%d)", filter_num); + return; + } + + if(pid != -1 && filtertype == TYPE_ECM) + { + curpid = &demux[demux_id].ECMpids[pid]; + } + + int32_t filt_match = filtermatch(buffer, filter_num, demux_id, sctlen); // acts on all filters (sdt/emm/ecm) + if(!filt_match) + { + cs_log_dbg(D_DVBAPI,"Demuxer %d receiver returned data not matching the filter -> delivered filter data discarded!", demux_id); + return; + } + + if(curpid && curpid->tries <= 0xF0 && filtertype == TYPE_ECM) + { + curpid->irdeto_maxindex = 0; + curpid->irdeto_curindex = 0xFE; + curpid->tries = 0xFE; // reset timeout retry flag + curpid->irdeto_cycle = 0xFE; // reset irdetocycle + curpid->table = 0; + curpid->checked = 4; // flag ecmpid as checked + curpid->status = -1; // flag ecmpid as unusable + + if(pid == demux[demux_id].pidindex) + { + // current pid delivered problems so this pid isn't + // being used to descramble any longer -> clear pidindex + demux[demux_id].pidindex = -1; + + // remove this pid from channel cache since we had no founds on any ecmpid! + dvbapi_edit_channel_cache(demux_id, pid, 0); + } + + dvbapi_stop_filternum(demux_id, filter_num, msgid); // stop this ecm filter! + return; + } + + if(filtertype == TYPE_ECM && curpid) + { + uint32_t chid = 0x10000; + int8_t pvu_skip = 0; + ECM_REQUEST *er; + + if(len != 0) // len = 0 receiver encountered an internal bufferoverflow! + { + cs_log_dump_dbg(D_DVBAPI, buffer, sctlen, "Demuxer %d filter %d fetched ECM data (length = 0x%03X):", + demux_id, filter_num + 1, sctlen); + + if(sctlen > MAX_ECM_SIZE) // ecm too long to handle! + { + cs_log_dbg(D_DVBAPI, "Received data with total length 0x%03X but max supported ECM length is 0x%03X -> Please report!", + sctlen, MAX_ECM_SIZE); + + curpid->tries -= 0x0E; + + return; + } + + if(!(buffer[0] == 0x80 || buffer[0] == 0x81)) + { + cs_log_dbg(D_DVBAPI, "Received an ECM with invalid ecmtable ID %02X -> ignoring!", buffer[0]); + curpid->tries--; + + return; + } + +#ifdef WITH_EMU + if(caid_is_powervu(curpid->CAID)) // ecm counter for powervu + { + pvu_skip = 1; + + if(sctlen - 11 > buffer[9]) + { + if(buffer[11 + buffer[9]] > curpid->pvu_counter + || (curpid->pvu_counter == 255 && buffer[11 + buffer[9]] == 0) + || ((curpid->pvu_counter - buffer[11 + buffer[9]]) > 5)) + { + curpid->pvu_counter = buffer[11 + buffer[9]]; + pvu_skip = 0; + } + } + } +#endif + // wait for odd / even ecm change (only not for irdeto!) + if((curpid->table == buffer[0] && !caid_is_irdeto(curpid->CAID)) || pvu_skip) + { + if(!(er = get_ecmtask())) + { + return; + } + er->srvid = demux[demux_id].program_number; + +#ifdef WITH_STAPI5 + cs_strncpy(er->dev_name, dev_list[demux[demux_id].dev_index].name, sizeof(dev_list[demux[demux_id].dev_index].name)); +#endif + er->tsid = demux[demux_id].tsid; + er->onid = demux[demux_id].onid; + er->pmtpid = demux[demux_id].pmtpid; + er->ens = demux[demux_id].ens; + er->caid = curpid->CAID; + er->pid = curpid->ECM_PID; + er->prid = curpid->PROVID; + er->vpid = curpid->VPID; + er->ecmlen = sctlen; + memcpy(er->ecm, buffer, er->ecmlen); + chid = get_subid(er); // fetch chid or fake chid + er->chid = chid; + er->msgid = msgid; + dvbapi_set_section_filter(demux_id, er, filter_num); + NULLFREE(er); + return; + } + + if(caid_is_irdeto(curpid->CAID)) + { + // 80 70 39 53 04 05 00 88 + // 81 70 41 41 01 06 00 13 00 06 80 38 1F 52 93 D2 + //if(buffer[5]>20) return; + if(curpid->irdeto_maxindex != buffer[5]) // 6, register max irdeto index + { + cs_log_dbg(D_DVBAPI, "Found %d IRDETO ECM CHIDs", buffer[5] + 1); + curpid->irdeto_maxindex = buffer[5]; // numchids = 7 (0..6) + } + } + } + + if(!(er = get_ecmtask())) + { + return; + } + er->srvid = demux[demux_id].program_number; + +#ifdef WITH_STAPI5 + cs_strncpy(er->dev_name, dev_list[demux[demux_id].dev_index].name, sizeof(dev_list[demux[demux_id].dev_index].name)); +#endif + + er->tsid = demux[demux_id].tsid; + er->onid = demux[demux_id].onid; + er->pmtpid = demux[demux_id].pmtpid; + er->ens = demux[demux_id].ens; + er->caid = curpid->CAID; + er->pid = curpid->ECM_PID; + er->prid = curpid->PROVID; + er->vpid = curpid->VPID; + er->ecmlen = sctlen; + memcpy(er->ecm, buffer, er->ecmlen); + er->msgid = (msgid > 0) ? msgid : demux[demux_id].msgid; + chid = get_subid(er); // fetch chid or fake chid + uint32_t fixedprovid = chk_provid(er->ecm, er->caid); + + if(fixedprovid && fixedprovid != er->prid) + { + cs_log_dbg(D_DVBAPI, "Fixing provid ecmpid %d from %06X -> %06X", pid, curpid->PROVID, fixedprovid); + curpid->PROVID = fixedprovid; + + if(!USE_OPENXCAS) + { + cs_log_dbg(D_DVBAPI, "Fixing provid filter %d from %06X -> %06X", + filter_num + 1, demux[demux_id].demux_fd[filter_num].provid, fixedprovid); + + demux[demux_id].demux_fd[filter_num].provid = fixedprovid; + } + cs_log_dbg(D_DVBAPI, "Fixing provid ecmrequest from %06X -> %06X", er->prid, fixedprovid); + er->prid = fixedprovid; + } + er->chid = chid; + + // only used on receiver internal buffer overflow + // to get quickly fresh ecm filterdata otherwise freezing! + if(len == 0) + { + curpid->table = 0; + dvbapi_set_section_filter(demux_id, er, filter_num); + NULLFREE(er); + return; + } + + if(caid_is_irdeto(curpid->CAID)) + { + if(curpid->irdeto_curindex != buffer[4]) // old style wrong irdeto index + { + if(curpid->irdeto_curindex == 0xFE) // check if this ecmfilter just started up + { + // on startup set the current index to the irdeto index of the ecm + curpid->irdeto_curindex = buffer[4]; + } + else // we are already running and not interested in this ecm + { + if(curpid->table != buffer[0]) // fix for receivers not supporting section filtering + { + curpid->table = 0; + } + + // set ecm filter to odd + even since + // this ecm doesn't match with current irdeto index + dvbapi_set_section_filter(demux_id, er, filter_num); + + NULLFREE(er); + return; + } + } + else // fix for receivers not supporting section filtering + { + if(curpid->table == buffer[0]) + { + NULLFREE(er); + return; + } + } + cs_log_dbg(D_DVBAPI, "Demuxer %d ECMTYPE %02X CAID %04X PROVID %06X ECMPID %04X IRDETO INDEX %02X MAX INDEX %02X CHID %04X CYCLE %02X VPID %04X", + demux_id, er->ecm[0], er->caid, er->prid, er->pid, er->ecm[4], er->ecm[5], er->chid, curpid->irdeto_cycle, er->vpid); + } + else + { + cs_log_dbg(D_DVBAPI, "Demuxer %d ECMTYPE %02X CAID %04X PROVID %06X ECMPID %04X FAKECHID %04X (unique part in ecm)", + demux_id, er->ecm[0], er->caid, er->prid, er->pid, er->chid); + } + + // check for matching chid (unique ecm part in case of non-irdeto cas) + // plus added fix for seca2 monthly changing fakechid + if((curpid->CHID < 0x10000) && !((chid == curpid->CHID) || ((curpid->CAID >> 8 == 0x01) && (chid & 0xF0FF) == (curpid->CHID & 0xF0FF)))) + { + if(caid_is_irdeto(curpid->CAID)) + { + // if same: we cycled all indexes but no luck! + if((curpid->irdeto_cycle < 0xFE) && (curpid->irdeto_cycle == curpid->irdeto_curindex)) + { + struct s_dvbapi_priority *forceentry = dvbapi_check_prio_match(demux_id, pid, 'p'); + + // forced pid? keep trying the forced ecmpid, no force kill ecm filter + if(!forceentry || !forceentry->force) + { + if(curpid->checked == 2) + { + curpid->checked = 4; + } + + if(curpid->checked == 1) + { + curpid->checked = 2; + curpid->CHID = 0x10000; + } + + dvbapi_stop_filternum(demux_id, filter_num, msgid); // stop this ecm filter! + NULLFREE(er); + return; + } + } + curpid->irdeto_curindex++; // set check on next index + + if(curpid->irdeto_cycle == 0xFE) + { + curpid->irdeto_cycle = buffer[4]; // on startup set to current irdeto index + } + + if(curpid->irdeto_curindex > curpid->irdeto_maxindex) + { + curpid->irdeto_curindex = 0; // check if we reached max irdeto index, if so reset to 0 + } + curpid->table = 0; + + // set ecm filter to odd + even since + // this ecm doesn't match with current irdeto index + dvbapi_set_section_filter(demux_id, er, filter_num); + + NULLFREE(er); + return; + } + else // all non irdeto cas systems + { + struct s_dvbapi_priority *forceentry = dvbapi_check_prio_match(demux_id, pid, 'p'); + curpid->table = 0; + + // set ecm filter to odd + even since + // this ecm doesn't match with current irdeto index + dvbapi_set_section_filter(demux_id, er, filter_num); + + if(forceentry && forceentry->force) + { + NULLFREE(er); + return; // forced pid? keep trying the forced ecmpid! + } + + if(curpid->checked == 2) + { + curpid->checked = 4; + } + + if(curpid->checked == 1) + { + curpid->checked = 2; + curpid->CHID = 0x10000; + } + + dvbapi_stop_filternum(demux_id, filter_num, msgid); // stop this ecm filter! + NULLFREE(er); + return; + } + } + + struct s_dvbapi_priority *p; + for(p = dvbapi_priority; p != NULL; p = p->next) + { + if(p->type != 'l' + || (p->caid && p->caid != curpid->CAID) + || (p->provid && p->provid != curpid->PROVID) + || (p->ecmpid && p->ecmpid != curpid->ECM_PID) + || (p->srvid && p->srvid != demux[demux_id].program_number)) + { + continue; + } + + if((uint)p->delay == sctlen && p->force < 6) + { + p->force++; + NULLFREE(er); + return; + } + + if(p->force >= 6) + { + p->force = 0; + } + } + + if(!curpid->PROVID) + { + curpid->PROVID = chk_provid(buffer, curpid->CAID); + } + + if(caid_is_irdeto(curpid->CAID)) // irdeto: wait for the correct index + { + if(buffer[4] != curpid->irdeto_curindex) + { + curpid->table = 0; + + // set ecm filter to odd + even since + // this ecm doesn't match with current irdeto index + dvbapi_set_section_filter(demux_id, er, filter_num); + + NULLFREE(er); + return; + } + } + + // we have an ecm with the correct irdeto index (or fakechid) + for(p = dvbapi_priority; p != NULL ; p = p->next) // check for ignore! + { + if((p->type != 'i') + || (p->caid && p->caid != curpid->CAID) + || (p->provid && p->provid != curpid->PROVID) + || (p->ecmpid && p->ecmpid != curpid->ECM_PID) + || (p->pidx && p->pidx - 1 != pid) + || (p->srvid && p->srvid != demux[demux_id].program_number)) + { + continue; + } + + // found an ignore chid match with current ecm -> ignoring this irdeto index + if(p->type == 'i' && (p->chid < 0x10000 && p->chid == chid)) + { + curpid->irdeto_curindex++; + if(curpid->irdeto_cycle == 0xFE) + { + curpid->irdeto_cycle = buffer[4]; // on startup set to current irdeto index + } + + if(curpid->irdeto_curindex > curpid->irdeto_maxindex) // check if curindex is over the max + { + curpid->irdeto_curindex = 0; + } + curpid->table = 0; + + // irdeto: wait for the correct index + check if we cycled all + if(caid_is_irdeto(curpid->CAID) && (curpid->irdeto_cycle != curpid->irdeto_curindex)) + { + // set ecm filter to odd + even since this chid has to be ignored! + dvbapi_set_section_filter(demux_id, er, filter_num); + } + else // this fakechid has to be ignored, kill this filter! + { + if(curpid->checked == 2) + { + curpid->checked = 4; + } + + if(curpid->checked == 1) + { + curpid->checked = 2; + curpid->CHID = 0x10000; + } + + dvbapi_stop_filternum(demux_id, filter_num, msgid); // stop this ecm filter! + } + NULLFREE(er); + return; + } + } + + if(er) + { + curpid->table = er->ecm[0]; + } + + request_cw(dvbapi_client, er, demux_id, 1); // register this ecm for delayed ecm response check + return; // end of ecm filterhandling! + } + + if(filtertype == TYPE_EMM) + { + if(len != 0) // len = 0 receiver encountered an internal buffer overflow! + { + cs_log_dump_dbg(D_DVBAPI, buffer, sctlen, "Demuxer %d filter %d fetched EMM data (length = 0x%03X):", + demux_id, filter_num + 1, sctlen); + + if(sctlen > MAX_EMM_SIZE) // emm too long to handle! + { + cs_log_dbg(D_DVBAPI, "Received data with total length 0x%03X but max supported EMM length is 0x%03X -> Please report!", + sctlen, MAX_EMM_SIZE); + + return; + } + } + else + { + return; // just skip on internal buffer overflow + } + +#ifdef WITH_EMU + if(caid_is_director(demux[demux_id].demux_fd[filter_num].caid)) + { + uint32_t i; + uint32_t emmhash; + + if(sctlen < 4) + { + return; + } + + for(i = 0; i + 2 < sctlen; i++) + { + if(buffer[i] == 0xF0 && (buffer[i + 2] == 0xE1 || buffer[i + 2] == 0xE4)) + { + emmhash = (buffer[3] << 8) | buffer[sctlen - 2]; + if(demux[demux_id].demux_fd[filter_num].cadata == emmhash) + { + return; + } + + demux[demux_id].demux_fd[filter_num].cadata = emmhash; + dvbapi_process_emm(demux_id, filter_num, buffer, sctlen); + return; + } + } + return; + } +#endif + // fix to handle more than one irdeto emm packet + uint8_t *pbuf = buffer; + int32_t done = 0; + int32_t unhandled = len; + + while(len > done) + { + pbuf += done; + sctlen = SCT_LEN(pbuf); + + if(unhandled < 4 || (int32_t)sctlen > unhandled || sctlen > MAX_EMM_SIZE || sctlen < 4) + { + break; + } + + dvbapi_process_emm(demux_id, filter_num, pbuf, sctlen); + done += sctlen; + unhandled -= sctlen; + } + } + + if(filtertype == TYPE_SDT) + { + cs_log_dump_dbg(D_DVBAPI, buffer, sctlen, "Demuxer %d filter %d fetched SDT data (length = 0x%03X):", + demux_id, filter_num + 1, sctlen); + + dvbapi_parse_sdt(demux_id, buffer, sctlen, msgid); + } + + if(filtertype == TYPE_PAT) + { + cs_log_dump_dbg(D_DVBAPI, buffer, sctlen, "Demuxer %d filter %d fetched PAT data (length = 0x%03X):", + demux_id, filter_num + 1, sctlen); + + dvbapi_parse_pat(demux_id, buffer, sctlen, msgid); + } + + if(filtertype == TYPE_PMT) + { + cs_log_dump_dbg(D_DVBAPI, buffer, sctlen, "Demuxer %d filter %d fetched PMT data (length = 0x%03X):", + demux_id, filter_num + 1, sctlen); + + dvbapi_parse_pmt(demux_id, buffer, sctlen, msgid); + } + + if(filtertype == TYPE_CAT) + { + cs_log_dump_dbg(D_DVBAPI, buffer, sctlen, "Demuxer %d filter %d fetched CAT data (length = 0x%03X):", + demux_id, filter_num + 1, sctlen); + + dvbapi_parse_cat(demux_id, buffer, sctlen, msgid); + } +} + +static int32_t dvbapi_recv(int32_t connfd, uint8_t *mbuf, size_t rlen) +{ + ssize_t len = cs_recv(connfd, mbuf, rlen, MSG_DONTWAIT); + + if((len == -1 && (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)) || (len == 0)) + { + return -1; + } + + if(len == -1) + { + return 0; + } + + return len; +} + +static uint16_t dvbapi_get_nbof_missing_header_bytes(uint8_t *mbuf, uint16_t mbuf_len, uint32_t msgid_size) +{ + uint16_t commandsize = 4; + commandsize += msgid_size; + if(mbuf_len < commandsize) + { + return commandsize - mbuf_len; + } + else + { + mbuf += msgid_size; + uint32_t opcode = b2i(4, mbuf); + + if((opcode & 0xFFFFF000) == DVBAPI_AOT_CA) + { + if(mbuf[3] & 0x80) + { + uint32_t size = mbuf[3] & 0x7F; + if(mbuf_len < (commandsize + size)) + { + return (commandsize + size) - mbuf_len; + } + } + return 0; + } + else + { + switch (opcode) + { + case DVBAPI_FILTER_DATA: + commandsize = 9; + commandsize += msgid_size; + if(mbuf_len < commandsize) + { + return commandsize - mbuf_len; + } + return 0; + + case DVBAPI_CLIENT_INFO: + commandsize = 7; + commandsize += msgid_size; + if(mbuf_len < commandsize) + { + return commandsize - mbuf_len; + } + return 0; + + default: + return 0; + } + } + } +} + +static void set_chunksize_data_len_to_invalid(uint16_t *chunksize, uint16_t *data_len) +{ + (*chunksize) = 1; + (*data_len) = 1; +} + +static void log_packeterror(uint16_t mbuf_len, const char* command) +{ + cs_log("dvbapi_get_packet_size(): error - buffer length (%" PRIu16 ") too short for %s", mbuf_len, command); +} + +static bool is_commandsize_valid(uint32_t commandsize, uint16_t mbuf_len, const char* command) +{ + bool isValid = mbuf_len >= commandsize; + if(!isValid) + { + log_packeterror(mbuf_len, command); + } + return isValid; +} + +static uint8_t get_asn1packetsize(uint8_t *mbuf, uint16_t mbuf_len, const char *command, uint32_t *tmp_data_len) +{ + uint8_t sizebytes = 0; + uint8_t commandsize = 4; + *tmp_data_len = mbuf[3] & 0x7F; + if(mbuf[3] & 0x80) + { + sizebytes = *tmp_data_len; + if(is_commandsize_valid(3 + sizebytes, mbuf_len, command)) + { + *tmp_data_len = b2i(sizebytes, mbuf + 4); + } + else + { + return 0; + } + } + return commandsize + sizebytes; +} + +static void dvbapi_get_packet_size(uint8_t *mbuf, uint16_t mbuf_len, uint16_t *chunksize, uint16_t *data_len) +{ + //chunksize: size of complete chunk in the buffer (an opcode with the data) + //data_len: variable for internal data length (eg. for the filter data size, PMT len) + (*chunksize) = 0; + (*data_len) = 0; + + if(mbuf_len < 4) + { + cs_log("dvbapi_get_packet_size(): error - buffer length (%" PRIu16 ") too short", mbuf_len); + set_chunksize_data_len_to_invalid(chunksize, data_len); + return; + } + + int32_t commandsize = 0; + char* command = "DVBAPI_UNKNOWN_COMMAND"; + uint32_t tmp_data_len = 0; + uint32_t opcode = b2i(4, mbuf); + + switch (opcode) + { + case DVBAPI_AOT_CA_STOP: + { + command = "DVBAPI_AOT_CA_STOP"; + commandsize = get_asn1packetsize(mbuf, mbuf_len, command, &tmp_data_len); + break; + } + case DVBAPI_FILTER_DATA: + { + command = "DVBAPI_FILTER_DATA"; + commandsize = 9; + if(is_commandsize_valid(commandsize, mbuf_len, command)) + { + tmp_data_len = b2i(2, mbuf + 7) & 0x0FFF; + } + break; + } + + case DVBAPI_CLIENT_INFO: + { + command = "DVBAPI_CLIENT_INFO"; + commandsize = 7; + if(is_commandsize_valid(commandsize, mbuf_len, command)) + { + tmp_data_len = mbuf[6]; + } + break; + } + + default: + { + if((opcode & 0xFFFFFF00) == DVBAPI_AOT_CA_PMT) + { + command = "DVBAPI_AOT_CA_PMT"; + commandsize = get_asn1packetsize(mbuf, mbuf_len, command, &tmp_data_len); + break; + } + else + { + cs_log("Unknown socket command received: 0x%08X", opcode); + } + break; + } + } + + if(tmp_data_len == 0 || commandsize == 0) + { + set_chunksize_data_len_to_invalid(chunksize, data_len); + return; + } + + if(tmp_data_len + commandsize > 0xFFFF) + { + cs_log("This packet is too big: %d bytes => truncated!", tmp_data_len); + tmp_data_len = 0xFFFF - commandsize; + } + + (*data_len) = tmp_data_len; + (*chunksize) += commandsize + tmp_data_len; + + if(*chunksize > mbuf_len) + { + cs_log_dbg(D_DVBAPI, "This %s packet is incomplete => command length is (%" PRIu16 ")", command, *chunksize); + } + else + { + cs_log_dbg(D_DVBAPI, "This is a %s packet with size %d => lets process it!", command, (*chunksize)); + } +} + +static void dvbapi_handlesockmsg(uint8_t *mbuf, uint16_t chunksize, uint16_t data_len, int32_t connfd, uint16_t *client_proto_version) +{ + uint32_t msgid = 0; + if(*client_proto_version >= 3) + { + if(mbuf[0] != 0xa5) + { + cs_log("Error: network packet malformed! (no start)"); + return; + } + msgid = b2i(4, mbuf + 1); + mbuf += 5; + } + + uint32_t opcode = b2i(4, mbuf); + switch(opcode) + { + case DVBAPI_FILTER_DATA: + { + int32_t demux_id = mbuf[4]; + int32_t filter_num = mbuf[5]; + + if(demux_id < 0 || demux_id >= MAX_DEMUX) + { + cs_log("dvbapi_handlesockmsg(): error - received invalid demux_id (%d)", demux_id); + break; + } + + if(filter_num < 0 || filter_num >= MAX_FILTER) + { + cs_log("dvbapi_handlesockmsg(): error - received invalid filter_num (%d)", filter_num); + break; + } + dvbapi_process_input(demux_id, filter_num, mbuf + 6, data_len + 3, msgid); + break; + } + + case DVBAPI_CLIENT_INFO: + { + uint16_t client_proto = b2i(2, mbuf + 4); + NULLFREE(last_client_name); + + if(cs_malloc(&last_client_name, data_len + 1)) + { + memcpy(last_client_name, &mbuf[7], data_len); + last_client_name[data_len] = 0; + cs_log("Client connected: '%s' (protocol version = %" PRIu16 ")", last_client_name, client_proto); + } + dvbapi_net_send(DVBAPI_SERVER_INFO, connfd, msgid, -1, -1, NULL, NULL, NULL, client_proto); + + // now the protocol handshake is complete set correct version so all further packets are sent with correct message id. + (*client_proto_version) = client_proto; + + // setting the global var according to the client + last_client_proto_version = client_proto; + break; + } + + case DVBAPI_AOT_CA_PMT: + { + cs_log_dbg(D_DVBAPI,"Received DVBAPI_AOT_CA_PMT object on socket %d:", connfd); + dvbapi_parse_capmt(mbuf + (chunksize - data_len), data_len, connfd, NULL, *client_proto_version, msgid); + break; + } + + case DVBAPI_AOT_CA_STOP: + { + cs_log_dbg(D_DVBAPI, "Received DVBAPI_AOT_CA_STOP object on socket %d:", connfd); + if(cfg.dvbapi_boxtype == BOXTYPE_IPBOX || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX || dvbapi_listenport_active) + { + int32_t i; + int32_t demux_index = mbuf[7]; + for(i = 0; i < MAX_DEMUX; i++) + { + // 0xff demux_index is a wildcard => close all related demuxers + if(demux_index == 0xff) + { + if(demux[i].socket_fd == connfd) + { + dvbapi_stop_descrambling(i, msgid); + } + } + else if(demux[i].demux_index == demux_index) + { + dvbapi_stop_descrambling(i, msgid); + break; + } + } + + // ipbox fix + if(cfg.dvbapi_boxtype == BOXTYPE_IPBOX) + { + // check do we have any demux running on this fd + int16_t execlose = 1; + for(i = 0; i < MAX_DEMUX; i++) + { + if(demux[i].socket_fd == connfd) + { + execlose = 0; + break; + } + } + if(execlose) + { + int32_t ret = close(connfd); + if(ret < 0) + { + cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno)); + } + } + } + } + else if(cfg.dvbapi_pmtmode != 6) + { + int32_t ret = close(connfd); + if(ret < 0) + { + cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno)); + } + } + break; + } + default: + { + if((opcode & 0xFFFFFF00) == DVBAPI_AOT_CA_PMT) + { + cs_log_dbg(D_DVBAPI, "Received DVBAPI_AOT_CA_PMT object on socket %d:", connfd); + dvbapi_parse_capmt(mbuf + (chunksize - data_len), data_len, connfd, NULL, *client_proto_version, msgid); + } + else + { + cs_log("Unknown socket command received: 0x%08X", opcode); + } + break; + } + } +} + +static bool dvbapi_handlesockdata(int32_t connfd, uint8_t *mbuf, uint16_t mbuf_size, uint16_t unhandled_len, uint16_t *new_unhandled_len, uint16_t *client_proto_version) +{ + int32_t recv_result; + uint16_t chunksize = 1, data_len = 1; + uint8_t packet_count = 1; + uint32_t msgid_size = 0; + uint16_t missing_header_bytes = 0; + if(*client_proto_version >= 3) + { + msgid_size = 5; + } + + do + { + missing_header_bytes = dvbapi_get_nbof_missing_header_bytes(mbuf, unhandled_len, msgid_size); + + if(missing_header_bytes != 0) + { + // read first few bytes so we know packet type and length + cs_log_dbg(D_TRACE, "%s reading %" PRIu16 " bytes from connection fd %d", (unhandled_len == 0) ? "Try" : "Continue", missing_header_bytes, connfd); + + recv_result = dvbapi_recv(connfd, mbuf + unhandled_len, mbuf_size - unhandled_len); + if(recv_result < 1) + { + (*new_unhandled_len) = unhandled_len; + return (recv_result != -1); + } + else + { + unhandled_len += recv_result; + if(unhandled_len < missing_header_bytes) + { + (*new_unhandled_len) = unhandled_len; + return true; + } + } + } + + cs_log_dump_dbg(D_DVBAPI, mbuf, unhandled_len, "Got packetdata (msgid size: %d, clientprotocol: %d)", msgid_size, *client_proto_version); + dvbapi_get_packet_size(mbuf+msgid_size, unhandled_len-msgid_size, &chunksize, &data_len); + + chunksize+=msgid_size; + if(chunksize > mbuf_size) + { + cs_log("***** WARNING: SOCKET DATA BUFFER OVERFLOW (%" PRIu16 " bytes), PLEASE REPORT! ****** ", chunksize); + (*new_unhandled_len) = 0; + return true; + } + + if(unhandled_len < chunksize) // we are missing some bytes, try to read them + { + cs_log_dbg(D_TRACE, "Continue to read the missing %d bytes from connection fd %d", chunksize - unhandled_len, connfd); + recv_result = dvbapi_recv(connfd, mbuf + unhandled_len, mbuf_size - unhandled_len); + if(recv_result < 1) + { + (*new_unhandled_len) = unhandled_len; + return (recv_result != -1); + } + else + { + unhandled_len += recv_result; + if(unhandled_len < chunksize) + { + (*new_unhandled_len) = unhandled_len; + return true; + } + } + } + + dvbapi_handlesockmsg(mbuf, chunksize-msgid_size, data_len, connfd, client_proto_version); + + unhandled_len -= chunksize; + if(unhandled_len > 0) + { + memmove(mbuf, mbuf + chunksize, unhandled_len); + } + } while(unhandled_len != 0 && packet_count++ < 8); + + cs_log_dbg(D_DVBAPI, "Processing socketdata completed after %d packets with %d bytes left unprocessed", packet_count, unhandled_len); + + (*new_unhandled_len) = unhandled_len; + return true; +} + +static void add_to_assoc_fd(int sock) +{ + uint i; + + for(i = 0; i < MAX_ASSOC_FD; i++) + { + if(assoc_fd[i] == sock) + { + return; // do not add twice + } + } + + for(i = 0; i < MAX_ASSOC_FD; i++) + { + if(!assoc_fd[i]) + { + assoc_fd[i] = sock; + return; + } + } +} + +static void del_from_assoc_fd(int sock) +{ + uint i; + + for(i = 0; i < MAX_ASSOC_FD; i++) + { + if(assoc_fd[i] == sock) + { + assoc_fd[i] = 0; + } + } +} + +static void *dvbapi_main_local(void *cli) +{ + int32_t i, j, l; + struct s_client *client = (struct s_client *)cli; + client->thread = pthread_self(); + SAFE_SETSPECIFIC(getclient, cli); + dvbapi_client = cli; + int32_t maxpfdsize = (MAX_DEMUX * maxfilter) + MAX_DEMUX + 2; + struct pollfd pfd2[maxpfdsize]; + struct timeb start, end; // start time poll, end time poll +#define PMT_SERVER_SOCKET "/tmp/.listen.camd.socket" + struct sockaddr_un saddr; + saddr.sun_family = AF_UNIX; + cs_strncpy(saddr.sun_path, PMT_SERVER_SOCKET, sizeof(saddr.sun_path)); + int32_t rc, pfdcount, g, connfd, clilen; + int32_t ids[maxpfdsize], fdn[maxpfdsize], type[maxpfdsize]; + struct SOCKADDR servaddr; + ssize_t len = 0; + static const uint16_t mbuf_size = 2048; + uint8_t *mbuf; + uint16_t unhandled_buf_len[maxpfdsize], unhandled_buf_used[maxpfdsize]; + uint8_t *unhandled_buf[maxpfdsize]; + struct s_auth *account; + int32_t ok = 0; + uint16_t client_proto_version[maxpfdsize]; + + if(!cs_malloc(&mbuf, sizeof(uint8_t) * mbuf_size)) + { + return NULL; + } + + for(i = 0; i < maxpfdsize; i++) + { + unhandled_buf[i] = NULL; + unhandled_buf_len[i] = 0; + unhandled_buf_used[i] = 0; + client_proto_version[i] = 0; + } + + for(account = cfg.account; account != NULL; account = account->next) + { + if((ok = is_dvbapi_usr(account->usr))) + { + break; + } + } + + cs_auth_client(client, ok ? account : (struct s_auth *)(-1), "dvbapi"); + memset(demux, 0, sizeof(demux)); + + for(i = 0; i < MAX_DEMUX; i++) + { + SAFE_MUTEX_INIT(&demux[i].answerlock, NULL); + for(j = 0; j < MAX_ECM_PIDS; j++) + { + for(l = 0; l < MAX_STREAM_INDICES; l++) + { + demux[i].ECMpids[j].index[l] = INDEX_INVALID; + } + } + demux[i].pidindex = -1; + demux[i].curindex = -1; + } + + memset(ca_fd, 0, sizeof(ca_fd)); + memset(assoc_fd, 0, sizeof(assoc_fd)); + dvbapi_read_priority(); + dvbapi_load_channel_cache(); + dvbapi_detect_api(); + + if(selected_box == -1 || selected_api == -1) + { + cs_log("ERROR: Could not detect DVBAPI version."); + free(mbuf); + return NULL; + } + + // detect box type first and then get descrambler info + dvbapi_get_descrambler_info(); + + if(cfg.dvbapi_pmtmode == 1) + { + disable_pmt_files = 1; + } + + int32_t listenfd = -1; + if(cfg.dvbapi_boxtype != BOXTYPE_IPBOX_PMT && + cfg.dvbapi_pmtmode != 2 && cfg.dvbapi_pmtmode != 5 && cfg.dvbapi_pmtmode != 6) + { + if(!dvbapi_listenport_active) + { + listenfd = dvbapi_init_listenfd(); + } + else + { + listenfd = dvbapi_net_init_listenfd(); + } + + if(listenfd < 1) + { + cs_log("ERROR: Could not init socket: (errno=%d: %s)", errno, strerror(errno)); + free(mbuf); + return NULL; + } + } + + for(i = 0; i < MAX_DEMUX; i++) // init all demuxers! + { + demux[i].pidindex = -1; + demux[i].curindex = -1; + } + + if(cfg.dvbapi_pmtmode != 4 && cfg.dvbapi_pmtmode != 5 && cfg.dvbapi_pmtmode != 6) + { + struct sigaction signal_action; + signal_action.sa_handler = event_handler; + sigemptyset(&signal_action.sa_mask); + signal_action.sa_flags = SA_RESTART; + sigaction(SIGRTMIN + 1, &signal_action, NULL); + + dir_fd = open(TMPDIR, O_RDONLY); + if(dir_fd >= 0) + { + fcntl(dir_fd, F_SETSIG, SIGRTMIN + 1); + fcntl(dir_fd, F_NOTIFY, DN_MODIFY | DN_CREATE | DN_DELETE | DN_MULTISHOT); + event_handler(SIGRTMIN + 1); + } + } + else + { + int32_t ret = start_thread("dvbapi event", dvbapi_event_thread, (void *) dvbapi_client, NULL, 1, 0); + if(ret) + { + free(mbuf); + return NULL; + } + } + + if(listenfd != -1) + { + pfd2[0].fd = listenfd; + pfd2[0].events = (POLLIN | POLLPRI); + type[0] = 1; + } + +#if defined WITH_COOLAPI || defined WITH_COOLAPI2 || defined WITH_NEUTRINO + int sysret = system("pzapit -rz"); + if(sysret == -1){ + // To avoid not used and correct error handling by not ignoring system return value + } +#endif + cs_ftime(&start); // register start time + + while(!exit_oscam) + { + if(pausecam) // for dbox2, STAPI or PC in standby mode don't parse any ecm/emm or try to start next filter + { + continue; + } + + if(cfg.dvbapi_pmtmode == 6) + { + if(listenfd < 0) + { + cs_log("PMT mode 6: Connecting to enigma CA PMT listen socket..."); + + // socket init + if((listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + cs_log("socket error (errno=%d %s)", errno, strerror(errno)); + listenfd = -1; + } + else if(connect(listenfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) + { + cs_log("socket connect error (errno=%d %s)", errno, strerror(errno)); + close(listenfd); + listenfd = -1; + } + else + { + pfd2[0].fd = listenfd; + pfd2[0].events = (POLLIN | POLLPRI); + type[0] = 1; + client_proto_version[0] = 0; + cs_log("PMT mode 6: Successfully connected to CA PMT server (fd %d)", listenfd); + } + } + + if(listenfd == -1) // not connected! + { + cs_sleepms(1000); + continue; // start fresh connect attempt! + } + } + + pfdcount = (listenfd > -1) ? 1 : 0; + + for(i = 0; i < MAX_ASSOC_FD; i++) // add all associated fds (this should include also demux[X].socket_fd) + { + if(assoc_fd[i]) + { + pfd2[pfdcount].fd = assoc_fd[i]; + pfd2[pfdcount].events = (POLLIN | POLLPRI); + client_proto_version[pfdcount] = last_client_proto_version; + type[pfdcount++] = 1; + } + } + + for(i = 0; i < MAX_DEMUX; i++) + { + if(demux[i].program_number == 0) + { + continue; // only evalutate demuxers that have channels assigned + } + + uint32_t ecmcounter = 0, emmcounter = 0; + for(g = 0; g < maxfilter; g++) + { + if(demux[i].demux_fd[g].fd <= 0) + { + continue; // deny obvious invalid fd! + } + + if(!dvbapi_listenport_active && cfg.dvbapi_boxtype != BOXTYPE_PC_NODMX + && selected_api != STAPI && selected_api != COOLAPI) + { + pfd2[pfdcount].fd = demux[i].demux_fd[g].fd; + pfd2[pfdcount].events = (POLLIN | POLLPRI); + ids[pfdcount] = i; + fdn[pfdcount] = g; + type[pfdcount++] = 0; + } + + // count ecm filters to see if demuxing is possible anyway +#ifdef MODULE_STREAMRELAY + if(cfg.dvbapi_demuxer_fix) + { + if(demux[i].demux_fd[g].type == TYPE_ECM || demux[i].demux_fd[g].type == 3 || demux[i].demux_fd[g].type == 6) + { + ecmcounter++; + } + } + else + { +#endif + if(demux[i].demux_fd[g].type == TYPE_ECM) + { + ecmcounter++; + } +#ifdef MODULE_STREAMRELAY + } +#endif + + // count emm filters also + if(demux[i].demux_fd[g].type == TYPE_EMM) + { + emmcounter++; + } + } + + // only produce log if something changed + if(ecmcounter != demux[i].old_ecmfiltercount || emmcounter != demux[i].old_emmfiltercount) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d has %d ecmpids, %d streampids, %d ecmfilters and %d of max %d emmfilters", + i, demux[i].ECMpidcount, demux[i].STREAMpidcount, ecmcounter, emmcounter, demux[i].max_emm_filter); + + demux[i].old_ecmfiltercount = ecmcounter; // save new amount of ecm filters + demux[i].old_emmfiltercount = emmcounter; // save new amount of emm filters + } + + // delayed emm start for non irdeto caids, + // start emm cat if not already done for this demuxer! + struct timeb now; + cs_ftime(&now); + int64_t gone; + int8_t do_emm_start = (cfg.dvbapi_au > 0 && demux[i].emm_filter == -1 && demux[i].EMMpidcount == 0 && emmcounter == 0); + int8_t do_sdt_start = (cfg.dvbapi_read_sdt && demux[i].sdt_filter == -1 && cfg.dvbapi_boxtype != BOXTYPE_SAMYGO); + + if(do_emm_start || do_sdt_start) + { + gone = comp_timeb(&now, &demux[i].emmstart); + if(gone > 20 * 1000) + { + if(do_emm_start) + { + cs_ftime(&demux[i].emmstart); // trick to let emm fetching start after 30 seconds to speed up zapping + dvbapi_start_cat_filter(i); + } + } + + if(gone > 5 * 1000) + { + if(do_sdt_start) + { + dvbapi_start_sdt_filter(i); + } + } + } + + // early start for irdeto since they need emm before ecm + // (pmt emmstart = 1 if detected caid 0x06) + int32_t emmstarted = demux[i].emm_filter; + + // check every time since share readers might + // give us new filters due to hexserial change + if(cfg.dvbapi_au && demux[i].EMMpidcount > 0) + { + if(!emmcounter && emmstarted == -1) + { + demux[i].emmstart = now; + dvbapi_start_emm_filter(i); // start emm filtering if emm pids are found + } + else + { + gone = comp_timeb(&now, &demux[i].emmstart); + if(gone > 30 * 1000) + { + demux[i].emmstart = now; + dvbapi_start_emm_filter(i); // start emm filtering delayed if filters already were running + rotate_emmfilter(i); // rotate active emm filters + } + } + } + + // Restart decoding all caids we have ecmpids but no ecm filters! + if(ecmcounter == 0 && demux[i].ECMpidcount > 0) + { + int32_t started = 0; + + // avoid race: not all pids are asked and checked out yet! + for(g = 0; g < demux[i].ECMpidcount; g++) + { + // check if prio run is done + if(demux[i].ECMpids[g].checked == 0 && demux[i].ECMpids[g].status >= 0) + { + dvbapi_try_next_caid(i, 0, 0); // not done, so start next prio pid + started = 1; + break; + } + } + + if(started) + { + continue; // if started a filter proceed with next demuxer + } + + // all usable pids (with prio) are tried, lets start over again without prio! + if(g == demux[i].ECMpidcount) + { + // avoid race: not all pids are asked and checked out yet! + for(g = 0; g < demux[i].ECMpidcount; g++) + { + // check if noprio run is done + if(demux[i].ECMpids[g].checked == 2 && demux[i].ECMpids[g].status >= 0) + { + demux[i].ECMpids[g].irdeto_curindex = 0xFE; + demux[i].ECMpids[g].irdeto_maxindex = 0; + demux[i].ECMpids[g].irdeto_cycle = 0xFE; + demux[i].ECMpids[g].tries = 0xFE; + demux[i].ECMpids[g].table = 0; + demux[i].ECMpids[g].CHID = 0x10000; // remove chid prio + + dvbapi_try_next_caid(i, 2, 0); // not done, so start next no prio pid + started = 1; + break; + } + } + } + + if(started) + { + continue; // if started a filter proceed with next demuxer + } + + if(g == demux[i].ECMpidcount) // all usable pids are tried, lets start over again! + { + if(demux[i].decodingtries == -1) // first redecoding attempt? + { + cs_ftime(&demux[i].decstart); + + // re-init some used things from second run (without prio) + for(g = 0; g < demux[i].ECMpidcount; g++) + { + demux[i].ECMpids[g].checked = 0; + demux[i].ECMpids[g].irdeto_curindex = 0xFE; + demux[i].ECMpids[g].irdeto_maxindex = 0; + demux[i].ECMpids[g].irdeto_cycle = 0xFE; + demux[i].ECMpids[g].table = 0; + demux[i].decodingtries = 0; + + // remove this pid from channel cache since we had no founds on any ecmpid! + dvbapi_edit_channel_cache(i, g, 0); + } + } + + uint8_t number_of_enabled_pids = 0; + demux[i].decodingtries++; + dvbapi_resort_ecmpids(i); + + for(g = 0; g < demux[i].ECMpidcount; g++) // count number of enabled pids! + { + if(demux[i].ECMpids[g].status >= 0) number_of_enabled_pids++; + } + + if(!number_of_enabled_pids) + { + if(demux[i].decodingtries == 10) + { + demux[i].decodingtries = 0; + cs_log("Demuxer %d no enabled matching ecmpids -> decoding is waiting for matching readers!",i); + } + } + else + { + cs_ftime(&demux[i].decend); + demux[i].decodingtries = -1; // reset to first run again! + gone = comp_timeb(&demux[i].decend, &demux[i].decstart); + + cs_log("Demuxer %d restarting decoding requests after %"PRId64" ms with %d enabled and %d disabled ecmpids!", + i, gone, number_of_enabled_pids, (demux[i].ECMpidcount-number_of_enabled_pids)); + + dvbapi_try_next_caid(i, 0, 0); + } + } + } + } + + rc = 0; + while(!(listenfd == -1 && cfg.dvbapi_pmtmode == 6)) + { + rc = poll(pfd2, pfdcount, 500); + if(rc < 0) // error occured while polling for fd's with fresh data + { + if(errno == EINTR || errno == EAGAIN) // try again in case of interrupt + { + continue; + } + cs_log("ERROR: error on poll of %d fd's (errno=%d %s)", pfdcount, errno, strerror(errno)); + break; + } + else + { + break; + } + } + + if(rc > 0) + { + cs_ftime(&end); // register end time + int64_t timeout = comp_timeb(&end, &start); + if(timeout < 0) + { + cs_log("*** WARNING: BAD TIME AFFECTING WHOLE OSCAM ECM HANDLING ****"); + } + cs_log_dbg(D_TRACE, "New events occurred on %d of %d handlers after %"PRId64" ms inactivity", rc, pfdcount, timeout); + cs_ftime(&start); // register new start time for next poll + } + + for(i = 0; i < pfdcount && rc > 0; i++) + { + if(pfd2[i].revents == 0) { continue; } // skip sockets with no changes + rc--; //event handled! + cs_log_dbg(D_TRACE, "Now handling fd %d that reported event %d", pfd2[i].fd, pfd2[i].revents); + + if(pfd2[i].revents & (POLLHUP | POLLNVAL | POLLERR)) + { + if(type[i] == 1) + { + del_from_assoc_fd(pfd2[i].fd); + + for(j = 0; j < MAX_DEMUX; j++) + { + // if listenfd closes stop all assigned decoding! + if(demux[j].socket_fd == pfd2[i].fd) + { + dvbapi_stop_descrambling(j, 0); + } + } + + int32_t ret = close(pfd2[i].fd); + if(ret < 0 && errno != 9) + { + cs_log("ERROR: Could not close demuxer socket fd (errno=%d %s)", errno, strerror(errno)); + } + + if(pfd2[i].fd == listenfd && cfg.dvbapi_pmtmode == 6) + { + listenfd = -1; + } + cs_log_dbg(D_DVBAPI, "Socket %d reported hard connection close", pfd2[i].fd); + } + else // type = 0 + { + int32_t demux_id = ids[i]; + int32_t n = fdn[i]; + + if(cfg.dvbapi_boxtype != BOXTYPE_SAMYGO) + { + // stop filter since its giving errors and wont return anything good + dvbapi_stop_filternum(demux_id, n, 0); + } + else + { + int32_t ret, pid; + uint8_t filter[32]; + struct dmx_sct_filter_params sFP; + cs_log_dbg(D_DVBAPI, "re-opening connection to demux socket"); + close(demux[demux_id].demux_fd[n].fd); + demux[demux_id].demux_fd[n].fd = -1; + + ret = dvbapi_open_device(0, demux[demux_id].demux_index, demux[demux_id].adapter_index); + if(ret != -1) + { + demux[demux_id].demux_fd[n].fd = ret; + pid = demux[demux_id].curindex; + memset(filter, 0, 32); + memset(&sFP, 0, sizeof(sFP)); + filter[0] = 0x80; + filter[16] = 0xF0; + sFP.pid = demux[demux_id].ECMpids[pid].ECM_PID; + sFP.timeout = 3000; + sFP.flags = DMX_IMMEDIATE_START; + memcpy(sFP.filter.filter, filter, 16); + memcpy(sFP.filter.mask, filter + 16, 16); + ret = dvbapi_ioctl(demux[demux_id].demux_fd[n].fd, DMX_SET_FILTER, &sFP); + } + + if(ret == -1) + { + // stop filter since it's giving errors and wont return anything good + dvbapi_stop_filternum(demux_id, n, 0); + } + } + } + continue; // continue with other events + } + + if(pfd2[i].revents & (POLLIN | POLLPRI)) + { + if(type[i] == 1) + { + if(pfd2[i].fd == listenfd) + { + if(cfg.dvbapi_pmtmode == 6) + { + connfd = listenfd; + disable_pmt_files = 1; + } + else + { + clilen = sizeof(servaddr); + connfd = accept(listenfd, (struct sockaddr *)&servaddr, (socklen_t *)&clilen); + cs_log_dbg(D_DVBAPI, "new socket connection fd: %d", connfd); + + if(dvbapi_listenport_active) + { + // update webif data + client->ip = SIN_GET_ADDR(servaddr); + client->port = ntohs(SIN_GET_PORT(servaddr)); + } + + add_to_assoc_fd(connfd); + + if(cfg.dvbapi_pmtmode == 3 || cfg.dvbapi_pmtmode == 0) + { + disable_pmt_files = 1; + } + + if(connfd <= 0) + { + cs_log_dbg(D_DVBAPI, "accept() returns error on fd event %d (errno=%d %s)", + pfd2[i].revents, errno, strerror(errno)); + } + } + } + else + { + connfd = pfd2[i].fd; + } + + //reading and completing data from socket + if(connfd > 0) + { + if(unhandled_buf_used[i]) + { + memcpy(mbuf, unhandled_buf[i], unhandled_buf_used[i]); + } + + if(!dvbapi_handlesockdata(connfd, mbuf, mbuf_size, unhandled_buf_used[i], &unhandled_buf_used[i], &client_proto_version[i])) + { + unhandled_buf_used[i] = 0; + client_proto_version[i] = 0; // reset protocol, next client could old protocol. + last_client_proto_version = 0; + // client disconnects, stop all assigned decoding + cs_log_dbg(D_DVBAPI, "Socket %d reported connection close", connfd); + int active_conn = 0; // other active connections counter + + for(j = 0; j < MAX_DEMUX; j++) + { + if(demux[j].socket_fd == connfd) + { + dvbapi_stop_descrambling(j, 0); + } + else if(demux[j].socket_fd) + { + active_conn++; + } + } + + // stop polling on this socket + del_from_assoc_fd(connfd); + close(connfd); + + // last connection closed + if(!active_conn && (dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX)) + { + if(dvbapi_listenport_active) + { + // update webif data + client->ip = get_null_ip(); + client->port = 0; + } + } + continue; + } + + if(unhandled_buf_used[i]) + { + if(unhandled_buf_used[i] > unhandled_buf_len[i]) + { + NULLFREE(unhandled_buf[i]); + unhandled_buf_len[i] = unhandled_buf_used[i] < 128 ? 128 : unhandled_buf_used[i]; + if(!cs_malloc(&unhandled_buf[i], sizeof(uint8_t) * unhandled_buf_len[i])) + { + unhandled_buf_len[i] = 0; + unhandled_buf_used[i] = 0; + continue; + } + } + memcpy(unhandled_buf[i], mbuf, unhandled_buf_used[i]); + } + } + } + else // type == 0 + { + int32_t demux_id = ids[i]; + int32_t n = fdn[i]; + + if((int)demux[demux_id].demux_fd[n].fd != pfd2[i].fd) + { + continue; // filter already killed, no need to process this data! + } + + len = dvbapi_read_device(pfd2[i].fd, mbuf, mbuf_size); + if(len < 0) // serious filterdata read error + { + // stop filter since it's giving errors and won't return anything good + dvbapi_stop_filternum(demux_id, n, 0); + + maxfilter--; // lower maxfilters to avoid this with new filter setups! + continue; + } + + if(!len) // receiver internal filter buffer overflow + { + memset(mbuf, 0, mbuf_size); + } + dvbapi_process_input(demux_id, n, mbuf, len, 0); + } + continue; // continue with other events! + } + } + } + + for(j = 0; j < maxpfdsize; j++) + { + NULLFREE(unhandled_buf[j]); + } + free(mbuf); + + return NULL; +} + +void dvbapi_write_cw(int32_t demux_id, int32_t pid, int32_t stream_id, uint8_t *cw, uint8_t cw_length, uint8_t *iv, + uint8_t iv_length, enum ca_descr_algo algo, enum ca_descr_cipher_mode cipher_mode, uint32_t msgid) +{ + int8_t n, cw_empty = 0; + uint8_t null_cw[cw_length]; + ca_descr_t ca_descr; + ca_descr_mode_t ca_descr_mode; + ca_descr_data_t ca_descr_data; + + // determine if soft-mode CSA_ALT should be active on this demux + ca_soft_csa[demux_id] = + ( + (algo == CW_ALGO_CSA_ALT) && + (demux[demux_id].client_proto_version == 3) +#ifdef WITH_EXTENDED_CW + && (cfg.dvbapi_extended_cw_api >= 1) +#endif + ); + + memset(null_cw, 0, cw_length); + memset(&ca_descr, 0, sizeof(ca_descr)); + memset(&ca_descr_mode, 0, sizeof(ca_descr_mode)); + memset(&ca_descr_data, 0, sizeof(ca_descr_data)); + + if(memcmp(demux[demux_id].last_cw[stream_id][0], null_cw, cw_length) == 0 + && memcmp(demux[demux_id].last_cw[stream_id][1], null_cw, cw_length) == 0) + { + cw_empty = 1; // to make sure that both cws get written on constantcw + } + + for(n = 0; n < 2; n++) + { + // Check if cw has changed and if new cw is empty (all zeros) + // Skip check for BISS1 - cw could be indeed zero + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + if((memcmp(cw + (n * cw_length), demux[demux_id].last_cw[stream_id][n], cw_length) != 0 || cw_empty) + && (memcmp(cw + (n * cw_length), null_cw, cw_length) != 0 || caid_is_biss(demux[demux_id].ECMpids[pid].CAID))) + { + // prepare ca device + uint32_t idx = dvbapi_ca_set_pid(demux_id, pid, stream_id, (algo == CA_ALGO_DES), msgid); + if(idx == INDEX_INVALID) + { + return; // return on no index! + } + + // just to make the compiler happy (-Wunused-parameter) + // (better move the coolapi code to a separate function) + ca_descr_mode.cipher_mode = cipher_mode; + ca_descr_data.data = iv; + ca_descr_data.length = iv_length; + +#if defined WITH_COOLAPI || defined WITH_COOLAPI2 + ca_descr.index = idx; + ca_descr.parity = n; + + memcpy(demux[demux_id].last_cw[stream_id][n], cw + (n * 8), 8); + memcpy(ca_descr.cw, cw + (n * 8), 8); + + cs_log_dbg(D_DVBAPI, "Demuxer %d write cw%d index: %d (ca_mask %d)", + demux_id, n, ca_descr.index, demux[demux_id].ca_mask); + + coolapi_write_cw(demux[demux_id].ca_mask, demux[demux_id].STREAMpids, demux[demux_id].STREAMpidcount, &ca_descr); +#else + int32_t i, j, write_cw = 0; + uint32_t usedidx, lastidx; + + char lastcw[2 * cw_length + 1]; + char newcw[2 * cw_length + 1]; +#ifdef WITH_DEBUG + if(cs_dblevel & D_DVBAPI) + { + cs_hexdump(0, demux[demux_id].last_cw[stream_id][n], cw_length, lastcw, sizeof(lastcw)); + cs_hexdump(0, cw + (n * cw_length), cw_length, newcw, sizeof(newcw)); + } +#endif + + for(i = 0; i < CA_MAX; i++) + { + if(!(demux[demux_id].ca_mask & (1 << i))) + { + continue; // ca not in use by this demuxer! + } + lastidx = INDEX_INVALID; + + for(j = 0; j < demux[demux_id].STREAMpidcount; j++) + { + write_cw = 0; + if(!demux[demux_id].ECMpids[pid].streams || ((demux[demux_id].ECMpids[pid].streams & (1 << j)) == (uint) (1 << j))) + { + usedidx = is_ca_used(i, demux[demux_id].STREAMpids[j]); + if(usedidx != INDEX_INVALID) + { + if(idx != usedidx) + { + cs_log_dbg(D_DVBAPI,"Demuxer %d ca%d is using index %d for streampid %04X -> skip!", + demux_id, i, usedidx, demux[demux_id].STREAMpids[j]); + continue; // if not used for descrambling -> skip! + } + else + { + if(usedidx == lastidx) + { + cs_log_dbg(D_DVBAPI,"Demuxer %d ca%d is using index %d for streampid %04X -> skip, %s part of cw already written!", + demux_id, i, usedidx, demux[demux_id].STREAMpids[j], (n == 1 ? "even" : "odd")); + continue; + } + + cs_log_dbg(D_DVBAPI,"Demuxer %d ca%d is using index %d for streampid %04X -> write %s part of cw!", + demux_id, i, usedidx, demux[demux_id].STREAMpids[j], (n == 1 ? "even" : "odd")); + + write_cw = 1; + } + } + } + + if(!write_cw) + { + continue; // no need to write the cw since this ca isnt using it! + } + + lastidx = usedidx; + ca_descr.index = usedidx; + ca_descr.parity = n; + + memcpy(demux[demux_id].last_cw[stream_id][n], cw + (n * cw_length), cw_length); + memcpy(ca_descr.cw, cw + (n * 8), 8); // ca_descr is only used for 8 byte CWs + + cs_log_dbg(D_DVBAPI, "Demuxer %d writing %s part (%s) of controlword, replacing expired (%s)", + demux_id, (n == 1 ? "even" : "odd"), newcw, lastcw); + + cs_log_dbg(D_DVBAPI, "Demuxer %d write cw%d index: %d (ca%d)", demux_id, n, ca_descr.index, i); +#ifdef WITH_EXTENDED_CW + if(cfg.dvbapi_extended_cw_api == 1) // Set descrambler algorithm and mode + { + ca_descr_mode.index = usedidx; + ca_descr_mode.algo = algo; + ca_descr_mode.cipher_mode = cipher_mode; + + if((cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) || ca_soft_csa[demux_id]) + { + dvbapi_net_send(DVBAPI_CA_SET_DESCR_MODE, demux[demux_id].socket_fd, msgid, demux_id, -1 /*unused*/, + (uint8_t *) &ca_descr_mode, NULL, NULL, demux[demux_id].client_proto_version); + } + else + { + if(ca_fd[i] <= 0) + { + ca_fd[i] = dvbapi_open_device(1, i, demux[demux_id].adapter_index); + if(ca_fd[i] <= 0) { continue; } + } + + if(dvbapi_ioctl(ca_fd[i], CA_SET_DESCR_MODE, &ca_descr_mode) < 0) + { + cs_log("ERROR: ioctl(CA_SET_DESCR_MODE): %s", strerror(errno)); + } + } + } + + // Send 16 byte CW and IV for AES128, DVB-CISSA + if(cfg.dvbapi_extended_cw_api == 1 && algo == CA_ALGO_AES128) + { + // First send IV + ca_descr_data.index = usedidx; + ca_descr_data.data_type = CA_DATA_IV; + ca_descr_data.data = iv; + ca_descr_data.length = iv_length; + + if((cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) || ca_soft_csa[demux_id]) + { + dvbapi_net_send(DVBAPI_CA_SET_DESCR_DATA, demux[demux_id].socket_fd, msgid, demux_id, -1 /*unused*/, + (uint8_t *) &ca_descr_data, NULL, NULL, demux[demux_id].client_proto_version); + } + else + { + if(dvbapi_ioctl(ca_fd[i], CA_SET_DESCR_DATA, &ca_descr_data) < 0) + { + cs_log("ERROR: ioctl(CA_SET_DESCR_DATA): %s", strerror(errno)); + } + } + + // Then send CW + ca_descr_data.index = usedidx; + ca_descr_data.data_type = CA_DATA_KEY; + ca_descr_data.data = cw + (n * cw_length); + ca_descr_data.length = cw_length; + ca_descr_data.parity = n; + + if((cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) || ca_soft_csa[demux_id]) + { + dvbapi_net_send(DVBAPI_CA_SET_DESCR_DATA, demux[demux_id].socket_fd, msgid, demux_id, -1 /*unused*/, + (uint8_t *) &ca_descr_data, NULL, NULL, demux[demux_id].client_proto_version); + } + else + { + if(dvbapi_ioctl(ca_fd[i], CA_SET_DESCR_DATA, &ca_descr_data) < 0) + { + cs_log("ERROR: ioctl(CA_SET_DESCR_DATA): %s", strerror(errno)); + } + } + } + else // Send 8 byte CW for DVB-CSA or DES +#endif + { + if ((cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) || ca_soft_csa[demux_id]) + { + dvbapi_net_send(DVBAPI_CA_SET_DESCR, demux[demux_id].socket_fd, msgid, demux_id, -1 /*unused*/, + (uint8_t *) &ca_descr, NULL, NULL, demux[demux_id].client_proto_version); + } + else + { + if(ca_fd[i] <= 0) + { + ca_fd[i] = dvbapi_open_device(1, i, demux[demux_id].adapter_index); + if(ca_fd[i] <= 0) + { + continue; + } + } + + if(dvbapi_ioctl(ca_fd[i], CA_SET_DESCR, &ca_descr) < 0) + { + cs_log("ERROR: ioctl(CA_SET_DESCR): %s", strerror(errno)); + } + } + } + } + } +#endif + } + } + cs_log_dbg(D_DVBAPI, "Using %d of %d total descramblers", ca_descramblers_used, ca_descramblers_total); +} + +void delayer(ECM_REQUEST *er, uint32_t delay) +{ + if(delay <= 0) { return; } + + struct timeb tpe; + cs_ftime(&tpe); + int64_t gone = comp_timeb(&tpe, &er->tps); + + if(gone < delay) + { + cs_log_dbg(D_DVBAPI, "delayer: gone=%"PRId64" ms, cfg=%d ms -> delay=%"PRId64" ms", gone, delay, delay - gone); + cs_sleepms(delay - gone); + } +} + +void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er) +{ + int32_t i, j, k, handled = 0; +#ifdef MODULE_STREAMRELAY + uint8_t null_cw8[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +#endif + for(i = 0; i < MAX_DEMUX; i++) + { + uint32_t nocw_write = 0; // 0 = write cw, 1 = dont write cw to hardware demuxer + + // ignore empty demuxers, skip ecm response for other + // srvid and ecm recponse for different adapter + if(demux[i].program_number == 0 + || demux[i].program_number != er->srvid + || demux[i].adapter_index != er->adapter_index) + { + continue; + } + +#ifdef WITH_STAPI5 + if(strcmp(dev_list[demux[i].dev_index].name, er->dev_name) != 0) + { + continue; // skip request if PTI device doesn't match request + } +#endif + + demux[i].rdr = er->selected_reader; + + for(j = 0; j < demux[i].ECMpidcount; j++) // check for matching ecmpid + { + if((demux[i].ECMpids[j].CAID == er->caid || demux[i].ECMpids[j].CAID == er->ocaid) + && demux[i].ECMpids[j].ECM_PID == er->pid && demux[i].ECMpids[j].PROVID == er->prid + && demux[i].ECMpids[j].VPID == er->vpid) + { + break; + } + } + + if(j == demux[i].ECMpidcount) + { + continue; // ecm response srvid ok but no matching ecmpid, perhaps this for other demuxer + } + + cs_log_dbg(D_DVBAPI, "Demuxer %d %scontrol word received for PID %d CAID %04X PROVID %06X ECMPID %04X CHID %04X VPID %04X", + i, (er->rc >= E_NOTFOUND ? "no " : ""), j, er->caid, er->prid, er->pid, er->chid, er->vpid); + + uint32_t status = dvbapi_check_ecm_delayed_delivery(i, er); + uint32_t comparecw0 = 0, comparecw1 = 0; + char ecmd5[17 * 3]; +#ifdef WITH_DEBUG + if(cs_dblevel & D_DVBAPI) + { + cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); + } +#endif + if(status == 1 && er->rc) // wrong ecmhash + { + cs_log_dbg(D_DVBAPI, "Demuxer %d not interested in response ecmhash %s (requested different one)", i, ecmd5); + continue; + } + + if(status == 2) // no filter + { + cs_log_dbg(D_DVBAPI, "Demuxer %d not interested in response ecmhash %s (filter already killed)", i, ecmd5); + continue; + } + + if(status == 5) // empty cw + { + cs_log_dbg(D_DVBAPI, "Demuxer %d not interested in response ecmhash %s (delivered cw is empty!)", i, ecmd5); + nocw_write = 1; + + if(er->rc < E_NOTFOUND) + { + er->rc = E_NOTFOUND; + } + } + + // 0=matching ecm hash, 2=no filter, 3=table reset, 4=cache-ex response + // Check only against last_cw[0] (index 0) - No need to check the rest + // Skip check for BISS1 - cw could be indeed zero + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + if((status == 0 || status == 3 || status == 4) && er->rc < E_NOTFOUND && !caid_is_biss(er->caid)) + { + // check for matching control word + if(memcmp(er->cw, demux[i].last_cw[0][0], 8) == 0 && + memcmp(er->cw + 8, demux[i].last_cw[0][1], 8) == 0) + { + comparecw0 = 1; + } + else if(memcmp(er->cw, demux[i].last_cw[0][1], 8) == 0 && + memcmp(er->cw + 8, demux[i].last_cw[0][0], 8) == 0) + { + comparecw1 = 1; + } + + if(comparecw0 == 1 || comparecw1 == 1) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d duplicate controlword ecm response hash %s (duplicate controlword!)", i, ecmd5); + nocw_write = 1; + } + } + + if(status == 3) // table reset + { + cs_log_dbg(D_DVBAPI, "Demuxer %d luckyshot new controlword ecm response hash %s (ecm table reset)", i, ecmd5); + } + + if(status == 4) // no check on cache-ex responses! + { + cs_log_dbg(D_DVBAPI, "Demuxer %d new controlword from cache-ex reader (no ecmhash check possible)", i); + } + + handled = 1; // mark this ecm response as handled + if(er->rc < E_NOTFOUND && cfg.dvbapi_requestmode == 0 && (demux[i].pidindex == -1) && er->caid != 0) + { + demux[i].ECMpids[j].tries = 0xFE; // reset timeout retry flag + demux[i].ECMpids[j].irdeto_cycle = 0xFE; // reset irdetocycle + demux[i].pidindex = j; // set current index as *the* pid to descramble + demux[i].ECMpids[j].checked = 4; + + cs_log_dbg(D_DVBAPI, "Demuxer %d descrambling PID %d CAID %04X PROVID %06X ECMPID %04X CHID %02X VPID %04X", + i, demux[i].pidindex, er->caid, er->prid, er->pid, er->chid, er->vpid); + } + + if(er->rc < E_NOTFOUND && cfg.dvbapi_requestmode == 1 && er->caid != 0) // FOUND + { + SAFE_MUTEX_LOCK(&demux[i].answerlock); // only process one ecm answer + if(demux[i].ECMpids[j].checked != 4) + { + int32_t t, o, ecmcounter = 0; + int32_t oldpidindex = demux[i].pidindex; + demux[i].pidindex = j; // set current ecmpid as the new pid to descramble + + if(oldpidindex != -1) + { + for(k = 0; k < MAX_STREAM_INDICES; k++) + { + demux[i].ECMpids[j].index[k] = demux[i].ECMpids[oldpidindex].index[k]; // swap index with lower status pid that was descrambling + demux[i].ECMpids[j].useMultipleIndices = demux[i].ECMpids[oldpidindex].useMultipleIndices; + } + } + + // check this pid with control word FOUND for higher status + for(t = 0; t < demux[i].ECMpidcount; t++) + { + if(t != j && demux[i].ECMpids[j].status >= demux[i].ECMpids[t].status) + { + // check if ecm filter is in use and + // stop all ecm filters of lower status pids + for(o = 0; o < maxfilter; o++) + { + if(demux[i].demux_fd[o].fd > 0 && demux[i].demux_fd[o].type == TYPE_ECM + && demux[i].demux_fd[o].pidindex == t) + { + // ecm filter belongs to lower status pid -> kill! + dvbapi_stop_filternum(i, o, er->msgid); + } + } + dvbapi_edit_channel_cache(i, t, 0); // remove lower status pid from channel cache + demux[i].ECMpids[t].checked = 4; // mark index t as low status + } + } + + for(o = 0; o < maxfilter; o++) + { + if(demux[i].demux_fd[o].type == TYPE_ECM) + { + ecmcounter++; // count all ecm filters + } + } + + demux[i].ECMpids[j].tries = 0xFE; // reset timeout retry flag + demux[i].ECMpids[j].irdeto_cycle = 0xFE; // reset irdeto cycle + + if(ecmcounter == 1) // if total found running ecmfilters is 1 -> we found the "best" pid + { + dvbapi_edit_channel_cache(i, j, 1); + demux[i].ECMpids[j].checked = 4; // mark best pid last ;) + } + cs_log_dbg(D_DVBAPI, "Demuxer %d descrambling PID %d CAID %04X PROVID %06X ECMPID %04X CHID %02X VPID %04X", + i, demux[i].pidindex, er->caid, er->prid, er->pid, er->chid, er->vpid); + } + SAFE_MUTEX_UNLOCK(&demux[i].answerlock); // and release it! + } + + if(er->rc >= E_NOTFOUND) // not found on requestmode 0 + 1 + { + if(er->rc == E_SLEEPING) + { + dvbapi_stop_descrambling(i, er->msgid); + return; + } + + struct s_dvbapi_priority *forceentry = dvbapi_check_prio_match(i, j, 'p'); + if(forceentry && forceentry->force) // forced pid? keep trying the forced ecmpid! + { + // all cas or irdeto cas with forced prio chid + if(!caid_is_irdeto(er->caid) || forceentry->chid < 0x10000) + { + demux[i].ECMpids[j].table = 0; + dvbapi_set_section_filter(i, er, -1); + continue; + } + else // irdeto cas without chid prio forced + { + // init irdeto current index to first one + if(demux[i].ECMpids[j].irdeto_curindex == 0xFE) + { + demux[i].ECMpids[j].irdeto_curindex = 0x00; + } + + // check for last / max chid + if(!(demux[i].ECMpids[j].irdeto_curindex + 1 > demux[i].ECMpids[j].irdeto_maxindex)) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d trying next irdeto chid of FORCED PID %d CAID %04X PROVID %06X ECMPID %04X", + i, j, er->caid, er->prid, er->pid); + + demux[i].ECMpids[j].irdeto_curindex++; // irdeto index one up + demux[i].ECMpids[j].table = 0; + dvbapi_set_section_filter(i, er, -1); + continue; + } + } + } + + // in case of timeout or fatal LB event give + // this pid another try but no more than 1 try + if((er->rc == E_TIMEOUT || (er->rcEx && er->rcEx <= E2_CCCAM_NOCARD)) + && demux[i].ECMpids[j].tries == 0xFE) + { + demux[i].ECMpids[j].tries -= 0x07; + demux[i].ECMpids[j].table = 0; + dvbapi_set_section_filter(i, er, -1); + continue; + } + else // all not found responses exception: first timeout response and first fatal loadbalancer response + { + demux[i].ECMpids[j].CHID = 0x10000; // get rid of this prio chid since it failed! + demux[i].ECMpids[j].tries = 0xFE; // reset timeout retry + } + + if(caid_is_irdeto(er->caid)) + { + // init irdeto current index to first one + if(demux[i].ECMpids[j].irdeto_curindex == 0xFE) + { + demux[i].ECMpids[j].irdeto_curindex = 0x00; + } + + // check for last / max chid + if(!(demux[i].ECMpids[j].irdeto_curindex + 1 > demux[i].ECMpids[j].irdeto_maxindex)) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d trying next irdeto chid of PID %d CAID %04X PROVID %06X ECMPID %04X VPID %04X", + i, j, er->caid, er->prid, er->pid, er->vpid); + + demux[i].ECMpids[j].irdeto_curindex++; // irdeto index one up + demux[i].ECMpids[j].table = 0; + dvbapi_set_section_filter(i, er, -1); + continue; + } + } + + dvbapi_edit_channel_cache(i, j, 0); // remove this pid from channelcache + + if(demux[i].pidindex == j) + { + // current pid delivered a notfound so this pid isn't + // being used to descramble any longer -> clear pidindex + demux[i].pidindex = -1; + } + + demux[i].ECMpids[j].irdeto_maxindex = 0; + demux[i].ECMpids[j].irdeto_curindex = 0xFE; + demux[i].ECMpids[j].tries = 0xFE; // reset timeout retry flag + demux[i].ECMpids[j].irdeto_cycle = 0xFE; // reset irdetocycle + demux[i].ECMpids[j].table = 0; + demux[i].ECMpids[j].checked = 4; // flag ecmpid as checked + demux[i].ECMpids[j].status = -1; // flag ecmpid as unusable + + int32_t found = 1; // setup for first run + int32_t filternum = -1; + + while(found > 0) // disable all ecm + emm filters for this notfound + { + found = 0; + + filternum = dvbapi_get_filternum(i, er, TYPE_ECM); // get ecm filternumber + if(filternum > -1) // in case valid filter found + { + int32_t fd = demux[i].demux_fd[filternum].fd; + if(fd > 0) // in case valid fd + { + dvbapi_stop_filternum(i, filternum, er->msgid); // stop ecmfilter + found = 1; + } + } + + if(caid_is_irdeto(er->caid)) // in case irdeto cas stop old emm filters + { + filternum = dvbapi_get_filternum(i, er, TYPE_EMM); // get emm filternumber + if(filternum > -1) // in case valid filter found + { + int32_t fd = demux[i].demux_fd[filternum].fd; + if(fd > 0) // in case valid fd + { + dvbapi_stop_filternum(i, filternum, er->msgid); // stop emmfilter + found = 1; + } + } + } + } + continue; + } + + // below this should be only run in case of ecm answer is found + uint32_t chid = get_subid(er); // derive current chid in case of irdeto, or a unique part of ecm on other cas systems + demux[i].ECMpids[j].CHID = (chid != 0 ? chid : 0x10000); // if not zero apply, otherwise use no chid value 0x10000 + dvbapi_edit_channel_cache(i, j, 1); // do it here to here after the right CHID is registered + + //dvbapi_set_section_filter(i, er); is not needed anymore (unsure) + demux[i].ECMpids[j].tries = 0xFE; // reset timeout retry flag + demux[i].ECMpids[j].irdeto_cycle = 0xFE; // reset irdeto cycle + + // cw was already written by another filter or current pid + // isn't pid used to descramble so it ends here! + if(nocw_write || demux[i].pidindex != j) + { + continue; + } + + struct s_dvbapi_priority *delayentry = dvbapi_check_prio_match(i, demux[i].pidindex, 'd'); + uint32_t delay = 0; + + if(delayentry) + { + if(delayentry->delay < 1000) + { + delay = delayentry->delay; + cs_log_dbg(D_DVBAPI, "specific delay: write cw %d ms after ecmrequest", delay); + } + } + else if(cfg.dvbapi_delayer > 0) + { + delay = cfg.dvbapi_delayer; + cs_log_dbg(D_DVBAPI, "generic delay: write cw %d ms after ecmrequest", delay); + } + + delayer(er, delay); + +#ifdef MODULE_STREAMRELAY + bool set_dvbapi_cw = true; + if((cfg.stream_relay_ctab.ctnum == 0 || chk_ctab_ex(er->caid, &cfg.stream_relay_ctab)) && cfg.stream_relay_enabled) + { + // streamserver set cw + set_dvbapi_cw = !stream_write_cw(er); + } + if (set_dvbapi_cw) +#endif + switch(selected_api) + { +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + case STAPI: + stapi_write_cw(i, er->cw, demux[i].STREAMpids, demux[i].STREAMpidcount, demux[i].pmt_file); + break; +#endif + default: + { +#ifdef WITH_EXTENDED_CW + if(er->cw_ex.mode != demux[i].ECMpids[j].useMultipleIndices) + { + uint32_t idx; + + for(k = 0; k < demux[i].STREAMpidcount; k++) + { + if(demux[i].ECMpids[j].useMultipleIndices) + { + idx = demux[i].ECMpids[j].index[k]; + } + else + { + idx = demux[i].ECMpids[j].index[0]; + } + dvbapi_set_pid(i, k, idx, false, false, er->msgid); // disable streampid + } + + for(k = 0; k < MAX_STREAM_INDICES; k++) + { + demux[i].ECMpids[j].index[k] = INDEX_INVALID; + } + } + + if(er->cw_ex.mode == CW_MODE_MULTIPLE_CW) + { + int32_t key_pos_a = 0; + demux[i].ECMpids[j].useMultipleIndices = 1; + + for(k = 0; k < demux[i].STREAMpidcount; k++) + { + if(demux[i].STREAMpidsType[k] == STREAM_VIDEO) + { + dvbapi_write_cw(i, j, k, er->cw, 8, NULL, 0, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid); + } + else if(demux[i].STREAMpidsType[k] == STREAM_AUDIO) + { + if(key_pos_a < 4) + { + dvbapi_write_cw(i, j, k, er->cw_ex.audio[key_pos_a], 8, NULL, 0, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid); + key_pos_a++; + } + } + // Every channel that uses the extended cw has unencrypted subtitle streams, + // so disable CW writing to save indices for audio streams and recordings. + //else // Data + //{ + // dvbapi_write_cw(i, j, k, er->cw_ex.data, 8, NULL, 0, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid); + //} + } + } + else + { + demux[i].ECMpids[j].useMultipleIndices = 0; + + if(er->cw_ex.algo == CW_ALGO_AES128) + { + dvbapi_write_cw(i, j, 0, er->cw_ex.session_word, 16, er->cw_ex.data, 16, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid); + } + else if(er->cw_ex.algo == CW_ALGO_CSA) + { + if(select_csa_alt(er)) + { + er->cw_ex.algo = CW_ALGO_CSA_ALT; + } + dvbapi_write_cw(i, j, 0, er->cw, 8, NULL, 0, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid); + } + else + { + dvbapi_write_cw(i, j, 0, er->cw, 8, NULL, 0, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid); + } + } +#else + cfg.dvbapi_extended_cw_api = 0; // in CSA mode extended_cw_api should be always 0 regardless what user selected! + dvbapi_write_cw(i, j, 0, er->cw, 8, NULL, 0, CA_ALGO_DVBCSA, CA_MODE_CBC, er->msgid); +#endif + break; + } + } + +#ifdef MODULE_STREAMRELAY +#ifdef WITH_EXTENDED_CW + if(!(set_dvbapi_cw || er->cw_ex.algo == CA_ALGO_AES128)) +#else + if(!(set_dvbapi_cw)) +#endif + { + if(memcmp(er->cw, null_cw8, 8) != 0) + { + memcpy(demux[i].last_cw[0][0], er->cw, 8); + } + else + { + memcpy(demux[i].last_cw[0][1], er->cw + 8, 8); + } + } +#endif + + // reset idle-Time + client->last = time((time_t *)0); // ********* TO BE FIXED LATER ON ****** + + if((dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) || ca_soft_csa[i]) + { + dvbapi_net_send(DVBAPI_ECM_INFO, demux[i].socket_fd, er->msgid, i, 0, NULL, client, er, demux[i].client_proto_version); + } + + if(cfg.dvbapi_ecminfo_file != 0 && cfg.dvbapi_boxtype != BOXTYPE_SAMYGO) + { +#ifdef WITH_EXTENDED_CW + // Only print CWs for index 0 in ecm.info file + if(er->cw_ex.algo == CA_ALGO_AES128) + { + dvbapi_write_ecminfo_file(client, er, demux[i].last_cw[0][0], demux[i].last_cw[0][1], 16); + } + else + { + dvbapi_write_ecminfo_file(client, er, demux[i].last_cw[0][0], demux[i].last_cw[0][1], 8); + } +#else + dvbapi_write_ecminfo_file(client, er, demux[i].last_cw[0][0], demux[i].last_cw[0][1], 8); +#endif + } + } + + if(handled == 0) + { + cs_log_dbg(D_DVBAPI, "Unhandled ECM response received for CAID %04X PROVID %06X ECMPID %04X CHID %04X VPID %04X", + er->caid, er->prid, er->pid, er->chid, er->vpid); + } +} + +static int8_t isValidCW(uint8_t *cw) +{ + uint8_t i; + for(i = 0; i < 16; i += 4) + { + if(((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff) != cw[i + 3]) + { + return 0; + } + } + return 1; +} + +void dvbapi_write_ecminfo_file(struct s_client *client, ECM_REQUEST *er, uint8_t *lastcw0, uint8_t *lastcw1, uint8_t cw_length) +{ +#define ECMINFO_TYPE_OSCAM 0 +#define ECMINFO_TYPE_OSCAM_MS 1 +#define ECMINFO_TYPE_WICARDD 2 +#define ECMINFO_TYPE_MGCAMD 3 +#define ECMINFO_TYPE_CCCAM 4 +#define ECMINFO_TYPE_CAMD3 5 +#define ECMINFO_TYPE_GBOX 6 + + FILE *ecmtxt = fopen(ECMINFO_FILE, "w"); + if(ecmtxt != NULL && er->rc < E_NOTFOUND) + { + char tmp[49]; // holds 16 byte cw - (2 hex digits + 1 space) * 16 byte + string termination) + const char *reader_name = NULL, *from_name = NULL, *proto_name = NULL, *from_device= NULL ; + int8_t hops = 0; + int32_t from_port = 0; + char system_name[64]; + const char *const_system_name = get_cardsystem_desc_by_caid(er->caid); + + cs_strncpy(system_name, const_system_name, sizeof(system_name)); + system_name[0] = (char)toupper((int)system_name[0]); + + if(cfg.dvbapi_ecminfo_type <= ECMINFO_TYPE_WICARDD) + { + if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_WICARDD) + { + fprintf(ecmtxt, "system: %s\n", system_name); + } + + fprintf(ecmtxt, "caid: 0x%04X\npid: 0x%04X\n", er->caid, er->pid); + + if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_WICARDD) + { + fprintf(ecmtxt, "prov: %06X\n", (uint) er->prid); + } + else + { + fprintf(ecmtxt, "prov: 0x%06X\n", (uint) er->prid); + } + + fprintf(ecmtxt, "chid: 0x%04X\n", er->chid); + } + else if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_MGCAMD) + { + fprintf(ecmtxt, "===== %s ECM on CaID 0x%04X, pid 0x%04X =====\nprov: %06X\n", + system_name, er->caid, er->pid, (uint) er->prid); + } + else if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_CCCAM) + { + char provider_name[128]; + get_providername(er->prid, er->caid, provider_name, sizeof(provider_name)); + + if(provider_name[0]) + { + fprintf(ecmtxt, "system: %s\ncaid: 0x%04X\nprovider: %s\nprovid: 0x%06X\npid: 0x%04X\n", + system_name, er->caid, provider_name, (uint) er->prid, er->pid); + } + else + { + fprintf(ecmtxt, "system: %s\ncaid: 0x%04X\nprovid: 0x%06X\npid: 0x%04X\n", + system_name, er->caid, (uint) er->prid, er->pid); + } + } + else if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_CAMD3) + { + fprintf(ecmtxt, "CAID 0x%04X, PID 0x%04X, PROVIDER 0x%06X\n", + er->caid, er->pid, (uint) er->prid); + } +#ifdef MODULE_GBOX + else if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_GBOX) + { + fprintf(ecmtxt, "===== %s ECM on CaID 0x%04X, pid 0x%04X, sid 0x%04X =====\nprov: %04X, slot: %d, level: %d, dist: %d\nprovider: %06X\n", + system_name, er->caid, er->pid, er->srvid, er->selected_reader->gbox_cw_src_peer, er->selected_reader->gbox_crd_slot_lev >> 4, + er->selected_reader->gbox_crd_slot_lev & 0xf, er->selected_reader->currenthops, (uint) er->prid); + } +#endif + + switch(er->rc) + { + case E_FOUND: + if(er->selected_reader) + { + reader_name = er->selected_reader->label; + if(is_network_reader(er->selected_reader)) + { + from_name = er->selected_reader->device; + from_port = er->selected_reader->r_port; + } + else + { + from_name = "local"; + from_device = er->selected_reader->device; + } + proto_name = reader_get_type_desc(er->selected_reader, 1); + hops = er->selected_reader->currenthops; + } + else + { + reader_name = "none"; + from_name = "local"; + proto_name = "none"; + } + break; + + case E_CACHE1: + reader_name = "Cache"; + from_name = "cache1"; + proto_name = "none"; + break; + + case E_CACHE2: + reader_name = "Cache"; + from_name = "cache2"; + proto_name = "none"; + break; + + case E_CACHEEX: + reader_name = "Cache"; + from_name = "cache3"; + proto_name = "none"; + break; + } + + if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_GBOX) + { + switch(er->rc) + { + case E_FOUND: + if(er->selected_reader) + { + if(is_network_reader(er->selected_reader)) + { + fprintf(ecmtxt, "reader: %s\nfrom: %s:%d\nprotocol: %s\n", + reader_name, from_name, from_port, proto_name); + } + else + { + fprintf(ecmtxt, "reader: %s\nfrom: %s - %s\nprotocol: %s\n", + reader_name, from_name, from_device, proto_name); + } + } + break; + + case E_CACHE1: + case E_CACHE2: + case E_CACHEEX: + fprintf(ecmtxt, "reader: %s\nfrom: %s:%d\nprotocol: %s\n", + reader_name, from_name, from_port, proto_name); + break; + } + fprintf(ecmtxt, "ecm time: %.3f\n", (float) client->cwlastresptime / 1000); + } + + if(cfg.dvbapi_ecminfo_type <= ECMINFO_TYPE_OSCAM_MS) + { + switch(er->rc) + { + case E_FOUND: + if(er->selected_reader) + { + if(is_network_reader(er->selected_reader)) + { + fprintf(ecmtxt, "reader: %s\nfrom: %s:%d\nprotocol: %s\nhops: %d\n", + reader_name, from_name, from_port, proto_name, hops); + } + else + { + fprintf(ecmtxt, "reader: %s\nfrom: %s - %s\nprotocol: %s\nhops: %d\n", + reader_name, from_name, from_device, proto_name, hops); + } + } + break; + + case E_CACHE1: + case E_CACHE2: + case E_CACHEEX: + fprintf(ecmtxt, "reader: %s\nfrom: %s:%d\nprotocol: %s\n", + reader_name, from_name, from_port, proto_name); + break; + } + + if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_OSCAM) + { + fprintf(ecmtxt, "ecm time: %.3f\n", (float) client->cwlastresptime / 1000); + } + else + { + fprintf(ecmtxt, "ecm time: %d\n", client->cwlastresptime); + } + } + + if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_CAMD3) + { + fprintf(ecmtxt, "FROM: %s\n", reader_name); + fprintf(ecmtxt, "CW0: %s\n", cs_hexdump(1, lastcw0, cw_length, tmp, sizeof(tmp))); + fprintf(ecmtxt, "CW1: %s\n", cs_hexdump(1, lastcw1, cw_length, tmp, sizeof(tmp))); + } + else + { + fprintf(ecmtxt, "cw0: %s\n", cs_hexdump(1, lastcw0, cw_length, tmp, sizeof(tmp))); + fprintf(ecmtxt, "cw1: %s\n", cs_hexdump(1, lastcw1, cw_length, tmp, sizeof(tmp))); + } + + if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_WICARDD || cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_MGCAMD) + { + time_t walltime; + struct tm lt; + char timebuf[32]; + + if(cw_length == 8) // only check checksum for 8 byte CWs + { + fprintf(ecmtxt, "Signature %s\n", (isValidCW(lastcw0) || isValidCW(lastcw1)) ? "OK" : "NOK"); + } + else + { + fprintf(ecmtxt, "Signature %s\n", "OK"); + } + + if(reader_name != NULL) + { + fprintf(ecmtxt, "source: %s (%s at %s:%d)\n", reader_name, proto_name, from_name, from_port); + } + + walltime = cs_time(); + localtime_r(&walltime, <); + + if(strftime(timebuf, 32, "%a %b %d %H:%M:%S %Y", <) != 0) + { + fprintf(ecmtxt, "%d msec -- %s\n", client->cwlastresptime, timebuf); + } + } + + if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_CCCAM) + { + if(reader_name != NULL) + { + fprintf(ecmtxt, "using: %s\naddress: %s:%d\nhops: %d\n", + proto_name, from_name, from_port, hops); + } + fprintf(ecmtxt, "ecm time: %d\n", client->cwlastresptime); + } + } + + if(ecmtxt) + { + int32_t ret = fclose(ecmtxt); + if(ret < 0) + { + cs_log("ERROR: Could not close ecmtxt fd (errno=%d %s)", errno, strerror(errno)); + } + ecmtxt = NULL; + } +} + + +void *dvbapi_start_handler(struct s_client *cl, uint8_t *UNUSED(mbuf), int32_t module_idx, void *(*_main_func)(void *)) +{ + // cs_log("dvbapi loaded fd=%d", idx); + if(cfg.dvbapi_enabled == 1) + { + cl = create_client(get_null_ip()); + cl->module_idx = module_idx; + cl->typ = 'c'; + + int32_t ret = start_thread("dvbapi handler", _main_func, (void *)cl, &cl->thread, 1, 0); + if(ret) + { + return NULL; + } + } + return NULL; +} + +void *dvbapi_handler(struct s_client *cl, uint8_t *mbuf, int32_t module_idx) +{ + return dvbapi_start_handler(cl, mbuf, module_idx, dvbapi_main_local); +} + +int32_t dvbapi_set_section_filter(int32_t demux_id, ECM_REQUEST *er, int32_t n) +{ + if(!er) { return -1; } + + if(USE_OPENXCAS || (selected_api != DVBAPI_3 && selected_api != DVBAPI_1 && selected_api != STAPI) // only valid for dvbapi3, dvbapi1 and STAPI + || (cfg.dvbapi_boxtype == BOXTYPE_IPBOX || cfg.dvbapi_boxtype == BOXTYPE_IPBOX_PMT)) // reported buggy using sectionfiltering after 1~4 hours -> for now disabled! + { + return 0; + } + + if(n == -1) + { + n = dvbapi_get_filternum(demux_id, er, TYPE_ECM); + } + + if(n < 0) // in case no valid filter found; + { + return -1; + } + + int32_t fd = demux[demux_id].demux_fd[n].fd; + if(fd < 1) // in case no valid fd + { + return -1; + } + + uint8_t filter[16]; + uint8_t mask[16]; + memset(filter, 0, 16); + memset(mask, 0, 16); + struct s_ecmpid *curpid = NULL; + + int32_t pid = demux[demux_id].demux_fd[n].pidindex; + if(pid == -1) + { + return -1; + } + curpid = &demux[demux_id].ECMpids[pid]; + + if(curpid->table != er->ecm[0] && curpid->table != 0) + { + return -1; // if current ecmtype differs from latest requested ecmtype do not apply section filtering! + } + + uint8_t ecmfilter = 0; + + if(er->ecm[0] == 0x80) + { + ecmfilter = 0x81; // current processed ecm is even, next will be filtered for odd + } + else + { + ecmfilter = 0x80; // current processed ecm is odd, next will be filtered for even + } + + if(curpid->table != 0) // cycle ecmtype from odd to even or even to odd + { + filter[0] = ecmfilter; // only accept new ecms (if previous odd, filter for even and vice versa) + mask[0] = 0xFF; + cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d set ecmtable to %s (CAID %04X PROVID %06X FD %d)", + demux_id, n + 1, (ecmfilter == 0x80 ? "EVEN" : "ODD"), curpid->CAID, curpid->PROVID, fd); + } + else // not decoding right now so we are interessted in all ecm types! + { + filter[0] = 0x80; // set filter to wait for any ecms + mask[0] = 0xF0; + cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d set ecmtable to ODD+EVEN (CAID %04X PROVID %06X FD %d)", + demux_id, n + 1, curpid->CAID, curpid->PROVID, fd); + } + + uint32_t offset = 0, extramask = 0xFF; + struct s_dvbapi_priority *forceentry = dvbapi_check_prio_match(demux_id, pid, 'p'); + //cs_log("**** curpid->CHID %04X, checked = %d, er->chid = %04X *****", curpid->CHID, curpid->checked, er->chid); + // checked 4 to make sure we dont set chid filter and no such ecm in dvbstream except for forced pids! + + if(curpid->CHID < 0x10000 && (curpid->checked == 4 || (forceentry && forceentry->force))) + { + switch(er->caid >> 8) + { + case 0x01: // seca + offset = 7; + extramask = 0xF0; + break; + + case 0x05: // viaccess + offset = 8; + break; + + case 0x06: // irdeto + offset = 6; + break; + + case 0x09: // videoguard + offset = 11; + break; + + case 0x4A: // DRE-Crypt, Bulcrypt, Tongang and others? + if(!caid_is_bulcrypt(er->caid)) + { + offset = 6; + } + break; + } + } + + int32_t irdetomatch = 1; // check if wanted irdeto index is the one the delivers current chid! + if(caid_is_irdeto(curpid->CAID)) + { + if(curpid->irdeto_curindex == er->ecm[4]) { irdetomatch = 1; } // ok apply chid filtering + else { irdetomatch = 0; } // skip chid filtering but apply irdeto index filtering + } + + if(offset && irdetomatch) // we have a cas with chid or unique part in checked ecm + { + i2b_buf(2, curpid->CHID, filter + (offset - 2)); + mask[(offset - 2)] = 0xFF & extramask; // additional mask seca2 chid can be FC10 or FD10 varies each month so only apply F?10 + mask[(offset - 1)] = 0xFF; + cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d set chid to %04X on fd %d", demux_id, n + 1, curpid->CHID, fd); + } + else + { + // on irdeto we can always apply irdeto index filtering! + if(caid_is_irdeto(curpid->CAID) && (curpid->irdeto_curindex < 0xFE)) + { + filter[2] = curpid->irdeto_curindex; + mask[2] = 0xFF; + cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d set irdetoindex to %d on fd %d", + demux_id, n + 1, curpid->irdeto_curindex, fd); + } + else // all other cas systems also cas systems without chid or unique ecm part + { + cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d set chid to ANY CHID on fd %d", demux_id, n + 1, fd); + } + } + + int32_t ret = dvbapi_activate_section_filter(demux_id, n, fd, curpid->ECM_PID, filter, mask, er->msgid); + if(ret < 0) // something went wrong setting filter! + { + cs_log("Demuxer %d Filter %d (fd %d) error setting section filtering -> stop filter!", demux_id, n + 1, fd); + + ret = dvbapi_stop_filternum(demux_id, n, er->msgid); + if(ret == -1) + { + cs_log("Demuxer %d Filter %d (fd %d) stopping filter failed -> kill all filters of this demuxer!", demux_id, n + 1, fd); + dvbapi_stop_filter(demux_id, TYPE_EMM, er->msgid); + dvbapi_stop_filter(demux_id, TYPE_ECM, er->msgid); + } + return -1; + } + return n; +} + +int32_t dvbapi_activate_section_filter(int32_t demux_id, int32_t num, int32_t fd, int32_t pid, uint8_t *filter, uint8_t *mask, uint32_t msgid) +{ + int32_t ret = -1; + + switch(selected_api) + { + case DVBAPI_3: + { + struct dmx_sct_filter_params sFP2; + memset(&sFP2, 0, sizeof(sFP2)); + sFP2.pid = pid; + sFP2.timeout = 0; + sFP2.flags = DMX_IMMEDIATE_START; + + if(cfg.dvbapi_boxtype == BOXTYPE_NEUMO) + { + //DeepThought: on dgs/cubestation and neumo images, perhaps others + //the following code is needed to descramble + sFP2.filter.filter[0] = filter[0]; + sFP2.filter.mask[0] = mask[0]; + sFP2.filter.filter[1] = 0; + sFP2.filter.mask[1] = 0; + sFP2.filter.filter[2] = 0; + sFP2.filter.mask[2] = 0; + memcpy(sFP2.filter.filter + 3, filter + 1, 16 - 3); + memcpy(sFP2.filter.mask + 3, mask + 1, 16 - 3); + + //DeepThought: in the drivers of the dgs/cubestation and neumo images, + //dvbapi 1 and 3 are somehow mixed. In the kernel drivers, the DMX_SET_FILTER + //ioctl expects to receive a dmx_sct_filter_params structure (DVBAPI 3) but + //due to a bug its sets the "positive mask" wrongly (they should be all 0). + //On the other hand, the DMX_SET_FILTER1 ioctl also uses the dmx_sct_filter_params + //structure, which is incorrect (it should be dmxSctFilterParams). + //The only way to get it right is to call DMX_SET_FILTER1 with the argument + //expected by DMX_SET_FILTER. Otherwise, the timeout parameter is not passed correctly. + + ret = dvbapi_ioctl(fd, DMX_SET_FILTER1, &sFP2); + } + else + { + memcpy(sFP2.filter.filter, filter, 16); + memcpy(sFP2.filter.mask, mask, 16); + + if(dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) + { + ret = dvbapi_net_send(DVBAPI_DMX_SET_FILTER, + demux[demux_id].socket_fd, + msgid, + demux_id, + num, + (uint8_t *) &sFP2, + NULL, + NULL, + demux[demux_id].client_proto_version); + } + else + { + ret = dvbapi_ioctl(fd, DMX_SET_FILTER, &sFP2); + } + } + break; + } + + case DVBAPI_1: + { + struct dmxSctFilterParams sFP1; + memset(&sFP1, 0, sizeof(sFP1)); + sFP1.pid = pid; + sFP1.timeout = 0; + sFP1.flags = DMX_IMMEDIATE_START; + memcpy(sFP1.filter.filter, filter, 16); + memcpy(sFP1.filter.mask, mask, 16); + ret = dvbapi_ioctl(fd, DMX_SET_FILTER1, &sFP1); + break; + } + +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + case STAPI: + { + ret = stapi_activate_section_filter(fd, filter, mask); + break; + } +#endif + // Isn't implemented in COOLAPI-1 (legacy) +#if defined WITH_COOLAPI2 + case COOLAPI: + { + int32_t n = coolapi_get_filter_num(fd); + if(n < 0) + { + return n; + } + coolapi_set_filter(fd, n, pid, filter, mask, TYPE_ECM); + break; + } +#endif + + default: + break; + } + + if(ret != -1) // only change filter/mask for comparing if box returned no errors! + { + // copy filter and mask to check later on if receiver delivered accordingly + memcpy(demux[demux_id].demux_fd[num].filter, filter, 16); + memcpy(demux[demux_id].demux_fd[num].mask, mask, 16); + } + return ret; +} + +int32_t dvbapi_check_ecm_delayed_delivery(int32_t demux_id, ECM_REQUEST *er) +{ + int32_t ret = 0; + int32_t filternum = dvbapi_get_filternum(demux_id, er, TYPE_ECM); + char nullcw[CS_ECMSTORESIZE]; + memset(nullcw, 0, CS_ECMSTORESIZE); + + if(filternum < 0) // if no matching filter act like ecm response is delayed + { + return 2; + } + + if(memcmp(demux[demux_id].demux_fd[filternum].lastecmd5, nullcw, CS_ECMSTORESIZE)) + { + demux[demux_id].demux_fd[filternum].lastresult = er->rc; // save last result + char ecmd5[17 * 3]; +#ifdef WITH_DEBUG + if(cs_dblevel & D_DVBAPI) + { + cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); + } +#endif + cs_log_dbg(D_DVBAPI, "Demuxer %d requested controlword for ecm %s on fd %d", + demux_id, ecmd5, demux[demux_id].demux_fd[filternum].fd); + + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + MD5(er->ecm, er->ecmlen, md5tmp); + + // 1 = no response on the ecm we request last for this fd! + ret = (memcmp(demux[demux_id].demux_fd[filternum].lastecmd5, md5tmp, CS_ECMSTORESIZE) != 0 ? 1 : 0); + } + + // Check for null cw + // Skip check for BISS1 - cw could be indeed zero + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + if(memcmp(er->cw, nullcw, 8) == 0 && memcmp(er->cw + 8, nullcw, 8) == 0 && !caid_is_biss(er->caid)) + { + return 5; + } + + struct s_ecmpid *curpid = NULL; + int32_t pid = demux[demux_id].demux_fd[filternum].pidindex; + + if(pid !=-1) + { + curpid = &demux[demux_id].ECMpids[pid]; + if(curpid->table == 0) // on change table act like ecm response is found + { + return 3; + } + } + + if(er->rc == E_CACHEEX) // on cache-ex response act like ecm response is found + { + return 4; + } + + return ret; +} + +int32_t dvbapi_get_filternum(int32_t demux_id, ECM_REQUEST *er, int32_t type) +{ + if(!er) { return -1; } + + int32_t n; + int32_t fd = -1; + + for(n = 0; n < maxfilter; n++) // determine fd + { + // check for valid and right type (ecm or emm) + if(demux[demux_id].demux_fd[n].fd > 0 && demux[demux_id].demux_fd[n].type == type) + { + if(type == TYPE_ECM && er->srvid != demux[demux_id].program_number) + { + continue; + } + + if((demux[demux_id].demux_fd[n].pid == er->pid) && ((demux[demux_id].demux_fd[n].provid == er->prid) + || demux[demux_id].demux_fd[n].provid == 0 || er->prid == 0) && ((demux[demux_id].demux_fd[n].caid == er->caid) + || (demux[demux_id].demux_fd[n].caid == er->ocaid))) // current ecm pid? + { + fd = demux[demux_id].demux_fd[n].fd; // found! + if(demux[demux_id].demux_fd[n].caid == er->ocaid) + { + // clear ecmd5 hash since betatunneled ecms hash different! + memset(demux[demux_id].demux_fd[n].lastecmd5, 0, CS_ECMSTORESIZE); + } + break; + } + } + } + + if(fd > 0 && demux[demux_id].demux_fd[n].provid == 0) + { + demux[demux_id].demux_fd[n].provid = er->prid; // hack to fill in provid into demuxer + } + return (fd > 0 ? n : fd); // return -1(fd) on not found, on found return filternumber(n) +} + +uint32_t dvbapi_ca_set_pid(int32_t demux_id, int32_t pid, int32_t stream_id, bool use_des, uint32_t msgid) +{ + uint32_t idx; + int32_t n; + + if(pid == -1 || pid > demux[demux_id].ECMpidcount) + { + return INDEX_INVALID; + } + + if(demux[demux_id].ECMpids[pid].useMultipleIndices) + { + n = stream_id; + idx = demux[demux_id].ECMpids[pid].index[n]; + + if(idx == INDEX_INVALID) // if we have no index for this pid, get one! + { + idx = dvbapi_get_desc_index(demux_id, pid, n); + if(idx == INDEX_INVALID) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d PID: %d CAID: %04X ECMPID: %04X has no free index", + demux_id, pid, demux[demux_id].ECMpids[pid].CAID, demux[demux_id].ECMpids[pid].ECM_PID); + + return INDEX_INVALID; + } + + cs_log_dbg(D_DVBAPI, "Demuxer %d PID: %d CAID: %04X ECMPID: %04X is using index %d for stream %d", + demux_id, pid, demux[demux_id].ECMpids[pid].CAID, demux[demux_id].ECMpids[pid].ECM_PID, idx, n); + } + + if(!demux[demux_id].ECMpids[pid].streams || ((demux[demux_id].ECMpids[pid].streams & (1 << n)) == (uint) (1 << n))) + { + dvbapi_set_pid(demux_id, n, idx, true, use_des, msgid); // enable stream pid + } + else + { + dvbapi_set_pid(demux_id, n, idx, false, false, msgid); // disable stream pid + } + } + else + { + idx = demux[demux_id].ECMpids[pid].index[0]; + + if(idx == INDEX_INVALID) // if we have no index for this pid, get one! + { + idx = dvbapi_get_desc_index(demux_id, pid, 0); + if(idx == INDEX_INVALID) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d PID: %d CAID: %04X ECMPID: %04X has no free index", + demux_id, pid, demux[demux_id].ECMpids[pid].CAID, demux[demux_id].ECMpids[pid].ECM_PID); + + return INDEX_INVALID; + } + + cs_log_dbg(D_DVBAPI, "Demuxer %d PID: %d CAID: %04X ECMPID: %04X is using index %d", + demux_id, pid, demux[demux_id].ECMpids[pid].CAID, demux[demux_id].ECMpids[pid].ECM_PID, idx); + } + + for(n = 0; n < demux[demux_id].STREAMpidcount; n++) + { + if(!demux[demux_id].ECMpids[pid].streams || ((demux[demux_id].ECMpids[pid].streams & (1 << n)) == (uint) (1 << n))) + { + dvbapi_set_pid(demux_id, n, idx, true, use_des, 0); // enable stream pid + } + else + { + dvbapi_set_pid(demux_id, n, idx, false, false, 0); // disable stream pid + } + } + } + return idx; // return ca index +} + +int8_t update_streampid_list(uint8_t cadevice, uint16_t pid, uint32_t idx, bool use_des) +{ + struct s_streampid *listitem, *newlistitem; + LL_ITER itr; + if(!ll_activestreampids) + { + ll_activestreampids = ll_create("ll_activestreampids"); + } + + if(idx >= INDEX_MAX) + { + return INVALID_STREAMPID_INDEX; + } + + if(ll_count(ll_activestreampids) > 0) + { + itr = ll_iter_create(ll_activestreampids); + while((listitem = ll_iter_next(&itr))) + { + if(cadevice == listitem->cadevice && pid == listitem->streampid) + { + if((listitem->activeindexers & (1 << idx)) == (uint64_t) (1 << idx)) + { +#ifdef WITH_EXTENDED_CW + if(cfg.dvbapi_extended_cw_api == 2 && use_des != listitem->use_des) + { + listitem->use_des = use_des; + return FIRST_STREAMPID_INDEX; + } +#endif + return FOUND_STREAMPID_INDEX; // match found + } + else + { + listitem->activeindexers |= (1 << idx); // ca + pid found but not this index -> add this index + cs_log_dbg(D_DVBAPI, "Added existing streampid %04X with new index %d to ca%d", pid, idx, cadevice); +#ifdef WITH_EXTENDED_CW + if(cfg.dvbapi_extended_cw_api == 2 && use_des != listitem->use_des) + { + listitem->use_des = use_des; + return FIRST_STREAMPID_INDEX; + } +#endif + return ADDED_STREAMPID_INDEX; + } + } + } + } + + if(!cs_malloc(&newlistitem, sizeof(struct s_streampid))) + { + return FIRST_STREAMPID_INDEX; // not sure if this is correct + } + + newlistitem->cadevice = cadevice; + newlistitem->streampid = pid; + newlistitem->activeindexers = (1 << idx); + newlistitem->caindex = idx; // set this index as used to decode on ca device + newlistitem->use_des = use_des; + + ll_append(ll_activestreampids, newlistitem); + cs_log_dbg(D_DVBAPI, "Added new streampid %04X with index %d to ca%d", pid, idx, cadevice); + + return FIRST_STREAMPID_INDEX; +} + +int8_t remove_streampid_from_list(uint8_t cadevice, uint16_t pid, uint32_t idx) +{ + struct s_streampid *listitem; + int8_t removed = 0; + LL_ITER itr; + + if(!ll_activestreampids) + { + return NO_STREAMPID_LISTED; + } + + if(idx >= INDEX_MAX) + { + return INVALID_STREAMPID_INDEX; + } + + if(ll_count(ll_activestreampids) > 0) + { + itr = ll_iter_create(ll_activestreampids); + while((listitem = ll_iter_next(&itr))) + { + if(cadevice == listitem->cadevice && pid == listitem->streampid) + { + if(idx == INDEX_DISABLE_ALL) + { + listitem->activeindexers = 0; + removed = 1; + } + else if((listitem->activeindexers & (1 << idx)) == (uint64_t) (1 << idx)) + { + listitem->activeindexers &= ~(1 << idx); // flag it as disabled for this index + removed = 1; + } + + if(removed) + { + cs_log_dbg(D_DVBAPI, "Remove streampid %04X using indexer %d from ca%d", pid, idx, cadevice); + } + + if(listitem->activeindexers == 0 && removed == 1) // all indexers disabled? -> remove pid from list! + { + ll_iter_remove_data(&itr); + cs_log_dbg(D_DVBAPI, "Removed last indexer of streampid %04X from ca%d", pid, cadevice); + ca_descramblers_used = count_active_indexers(); + return REMOVED_STREAMPID_LASTINDEX; + } + else if(removed == 1) + { + if(idx != INDEX_DISABLE_ALL && idx != listitem->caindex) + { + return REMOVED_STREAMPID_INDEX; + } + else + { + listitem->caindex = INDEX_INVALID; + cs_log_dbg(D_DVBAPI, "Streampid %04X index %d was used for decoding on ca%d", pid, idx, cadevice); + return REMOVED_DECODING_STREAMPID_INDEX; + } + } + return INVALID_STREAMPID_INDEX; + } + } + } + return NO_STREAMPID_LISTED; +} + +void disable_unused_streampids(int16_t demux_id) +{ + int32_t ecmpid = demux[demux_id].pidindex; + + if(ecmpid == -1 // no active ecmpid! + || !ll_activestreampids + || selected_api == STAPI // stapi handles pids itself! + || ll_count(ll_activestreampids) == 0) // no items in list? + { + return; + } + + int32_t j; + if(demux[demux_id].ECMpids[ecmpid].useMultipleIndices == 0) + { + uint32_t idx = demux[demux_id].ECMpids[ecmpid].index[0]; + int32_t i, n; + struct s_streampid *listitem; + + // search for old enabled streampids on + // all ca devices that have to be disabled + for(i = 0; i < CA_MAX && idx != INDEX_INVALID; i++) + { + if(!((demux[demux_id].ca_mask & (1 << i)) == (uint32_t) (1 << i))) + { + continue; // ca is not used by this demuxer + } + + LL_ITER itr; + itr = ll_iter_create(ll_activestreampids); + + while((listitem = ll_iter_next(&itr))) + { + if(i != listitem->cadevice) + { + continue; // ca doesn't match + } + + if(!((listitem->activeindexers & (1 << (idx))) == (uint64_t) (1 << (idx)))) + { + continue; // index doesn't match + } + + for(n = 0; n < demux[demux_id].STREAMpidcount; n++) + { + if(demux[demux_id].ECMpidcount == 0) // FTA? -> disable stream! + { + n = demux[demux_id].STREAMpidcount; + break; + } + + // check if pid matches with current stream pid on demuxer + if(listitem->streampid == demux[demux_id].STREAMpids[n]) + { + break; + } + } + + if(n == demux[demux_id].STREAMpidcount) // no match found + { + demux[demux_id].STREAMpids[n] = listitem->streampid; // put it here temporarily! + dvbapi_set_pid(demux_id, n, idx, false, false, 0); // disable this unused streampid + demux[demux_id].STREAMpids[n] = 0; // remove it from temp position! + } + } + + // ECMpidcount != 0 -> skip enabling on fta + for(n = 0; n < demux[demux_id].STREAMpidcount && demux[demux_id].ECMpidcount != 0; n++) + { + ll_iter_reset(&itr); + if(!demux[demux_id].ECMpids[ecmpid].streams || ((demux[demux_id].ECMpids[ecmpid].streams & (1 << n)) == (uint) (1 << n))) + { + while((listitem = ll_iter_next(&itr))) + { + if(i != listitem->cadevice) // ca doesn't match + { + continue; + } + + if(!((listitem->activeindexers & (1 << (idx))) == (uint64_t) (1 << (idx)))) // index doesn't match + { + continue; + } + + // check if pid matches with current streampid on demuxer + if(listitem->streampid == demux[demux_id].STREAMpids[n]) + { + break; + } + } + + if(!listitem) // if streampid not listed -> enable it! + { + dvbapi_set_pid(demux_id, n, idx, true, false, 0); // enable streampid + } + } + } + } + } + else + { + uint32_t idx = INDEX_INVALID; + int32_t i, n; + uint8_t skip; + struct s_streampid *listitem; + + // search for old enabled streampids + // on all ca devices that have to be disabled + for(i = 0; i < CA_MAX && idx != INDEX_INVALID; i++) + { + if(!((demux[demux_id].ca_mask & (1 << i)) == (uint32_t) (1 << i))) + { + continue; // continue if ca is unused by this demuxer + } + + LL_ITER itr; + itr = ll_iter_create(ll_activestreampids); + + while((listitem = ll_iter_next(&itr))) + { + if(i != listitem->cadevice) + { + continue; // ca doesn't match + } + + for(skip = 1, j = 0; j < MAX_STREAM_INDICES; j++) + { + idx = demux[demux_id].ECMpids[ecmpid].index[j]; + if(idx == INDEX_INVALID) + { + continue; + } + + // index match + if((listitem->activeindexers & (1 << (idx))) == (uint64_t) (1 << (idx))) + { + skip = 0; + break; + } + } + + if(skip) + { + continue; + } + + for(n = 0; n < demux[demux_id].STREAMpidcount; n++) + { + if(demux[demux_id].ECMpidcount == 0) // FTA? -> disable stream! + { + n = demux[demux_id].STREAMpidcount; + break; + } + + // check if pid matches with current streampid on demuxer + if(listitem->streampid == demux[demux_id].STREAMpids[n]) + { + break; + } + } + + if(n == demux[demux_id].STREAMpidcount) + { + demux[demux_id].STREAMpids[n] = listitem->streampid; // put it temp here! + dvbapi_set_pid(demux_id, n, idx, false, false, 0); // no match found so disable this now unused streampid + demux[demux_id].STREAMpids[n] = 0; // remove temp! + } + } + + // ECMpidcount != 0 -> skip enabling on fta + for(n = 0; n < demux[demux_id].STREAMpidcount && demux[demux_id].ECMpidcount != 0; n++) + { + ll_iter_reset(&itr); + if(!demux[demux_id].ECMpids[ecmpid].streams || ((demux[demux_id].ECMpids[ecmpid].streams & (1 << n)) == (uint) (1 << n))) + { + while((listitem = ll_iter_next(&itr))) + { + if(i != listitem->cadevice) // ca doesn't match + { + continue; + } + + for(skip = 1, j = 0; j < MAX_STREAM_INDICES; j++) + { + idx = demux[demux_id].ECMpids[ecmpid].index[j]; + if(idx == INDEX_INVALID) + { + continue; + } + + if((listitem->activeindexers & (1 << (idx))) == (uint64_t) (1 << (idx))) + { + skip = 0; // index match + break; + } + } + + if(skip) + { + continue; + } + + // check if pid matches with current streampid on demuxer + if(listitem->streampid == demux[demux_id].STREAMpids[n]) + { + break; + } + } + + if(!listitem) // if streampid not listed -> enable it! + { + dvbapi_set_pid(demux_id, n, idx, true, false, 0); // enable streampid + } + } + } + } + } +} + +uint32_t is_ca_used(uint8_t cadevice, int32_t pid) +{ + struct s_streampid *listitem; + LL_ITER itr; + if(!ll_activestreampids) + { + return INDEX_INVALID; + } + + if(ll_count(ll_activestreampids) > 0) + { + itr = ll_iter_create(ll_activestreampids); + while((listitem = ll_iter_next(&itr))) + { + // if pid is 0, we match ca device only + if(listitem->cadevice != cadevice || (pid && listitem->streampid != pid)) + { + continue; + } + + uint32_t i = 0; + while(listitem->caindex == INDEX_INVALID && i < INDEX_MAX) + { + if((listitem->activeindexers & (1 << i)) == (uint64_t) (1 << i)) + { + listitem->caindex = i; // set fresh one + cs_log_dbg(D_DVBAPI, "Streampid %04X is now using index %d for decoding on ca%d", pid, i, cadevice); + break; + } + i++; + } + + if(listitem->caindex == INDEX_INVALID) + { + ll_iter_remove_data(&itr); + return INDEX_INVALID; + } + return listitem->caindex; + } + } + return INDEX_INVALID; // no indexer found for this pid! +} + +uint32_t count_active_indexers(void) +{ + uint i, usecounter = 0; + struct s_streampid *listitem; + LL_ITER itr; + + if(!ll_activestreampids) + { + return 0; + } + + bool indexer_in_use[ca_descramblers_total]; + memset(&indexer_in_use, 0, sizeof(indexer_in_use)); + + if(ll_count(ll_activestreampids) > 0) + { + itr = ll_iter_create(ll_activestreampids); + while((listitem = ll_iter_next(&itr))) + { + if(listitem->caindex != INDEX_INVALID && listitem->caindex < INDEX_MAX) + { + indexer_in_use[listitem->caindex] = true; + } + } + + for(i = 0; i < ca_descramblers_total; i++) + { + if(indexer_in_use[i] == true) + { + usecounter++; + } + } + } + + return usecounter; +} + +uint16_t dvbapi_get_client_proto_version(void) +{ + return last_client_proto_version; +} + +const char *dvbapi_get_client_name(void) +{ + return last_client_name ? last_client_name : ""; +} + +void check_add_emmpid(int32_t demux_id, uint8_t *filter, int32_t l, int32_t emmtype) +{ + if(l < 0) { return; } + + uint32_t typtext_idx = 0; + int32_t ret = -1; + const char *typtext[] = { "UNIQUE", "SHARED", "GLOBAL", "UNKNOWN" }; + + while(((emmtype >> typtext_idx) & 0x01) == 0 && typtext_idx < sizeof(typtext) / sizeof(const char *)) + { + ++typtext_idx; + } + + // filter already in list? + if(is_emmfilter_in_list(filter, demux[demux_id].EMMpids[l].PID, demux[demux_id].EMMpids[l].PROVID, demux[demux_id].EMMpids[l].CAID)) + { + cs_log_dbg(D_DVBAPI, "Demuxer %d duplicate emm filter type %s, emmpid: 0x%04X, emmcaid: %04X, emmprovid: %06X -> SKIPPED!", + demux_id, + typtext[typtext_idx], + demux[demux_id].EMMpids[l].PID, + demux[demux_id].EMMpids[l].CAID, + demux[demux_id].EMMpids[l].PROVID); + return; + } + + if(demux[demux_id].emm_filter < demux[demux_id].max_emm_filter) // can this filter be started? + { + // try to activate this emmfilter + ret = dvbapi_set_filter(demux_id, + selected_api, + demux[demux_id].EMMpids[l].PID, + demux[demux_id].EMMpids[l].CAID, + demux[demux_id].EMMpids[l].PROVID, + filter, + filter + 16, + 0, + demux[demux_id].pidindex, + TYPE_EMM, 1); + } + + if(ret != -1) // -1 if maxfilter reached or filter start error! + { + if(demux[demux_id].emm_filter == -1) // -1: first run of emm filtering on this demuxer + { + demux[demux_id].emm_filter = 0; + } + demux[demux_id].emm_filter++; // increase total active filters + + cs_log_dump_dbg(D_DVBAPI, filter, 32, "Demuxer %d started emm filter type %s, pid: 0x%04X", + demux_id, typtext[typtext_idx], demux[demux_id].EMMpids[l].PID); + return; + } + else // not set successful, so add it to the list for try again later on! + { + add_emmfilter_to_list(demux_id, filter, demux[demux_id].EMMpids[l].CAID, demux[demux_id].EMMpids[l].PROVID, demux[demux_id].EMMpids[l].PID, 0, false); + cs_log_dump_dbg(D_DVBAPI, filter, 32, "Demuxer %d added inactive emm filter type %s, pid: 0x%04X", + demux_id, typtext[typtext_idx], demux[demux_id].EMMpids[l].PID); + } + return; +} + +void rotate_emmfilter(int32_t demux_id) +{ + // emm filter iteration + if(!ll_emm_active_filter) + { + ll_emm_active_filter = ll_create("ll_emm_active_filter"); + } + + if(!ll_emm_inactive_filter) + { + ll_emm_inactive_filter = ll_create("ll_emm_inactive_filter"); + } + + if(!ll_emm_pending_filter) + { + ll_emm_pending_filter = ll_create("ll_emm_pending_filter"); + } + + uint32_t filter_count = ll_count(ll_emm_active_filter) + ll_count(ll_emm_inactive_filter); + if(demux[demux_id].max_emm_filter > 0 && ll_count(ll_emm_inactive_filter) > 0 && filter_count > demux[demux_id].max_emm_filter) + { + int32_t filter_queue = ll_count(ll_emm_inactive_filter); + int32_t stopped = 0, started = 0; + struct timeb now; + cs_ftime(&now); + struct s_emm_filter *filter_item; + LL_ITER itr; + itr = ll_iter_create(ll_emm_active_filter); + + while((filter_item = ll_iter_next(&itr)) != NULL) + { + if(!ll_count(ll_emm_inactive_filter) || started == filter_queue) + { + break; + } + + int64_t gone = comp_timeb(&now, &filter_item->time_started); + if(gone > 45 * 1000) + { + struct s_dvbapi_priority *forceentry = dvbapi_check_prio_match_emmpid(filter_item->demux_id, filter_item->caid, filter_item->provid, 'p'); + if(!forceentry || (forceentry && !forceentry->force)) + { + // stop active filter and add to pending list + dvbapi_stop_filternum(filter_item->demux_id, filter_item->num - 1, 0); + ll_iter_remove_data(&itr); + add_emmfilter_to_list(filter_item->demux_id, filter_item->filter, filter_item->caid, filter_item->provid, filter_item->pid, -1, false); + stopped++; + } + } + + int32_t ret; + if(stopped > started) // we have room for new filters, try to start an inactive emmfilter! + { + struct s_emm_filter *filter_item2; + LL_ITER itr2 = ll_iter_create(ll_emm_inactive_filter); + while((filter_item2 = ll_iter_next(&itr2))) + { + ret = dvbapi_set_filter(filter_item2->demux_id, + selected_api, + filter_item2->pid, + filter_item2->caid, + filter_item2->provid, + filter_item2->filter, + filter_item2->filter + 16, + 0, + demux[filter_item2->demux_id].pidindex, + TYPE_EMM, 1); + if(ret != -1) + { + ll_iter_remove_data(&itr2); + started++; + break; + } + } + } + } + + itr = ll_iter_create(ll_emm_pending_filter); + while((filter_item = ll_iter_next(&itr)) != NULL) // move pending filters to inactive + { + add_emmfilter_to_list(filter_item->demux_id, filter_item->filter, filter_item->caid, filter_item->provid, filter_item->pid, 0, false); + ll_iter_remove_data(&itr); + } + } +} + +int32_t filtermatch(uint8_t *buffer, int32_t filter_num, int32_t demux_id, int32_t len) +{ + int32_t i, k, match; + uint8_t flt, mask; + + match = 1; + for(i = 0, k = 0; i < 16 && match; i++, k++) + { + mask = demux[demux_id].demux_fd[filter_num].mask[i]; + if(k == 1) // skip len bytes + { + k += 2; + } + + if(!mask) + { + continue; + } + + flt = (demux[demux_id].demux_fd[filter_num].filter[i]&mask); + cs_log_dbg(D_DVBAPI,"Demuxer %d filter%d[%d] = %02X, filter mask[%d] = %02X, flt&mask = %02X , buffer[%d] = %02X, buffer[%d] & mask = %02X", + demux_id, filter_num + 1, i, demux[demux_id].demux_fd[filter_num].filter[i], i, mask, flt&mask, k, buffer[k], k, buffer[k] & mask); + + if(k <= len) + { + match = (flt == (buffer[k] & mask)); + } + else + { + match = 0; + } + } + return (match && i == 16); // 0 = delivered data does not match with filter, 1 = delivered data matches with filter +} + +/* + * protocol structure + */ +void module_dvbapi(struct s_module *ph) +{ + ph->desc = "dvbapi"; + ph->type = MOD_CONN_SERIAL; + ph->listenertype = LIS_DVBAPI; +#if defined(WITH_AZBOX) + ph->s_handler = azbox_handler; + ph->send_dcw = azbox_send_dcw; +#elif defined(WITH_MCA) + ph->s_handler = mca_handler; + ph->send_dcw = mca_send_dcw; + selected_box = selected_api = 0; // HACK: This fixes incorrect warning about out of bounds array access in functionas that are not even called when WITH_MCA is defined +#else + ph->s_handler = dvbapi_handler; + ph->send_dcw = dvbapi_send_dcw; +#endif +} +#endif // HAVE_DVBAPI diff --git a/module-dvbapi.h b/module-dvbapi.h new file mode 100644 index 0000000..f35daf2 --- /dev/null +++ b/module-dvbapi.h @@ -0,0 +1,552 @@ +#ifndef MODULE_DVBAPI_H_ +#define MODULE_DVBAPI_H_ + +#ifdef HAVE_DVBAPI +#include + +#define TYPE_ECM 1 +#define TYPE_EMM 2 +#define TYPE_SDT 3 +#define TYPE_PAT 4 +#define TYPE_PMT 5 +#define TYPE_CAT 6 + +// api +#define DVBAPI_3 0 +#define DVBAPI_1 1 +#define STAPI 2 +#define COOLAPI 3 + +#ifdef __CYGWIN__ +#define TMPDIR "./" +#define STANDBY_FILE "./.pauseoscam" +#define ECMINFO_FILE "./ecm.info" +#else +#define TMPDIR "/tmp/" +#define STANDBY_FILE "/tmp/.pauseoscam" +#define ECMINFO_FILE "/tmp/ecm.info" +#endif + +#define BOX_COUNT 7 + +#define BOXTYPE_DREAMBOX 1 +#define BOXTYPE_DUCKBOX 2 +#define BOXTYPE_UFS910 3 +#define BOXTYPE_DBOX2 4 +#define BOXTYPE_IPBOX 5 +#define BOXTYPE_IPBOX_PMT 6 +#define BOXTYPE_DM7000 7 +#define BOXTYPE_QBOXHD 8 +#define BOXTYPE_COOLSTREAM 9 +#define BOXTYPE_NEUMO 10 +#define BOXTYPE_PC 11 +#define BOXTYPE_PC_NODMX 12 +#define BOXTYPE_SAMYGO 13 +#define BOXTYPES 13 +#define DMXMD5HASHSIZE 16 // use MD5() + +// we store the results of remove_streampid_from_list() +// and update_streampid_list() in one variable, so make sure +// the return values do not collide + +// remove_streampid_from_list() +#define NO_STREAMPID_LISTED 0x00 +#define REMOVED_STREAMPID_INDEX 0x01 +#define REMOVED_STREAMPID_LASTINDEX 0x02 +#define REMOVED_DECODING_STREAMPID_INDEX 0x03 + +// update_streampid_list(): +#define FOUND_STREAMPID_INDEX 0x10 +#define ADDED_STREAMPID_INDEX 0x11 +#define FIRST_STREAMPID_INDEX 0x12 + +// remove_streampid_from_list() and update_streampid_list() +#define INVALID_STREAMPID_INDEX 0x20 + +#define INDEX_DISABLE_ALL 0xEFFFFFFD // used for remove_streampid_from_list(), dvbapi_set_pid() +#define INDEX_INVALID 0xEFFFFFFF + +#define DUMMY_FD 0xFFFF + +//----------------------------------------------------------------------------- +// constants used in socket communication +//----------------------------------------------------------------------------- + +#define DVBAPI_PROTOCOL_VERSION 3 +#define DVBAPI_MAX_PACKET_SIZE 262 // maximum possible packet size + +#define DVBAPI_CA_GET_DESCR_INFO 0x80086F83 +#define DVBAPI_CA_SET_DESCR 0x40106F86 +#define DVBAPI_CA_SET_PID 0x40086F87 +#define DVBAPI_CA_SET_DESCR_MODE 0x400C6F88 +#define DVBAPI_CA_SET_DESCR_DATA 0x40186F89 +//#define DVBAPI_DMX_START 0x00006F29 // in case we ever need this +#define DVBAPI_DMX_STOP 0x00006F2A +#define DVBAPI_DMX_SET_FILTER 0x403C6F2B + +#define DVBAPI_AOT_CA 0x9F803000 +#define DVBAPI_AOT_CA_PMT 0x9F803200 // least significant byte is length (ignored) +#define DVBAPI_AOT_CA_STOP 0x9F803F04 +#define DVBAPI_FILTER_DATA 0xFFFF0000 +#define DVBAPI_CLIENT_INFO 0xFFFF0001 +#define DVBAPI_SERVER_INFO 0xFFFF0002 +#define DVBAPI_ECM_INFO 0xFFFF0003 + +#define DVBAPI_INDEX_DISABLE 0xFFFFFFFF // only used for ca_pid_t + +//----------------------------------------------------------------------------- +// CA PMT defined values according to EN 50221 +// https://www.dvb.org/resources/public/standards/En50221.V1.pdf +// https://www.dvb.org/resources/public/standards/R206-001.V1.pdf +//----------------------------------------------------------------------------- + +// ca_pmt_list_management: This parameter is used to indicate whether the user has selected a single program or several +// programs. The following values can be used: + +#define CA_PMT_LIST_MORE 0x00 // The CA PMT object is neither the first one, nor the last one of the list. + +#define CA_PMT_LIST_FIRST 0x01 // The CA PMT object is the first one of a new list of more than one CA PMT object. + // All previously selected programs are being replaced by the programs of the new list. + +#define CA_PMT_LIST_LAST 0x02 // The CA PMT object is the last of the list. + +#define CA_PMT_LIST_ONLY 0x03 // The list is made of a single CA PMT object. + +#define CA_PMT_LIST_ADD 0x04 // The CA PMT has to be added to an existing list, that is, a new program has been seleced + // by the user, but all previously selected programs remain selected. + +#define CA_PMT_LIST_UPDATE 0x05 // The CA PMT of a program already in the list is sent again because the version_number or + // the current_next_indicator has changed. + +// ca_pmt_cmd_id: This parameter indicates what response is required from the application to a CA PMT object. It can +// take the following values: + +#define CA_PMT_CMD_OK_DESCRAMBLING 0x01 // The host does not expect answer to the CA PMT and the application can start + // descrambling the program or start an MMI dialogue immediately. + +#define CA_PMT_CMD_OK_MMI 0x02 // The application can start an MMI dialogue, but shall not start descrambling + // before reception of a new CA PMT object with "ca_pmt_cmd_id" set to + // "ok_descrambling". In this case the host shall quarantee that an MMI session + // can be opened by the CA application. + +#define CA_PMT_CMD_QUERY 0x03 // The host expects to receive a CA PMT reply. In this case, the applicaiton is + // not allowed to start descrambling or MMI dialogue before reception of a new + // CA PMT object with "ca_pmt_cmd_id" set to "ok_descrambling or "ok_mmi". + +#define CA_PMT_CMD_NOT_SELECTED 0x04 // It indicates to the CA application that the host no longer requires that CA + // application to attempt to descramble the service. The CA application shall + // close any MMI dialogue it has opened. +//---------------- +// ca descriptors +//---------------- + +#define CA 0x09 +#define ENIGMA_NAMESPACE 0x81 +#define DEMUX_CA_MASK_ADAPTER 0x82 // deprecated - applications should use descriptors ADAPTER_DEVICE, DEMUX_DEVICE and CA_DEVICE instead +#define ADAPTER_DEVICE 0x83 +#define PMT_PID 0x84 +#define SERVICE_TYPE_MASK 0x85 // not used by OSCam +#define DEMUX_DEVICE 0x86 +#define CA_DEVICE 0x87 + +//----------------------------------------------------------------------------- +// api used for internal device communication +//----------------------------------------------------------------------------- + +#define DMX_FILTER_SIZE 16 + +// The following is part of the linux dvb api (v1), +// but modifed to overcome some bugs in specific devices + +typedef struct dmxFilter +{ + uint8_t filter[DMX_FILTER_SIZE]; + uint8_t mask[DMX_FILTER_SIZE]; +} dmxFilter_t; + +struct dmxSctFilterParams +{ + uint16_t pid; + dmxFilter_t filter; + uint32_t timeout; + uint32_t flags; + +#define DMX_CHECK_CRC 1 +#define DMX_ONESHOT 2 +#define DMX_IMMEDIATE_START 4 +}; + +#define DMX_START1 _IOW('o', 41, int) +#define DMX_STOP1 _IOW('o', 42, int) +#define DMX_SET_FILTER1 _IOW('o', 43, struct dmxSctFilterParams *) + +// The following is part of the linux dvb api +// https://www.kernel.org/doc/html/latest/media/uapi/dvb/demux.html +// https://github.com/torvalds/linux/blob/master/include/uapi/linux/dvb/dmx.h + +typedef struct dmx_filter +{ + uint8_t filter[DMX_FILTER_SIZE]; + uint8_t mask[DMX_FILTER_SIZE]; + uint8_t mode[DMX_FILTER_SIZE]; +} dmx_filter_t; + +struct dmx_sct_filter_params +{ + uint16_t pid; + dmx_filter_t filter; + uint32_t timeout; + uint32_t flags; + +#define DMX_CHECK_CRC 1 +#define DMX_ONESHOT 2 +#define DMX_IMMEDIATE_START 4 +}; + +#define DMX_START _IO('o', 41) +#define DMX_STOP _IO('o', 42) +#define DMX_SET_FILTER _IOW('o', 43, struct dmx_sct_filter_params) + +// The following is part of the linux dvb api +// https://www.kernel.org/doc/html/latest/media/uapi/dvb/ca.html +// https://github.com/torvalds/linux/blob/master/include/uapi/linux/dvb/ca.h + +typedef struct ca_descr_info +{ + uint32_t num; + uint32_t type; /* bitmask: 1 == ECD, 2 == NDS, 4 == DDS */ +} ca_descr_info_t; + +typedef struct ca_descr +{ + uint32_t index; + uint32_t parity; /* 0 == even, 1 == odd */ + uint8_t cw[8]; +} ca_descr_t; + +// ca_pid has been removed from the api, but we still use it +typedef struct ca_pid +{ + uint32_t pid; + int32_t index; /* -1 == disable */ +} ca_pid_t; + +enum ca_descr_algo +{ + CA_ALGO_DVBCSA, + CA_ALGO_DES, + CA_ALGO_AES128, +}; + +enum ca_descr_cipher_mode +{ + CA_MODE_ECB, + CA_MODE_CBC, +}; + +// Structs "ca_descr_mode" and "ca_descr_data" and respective ioctl +// commands are part of a custom api + +/* +* struct ca_descr_mode - Used to select a crypto algorithm and mode +* for a key slot. +* +* @index: Key slot allocated for a PID or service. +* See CA_SET_PID and struct ca_pid. +* @algo: Algorithm to select for @index. +* @cipher_mode: Cipher mode to use with @algo. +*/ + +typedef struct ca_descr_mode +{ + uint32_t index; + enum ca_descr_algo algo; + enum ca_descr_cipher_mode cipher_mode; +} ca_descr_mode_t; + +/* +* struct ca_descr_data - Used to write Keys and IVs to a descrambler. +* +* @index: Key slot allocated for a PID or service. +* See CA_SET_PID and struct ca_pid. +* @parity: Indicates even or odd parity for control words. +* @data_type: Key or IV. +* @length: Size of @data array; depends on selected algorithm and +* key or block size. +* @data: Pointer to variable @length key or initialization vector data. +*/ + +enum ca_descr_data_type +{ + CA_DATA_IV, + CA_DATA_KEY, +}; + +enum ca_descr_parity +{ + CA_PARITY_EVEN, + CA_PARITY_ODD, +}; + +typedef struct ca_descr_data +{ + uint32_t index; + enum ca_descr_parity parity; + enum ca_descr_data_type data_type; + uint32_t length; + uint8_t *data; +} ca_descr_data_t; + +#define CA_GET_DESCR_INFO _IOR('o', 131, ca_descr_info_t) +#define CA_SET_DESCR _IOW('o', 134, ca_descr_t) +#define CA_SET_PID _IOW('o', 135, ca_pid_t) +#define CA_SET_DESCR_MODE _IOW('o', 136, ca_descr_mode_t) +#define CA_SET_DESCR_DATA _IOW('o', 137, ca_descr_data_t) + +//----------------------------------------------------------------------------- +// OSCam defined structures +//----------------------------------------------------------------------------- + +struct box_devices +{ + char *path; + char *ca_device; + char *demux_device; + char *cam_socket_path; + int8_t api; +}; + +typedef struct filter_s +{ + uint32_t fd; // filter handle + int32_t pidindex; + int32_t pid; + uint16_t caid; + uint32_t provid; + uint16_t type; + int32_t count; + uint8_t filter[16]; + uint8_t mask[16]; + uint8_t lastecmd5[CS_ECMSTORESIZE]; // last requested ecm md5 + int32_t lastresult; + uint8_t prevecmd5[CS_ECMSTORESIZE]; // previous requested ecm md5 + int32_t prevresult; +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + int32_t NumSlots; + uint32_t SlotHandle[10]; + uint32_t BufferHandle[10]; +#endif +#ifdef WITH_EMU + uint32_t cadata; +#endif +} FILTERTYPE; + +#ifdef WITH_EXTENDED_CW +#define MAX_STREAM_INDICES 32 // In practice, 5 is the maximum ever used +#else +#define MAX_STREAM_INDICES 1 +#endif + +#define CA_MAX 32 // Max ca devices supported by oscam - limited by sizeof(ca_mask) of struct demux_s (32 bits) +#define INDEX_MAX 64 // Max descramblers per ca device - limited by sizeof(activeindexers) of struct s_streampid (64 bits) +#define INDEX_MAX_LOCAL 16 // Max total descramblers to use for enigma2 and other STBs when dvbapi_get_descrambler_info() fails +#define INDEX_MAX_NET 64 // Max total descramblers to use for PC (VDR, Tvheadend, etc) + +typedef struct s_ecmpid +{ + uint16_t CAID; + uint32_t PROVID; // provider + uint16_t ECM_PID; + uint32_t CHID; + uint16_t EMM_PID; + uint32_t VPID; // video pid + uint8_t irdeto_maxindex; // max irdeto indices always fresh fetched from current ecm + uint8_t irdeto_curindex; // current irdeto index we want to handle + uint8_t irdeto_cycle; // temp var that holds the irdeto index we started with to detect if we cycled trough all indices + int8_t checked; + int8_t status; + uint8_t tries; + uint8_t table; + int8_t useMultipleIndices; // whether or not to use multiple indices for this ecm pid + uint32_t index[MAX_STREAM_INDICES]; // ca indices used for this ecm pid (index[0] holds ca index for STREAMmpids[0] and so on) + uint32_t streams; // bit mask of STREAMpids enabled for this ECMpid + uint32_t cadata; +#ifdef WITH_EMU + int16_t pvu_counter; +#endif +} ECMPIDTYPE; + +typedef struct s_emmpid +{ + uint16_t CAID; + uint32_t PROVID; + uint16_t PID; + uint8_t type; + uint32_t cadata; +} EMMPIDTYPE; + +enum stream_type +{ + STREAM_UNDEFINED, + STREAM_VIDEO, + STREAM_AUDIO, + STREAM_SUBTITLE +}; + +#define MAX_DEMUX 32 // Max number of demuxes supported by OSCam - each channel/service occupies one demux +#define MAX_ECM_PIDS 24 // Max number of ECM pids per demux +#define MAX_EMM_PIDS 24 // Max number of EMM pids per demux +#define MAX_STREAM_PIDS 32 // Max number of pids other than ECM and EMM (e.g. audio, video, subtitle, etc) per demux (hardware descramblers might have a capacity of 30 pids) +#define MAX_FILTER 64 +#define MAX_ASSOC_FD MAX_DEMUX + +#define PTINUM 10 +#define SLOTNUM 20 + +typedef struct demux_s +{ + int8_t demux_index; // ID of the (hardware) demux device carrying this program - we get this via CA PMT + int8_t adapter_index; // ID of the adapter device carrying this program - we get this via CA PMT + uint32_t ca_mask; // Bit mask of ca devices used for descrambling this program - we get this via CA PMT + int32_t socket_fd; // Connection identifier through which we received the CA PMT object + uint16_t client_proto_version; + FILTERTYPE demux_fd[MAX_FILTER]; + int8_t ECMpidcount; // Count of ECM pids in this program + ECMPIDTYPE ECMpids[MAX_ECM_PIDS]; + int8_t EMMpidcount; // Count of EMM pids in this program + EMMPIDTYPE EMMpids[MAX_EMM_PIDS]; + struct timeb emmstart; // last time emm cat was started + uint16_t max_emm_filter; + int8_t STREAMpidcount; + uint16_t STREAMpids[MAX_STREAM_PIDS]; + enum stream_type STREAMpidsType[MAX_STREAM_PIDS]; // type (audio, video, subtitle, etc) of the corresponding stream pid + int16_t pidindex; // ECMpid used for descrambling - holds index of the ECMpids[] array + int16_t curindex; + int8_t max_status; + uint16_t program_number; // also called service id (srvid) + uint16_t onid; // original network id + uint16_t tsid; // transport stream id + uint16_t pmtpid; // PMT pid for the program_number + uint32_t ens; // enigma namespace + uint8_t last_cw[MAX_STREAM_INDICES][2][16]; // even/odd pairs of 16 byte CWs used for descrambling on the last crypto period + int8_t emm_filter; + int8_t sdt_filter; + uint8_t hexserial[8]; + struct s_reader *rdr; + char pmt_file[30]; + time_t pmt_time; + bool stop_descrambling; // Program is marked to stop descrambling (not selected in the new CA PMT list) + bool running; // Descrambling is currently running for this program + uint8_t old_ecmfiltercount; // previous ecm filter count + uint8_t old_emmfiltercount; // previous emm filter count + pthread_mutex_t answerlock; // request mode 1 avoid race +#ifdef WITH_STAPI + uint32_t DescramblerHandle[PTINUM]; + int32_t desc_pidcount; + uint32_t slot_assc[PTINUM][SLOTNUM]; +#endif +#ifdef WITH_STAPI5 + uint32_t dev_index; +#endif + int8_t decodingtries; // -1 = first run + struct timeb decstart; + struct timeb decend; + uint32_t msgid; +} DEMUXTYPE; + +typedef struct s_streampid +{ + uint16_t streampid; // pid of this stream + uint8_t cadevice; // CA device used for descramlbing + uint32_t caindex; // index (slot) of the CA device used + uint64_t activeindexers; // bitmask indexers if streampid enabled for index, bit is set + bool use_des; // whether to use DES for descrambling this streampid +} STREAMPIDTYPE; + +struct s_dvbapi_priority +{ + char type; // can be 'p', 'i', 'm', 'd', 's', 'l', 'j', 'a' or 'x' + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint32_t chid; + uint16_t ecmpid; + uint32_t cadata; + uint16_t mapcaid; + uint32_t mapprovid; + uint16_t mapecmpid; + int16_t delay; + int8_t force; + int8_t pidx; +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + char devname[30]; + char pmtfile[30]; + int8_t disablefilter; +#endif + struct s_dvbapi_priority *next; +}; + +//----------------------------------------------------------------------------- +// function declarations +//----------------------------------------------------------------------------- + +void dvbapi_stop_descrambling(int32_t demux_id, uint32_t msgid); +void dvbapi_stop_all_descrambling(uint32_t msgid); +void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer, int32_t len, uint32_t msgid); +int32_t dvbapi_open_device(int32_t, int32_t, int); +int32_t dvbapi_stop_filternum(int32_t demux_id, int32_t num, uint32_t msgid); +int32_t dvbapi_stop_filter(int32_t demux_id, int32_t type, uint32_t msgid); +struct s_dvbapi_priority *dvbapi_check_prio_match(int32_t demux_id, int32_t pidindex, char type); +void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er); +void dvbapi_write_cw(int32_t demux_id, int32_t pid, int32_t stream_id, uint8_t *cw, uint8_t cw_length, uint8_t *iv, uint8_t iv_length, enum ca_descr_algo algo, enum ca_descr_cipher_mode cipher_mode, uint32_t msgid); +int32_t dvbapi_parse_capmt(const uint8_t *buffer, uint32_t length, int32_t connfd, char *pmtfile, uint16_t client_proto_version, uint32_t msgid); +void request_cw(struct s_client *client, ECM_REQUEST *er, int32_t demux_id, uint8_t delayed_ecm_check); +void dvbapi_try_next_caid(int32_t demux_id, int8_t checked, uint32_t msgid); +void dvbapi_read_priority(void); +int32_t dvbapi_set_section_filter(int32_t demux_id, ECM_REQUEST *er, int32_t n); +int32_t dvbapi_activate_section_filter(int32_t demux_id, int32_t num, int32_t fd, int32_t pid, uint8_t *filter, uint8_t *mask, uint32_t msgid); +int32_t dvbapi_check_ecm_delayed_delivery(int32_t demux_id, ECM_REQUEST *er); +int32_t dvbapi_get_filternum(int32_t demux_id, ECM_REQUEST *er, int32_t type); +uint32_t dvbapi_ca_set_pid(int32_t demux_id, int32_t pid, int32_t stream_id, bool use_des, uint32_t msgid); +void dvbapi_set_pid(int32_t demux_id, int32_t num, uint32_t idx, bool enable, bool use_des, uint32_t msgid); +int8_t update_streampid_list(uint8_t cadevice, uint16_t pid, uint32_t idx, bool use_des); +int8_t remove_streampid_from_list(uint8_t cadevice, uint16_t pid, uint32_t idx); +void disable_unused_streampids(int16_t demux_id); +uint32_t is_ca_used(uint8_t cadevice, int32_t pid); +uint32_t count_active_indexers(void); +uint16_t dvbapi_get_client_proto_version(void); +const char *dvbapi_get_client_name(void); +void rotate_emmfilter(int32_t demux_id); +int32_t filtermatch(uint8_t *buffer, int32_t filter_num, int32_t demux_id, int32_t len); +void delayer(ECM_REQUEST *er, uint32_t delay); +void check_add_emmpid(int32_t demux_id, uint8_t *filter, int32_t l, int32_t emmtype); +void *dvbapi_start_handler(struct s_client *cl, uint8_t *mbuf, int32_t module_idx, void *(*_main_func)(void *)); +uint32_t dvbapi_get_desc_index(int32_t demux_id, int32_t pid, int32_t stream_id); +void dvbapi_write_ecminfo_file(struct s_client *client, ECM_REQUEST *er, uint8_t *lastcw0, uint8_t *lastcw1, uint8_t cw_length); + +#if defined(WITH_AZBOX) || defined(WITH_MCA) +#define USE_OPENXCAS 1 +extern int32_t openxcas_provid; +extern uint16_t openxcas_sid, openxcas_caid, openxcas_ecm_pid; +static inline void openxcas_set_caid(uint16_t _caid) { openxcas_caid = _caid; } +static inline void openxcas_set_ecm_pid(uint16_t _pid) { openxcas_ecm_pid = _pid; } +static inline void openxcas_set_sid(uint16_t _sid) { openxcas_sid = _sid; } +static inline void openxcas_set_provid(uint32_t _provid) { openxcas_provid = _provid; } +#else +#define USE_OPENXCAS 0 +static inline void openxcas_set_caid(uint16_t UNUSED(_caid)) { } +static inline void openxcas_set_ecm_pid(uint16_t UNUSED(_pid)) { } +static inline void openxcas_set_sid(uint16_t UNUSED(_sid)) { } +static inline void openxcas_set_provid(uint32_t UNUSED(_provid)) { } +#endif + +bool is_dvbapi_usr(char *usr); +static inline bool module_dvbapi_enabled(void) { return cfg.dvbapi_enabled; } +#else +static inline void dvbapi_stop_all_descrambling(uint32_t UNUSED(msgid)) { } +static inline void dvbapi_read_priority(void) { } +static inline bool is_dvbapi_usr(char *UNUSED(usr)) { return 0; } +static inline bool module_dvbapi_enabled(void) { return 0; } +#endif // WITH_DVBAPI + +#endif // MODULE_DVBAPI_H_ diff --git a/module-emulator-biss.c b/module-emulator-biss.c new file mode 100644 index 0000000..a6b0c9e --- /dev/null +++ b/module-emulator-biss.c @@ -0,0 +1,883 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "module-emulator-osemu.h" +#include "module-emulator-biss.h" +#include "oscam-aes.h" +#include "oscam-string.h" +#include +#include +//#include +#include + +// DVB-CISSA v1 IV as defined in ETSI TS 103 127 +static const uint8_t dvb_cissa_iv[16] = +{ + 0x44, 0x56, 0x42, 0x54, 0x4D, 0x43, 0x50, 0x54, + 0x41, 0x45, 0x53, 0x43, 0x49, 0x53, 0x53, 0x41 +}; + +static void unify_orbitals(uint32_t *namespace) +{ + // Unify orbitals to produce same namespace among users + // Set positions according to http://satellites-xml.org + + uint16_t pos = (*namespace & 0x0FFF0000) >> 16; + + switch (pos) + { + case 29: // Rascom QAF 1R + case 31: // Eutelsat 3B + { + pos = 30; + break; + } + + case 49: + case 50: // SES 5 + { + pos = 48; // Astra 4A + break; + } + + case 215: + { + pos = 216; // Eutelsat 21B + break; + } + + case 285: // Astra 2E + { + pos = 282; // Astra 2F/2G + break; + } + + case 328: // Intelsat 28 + case 329: + case 331: // Eutelsat 33C + { + pos = 330; + break; + } + + case 359: // Eutelsat 36B + case 361: // Express AMU1 + { + pos = 360; + break; + } + + case 451: // Intelsat 904 + { + pos = 450; // Intelsat 12 + break; + } + + case 550: + case 551: // G-Sat 8/16 + { + pos = 549; // Yamal 402 + break; + } + + case 748: + case 749: // ABS 2A + { + pos = 750; + break; + } + + case 848: // Horizons 2 + case 852: // Intelsat 15 + { + pos = 850; + break; + } + + case 914: // Mesasat 3a + { + pos = 915; // Mesasat 3/3b + break; + } + + case 934: // G-Sat 17 + case 936: // Insat 4B + { + pos = 935; // G-Sat 15 + break; + } + + case 3600 - 911: // Nimiq 6 + { + pos = 3600 - 910; // Galaxy 17 + break; + } + + case 3600 - 870: // SES 2 + case 3600 - 872: // TKSat 1 + { + pos = 3600 - 871; + break; + } + + case 3600 - 432: // Sky Brasil 1 + case 3600 - 430: // Intelsat 11 + { + pos = 3600 - 431; + break; + } + + case 3600 - 376: // Telstar 11N + case 3600 - 374: // NSS 10 + { + pos = 3600 - 375; + break; + } + + case 3600 - 359: // Hispasat 36W-1 + { + pos = 3600 - 360; // Eutelsat 36 West A + break; + } + + case 3600 - 81: // Eutelsat 8 West B + { + pos = 3600 - 80; + break; + } + + case 3600 - 73: // Eutelsat 7 West A + case 3600 - 72: + case 3600 - 71: + { + pos = 3600 - 70; // Nilesat 201 + break; + } + + case 3600 - 10: // Intelsat 10-02 + case 3600 - 9: // Thor 6 + case 3600 - 7: // Thor 7 + case 3600 - 6: // Thor 7 + { + pos = 3600 - 8; // Thor 5 + break; + } + } + + *namespace = (*namespace & 0xF000FFFF) | (pos << 16); +} + +static void annotate(char *buf, uint8_t len, const uint8_t *ecm, uint16_t ecmLen, + uint32_t hash, int8_t isNamespaceHash, int8_t datecoded) +{ + // Extract useful information to append to the "Example key ..." message. + // + // For feeds, the orbital position & frequency are usually embedded in the namespace. + // See https://github.com/OpenPLi/enigma2/blob/develop/lib/dvb/frontend.cpp#L476 + // hash = (sat.orbital_position << 16); + // hash |= ((sat.frequency/1000)&0xFFFF)|((sat.polarisation&1) << 15); + // + // If the onid & tsid appear to be a unique DVB identifier, enigma2 strips the frequency + // from our namespace. See https://github.com/OpenPLi/enigma2/blob/develop/lib/dvb/scan.cpp#L55 + // In that case, our annotation contains the onid:tsid:sid triplet in lieu of frequency. + // + // For the universal case, we print the number of elementary stream pids & pmtpid. + // The sid and current time are included for all. Examples: + // + // F 1A2B3C4D 00000000 XXXXXXXXXXXXXXXX ; 110.5W 12345H sid:0001 added: 2017-10-17 @ 13:14:15 // namespace + // F 1A2B3C4D 20180123 XXXXXXXXXXXXXXXX ; 33.5E ABCD:9876:1234 added: 2017-10-17 @ 13:14:15 // stripped namespace + // F 1A2B3C4D 20180123 XXXXXXXXXXXXXXXX ; av:5 pmt:0134 sid:0001 added: 2017-10-17 @ 13:14:15 // universal + + uint8_t pidcount; + uint16_t frequency, degrees, pmtpid, srvid, tsid, onid; + uint32_t ens; + char compass, polarisation, timeStr1[9], timeStr2[19]; + + if (datecoded) + { + date_to_str(timeStr1, sizeof(timeStr1), 4, 3); + } + else + { + snprintf(timeStr1, sizeof(timeStr1), "00000000"); + } + + date_to_str(timeStr2, sizeof(timeStr2), 0, 2); + + if (isNamespaceHash) // Namespace hash + { + ens = b2i(4, ecm + ecmLen - 4); // Namespace will be the last 4 bytes of the ecm + degrees = (ens >> 16) & 0x0FFF; // Remove not-a-pid flag + + if (degrees > 1800) + { + degrees = 3600 - degrees; + compass = 'W'; + } + else + { + compass = 'E'; + } + + if (0 == (ens & 0xFFFF)) // Stripped namespace hash + { + srvid = b2i(2, ecm + 3); + tsid = b2i(2, ecm + ecmLen - 8); + onid = b2i(2, ecm + ecmLen - 6); + // Printing degree sign "\u00B0" requires c99 standard + snprintf(buf, len, "F %08X %s XXXXXXXXXXXXXXXX ; %5.1f%c %04X:%04X:%04X added: %s", + hash, timeStr1, degrees / 10.0, compass, onid, tsid, srvid, timeStr2); + } + else // Full namespace hash + { + srvid = b2i(2, ecm + 3); + frequency = ens & 0x7FFF; // Remove polarity bit + polarisation = ens & 0x8000 ? 'V' : 'H'; + // Printing degree sign "\u00B0" requires c99 standard + snprintf(buf, len, "F %08X %s XXXXXXXXXXXXXXXX ; %5.1f%c %5d%c sid:%04X added: %s", + hash, timeStr1, degrees / 10.0, compass, frequency, polarisation, srvid, timeStr2); + } + } + else // Universal hash + { + srvid = b2i(2, ecm + 3); + pmtpid = b2i(2, ecm + 5); + pidcount = (ecmLen - 15) / 2; // video + audio pids count + snprintf(buf, len, "F %08X %s XXXXXXXXXXXXXXXX ; av:%d pmt:%04X sid:%04X added: %s", + hash, timeStr1, pidcount, pmtpid, srvid, timeStr2); + } +} + +static int8_t is_common_hash(uint32_t hash) +{ + // Check universal hash against a number of commnon universal + // hashes in order to warn users about potential key clashes + + switch (hash) + { + case 0xBAFCD9FD: // 0001 0020 0200 1010 1020 (most common hash) + return 1; + case 0xA6A4FBD4: // 0001 0800 0200 1010 1020 + return 1; + case 0xEFAB7A4D: // 0001 0800 1010 1020 0200 + return 1; + case 0x83FA15D1: // 0001 0020 0134 0100 0101 + return 1; + case 0x58934C38: // 0001 0800 1010 1020 1030 0200 + return 1; + case 0x2C3CEC17: // 0001 0020 0134 0100 + return 1; + case 0x73DF7F7E: // 0001 0020 0200 1010 1020 1030 + return 1; + case 0xAFA85BC8: // 0001 0020 0021 0022 0023 + return 1; + case 0x8C51F31D: // 0001 0800 0200 1010 1020 1030 1040 + return 1; + case 0xE2F9BD29: // 0001 0800 0200 1010 1020 1030 + return 1; + case 0xB9EBE0FF: // 0001 0100 0200 1010 1020 (less common hash) + return 1; + default: + return 0; + } +} + +static int8_t is_valid_namespace(uint32_t namespace) +{ + // Note to developers: + // If we ever have a satellite at 0.0E, edit to allow stripped namespace + // '0xA0000000' with an additional test on tsid and onid being != 0 + + uint16_t orbital, frequency; + + orbital = (namespace >> 16) & 0x0FFF; + frequency = namespace & 0x7FFF; + + if ((namespace & 0xF0000000) != 0xA0000000) return 0; // Value isn't flagged as namespace + if ((namespace & 0x0FFFFFFF) == 0x00000000) return 0; // Empty namespace + if (orbital > 3599) return 0; // Allow only DVB-S + if (frequency == 0) return 1; // Stripped namespace + if (frequency >= 3400 && frequency <= 4200) return 1; // Super extended C band + if (frequency >= 10700 && frequency <= 12750) return 1; // Ku band Europe + + return 0; +} + +static int8_t get_sw(uint32_t provider, uint8_t *sw, uint8_t sw_length, int8_t dateCoded, int8_t printMsg) +{ + // If date-coded keys are enabled in the webif, this function evaluates the expiration date + // of the found keys. Expired keys are not sent to the calling function. If date-coded keys + // are disabled, then every key is sent without any evaluation. It takes the "provider" as + // input and outputs the "sw". Returns 0 (key not found, or expired) or 1 (key found). + + // printMsg: 0 => No message + // printMsg: 1 => Print message only if key is found + // printMsg: 2 => Always print message, regardless if key is found or not + + char keyExpDate[9] = "00000000"; + + if (emu_find_key('F', provider, 0, keyExpDate, sw, sw_length, 0, 0, 0, NULL)) // Key found + { + if (dateCoded) // Date-coded keys are enabled, evaluate expiration date + { + char currentDate[9]; + date_to_str(currentDate, sizeof(currentDate), 0, 3); + + if (strncmp("00000000", keyExpDate, 9) == 0 || strncmp(currentDate, keyExpDate, 9) < 0) // Evergreen or not expired + { + if (printMsg == 1 || printMsg == 2) cs_log("Key found: F %08X %s", provider, keyExpDate); + return 1; + } + else // Key expired + { + sw = NULL; // Make sure we don't send any expired key + if (printMsg == 2) cs_log("Key expired: F %08X %s", provider, keyExpDate); + return 0; + } + } + else // Date-coded keys are disabled, don't evaluate expiration date + { + if (printMsg == 1 || printMsg == 2) cs_log("Key found: F %08X %s", provider, keyExpDate); + return 1; + } + } + else // Key not found + { + if (printMsg == 2) cs_log("Key not found: F %08X", provider); + return 0; + } +} + +static int8_t biss_mode1_ecm(struct s_reader *rdr, const uint8_t *ecm, uint16_t caid, uint16_t ecm_pid, uint8_t *dw, EXTENDED_CW *cw_ex) +{ + // Oscam's fake ecm consists of [sid] [pmtpid] [pid1] [pid2] ... [pidx] [tsid] [onid] [ens] + // On enigma boxes tsid, onid and namespace should be non zero, while on non-enigma + // boxes they are usually all zero. The top 4 bits of the namespace are flagged with 0xA. + + // The emulator creates a unique channel hash using srvid and enigma namespace or + // srvid, tsid, onid and namespace (in case of namespace without frequency) and + // another weaker (not unique) hash based on every pid of the channel. This universal + // hash should be available on all types of stbs (enigma and non-enigma). + + // Key searches are made from highest priority (tightest test first) to lowest priority + // (loosest test last): + // 1. Namespace hash (only on enigma boxes) + // 2. Universal hash (all box types with emu r752+) + // 3. Valid tsid, onid combination + // 4. Reverse order pid (audio, video, pmt) + // 5. Legacy srvid, ecm pid combination + // 6. Default "All Feeds" key + + // If enabled in the webif, a date based key search is performed. If the expiration + // date has passed, the key is not sent back from get_sw(). This option is used only + // in the namespace hash, universal hash and the "All Feeds" search methods. + + uint32_t i, ens = 0, hash = 0; + uint16_t srvid, tsid = 0, onid = 0, pid, ecm_len = SCT_LEN(ecm); + uint8_t *sw, sw_length, ecm_copy[ecm_len]; + char tmp_buffer1[33], tmp_buffer2[90] = "0", tmp_buffer3[90] = "0"; + + if (caid == 0x2602 && cw_ex != NULL) // BISS2 + { + cw_ex->mode = CW_MODE_ONE_CW; + cw_ex->algo = CW_ALGO_AES128; + cw_ex->algo_mode = CW_ALGO_MODE_CBC; + memcpy(cw_ex->data, dvb_cissa_iv, 16); + + sw = cw_ex->session_word; + sw_length = 16; + } + else // BISS1 + { + sw = dw; + sw_length = 8; + } + + srvid = b2i(2, ecm + 3); + + if (ecm_len >= 17) // Likely an r752+ extended ecm + { + tsid = b2i(2, ecm + ecm_len - 8); + onid = b2i(2, ecm + ecm_len - 6); + ens = b2i(4, ecm + ecm_len - 4); + } + + // 1. Namespace hash (enigma only) + if (is_valid_namespace(ens)) + { + unify_orbitals(&ens); + memcpy(ecm_copy, ecm, ecm_len); + i2b_buf(4, ens, ecm_copy + ecm_len - 4); + + for (i = 0; i < 5; i++) // Find key matching hash made with frequency modified to: f+0, then f-1, f+1, f-2, lastly f+2 + { + ecm_copy[ecm_len - 1] = (i & 1) ? ecm_copy[ecm_len - 1] - i : ecm_copy[ecm_len - 1] + i; // frequency +/- 1, 2 MHz + + if (0 != (ens & 0xFFFF)) // Full namespace - Calculate hash with srvid and namespace only + { + i2b_buf(2, srvid, ecm_copy + ecm_len - 6); // Put [srvid] right before [ens] + hash = crc32(caid, ecm_copy + ecm_len - 6, 6); + } + else // Namespace without frequency - Calculate hash with srvid, tsid, onid and namespace + { + i2b_buf(2, srvid, ecm_copy + ecm_len - 10); // Put [srvid] right before [tsid] [onid] [ens] sequence + hash = crc32(caid, ecm_copy + ecm_len - 10, 10); + } + + if (get_sw(hash, sw, sw_length, rdr->emu_datecodedenabled, i == 0 ? 2 : 1)) // Do not print "key not found" for frequency off by 1, 2 + { + memcpy(sw + sw_length, sw, sw_length); + return EMU_OK; + } + + if (i == 0) // No key found matching our hash: create example SoftCam.Key BISS line for the live log + { + annotate(tmp_buffer2, sizeof(tmp_buffer2), ecm_copy, ecm_len, hash, 1, rdr->emu_datecodedenabled); + } + + if (0 == (ens & 0xFFFF)) // Namespace without frequency - Do not iterate + { + break; + } + } + } + + // 2. Universal hash (in r752+ style ecms that contain pmt pid) + if ((ens & 0xF0000000) == 0xA0000000) + { + hash = crc32(caid, ecm + 3, ecm_len - 3 - 8); // Do not include [tsid] [onid] [ens] in the hash + + if (get_sw(hash, sw, sw_length, rdr->emu_datecodedenabled, 2)) + { + memcpy(sw + sw_length, sw, sw_length); + return EMU_OK; + } + + // No key found matching our hash: create example SoftCam.Key BISS line for the live log + annotate(tmp_buffer3, sizeof(tmp_buffer3), ecm_copy, ecm_len, hash, 0, rdr->emu_datecodedenabled); + } + + // 3. Valid [tsid] [onid] combination (per enigma2) + if (onid != 0 && (onid != 1 || tsid >= 2) && onid < 0xFF00) + { + if (get_sw(tsid << 16 | onid, sw, sw_length, 0, 2)) + { + memcpy(sw + sw_length, sw, sw_length); + return EMU_OK; + } + } + + // 4. Reverse order pid search + // (better identifies channels with variable counts of audio pids) + // Strip [tsid] [onid] [ens] on r752+ ecms to be compatible with older versions) + if ((ens & 0xF0000000) == 0xA0000000) + { + ecm_len -= 8; + } + + for (i = ecm_len - 2; i >= 5; i -= 2) + { + pid = b2i(2, ecm + i); + + if (get_sw((srvid << 16) | pid, sw, sw_length, 0, 2)) + { + memcpy(sw + sw_length, sw, sw_length); + return EMU_OK; + } + } + + // 5. Legacy [srvid] [ecm pid] combination + if (get_sw((srvid << 16) | ecm_pid, sw, sw_length, 0, 2)) + { + memcpy(sw + sw_length, sw, sw_length); + return EMU_OK; + } + + // 6. Default BISS key for events with many feeds sharing the same session word + // (limited to local ecms, network ecms with ecm pid equal to zero are blocked) + if (ecm_pid != 0 && get_sw(0xA11FEED5, sw, sw_length, rdr->emu_datecodedenabled, 2)) + { + memcpy(sw + sw_length, sw, sw_length); + cs_hexdump(0, sw, sw_length, tmp_buffer1, sizeof(tmp_buffer1)); + cs_log("No specific match found. Using 'All Feeds' key: %s", tmp_buffer1); + return EMU_OK; + } + + // Print example key lines for available hash search methods, if no key is found + if (strncmp(tmp_buffer2, "0", 2)) cs_log("Example key based on namespace hash: %s", tmp_buffer2); + if (strncmp(tmp_buffer3, "0", 2)) cs_log("Example key based on universal hash: %s", tmp_buffer3); + + // Check if universal hash is common and warn user + if (is_common_hash(hash)) cs_log("Feed has commonly used pids, universal hash clashes in SoftCam.Key are likely!"); + + return EMU_KEY_NOT_FOUND; +} + +static inline int8_t get_ecm_key(uint16_t onid, uint16_t esid, uint8_t parity, uint8_t *key) +{ + return emu_find_key('G', onid << 16 | esid, 0, parity == 0 ? "00" : "01", key, 16, 1, 0, 0, NULL); +} + +static int8_t biss2_mode_ca_ecm(const uint8_t *ecm, EXTENDED_CW *cw_ex) +{ + uint8_t ecm_cipher_type, session_key_parity; + uint8_t session_key[16], iv[16]; + uint16_t entitlement_session_id, original_network_id, descriptor_length; + uint16_t position, ecm_length = SCT_LEN(ecm); + uint32_t payload_checksum, calculated_checksum; + char tmp_buffer[64]; + struct aes_keys aes; + + // Calculate crc32 checksum and compare against the checksum bytes of the ECM + payload_checksum = b2i(4, ecm + ecm_length - 4); + calculated_checksum = ccitt32_crc((uint8_t *)ecm, ecm_length - 4); + + if (payload_checksum != calculated_checksum) + { + cs_log_dbg(D_TRACE, "ECM checksum mismatch (payload: %08X vs calculated: %08X", + payload_checksum, calculated_checksum); + return EMU_CHECKSUM_ERROR; + } + + // Unique identifiers of the session key + entitlement_session_id = b2i(2, ecm + 3); + original_network_id = b2i(2, ecm + 8); + + ecm_cipher_type = ecm[10] >> 5; + if (ecm_cipher_type != 0) // Session words shall be encrypted with AES_128_CBC + { + cs_log("ECM cipher type %d not supported", ecm_cipher_type); + return EMU_NOT_SUPPORTED; + } + + descriptor_length = b2i(2, ecm + 10) & 0x0FFF; + position = 12 + descriptor_length; + + session_key_parity = ecm[position] >> 7; // Parity can be "00" or "01" + position++; + + if (!get_ecm_key(original_network_id, entitlement_session_id, session_key_parity, session_key)) + { + return EMU_KEY_NOT_FOUND; + } + + memcpy(iv, ecm + position, 16); // "AES_128_CBC_enc_session_word_iv" + memcpy(cw_ex->session_word, ecm + position + 16, 16); // "AES_128_CBC_enc_session_word_0" + memcpy(cw_ex->session_word + 16, ecm + position + 32, 16); // "AES_128_CBC_enc_session_word_1" + + // Delete these cs_log calls when everything is confirmed to work correctly + cs_hexdump(3, iv, 16, tmp_buffer, sizeof(tmp_buffer)); + cs_log_dbg(D_TRACE, "session_word_iv: %s", tmp_buffer); + + cs_hexdump(3, cw_ex->session_word, 16, tmp_buffer, sizeof(tmp_buffer)); + cs_log_dbg(D_TRACE, "encrypted session_word_0: %s", tmp_buffer); + + cs_hexdump(3, cw_ex->session_word + 16, 16, tmp_buffer, sizeof(tmp_buffer)); + cs_log_dbg(D_TRACE, "encrypted session_word_1: %s", tmp_buffer); + + // Decrypt session words + aes_set_key(&aes, (char *)session_key); + aes_cbc_decrypt(&aes, cw_ex->session_word, 16, iv); + memcpy(iv, ecm + position, 16); // Set iv again to the correct one + aes_cbc_decrypt(&aes, cw_ex->session_word + 16, 16, iv); + + // Delete these cs_log calls when everything is confirmed to work correctly + cs_hexdump(3, cw_ex->session_word, 16, tmp_buffer, sizeof(tmp_buffer)); + cs_log_dbg(D_TRACE, "decrypted session_word_0: %s", tmp_buffer); + + cs_hexdump(3, cw_ex->session_word + 16, 16, tmp_buffer, sizeof(tmp_buffer)); + cs_log_dbg(D_TRACE, "decrypted session_word_1: %s", tmp_buffer); + + cw_ex->mode = CW_MODE_ONE_CW; + cw_ex->algo = CW_ALGO_AES128; + cw_ex->algo_mode = CW_ALGO_MODE_CBC; + memcpy(cw_ex->data, dvb_cissa_iv, 16); + + return EMU_OK; +} + +int8_t biss_ecm(struct s_reader *rdr, const uint8_t *ecm, uint16_t caid, uint16_t ecm_pid, uint8_t *dw, EXTENDED_CW *cw_ex) +{ + switch (caid) + { + case 0x2600: + return biss_mode1_ecm(rdr, ecm, caid, ecm_pid, dw, NULL); + + case 0x2602: + return biss_mode1_ecm(rdr, ecm, caid, ecm_pid, NULL, cw_ex); + + case 0x2610: + return biss2_mode_ca_ecm(ecm, cw_ex); + + default: + cs_log("Unknown Biss caid %04X - Please report!", caid); + return EMU_NOT_SUPPORTED; + } +} + +static uint16_t parse_session_data_descriptor(const uint8_t *data, uint16_t esid, uint16_t onid, uint32_t *keysAdded) +{ + uint8_t descriptor_tag = data[0]; + uint8_t descriptor_length = data[1]; + + switch (descriptor_tag) + { + case 0x81: // session_key_descriptor + { + uint8_t session_key_type = data[2] >> 1; + if (session_key_type == 0) // AES-128 + { + uint8_t session_key_parity = data[2] & 0x01; + uint8_t session_key_data[16]; + memcpy(session_key_data, data + 3, 16); // This is the ECM key + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + if (emu_update_key('G', onid << 16 | esid, session_key_parity ? "01" : "00", session_key_data, 16, 1, NULL)) + { + (*keysAdded)++; + char tmp[33]; + cs_hexdump(0, session_key_data, 16, tmp, sizeof(tmp)); + cs_log("Key found in EMM: G %08X %02d %s", onid << 16 | esid, session_key_parity, tmp); + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + } + break; + } + + case 0x82: // entitlement_flags_descriptor + break; + + default: + break; + } + + return 2 + descriptor_length; +} + +static int8_t parse_session_data(const uint8_t *data, RSA *key, uint16_t esid, uint16_t onid, uint32_t *keysAdded) +{ + // session_data is encrypted with RSA 2048 bit OAEP + // Maximum size of decrypted session_data is less than (256-41) bytes + uint8_t session_data[214]; + + if (RSA_private_decrypt(256, data, session_data, key, RSA_PKCS1_OAEP_PADDING) > 0) + { + uint16_t pos = 0; + uint16_t descriptor_length = b2i(2, session_data) & 0x0FFF; + + while (pos < descriptor_length) + { + pos += parse_session_data_descriptor(session_data + 2 + pos, esid, onid, keysAdded); + } + + return EMU_OK; + } + + return EMU_NOT_SUPPORTED; // Decryption failed for whatever reason +} + +static int8_t get_rsa_key(struct s_reader *rdr, const uint8_t *ekid, RSA **key) +{ + LL_ITER itr; + biss2_rsa_key_t *data; + + itr = ll_iter_create(rdr->ll_biss2_rsa_keys); + while ((data = ll_iter_next(&itr))) + { + if (data->ekid == ekid) + { + *key = data->key; + return 1; + } + } + + return 0; +} + +int8_t biss_emm(struct s_reader *rdr, const uint8_t *emm, uint32_t *keysAdded) +{ + uint8_t emm_cipher_type, entitlement_priv_data_loop, entitlement_key_id[8]; + uint16_t entitlement_session_id, original_network_id, descriptor_length; + uint16_t pos, emm_length = SCT_LEN(emm); + uint32_t payload_checksum, calculated_checksum; + int8_t result = EMU_NOT_SUPPORTED; + char tmp[17]; + RSA *key; + + // Calculate crc32 checksum and compare against the checksum bytes of the EMM + payload_checksum = b2i(4, emm + emm_length - 4); + calculated_checksum = ccitt32_crc((uint8_t *)emm, emm_length - 4); + + if (payload_checksum != calculated_checksum) + { + cs_log_dbg(D_TRACE, "EMM checksum mismatch (payload: %08X vs calculated: %08X", + payload_checksum, calculated_checksum); + return EMU_CHECKSUM_ERROR; + } + + // Identifiers of the session key carried in the EMM + // We just pass them to the "parse_session_data()" function + entitlement_session_id = b2i(2, emm + 3); + original_network_id = b2i(2, emm + 8); + cs_log_dbg(D_TRACE, "onid: %04X, esid: %04X", original_network_id, entitlement_session_id); + + emm_cipher_type = emm[11] >> 5; // top 3 bits; + entitlement_priv_data_loop = (emm[11] >> 4) & 0x01; // 4th bit + + if (emm_cipher_type != 0) // EMM payload is not encrypted with RSA_2048_OAEP + { + cs_log_dbg(D_TRACE, "EMM cipher type %d not supported", emm_cipher_type); + return EMU_NOT_SUPPORTED; + } + + descriptor_length = b2i(2, emm + 12) & 0x0FFF; + pos = 14 + descriptor_length; + + while (pos < emm_length - 4) + { + // Unique identifier of the public rsa key used for "session_data" encryption + memcpy(entitlement_key_id, emm + pos, 8); + pos += 8; + + if (get_rsa_key(rdr, entitlement_key_id, &key)) // Key found + { + cs_hexdump(0, entitlement_key_id, 8, tmp, sizeof(tmp)); + cs_log_dbg(D_TRACE, "RSA key found (ekid: %s)", tmp); + + // Parse "encrypted_session_data" + result = parse_session_data(emm + pos, key, entitlement_session_id, original_network_id, keysAdded); + if (result == EMU_OK) + { + break; // No need to decrypt again with another key + } + } + else // Multiple ekid's can be present in the EMM - Do not exit just yet + { + cs_hexdump(0, entitlement_key_id, 8, tmp, sizeof(tmp)); + cs_log_dbg(D_TRACE, "RSA key not found (ekid: %s)", tmp); + + result = EMU_KEY_NOT_FOUND; + } + + pos += 256; // 2048 bits + + if (entitlement_priv_data_loop) // Skip any remaining bytes + { + pos += 2 + (b2i(2, emm + pos) & 0x0FFF); + } + } + + return result; +} + +static int8_t rsa_key_exists(struct s_reader *rdr, const biss2_rsa_key_t *item) +{ + LL_ITER itr; + biss2_rsa_key_t *data; + + itr = ll_iter_create(rdr->ll_biss2_rsa_keys); + while ((data = ll_iter_next(&itr))) + { + if (data->ekid == item->ekid) + { + return 1; + } + } + + return 0; +} + +uint16_t biss_read_pem(struct s_reader *rdr, uint8_t max_keys) +{ + FILE *fp_pri = NULL; + //FILE *fp_pub = NULL; + + char tmp[256]; + uint8_t hash[32], *der = NULL; + uint16_t i, length, count = 0;; + biss2_rsa_key_t *new_item; + + if (!rdr->ll_biss2_rsa_keys) + { + rdr->ll_biss2_rsa_keys = ll_create("ll_biss2_rsa_keys"); + } + + for (i = 0; i < max_keys; i++) + { + if (!cs_malloc(&new_item, sizeof(biss2_rsa_key_t))) + { + break; // No memory available (?) - Exit + } + + snprintf(tmp, sizeof(tmp), "%sbiss2_private_%02d.pem", emu_keyfile_path, i); + if ((fp_pri = fopen(tmp, "r")) == NULL) + { + continue; // File does not exist + } + + cs_log("Reading RSA key from: biss2_private_%02d.pem", i); + + // Read RSA private key + if ((new_item->key = PEM_read_RSAPrivateKey(fp_pri, NULL, NULL, NULL)) == NULL) + { + cs_log("Error reading RSA private key"); + continue; + } + + fclose(fp_pri); + + // Write public key in PEM formatted file + /*snprintf(tmp, sizeof(tmp), "%sbiss2_public_%02d.pem", emu_keyfile_path, i); + if ((fp_pub = fopen(tmp, "w")) != NULL) + { + PEM_write_RSA_PUBKEY(fp_pub, item->key); + fclose(fp_pub); + }*/ + + // Write public key in DER formatted file + /*snprintf(tmp, sizeof(tmp), "%sbiss2_public_%02d.der", emu_keyfile_path, i); + if ((fp_pub = fopen(tmp, "wb")) != NULL) + { + i2d_RSA_PUBKEY_fp(fp_pub, item->key); + fclose(fp_pub); + }*/ + + // Encode RSA public key into DER format + if ((length = i2d_RSA_PUBKEY(new_item->key, &der)) <= 0) + { + cs_log("Error encoding to DER format"); + NULLFREE(der); + continue; + } + + // Create SHA256 digest + EVP_MD_CTX *mdctx; + if ((mdctx = EVP_MD_CTX_create()) == NULL) + { + NULLFREE(der); + continue; + } + + EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL); + EVP_DigestUpdate(mdctx, der, length); + EVP_DigestFinal_ex(mdctx, hash, NULL); + EVP_MD_CTX_destroy(mdctx); + + NULLFREE(der); + memcpy(new_item->ekid, hash, 8); + + // Add new RSA key, if not already present + if (!rsa_key_exists(rdr, new_item)) + { + ll_append(rdr->ll_biss2_rsa_keys, new_item); + count++; + } + } + + return count; +} + +#endif // WITH_EMU diff --git a/module-emulator-biss.h b/module-emulator-biss.h new file mode 100644 index 0000000..0f5900c --- /dev/null +++ b/module-emulator-biss.h @@ -0,0 +1,22 @@ +#ifndef MODULE_EMULATOR_BISS_H +#define MODULE_EMULATOR_BISS_H + +#ifdef WITH_EMU + +#include + +#define BISS2_MAX_RSA_KEYS 16 + +typedef struct biss2_rsa_key +{ + uint8_t ekid[8]; + RSA *key; +} biss2_rsa_key_t; + +int8_t biss_ecm(struct s_reader *rdr, const uint8_t *ecm, uint16_t caid, uint16_t ecm_pid, uint8_t *dw, EXTENDED_CW *cw_ex); +int8_t biss_emm(struct s_reader *rdr, const uint8_t *emm, uint32_t *keysAdded); +uint16_t biss_read_pem(struct s_reader *rdr, uint8_t max_keys); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_BISS_H diff --git a/module-emulator-cryptoworks.c b/module-emulator-cryptoworks.c new file mode 100644 index 0000000..df6509a --- /dev/null +++ b/module-emulator-cryptoworks.c @@ -0,0 +1,688 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "cscrypt/des.h" +#include "module-emulator-osemu.h" + +// Cryptoworks EMU + +static int8_t get_key(uint8_t *buf,uint32_t ident, uint8_t keyIndex, uint32_t keyLength, uint8_t isCriticalKey) +{ + char keyName[EMU_MAX_CHAR_KEYNAME]; + uint32_t tmp; + + if ((ident >> 4) == 0xD02A) + { + keyIndex &= 0xFE; // map to even number key indexes + } + + if ((ident >> 4) == 0xD00C) + { + ident = 0x0D00C0; // map provider C? to C0 + } + else if (keyIndex == 6 && ((ident >> 8) == 0x0D05)) + { + ident = 0x0D0504; // always use provider 04 system key + } + + tmp = keyIndex; + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%.2X", tmp); + + if (emu_find_key('W', ident, 0, keyName, buf, keyLength, isCriticalKey, 0, 0, NULL)) + { + return 1; + } + + return 0; +} + +static const uint8_t cw_sbox1[64] = +{ + 0xD8, 0xD7, 0x83, 0x3D, 0x1C, 0x8A, 0xF0, 0xCF, 0x72, 0x4C, 0x4D, 0xF2, 0xED, 0x33, 0x16, 0xE0, + 0x8F, 0x28, 0x7C, 0x82, 0x62, 0x37, 0xAF, 0x59, 0xB7, 0xE0, 0x00, 0x3F, 0x09, 0x4D, 0xF3, 0x94, + 0x16, 0xA5, 0x58, 0x83, 0xF2, 0x4F, 0x67, 0x30, 0x49, 0x72, 0xBF, 0xCD, 0xBE, 0x98, 0x81, 0x7F, + 0xA5, 0xDA, 0xA7, 0x7F, 0x89, 0xC8, 0x78, 0xA7, 0x8C, 0x05, 0x72, 0x84, 0x52, 0x72, 0x4D, 0x38 +}; + +static const uint8_t cw_sbox2[64] = +{ + 0xD8, 0x35, 0x06, 0xAB, 0xEC, 0x40, 0x79, 0x34, 0x17, 0xFE, 0xEA, 0x47, 0xA3, 0x8F, 0xD5, 0x48, + 0x0A, 0xBC, 0xD5, 0x40, 0x23, 0xD7, 0x9F, 0xBB, 0x7C, 0x81, 0xA1, 0x7A, 0x14, 0x69, 0x6A, 0x96, + 0x47, 0xDA, 0x7B, 0xE8, 0xA1, 0xBF, 0x98, 0x46, 0xB8, 0x41, 0x45, 0x9E, 0x5E, 0x20, 0xB2, 0x35, + 0xE4, 0x2F, 0x9A, 0xB5, 0xDE, 0x01, 0x65, 0xF8, 0x0F, 0xB2, 0xD2, 0x45, 0x21, 0x4E, 0x2D, 0xDB +}; + +static const uint8_t cw_sbox3[64] = +{ + 0xDB, 0x59, 0xF4, 0xEA, 0x95, 0x8E, 0x25, 0xD5, 0x26, 0xF2, 0xDA, 0x1A, 0x4B, 0xA8, 0x08, 0x25, + 0x46, 0x16, 0x6B, 0xBF, 0xAB, 0xE0, 0xD4, 0x1B, 0x89, 0x05, 0x34, 0xE5, 0x74, 0x7B, 0xBB, 0x44, + 0xA9, 0xC6, 0x18, 0xBD, 0xE6, 0x01, 0x69, 0x5A, 0x99, 0xE0, 0x87, 0x61, 0x56, 0x35, 0x76, 0x8E, + 0xF7, 0xE8, 0x84, 0x13, 0x04, 0x7B, 0x9B, 0xA6, 0x7A, 0x1F, 0x6B, 0x5C, 0xA9, 0x86, 0x54, 0xF9 +}; + +static const uint8_t cw_sbox4[64] = +{ + 0xBC, 0xC1, 0x41, 0xFE, 0x42, 0xFB, 0x3F, 0x10, 0xB5, 0x1C, 0xA6, 0xC9, 0xCF, 0x26, 0xD1, 0x3F, + 0x02, 0x3D, 0x19, 0x20, 0xC1, 0xA8, 0xBC, 0xCF, 0x7E, 0x92, 0x4B, 0x67, 0xBC, 0x47, 0x62, 0xD0, + 0x60, 0x9A, 0x9E, 0x45, 0x79, 0x21, 0x89, 0xA9, 0xC3, 0x64, 0x74, 0x9A, 0xBC, 0xDB, 0x43, 0x66, + 0xDF, 0xE3, 0x21, 0xBE, 0x1E, 0x16, 0x73, 0x5D, 0xA2, 0xCD, 0x8C, 0x30, 0x67, 0x34, 0x9C, 0xCB +}; + +static const uint8_t AND_bit1[8] = { 0x00, 0x40, 0x04, 0x80, 0x21, 0x10, 0x02, 0x08 }; +static const uint8_t AND_bit2[8] = { 0x80, 0x08, 0x01, 0x40, 0x04, 0x20, 0x10, 0x02 }; +static const uint8_t AND_bit3[8] = { 0x82, 0x40, 0x01, 0x10, 0x00, 0x20, 0x04, 0x08 }; +static const uint8_t AND_bit4[8] = { 0x02, 0x10, 0x04, 0x40, 0x80, 0x08, 0x01, 0x20 }; + +static void swap_key(uint8_t *key) +{ + uint8_t k[8]; + memcpy(k, key, 8); + memcpy(key, key + 8, 8); + memcpy(key + 8, k, 8); +} + +static void swap_data(uint8_t *k) +{ + uint8_t d[4]; + memcpy(d, k + 4, 4); + memcpy(k + 4, k, 4); + memcpy(k, d, 4); +} + +static void des_round(uint8_t *d, uint8_t *k) +{ + uint8_t aa[44] = + { + 1, 0, 3, 1, 2, 2, 3, 2, 1, 3, 1, 1, 3, 0, 1, 2, 3, 1, 3, 2, 2, 0, + 7, 6, 5, 4, 7, 6, 5, 7, 6, 5, 6, 7, 5, 7, 5, 7, 6, 6, 7, 5, 4, 4 + }; + + uint8_t bb[44] = + { + 0x80, 0x08, 0x10, 0x02, 0x08, 0x40, 0x01, 0x20, 0x40, 0x80, 0x04, + 0x10, 0x04, 0x01, 0x01, 0x02, 0x20, 0x20, 0x02, 0x01, 0x80, 0x04, + 0x02, 0x02, 0x08, 0x02, 0x10, 0x80, 0x01, 0x20, 0x08, 0x80, 0x01, + 0x08, 0x40, 0x01, 0x02, 0x80, 0x10, 0x40, 0x40, 0x10, 0x08, 0x01 + }; + + uint8_t ff[4] = { 0x02, 0x10, 0x04, 0x04}; + uint8_t l[24] = { 0, 2, 4, 6, 7, 5, 3, 1, 4, 5, 6, 7, 7, 6, 5, 4, 7, 4, 5, 6, 4, 7, 6, 5 }; + + uint8_t des_td[8], i, o, n, c = 1, m = 0, r = 0; + uint8_t *a = aa, *b = bb, *f = ff, *p1 = l, *p2 = l + 8, *p3 = l + 16; + + for (m = 0; m < 2; m++) + { + for (i = 0; i < 4; i++) + { + des_td[*p1++] = (m) ? ((d[*p2++] * 2) & 0x3F) | ((d[*p3++] & 0x80) ? 0x01 : 0x00) : + (d[*p2++] / 2) | ((d[*p3++] & 0x01) ? 0x80 : 0x00); + } + } + + for (i = 0; i < 8; i++) + { + c = (c) ? 0 : 1; + r = (c) ? 6 : 7; + n = (i) ? i - 1 : 1; + o = (c) ? ((k[n] & *f++) ? 1 : 0) : des_td[n]; + + for (m = 1; m < r; m++) + { + o = (c) ? (o * 2) | ((k[*a++] & *b++) ? 0x01 : 0x00) : (o / 2) | ((k[*a++] & *b++) ? 0x80 : 0x00); + } + + n = (i) ? n + 1 : 0; + des_td[n] = (c) ? des_td[n] ^ o : (o ^ des_td[n]) / 4; + } + + for (i = 0; i < 8; i++) + { + d[0] ^= (AND_bit1[i] & cw_sbox1[des_td[i]]); + d[1] ^= (AND_bit2[i] & cw_sbox2[des_td[i]]); + d[2] ^= (AND_bit3[i] & cw_sbox3[des_td[i]]); + d[3] ^= (AND_bit4[i] & cw_sbox4[des_td[i]]); + } + + swap_data(d); +} + +static void cw_48_key(uint8_t *inkey, uint8_t *outkey, uint8_t algotype) +{ + uint8_t round_counter, i = 8; + uint8_t *key128 = inkey; + uint8_t *key48 = inkey + 0x10; + + round_counter = 7 - (algotype & 7); + + memset(outkey, 0, 16); + memcpy(outkey, key48, 6); + + for ( ; i > round_counter; i--) + { + if (i > 1) + { + outkey[i - 2] = key128[i]; + } + } +} + +static void ls_des_key(uint8_t *key, uint8_t rotate_counter) +{ + uint8_t i, n; + uint8_t rnd[] = { 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 1 }; + uint16_t k[8]; + + n = rnd[rotate_counter]; + + for (i = 0; i < 8; i++) + { + k[i] = key[i]; + } + + for (i = 1; i < n + 1; i++) + { + k[7] = (k[7] * 2) | ((k[4] & 0x008) ? 1 : 0); + k[6] = (k[6] * 2) | ((k[7] & 0xF00) ? 1 : 0); + k[7] &= 0xFF; + + k[5] = (k[5] * 2) | ((k[6] & 0xF00) ? 1 : 0); + k[6] &= 0xFF; + + k[4] = ((k[4] * 2) | ((k[5] & 0xF00) ? 1 : 0)) & 0xFF; + k[5] &= 0xFF; + + k[3] = (k[3] * 2) | ((k[0] & 0x008) ? 1 : 0); + k[2] = (k[2] * 2) | ((k[3] & 0xF00) ? 1 : 0); + k[3] &= 0xFF; + + k[1] = (k[1] * 2) | ((k[2] & 0xF00) ? 1 : 0); + k[2] &= 0xFF; + + k[0] = ((k[0] * 2) | ((k[1] & 0xF00) ? 1 : 0)) & 0xFF; + k[1] &= 0xFF; + } + + for (i = 0; i < 8; i++) + { + key[i] = (uint8_t) k[i]; + } +} + +static void rs_des_key(uint8_t *k, uint8_t rotate_counter) +{ + uint8_t i, c; + + for (i = 1; i < rotate_counter + 1; i++) + { + c = (k[3] & 0x10) ? 0x80 : 0; + k[3] /= 2; + + if (k[2] & 1) + { + k[3] |= 0x80; + } + + k[2] /= 2; + + if (k[1] & 1) + { + k[2] |= 0x80; + } + + k[1] /= 2; + + if (k[0] & 1) + { + k[1] |= 0x80; + } + + k[0] /= 2; + k[0] |= c ; + c = (k[7] & 0x10) ? 0x80 : 0; + k[7] /= 2; + + if (k[6] & 1) + { + k[7] |= 0x80; + } + + k[6] /= 2; + + if (k[5] & 1) + { + k[6] |= 0x80; + } + + k[5] /= 2; + + if (k[4] & 1) + { + k[5] |= 0x80; + } + + k[4] /= 2; + k[4] |= c; + } +} + +static void rs_des_subkey(uint8_t *k, uint8_t rotate_counter) +{ + uint8_t rnd[] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; + + rs_des_key(k, rnd[rotate_counter]); +} + +static void prep_key(uint8_t *key) +{ + int32_t round_counter = 6, i, a; + uint8_t DES_key[8], j; + + key[7] = 6; + memset(DES_key, 0, 8); + + do + { + a = 7; + i = key[7]; + j = key[round_counter]; + + do + { + DES_key[i] = ( (DES_key[i] * 2) | ((j & 1) ? 1 : 0) ) & 0xFF; + + j /= 2; + i--; + + if (i < 0) + { + i = 6; + } + a--; + } + while (a >= 0); + + key[7] = i; + round_counter--; + } + while (round_counter >= 0); + + a = DES_key[4]; + DES_key[4] = DES_key[6]; + DES_key[6] = a; + DES_key[7] = (DES_key[3] * 16) & 0xFF; + + memcpy(key, DES_key, 8); + rs_des_key(key, 4); +} + +static void l2_des(uint8_t *data, uint8_t *key, uint8_t algo) +{ + uint8_t i, k0[22], k1[22]; + + memcpy(k0, key, 22); + memcpy(k1, key, 22); + + cw_48_key(k0, k1, algo); + prep_key(k1); + + for (i = 0; i < 2; i++) + { + ls_des_key(k1, 15); + des_round(data, k1); + } +} + +static void r2_des(uint8_t *data, uint8_t *key, uint8_t algo) +{ + uint8_t i, k0[22], k1[22]; + + memcpy(k0, key, 22); + memcpy(k1, key, 22); + + cw_48_key(k0, k1, algo); + prep_key(k1); + + for (i = 0; i < 2; i++) + { + ls_des_key(k1, 15); + } + + for (i = 0; i < 2; i++) + { + des_round(data, k1); + rs_des_subkey(k1, 1); + } + + swap_data(data); +} + +static void cw_des(uint8_t *data, uint8_t *inkey, uint8_t m) +{ + uint8_t key[22], i; + + memcpy(key, inkey + 9, 8); + prep_key(key); + + for (i = 16; i > 0; i--) + { + if (m == 1) + { + ls_des_key(key, (uint8_t) (i - 1)); + } + + des_round( data ,key); + + if (m == 0) + { + rs_des_subkey(key, (uint8_t) (i - 1)); + } + } +} + +static void cw_dec_enc(uint8_t *d, uint8_t *k, uint8_t a, uint8_t m) +{ + uint8_t n = m & 1; + + l2_des(d, k, a); + cw_des(d, k, n); + r2_des(d, k, a); + + if (m & 2) + { + swap_key(k); + } +} + +static uint8_t process_nano80(uint8_t *data, uint32_t caid, int32_t provider, uint8_t *opKey, + uint8_t nanoLength, uint8_t nano80Algo) +{ + int32_t i, j; + uint8_t key[16], desKey[16], t[8], dat1[8], dat2[8], k0D00C000[16]; + + if (nanoLength < 11) + { + return 0; + } + + if (caid == 0x0D00 && provider != 0xA0 && !get_key(k0D00C000, 0x0D00C0, 0, 16, 1)) + { + return 0; + } + + if (nano80Algo > 1) + { + return 0; + } + + memset(t, 0, 8); + memcpy(dat1, data, 8); + + if(caid == 0x0D00 && provider != 0xA0) + { + memcpy(key, k0D00C000, 16); + } + else + { + memcpy(key, opKey, 16); + } + + des_ecb3_decrypt(data, key); + memcpy(desKey, data, 8); + memcpy(data, dat1, 8); + + if (caid == 0x0D00 && provider != 0xA0) + { + memcpy(key, &k0D00C000[8], 8); + memcpy(&key[8], k0D00C000, 8); + } + else + { + memcpy(key, &opKey[8], 8); + memcpy(&key[8], opKey, 8); + } + + des_ecb3_decrypt(data, key); + memcpy(&desKey[8], data, 8); + + for (i = 8; i + 7 < nanoLength; i += 8) + { + memcpy(dat1, &data[i], 8); + memcpy(dat2, dat1, 8); + memcpy(key, desKey, 16); + des_ecb3_decrypt(dat1, key); + + for (j = 0; j < 8; j++) + { + dat1[j] ^= t[j]; + } + + memcpy(&data[i], dat1, 8); + memcpy(t, dat2, 8); + } + + return data[10] + 5; +} + +static void cryptoworks_signature(const uint8_t *data, uint32_t length, uint8_t *key, uint8_t *signature) +{ + uint32_t i, sigPos; + int8_t algo, first; + + algo = data[0] & 7; + if (algo == 7) + { + algo = 6; + } + + memset(signature, 0, 8); + first = 1; + sigPos = 0; + + for (i = 0; i < length; i++) + { + signature[sigPos] ^= data[i]; + sigPos++; + + if (sigPos > 7) + { + if (first) + { + l2_des(signature, key, algo); + } + + cw_des(signature, key, 1); + + sigPos = 0; + first = 0; + } + } + + if (sigPos > 0) + { + cw_des(signature, key, 1); + } + + r2_des(signature, key, algo); +} + +static void decrypt_des(uint8_t *data, uint8_t algo, uint8_t *key) +{ + int32_t i; + uint8_t k[22], t[8]; + + algo &= 7; + + if (algo < 7) + { + cw_dec_enc(data, key, algo, 0); + } + else + { + memcpy(k, key, 22); + + for (i = 0; i < 3; i++) + { + cw_dec_enc(data, k, algo, i & 1); + + memcpy(t, k, 8); + memcpy(k, k + 8, 8); + memcpy(k + 8, t, 8); + } + } +} + +int8_t cryptoworks_ecm(uint32_t caid, uint8_t *ecm, uint8_t *cw) +{ + int32_t provider = -1; + uint8_t keyIndex = 0, nanoLength, newEcmLength, key[22], signature[8], nano80Algo = 1; + uint16_t i, j, ecmLen = SCT_LEN(ecm); + uint32_t ident; + + if (ecmLen < 8) + { + return EMU_NOT_SUPPORTED; + } + + if (ecm[7] != ecmLen - 8) + { + return EMU_NOT_SUPPORTED; + } + + memset(key, 0, 22); + + for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2) + { + if (ecm[i] == 0x83 && i + 2 < ecmLen) + { + provider = ecm[i + 2] & 0xFC; + keyIndex = ecm[i + 2] & 3; + keyIndex = keyIndex ? 1 : 0; + } + else if (ecm[i] == 0x84 && i + 3 < ecmLen) + { + //nano80Provider = ecm[i + 2] & 0xFC; + //nano80KeyIndex = ecm[i + 2] & 3; + //nano80KeyIndex = nano80KeyIndex ? 1 : 0; + nano80Algo = ecm[i + 3]; + } + } + + if (provider < 0) + { + switch (caid) + { + case 0x0D00: + provider = 0xC0; + break; + + case 0x0D02: + provider = 0xA0; + break; + + case 0x0D03: + provider = 0x04; + break; + + case 0x0D05: + provider = 0x04; + break; + + default: + return EMU_NOT_SUPPORTED; + } + } + + ident = (caid << 8) | provider; + + if (!get_key(key, ident, keyIndex, 16, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + if (!get_key(&key[16], ident, 6, 6, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2) + { + if (ecm[i] == 0x80 && i + 2 + 7 < ecmLen && i + 2 + ecm[i + 1] <= ecmLen && + (provider == 0xA0 || provider == 0xC0 || provider == 0xC4 || provider == 0xC8)) + { + nanoLength = ecm[i + 1]; + newEcmLength = process_nano80(ecm + i + 2, caid, provider, key, nanoLength, nano80Algo); + + if (newEcmLength == 0 || newEcmLength > ecmLen - (i + 2 + 3)) + { + return EMU_NOT_SUPPORTED; + } + + ecm[i + 2 + 3] = 0x81; + ecm[i + 2 + 4] = 0x70; + ecm[i + 2 + 5] = newEcmLength; + ecm[i + 2 + 6] = 0x81; + ecm[i + 2 + 7] = 0xFF; + + return cryptoworks_ecm(caid, ecm + i + 2 + 3, cw); + } + } + + if (ecmLen - 15 < 1) + { + return EMU_NOT_SUPPORTED; + } + + cryptoworks_signature(ecm + 5, ecmLen - 15, key, signature); + + for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2) + { + switch (ecm[i]) + { + case 0xDA: + case 0xDB: + case 0xDC: + if (i + 2 + ecm[i + 1] > ecmLen) + { + break; + } + for (j = 0; j + 7 < ecm[i + 1]; j += 8) + { + decrypt_des(&ecm[i + 2 + j], ecm[5], key); + } + break; + + case 0xDF: + if (i + 2 + 8 > ecmLen) + { + break; + } + if (memcmp(&ecm[i + 2], signature, 8)) + { + return EMU_CHECKSUM_ERROR; + } + break; + } + } + + for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2) + { + switch (ecm[i]) + { + case 0xDB: + if (i + 2 + ecm[i + 1] <= ecmLen && ecm[i + 1] == 16) + { + memcpy(cw, &ecm[i + 2], 16); + return EMU_OK; + } + break; + } + } + + return EMU_CW_NOT_FOUND; +} + +#endif // WITH_EMU diff --git a/module-emulator-cryptoworks.h b/module-emulator-cryptoworks.h new file mode 100644 index 0000000..c2424e2 --- /dev/null +++ b/module-emulator-cryptoworks.h @@ -0,0 +1,10 @@ +#ifndef MODULE_EMULATOR_CRYPTOWORKS_H +#define MODULE_EMULATOR_CRYPTOWORKS_H + +#ifdef WITH_EMU + +int8_t cryptoworks_ecm(uint32_t caid, uint8_t *ecm, uint8_t *cw); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_CRYPTOWORKS_H diff --git a/module-emulator-director.c b/module-emulator-director.c new file mode 100644 index 0000000..c8c28b9 --- /dev/null +++ b/module-emulator-director.c @@ -0,0 +1,644 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "cscrypt/des.h" +#include "module-emulator-osemu.h" +#include "oscam-aes.h" +#include "oscam-string.h" + +/*************************************************************************************************/ + +// Shared functions + +static uint16_t calculate_checksum(uint8_t *data, uint8_t length) +{ + /* + * ECM and EMM checksum calculation + * 1. Combine data in 2 byte groups + * 2. Add them together + * 3. Multiply result by itself (power of 7) + * 4. XOR with fixed value 0x17E3 + */ + + uint8_t i; + uint16_t checksum = 0; + + for (i = 0; i < length; i += 2) + { + checksum += (data[i] << 8) | data[i + 1]; + } + + checksum = checksum * checksum * checksum * checksum * checksum * checksum * checksum; + checksum ^= 0x17E3; + + return checksum; +} + +static inline int8_t get_key(uint32_t keyIndex, char *keyName, uint8_t *key, uint32_t keyLength) +{ + /* + * keyIndex meaning for: + * ecm keys --> entitlementId + * emm keys --> aeskeyIndex + * aes keys --> keyIndex + * + * keyName meaning for: + * ecm keys --> "01" + * emm keys --> "MK" or "MK01" + * aes keys --> "AES" + */ + + return emu_find_key('T', keyIndex, 0, keyName, key, keyLength, 1, 0, 0, NULL); +} + +/*************************************************************************************************/ + +/* + * Director ECM emulator + * Supported versions: v4, v5, v6 (not working correctly) +*/ + +int8_t director_ecm(uint8_t *ecm, uint8_t *dw) +{ + uint8_t nanoType, nanoLength; + uint8_t *nanoData; + uint32_t pos = 3; + uint32_t entitlementId; + uint32_t ks[32]; + uint8_t ecmKey[8]; + uint16_t ecmLen = SCT_LEN(ecm); + + if (ecmLen < 5) + { + return EMU_NOT_SUPPORTED; + } + + do + { + nanoType = ecm[pos]; + nanoLength = ecm[pos + 1]; + + if (pos + 2 + nanoLength > ecmLen) + { + break; + } + + nanoData = ecm + pos + 2; + + // ECM validation + uint16_t payloadChecksum = (nanoData[nanoLength - 2] << 8) | nanoData[nanoLength - 1]; + uint16_t calculatedChecksum = calculate_checksum(nanoData, nanoLength - 2); + + if (calculatedChecksum != payloadChecksum) + { + cs_log_dbg(D_READER, "ECM checksum error (%.4X instead of %.4X)", calculatedChecksum, payloadChecksum); + return EMU_CHECKSUM_ERROR; + } + // End of ECM validation + + switch (nanoType) + { + case 0xEC: // Director v6 (September 2017) + { + if (nanoLength != 0x28) + { + cs_log_dbg(D_READER, "WARNING: nanoType EC length (%d) != %d", nanoLength, 0x28); + break; + } + + entitlementId = b2i(4, nanoData); + cs_log_dbg(D_READER, "INFO: Using entitlement id %.4X", entitlementId); + + if (!get_key(entitlementId, "01", ecmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + // Step 1 - Decrypt DES CBC with ecmKey and iv = { 0 } (equal to nanoED) + uint8_t encryptedData[32] = { 0 }; + memcpy(encryptedData, nanoData + 6, 32); + + uint8_t iv[8] = { 0 }; + des_cbc_decrypt(encryptedData, iv, ecmKey, 32); + + uint8_t nanoMode = nanoData[5]; + + if ((nanoMode & 0x20) == 0) // Old algo + { + // Step 2 - Create CW (equal to nano ED) + dw[0] = encryptedData[0x05]; + dw[1] = encryptedData[0x19]; + dw[2] = encryptedData[0x1D]; + dw[3] = (dw[0] + dw[1] + dw[2]) & 0xFF; + dw[4] = encryptedData[0x0B]; + dw[5] = encryptedData[0x12]; + dw[6] = encryptedData[0x1A]; + dw[7] = (dw[4] + dw[5] + dw[6]) & 0xFF; + dw[8] = encryptedData[0x16]; + dw[9] = encryptedData[0x03]; + dw[10] = encryptedData[0x11]; + dw[11] = (dw[8] + dw[9] + dw[10]) & 0xFF; + dw[12] = encryptedData[0x18]; + dw[13] = encryptedData[0x10]; + dw[14] = encryptedData[0x0E]; + dw[15] = (dw[12] + dw[13] + dw[14]) & 0xFF; + + return EMU_OK; + } + else // New algo (overencryption with AES) + { + // Step 2 - Prepare data for AES (it is like the creation of CW in nanoED but swapped each 8 bytes) + uint8_t dataEC[16] = { 0 }; + + dataEC[0] = encryptedData[0x02]; + dataEC[1] = encryptedData[0x0E]; + dataEC[2] = encryptedData[0x10]; + dataEC[3] = encryptedData[0x18]; + dataEC[4] = encryptedData[0x09]; + dataEC[5] = encryptedData[0x11]; + dataEC[6] = encryptedData[0x03]; + dataEC[7] = encryptedData[0x16]; + + dataEC[8] = encryptedData[0x13]; + dataEC[9] = encryptedData[0x1A]; + dataEC[10] = encryptedData[0x12]; + dataEC[11] = encryptedData[0x0B]; + dataEC[12] = encryptedData[0x04]; + dataEC[13] = encryptedData[0x1D]; + dataEC[14] = encryptedData[0x19]; + dataEC[15] = encryptedData[0x05]; + + // Step 3 - Decrypt AES CBC with new aesKey and iv 2EBD816A5E749A708AE45ADDD84333DE + uint8_t aesKeyIndex = nanoMode & 0x1F; // 32 possible AES keys + uint8_t aesKey[16] = { 0 }; + + char tmpBuffer[33]; + cs_hexdump(0, aesKey, 16, tmpBuffer, sizeof(tmpBuffer)); + cs_log_dbg(D_READER, "INFO: Using AES key index: %02X, value: %s", aesKeyIndex, tmpBuffer); + + if (!get_key(aesKeyIndex, "AES", aesKey, 16)) + { + return EMU_KEY_NOT_FOUND; + } + + struct aes_keys aes; + aes_set_key(&aes, (char *)aesKey); + + uint8_t ivAes[16] = { 0x2E, 0xBD, 0x81, 0x6A, 0x5E, 0x74, 0x9A, 0x70, 0x8A, 0xE4, 0x5A, 0xDD, 0xD8, 0x43, 0x33, 0xDE }; + aes_cbc_decrypt(&aes, dataEC, 16, ivAes); + + // Step 4 - Create CW (a simple swap) + uint8_t offset; + for (offset = 0; offset < 16; offset++) + { + dw[offset] = dataEC[15 - offset]; + } + + return EMU_OK; + } + } + + case 0xED: // Director v5 (September 2016) + { + if (nanoLength != 0x26) + { + cs_log_dbg(D_READER, "WARNING: nanoType ED length (%d) != %d", nanoLength, 0x26); + break; + } + + entitlementId = b2i(4, nanoData); + cs_log_dbg(D_READER, "INFO: Using entitlement id %.4X", entitlementId); + + if (!get_key(entitlementId, "01", ecmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + uint8_t encryptedData[32] = { 0 }; + memcpy(encryptedData, nanoData + 4, 32); + + uint8_t iv[8] = { 0 }; + des_cbc_decrypt(encryptedData, iv, ecmKey, 32); + + dw[0] = encryptedData[0x05]; + dw[1] = encryptedData[0x19]; + dw[2] = encryptedData[0x1D]; + dw[3] = (dw[0] + dw[1] + dw[2]) & 0xFF; + dw[4] = encryptedData[0x0B]; + dw[5] = encryptedData[0x12]; + dw[6] = encryptedData[0x1A]; + dw[7] = (dw[4] + dw[5] + dw[6]) & 0xFF; + dw[8] = encryptedData[0x16]; + dw[9] = encryptedData[0x03]; + dw[10] = encryptedData[0x11]; + dw[11] = (dw[8] + dw[9] + dw[10]) & 0xFF; + dw[12] = encryptedData[0x18]; + dw[13] = encryptedData[0x10]; + dw[14] = encryptedData[0x0E]; + dw[15] = (dw[12] + dw[13] + dw[14]) & 0xFF; + + return EMU_OK; + } + + case 0xEE: // Director v4 + { + if (nanoLength != 0x16) + { + cs_log_dbg(D_READER, "WARNING: nanoType EE length (%d) != %d", nanoLength, 0x16); + break; + } + + entitlementId = b2i(4, nanoData); + cs_log_dbg(D_READER, "INFO: Using entitlement id %.4X", entitlementId); + + if (!get_key(entitlementId, "01", ecmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + memcpy(dw, nanoData + 4 + 8, 8); // even + memcpy(dw + 8, nanoData + 4, 8); // odd + + des_set_key(ecmKey, ks); + + des(dw, ks, 0); + des(dw + 8, ks, 0); + + dw[3] = (dw[0] + dw[1] + dw[2]) & 0xFF; + dw[7] = (dw[4] + dw[5] + dw[6]) & 0xFF; + dw[11] = (dw[8] + dw[9] + dw[10]) & 0xFF; + dw[15] = (dw[12] + dw[13] + dw[14]) & 0xFF; + + return EMU_OK; + } + + default: + cs_log_dbg(D_READER, "WARNING: nanoType %.2X not supported", nanoType); + return EMU_NOT_SUPPORTED; + } + + pos += 2 + nanoLength; + + } while (pos < ecmLen); + + return EMU_NOT_SUPPORTED; +} + +/*************************************************************************************************/ + +/* + * Director EMM emulator + * Supported versions: v4, v5, v6 (same as v5) +*/ + +static const uint8_t MixTable[] = +{ + 0x12, 0x78, 0x4B, 0x19, 0x13, 0x80, 0x2F, 0x84, 0x86, 0x4C, 0x09, 0x53, 0x15, 0x79, 0x6B, 0x49, + 0x10, 0x4D, 0x33, 0x43, 0x18, 0x37, 0x83, 0x38, 0x82, 0x1B, 0x6E, 0x24, 0x2A, 0x85, 0x3C, 0x3D, + 0x5A, 0x58, 0x55, 0x5D, 0x20, 0x41, 0x65, 0x51, 0x0C, 0x45, 0x63, 0x7F, 0x0F, 0x46, 0x21, 0x7C, + 0x2C, 0x61, 0x7E, 0x0A, 0x42, 0x57, 0x35, 0x16, 0x87, 0x3B, 0x4F, 0x40, 0x34, 0x22, 0x26, 0x74, + 0x32, 0x69, 0x44, 0x7A, 0x6A, 0x6D, 0x0D, 0x56, 0x23, 0x2B, 0x5C, 0x72, 0x76, 0x36, 0x28, 0x25, + 0x2E, 0x52, 0x5B, 0x6C, 0x7D, 0x30, 0x0B, 0x5E, 0x47, 0x1F, 0x7B, 0x31, 0x3E, 0x11, 0x77, 0x1E, + 0x60, 0x75, 0x54, 0x27, 0x50, 0x17, 0x70, 0x59, 0x1A, 0x2D, 0x4A, 0x67, 0x3A, 0x5F, 0x68, 0x08, + 0x4E, 0x3F, 0x29, 0x6F, 0x81, 0x71, 0x39, 0x64, 0x48, 0x66, 0x73, 0x14, 0x0E, 0x1D, 0x62, 0x1C +}; + +/* +static void rotate_bytes(uint8_t *in, int8_t n) +{ + if (n > 1) + { + uint8_t *e = in + n - 1; + do + { + uint8_t temp = *in; + *in++ = *e; + *e-- = temp; + } + while (in < e); + } +} +*/ + +static void decrypt_ecm_key(uint8_t *emmKey, uint8_t *tagData, uint8_t *ecmKey) +{ + uint8_t temp, *e, *payLoad, iv[8] = { 0 }; + + //rotate_bytes(emmKey, 8); + + e = emmKey + 8 - 1; + do + { + temp = *emmKey; + *emmKey++ = *e; + *e-- = temp; + } + while (emmKey < e); + + payLoad = tagData + 4 + 5; + des_cbc_decrypt(payLoad, iv, emmKey, 16); + + ecmKey[0] = payLoad[0x0F]; + ecmKey[1] = payLoad[0x01]; + ecmKey[2] = payLoad[0x0B]; + ecmKey[3] = payLoad[0x03]; + ecmKey[4] = payLoad[0x0E]; + ecmKey[5] = payLoad[0x04]; + ecmKey[6] = payLoad[0x0A]; + ecmKey[7] = payLoad[0x08]; +} + +static int8_t parse_emm_nano_tags(uint8_t *data, uint32_t length, uint8_t keyIndex, uint32_t *keysAdded) +{ + uint8_t tagType, tagLength, *tagData, blockIndex, emmKey[8], tagDataDecrypted[16][8]; + uint32_t pos = 0, entitlementId, ks[32]; + int32_t i, k; + char keyValue[17]; + + if (length < 2) + { + return EMU_NOT_SUPPORTED; + } + + while (pos < length) + { + tagType = data[pos]; + tagLength = data[pos+1]; + + if (pos + 2 + tagLength > length) + { + return EMU_CORRUPT_DATA; + } + + tagData = data + pos + 2; + + switch (tagType) + { + case 0xE4: // EMM_TAG_SECURITY_TABLE_DESCRIPTOR (ram emm keys) + { + uint8_t tagMode = data[pos + 2]; + + switch (tagMode) + { + case 0x01: // keySet 01 (MK01) + { + if (tagLength != 0x8A) + { + cs_log_dbg(D_READER, "WARNING: nanoTag E4 length (%d) != %d", tagLength, 0x8A); + return EMU_NOT_SUPPORTED; + } + + if (!get_key(keyIndex, "MK01", emmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + uint8_t iv[8] = { 0 }; + uint8_t *tagPayload = tagData + 2; + des_cbc_decrypt(tagPayload, iv, emmKey, 136); + + for (k = 0; k < 16; k++) // loop 16 keys + { + for (i = 0; i < 8; i++) // loop 8 bytes of key + { + tagDataDecrypted[k][i] = tagPayload[MixTable[8 * k + i]]; + } + } + + blockIndex = tagData[1] & 0x03; + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + for (i = 0; i < 16; i++) + { + emu_set_key('T', (blockIndex << 4) + i, "MK01", tagDataDecrypted[i], 8, 0, NULL, NULL); + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + } + break; + + case 0xFF: // keySet FF (MK) + { + if (tagLength != 0x82) + { + cs_log_dbg(D_READER, "WARNING: nanoTag E4 length (%d) != %d", tagLength, 0x82); + return EMU_NOT_SUPPORTED; + } + + if (!get_key(keyIndex, "MK", emmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + des_set_key(emmKey, ks); + + for (i = 0; i < 16; i++) + { + des(tagData + 2 + (i * 8), ks, 0); + } + + blockIndex = tagData[1] & 0x03; + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + for (i = 0; i < 16; i++) + { + emu_set_key('T', (blockIndex << 4) + i, "MK", tagData + 2 + (i * 8), 8, 0, NULL, NULL); + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + } + break; + + default: + cs_log_dbg(D_READER, "WARNING: nanoTag E4 mode %.2X not supported", tagMode); + return EMU_NOT_SUPPORTED; + } + break; + } + + case 0xE1: // EMM_TAG_EVENT_ENTITLEMENT_DESCRIPTOR (ecm keys) + { + uint8_t tagMode = data[pos + 2 + 4]; + + switch (tagMode) + { + case 0x00: // ecm keys from mode FF + { + if (tagLength != 0x12) + { + cs_log_dbg(D_READER, "WARNING: nanoTag E1 length (%d) != %d", tagLength, 0x12); + return EMU_NOT_SUPPORTED; + } + + entitlementId = b2i(4, tagData); + + if (!get_key(keyIndex, "MK", emmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + des_set_key(emmKey, ks); + des(tagData + 4 + 5, ks, 0); + uint8_t ecmKeyChk[1] = { 0 }; + memcpy(ecmKeyChk, tagData + 4 + 5 + 7, 1); + + if (ecmKeyChk[0] != 0x00) // check if key looks valid (last byte 0x00) + { + cs_log_dbg(D_READER, "Key rejected from EMM (looks invalid)"); + return EMU_KEY_REJECTED; + } + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + if (emu_update_key('T', entitlementId, "01", tagData + 4 + 5, 8, 1, NULL)) + { + (*keysAdded)++; + cs_hexdump(0, tagData + 4 + 5, 8, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: T %.8X 01 %s", entitlementId, keyValue); + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + } + break; + + case 0x01: // ecm keys from mode 01 + { + if (tagLength != 0x1A) + { + cs_log_dbg(D_READER, "WARNING: nanoTag E1 length (%d) != %d", tagLength, 0x1A); + return EMU_NOT_SUPPORTED; + } + + entitlementId = b2i(4, tagData); + + if (!get_key(keyIndex, "MK01", emmKey, 8)) + { + return EMU_KEY_NOT_FOUND; + } + + uint8_t ecmKey[8] = { 0 }; + decrypt_ecm_key(emmKey, tagData, ecmKey); + + if (ecmKey[7] != 0x00) // check if key looks valid (last byte 0x00) + { + cs_log_dbg(D_READER, "Key rejected from EMM (looks invalid)"); + return EMU_KEY_REJECTED; + } + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + if (emu_update_key('T', entitlementId, "01", ecmKey, 8, 1, NULL)) + { + (*keysAdded)++; + cs_hexdump(0, ecmKey, 8, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: T %.8X 01 %s", entitlementId, keyValue); + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + } + break; + + default: + cs_log_dbg(D_READER, "WARNING: nanoTag E1 mode %.2X not supported", tagMode); + return EMU_NOT_SUPPORTED; + } + break; + } + + default: + cs_log_dbg(D_READER, "WARNING: nanoTag %.2X not supported", tagType); + return EMU_NOT_SUPPORTED; + } + + pos += 2 + tagLength; + } + + return EMU_OK; +} + +static int8_t parse_emm_nano_data(uint8_t *data, uint32_t *nanoLength, uint32_t maxLength, + uint8_t keyIndex, uint32_t *keysAdded) +{ + uint32_t pos = 0; + uint16_t sectionLength; + int8_t ret = EMU_OK; + + if (maxLength < 2) + { + (*nanoLength) = 0; + return EMU_NOT_SUPPORTED; + } + + sectionLength = ((data[pos] << 8) | data[pos + 1]) & 0x0FFF; + + if (pos + 2 + sectionLength > maxLength) + { + (*nanoLength) = pos; + return EMU_CORRUPT_DATA; + } + + ret = parse_emm_nano_tags(data + pos + 2, sectionLength, keyIndex, keysAdded); + + pos += 2 + sectionLength; + + (*nanoLength) = pos; + return ret; +} + +int8_t director_emm(uint8_t *emm, uint32_t *keysAdded) +{ + uint8_t keyIndex, ret = EMU_OK; + uint16_t emmLen = SCT_LEN(emm); + uint32_t pos = 3; + uint32_t permissionDataType; + uint32_t nanoLength = 0; + + while (pos < emmLen && !ret) + { + permissionDataType = emm[pos]; + + switch (permissionDataType) + { + case 0x00: + break; + + case 0x01: + pos += 0x0A; + break; + + case 0x02: + pos += 0x26; + break; + + default: + cs_log_dbg(D_READER, "ERROR: unknown permissionDataType %.2X (pos: %d)", permissionDataType, pos); + return EMU_NOT_SUPPORTED; + } + + if (pos + 6 >= emmLen) + { + return EMU_CORRUPT_DATA; + } + + keyIndex = emm[pos + 1]; + + // EMM validation + // Copy payload checksum bytes and then set them to zero, + // so they do not affect the calculated checksum. + uint16_t payloadChecksum = (emm[pos + 2] << 8) | emm[pos + 3]; + memset(emm + pos + 2, 0, 2); + uint16_t calculatedChecksum = calculate_checksum(emm + 3, emmLen - 3); + + if (calculatedChecksum != payloadChecksum) + { + cs_log_dbg(D_READER, "EMM checksum error (%.4X instead of %.4X)", calculatedChecksum, payloadChecksum); + return EMU_CHECKSUM_ERROR; + } + // End of EMM validation + + pos += 0x04; + ret = parse_emm_nano_data(emm + pos, &nanoLength, emmLen - pos, keyIndex, keysAdded); + pos += nanoLength; + } + + return ret; +} + +#endif // WITH_EMU diff --git a/module-emulator-director.h b/module-emulator-director.h new file mode 100644 index 0000000..de430cf --- /dev/null +++ b/module-emulator-director.h @@ -0,0 +1,11 @@ +#ifndef MODULE_EMULATOR_DIRECTOR_H +#define MODULE_EMULATOR_DIRECTOR_H + +#ifdef WITH_EMU + +int8_t director_ecm(uint8_t *ecm, uint8_t *dw); +int8_t director_emm(uint8_t *emm, uint32_t *keysAdded); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_DIRECTOR_H diff --git a/module-emulator-irdeto.c b/module-emulator-irdeto.c new file mode 100644 index 0000000..c9df970 --- /dev/null +++ b/module-emulator-irdeto.c @@ -0,0 +1,602 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "cscrypt/des.h" +#include "module-emulator-osemu.h" +#include "oscam-string.h" + +static inline void xxor(uint8_t *data, int32_t len, const uint8_t *v1, const uint8_t *v2) +{ + uint32_t i; + + switch (len) + { + case 16: + for (i = 0; i < 16; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + + case 8: + for (i = 0; i < 8; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + + case 4: + for (i = 0; i < 4; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + + default: + while (len--) + { + *data++ = *v1++ ^ *v2++; + } + break; + } +} + +// Irdeto EMU +static int8_t get_key(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex, + uint8_t isCriticalKey, uint32_t *keyRef) +{ + char keyStr[EMU_MAX_CHAR_KEYNAME]; + + if (*keyRef > 0xFF) + { + return 0; + } + + snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex); + + if (emu_find_key('I', ident, 0, keyStr, buf, 16, *keyRef > 0 ? 0 : isCriticalKey, *keyRef, 0, NULL)) + { + (*keyRef)++; + return 1; + } + + return 0; +} + +static void irdeto2_encrypt(uint8_t *data, const uint8_t *seed, const uint8_t *key, int32_t len) +{ + int32_t i; + const uint8_t *tmp = seed; + uint32_t ks1[32], ks2[32]; + + des_set_key(key, ks1); + des_set_key(key + 8, ks2); + + len &= ~7; + + for (i = 0; i + 7 < len; i += 8) + { + xxor(&data[i], 8, &data[i], tmp); + tmp = &data[i]; + des(&data[i], ks1, 1); + des(&data[i], ks2, 0); + des(&data[i], ks1, 1); + } +} + +static void irdeto2_decrypt(uint8_t *data, const uint8_t *seed, const uint8_t *key, int32_t len) +{ + int32_t i, n = 0; + uint8_t buf[2][8]; + uint32_t ks1[32], ks2[32]; + + des_set_key(key, ks1); + des_set_key(key + 8, ks2); + + len &= ~7; + + memcpy(buf[n], seed, 8); + + for (i = 0; i + 7 < len; i += 8, data += 8, n ^= 1) + { + memcpy(buf[1 - n], data, 8); + des(data, ks1, 0); + des(data, ks2, 1); + des(data, ks1, 0); + xxor(data, 8, data, buf[n]); + } +} + +static int8_t calculate_hash(const uint8_t *key, const uint8_t *iv, const uint8_t *data, int32_t len) +{ + int32_t l, y; + uint8_t cbuff[32]; + uint32_t ks1[32], ks2[32]; + + des_set_key(key, ks1); + des_set_key(key + 8, ks2); + + memset(cbuff, 0, sizeof(cbuff)); + + len -= 8; + + for (y = 0; y < len; y += 8) + { + if (y < len - 8) + { + xxor(cbuff, 8, cbuff, &data[y]); + } + else + { + l = len - y; + xxor(cbuff, l, cbuff, &data[y]); + xxor(cbuff + l, 8 - l, cbuff + l, iv + 8); + } + + des(cbuff, ks1, 1); + des(cbuff, ks2, 0); + des(cbuff, ks1, 1); + } + + return memcmp(cbuff, &data[len], 8) == 0; +} + +int8_t irdeto2_ecm(uint16_t caid, uint8_t *oecm, uint8_t *dw) +{ + uint8_t keyNr = 0, length, end, key[16], okeySeed[16], keySeed[16], keyIV[16], tmp[16]; + uint8_t ecmCopy[EMU_MAX_ECM_LEN], *ecm = oecm; + uint16_t ecmLen = SCT_LEN(ecm); + uint32_t key0Ref, keySeedRef, keyIVRef, ident, i, j, l; + + if (ecmLen < 12) + { + return EMU_NOT_SUPPORTED; + } + + length = ecm[11]; + keyNr = ecm[9]; + ident = ecm[8] | caid << 8; + + if (ecmLen < length + 12) + { + return EMU_NOT_SUPPORTED; + } + + key0Ref = 0; + + while (get_key(key, ident, '0', keyNr, 1, &key0Ref)) + { + keySeedRef = 0; + + while (get_key(okeySeed, ident, 'M', 1, 1, &keySeedRef)) + { + keyIVRef = 0; + + while (get_key(keyIV, ident, 'M', 2, 1, &keyIVRef)) + { + memcpy(keySeed, okeySeed, 16); + memcpy(ecmCopy, oecm, ecmLen); + + ecm = ecmCopy; + memset(tmp, 0, 16); + irdeto2_encrypt(keySeed, tmp, key, 16); + + ecm += 12; + irdeto2_decrypt(ecm, keyIV, keySeed, length); + + i = (ecm[0] & 7) + 1; + end = length - 8 < 0 ? 0 : length - 8; + + while (i < end) + { + l = ecm[i + 1] ? (ecm[i + 1] & 0x3F) + 2 : 1; + + switch (ecm[i]) + { + case 0x10: + case 0x50: + if (l == 0x13 && i <= length - 8 - l) + { + irdeto2_decrypt(&ecm[i + 3], keyIV, key, 16); + } + break; + + case 0x78: + if (l == 0x14 && i <= length - 8 - l) + { + irdeto2_decrypt(&ecm[i + 4], keyIV, key, 16); + } + break; + } + i += l; + } + + i = (ecm[0] & 7) + 1; + + if (calculate_hash(keySeed, keyIV, ecm - 6, length + 6)) + { + while (i < end) + { + l = ecm[i + 1] ? (ecm[i + 1] & 0x3F) + 2 : 1; + + switch (ecm[i]) + { + case 0x78: + { + if (l == 0x14 && i <= length - 8 - l) + { + memcpy(dw, &ecm[i + 4], 16); + + for (j = 0; j < 16; j += 4) // fix dw checksum bytes + { + dw[j + 3] = (dw[j] + dw[j + 1] + dw[j + 2]) & 0xFF; + } + return EMU_OK; + } + } + } + i += l; + } + } + } + + if (keyIVRef == 0) + { + return EMU_KEY_NOT_FOUND; + } + } + + if (keySeedRef == 0) + { + return EMU_KEY_NOT_FOUND; + } + } + + if (key0Ref == 0) + { + return EMU_KEY_NOT_FOUND; + } + + return EMU_NOT_SUPPORTED; +} + +// Irdeto2 EMM EMU +static int8_t do_emm_type_op(uint32_t ident, uint8_t *emm, uint8_t *keySeed, uint8_t *keyIV, uint8_t *keyPMK, + uint16_t emmLen, uint8_t startOffset, uint8_t length, uint32_t *keysAdded) +{ + uint8_t tmp[16]; + uint32_t end, i, l; + char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36]; + + memset(tmp, 0, 16); + irdeto2_encrypt(keySeed, tmp, keyPMK, 16); + irdeto2_decrypt(&emm[startOffset], keyIV, keySeed, length); + + i = 16; + end = startOffset + (length - 8 < 0 ? 0 : length - 8); + + while (i < end) + { + l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1; + + switch (emm[i]) + { + case 0x10: + case 0x50: + if (l == 0x13 && i <= startOffset + length - 8 - l) + { + irdeto2_decrypt(&emm[i + 3], keyIV, keyPMK, 16); + } + break; + + case 0x78: + if (l == 0x14 && i <= startOffset + length - 8 - l) + { + irdeto2_decrypt(&emm[i + 4], keyIV, keyPMK, 16); + } + break; + } + i += l; + } + + memmove(emm + 6, emm + 7, emmLen - 7); + + i = 15; + end = startOffset + (length - 9 < 0 ? 0 : length - 9); + + if (calculate_hash(keySeed, keyIV, emm + 3, emmLen - 4)) + { + while (i < end) + { + l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1; + + switch (emm[i]) + { + case 0x10: + case 0x50: + { + if (l == 0x13 && i <= startOffset + length - 9 - l) + { + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%02X", emm[i + 2] >> 2); + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + emu_set_key('I', ident, keyName, &emm[i + 3], 16, 1, NULL, NULL); + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + (*keysAdded)++; + cs_hexdump(0, &emm[i + 3], 16, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: I %06X %s %s", ident, keyName, keyValue); + } + } + } + i += l; + } + + if (*keysAdded > 0) + { + return 0; + } + } + + return 1; +} + +static int8_t do_emm_type_pmk(uint32_t ident, uint8_t *emm, uint8_t *keySeed, uint8_t *keyIV, uint8_t *keyPMK, + uint16_t emmLen, uint8_t startOffset, uint8_t length, uint32_t *keysAdded) +{ + uint32_t end, i, j, l; + char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36]; + + irdeto2_decrypt(&emm[startOffset], keyIV, keySeed, length); + + i = 13; + end = startOffset + (length - 8 < 0 ? 0 : length - 8); + + while (i < end) + { + l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1; + + switch (emm[i]) + { + case 0x10: + case 0x50: + if (l == 0x13 && i <= startOffset + length - 8 - l) + { + irdeto2_decrypt(&emm[i + 3], keyIV, keyPMK, 16); + } + break; + + case 0x78: + if (l == 0x14 && i <= startOffset + length - 8 - l) + { + irdeto2_decrypt(&emm[i + 4], keyIV, keyPMK, 16); + } + break; + + case 0x68: + if (l == 0x26 && i <= startOffset + length - 8 - l) + { + irdeto2_decrypt(&emm[i + 3], keyIV, keyPMK, 16 * 2); + } + break; + } + i += l; + } + + memmove(emm + 7, emm + 9, emmLen - 9); + + i = 11; + end = startOffset + (length - 10 < 0 ? 0 : length - 10); + + if (calculate_hash(keySeed, keyIV, emm + 3, emmLen - 5)) + { + while (i < end) + { + l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1; + + switch (emm[i]) + { + case 0x68: + { + if (l == 0x26 && i <= startOffset + length - 10 - l) + { + for (j = 0; j < 2; j++) + { + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "M%01X", 3 + j); + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + emu_set_key('I', ident, keyName, &emm[i + 3 + j * 16], 16, 1, NULL, NULL); + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + (*keysAdded)++; + cs_hexdump(0, &emm[i + 3 + j * 16], 16, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: I %06X %s %s", ident, keyName, keyValue); + } + } + } + } + i += l; + } + + if (*keysAdded > 0) + { + return 0; + } + } + + return 1; +} + +static const uint8_t fausto_xor[16] = +{ + 0x22, 0x58, 0xBD, 0x85, 0x2E, 0x8E, 0x52, 0x80, + 0xA3, 0x79, 0x98, 0x69, 0x68, 0xE2, 0xD8, 0x4D +}; + +int8_t irdeto2_emm(uint16_t caid, uint8_t *oemm, uint32_t *keysAdded) +{ + uint8_t length, okeySeed[16], keySeed[16], keyIV[16], keyPMK[16], startOffset, emmType; + uint8_t emmCopy[EMU_MAX_EMM_LEN], *emm = oemm; + uint16_t emmLen = SCT_LEN(emm); + uint32_t ident, keySeedRef, keyIVRef, keyPMK0Ref, keyPMK1Ref, keyPMK0ERef, keyPMK1ERef; + + if (emmLen < 11) + { + return EMU_NOT_SUPPORTED; + } + + if (emm[3] == 0xC3 || emm[3] == 0xCB) + { + emmType = 2; + startOffset = 11; + } + else + { + emmType = 1; + startOffset = 10; + } + + ident = emm[startOffset - 2] | caid << 8; + length = emm[startOffset - 1]; + + if (emmLen < length + startOffset) + { + return EMU_NOT_SUPPORTED; + } + + keySeedRef = 0; + + while (get_key(okeySeed, ident, 'M', emmType == 1 ? 0 : 0xA, 1, &keySeedRef)) + { + keyIVRef = 0; + + while (get_key(keyIV, ident, 'M', 2, 1, &keyIVRef)) + { + keyPMK0Ref = 0; + keyPMK1Ref = 0; + keyPMK0ERef = 0; + keyPMK1ERef = 0; + + while (get_key(keyPMK, ident, 'M', emmType == 1 ? 3 : 0xB, 1, &keyPMK0Ref)) + { + memcpy(keySeed, okeySeed, 16); + memcpy(emmCopy, oemm, emmLen); + emm = emmCopy; + + if (emmType == 1) + { + if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) + { + return EMU_OK; + } + } + else + { + if (do_emm_type_pmk(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) + { + return EMU_OK; + } + } + } + + if (emmType == 1) + { + while (get_key(keyPMK, ident, 'M', 4, 1, &keyPMK1Ref)) + { + memcpy(keySeed, okeySeed, 16); + memcpy(emmCopy, oemm, emmLen); + emm = emmCopy; + + if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) + { + return EMU_OK; + } + } + + while (get_key(keyPMK, ident, 'M', 5, 1, &keyPMK0ERef)) + { + xxor(keyPMK, 16, keyPMK, fausto_xor); + memcpy(keySeed, okeySeed, 16); + memcpy(emmCopy, oemm, emmLen); + emm = emmCopy; + + if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) + { + return EMU_OK; + } + } + + while (get_key(keyPMK, ident, 'M', 6, 1, &keyPMK1ERef)) + { + xxor(keyPMK, 16, keyPMK, fausto_xor); + memcpy(keySeed, okeySeed, 16); + memcpy(emmCopy, oemm, emmLen); + emm = emmCopy; + + if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0) + { + return EMU_OK; + } + } + } + + if (keyPMK0Ref == 0 && keyPMK1Ref == 0 && keyPMK0ERef == 0 && keyPMK1ERef == 0) + { + return EMU_KEY_NOT_FOUND; + } + } + + if (keyIVRef == 0) + { + return EMU_KEY_NOT_FOUND; + } + } + + if (keySeedRef == 0) + { + return 2; + } + + return EMU_NOT_SUPPORTED; +} + +int8_t irdeto2_get_hexserial(uint16_t caid, uint8_t *hexserial) +{ + uint32_t i, len; + KeyDataContainer *KeyDB; + KeyData *tmpKeyData; + + KeyDB = emu_get_key_container('I'); + + if (KeyDB == NULL) + { + return 0; + } + + for (i = 0; i < KeyDB->keyCount; i++) + { + if (KeyDB->EmuKeys[i].provider >> 8 != caid) + { + continue; + } + + if (strcmp(KeyDB->EmuKeys[i].keyName, "MC")) + { + continue; + } + + tmpKeyData = &KeyDB->EmuKeys[i]; + len = tmpKeyData->keyLength; + + if (len > 3) + { len = 3; } + + memcpy(hexserial + (3 - len), tmpKeyData->key, len); + return 1; + } + + return 0; +} + +#endif // WITH_EMU diff --git a/module-emulator-irdeto.h b/module-emulator-irdeto.h new file mode 100644 index 0000000..e13af0a --- /dev/null +++ b/module-emulator-irdeto.h @@ -0,0 +1,15 @@ +#ifndef MODULE_EMULATOR_IRDETO_H +#define MODULE_EMULATOR_IRDETO_H + +#ifdef WITH_EMU + +int8_t irdeto2_ecm(uint16_t caid, uint8_t *oecm, uint8_t *dw); +int8_t irdeto2_emm(uint16_t caid, uint8_t *oemm, uint32_t *keysAdded); + +// hexserial must be of type "uint8_t hexserial[3]" +// returns 0 on error, 1 on success +int8_t irdeto2_get_hexserial(uint16_t caid, uint8_t *hexserial); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_IRDETO_H diff --git a/module-emulator-nagravision.c b/module-emulator-nagravision.c new file mode 100644 index 0000000..a097871 --- /dev/null +++ b/module-emulator-nagravision.c @@ -0,0 +1,376 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "cscrypt/bn.h" +#include "cscrypt/des.h" +#include "cscrypt/idea.h" +#include "module-emulator-osemu.h" + +static void reverse_mem(uint8_t *in, int32_t len) +{ + uint8_t temp; + int32_t i; + + for (i = 0; i < (len / 2); i++) + { + temp = in[i]; + in[i] = in[len - i - 1]; + in[len - i - 1] = temp; + } +} + +static void reverse_mem_in_out(uint8_t *out, const uint8_t *in, int32_t n) +{ + if (n > 0) + { + out += n; + do + { + *(--out) = *(in++); + } + while (--n); + } +} + +static int8_t rsa_input(BIGNUM *d, const uint8_t *in, int32_t n, int8_t le) +{ + int8_t result = 0; + + if (le) + { + uint8_t *tmp = (uint8_t *)malloc(sizeof(uint8_t) * n); + + if (tmp == NULL) + { + return 0; + } + + reverse_mem_in_out(tmp, in, n); + result = BN_bin2bn(tmp, n, d) != 0; + free(tmp); + } + else + { + result = BN_bin2bn(in, n, d) != 0; + } + + return result; +} + +static int32_t rsa_output(uint8_t *out, int32_t n, BIGNUM *r, int8_t le) +{ + int32_t s = BN_num_bytes(r); + + if (s > n) + { + uint8_t *buff = (uint8_t *)malloc(sizeof(uint8_t) * s); + + if (buff == NULL) + { + return 0; + } + + BN_bn2bin(r, buff); + memcpy(out, buff + s - n, n); + free(buff); + } + else if (s < n) + { + int32_t l = n - s; + + memset(out, 0, l); + BN_bn2bin(r, out + l); + } + else + { + BN_bn2bin(r, out); + } + + if (le) + { + reverse_mem(out, n); + } + + return s; +} + +static int32_t emu_rsa(uint8_t *out, const uint8_t *in, int32_t n, BIGNUM *exp, BIGNUM *mod, int8_t le) +{ + BN_CTX *ctx; + BIGNUM *r, *d; + int32_t result = 0; + + ctx = BN_CTX_new(); + r = BN_new(); + d = BN_new(); + + if (rsa_input(d, in, n, le) && BN_mod_exp(r, d, exp, mod, ctx)) + { + result = rsa_output(out, n, r, le); + } + + BN_free(d); + BN_free(r); + BN_CTX_free(ctx); + + return result; +} + +// Nagra EMU + +static int8_t get_key(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex, uint8_t isCriticalKey) +{ + char keyStr[EMU_MAX_CHAR_KEYNAME]; + snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex); + + if (emu_find_key('N', ident, 0, keyStr, buf, keyName == 'M' ? 64 : 16, isCriticalKey, 0, 0, NULL)) + { + return 1; + } + + return 0; +} + +static int8_t nagra2_signature(const uint8_t *vkey, const uint8_t *sig, const uint8_t *msg, int32_t len) +{ + uint8_t buff[16], iv[8]; + int32_t i, j; + + memcpy(buff, vkey, sizeof(buff)); + + for (i = 0; i + 7 < len; i += 8) + { + IDEA_KEY_SCHEDULE ek; + + idea_set_encrypt_key(buff, &ek); + memcpy(buff, buff + 8, 8); + memset(iv, 0, sizeof(iv)); + idea_cbc_encrypt(msg + i, buff + 8, 8, &ek, iv, IDEA_ENCRYPT); + + for (j = 7; j >= 0; j--) + { + buff[j + 8] ^= msg[i + j]; + } + } + + buff[8] &= 0x7F; + + return (memcmp(sig, buff + 8, 8) == 0); +} + +static int8_t decrypt_ecm(uint8_t *in, uint8_t *out, const uint8_t *key, int32_t len, + const uint8_t *vkey, uint8_t *keyM) +{ + BIGNUM *exp, *mod; + uint8_t iv[8]; + int32_t i = 0, sign = in[0] & 0x80; + uint8_t binExp = 3; + int8_t result = 1; + + exp = BN_new(); + mod = BN_new(); + BN_bin2bn(&binExp, 1, exp); + BN_bin2bn(keyM, 64, mod); + + if (emu_rsa(out, in + 1, 64, exp, mod, 1) <= 0) + { + BN_free(exp); + BN_free(mod); + return 0; + } + + out[63] |= sign; + + if (len > 64) + { + memcpy(out + 64, in + 65, len - 64); + } + + memset(iv, 0, sizeof(iv)); + + if (in[0] & 0x04) + { + uint8_t key1[8], key2[8]; + + reverse_mem_in_out(key1, &key[0], 8); + reverse_mem_in_out(key2, &key[8], 8); + + for (i = 7; i >= 0; i--) + { + reverse_mem(out + 8 * i, 8); + } + + des_ede2_cbc_decrypt(out, iv, key1, key2, len); + + for (i = 7; i >= 0; i--) + { + reverse_mem(out + 8 * i, 8); + } + } + else + { + IDEA_KEY_SCHEDULE ek; + + idea_set_encrypt_key(key, &ek); + idea_cbc_encrypt(out, out, len & ~7, &ek, iv, IDEA_DECRYPT); + } + + reverse_mem(out, 64); + + if (result && emu_rsa(out, out, 64, exp, mod, 0) <= 0) + { + result = 0; + } + + if (result && vkey && !nagra2_signature(vkey, out, out + 8, len - 8)) + { + result = 0; + } + + BN_free(exp); + BN_free(mod); + return result; +} + +int8_t nagra2_ecm(uint8_t *ecm, uint8_t *dw) +{ + int8_t useVerifyKey = 0; + int32_t l = 0, s; + + uint8_t cmdLen, ideaKeyNr, *dec, ideaKey[16], vKey[16], m1Key[64], mecmAlgo = 0; + uint16_t i = 0, ecmLen = SCT_LEN(ecm); + uint32_t ident, identMask, tmp1, tmp2, tmp3; + + if (ecmLen < 8) + { + return EMU_NOT_SUPPORTED; + } + + cmdLen = ecm[4] - 5; + ident = (ecm[5] << 8) + ecm[6]; + ideaKeyNr = (ecm[7] & 0x10) >> 4; + + if (ideaKeyNr) + { + ideaKeyNr = 1; + } + + if (ident == 1283 || ident == 1285 || ident == 1297) + { + ident = 1281; + } + + if (cmdLen <= 63 || ecmLen < cmdLen + 10) + { + return EMU_NOT_SUPPORTED; + } + + if (!get_key(ideaKey, ident, '0', ideaKeyNr, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + if (get_key(vKey, ident, 'V', 0, 0)) + { + useVerifyKey = 1; + } + + if (!get_key(m1Key, ident, 'M', 1, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + reverse_mem(m1Key, 64); + + dec = (uint8_t *)malloc(sizeof(uint8_t) * cmdLen); + if (dec == NULL) + { + return EMU_OUT_OF_MEMORY; + } + + if (!decrypt_ecm(ecm + 9, dec, ideaKey, cmdLen, useVerifyKey ? vKey : 0, m1Key)) + { + free(dec); + return EMU_NOT_SUPPORTED; + } + + for (i = (dec[14] & 0x10) ? 16 : 20; i < cmdLen && l != 3; ) + { + switch (dec[i]) + { + case 0x10: + case 0x11: + if (i + 10 < cmdLen && dec[i + 1] == 0x09) + { + s = (~dec[i]) & 1; + mecmAlgo = dec[i + 2] & 0x60; + memcpy(dw + (s << 3), &dec[i + 3], 8); + i += 11; + l |= (s + 1); + } + else + { + i++; + } + break; + + case 0x00: + i += 2; + break; + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0xB0: + if (i + 1 < cmdLen) + { + i += dec[i + 1] + 2; + } + else + { + i++; + } + break; + + default: + i++; + continue; + } + } + + free(dec); + + if (l != 3) + { + return EMU_NOT_SUPPORTED; + } + + if (mecmAlgo > 0) + { + return EMU_NOT_SUPPORTED; + } + + identMask = ident & 0xFF00; + + if (identMask == 0x1100 || identMask == 0x500 || identMask == 0x3100) + { + memcpy(&tmp1, dw, 4); + memcpy(&tmp2, dw + 4, 4); + memcpy(&tmp3, dw + 12, 4); + memcpy(dw, dw + 8, 4); + memcpy(dw + 4, &tmp3, 4); + memcpy(dw + 8, &tmp1, 4); + memcpy(dw + 12, &tmp2, 4); + } + + return EMU_OK; +} + +#endif // WITH_EMU diff --git a/module-emulator-nagravision.h b/module-emulator-nagravision.h new file mode 100644 index 0000000..31810eb --- /dev/null +++ b/module-emulator-nagravision.h @@ -0,0 +1,10 @@ +#ifndef MODULE_EMULATOR_NAGRAVISION_H +#define MODULE_EMULATOR_NAGRAVISION_H + +#ifdef WITH_EMU + +int8_t nagra2_ecm(uint8_t *ecm, uint8_t *dw); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_NAGRAVISION_H diff --git a/module-emulator-omnicrypt.c b/module-emulator-omnicrypt.c new file mode 100644 index 0000000..805926d --- /dev/null +++ b/module-emulator-omnicrypt.c @@ -0,0 +1,72 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "module-emulator-osemu.h" +#include "module-emulator-omnicrypt.h" +#include "oscam-aes.h" +#include "oscam-string.h" + + +static inline int8_t get_ecm_key(uint16_t provider, uint8_t parity, uint8_t *key) +{ + return emu_find_key('O', provider, 0, parity == 0 ? "00" : "01", key, 16, 1, 0, 0, NULL); +} + +int8_t omnicrypt_ecm(uint8_t *ecm, uint8_t *dw) +{ + uint8_t section_syntax_indicator, session_key[16], session_key_parity, position; + uint16_t private_section_length, session_key_id, payload_length; + struct aes_keys aes; + + section_syntax_indicator = ecm[1] >> 7; + if (section_syntax_indicator != 0) // The private_data_bytes immediately follow the private_section_length field + { + cs_log("ECM section syntax indicator %d not supported", section_syntax_indicator); + return EMU_NOT_SUPPORTED; + } + + private_section_length = b2i(2, ecm + 1) & 0x0FFF; + if (private_section_length != 0x2D) + { + cs_log("ECM has an unsupported private section length of %d", private_section_length); + return EMU_NOT_SUPPORTED; + } + + session_key_parity = ecm[3] & 0x01; + session_key_id = b2i(2, ecm + 4); + + if (!get_ecm_key(session_key_id, session_key_parity, session_key)) + { + return EMU_KEY_NOT_FOUND; + } + aes_set_key(&aes, (char *)session_key); + + payload_length = b2i(2, ecm + 6) & 0x0FFF; + if (payload_length != 0x28) + { + cs_log("ECM has an unsupported payload length of %d", payload_length); + return EMU_NOT_SUPPORTED; + } + + for (position = 8; position + 1 < payload_length; position += 4 + 16) // Run twice for odd, even CW + { + uint8_t parity = ecm[position + 1] & 0x01; + uint8_t length = ecm[position + 3]; + + if (length != 16) + { + cs_log("CW %d has an unsupported length of %d", parity, length); + return EMU_NOT_SUPPORTED; + } + + aes_decrypt(&aes, ecm + position + 4, 16); + memcpy(dw + parity * 8, ecm + position + 4, 8); // Copy the first 8 bytes (rest are zeros) + } + + return EMU_OK; +} + +#endif // WITH_EMU diff --git a/module-emulator-omnicrypt.h b/module-emulator-omnicrypt.h new file mode 100644 index 0000000..3af8915 --- /dev/null +++ b/module-emulator-omnicrypt.h @@ -0,0 +1,10 @@ +#ifndef MODULE_EMULATOR_OMNICRYPT_H +#define MODULE_EMULATOR_OMNICRYPT_H + +#ifdef WITH_EMU + +int8_t omnicrypt_ecm(uint8_t *ecm, uint8_t *dw); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_OMNICRYPT_H diff --git a/module-emulator-osemu.c b/module-emulator-osemu.c new file mode 100644 index 0000000..a3f8db3 --- /dev/null +++ b/module-emulator-osemu.c @@ -0,0 +1,986 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "oscam-string.h" +#include "module-streamrelay.h" +#include "module-emulator-osemu.h" +#include "module-emulator-biss.h" +#include "module-emulator-cryptoworks.h" +#include "module-emulator-director.h" +#include "module-emulator-irdeto.h" +#include "module-emulator-nagravision.h" +#include "module-emulator-omnicrypt.h" +#include "module-emulator-powervu.h" +#include "module-emulator-viaccess.h" + +// Shared functions + +int8_t is_valid_dcw(uint8_t *dw) +{ + uint8_t i; + + for (i = 0; i < 8; i+= 4) + { + if (((dw[i] + dw[i + 1] + dw[i + 2]) & 0xFF) != dw[i + 3]) + { + return 0; + } + } + + return 1; +} + +int8_t char_to_bin(uint8_t *out, const char *in, uint32_t inLen) +{ + uint32_t i, tmp; + + for (i = 0; i < inLen / 2; i++) + { + if (sscanf(in + i * 2, "%02X", &tmp) != 1) + { + return 0; + } + out[i] = (uint8_t)tmp; + } + return 1; +} + +void date_to_str(char *dateStr, uint8_t len, int8_t offset, uint8_t format) +{ + // Creates a formatted date string for use in various functions. + // A positive or negative time offset (in hours) can be set as well + // as the format of the output string. + + time_t rawtime; + struct tm timeinfo; + + time(&rawtime); + rawtime += (time_t) offset * 60 * 60; // Add a positive or negative offset + localtime_r(&rawtime, &timeinfo); + + switch (format) + { + case 1: + strftime(dateStr, len, "%c", &timeinfo); + break; + + case 2: + strftime(dateStr, len, "%F @ %R", &timeinfo); + break; + + case 3: + strftime(dateStr, len, "%y%m%d%H", &timeinfo); + break; + } +} + +/* + * Key DB + * + * The Emu reader gets keys from the OSCcam-Emu binary and the "SoftCam.Key" file. + * + * The keys are stored in structures of type "KeyDataContainer", one per CAS. Each + * container points to a dynamically allocated array of type "KeyData", which holds + * the actual keys. The array initially holds up to 64 keys (64 * KeyData), and it + * is expanded by 16 every time it's filled with keys. The "KeyDataContainer" also + * includes info about the number of keys it contains ("KeyCount") and the maximum + * number of keys it can store ("KeyMax"). + * + * The "KeyData" structure, on the other hand, stores the actual key information, + * including the "identifier", "provider", "keyName", "key" and "keyLength". There + * is also a "nextKey" pointer to a similar "KeyData" structure which is only used + * for Irdeto multiple keys, in a linked list style structure. For all other CAS, + * the "nextKey" is a "NULL" pointer. + * + * For storing keys, the "SetKey" function is used. Duplicate keys are not allowed. + * When storing a key that is already present in the database, its "key" value is + * updated with the new one. For reading keys from the database, the "FindKey" + * function is used. To delete all keys in a container, the "DeleteKeysInContainer" + * function can be called. +*/ + +char *emu_keyfile_path = NULL; + +void emu_set_keyfile_path(const char *path) +{ + uint32_t pathLength; + + if (emu_keyfile_path != NULL) + { + free(emu_keyfile_path); + } + + pathLength = cs_strlen(path); + emu_keyfile_path = (char *)malloc(pathLength + 1); + if (emu_keyfile_path == NULL) + { + return; + } + cs_strncpy(emu_keyfile_path, path, pathLength + 1); +} + +KeyDataContainer CwKeys = { NULL, 0, 0 }; +KeyDataContainer ViKeys = { NULL, 0, 0 }; +KeyDataContainer NagraKeys = { NULL, 0, 0 }; +KeyDataContainer IrdetoKeys = { NULL, 0, 0 }; +KeyDataContainer BissSWs = { NULL, 0, 0 }; +KeyDataContainer Biss2Keys = { NULL, 0, 0 }; +KeyDataContainer OmnicryptKeys = { NULL, 0, 0 }; +KeyDataContainer PowervuKeys = { NULL, 0, 0 }; +KeyDataContainer TandbergKeys = { NULL, 0, 0 }; +KeyDataContainer StreamKeys = { NULL, 0, 0 }; + +KeyDataContainer *emu_get_key_container(char identifier) +{ + switch (identifier) + { + case 'W': + return &CwKeys; + case 'V': + return &ViKeys; + case 'N': + return &NagraKeys; + case 'I': + return &IrdetoKeys; + case 'F': + return &BissSWs; + case 'G': + return &Biss2Keys; + case 'O': + return &OmnicryptKeys; + case 'P': + return &PowervuKeys; + case 'T': + return &TandbergKeys; + case 'A': + return &StreamKeys; + default: + return NULL; + } +} + +static void write_key_to_file(char identifier, uint32_t provider, const char *keyName, uint8_t *key, + uint32_t keyLength, char *comment) +{ + char line[1200], dateText[100], filename[EMU_KEY_FILENAME_MAX_LEN + 1]; + char *path, *filepath, *keyValue; + uint32_t pathLength; + uint8_t fileNameLen = cs_strlen(EMU_KEY_FILENAME); + struct dirent *pDirent; + DIR *pDir; + FILE *file = NULL; + + pathLength = cs_strlen(emu_keyfile_path); + path = (char *)malloc(pathLength + 1); + if (path == NULL) + { + return; + } + cs_strncpy(path, emu_keyfile_path, pathLength + 1); + + pathLength = cs_strlen(path); + if (pathLength >= fileNameLen && strcasecmp(path + pathLength - fileNameLen, EMU_KEY_FILENAME) == 0) + { + // cut file name + path[pathLength - fileNameLen] = '\0'; + } + + pathLength = cs_strlen(path); + if (path[pathLength - 1] == '/' || path[pathLength - 1] == '\\') + { + // cut trailing / + path[pathLength - 1] = '\0'; + } + + pDir = opendir(path); + if (pDir == NULL) + { + cs_log("Cannot open key file path: %s", path); + free(path); + return; + } + + while ((pDirent = readdir(pDir)) != NULL) + { + if (strcasecmp(pDirent->d_name, EMU_KEY_FILENAME) == 0) + { + cs_strncpy(filename, pDirent->d_name, sizeof(filename)); + break; + } + } + closedir(pDir); + + if (pDirent == NULL) + { + cs_strncpy(filename, EMU_KEY_FILENAME, sizeof(filename)); + } + + pathLength = cs_strlen(path) + 1 + cs_strlen(filename) + 1; + filepath = (char *)malloc(pathLength); + if (filepath == NULL) + { + free(path); + return; + } + snprintf(filepath, pathLength, "%s/%s", path, filename); + free(path); + + cs_log("Writing key file: %s", filepath); + + file = fopen(filepath, "a"); + free(filepath); + if (file == NULL) + { + return; + } + + date_to_str(dateText, sizeof(dateText), 0, 1); + + keyValue = (char *)malloc((keyLength * 2) + 1); + if (keyValue == NULL) + { + fclose(file); + return; + } + cs_hexdump(0, key, keyLength, keyValue, (keyLength * 2) + 1); + + if (comment) + { + snprintf(line, sizeof(line), "\n%c %08X %s %s ; added by Emu %s %s", + identifier, provider, keyName, keyValue, dateText, comment); + } + else + { + snprintf(line, sizeof(line), "\n%c %08X %s %s ; added by Emu %s", + identifier, provider, keyName, keyValue, dateText); + } + + cs_log("Key written: %c %08X %s %s", identifier, provider, keyName, keyValue); + + free(keyValue); + + fwrite(line, cs_strlen(line), 1, file); + fclose(file); +} + +int8_t emu_set_key(char identifier, uint32_t provider, char *keyName, uint8_t *orgKey, uint32_t keyLength, + uint8_t writeKey, char *comment, struct s_reader *rdr) +{ + uint32_t i, j; + uint8_t *tmpKey = NULL; + KeyDataContainer *KeyDB; + KeyData *tmpKeyData, *newKeyData; + + identifier = (char)toupper((int)identifier); + + KeyDB = emu_get_key_container(identifier); + if (KeyDB == NULL) + { + return 0; + } + + keyName = strtoupper(keyName); + + if (identifier == 'F') // Prepare BISS keys before saving to the db + { + // Convert legacy BISS "00" & "01" keynames + if (0 == strcmp(keyName, "00") || 0 == strcmp(keyName, "01")) + { + keyName = "00000000"; + } + + // All keyNames should have a length of 8 after converting + if (cs_strlen(keyName) != 8) + { + cs_log("WARNING: Wrong key format in %s: F %08X %s", EMU_KEY_FILENAME, provider, keyName); + return 0; + } + + // Verify date-coded keyName (if enabled), ignoring old (expired) keys + if (rdr->emu_datecodedenabled) + { + char timeStr[9]; + date_to_str(timeStr, sizeof(timeStr), 0, 3); + + // Reject old date-coded keys, but allow our "00000000" evergreen label + if (strcmp("00000000", keyName) != 0 && strcmp(timeStr, keyName) >= 0) + { + return 0; + } + } + } + + // Fix checksum for BISS keys with a length of 6 + if (identifier == 'F' && keyLength == 6) + { + tmpKey = (uint8_t *)malloc(8 * sizeof(uint8_t)); + if(tmpKey == NULL) + { + return 0; + } + + tmpKey[0] = orgKey[0]; + tmpKey[1] = orgKey[1]; + tmpKey[2] = orgKey[2]; + tmpKey[3] = ((orgKey[0] + orgKey[1] + orgKey[2]) & 0xFF); + tmpKey[4] = orgKey[3]; + tmpKey[5] = orgKey[4]; + tmpKey[6] = orgKey[5]; + tmpKey[7] = ((orgKey[3] + orgKey[4] + orgKey[5]) & 0xFF); + + keyLength = 8; + } + else // All keys with a length of 8, including BISS + { + tmpKey = (uint8_t *)malloc(keyLength * sizeof(uint8_t)); + if (tmpKey == NULL) + { + return 0; + } + + memcpy(tmpKey, orgKey, keyLength); + } + + // Fix patched mgcamd format for Irdeto + if (identifier == 'I' && provider < 0xFFFF) + { + provider = provider << 8; + } + + // Key already exists on db, update its value + for (i = 0; i < KeyDB->keyCount; i++) + { + if (KeyDB->EmuKeys[i].provider != provider) + { + continue; + } + + // Don't match keyName (i.e. expiration date) for BISS1 and BISS2 mode 1/E sesssion words + if (identifier != 'F' && strcmp(KeyDB->EmuKeys[i].keyName, keyName)) + { + continue; + } + + // Allow multiple keys for Irdeto + if (identifier == 'I') + { + // Reject duplicates + tmpKeyData = &KeyDB->EmuKeys[i]; + do + { + if (memcmp(tmpKeyData->key, tmpKey, tmpKeyData->keyLength < keyLength ? tmpKeyData->keyLength : keyLength) == 0) + { + free(tmpKey); + return 0; + } + tmpKeyData = tmpKeyData->nextKey; + } + while(tmpKeyData != NULL); + + // Add new key + newKeyData = (KeyData *)malloc(sizeof(KeyData)); + if (newKeyData == NULL) + { + free(tmpKey); + return 0; + } + + newKeyData->identifier = identifier; + newKeyData->provider = provider; + + if (cs_strlen(keyName) < EMU_MAX_CHAR_KEYNAME) + { + cs_strncpy(newKeyData->keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + else + { + memcpy(newKeyData->keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + + newKeyData->keyName[EMU_MAX_CHAR_KEYNAME - 1] = 0; + newKeyData->key = tmpKey; + newKeyData->keyLength = keyLength; + newKeyData->nextKey = NULL; + + tmpKeyData = &KeyDB->EmuKeys[i]; + j = 0; + + while (tmpKeyData->nextKey != NULL) + { + if (j == 0xFE) + { + break; + } + tmpKeyData = tmpKeyData->nextKey; + j++; + } + + if (tmpKeyData->nextKey) + { + NULLFREE(tmpKeyData->nextKey->key); + NULLFREE(tmpKeyData->nextKey); + } + tmpKeyData->nextKey = newKeyData; + + if (writeKey) + { + write_key_to_file(identifier, provider, keyName, tmpKey, keyLength, comment); + } + } + else // identifier != 'I' + { + free(KeyDB->EmuKeys[i].key); + KeyDB->EmuKeys[i].key = tmpKey; + KeyDB->EmuKeys[i].keyLength = keyLength; + + if (identifier == 'F') // Update keyName (i.e. expiration date) for BISS + { + cs_strncpy(KeyDB->EmuKeys[i].keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + + if (writeKey) + { + write_key_to_file(identifier, provider, keyName, tmpKey, keyLength, comment); + } + } + return 1; + } + + // Key does not exist on db + if (KeyDB->keyCount + 1 > KeyDB->keyMax) + { + if (KeyDB->EmuKeys == NULL) // db is empty + { + KeyDB->EmuKeys = (KeyData *)malloc(sizeof(KeyData) * (KeyDB->keyMax + 64)); + if (KeyDB->EmuKeys == NULL) + { + free(tmpKey); + return 0; + } + KeyDB->keyMax += 64; + } + else // db is full, expand it + { + tmpKeyData = (KeyData *)realloc(KeyDB->EmuKeys, sizeof(KeyData) * (KeyDB->keyMax + 16)); + if (tmpKeyData == NULL) + { + free(tmpKey); + return 0; + } + KeyDB->EmuKeys = tmpKeyData; + KeyDB->keyMax += 16; + } + } + + KeyDB->EmuKeys[KeyDB->keyCount].identifier = identifier; + KeyDB->EmuKeys[KeyDB->keyCount].provider = provider; + + if (cs_strlen(keyName) < EMU_MAX_CHAR_KEYNAME) + { + cs_strncpy(KeyDB->EmuKeys[KeyDB->keyCount].keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + else + { + memcpy(KeyDB->EmuKeys[KeyDB->keyCount].keyName, keyName, EMU_MAX_CHAR_KEYNAME); + } + + KeyDB->EmuKeys[KeyDB->keyCount].keyName[EMU_MAX_CHAR_KEYNAME - 1] = 0; + KeyDB->EmuKeys[KeyDB->keyCount].key = tmpKey; + KeyDB->EmuKeys[KeyDB->keyCount].keyLength = keyLength; + KeyDB->EmuKeys[KeyDB->keyCount].nextKey = NULL; + KeyDB->keyCount++; + + if (writeKey) + { + write_key_to_file(identifier, provider, keyName, tmpKey, keyLength, comment); + } + return 1; +} + +int8_t emu_find_key(char identifier, uint32_t provider, uint32_t providerIgnoreMask, char *keyName, + uint8_t *key, uint32_t maxKeyLength, uint8_t isCriticalKey, uint32_t keyRef, + uint8_t matchLength, uint32_t *getProvider) +{ + uint32_t i; + uint16_t j; + uint8_t provider_matching_key_count = 0; + KeyDataContainer *KeyDB; + KeyData *tmpKeyData; + + KeyDB = emu_get_key_container(identifier); + if (KeyDB == NULL) + { + return 0; + } + + for (i = 0; i < KeyDB->keyCount; i++) + { + if ((KeyDB->EmuKeys[i].provider & ~providerIgnoreMask) != provider) + { + continue; + } + + // Don't match keyName (i.e. expiration date) for BISS + if (identifier != 'F' && strcmp(KeyDB->EmuKeys[i].keyName, keyName)) + { + continue; + } + + // "matchLength" cannot be used when multiple keys are allowed + // for a single provider/keyName combination. + // Currently this is the case only for Irdeto keys. + if (matchLength && KeyDB->EmuKeys[i].keyLength != maxKeyLength) + { + continue; + } + + if (providerIgnoreMask) + { + if (provider_matching_key_count < keyRef) + { + provider_matching_key_count++; + continue; + } + else + { + keyRef = 0; + } + } + + tmpKeyData = &KeyDB->EmuKeys[i]; + + j = 0; + while (j < keyRef && tmpKeyData->nextKey != NULL) + { + j++; + tmpKeyData = tmpKeyData->nextKey; + } + + if (j == keyRef) + { + memcpy(key, tmpKeyData->key, tmpKeyData->keyLength > maxKeyLength ? maxKeyLength : tmpKeyData->keyLength); + if (tmpKeyData->keyLength < maxKeyLength) + { + memset(key + tmpKeyData->keyLength, 0, maxKeyLength - tmpKeyData->keyLength); + } + + // Report the keyName (i.e. expiration date) of the session word found + if (identifier == 'F') + { + cs_strncpy(keyName, tmpKeyData->keyName, EMU_MAX_CHAR_KEYNAME); + } + + if (getProvider != NULL) + { + (*getProvider) = tmpKeyData->provider; + } + return 1; + } + else + { + break; + } + } + + if (isCriticalKey) + { + cs_log("Key not found: %c %X %s", identifier, provider, keyName); + } + + return 0; +} + +int8_t emu_update_key(char identifier, uint32_t provider, char *keyName, uint8_t *key, + uint32_t keyLength, uint8_t writeKey, char *comment) +{ + uint32_t keyRef = 0; + uint8_t *tmpKey = (uint8_t *)malloc(sizeof(uint8_t) * keyLength); + + if (tmpKey == NULL) + { + return 0; + } + + while (emu_find_key(identifier, provider, 0, keyName, tmpKey, keyLength, 0, keyRef, 0, NULL)) + { + if (memcmp(tmpKey, key, keyLength) == 0) + { + free(tmpKey); + return 0; + } + + keyRef++; + } + + free(tmpKey); + return emu_set_key(identifier, provider, keyName, key, keyLength, writeKey, comment, NULL); +} + +static int32_t delete_keys_in_container(char identifier) +{ + // Deletes all keys stored in memory for the specified identifier, + // but keeps the container itself, re-initialized at { NULL, 0, 0 }. + // Returns the count of deleted keys. + + uint32_t oldKeyCount, i; + KeyData *tmpKeyData; + KeyDataContainer *KeyDB = emu_get_key_container(identifier); + + if (KeyDB == NULL || KeyDB->EmuKeys == NULL || KeyDB->keyCount == 0) + { + return 0; + } + + for (i = 0; i < KeyDB->keyCount; i++) + { + // For Irdeto multiple keys only (linked list structure) + while (KeyDB->EmuKeys[i].nextKey != NULL) + { + tmpKeyData = KeyDB->EmuKeys[i].nextKey; + KeyDB->EmuKeys[i].nextKey = KeyDB->EmuKeys[i].nextKey->nextKey; + free(tmpKeyData->key); // Free key + free(tmpKeyData); // Free KeyData + } + + // For single keys (all identifiers, including Irdeto) + free(KeyDB->EmuKeys[i].key); // Free key + } + + // Free the KeyData array + NULLFREE(KeyDB->EmuKeys); + oldKeyCount = KeyDB->keyCount; + KeyDB->keyCount = 0; + KeyDB->keyMax = 0; + + return oldKeyCount; +} + +void emu_clear_keydata(void) +{ + uint32_t total = 0; + + total = CwKeys.keyCount; + total += ViKeys.keyCount; + total += NagraKeys.keyCount; + total += IrdetoKeys.keyCount; + total += BissSWs.keyCount; + total += Biss2Keys.keyCount; + total += OmnicryptKeys.keyCount; + total += PowervuKeys.keyCount; + total += TandbergKeys.keyCount; + total += StreamKeys.keyCount; + + if (total != 0) + { + cs_log("Freeing keys in memory: W:%d V:%d N:%d I:%d F:%d G:%d O:%d P:%d T:%d A:%d", + CwKeys.keyCount, ViKeys.keyCount, NagraKeys.keyCount, IrdetoKeys.keyCount, BissSWs.keyCount, + Biss2Keys.keyCount, OmnicryptKeys.keyCount, PowervuKeys.keyCount, TandbergKeys.keyCount, + StreamKeys.keyCount); + + delete_keys_in_container('W'); + delete_keys_in_container('V'); + delete_keys_in_container('N'); + delete_keys_in_container('I'); + delete_keys_in_container('F'); + delete_keys_in_container('G'); + delete_keys_in_container('O'); + delete_keys_in_container('P'); + delete_keys_in_container('T'); + delete_keys_in_container('A'); + } +} + +uint8_t emu_read_keyfile(struct s_reader *rdr, const char *opath) +{ + char line[1200], keyName[EMU_MAX_CHAR_KEYNAME], keyString[1026], identifier; + char *path, *filepath, filename[EMU_KEY_FILENAME_MAX_LEN + 1]; + uint32_t pathLength, provider, keyLength; + uint8_t fileNameLen = cs_strlen(EMU_KEY_FILENAME); + uint8_t *key; + struct dirent *pDirent; + DIR *pDir; + FILE *file = NULL; + + pathLength = cs_strlen(opath); + path = (char *)malloc(pathLength + 1); + if (path == NULL) + { + return 0; + } + cs_strncpy(path, opath, pathLength + 1); + + pathLength = cs_strlen(path); + if (pathLength >= fileNameLen && strcasecmp(path + pathLength - fileNameLen, EMU_KEY_FILENAME) == 0) + { + // cut file name + path[pathLength - fileNameLen] = '\0'; + } + + pathLength = cs_strlen(path); + if (path[pathLength - 1] == '/' || path[pathLength - 1] == '\\') + { + // cut trailing / + path[pathLength - 1] = '\0'; + } + + pDir = opendir(path); + if (pDir == NULL) + { + cs_log("Cannot open key file path: %s", path); + free(path); + return 0; + } + + while ((pDirent = readdir(pDir)) != NULL) + { + if (strcasecmp(pDirent->d_name, EMU_KEY_FILENAME) == 0) + { + cs_strncpy(filename, pDirent->d_name, sizeof(filename)); + break; + } + } + closedir(pDir); + + if (pDirent == NULL) + { + cs_log("Key file not found in: %s", path); + free(path); + return 0; + } + + pathLength = cs_strlen(path) + 1 + cs_strlen(filename) + 1; + filepath = (char *)malloc(pathLength); + if (filepath == NULL) + { + free(path); + return 0; + } + snprintf(filepath, pathLength, "%s/%s", path, filename); + free(path); + + cs_log("Reading key file: %s", filepath); + + file = fopen(filepath, "r"); + free(filepath); + if (file == NULL) + { + return 0; + } + + emu_set_keyfile_path(opath); + + while (fgets(line, 1200, file)) + { + if (sscanf(line, "%c %8x %11s %1024s", &identifier, &provider, keyName, keyString) != 4) + { + continue; + } + + keyLength = cs_strlen(keyString) / 2; + key = (uint8_t *)malloc(keyLength); + if (key == NULL) + { + fclose(file); + return 0; + } + + if (char_to_bin(key, keyString, cs_strlen(keyString))) // Conversion OK + { + emu_set_key(identifier, provider, keyName, key, keyLength, 0, NULL, rdr); + } + else // Non-hex characters in keyString + { + if ((identifier != ';' && identifier != '#' && // Skip warning for comments, etc. + identifier != '=' && identifier != '-' && + identifier != ' ') && + !(identifier == 'F' && 0 == strncmp(keyString, "XXXXXXXXXXXX", 12))) // Skip warning for BISS 'Example key' lines + { + // Alert user regarding faulty line + cs_log("WARNING: non-hex value in %s at %c %08X %s %s", + EMU_KEY_FILENAME, identifier, provider, keyName, keyString); + } + } + free(key); + } + fclose(file); + + return 1; +} + +#if defined(WITH_SOFTCAM) && !defined(__APPLE__) && !defined(__ANDROID__) +extern uint8_t SoftCamKey_Data[] __asm__("_binary_SoftCam_Key_start"); +extern uint8_t SoftCamKey_DataEnd[] __asm__("_binary_SoftCam_Key_end"); + +void emu_read_keymemory(struct s_reader *rdr) +{ + char *keyData, *line, *saveptr, keyName[EMU_MAX_CHAR_KEYNAME], keyString[1026], identifier; + uint32_t provider, keyLength; + uint8_t *key; + + keyData = (char *)malloc(SoftCamKey_DataEnd - SoftCamKey_Data + 1); + if (keyData == NULL) + { + return; + } + memcpy(keyData, SoftCamKey_Data, SoftCamKey_DataEnd - SoftCamKey_Data); + keyData[SoftCamKey_DataEnd-SoftCamKey_Data] = 0x00; + + line = strtok_r(keyData, "\n", &saveptr); + while (line != NULL) + { + if (sscanf(line, "%c %8x %11s %1024s", &identifier, &provider, keyName, keyString) != 4) + { + line = strtok_r(NULL, "\n", &saveptr); + continue; + } + + keyLength = cs_strlen(keyString) / 2; + + key = (uint8_t *)malloc(keyLength); + if (key == NULL) + { + free(keyData); + return; + } + + if (char_to_bin(key, keyString, cs_strlen(keyString))) // Conversion OK + { + emu_set_key(identifier, provider, keyName, key, keyLength, 0, NULL, rdr); + } + else // Non-hex characters in keyString + { + if ((identifier != ';' && identifier != '#' && // Skip warning for comments, etc. + identifier != '=' && identifier != '-' && + identifier != ' ') && + !(identifier == 'F' && 0 == strncmp(keyString, "XXXXXXXXXXXX", 12))) // Skip warning for BISS 'Example key' lines + { + // Alert user regarding faulty line + cs_log("WARNING: non-hex value in internal keyfile at %c %08X %s %s", + identifier, provider, keyName, keyString); + } + } + free(key); + line = strtok_r(NULL, "\n", &saveptr); + } + free(keyData); +} +#else +void emu_read_keymemory(struct s_reader *UNUSED(rdr)) { } +#endif + +static const char *get_error_reason(int8_t result) +{ + switch (result) + { + case EMU_OK: + return "No error"; + + case EMU_NOT_SUPPORTED: + return "Not supported"; + + case EMU_KEY_NOT_FOUND: + return "Key not found"; + + case EMU_KEY_REJECTED: + return "ECM key rejected"; + + case EMU_CORRUPT_DATA: + return "Corrupt data"; + + case EMU_CW_NOT_FOUND: + return "CW not found"; + + case EMU_CHECKSUM_ERROR: + return "Checksum error"; + + case EMU_OUT_OF_MEMORY: + return "Out of memory"; + + default: + return "Unknown reason"; + } +} + +int8_t emu_process_ecm(struct s_reader *rdr, const ECM_REQUEST *er, uint8_t *cw, EXTENDED_CW *cw_ex) +{ + if (er->ecmlen < 3) + { + cs_log_dbg(D_TRACE, "Received ecm data of zero length!"); + return 4; + } + + uint16_t ecmLen = SCT_LEN(er->ecm); + uint8_t ecmCopy[ecmLen]; + int8_t result = 1; + + if (ecmLen != er->ecmlen) + { + cs_log_dbg(D_TRACE, "Actual ecm data length 0x%03X but ecm section length is 0x%03X", + er->ecmlen, ecmLen); + return 4; + } + + if (ecmLen > EMU_MAX_ECM_LEN) + { + cs_log_dbg(D_TRACE, "Actual ecm data length 0x%03X but maximum supported ecm length is 0x%03X", + er->ecmlen, EMU_MAX_ECM_LEN); + return 1; + } + + memcpy(ecmCopy, er->ecm, ecmLen); + + if (caid_is_viaccess(er->caid)) result = viaccess_ecm(ecmCopy, cw); + else if (caid_is_irdeto(er->caid)) result = irdeto2_ecm(er->caid, ecmCopy, cw); + else if (caid_is_cryptoworks(er->caid)) result = cryptoworks_ecm(er->caid, ecmCopy, cw); + else if (caid_is_powervu(er->caid)) + { +#ifdef MODULE_STREAMRELAY + result = powervu_ecm(ecmCopy, cw, cw_ex, er->srvid, er->caid, er->tsid, er->onid, er->ens, NULL); +#else + result = powervu_ecm(ecmCopy, cw, cw_ex, er->srvid, er->caid, er->tsid, er->onid, er->ens); +#endif + } + else if (caid_is_director(er->caid)) result = director_ecm(ecmCopy, cw); + else if (caid_is_nagra(er->caid)) result = nagra2_ecm(ecmCopy, cw); + else if (caid_is_biss(er->caid)) result = biss_ecm(rdr, er->ecm, er->caid, er->pid, cw, cw_ex); + else if (er->caid == 0x00FF) result = omnicrypt_ecm(ecmCopy, cw); // temp caid + + if (result != 0) + { + cs_log("ECM failed: %s", get_error_reason(result)); + } + + return result; +} + +int8_t emu_process_emm(struct s_reader *rdr, uint16_t caid, const uint8_t *emm, uint32_t *keysAdded) +{ + uint16_t emmLen = SCT_LEN(emm); + uint8_t emmCopy[emmLen]; + int8_t result = 1; + + if (emmLen > EMU_MAX_EMM_LEN) + { + return 1; + } + memcpy(emmCopy, emm, emmLen); + *keysAdded = 0; + + if (caid_is_viaccess(caid)) result = viaccess_emm(emmCopy, keysAdded); + else if (caid_is_irdeto(caid)) result = irdeto2_emm(caid, emmCopy, keysAdded); + else if (caid_is_powervu(caid)) result = powervu_emm(emmCopy, keysAdded); + else if (caid_is_director(caid)) result = director_emm(emmCopy, keysAdded); + else if (caid_is_biss_dynamic(caid)) result = biss_emm(rdr, emmCopy, keysAdded); + + if (result != 0) + { + cs_log_dbg(D_EMM,"EMM failed: %s", get_error_reason(result)); + } + + return result; +} + +#endif // WITH_EMU diff --git a/module-emulator-osemu.h b/module-emulator-osemu.h new file mode 100644 index 0000000..a83dc64 --- /dev/null +++ b/module-emulator-osemu.h @@ -0,0 +1,96 @@ +#ifndef MODULE_EMULATOR_OSEMU_H_ +#define MODULE_EMULATOR_OSEMU_H_ + +#ifdef WITH_EMU + +// Version info +#define EMU_VERSION 802 + +#define EMU_MAX_CHAR_KEYNAME 12 +#define EMU_KEY_FILENAME "SoftCam.Key" +#define EMU_KEY_FILENAME_MAX_LEN 31 +#define EMU_MAX_ECM_LEN MAX_ECM_SIZE +#define EMU_MAX_EMM_LEN MAX_EMM_SIZE + +/* + * Error codes for ProccessECM and ProccessEMM functions + * 0 - OK + * 1 - ECM / EMM not supported + * 2 - ECM / EMM key not found + * 3 - ECM key rejected + * 4 - Corrupt data + * 5 - CW not found + * 6 - CW / ECM / EMM checksum error + * 7 - Out of memory +*/ + +#define EMU_OK 0 +#define EMU_NOT_SUPPORTED 1 +#define EMU_KEY_NOT_FOUND 2 +#define EMU_KEY_REJECTED 3 +#define EMU_CORRUPT_DATA 4 +#define EMU_CW_NOT_FOUND 5 +#define EMU_CHECKSUM_ERROR 6 +#define EMU_OUT_OF_MEMORY 7 + +typedef struct KeyData KeyData; + +struct KeyData +{ + char identifier; + uint32_t provider; + char keyName[EMU_MAX_CHAR_KEYNAME]; + uint8_t *key; + uint32_t keyLength; + KeyData *nextKey; +}; + +typedef struct +{ + KeyData *EmuKeys; + uint32_t keyCount; + uint32_t keyMax; +} KeyDataContainer; + +extern KeyDataContainer CwKeys; +extern KeyDataContainer ViKeys; +extern KeyDataContainer NagraKeys; +extern KeyDataContainer IrdetoKeys; +extern KeyDataContainer BissSWs; // 'F' identifier - BISS1 and BISS2 mode 1/E session words +extern KeyDataContainer Biss2Keys; // 'G' identifier - BISS2 mode CA session keys (ECM keys) +extern KeyDataContainer OmnicryptKeys; +extern KeyDataContainer PowervuKeys; +extern KeyDataContainer TandbergKeys; +extern KeyDataContainer StreamKeys; +extern uint8_t viasat_const[]; +extern char *emu_keyfile_path; +extern pthread_mutex_t emu_key_data_mutex; + +void emu_set_keyfile_path(const char *path); +void emu_clear_keydata(void); +uint8_t emu_read_keyfile(struct s_reader *rdr, const char *path); +void emu_read_keymemory(struct s_reader *rdr); + +int8_t is_valid_dcw(uint8_t *dw); +int8_t char_to_bin(uint8_t *out, const char *in, uint32_t inLen); +void date_to_str(char *dateStr, uint8_t len, int8_t offset, uint8_t format); + +KeyDataContainer *emu_get_key_container(char identifier); + +int8_t emu_process_ecm(struct s_reader *rdr, const ECM_REQUEST *er, uint8_t *cw, EXTENDED_CW* cw_ex); + +int8_t emu_process_emm(struct s_reader *rdr, uint16_t caid, const uint8_t *emm, uint32_t *keysAdded); + +int8_t emu_find_key(char identifier, uint32_t provider, uint32_t providerIgnoreMask, char *keyName, + uint8_t *key, uint32_t maxKeyLength, uint8_t isCriticalKey, uint32_t keyRef, + uint8_t matchLength, uint32_t *getProvider); + +int8_t emu_set_key(char identifier, uint32_t provider, char *keyName, uint8_t *orgKey, uint32_t keyLength, + uint8_t writeKey, char *comment, struct s_reader *rdr); + +int8_t emu_update_key(char identifier, uint32_t provider, char *keyName, uint8_t *key, uint32_t keyLength, + uint8_t writeKey, char *comment); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_H_ diff --git a/module-emulator-powervu.c b/module-emulator-powervu.c new file mode 100644 index 0000000..810fcec --- /dev/null +++ b/module-emulator-powervu.c @@ -0,0 +1,2795 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "cscrypt/des.h" +#include "module-streamrelay.h" +#include "module-emulator-osemu.h" +#include "module-emulator-powervu.h" +#include "oscam-string.h" +#include "oscam-time.h" + +static inline uint8_t get_bit(uint8_t byte, uint8_t bitnb) +{ + return ((byte & (1 << bitnb)) ? 1 : 0); +} + +static inline uint8_t set_bit(uint8_t val, uint8_t bitnb, uint8_t biton) +{ + return (biton ? (val | (1 << bitnb)) : (val & ~(1 << bitnb))); +} + +static uint8_t crc8_calc(uint8_t *data, int len) +{ + int i; + uint8_t crc = 0; + uint8_t crcTable[256] = + { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 + }; + + for (i = 0; i < len; i++) + { + crc = crcTable[data[i] ^ crc]; + } + + return crc; +} + +static void pad_data(uint8_t *data, int len, uint8_t *dataPadded) +{ + int i; + uint8_t pad[] = + { + 0x01, 0x02, 0x22, 0x04, 0x20, 0x2A, 0x1F, 0x03, + 0x04, 0x06, 0x02, 0x0C, 0x2B, 0x2B, 0x01, 0x7B + }; + + for (i = 0; i < len; i++) + { + dataPadded[i] = data[i]; + } + + dataPadded[len] = 0x01; + + for (i = len + 1; i < 0x2F; i++) + { + dataPadded[i] = 0x00; + } + + dataPadded[0x2F] = len; + + for (i = 0; i < 16; i++) + { + dataPadded[0x30 + i] = pad[i]; + } +} + +static void hash_mode_01_custom_md5(uint8_t *data, uint8_t *hash) +{ + int i, j, s; + uint32_t a, b, c, d, f = 0, g; + + uint32_t T[] = + { + 0x783E16F6, 0xC267AC13, 0xA2B17F12, 0x6B8A31A4, + 0xF910654D, 0xB702DBCB, 0x266CEF60, 0x5145E47C, + 0xB92E00D6, 0xE80A4A64, 0x8A07FA77, 0xBA7D89A9, + 0xEBED8022, 0x653AAF2B, 0xF118B03B, 0x6CC16544, + 0x96EB6583, 0xF4E27E35, 0x1ABB119E, 0x068D3EF2, + 0xDAEAA8A5, 0x3C312A3D, 0x59538388, 0xA100772F, + 0xAB0165CE, 0x979959E7, 0x5DD8F53D, 0x189662BA, + 0xFD021A9C, 0x6BC2D338, 0x1EFF667E, 0x40C66888, + 0x6E9F07FF, 0x0CEF442F, 0x82D20190, 0x4E8CAEAC, + 0x0F7CB305, 0x2E73FBE7, 0x1CE884A2, 0x7A60BD52, + 0xC348B30D, 0x081CE3AA, 0xA12220E7, 0x38C7EC79, + 0xCBD8DD3A, 0x62B4FBA5, 0xAD2A63DB, 0xE4D0852E, + 0x53DE980F, 0x9C8DDA59, 0xA6B4CEDE, 0xB48A7692, + 0x0E2C46A4, 0xEB9367CB, 0x165D72EE, 0x75532B45, + 0xB9CA8E97, 0x08C8837B, 0x966F917B, 0x527515B4, + 0xF27A5E5D, 0xB71E6267, 0x7603D7E6, 0x9837DD69 + }; // CUSTOM T + + uint8_t r[] = + { + 0x06, 0x0A, 0x0F, 0x15, 0x05, 0x09, 0x0E, 0x14, + 0x04, 0x0B, 0x10, 0x17, 0x07, 0x0C, 0x11, 0x16 + }; // STANDARD REORDERED + + uint8_t tIdxInit[] = { 0, 1, 5, 0 }; // STANDARD + uint8_t tIdxIncr[] = { 1, 5, 3, 7 }; // STANDARD + + uint32_t h[] = { 0xEAD81D2E, 0xCE4DC6E9, 0xF9B5C301, 0x10325476 }; // CUSTOM h0, h1, h2, STANDARD h3 + uint32_t dataLongs[16]; + + for (i = 0; i < 16; i++) + { + dataLongs[i] = (data[4 * i + 0] << 0) + (data[4 * i + 1] << 8) + + (data[4 * i + 2] << 16) + (data[4 * i + 3] << 24); + } + + a = h[0]; + b = h[1]; + c = h[2]; + d = h[3]; + + for (i = 0; i < 4; i++) + { + g = tIdxInit[i]; + + for (j = 0; j < 16; j++) + { + if (i == 0) + { + f = (b & c) | (~b & d); + } + else if (i == 1) + { + f = (b & d) | (~d & c); + } + else if (i == 2) + { + f = (b ^ c ^ d); + } + else if (i == 3) + { + f = (~d | b) ^ c; + } + + f = dataLongs[g] + a + T[16 * i + j] + f; + + s = r[4 * i + (j & 3)]; + f = (f << s) | (f >> (32 - s)); + + a = d; + d = c; + c = b; + b += f; + + g = (g + tIdxIncr[i]) & 0xF; + } + } + + h[0] += a; + h[1] += b; + h[2] += c; + h[3] += d; + + for (i = 0; i < 4; i++) + { + hash[4 * i + 0] = h[i] >> 0; + hash[4 * i + 1] = h[i] >> 8; + hash[4 * i + 2] = h[i] >> 16; + hash[4 * i + 3] = h[i] >> 24; + } +} + +static void hash_mode_02(uint8_t *data, uint8_t *hash) +{ + int i; + uint32_t a, b, c, d, e, f = 0, tmp; + uint32_t h[] = { 0x81887F3A, 0x36CCA480, 0x99056FB1, 0x79705BAE }; + uint32_t dataLongs[80]; + + for (i = 0; i < 16; i++) + { + dataLongs[i] = (data[4 * i + 0] << 24) + (data[4 * i + 1] << 16) + + (data[4 * i + 2] << 8) + (data[4 * i + 3] << 0); + } + + for (i = 0; i < 64; i++) + { + dataLongs[16 + i] = dataLongs[16 + i - 2]; + dataLongs[16 + i] ^= dataLongs[16 + i - 7]; + dataLongs[16 + i] ^= dataLongs[16 + i - 13]; + dataLongs[16 + i] ^= dataLongs[16 + i - 16]; + } + + a = dataLongs[0]; + b = dataLongs[1]; + c = dataLongs[2]; + d = dataLongs[3]; + e = dataLongs[4]; + + for (i = 0; i < 80; i++) + { + if (i < 0x15) f = (b & c) | (~b & d); + else if (i < 0x28) f = (b ^ c ^ d); + else if (i < 0x3D) f = (b & c) | (c & d) | (b & d); + else if (i < 0x50) f = (b ^ c ^ d); + + tmp = a; + a = e + f + (a << 5) + (a >> 27) + h[i / 0x14] + dataLongs[i]; + e = d; + d = c; + c = (b << 30) + (b >> 2); + b = tmp; + } + + dataLongs[0] += a; + dataLongs[1] += b; + dataLongs[2] += c; + dataLongs[3] += d; + + for (i = 0; i < 4; i++) + { + hash[4 * i + 0] = dataLongs[i] >> 24; + hash[4 * i + 1] = dataLongs[i] >> 16; + hash[4 * i + 2] = dataLongs[i] >> 8; + hash[4 * i + 3] = dataLongs[i] >> 0; + } +} + +static void hash_mode_03(uint8_t *data, uint8_t *hash) +{ + int i, j, k, s, s2, tmp; + uint32_t a, b, c, d, f = 0, g; + uint32_t a2, b2, c2, d2, f2 = 0, g2; + + uint32_t T[] = { 0xC88F3F2E, 0x967506BA, 0xDA877A7B, 0x0DECCDFE }; + uint32_t T2[] = { 0x01F42668, 0x39C7CDA5, 0xD490E2FE, 0x9965235D }; + + uint8_t r[] = + { + 0x0B, 0x0E, 0x0F, 0x0C, 0x05, 0x08, 0x07, 0x09, + 0x0B, 0x0D, 0x0E, 0x0F, 0x06, 0x07, 0x09, 0x08, + 0x07, 0x06, 0x08, 0x0D, 0x0B, 0x09, 0x07, 0x0F, + 0x07, 0x0C, 0x0F, 0x09, 0x0B, 0x07, 0x0D, 0x0C + }; + + uint8_t tIdxIncr[] = + { + 0x07, 0x04, 0x0D, 0x01, 0x0A, 0x06, 0x0F, 0x03, + 0x0C, 0x00, 0x09, 0x05, 0x02, 0x0E, 0x0B, 0x08, + 0x05, 0x0D, 0x02, 0x00, 0x04, 0x09, 0x03, 0x08, + 0x01, 0x0A, 0x07, 0x0B, 0x06, 0x0F, 0x0C, 0x0E + }; + + uint32_t h[] = { 0xC8616857, 0x9D3F5B8E, 0x4D7B8F76, 0x97BC8D80 }; + + uint32_t dataLongs[80]; + uint32_t result[4]; + + for (i = 0; i < 16; i++) + { + dataLongs[i] = (data[4 * i + 0] << 24) + (data[4 * i + 1] << 16) + + (data[4 * i + 2] << 8) + (data[4 * i + 3] << 0); + } + + a = h[0]; + b = h[1]; + c = h[2]; + d = h[3]; + + a2 = h[3]; + b2 = h[2]; + c2 = h[1]; + d2 = h[0]; + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 16; j++) + { + tmp = j; + + for (k = 0; k < i; k++) + { + tmp = tIdxIncr[tmp]; + } + + g = 0x0F - tmp; + g2 = tmp; + + if (i == 0) f = (b & d) | (~d & c); + else if (i == 1) f = (~c | b) ^ d; + else if (i == 2) f = (~b & d) | (b & c); + else if (i == 3) f = (b ^ c ^ d); + + if (i == 0) f2 = (b2 ^ c2 ^ d2); + else if (i == 1) f2 = (~b2 & d2) | (b2 & c2); + else if (i == 2) f2 = (~c2 | b2) ^ d2; + else if (i == 3) f2 = (b2 & d2) | (~d2 & c2); + + f = dataLongs[g] + a + T[i] + f; + s = r[0x0F + (((i & 1) ^ 1) << 4) - j]; + f = (f << s) | (f >> (32 - s)); + + f2 = dataLongs[g2] + a2 + T2[i] + f2; + s2 = r[((i & 1) << 4) + j]; + f2 = (f2 << s2) | (f2 >> (32 - s2)); + + a = d; + d = (c << 10) | (c >> 22); + c = b; + b = f; + + a2 = d2; + d2 = (c2 << 10) | (c2 >> 22); + c2 = b2; + b2 = f2; + } + } + + result[0] = h[3] + b + a2; + result[1] = h[2] + c + b2; + result[2] = h[1] + d + c2; + result[3] = h[0] + a + d2; + + for (i = 0; i < 4; i++) + { + hash[4 * i + 0] = result[i] >> 0; + hash[4 * i + 1] = result[i] >> 8; + hash[4 * i + 2] = result[i] >> 16; + hash[4 * i + 3] = result[i] >> 24; + } +} + +static const uint8_t table04[] = +{ + 0x02, 0x03, 0x07, 0x0B, 0x0D, 0x08, 0x00, 0x01, 0x2B, 0x2D, 0x28, 0x20, 0x21, 0x0A, 0x0C, 0x0E, + 0x22, 0x36, 0x23, 0x27, 0x29, 0x24, 0x25, 0x26, 0x2A, 0x3C, 0x3E, 0x3F, 0x0F, 0x2C, 0x2E, 0x2F, + 0x12, 0x13, 0x17, 0x1B, 0x1C, 0x18, 0x10, 0x11, 0x19, 0x14, 0x15, 0x16, 0x1A, 0x09, 0x04, 0x05, + 0x32, 0x33, 0x37, 0x3B, 0x06, 0x1C, 0x1E, 0x1F, 0x3D, 0x38, 0x30, 0x31, 0x39, 0x34, 0x35, 0x3A +}; + +static const uint8_t table05[] = +{ + 0x08, 0x09, 0x0A, 0x03, 0x04, 0x3F, 0x27, 0x28, 0x29, 0x2A, 0x05, 0x0B, 0x1B, 0x1C, 0x1C, 0x1E, + 0x20, 0x0C, 0x0D, 0x22, 0x23, 0x24, 0x00, 0x01, 0x02, 0x06, 0x07, 0x25, 0x26, 0x0E, 0x0F, 0x21, + 0x10, 0x11, 0x12, 0x2E, 0x2F, 0x13, 0x14, 0x15, 0x2B, 0x2C, 0x2D, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x30, 0x31, 0x37, 0x3B, 0x3C, 0x3D, 0x3E, 0x1F, 0x38, 0x39, 0x32, 0x33, 0x34, 0x35, 0x36, 0x3A +}; + +static const uint8_t table06[] = +{ + 0x00, 0x01, 0x02, 0x06, 0x07, 0x08, 0x03, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x04, 0x05, 0x09, 0x0D, + 0x20, 0x21, 0x22, 0x26, 0x27, 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x10, 0x11, 0x12, 0x16, 0x17, 0x28, + 0x18, 0x13, 0x14, 0x15, 0x19, 0x1C, 0x1A, 0x1B, 0x1C, 0x1E, 0x1F, 0x23, 0x24, 0x25, 0x29, 0x2D, + 0x30, 0x31, 0x32, 0x36, 0x37, 0x38, 0x33, 0x34, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x35, 0x39, 0x3D +}; + +static const uint8_t table07[] = +{ + 0x10, 0x11, 0x12, 0x17, 0x1C, 0x1E, 0x0E, 0x38, 0x39, 0x3A, 0x13, 0x14, 0x29, 0x2A, 0x16, 0x1F, + 0x00, 0x01, 0x02, 0x3C, 0x3D, 0x3E, 0x3F, 0x07, 0x08, 0x09, 0x03, 0x04, 0x05, 0x06, 0x3B, 0x0A, + 0x20, 0x21, 0x22, 0x19, 0x1A, 0x1B, 0x1C, 0x0B, 0x0C, 0x15, 0x23, 0x24, 0x25, 0x26, 0x18, 0x0F, + 0x30, 0x31, 0x2B, 0x33, 0x34, 0x35, 0x36, 0x37, 0x27, 0x28, 0x2C, 0x2D, 0x2E, 0x2F, 0x32, 0x0D +}; + +static const uint8_t table08[] = +{ + 0x10, 0x11, 0x1E, 0x17, 0x18, 0x19, 0x12, 0x13, 0x14, 0x1C, 0x1C, 0x15, 0x0D, 0x05, 0x06, 0x0A, + 0x00, 0x01, 0x0E, 0x07, 0x08, 0x09, 0x02, 0x2D, 0x25, 0x26, 0x2A, 0x2B, 0x2F, 0x03, 0x04, 0x0C, + 0x20, 0x21, 0x2E, 0x27, 0x28, 0x29, 0x30, 0x31, 0x3E, 0x37, 0x38, 0x39, 0x22, 0x23, 0x24, 0x2C, + 0x32, 0x33, 0x34, 0x3C, 0x3D, 0x35, 0x36, 0x3A, 0x3B, 0x0B, 0x0F, 0x16, 0x1A, 0x1B, 0x1F, 0x3F +}; + +static const uint8_t table09[] = +{ + 0x20, 0x21, 0x24, 0x22, 0x23, 0x2A, 0x2B, 0x33, 0x35, 0x38, 0x39, 0x36, 0x2D, 0x2C, 0x2E, 0x2F, + 0x00, 0x01, 0x04, 0x02, 0x25, 0x28, 0x08, 0x09, 0x06, 0x07, 0x0A, 0x0B, 0x0D, 0x0C, 0x0E, 0x0F, + 0x10, 0x11, 0x14, 0x12, 0x13, 0x15, 0x19, 0x16, 0x29, 0x26, 0x03, 0x17, 0x1A, 0x1C, 0x1C, 0x1E, + 0x30, 0x31, 0x34, 0x32, 0x37, 0x3A, 0x3B, 0x3D, 0x3C, 0x3E, 0x3F, 0x1B, 0x05, 0x18, 0x27, 0x1F +}; + +static const uint8_t table0A[] = +{ + 0x00, 0x04, 0x05, 0x0B, 0x0C, 0x06, 0x09, 0x0A, 0x0E, 0x0D, 0x0F, 0x25, 0x15, 0x1B, 0x1C, 0x16, + 0x10, 0x11, 0x01, 0x02, 0x03, 0x07, 0x08, 0x12, 0x13, 0x17, 0x18, 0x14, 0x23, 0x27, 0x28, 0x24, + 0x30, 0x31, 0x32, 0x33, 0x37, 0x38, 0x34, 0x35, 0x3B, 0x3C, 0x20, 0x21, 0x22, 0x2B, 0x2C, 0x26, + 0x36, 0x39, 0x3A, 0x3E, 0x3D, 0x19, 0x1A, 0x1E, 0x1C, 0x1F, 0x3F, 0x29, 0x2A, 0x2E, 0x2D, 0x2F +}; + +static void hash_modes_04_to_0A_tables(uint8_t *data, uint8_t *hash, const uint8_t *table) +{ + int i; + + for (i = 0; i < 16; i++) + { + hash[i] = table[i]; + hash[i] ^= data[table[i]]; + hash[i] ^= table[16 + i]; + hash[i] ^= data[table[16 + i]]; + hash[i] ^= table[32 + i]; + hash[i] ^= data[table[32 + i]]; + hash[i] ^= table[48 + i]; + hash[i] ^= data[table[48 + i]]; + } +} + +static const uint8_t table0F[] = { 0xC7, 0x45, 0x15, 0x71, 0x61, 0x07, 0x05, 0x47 }; +static const uint8_t table10[] = { 0x0F, 0x47, 0x2B, 0x6C, 0xAD, 0x0F, 0xB3, 0xEA }; +static const uint8_t table11[] = { 0xB1, 0x46, 0xD1, 0x66, 0x5D, 0x28, 0x59, 0xD2 }; +static const uint8_t table12[] = { 0x0B, 0x4B, 0xD7, 0x68, 0x5F, 0xAD, 0x4B, 0xBB }; +static const uint8_t table13[] = { 0x4F, 0x4E, 0xE1, 0x6A, 0x21, 0xD3, 0xF7, 0xA6 }; +static const uint8_t table14[] = { 0xDD, 0x39, 0xB9, 0x65, 0x03, 0x91, 0xF1, 0xAC }; +static const uint8_t table15[] = { 0x3F, 0x50, 0xB5, 0x6F, 0x37, 0xC9, 0x13, 0x5D }; +static const uint8_t table16[] = { 0xF9, 0x5C, 0xFD, 0x72, 0x19, 0x42, 0x23, 0x6B }; +static const uint8_t table17[] = { 0xDF, 0x60, 0x93, 0x64, 0x33, 0x16, 0xB3, 0x8A }; +static const uint8_t table18[] = { 0x09, 0x64, 0x5F, 0x6B, 0xFB, 0x21, 0x19, 0xE4 }; + +static void hash_modes_0F_to_18_tables(uint8_t *data, uint8_t *hash, const uint8_t *table) +{ + int i; + uint32_t t[4], tmp; + + memset(hash, 0x00, 16); + + t[0] = (table[1] << 8) + table[0]; + t[1] = (table[3] << 8) + table[2]; + t[2] = (table[5] << 8) + table[4]; + t[3] = (table[7] << 8) + table[6]; + + for (i = 0; i < 60; i += 4) + { + t[0] = ((t[0] & 0xFFFF) * t[2]) + (t[0] >> 16); + t[1] = ((t[1] & 0xFFFF) * t[3]) + (t[1] >> 16); + tmp = t[0] + t[1]; + + hash[(i + 0) & 0x0F] = hash[(i + 0) & 0x0F] ^ data[i + 0] ^ (tmp >> 24); + hash[(i + 1) & 0x0F] = hash[(i + 1) & 0x0F] ^ data[i + 1] ^ (tmp >> 16); + hash[(i + 2) & 0x0F] = hash[(i + 2) & 0x0F] ^ data[i + 2] ^ (tmp >> 8); + hash[(i + 3) & 0x0F] = hash[(i + 3) & 0x0F] ^ data[i + 3] ^ (tmp >> 0); + } +} + +static const uint8_t table19[] = { 0x02, 0x03, 0x05, 0x10 }; +static const uint8_t table1A[] = { 0x01, 0x05, 0x08, 0x10 }; +static const uint8_t table1B[] = { 0x03, 0x07, 0x08, 0x10 }; +static const uint8_t table1C[] = { 0x03, 0x05, 0x0A, 0x10 }; +static const uint8_t table1D[] = { 0x03, 0x07, 0x0A, 0x10 }; +static const uint8_t table1E[] = { 0x01, 0x05, 0x0B, 0x10 }; +static const uint8_t table1F[] = { 0x06, 0x07, 0x0B, 0x10 }; +static const uint8_t table20[] = { 0x01, 0x08, 0x0B, 0x10 }; +static const uint8_t table21[] = { 0x01, 0x07, 0x0C, 0x10 }; +static const uint8_t table22[] = { 0x05, 0x0B, 0x0C, 0x10 }; +static const uint8_t table23[] = { 0x0B, 0x0C, 0x0D, 0x10 }; +static const uint8_t table24[] = { 0x07, 0x09, 0x0E, 0x10 }; +static const uint8_t table25[] = { 0x01, 0x04, 0x0F, 0x10 }; +static const uint8_t table26[] = { 0x07, 0x08, 0x0F, 0x10 }; +static const uint8_t table27[] = { 0x02, 0x0B, 0x0F, 0x10 }; + +static void hash_modes_19_to_27_tables_3(uint8_t *data, uint8_t *hash, const uint8_t *table) +{ + int i; + uint8_t val, it[4]; + uint16_t seed = 0xFFFF, tmp; + + memset(hash, 0x00, 16); + + for (i = 0; i < 4; i++) + { + it[i] = 0x10 - table[i]; + } + + for (i = 0; i < 16; i++) + { + val = ((seed >> it[0]) ^ (seed >> it[1]) ^ (seed >> it[2]) ^ (seed >> it[3])) & 0x01; + + if (val == 0x00) + { + seed = seed >> 1; + } + else + { + seed = (seed >> 1) | 0x8000; + } + tmp = seed + (data[i] << 8) + data[i + 32]; + + val = ((seed >> it[0]) ^ (seed >> it[1]) ^ (seed >> it[2]) ^ (seed >> it[3])) & 0x01; + + if (val == 0x00) + { + seed = seed >> 1; + } + else + { + seed = (seed >> 1) | 0x8000; + } + tmp = tmp + seed + (data[i + 16] << 8) + data[i + 48]; + + hash[i & 0x0F] ^= tmp >> 8; + hash[(i + 1) & 0x0F] ^= tmp; + } +} + +static void create_hash(uint8_t *data, int len, uint8_t *hash, int mode) +{ + if ((mode > 0x27) || (mode == 0x0B) || (mode == 0x0C) || + (mode == 0x0D) || (mode == 0x0E) || (mode == 0)) + { + memset(hash, 0, 16); + return; + } + + uint8_t dataPadded[64]; + + pad_data(data, len, dataPadded); + + switch (mode) + { + case 1: + hash_mode_01_custom_md5(dataPadded, hash); + break; + + case 2: + hash_mode_02(dataPadded, hash); + break; + + case 3: + hash_mode_03(dataPadded, hash); + break; + + case 4: + hash_modes_04_to_0A_tables(dataPadded, hash, table04); + break; + + case 5: + hash_modes_04_to_0A_tables(dataPadded, hash, table05); + break; + + case 6: + hash_modes_04_to_0A_tables(dataPadded, hash, table06); + break; + + case 7: + hash_modes_04_to_0A_tables(dataPadded, hash, table07); + break; + + case 8: + hash_modes_04_to_0A_tables(dataPadded, hash, table08); + break; + + case 9: + hash_modes_04_to_0A_tables(dataPadded, hash, table09); + break; + + case 10: + hash_modes_04_to_0A_tables(dataPadded, hash, table0A); + break; + + case 15: + hash_modes_0F_to_18_tables(dataPadded, hash, table0F); + break; + + case 16: + hash_modes_0F_to_18_tables(dataPadded, hash, table10); + break; + + case 17: + hash_modes_0F_to_18_tables(dataPadded, hash, table11); + break; + + case 18: + hash_modes_0F_to_18_tables(dataPadded, hash, table12); + break; + + case 19: + hash_modes_0F_to_18_tables(dataPadded, hash, table13); + break; + + case 20: + hash_modes_0F_to_18_tables(dataPadded, hash, table14); + break; + + case 21: + hash_modes_0F_to_18_tables(dataPadded, hash, table15); + break; + + case 22: + hash_modes_0F_to_18_tables(dataPadded, hash, table16); + break; + + case 23: + hash_modes_0F_to_18_tables(dataPadded, hash, table17); + break; + + case 24: + hash_modes_0F_to_18_tables(dataPadded, hash, table18); + break; + + case 25: + hash_modes_19_to_27_tables_3(dataPadded, hash, table19); + break; + + case 26: + hash_modes_19_to_27_tables_3(dataPadded, hash, table1A); + break; + + case 27: + hash_modes_19_to_27_tables_3(dataPadded, hash, table1B); + break; + + case 28: + hash_modes_19_to_27_tables_3(dataPadded, hash, table1C); + break; + + case 29: + hash_modes_19_to_27_tables_3(dataPadded, hash, table1D); + break; + + case 30: + hash_modes_19_to_27_tables_3(dataPadded, hash, table1E); + break; + + case 31: + hash_modes_19_to_27_tables_3(dataPadded, hash, table1F); + break; + + case 32: + hash_modes_19_to_27_tables_3(dataPadded, hash, table20); + break; + + case 33: + hash_modes_19_to_27_tables_3(dataPadded, hash, table21); + break; + + case 34: + hash_modes_19_to_27_tables_3(dataPadded, hash, table22); + break; + + case 35: + hash_modes_19_to_27_tables_3(dataPadded, hash, table23); + break; + + case 36: + hash_modes_19_to_27_tables_3(dataPadded, hash, table24); + break; + + case 37: + hash_modes_19_to_27_tables_3(dataPadded, hash, table25); + break; + + case 38: + hash_modes_19_to_27_tables_3(dataPadded, hash, table26); + break; + + case 39: + hash_modes_19_to_27_tables_3(dataPadded, hash, table27); + break; + + default: + cs_log("A new hash mode [%d] is in use.", mode); + break; + } +} + +static void create_hash_mode_03(uint8_t *data, uint8_t *hash) +{ + int i, j, c; + uint8_t buffer0[16], buffer1[8], buffer2[8], tmpBuff1[4], tmpBuff2[4]; + + uint8_t table[] = + { + 0x68, 0xCE, 0xE7, 0x71, 0xCC, 0x3A, 0x0B, 0x6E, 0x2A, 0x43, 0x17, 0x07, 0x5A, 0xD9, 0x14, 0x5B, + 0xB0, 0x8E, 0xA8, 0x7F, 0xD8, 0xA2, 0xCF, 0x73, 0xC2, 0xB9, 0x5D, 0x46, 0xDD, 0x2C, 0xE2, 0x2D, + 0xFD, 0x50, 0xE9, 0x7C, 0x28, 0x72, 0x9B, 0xAA, 0xEC, 0x24, 0x74, 0xAB, 0x00, 0x1C, 0x8B, 0x65, + 0x38, 0x13, 0x22, 0x82, 0xAC, 0x9A, 0x4D, 0x2B, 0xEA, 0x04, 0x31, 0x84, 0x32, 0x3D, 0x36, 0x53, + 0x5F, 0x42, 0x96, 0xDE, 0x47, 0x08, 0x51, 0x4B, 0x3E, 0xD1, 0x1E, 0x12, 0xD2, 0x1F, 0x7D, 0x26, + 0xCD, 0x57, 0x8C, 0xB6, 0xD3, 0xF8, 0x11, 0xAD, 0x6A, 0x88, 0x95, 0x21, 0xE8, 0xBF, 0x6B, 0x27, + 0xBE, 0xA3, 0x33, 0xB8, 0x9E, 0xB3, 0x6C, 0xC3, 0x06, 0xC7, 0x6F, 0x99, 0x97, 0xDA, 0x09, 0xAF, + 0xAE, 0xCB, 0x79, 0x37, 0x55, 0x85, 0x8D, 0x2F, 0x8A, 0x70, 0xA1, 0x7A, 0x66, 0x29, 0x67, 0x0F, + 0xEB, 0x9C, 0xC8, 0xC4, 0xD6, 0x4C, 0xDF, 0x1A, 0xC0, 0x01, 0x64, 0xBC, 0x4E, 0xE1, 0x54, 0xD7, + 0x4F, 0xB7, 0x5E, 0xCA, 0xF0, 0x91, 0xE4, 0x59, 0x4A, 0xC6, 0x83, 0x8F, 0xBD, 0x61, 0xFF, 0x56, + 0x92, 0xF1, 0x5C, 0x77, 0xC9, 0x20, 0xF4, 0xE5, 0x10, 0x69, 0x03, 0x1D, 0xD5, 0x45, 0xF6, 0x0E, + 0xEF, 0xA0, 0xE3, 0x58, 0xFC, 0xED, 0x80, 0x16, 0xEE, 0xFA, 0x02, 0xF5, 0xB4, 0x0A, 0xE0, 0x0C, + 0xF7, 0xF9, 0xBA, 0x7E, 0x18, 0x78, 0x19, 0xB5, 0x0D, 0x44, 0x34, 0xD4, 0xDC, 0x30, 0x6D, 0x3B, + 0x63, 0x41, 0x48, 0x40, 0xA7, 0xA5, 0xC5, 0x98, 0x76, 0x3F, 0xC1, 0x25, 0x93, 0x49, 0xD0, 0x62, + 0x2E, 0x75, 0xDB, 0x94, 0xF3, 0x52, 0x05, 0x81, 0xFB, 0xBB, 0xA6, 0x89, 0x39, 0xA4, 0xF2, 0xA9, + 0xFE, 0x60, 0x3C, 0x15, 0xB1, 0x35, 0x86, 0x9D, 0x9F, 0x90, 0x1B, 0xE6, 0x7B, 0x23, 0x87, 0xB2 + }; + + for (i = 0; i < 4; i++) + { + buffer0[0 + i] = data[12 + i]; + buffer0[4 + i] = data[8 + i]; + buffer0[8 + i] = data[4 + i]; + buffer0[12 + i] = data[0 + i]; + } + + for (c = 0; c < 12; c++) + { + for (i = 0; i < 4; i++) + { + buffer1[0 + i] = buffer0[8 + i] ^ buffer0[12 + i]; + buffer1[4 + i] = buffer0[0 + i] ^ buffer0[4 + i]; + } + + for (i = 0; i < 8; i++) + { + buffer1[i] = table[buffer1[i] ^ data[16 + 16 * (c % 3) + i]]; + } + + for (j = 0; j < 8; j++) + { + buffer2[j] = 0; + for (i = 0; i < 8; i++) + { + buffer2[j] ^= buffer1[i] * (j * i + 1); + } + } + + for (i = 0; i < 8; i++) + { + buffer2[i] = table[buffer2[i] ^ data[24 + 16 * (c % 3) + i]] ^ data[16 + 16 * (c % 3) + i]; + } + + for (i = 0; i < 4; i++) + { + buffer0[12 + i] ^= buffer2[0 + i]; + buffer0[8 + i] ^= buffer2[0 + i]; + buffer0[4 + i] ^= buffer2[4 + i]; + buffer0[0 + i] ^= buffer2[4 + i]; + } + + tmpBuff1[0] = buffer0[14]; + tmpBuff1[1] = buffer0[15]; + tmpBuff1[2] = buffer0[12] ^ buffer0[14]; + tmpBuff1[3] = buffer0[13] ^ buffer0[15]; + + tmpBuff2[0] = buffer0[6]; + tmpBuff2[1] = buffer0[7]; + tmpBuff2[2] = buffer0[4] ^ buffer0[6]; + tmpBuff2[3] = buffer0[5] ^ buffer0[7]; + + for (i = 0; i < 4; i++) + { + buffer0[12 + i] = tmpBuff1[i]; + buffer0[4 + i] = tmpBuff2[i]; + } + } + + for (i = 0; i < 4; i++) + { + hash[0 + i] = buffer0[12 + i] ^ data[0 + i]; + hash[4 + i] = buffer0[8 + i] ^ data[4 + i]; + hash[8 + i] = buffer0[4 + i] ^ data[8 + i]; + hash[12 + i] = buffer0[0 + i] ^ data[12 + i]; + } +} + +static void create_data_cw_mode_03(uint8_t *seed, int lenSeed, uint8_t *basecw, + uint8_t val, uint8_t *ecmBody, uint8_t *data) +{ + int idxData = 8, idxSeed = 0, idxBase = 0; + uint8_t padding[] = + { + 0x4A, 0x56, 0x7F, 0x16, 0xFC, 0x1F, 0x5B, 0x95, + 0x19, 0xEF, 0x75, 0x14, 0x0E, 0x9E, 0x17, 0x3C, + 0xF5, 0xB7, 0xA0, 0x93, 0xA3, 0x0F, 0xFA, 0x38, + 0x7A, 0x34, 0x6C, 0xDC, 0xFB, 0xB0, 0x24, 0x42, + 0x74, 0x72, 0x1C, 0xDC, 0x1E, 0xA1, 0x6D, 0xAB, + 0xC8, 0x44, 0x53, 0xEF, 0x56, 0x00, 0xE9, 0x97, + 0x48, 0x77, 0xF8, 0x00, 0x8E, 0x0B, 0x78, 0xA2 + }; + + memcpy(data + 8, padding, 56); + + data[0] = ecmBody[0x0F]; + data[1] = ecmBody[0x09]; + data[2] = ecmBody[0x10]; + data[3] = ecmBody[0x11]; + data[4] = ecmBody[0x05]; + data[5] = ecmBody[0x07]; + data[6] = ecmBody[0x08]; + data[7] = ecmBody[0x0A]; + + while (idxBase < 7) + { + if ((idxBase == 0) || (idxBase == 2) || (idxBase == 5)) + { + data[idxData++] = val; + } + + if (idxSeed < lenSeed) + { + data[idxData++] = seed[idxSeed++]; + } + + data[idxData++] = basecw[idxBase++]; + } +} + +static void create_data_unmask_mode_03(uint8_t *ecmBody, uint8_t *data) +{ + uint8_t padding[] = + { + 0xB1, 0x7C, 0xD2, 0xA7, 0x5E, 0x45, 0x6C, 0x36, + 0xF0, 0xB6, 0x81, 0xF3, 0x25, 0x06, 0x65, 0x06, + 0x6B, 0xBF, 0x4C, 0xE7, 0xED, 0x6E, 0x85, 0x00, + 0xCC, 0xF2, 0x61, 0x48, 0x62, 0x24, 0x0E, 0x3C, + 0x05, 0x89, 0xA5, 0x39, 0x5A, 0x4E, 0x9B, 0xC8, + 0x14, 0x78, 0xEA, 0xB6, 0xFB, 0xF8, 0x10, 0xE6, + 0x61, 0xF5, 0x3A, 0xBC, 0x5B, 0x79, 0x09, 0x97 + }; + + memcpy(data + 8, padding, 56); + + data[0] = ecmBody[0x17]; + data[1] = ecmBody[0x26]; + data[2] = ecmBody[0x19]; + data[3] = ecmBody[0x21]; + data[4] = ecmBody[0x26]; + data[5] = ecmBody[0x31]; + data[6] = ecmBody[0x21]; + data[7] = ecmBody[0x27]; +} + +static void hash_04_add(uint32_t *buffer, int a, int b, int c, int d, int e, int f) +{ + uint32_t tmp1 = (buffer[a] & 1) + (buffer[b] & 1); + uint32_t tmp2 = (buffer[a] >> 1) + (buffer[b] >> 1) + (tmp1 >> 1); + + buffer[e] = buffer[c] + buffer[d] + (tmp2 >> 31); + buffer[f] = tmp2 + tmp2 + (tmp1 & 1); +} + +static void hash_04_shift(uint32_t *buffer, int a, int b, uint8_t shift) +{ + uint32_t tmp1 = (buffer[a] >> (32 - shift)) + (buffer[b] << shift); + uint32_t tmp2 = (buffer[b] >> (32 - shift)) + (buffer[a] << shift); + + buffer[b] = tmp1; + buffer[a] = tmp2; +} + +static void hash_04_xor(uint32_t *buffer, int a, int b, int c, int d) +{ + buffer[a] ^= buffer[b]; + buffer[c] ^= buffer[d]; +} + +static void hash_04_swap(uint32_t *buffer, int a, int b) +{ + uint32_t tmp = buffer[a]; + + buffer[a] = buffer[b]; + buffer[b] = tmp; +} + +static void hash_04_core(uint32_t *buffer) +{ + hash_04_add(buffer, 0, 6, 7, 1, 7, 6); + hash_04_shift(buffer, 5, 4, 0x0D); + hash_04_xor(buffer, 4, 2, 5, 3); + hash_04_swap(buffer, 7, 6); + hash_04_add(buffer, 6, 2, 3, 7, 3, 2); + hash_04_shift(buffer, 1, 0, 0x10); + hash_04_xor(buffer, 0, 4, 1, 5); + hash_04_add(buffer, 6, 2, 3, 7, 7, 6); + hash_04_shift(buffer, 1, 0, 0x15); + hash_04_add(buffer, 6, 0, 1, 7, 1, 0); + hash_04_xor(buffer, 2, 4, 3, 5); + hash_04_shift(buffer, 5, 4, 0x11); + hash_04_xor(buffer, 4, 2, 5, 3); + hash_04_swap(buffer, 3, 2); +} + +static void create_hash_mode_04(uint8_t *data, uint8_t *hash) +{ + int i, j; + uint32_t d0, d1, h0, h1, h2, h3; + uint32_t buffer[] = + { + 0x1F253724, 0x3E8136B3, 0x9677CEDF, 0x25B5E75A, + 0x9494BC16, 0xCFD3FB34, 0xF37C75BB, 0x97D4632E + }; + + for (j = 0; j < 64; j += 8) + { + d0 = (data[j + 3] << 24) + (data[j + 2] << 16) + (data[j + 1] << 8) + data[j + 0]; + d1 = (data[j + 7] << 24) + (data[j + 6] << 16) + (data[j + 5] << 8) + data[j + 4]; + + buffer[0] ^= d0; + buffer[1] ^= d1; + + for (i = 0; i < 2; i++) + { + hash_04_core(buffer); + } + + buffer[6] ^= d0; + buffer[7] ^= d1; + } + + buffer[1] ^= 0x40000000; + buffer[0] ^= 0x00000000; + + for (i = 0; i < 2; i++) + { + hash_04_core(buffer); + } + + buffer[7] ^= 0x40000000; + buffer[6] ^= 0x00000000; + buffer[2] ^= 0xEE; + + for (i = 0; i < 4; i++) + { + hash_04_core(buffer); + } + + h0 = buffer[0] ^ buffer[2] ^ buffer[4] ^ buffer[6]; + h1 = buffer[1] ^ buffer[3] ^ buffer[5] ^ buffer[7]; + + hash[0] = (uint8_t) h0; + hash[1] = (uint8_t) (h0 >> 8); + hash[2] = (uint8_t) (h0 >> 16); + hash[3] = (uint8_t) (h0 >> 24); + hash[4] = (uint8_t) h1; + hash[5] = (uint8_t) (h1 >> 8); + hash[6] = (uint8_t) (h1 >> 16); + hash[7] = (uint8_t) (h1 >> 24); + + buffer[4] ^= 0xDD; + + for (i = 0; i < 4; i++) + { + hash_04_core(buffer); + } + + h2 = buffer[0] ^ buffer[2] ^ buffer[4] ^ buffer[6]; + h3 = buffer[1] ^ buffer[3] ^ buffer[5] ^ buffer[7]; + + hash[8] = (uint8_t) h2; + hash[9] = (uint8_t) (h2 >> 8); + hash[10] = (uint8_t) (h2 >> 16); + hash[11] = (uint8_t) (h2 >> 24); + hash[12] = (uint8_t) h3; + hash[13] = (uint8_t) (h3 >> 8); + hash[14] = (uint8_t) (h3 >> 16); + hash[15] = (uint8_t) (h3 >> 24); +} + +static void create_data_cw_mode_04(uint8_t *seed, int lenSeed, uint8_t *basecw, + uint8_t val, uint8_t *ecmBody, uint8_t *data) +{ + uint8_t padding[] = + { + 0x18, 0xD6, 0x24, 0xA8, 0xDE, 0x14, 0xD8, 0x30, + 0x3C, 0xB2, 0x24, 0x54, 0x17, 0x5A, 0x28, 0x61, + 0xBC, 0xB9, 0x29, 0xAD, 0xA5, 0x13, 0xD4, 0x24, + 0x6D, 0x61, 0x40, 0xC8, 0xFD, 0x27, 0xD7, 0xFF, + 0x3E, 0x84, 0x50, 0xC2, 0x47, 0x4C, 0xD5, 0xC5, + 0xF2, 0x79, 0xAD, 0x02, 0xC5, 0x05, 0x7B, 0xFD, + 0x60, 0x4A, 0x16, 0xE5, 0xAA, 0x0E, 0x97, 0x1C + }; + + memcpy(data + 8, padding, 56); + + data[0] = ecmBody[0x0E]; + data[1] = ecmBody[0x0A]; + data[2] = ecmBody[0x0C]; + data[3] = ecmBody[0x04]; + data[4] = ecmBody[0x10]; + data[5] = ecmBody[0x08]; + data[6] = ecmBody[0x05]; + data[7] = ecmBody[0x0F]; + + int idxData = 8, idxSeed = 0, idxBase = 0; + + while (idxBase < 7) + { + if ((idxBase == 0) || (idxBase == 1) || (idxBase == 2)) + { + data[idxData++] = val; + } + + if (idxSeed < lenSeed) + { + data[idxData++] = seed[idxSeed++]; + } + + data[idxData++] = basecw[idxBase++]; + } +} + +static void create_data_unmask_mode_04(uint8_t *ecmBody, uint8_t *data) +{ + uint8_t padding[] = + { + 0x0E, 0x4A, 0x85, 0x85, 0xF9, 0xC0, 0xCC, 0x00, + 0xBA, 0x9B, 0x98, 0x35, 0x4C, 0xD2, 0xC1, 0x6C, + 0x87, 0x32, 0x9B, 0x82, 0x31, 0x5B, 0x1D, 0xB4, + 0xB8, 0x98, 0x74, 0xFF, 0x31, 0x66, 0x08, 0x79, + 0x47, 0xCE, 0x96, 0x4D, 0xE9, 0x52, 0xCF, 0x8F, + 0xEC, 0x5C, 0x07, 0xBC, 0x09, 0xA2, 0x82, 0x78, + 0x3D, 0xB9, 0xFF, 0x3F, 0x76, 0x72, 0x6F, 0x9C + }; + + memcpy(data + 8, padding, 56); + + data[0] = ecmBody[0x17]; + data[1] = ecmBody[0x2B]; + data[2] = ecmBody[0x1D]; + data[3] = ecmBody[0x2D]; + data[4] = ecmBody[0x0B]; + data[5] = ecmBody[0x06]; + data[6] = ecmBody[0x2F]; + data[7] = ecmBody[0x1E]; +} + +static uint8_t get_mode_cw(uint8_t *extraData) +{ + uint64_t data = ((uint32_t)extraData[0] << 24) + (extraData[1] << 16) + (extraData[2] << 8) + extraData[3]; + uint64_t t1 = (data * 0x76E9DEA7) >> 50; + uint64_t t2 = (t1 * 0x51EB851F) >> 36; + uint64_t t3 = t2 * 0x32; + uint8_t r = t1 - t3; + return r; +} + +static uint8_t get_mode_unmask(uint8_t *extraData) +{ + uint64_t data = ((uint32_t)extraData[0] << 24) + (extraData[1] << 16) + (extraData[2] << 8) + extraData[3]; + uint64_t t1 = (data * 0xB9CD6BE5) >> 45; + uint64_t t2 = (t1 * 0x51EB851F) >> 36; + uint64_t t3 = t2 * 0x32; + uint8_t r = t1 - t3; + return r; +} + +static void create_data_ecm_emm(uint8_t *emmEcm, uint8_t *pos, int lenHeader, int len, uint8_t *data) +{ + int i; + + for (i = 0; i < len; i++) + { + data[i] = emmEcm[lenHeader + pos[i]]; + } +} + +static uint8_t create_data_cw(uint8_t *seed, uint8_t lenSeed, uint8_t *baseCw, + uint8_t val, uint8_t *seedEcmCw, uint8_t *data) +{ + int i; + + for (i = 0; i < lenSeed; i++) + { + data[i] = seed[i]; + } + + for (i = 0; i < 7; i++) + { + data[lenSeed + i] = baseCw[i]; + } + + data[lenSeed + 7] = val; + + for (i = 0; i < 16; i++) + { + data[lenSeed + 7 + 1 + i] = seedEcmCw[i]; + } + + return lenSeed + 7 + 1 + 0x10; +} + +static uint8_t unmask_ecm(uint8_t *ecm, uint8_t *seedEcmCw, uint8_t *modeCW) +{ + int i, l; + uint8_t data[64], mask[16]; + uint8_t hashModeEcm, hashModeCw, modeUnmask = 0; + uint32_t crc; + + uint8_t sourcePos[] = + { + 0x04, 0x05, 0x06, 0x07, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x17, 0x1C, 0x1D, 0x1F, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x29, 0x2C, 0x2D, 0x2E + }; + + uint8_t destPos[] = + { + 0x08, 0x09, 0x11, 0x18, 0x19, 0x1A, 0x1B, 0x1E, + 0x20, 0x21, 0x22, 0x28, 0x2A, 0x2B, 0x2F, 0x30 + }; + + uint8_t seedCwPos[] = { 0x07, 0x0A, 0x04, 0x0D, 0x05, 0x0E, 0x06, 0x0B, 0x10, 0x0C, 0x0F }; + + // Create seed for CW decryption + memset(seedEcmCw, 0, 16); + + int extraBytesLen = ecm[9]; + int startOffset = extraBytesLen + 10; + + for (i = 0; i < 11; i++) + { + seedEcmCw[i] = ecm[startOffset + seedCwPos[i]]; + } + + *modeCW = 0; + if (extraBytesLen > 0) + { + *modeCW = get_mode_cw(ecm + 10); + } + + // Read hash mode CW + hashModeCw = ecm[28 + extraBytesLen] ^ crc8_calc(seedEcmCw, 16); + + // Create mask for ECM decryption + create_data_ecm_emm(ecm, sourcePos, startOffset, 24, data); + + hashModeEcm = ecm[8] ^ crc8_calc(data, 24); + + if (extraBytesLen > 0) + { + modeUnmask = get_mode_unmask(ecm + 10); + } + + if (modeUnmask == 0x03) + { + ecm[startOffset + 0x21] -= ecm[startOffset + 0x07]; + ecm[startOffset + 0x26] -= ecm[startOffset + 0x05]; + ecm[startOffset + 0x26] -= ecm[startOffset + 0x08]; + ecm[startOffset + 0x19] -= ecm[startOffset + 0x06]; + ecm[startOffset + 0x31] -= ecm[startOffset + 0x09]; + ecm[startOffset + 0x27] -= ecm[startOffset + 0x0C]; + ecm[startOffset + 0x21] -= ecm[startOffset + 0x0B]; + ecm[startOffset + 0x17] -= ecm[startOffset + 0x04]; + + create_data_unmask_mode_03(ecm + startOffset, data); + create_hash_mode_03(data, mask); + + // Unmask body + ecm[startOffset + 0x06] ^= mask[0x02]; + ecm[startOffset + 0x0B] ^= mask[0x06]; + ecm[startOffset + 0x0C] ^= mask[0x07]; + ecm[startOffset + 0x0D] ^= mask[0x08]; + ecm[startOffset + 0x0E] ^= mask[0x09]; + ecm[startOffset + 0x0F] ^= mask[0x0A]; + ecm[startOffset + 0x11] ^= mask[0x0B]; + ecm[startOffset + 0x18] ^= mask[0x0C]; + ecm[startOffset + 0x2D] ^= mask[0x0A]; + ecm[startOffset + 0x07] ^= mask[0x03]; + ecm[startOffset + 0x1B] ^= mask[0x0D]; + ecm[startOffset + 0x30] ^= mask[0x0C]; + ecm[startOffset + 0x1C] ^= mask[0x0E]; + ecm[startOffset + 0x1E] ^= mask[0x00]; + ecm[startOffset + 0x04] ^= mask[0x00]; + ecm[startOffset + 0x05] ^= mask[0x01]; + ecm[startOffset + 0x1F] ^= mask[0x01]; + ecm[startOffset + 0x2C] ^= mask[0x09]; + ecm[startOffset + 0x20] ^= mask[0x02]; + ecm[startOffset + 0x1D] ^= mask[0x0F]; + ecm[startOffset + 0x23] ^= mask[0x04]; + ecm[startOffset + 0x09] ^= mask[0x05]; + ecm[startOffset + 0x22] ^= mask[0x03]; + ecm[startOffset + 0x24] ^= mask[0x05]; + ecm[startOffset + 0x08] ^= mask[0x04]; + ecm[startOffset + 0x28] ^= mask[0x06]; + ecm[startOffset + 0x29] ^= mask[0x07]; + ecm[startOffset + 0x2A] ^= mask[0x08]; + ecm[startOffset + 0x2E] ^= mask[0x0B]; + + for (i = 0; i < ecm[9]; i++) + { + ecm[10 + i] = 0x00; + } + } + else if (modeUnmask == 0x04) + { + ecm[startOffset + 0x1E] -= ecm[startOffset + 0x0D]; + ecm[startOffset + 0x1D] -= ecm[startOffset + 0x07]; + ecm[startOffset + 0x2B] -= ecm[startOffset + 0x05]; + ecm[startOffset + 0x2D] -= ecm[startOffset + 0x08]; + ecm[startOffset + 0x17] -= ecm[startOffset + 0x04]; + ecm[startOffset + 0x2F] -= ecm[startOffset + 0x0C]; + ecm[startOffset + 0x06] -= ecm[startOffset + 0x0A]; + ecm[startOffset + 0x0B] -= ecm[startOffset + 0x09]; + + create_data_unmask_mode_04(ecm + startOffset, data); + create_hash_mode_04(data, mask); + + // Unmask body + ecm[startOffset + 0x04] ^= mask[0x00]; + ecm[startOffset + 0x05] ^= mask[0x01]; + ecm[startOffset + 0x07] ^= mask[0x02]; + ecm[startOffset + 0x08] ^= mask[0x03]; + ecm[startOffset + 0x09] ^= mask[0x04]; + ecm[startOffset + 0x0A] ^= mask[0x05]; + ecm[startOffset + 0x0C] ^= mask[0x06]; + ecm[startOffset + 0x0D] ^= mask[0x07]; + ecm[startOffset + 0x0E] ^= mask[0x08]; + ecm[startOffset + 0x10] ^= mask[0x09]; + ecm[startOffset + 0x11] ^= mask[0x0A]; + ecm[startOffset + 0x18] ^= mask[0x0B]; + ecm[startOffset + 0x1A] ^= mask[0x0C]; + ecm[startOffset + 0x1B] ^= mask[0x0D]; + ecm[startOffset + 0x1C] ^= mask[0x0E]; + ecm[startOffset + 0x1F] ^= mask[0x0F]; + ecm[startOffset + 0x22] ^= mask[0x00]; + ecm[startOffset + 0x24] ^= mask[0x01]; + ecm[startOffset + 0x25] ^= mask[0x02]; + ecm[startOffset + 0x26] ^= mask[0x03]; + ecm[startOffset + 0x27] ^= mask[0x04]; + ecm[startOffset + 0x28] ^= mask[0x05]; + ecm[startOffset + 0x29] ^= mask[0x06]; + ecm[startOffset + 0x2A] ^= mask[0x07]; + ecm[startOffset + 0x2C] ^= mask[0x08]; + ecm[startOffset + 0x2E] ^= mask[0x09]; + ecm[startOffset + 0x31] ^= mask[0x0A]; + + for (i = 0; i < ecm[9]; i++) + { + ecm[10 + i] = 0x00; + } + } + else + { + create_hash(data, 24, mask, hashModeEcm); + + // Unmask body + for (i = 0; i < 16; i++) + { + ecm[startOffset + destPos[i]] ^= mask[i & 0x0F]; + } + } + + // Fix header + ecm[3] &= 0x0F; + ecm[3] |= 0x30; + ecm[8] = 0x00; + ecm[28 + extraBytesLen] = 0x00; + + // Fix CRC (optional) + l = (((ecm[1] << 8) + ecm[2]) & 0xFFF) + 3 - 4; + + crc = ccitt32_crc(ecm, l); + + ecm[l + 0] = crc >> 24; + ecm[l + 1] = crc >> 16; + ecm[l + 2] = crc >> 8; + ecm[l + 3] = crc >> 0; + + for (i = 0; i < 11; i++) + { + seedEcmCw[i] = ecm[startOffset + seedCwPos[i]]; + } + + return hashModeCw; +} + +static void create_cw(uint8_t *seed, uint8_t lenSeed, uint8_t *baseCw, uint8_t val, uint8_t *seedEcmCw, + uint8_t *cw, int modeDesCsa, int hashMode, int modeCW, uint8_t *ecmBody) +{ + int i; + uint8_t data[64], hash[16], lenData; + uint8_t tableFixParity[] = + { + 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x07, 0x07, + 0x08, 0x08, 0x0B, 0x0B, 0x0D, 0x0D, 0x0E, 0x0E, + 0x10, 0x10, 0x13, 0x13, 0x15, 0x15, 0x16, 0x16, + 0x19, 0x19, 0x1A, 0x1A, 0x1C, 0x1C, 0x1F, 0x1F, + 0x20, 0x20, 0x23, 0x23, 0x25, 0x25, 0x26, 0x26, + 0x29, 0x29, 0x2A, 0x2A, 0x2C, 0x2C, 0x2F, 0x2F, + 0x31, 0x31, 0x32, 0x32, 0x34, 0x34, 0x37, 0x37, + 0x38, 0x38, 0x3B, 0x3B, 0x3D, 0x3D, 0x3E, 0x3E, + 0x40, 0x40, 0x43, 0x43, 0x45, 0x45, 0x46, 0x46, + 0x49, 0x49, 0x4A, 0x4A, 0x4C, 0x4C, 0x4F, 0x4F, + 0x51, 0x51, 0x52, 0x52, 0x54, 0x54, 0x57, 0x57, + 0x58, 0x58, 0x5B, 0x5B, 0x5D, 0x5D, 0x5E, 0x5E, + 0x61, 0x61, 0x62, 0x62, 0x64, 0x64, 0x67, 0x67, + 0x68, 0x68, 0x6B, 0x6B, 0x6D, 0x6D, 0x6E, 0x6E, + 0x70, 0x70, 0x73, 0x73, 0x75, 0x75, 0x76, 0x76, + 0x79, 0x79, 0x7A, 0x7A, 0x7C, 0x7C, 0x7F, 0x7F, + 0x80, 0x80, 0x83, 0x83, 0x85, 0x85, 0x86, 0x86, + 0x89, 0x89, 0x8A, 0x8A, 0x8C, 0x8C, 0x8F, 0x8F, + 0x91, 0x91, 0x92, 0x92, 0x94, 0x94, 0x97, 0x97, + 0x98, 0x98, 0x9B, 0x9B, 0x9D, 0x9D, 0x9E, 0x9E, + 0xA1, 0xA1, 0xA2, 0xA2, 0xA4, 0xA4, 0xA7, 0xA7, + 0xA8, 0xA8, 0xAB, 0xAB, 0xAD, 0xAD, 0xAE, 0xAE, + 0xB0, 0xB0, 0xB3, 0xB3, 0xB5, 0xB5, 0xB6, 0xB6, + 0xB9, 0xB9, 0xBA, 0xBA, 0xBC, 0xBC, 0xBF, 0xBF, + 0xC1, 0xC1, 0xC2, 0xC2, 0xC4, 0xC4, 0xC7, 0xC7, + 0xC8, 0xC8, 0xCB, 0xCB, 0xCD, 0xCD, 0xCE, 0xCE, + 0xD0, 0xD0, 0xD3, 0xD3, 0xD5, 0xD5, 0xD6, 0xD6, + 0xD9, 0xD9, 0xDA, 0xDA, 0xDC, 0xDC, 0xDF, 0xDF, + 0xE0, 0xE0, 0xE3, 0xE3, 0xE5, 0xE5, 0xE6, 0xE6, + 0xE9, 0xE9, 0xEA, 0xEA, 0xEC, 0xEC, 0xEF, 0xEF, + 0xF1, 0xF1, 0xF2, 0xF2, 0xF4, 0xF4, 0xF7, 0xF7, + 0xF8, 0xF8, 0xFB, 0xFB, 0xFD, 0xFD, 0xFE, 0xFE + }; + + if (modeCW == 0x03) + { + create_data_cw_mode_03(seed, lenSeed, baseCw, val, ecmBody, data); + create_hash_mode_03(data, hash); + + cw[0] = hash[0x09]; + cw[1] = hash[0x01]; + cw[2] = hash[0x0F]; + cw[3] = hash[0x0E]; + cw[4] = hash[0x04]; + cw[5] = hash[0x02]; + cw[6] = hash[0x05]; + cw[7] = hash[0x0D]; + } + else if (modeCW == 0x04) + { + create_data_cw_mode_04(seed, lenSeed, baseCw, val, ecmBody, data); + create_hash_mode_04(data, hash); + + cw[0] = hash[0x08]; + cw[1] = hash[0x0F]; + cw[2] = hash[0x02]; + cw[3] = hash[0x0A]; + cw[4] = hash[0x06]; + cw[5] = hash[0x03]; + cw[6] = hash[0x09]; + cw[7] = hash[0x0D]; + } + else + { + lenData = create_data_cw(seed, lenSeed, baseCw, val, seedEcmCw, data); + create_hash(data, lenData, hash, hashMode); + + for (i = 0; i < 8; i++) + { + cw[i] = hash[i]; + } + } + + if (modeDesCsa == 0) // DES - Fix Parity Bits + { + for (i = 0; i < 8; i++) + { + cw[i] = tableFixParity[cw[i]]; + } + } + else if (modeDesCsa == 1) // CSA - Fix Checksums + { + cw[3] = cw[0] + cw[1] + cw[2]; + cw[7] = cw[4] + cw[5] + cw[6]; + } +} + +static uint32_t create_channel_hash(uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens) +{ + uint8_t buffer[8]; + uint32_t channel_hash = 0; + + if (ens) + { + i2b_buf(2, tsid, buffer); + i2b_buf(2, onid, buffer + 2); + i2b_buf(4, ens, buffer + 4); + + channel_hash = crc32(caid, buffer, sizeof(buffer)); + } + + return channel_hash; +} + +static uint16_t get_channel_group(uint32_t channel_hash) +{ + uint8_t tmp[2]; + uint16_t group = 0; + + if (channel_hash && emu_find_key('P', channel_hash, 0x00000000, "GROUP", tmp, 2, 0, 0, 0, NULL)) + { + group = b2i(2, tmp); + } + + return group; +} + +static inline int8_t get_ecm_key(uint8_t *key, uint32_t provider, uint32_t ignore_mask, uint8_t keyIndex, uint32_t keyRef) +{ + return emu_find_key('P', provider, ignore_mask, keyIndex == 1 ? "01" : "00", key, 7, 0, keyRef, 0, NULL); +} + +static inline int8_t get_emm_key(uint8_t *key, char *uniqueAddress, uint32_t keyRef, uint32_t *groupId) +{ + return emu_find_key('P', 0, 0xFFFFFFFF, uniqueAddress, key, 7, 0, keyRef, 0, groupId); +} + +static const uint8_t PowerVu_A0_S_1[16] = +{ + 0x33, 0xA4, 0x44, 0x3C, 0xCA, 0x2E, 0x75, 0x7B, + 0xBC, 0xE6, 0xE5, 0x35, 0xA0, 0x55, 0xC9, 0xA2 +}; + +static const uint8_t PowerVu_A0_S_2[16] = +{ + 0x5A, 0xB0, 0x2C, 0xBC, 0xDA, 0x32, 0xE6, 0x92, + 0x40, 0x53, 0x6E, 0xF9, 0x69, 0x11, 0x1E, 0xFB +}; + +static const uint8_t PowerVu_A0_S_3[16] = +{ + 0x4E, 0x18, 0x9B, 0x19, 0x79, 0xFB, 0x01, 0xFA, + 0xE3, 0xE1, 0x28, 0x3D, 0x32, 0xE4, 0x92, 0xEA +}; + +static const uint8_t PowerVu_A0_S_4[16] = +{ + 0x05, 0x6F, 0x37, 0x66, 0x35, 0xE1, 0x58, 0xD0, + 0xB4, 0x6A, 0x97, 0xAE, 0xD8, 0x91, 0x27, 0x56 +}; + +static const uint8_t PowerVu_A0_S_5[16] = +{ + 0x7B, 0x26, 0xAD, 0x34, 0x3D, 0x77, 0x39, 0x51, + 0xE0, 0xE0, 0x48, 0x8C, 0x39, 0xF5, 0xE8, 0x47 +}; + +static const uint8_t PowerVu_A0_S_6[16] = +{ + 0x74, 0xFA, 0x4D, 0x79, 0x42, 0x39, 0xD1, 0xA4, + 0x99, 0xA3, 0x97, 0x07, 0xDF, 0x14, 0x3A, 0xC4 +}; + +static const uint8_t PowerVu_A0_S_7[16] = +{ + 0xC6, 0x1E, 0x3C, 0x24, 0x11, 0x08, 0x5D, 0x6A, + 0xEB, 0x97, 0xB9, 0x25, 0xA7, 0xFA, 0xE9, 0x1A +}; + +static const uint8_t PowerVu_A0_S_8[16] = +{ + 0x9A, 0xAD, 0x72, 0xD7, 0x7C, 0x68, 0x3B, 0x55, + 0x1D, 0x4A, 0xA2, 0xB0, 0x38, 0xB9, 0x56, 0xD0 +}; + +static const uint8_t PowerVu_A0_S_9[32] = +{ + 0x61, 0xDA, 0x5F, 0xB7, 0xEB, 0xC6, 0x3F, 0x6C, + 0x09, 0xF3, 0x64, 0x38, 0x33, 0x08, 0xAA, 0x15, + 0xCC, 0xEF, 0x22, 0x64, 0x01, 0x2C, 0x12, 0xDE, + 0xF4, 0x6E, 0x3C, 0xCD, 0x1A, 0x64, 0x63, 0x7C +}; + +static const uint8_t PowerVu_00_S_1[16] = +{ + 0x97, 0x13, 0xEB, 0x6B, 0x04, 0x5E, 0x60, 0x3A, + 0xD9, 0xCC, 0x91, 0xC2, 0x5A, 0xFD, 0xBA, 0x0C +}; + +static const uint8_t PowerVu_00_S_2[16] = +{ + 0x61, 0x3C, 0x03, 0xB0, 0xB5, 0x6F, 0xF8, 0x01, + 0xED, 0xE0, 0xE5, 0xF3, 0x78, 0x0F, 0x0A, 0x73 +}; + +static const uint8_t PowerVu_00_S_3[16] = +{ + 0xFD, 0xDF, 0xD2, 0x97, 0x06, 0x14, 0x91, 0xB5, + 0x36, 0xAD, 0xBC, 0xE1, 0xB3, 0x00, 0x66, 0x41 +}; + +static const uint8_t PowerVu_00_S_4[16] = +{ + 0x8B, 0xD9, 0x18, 0x0A, 0xED, 0xEE, 0x61, 0x34, + 0x1A, 0x79, 0x80, 0x8C, 0x1E, 0x7F, 0xC5, 0x9F +}; + +static const uint8_t PowerVu_00_S_5[16] = +{ + 0xB0, 0xA1, 0xF2, 0xB8, 0xEA, 0x72, 0xDD, 0xD3, + 0x30, 0x65, 0x2B, 0x1E, 0xE9, 0xE1, 0x45, 0x29 +}; + +static const uint8_t PowerVu_00_S_6[16] = +{ + 0x5D, 0xCA, 0x53, 0x75, 0xB2, 0x24, 0xCE, 0xAF, + 0x21, 0x54, 0x9E, 0xBE, 0x02, 0xA9, 0x4C, 0x5D +}; + +static const uint8_t PowerVu_00_S_7[16] = +{ + 0x42, 0x66, 0x72, 0x83, 0x1B, 0x2D, 0x22, 0xC9, + 0xF8, 0x4D, 0xBA, 0xCD, 0xBB, 0x20, 0xBD, 0x6B +}; + +static const uint8_t PowerVu_00_S_8[16] = +{ + 0xC4, 0x0C, 0x6B, 0xD3, 0x6D, 0x94, 0x7E, 0x53, + 0xCE, 0x96, 0xAC, 0x40, 0x2C, 0x7A, 0xD3, 0xA9 +}; + +static const uint8_t PowerVu_00_S_9[32] = +{ + 0x31, 0x82, 0x4F, 0x9B, 0xCB, 0x6F, 0x9D, 0xB7, + 0xAE, 0x68, 0x0B, 0xA0, 0x93, 0x15, 0x32, 0xE2, + 0xED, 0xE9, 0x47, 0x29, 0xC2, 0xA8, 0x92, 0xEF, + 0xBA, 0x27, 0x22, 0x57, 0x76, 0x54, 0xC0, 0x59 +}; + +static uint8_t powervu_sbox(uint8_t *input, uint8_t mode) +{ + uint8_t s_index, bit, last_index, last_bit; + uint8_t const *Sbox1, *Sbox2, *Sbox3, *Sbox4, *Sbox5, *Sbox6, *Sbox7, *Sbox8, *Sbox9; + + if (mode) + { + Sbox1 = PowerVu_A0_S_1; + Sbox2 = PowerVu_A0_S_2; + Sbox3 = PowerVu_A0_S_3; + Sbox4 = PowerVu_A0_S_4; + Sbox5 = PowerVu_A0_S_5; + Sbox6 = PowerVu_A0_S_6; + Sbox7 = PowerVu_A0_S_7; + Sbox8 = PowerVu_A0_S_8; + Sbox9 = PowerVu_A0_S_9; + } + else + { + Sbox1 = PowerVu_00_S_1; + Sbox2 = PowerVu_00_S_2; + Sbox3 = PowerVu_00_S_3; + Sbox4 = PowerVu_00_S_4; + Sbox5 = PowerVu_00_S_5; + Sbox6 = PowerVu_00_S_6; + Sbox7 = PowerVu_00_S_7; + Sbox8 = PowerVu_00_S_8; + Sbox9 = PowerVu_00_S_9; + } + + bit = (get_bit(input[2], 0) << 2) | (get_bit(input[3], 4) << 1) | (get_bit(input[5], 3)); + s_index = (get_bit(input[0], 0) << 3) | (get_bit(input[2], 6) << 2) | (get_bit(input[2], 4) << 1) | (get_bit(input[5], 7)); + last_bit = get_bit(Sbox1[s_index], 7 - bit); + + bit = (get_bit(input[5], 0) << 2) | (get_bit(input[4], 0) << 1) | (get_bit(input[6], 2)); + s_index = (get_bit(input[2], 1) << 3) | (get_bit(input[2], 2) << 2) | (get_bit(input[5], 5) << 1) | (get_bit(input[5], 1)); + last_bit = last_bit | (get_bit(Sbox2[s_index], 7 - bit) << 1); + + bit = (get_bit(input[6], 0) << 2) | (get_bit(input[1], 7) << 1) | (get_bit(input[6], 7)); + s_index = (get_bit(input[1], 3) << 3) | (get_bit(input[3], 7) << 2) | (get_bit(input[1], 5) << 1) | (get_bit(input[5], 2)); + last_bit = last_bit | (get_bit(Sbox3[s_index], 7 - bit) << 2); + + bit = (get_bit(input[1], 0) << 2) | (get_bit(input[2], 7) << 1) | (get_bit(input[2], 5)); + s_index = (get_bit(input[6], 3) << 3) | (get_bit(input[6], 4) << 2) | (get_bit(input[6], 6) << 1) | (get_bit(input[3], 5)); + last_index = get_bit(Sbox4[s_index], 7 - bit); + + bit = (get_bit(input[3], 3) << 2) | (get_bit(input[4], 6) << 1) | (get_bit(input[3], 2)); + s_index = (get_bit(input[3], 1) << 3) | (get_bit(input[4], 5) << 2) | (get_bit(input[3], 0) << 1) | (get_bit(input[4], 7)); + last_index = last_index | (get_bit(Sbox5[s_index], 7 - bit) << 1); + + bit = (get_bit(input[5], 4) << 2) | (get_bit(input[4], 4) << 1) | (get_bit(input[1], 2)); + s_index = (get_bit(input[2], 3) << 3) | (get_bit(input[6], 5) << 2) | (get_bit(input[1], 4) << 1) | (get_bit(input[4], 1)); + last_index = last_index | (get_bit(Sbox6[s_index], 7 - bit) << 2); + + bit = (get_bit(input[0], 6) << 2) | (get_bit(input[0], 7) << 1) | (get_bit(input[0], 4)); + s_index = (get_bit(input[0], 5) << 3) | (get_bit(input[0], 3) << 2) | (get_bit(input[0], 1) << 1) | (get_bit(input[0], 2)); + last_index = last_index | (get_bit(Sbox7[s_index], 7 - bit) << 3); + + bit = (get_bit(input[4], 2) << 2) | (get_bit(input[4], 3) << 1) | (get_bit(input[1], 1)); + s_index = (get_bit(input[1], 6) << 3) | (get_bit(input[6], 1) << 2) | (get_bit(input[5], 6) << 1) | (get_bit(input[3], 6)); + last_index = last_index | (get_bit(Sbox8[s_index], 7 - bit) << 4); + + return (get_bit(Sbox9[last_index & 0x1F], 7 - last_bit) & 1) ? 1 : 0; +} + +static void powervu_decrypt(uint8_t *data, uint32_t length, uint8_t *key, uint8_t sbox) +{ + uint32_t i; + int32_t j, k; + uint8_t curByte, tmpBit; + + for (i = 0; i < length; i++) + { + curByte = data[i]; + + for (j = 7; j >= 0; j--) + { + data[i] = set_bit(data[i], j, (get_bit(curByte, j) ^ powervu_sbox(key, sbox)) ^ get_bit(key[0], 7)); + tmpBit = get_bit(data[i], j) ^ (get_bit(key[6], 0)); + + if (tmpBit) + { + key[3] ^= 0x10; + } + + for (k = 6; k > 0; k--) + { + key[k] = (key[k] >> 1) | (key[k - 1] << 7); + } + + key[0] = (key[0] >> 1); + key[0] = set_bit(key[0], 7, tmpBit); + } + } +} + +static void expand_des_key(unsigned char *key) +{ + uint8_t i, j, parity; + uint8_t tmpKey[7]; + + memcpy(tmpKey, key, 7); + + key[0] = (tmpKey[0] & 0xFE); + key[1] = ((tmpKey[0] << 7) | ((tmpKey[1] >> 1) & 0xFE)); + key[2] = ((tmpKey[1] << 6) | ((tmpKey[2] >> 2) & 0xFE)); + key[3] = ((tmpKey[2] << 5) | ((tmpKey[3] >> 3) & 0xFE)); + key[4] = ((tmpKey[3] << 4) | ((tmpKey[4] >> 4) & 0xFE)); + key[5] = ((tmpKey[4] << 3) | ((tmpKey[5] >> 5) & 0xFE)); + key[6] = ((tmpKey[5] << 2) | ((tmpKey[6] >> 6) & 0xFE)); + key[7] = (tmpKey[6] << 1); + + for (i = 0; i < 8; i++) + { + parity = 1; + + for (j = 1; j < 8; j++) + { + if ((key[i] >> j) & 0x1) + { + parity = ~parity & 0x01; + } + } + + key[i] |= parity; + } +} + +static uint8_t get_conv_cw_index(uint8_t ecmTag) +{ + switch (ecmTag) + { + case PVU_CONVCW_VID_ECM: + return PVU_CW_VID; + + case PVU_CONVCW_HSD_ECM: + return PVU_CW_HSD; + + case PVU_CONVCW_A1_ECM: + return PVU_CW_A1; + + case PVU_CONVCW_A2_ECM: + return PVU_CW_A2; + + case PVU_CONVCW_A3_ECM: + return PVU_CW_A3; + + case PVU_CONVCW_A4_ECM: + return PVU_CW_A4; + + case PVU_CONVCW_UTL_ECM: + return PVU_CW_UTL; + + case PVU_CONVCW_VBI_ECM: + return PVU_CW_VBI; + + default: + return PVU_CW_VBI; + } +} + +static uint16_t get_seed_iv(uint8_t seedType, uint8_t *ecm) +{ + switch (seedType) + { + case PVU_CW_VID: + return ((ecm[0x10] & 0x1F) << 3) | 0; + + case PVU_CW_HSD: + return ((ecm[0x12] & 0x1F) << 3) | 2; + + case PVU_CW_A1: + return ((ecm[0x11] & 0x3F) << 3) | 1; + + case PVU_CW_A2: + return ((ecm[0x13] & 0x3F) << 3) | 1; + + case PVU_CW_A3: + return ((ecm[0x19] & 0x3F) << 3) | 1; + + case PVU_CW_A4: + return ((ecm[0x1A] & 0x3F) << 3) | 1; + + case PVU_CW_UTL: + return ((ecm[0x14] & 0x0F) << 3) | 4; + + case PVU_CW_VBI: + return (((ecm[0x15] & 0xF8) >> 3) << 3) | 5; + + default: + return 0; + } +} + +static uint8_t expand_seed(uint8_t seedType, uint8_t *seed) +{ + uint8_t seedLength = 0, i; + + switch (seedType) + { + case PVU_CW_VID: + case PVU_CW_HSD: + seedLength = 4; + break; + + case PVU_CW_A1: + case PVU_CW_A2: + case PVU_CW_A3: + case PVU_CW_A4: + seedLength = 3; + break; + + case PVU_CW_UTL: + case PVU_CW_VBI: + seedLength = 2; + break; + + default: + return seedLength; + } + + for (i = seedLength; i < 7; i++) + { + seed[i] = seed[i % seedLength]; + } + + return seedLength; +} + +static void calculate_seed(uint8_t seedType, uint8_t *ecm, uint8_t *seedBase, + uint8_t *key, uint8_t *seed, uint8_t sbox) +{ + uint16_t tmpSeed; + + tmpSeed = get_seed_iv(seedType, ecm + 23); + + seed[0] = (tmpSeed >> 2) & 0xFF; + seed[1] = ((tmpSeed & 0x3) << 6) | (seedBase[0] >> 2); + seed[2] = ( seedBase[0] << 6) | (seedBase[1] >> 2); + seed[3] = ( seedBase[1] << 6) | (seedBase[2] >> 2); + seed[4] = ( seedBase[2] << 6) | (seedBase[3] >> 2); + seed[5] = ( seedBase[3] << 6); + + powervu_decrypt(seed, 6, key, sbox); + + seed[0] = (seed[1] << 2) | (seed[2] >> 6); + seed[1] = (seed[2] << 2) | (seed[3] >> 6); + seed[2] = (seed[3] << 2) | (seed[4] >> 6); + seed[3] = (seed[4] << 2) | (seed[5] >> 6); +} + +static void calculate_cw(uint8_t seedType, uint8_t *seed, uint8_t csaUsed, uint8_t *convolvedCw, + uint8_t *cw, uint8_t *baseCw, uint8_t *seedEcmCw, uint8_t hashModeCw, + uint8_t needsUnmasking, uint8_t xorMode, int modeCW, uint8_t* ecmBody) +{ + int32_t k; + uint8_t seedLength, val = 0; + + seedLength = expand_seed(seedType, seed); + + if (needsUnmasking && (((modeCW >= 0x00) && (hashModeCw > 0) && (hashModeCw <= 0x27) && + (hashModeCw != 0x0B) && (hashModeCw != 0x0C) && (hashModeCw != 0x0D) && (hashModeCw != 0x0E)) || + (modeCW == 0x03) || (modeCW == 0x04))) + { + switch (seedType) + { + case PVU_CW_VID: + val = 0; + break; + + case PVU_CW_A1: + case PVU_CW_A2: + case PVU_CW_A3: + case PVU_CW_A4: + val = 1; + break; + + case PVU_CW_HSD: + val = 2; + break; + + case PVU_CW_UTL: + val = 4; + break; + + case PVU_CW_VBI: + val = 5; + break; + } + + create_cw(seed, seedLength, baseCw, val, seedEcmCw, cw, csaUsed, hashModeCw, modeCW, ecmBody); + + if (csaUsed) + { + cw[0] = cw[0] ^ convolvedCw[0]; + cw[1] = cw[1] ^ convolvedCw[1]; + cw[2] = cw[2] ^ convolvedCw[2]; + cw[3] = cw[3] ^ convolvedCw[3]; + cw[4] = cw[4] ^ convolvedCw[4]; + cw[5] = cw[5] ^ convolvedCw[5]; + cw[6] = cw[6] ^ convolvedCw[6]; + cw[7] = cw[7] ^ convolvedCw[7]; + + cw[3] = cw[0] + cw[1] + cw[2]; + cw[7] = cw[4] + cw[5] + cw[6]; + } + } + else + { + if (csaUsed) + { + for (k = 0; k < 7; k++) + { + seed[k] ^= baseCw[k]; + } + + cw[0] = seed[0] ^ convolvedCw[0]; + cw[1] = seed[1] ^ convolvedCw[1]; + cw[2] = seed[2] ^ convolvedCw[2]; + cw[3] = seed[3] ^ convolvedCw[3]; + cw[4] = seed[3] ^ convolvedCw[4]; + cw[5] = seed[4] ^ convolvedCw[5]; + cw[6] = seed[5] ^ convolvedCw[6]; + cw[7] = seed[6] ^ convolvedCw[7]; + } + else + { + if (xorMode == 0) + { + for (k = 0; k < 7; k++) + { + cw[k] = seed[k] ^ baseCw[k]; + } + } + + if (xorMode == 1) + { + for (k = 0; k < 3; k++) + { + cw[k] = seed[k] ^ baseCw[k]; + } + + for (k = 3; k < 7; k++) + { + cw[k] = baseCw[k]; + } + } + + expand_des_key(cw); + } + } +} + +#ifdef MODULE_STREAMRELAY +int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens, emu_stream_client_key_data *cdata) +#else +int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens) +#endif +{ + uint32_t i, j, k; + uint32_t ecmCrc32, keyRef0, keyRef1, keyRef2, channel_hash, group_id = 0; + + uint16_t ecmLen = SCT_LEN(ecm); + uint16_t nanoLen, channelId, ecmSrvid; + + uint8_t keyIndex, sbox, decrypt_ok, calculateAll, hashModeCw = 0, needsUnmasking, xorMode; + uint8_t nanoCmd, nanoChecksum, keyType, fixedKey, oddKey, bid, csaUsed, modeCW = 0, offsetBody; + + uint8_t ecmKey[7], tmpEcmKey[7], seedBase[4], baseCw[7], seed[8][8], cw[8][8], convolvedCw[8][8]; + uint8_t ecmPart1[14], ecmPart2[27], unmaskedEcm[ecmLen], seedEcmCw[16]; + + //char tmpBuffer1[512]; + char tmpBuffer2[17]; + +#ifdef MODULE_STREAMRELAY + emu_stream_cw_item *cw_item; + int8_t update_global_key = 0; + int8_t update_global_keys[EMU_STREAM_SERVER_MAX_CONNECTIONS]; + + memset(update_global_keys, 0, sizeof(update_global_keys)); +#else +#define EMU_STREAM_MAX_AUDIO_SUB_TRACKS 4 +#endif + + if (ecmLen < 7) + { + return EMU_NOT_SUPPORTED; + } + + needsUnmasking = (ecm[3] & 0xF0) == 0x50; + + //cs_log_dbg(D_ATR, "ecm1: %s", cs_hexdump(0, ecm, ecmLen, tmpBuffer1, sizeof(tmpBuffer1))); + + if (needsUnmasking) + { + hashModeCw = unmask_ecm(ecm, seedEcmCw, &modeCW); + } + + //cs_log_dbg(D_ATR, "needsUnmasking=%d", needsUnmasking); + //cs_log_dbg(D_ATR, "ecm2: %s", cs_hexdump(0, ecm, ecmLen, tmpBuffer1, sizeof(tmpBuffer1))); + + memcpy(unmaskedEcm, ecm, ecmLen); + + ecmCrc32 = b2i(4, ecm + ecmLen - 4); + + if (ccitt32_crc(ecm, ecmLen - 4) != ecmCrc32) + { + return EMU_CHECKSUM_ERROR; + } + ecmLen -= 4; + + for (i = 0; i < 8; i++) + { + memset(convolvedCw[i], 0, 8); + } + + for (i = 3; i + 3 < ecmLen; ) + { + nanoLen = (((ecm[i] & 0x0F) << 8) | ecm[i + 1]); + i += 2; + + if (nanoLen > 0) + { + nanoLen--; + } + nanoCmd = ecm[i++]; + + if (i + nanoLen > ecmLen) + { + return EMU_NOT_SUPPORTED; + } + + switch (nanoCmd) + { + case 0x27: + if (nanoLen < 15) + { + break; + } + + nanoChecksum = 0; + for (j = 4; j < 15; j++) + { + nanoChecksum += ecm[i + j]; + } + + if (nanoChecksum != 0) + { + break; + } + + keyType = get_conv_cw_index(ecm[i + 4]); + memcpy(convolvedCw[keyType], &ecm[i + 6], 8); + break; + + default: + break; + } + + i += nanoLen; + } + + for (i = 3; i + 3 < ecmLen; ) + { + nanoLen = (((ecm[i] & 0x0F) << 8) | ecm[i + 1]); + i += 2; + + if (nanoLen > 0) + { + nanoLen--; + } + nanoCmd = ecm[i++]; + + if (i + nanoLen > ecmLen) + { + return EMU_NOT_SUPPORTED; + } + + switch (nanoCmd) + { + case 0x20: + { + if (nanoLen < 54) + { + break; + } + + offsetBody = i + 4 + ecm[i + 3]; + i += ecm[i + 3]; // Extra Data Length + + csaUsed = get_bit(ecm[i + 7], 7); + fixedKey = !get_bit(ecm[i + 6], 5); + oddKey = get_bit(ecm[i + 6], 4); + xorMode = get_bit(ecm[i + 6], 0); + bid = (get_bit(ecm[i + 7], 1) << 1) | get_bit(ecm[i + 7], 0); + sbox = get_bit(ecm[i + 6], 3); + + keyIndex = (fixedKey << 3) | (bid << 2) | oddKey; + channelId = b2i(2, ecm + i + 23); + ecmSrvid = (channelId >> 4) | ((channelId & 0xF) << 12); + + cs_log_dbg(D_ATR, "csaUsed: %d, xorMode: %d, ecmSrvid: %04X (%d), hashModeCw: %d, modeCW: %d", + csaUsed, xorMode, ecmSrvid, srvid, hashModeCw, modeCW); + + channel_hash = create_channel_hash(caid, tsid, onid, ens); + group_id = get_channel_group(channel_hash); + + cs_log_dbg(D_ATR, "channel hash: %08X, group id: %04X", channel_hash, group_id); + + decrypt_ok = 0; + + memcpy(ecmPart1, ecm + i + 8, 14); + memcpy(ecmPart2, ecm + i + 27, 27); + + keyRef0 = 0; + keyRef1 = 0; + keyRef2 = 0; + + do + { + if (!group_id || !get_ecm_key(ecmKey, group_id << 16, 0x0000FFFF, keyIndex, keyRef0++)) + { + if (!get_ecm_key(ecmKey, ecmSrvid, 0xFFFF0000, keyIndex, keyRef1++)) + { + if (!get_ecm_key(ecmKey, channelId, 0xFFFF0000, keyIndex, keyRef2++)) + { + cs_log("Key not found or invalid: P ****%04X %02X", ecmSrvid, keyIndex); + + if (group_id) // Print only if there is a matching "GROUP" entry + { + cs_log("Key not found or invalid: P %04XFFFF %02X", group_id, keyIndex); + } + + return EMU_KEY_NOT_FOUND; + } + } + } + + powervu_decrypt(ecm + i + 8, 14, ecmKey, sbox); + + if ((ecm[i + 6] != ecm[i + 6 + 7]) || (ecm[i + 6 + 8] != ecm[i + 6 + 15])) + { + memcpy(ecm + i + 8, ecmPart1, 14); + continue; + } + + memcpy(tmpEcmKey, ecmKey, 7); + + powervu_decrypt(ecm + i + 27, 27, ecmKey, sbox); + + if ((ecm[i + 23] != ecm[i + 23 + 29]) || (ecm[i + 23 + 1] != ecm[i + 23 + 30])) + { + memcpy(ecm + i + 8, ecmPart1, 14); + memcpy(ecm + i + 27, ecmPart2, 27); + continue; + } + + decrypt_ok = 1; + } + while (!decrypt_ok); + + memcpy(seedBase, ecm + i + 6 + 2, 4); + +#ifdef MODULE_STREAMRELAY + if (cdata == NULL) + { + SAFE_MUTEX_LOCK(&emu_fixed_key_srvid_mutex); + for (j = 0; j < EMU_STREAM_SERVER_MAX_CONNECTIONS; j++) + { + if (!stream_server_has_ecm[j] && emu_stream_cur_srvid[j] == srvid) + { + update_global_key = 1; + update_global_keys[j] = 1; + } + } + SAFE_MUTEX_UNLOCK(&emu_fixed_key_srvid_mutex); + } + + calculateAll = cdata != NULL || update_global_key || cw_ex != NULL; +#else + calculateAll = cw_ex != NULL; +#endif + + if (calculateAll) // Calculate all seeds + { + for (j = 0; j < 8; j++) + { + memcpy(ecmKey, tmpEcmKey, 7); + calculate_seed(j, ecm + i, seedBase, ecmKey, seed[j], sbox); + } + } + else // Calculate only video seed + { + memcpy(ecmKey, tmpEcmKey, 7); + calculate_seed(PVU_CW_VID, ecm + i, seedBase, ecmKey, seed[PVU_CW_VID], sbox); + } + + memcpy(baseCw, ecm + i + 6 + 8, 7); + + if (calculateAll) // Calculate all CWs + { + for (j = 0; j < 8; j++) + { + calculate_cw(j, seed[j], csaUsed, convolvedCw[j], cw[j], baseCw, seedEcmCw, + hashModeCw, needsUnmasking, xorMode, modeCW, unmaskedEcm + offsetBody); + + if (csaUsed) + { + for (k = 0; k < 8; k += 4) + { + cw[j][k + 3] = ((cw[j][k] + cw[j][k + 1] + cw[j][k + 2]) & 0xFF); + } + } + + cs_log_dbg(D_ATR, "calculated cw %d: %s", j, + cs_hexdump(0, cw[j], 8, tmpBuffer2, sizeof(tmpBuffer2))); + } + + //cs_log_dbg(D_ATR, "csaUsed=%d, cw: %s cdata=%x, cw_ex=%x", + // csaUsed, cs_hexdump(3, cw[0], 8, tmpBuffer1, sizeof(tmpBuffer1)), + // (unsigned int)cdata, (unsigned int)cw_ex); + +#ifdef MODULE_STREAMRELAY + if (update_global_key) + { + for (j = 0; j < EMU_STREAM_SERVER_MAX_CONNECTIONS; j++) + { + if (update_global_keys[j]) + { + cw_item = (emu_stream_cw_item *)malloc(sizeof(emu_stream_cw_item)); + if (cw_item != NULL) + { + cw_item->csa_used = csaUsed; + cw_item->is_even = ecm[0] == 0x80 ? 1 : 0; + cs_ftime(&cw_item->write_time); + add_ms_to_timeb(&cw_item->write_time, cfg.emu_stream_ecm_delay); + memcpy(cw_item->cw, cw, sizeof(cw)); + ll_append(ll_emu_stream_delayed_keys[j], cw_item); + } + } + } + } + + if (cdata != NULL) + { + for (j = 0; j < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; j++) + { + if (csaUsed) + { + if (ecm[0] == 0x80) + { + if (has_dvbcsa_ecm) + { + dvbcsa_bs_key_set(cw[j], key_data[cdata->connid].key[j][EVEN]); + } + } + else + { + if (has_dvbcsa_ecm) + { + dvbcsa_bs_key_set(cw[j], key_data[cdata->connid].key[j][ODD]); + } + } + + cdata->csa_used = 1; + } + else + { + if (ecm[0] == 0x80) + { + des_set_key(cw[j], cdata->pvu_des_ks[j][0]); + } + else + { + des_set_key(cw[j], cdata->pvu_des_ks[j][1]); + } + + cdata->csa_used = 0; + } + } + } +#endif + + if (cw_ex != NULL) + { + cw_ex->mode = CW_MODE_MULTIPLE_CW; + + if (csaUsed) + { + cw_ex->algo = CW_ALGO_CSA; + cw_ex->algo_mode = CW_ALGO_MODE_CBC; + } + else + { + cw_ex->algo = CW_ALGO_DES; + cw_ex->algo_mode = CW_ALGO_MODE_ECB; + } + + for (j = 0; j < EMU_STREAM_MAX_AUDIO_SUB_TRACKS; j++) + { + memset(cw_ex->audio[j], 0, 16); + + if (ecm[0] == 0x80) + { + memcpy(cw_ex->audio[j], cw[PVU_CW_A1 + j], 8); + } + else + { + memcpy(&cw_ex->audio[j][8], cw[PVU_CW_A1 + j], 8); + } + } + + memset(cw_ex->data, 0, 16); + + if (ecm[0] == 0x80) + { + memcpy(cw_ex->data, cw[PVU_CW_HSD], 8); + } + else + { + memcpy(&cw_ex->data[8], cw[PVU_CW_HSD], 8); + } + } + } + else // Calculate only video CW + { + calculate_cw(PVU_CW_VID, seed[PVU_CW_VID], csaUsed, convolvedCw[PVU_CW_VID], + cw[PVU_CW_VID], baseCw, seedEcmCw, hashModeCw, needsUnmasking, + xorMode, modeCW, unmaskedEcm + offsetBody); + + if (csaUsed) + { + for (k = 0; k < 8; k += 4) + { + cw[PVU_CW_VID][k + 3] = ((cw[PVU_CW_VID][k] + cw[PVU_CW_VID][k + 1] + cw[PVU_CW_VID][k + 2]) & 0xFF); + } + } + + cs_log_dbg(D_ATR, "calculated video only cw: %s", + cs_hexdump(0, cw[PVU_CW_VID], 8, tmpBuffer2, sizeof(tmpBuffer2))); + } + + memset(dw, 0, 16); + + if (ecm[0] == 0x80) + { + memcpy(dw, cw[PVU_CW_VID], 8); + } + else + { + memcpy(&dw[8], cw[PVU_CW_VID], 8); + } + + return EMU_OK; + } + + default: + break; + } + + i += nanoLen; + } + + return EMU_NOT_SUPPORTED; +} + +// PowerVu EMM EMU +static void create_data_unmask_emm_mode_03(uint8_t *emmBody, uint8_t *data) +{ + int i; + uint8_t padding[] = + { + 0xB3, 0x60, 0x35, 0xC8, 0x5C, 0x26, 0xC1, 0xD0, + 0x88, 0x86, 0x57, 0xB6, 0x45, 0xA7, 0xDF, 0x7E, + 0xF0, 0xA8, 0x49, 0xFB, 0x79, 0x6C, 0xAF, 0xB0 + }; + + memcpy(data + 40, padding, 24); + + for (i = 0; i < 5; i++) + { + data[0 + i * 8] = emmBody[0x18 + i * 0x1B]; + data[1 + i * 8] = emmBody[0x16 + i * 0x1B]; + data[2 + i * 8] = emmBody[0x07 + i * 0x1B]; + data[3 + i * 8] = emmBody[0x0B + i * 0x1B]; + data[4 + i * 8] = emmBody[0x06 + i * 0x1B]; + data[5 + i * 8] = emmBody[0x19 + i * 0x1B]; + data[6 + i * 8] = emmBody[0x15 + i * 0x1B]; + data[7 + i * 8] = emmBody[0x03 + i * 0x1B]; + } +} + +static void create_data_unmask_emm_mode_04(uint8_t *emmBody, uint8_t *data) +{ + int i; + uint8_t padding[] = + { + 0x56, 0xC7, 0x05, 0x66, 0xC7, 0x4E, 0xC1, 0xA0, + 0x9E, 0xD1, 0xFE, 0x92, 0xE8, 0xCD, 0x5F, 0xAF, + 0xCF, 0xE5, 0xE9, 0x9E, 0x7A, 0x38, 0xAC, 0x68 + }; + + memcpy(data + 0x28, padding, 0x18); + + for (i = 0; i < 5; i++) + { + data[0 + i * 8] = emmBody[0x06 + i * 0x1B]; + data[1 + i * 8] = emmBody[0x19 + i * 0x1B]; + data[2 + i * 8] = emmBody[0x16 + i * 0x1B]; + data[3 + i * 8] = emmBody[0x0A + i * 0x1B]; + data[4 + i * 8] = emmBody[0x13 + i * 0x1B]; + data[5 + i * 8] = emmBody[0x05 + i * 0x1B]; + data[6 + i * 8] = emmBody[0x14 + i * 0x1B]; + data[7 + i * 8] = emmBody[0x18 + i * 0x1B]; + } +} + +static uint8_t get_mode_unmask_emm(uint8_t *extraData) +{ + uint16_t data = ((uint16_t)extraData[0] << 8) + extraData[1]; + + if (data == 0) + { + return 0x00; + } + + switch (data & 0x0881) + { + case 0x0080: + case 0x0881: + return 0x01; + + case 0x0001: + case 0x0880: + return 0x02; + + case 0x0800: + case 0x0081: + return 0x03; + + case 0x0000: + case 0x0801: + switch (data & 0x9020) + { + case 0x8000: + case 0x9000: + return 0x04; + + case 0x0020: + case 0x9020: + return 0x05; + + case 0x0000: + case 0x1000: + return 0x06; + + case 0x1020: + case 0x8020: + switch (data & 0x2014) + { + case 0x2004: + case 0x2010: + return 0x07; + + case 0x0000: + case 0x0004: + return 0x08; + + case 0x0014: + case 0x2014: + return 0x09; + + case 0x0010: + case 0x2000: + return 0x00; + } + break; + } + break; + } + return 0x00; +} + +static void unmask_emm(uint8_t *emm) +{ + uint32_t crc, i, l; + uint8_t hashModeEmm, modeUnmask, data[64], mask[16]; + + uint8_t sourcePos[] = + { + 0x03, 0x0C, 0x0D, 0x11, 0x15, 0x18, 0x1D, 0x1F, 0x25, 0x2A, + 0x32, 0x35, 0x3A, 0x3B, 0x3E, 0x42, 0x47, 0x48, 0x53, 0x58, + 0x5C, 0x61, 0x66, 0x69, 0x71, 0x72, 0x78, 0x7B, 0x81, 0x84 + }; + + uint8_t destPos[] = + { + 0x02, 0x08, 0x0B, 0x0E, 0x13, 0x16, 0x1E, 0x23, 0x28, 0x2B, + 0x2F, 0x33, 0x38, 0x3C, 0x40, 0x44, 0x4A, 0x4D, 0x54, 0x57, + 0x5A, 0x63, 0x68, 0x6A, 0x70, 0x75, 0x76, 0x7D, 0x82, 0x85 + }; + + // Create Mask for ECM decryption + create_data_ecm_emm(emm, sourcePos, 19, 30, data); + + hashModeEmm = emm[8] ^ crc8_calc(data, 30); + modeUnmask = get_mode_unmask_emm(emm + 16); + + if ((modeUnmask == 0x00) || (modeUnmask > 4)) + { + create_hash(data, 30, mask, hashModeEmm); + + // Unmask Body + for (i = 0; i < 30; i++) + { + emm[19 + destPos[i]] ^= mask[i & 0x0F]; + } + } + else if (modeUnmask == 0x03) + { + for (i = 0; i < 5; i++) + { + emm[0x13 + 0x03 + i * 0x1B] -= emm[0x13 + 0x0D + i * 0x1B]; + emm[0x13 + 0x06 + i * 0x1B] -= emm[0x13 + 0x1A + i * 0x1B]; + emm[0x13 + 0x07 + i * 0x1B] -= emm[0x13 + 0x10 + i * 0x1B]; + emm[0x13 + 0x0B + i * 0x1B] -= emm[0x13 + 0x17 + i * 0x1B]; + emm[0x13 + 0x15 + i * 0x1B] -= emm[0x13 + 0x05 + i * 0x1B]; + emm[0x13 + 0x16 + i * 0x1B] -= emm[0x13 + 0x0F + i * 0x1B]; + emm[0x13 + 0x18 + i * 0x1B] -= emm[0x13 + 0x14 + i * 0x1B]; + emm[0x13 + 0x19 + i * 0x1B] -= emm[0x13 + 0x04 + i * 0x1B]; + } + + create_data_unmask_emm_mode_03(emm + 0x13, data); + create_hash_mode_03(data, mask); + + for (i = 0; i < 5; i++) + { + emm[0x13 + 0x14 + i * 0x1B] ^= mask[0x00]; + emm[0x13 + 0x0F + i * 0x1B] ^= mask[0x01]; + emm[0x13 + 0x10 + i * 0x1B] ^= mask[0x02]; + emm[0x13 + 0x17 + i * 0x1B] ^= mask[0x03]; + emm[0x13 + 0x1A + i * 0x1B] ^= mask[0x04]; + emm[0x13 + 0x04 + i * 0x1B] ^= mask[0x05]; + emm[0x13 + 0x05 + i * 0x1B] ^= mask[0x06]; + emm[0x13 + 0x0D + i * 0x1B] ^= mask[0x07]; + emm[0x13 + 0x09 + i * 0x1B] ^= mask[0x08]; + emm[0x13 + 0x0A + i * 0x1B] ^= mask[0x09]; + emm[0x13 + 0x0E + i * 0x1B] ^= mask[0x0A]; + emm[0x13 + 0x11 + i * 0x1B] ^= mask[0x0B]; + emm[0x13 + 0x12 + i * 0x1B] ^= mask[0x0C]; + emm[0x13 + 0x13 + i * 0x1B] ^= mask[0x0D]; + emm[0x13 + 0x08 + i * 0x1B] ^= mask[0x0E]; + emm[0x13 + 0x0C + i * 0x1B] ^= mask[0x0F]; + } + } + else if (modeUnmask == 0x04) + { + for (i = 0; i < 5; i++) + { + emm[0x13 + 0x05 + i * 0x1B] -= emm[0x13 + 0x04 + i * 0x1B]; + emm[0x13 + 0x06 + i * 0x1B] -= emm[0x13 + 0x0B + i * 0x1B]; + emm[0x13 + 0x0A + i * 0x1B] -= emm[0x13 + 0x17 + i * 0x1B]; + emm[0x13 + 0x13 + i * 0x1B] -= emm[0x13 + 0x1A + i * 0x1B]; + emm[0x13 + 0x14 + i * 0x1B] -= emm[0x13 + 0x0E + i * 0x1B]; + emm[0x13 + 0x16 + i * 0x1B] -= emm[0x13 + 0x15 + i * 0x1B]; + emm[0x13 + 0x18 + i * 0x1B] -= emm[0x13 + 0x08 + i * 0x1B]; + emm[0x13 + 0x19 + i * 0x1B] -= emm[0x13 + 0x12 + i * 0x1B]; + } + + create_data_unmask_emm_mode_04(emm + 0x13, data); + create_hash_mode_04(data, mask); + + for (i = 0; i < 5; i++) + { + emm[0x13 + 0x0B + i * 0x1B] ^= mask[0x00]; + emm[0x13 + 0x12 + i * 0x1B] ^= mask[0x01]; + emm[0x13 + 0x15 + i * 0x1B] ^= mask[0x02]; + emm[0x13 + 0x17 + i * 0x1B] ^= mask[0x03]; + emm[0x13 + 0x1A + i * 0x1B] ^= mask[0x04]; + emm[0x13 + 0x04 + i * 0x1B] ^= mask[0x05]; + emm[0x13 + 0x0E + i * 0x1B] ^= mask[0x06]; + emm[0x13 + 0x08 + i * 0x1B] ^= mask[0x07]; + emm[0x13 + 0x09 + i * 0x1B] ^= mask[0x08]; + emm[0x13 + 0x0C + i * 0x1B] ^= mask[0x09]; + emm[0x13 + 0x03 + i * 0x1B] ^= mask[0x0A]; + emm[0x13 + 0x0F + i * 0x1B] ^= mask[0x0B]; + emm[0x13 + 0x10 + i * 0x1B] ^= mask[0x0C]; + emm[0x13 + 0x07 + i * 0x1B] ^= mask[0x0D]; + emm[0x13 + 0x0D + i * 0x1B] ^= mask[0x0E]; + emm[0x13 + 0x11 + i * 0x1B] ^= mask[0x0F]; + } + } + else + { + cs_log("A new unknown emm mode [%d] is in use.", modeUnmask); + } + + // Fix Header + emm[3] &= 0x0F; + emm[3] |= 0x10; + emm[8] = 0x00; + + // Fix CRC (optional) + l = (((emm[1] << 8) + emm[2]) & 0xFFF) + 3 - 4; + crc = ccitt32_crc(emm, l); + + emm[l + 0] = crc >> 24; + emm[l + 1] = crc >> 16; + emm[l + 2] = crc >> 8; + emm[l + 3] = crc >> 0; +} + +static int8_t update_ecm_keys_by_group(uint32_t groupId, uint8_t keyIndex, uint8_t *Key, uint32_t uniqueAddress) +{ + int8_t ret = 0; + uint8_t oldKey[7]; + uint32_t foundProvider = 0, keyRef = 0; + char indexStr[3], uaInfo[13]; + + snprintf(indexStr, 3, "%02X", keyIndex); + snprintf(uaInfo, 13, "UA: %08X", uniqueAddress); + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + while (emu_find_key('P', groupId << 16 & 0xFFFF0000, 0x0000FFFF, indexStr, oldKey, 7, 0, keyRef, 0, &foundProvider)) + { + keyRef++; + + if (memcmp(oldKey, Key, 7) == 0) // New ECM key already in the db + { + continue; + } + + if (emu_set_key('P', foundProvider, indexStr, Key, 7, 1, uaInfo, NULL)) + { + ret = 1; + } + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + return ret; +} + +int8_t powervu_emm(uint8_t *emm, uint32_t *keysAdded) +{ + uint8_t emmInfo, emmType, decryptOk = 0; + uint8_t emmKey[7], tmpEmmKey[7], tmp[26]; + uint16_t emmLen = SCT_LEN(emm); + uint32_t i, uniqueAddress, groupId, keyRef = 0; + //uint32_t emmCrc32; + char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[16]; + + if (emmLen < 50) + { + return EMU_NOT_SUPPORTED; + } + + // Check if unmasking is needed + if ((emm[3] & 0xF0) == 0x50) + { + unmask_emm(emm); + } + + // looks like checksum does not work for all EMMs + //emmCrc32 = b2i(4, emm+emmLen-4); + // + //if(ccitt32_crc(emm, emmLen-4) != emmCrc32) + //{ + // return EMU_CHECKSUM_ERROR; + //} + emmLen -= 4; + + uniqueAddress = b2i(4, emm + 12); + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%.8X", uniqueAddress); + + do + { + if (!get_emm_key(emmKey, keyName, keyRef++, &groupId)) + { + //cs_log_dbg(D_ATR, "EMM key for UA %s is missing", keyName); + return EMU_KEY_NOT_FOUND; + } + + for (i = 19; i + 27 <= emmLen; i += 27) + { + emmInfo = emm[i]; + + if (!get_bit(emmInfo, 7)) + { + continue; + } + + //keyNb = emm[i] & 0x0F; + + memcpy(tmp, emm + i + 1, 26); + memcpy(tmpEmmKey, emmKey, 7); + powervu_decrypt(emm + i + 1, 26, tmpEmmKey, 0); + + if ((emm[13] != emm[i + 24]) || (emm[14] != emm[i + 25]) || (emm[15] != emm[i + 26])) + { + memcpy(emm + i + 1, tmp, 26); + memcpy(tmpEmmKey, emmKey, 7); + powervu_decrypt(emm + i + 1, 26, tmpEmmKey, 1); + + if ((emm[13] != emm[i + 24]) || (emm[14] != emm[i + 25]) || (emm[15] != emm[i + 26])) + { + memcpy(emm + i + 1, tmp, 26); + memcpy(tmpEmmKey, emmKey, 7); + continue; + } + } + + decryptOk = 1; + + emmType = emm[i + 2] & 0x7F; + + if (emmType > 1) + { + continue; + } + + if (emm[i + 3] == 0 && emm[i + 4] == 0) + { + cs_hexdump(0, &emm[i + 3], 7, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: P %04X**** %02X %s -> REJECTED (looks invalid) UA: %08X", + groupId, emmType, keyValue, uniqueAddress); + continue; + } + + update_ecm_keys_by_group(groupId, emmType, &emm[i + 3], uniqueAddress); + + (*keysAdded)++; + cs_hexdump(0, &emm[i + 3], 7, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: P %04X**** %02X %s ; UA: %08X", groupId, emmType, keyValue, uniqueAddress); + } + + } while (!decryptOk); + + return EMU_OK; +} + +int8_t powervu_get_hexserials(uint8_t hexserials[][4], uint32_t maxCount, uint16_t srvid) +{ + //srvid == 0xFFFF -> get all + + int8_t alreadyAdded; + uint8_t tmp[4]; + uint32_t i, j, k, groupid, length, count = 0; + KeyDataContainer *KeyDB; + + KeyDB = emu_get_key_container('P'); + if (KeyDB == NULL) + { + return 0; + } + + for (i = 0; i < KeyDB->keyCount && count < maxCount; i++) + { + if (KeyDB->EmuKeys[i].provider <= 0x0000FFFF) // skip EMM keys + { + continue; + } + + if (srvid != 0xFFFF && (KeyDB->EmuKeys[i].provider & 0x0000FFFF) != srvid) + { + continue; + } + + // This "groupid" has an ECM key with our "srvid" + // (in ECM keys "groupid" is top 16 bits) + groupid = KeyDB->EmuKeys[i].provider >> 16; + + for (j = 0; j < KeyDB->keyCount && count < maxCount; j++) + { + // Skip EMM keys belonging to other groups + // (in EMM keys "groupid" is bottom 16 bits) + if (KeyDB->EmuKeys[j].provider != groupid) + { + continue; + } + + length = cs_strlen(KeyDB->EmuKeys[j].keyName); + + if (length < 3) + { + continue; + } + + if (length > 8) + { + length = 8; + } + + memset(tmp, 0, 4); + char_to_bin(tmp + (4 - (length / 2)), KeyDB->EmuKeys[j].keyName, length); + + for (k = 0, alreadyAdded = 0; k < count; k++) + { + if (!memcmp(hexserials[k], tmp, 4)) + { + alreadyAdded = 1; + break; + } + } + + if (!alreadyAdded) + { + memcpy(hexserials[count], tmp, 4); + count++; + } + } + } + + return count; +} + +int8_t powervu_get_hexserials_new(uint8_t hexserials[][4], uint32_t maxCount, uint16_t caid, + uint16_t tsid, uint16_t onid, uint32_t ens) +{ + int8_t alreadyAdded; + uint8_t tmp[4]; + uint32_t i, j, channel_hash, group_id, length, count = 0; + KeyDataContainer *KeyDB; + + KeyDB = emu_get_key_container('P'); + if (KeyDB == NULL) + { + return 0; + } + + channel_hash = create_channel_hash(caid, tsid, onid, ens); + group_id = get_channel_group(channel_hash); + + if (group_id == 0) // No group found for this hash + { + return 0; + } + + for (i = 0; i < KeyDB->keyCount && count < maxCount; i++) + { + // Skip EMM keys belonging to other groups + // (in EMM keys "groupid" is bottom 16 bits) + if (KeyDB->EmuKeys[i].provider != group_id) + { + continue; + } + + length = cs_strlen(KeyDB->EmuKeys[i].keyName); + + if (length < 3) + { + continue; + } + + if (length > 8) + { + length = 8; + } + + memset(tmp, 0, 4); + char_to_bin(tmp + (4 - (length / 2)), KeyDB->EmuKeys[i].keyName, length); + + for (j = 0, alreadyAdded = 0; j < count; j++) + { + if (!memcmp(hexserials[j], tmp, 4)) + { + alreadyAdded = 1; + break; + } + } + + if (!alreadyAdded) + { + memcpy(hexserials[count], tmp, 4); + count++; + } + } + + return count; +} + +#endif // WITH_EMU diff --git a/module-emulator-powervu.h b/module-emulator-powervu.h new file mode 100644 index 0000000..b50d208 --- /dev/null +++ b/module-emulator-powervu.h @@ -0,0 +1,63 @@ +#ifndef MODULE_EMULATOR_POWERVU_H +#define MODULE_EMULATOR_POWERVU_H + +#ifdef WITH_EMU + +#define PVU_CW_VID 0 // VIDeo +#define PVU_CW_HSD 1 // High Speed Data +#define PVU_CW_A1 2 // Audio 1 +#define PVU_CW_A2 3 // Audio 2 +#define PVU_CW_A3 4 // Audio 3 +#define PVU_CW_A4 5 // Audio 4 +#define PVU_CW_UTL 6 // UTiLity +#define PVU_CW_VBI 7 // Vertical Blanking Interval + +#define PVU_CONVCW_VID_ECM 0x80 // VIDeo +#define PVU_CONVCW_HSD_ECM 0x40 // High Speed Data +#define PVU_CONVCW_A1_ECM 0x20 // Audio 1 +#define PVU_CONVCW_A2_ECM 0x10 // Audio 2 +#define PVU_CONVCW_A3_ECM 0x08 // Audio 3 +#define PVU_CONVCW_A4_ECM 0x04 // Audio 4 +#define PVU_CONVCW_UTL_ECM 0x02 // UTiLity +#define PVU_CONVCW_VBI_ECM 0x01 // Vertical Blanking Interval + +#ifdef MODULE_STREAMRELAY +int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens, emu_stream_client_key_data *cdata); +#else +int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens); +#endif +int8_t powervu_emm(uint8_t *emm, uint32_t *keysAdded); + +/* + * This function searches for EMM keys and adds their Unique Addresses (UA) as EMM filters. + * The EMM keys are picked from all group id's that have ECM keys for the srvid specified + * as input. If there is a large ammount of EMM keys matching these criteria, only the first + * "maxCount" UA's are added as EMM filters. The rest are not used at all. + * + * In the rare case where two or more EMM keys with the same UA belong to different groups, + * and these groups also have ECM keys for the srvid in request, there is a chance the ECM + * keys in the "wrong" group to be updated. This is because the EMM algorithm has no way of + * knowing in which group the service id belongs to. A workaround for this designing flaw + * is to make sure there are no EMM keys with the same UA between different groups. + * + * Hexserials must be of type "uint8_t hexserials[maxCount][4]". If srvid is equal to 0xFFFF + * all serials are added (no service id filtering is done). Returns the count of hexserials + * added as filters. +*/ +int8_t powervu_get_hexserials(uint8_t hexserials[][4], uint32_t maxCount, uint16_t srvid); + +/* + * Like the previous function, it adds UAs as EMM filters. It is used in conjunction with the + * new method of entering ECM keys, where one key can serve every channel in the group. Since + * there is no srvid to search for, we need to know the group id prior to searching for EMM + * keys. To do so, this function calulates a hash using the tsid, onid and enigma namespace of + * the transponder, which is only available in enigma2. + * + * Hexserials must be of type "uint8_t hexserials[maxCount][4]" like before. It returns the + * count of hexserials added as filters. +*/ +int8_t powervu_get_hexserials_new(uint8_t hexserials[][4], uint32_t maxCount, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_POWERVU_H diff --git a/module-emulator-viaccess.c b/module-emulator-viaccess.c new file mode 100644 index 0000000..b5fea65 --- /dev/null +++ b/module-emulator-viaccess.c @@ -0,0 +1,1183 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "cscrypt/des.h" +#include "module-emulator-osemu.h" +#include "module-newcamd-des.h" +#include "oscam-aes.h" +#include "oscam-string.h" + +// from reader-viaccess.c: +void hdSurEncPhase1_D2_0F_11(uint8_t *CWs); +void hdSurEncPhase2_D2_0F_11(uint8_t *CWs); +void hdSurEncPhase1_D2_13_15(uint8_t *cws); +void hdSurEncPhase2_D2_13_15(uint8_t *cws); + +// Viaccess EMU + +static int8_t get_key(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex, + uint32_t keyLength, uint8_t isCriticalKey) +{ + char keyStr[EMU_MAX_CHAR_KEYNAME]; + snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex); + + if (emu_find_key('V', ident, 0, keyStr, buf, keyLength, isCriticalKey, 0, 0, NULL)) + { + return 1; + } + + if (ident == 0xD00040 && emu_find_key('V', 0x030B00, 0, keyStr, buf, keyLength, isCriticalKey, 0, 0, NULL)) + { + return 1; + } + + return 0; +} + +static void via1_mod(const uint8_t *key2, uint8_t *data) +{ + int32_t kb, db; + + for (db = 7; db >= 0; db--) + { + for (kb = 7; kb > 3; kb--) + { + int32_t a0 = kb ^ db; + int32_t pos = 7; + + if (a0 & 4) + { + a0 ^= 7; + pos ^= 7; + } + + a0 = (a0 ^ (kb & 3)) + (kb & 3); + + if (!(a0 & 4)) + { + data[db] ^= (key2[kb] ^ ((data[kb ^ pos] * key2[kb ^ 4]) & 0xFF)); + } + } + } + + for (db = 0; db < 8; db++) + { + for (kb = 0; kb < 4; kb++) + { + int32_t a0 = kb ^ db; + int32_t pos = 7; + + if (a0 & 4) + { + a0 ^= 7; + pos ^= 7; + } + + a0 = (a0 ^ (kb & 3)) + (kb & 3); + + if (!(a0 & 4)) + { + data[db] ^= (key2[kb] ^ ((data[kb ^ pos] * key2[kb ^ 4]) & 0xFF)); + } + } + } +} + +static void via1_decode(uint8_t *data, uint8_t *key) +{ + via1_mod(key + 8, data); + nc_des(key, DES_ECM_CRYPT, data); + via1_mod(key + 8, data); +} + +static void via1_hash(uint8_t *data, uint8_t *key) +{ + via1_mod(key + 8, data); + nc_des(key, DES_ECM_HASH, data); + via1_mod(key + 8, data); +} + +static inline void via1_do_hash(uint8_t *hashbuffer, uint8_t *pH, uint8_t data, uint8_t *hashkey) +{ + hashbuffer[*pH] ^= data; + (*pH)++; + + if (*pH == 8) + { + via1_hash(hashbuffer, hashkey); + *pH = 0; + } +} + +static int8_t via1_decrypt(uint8_t *ecm, uint8_t *dw, uint32_t ident, uint8_t desKeyIndex) +{ + int32_t msg_pos, encStart = 0, hash_start, i; + + uint8_t tmp, k, pH, foundData = 0; + uint8_t work_key[16], signature[8], hashbuffer[8], prepared_key[16], hashkey[16]; + uint8_t *data, *des_data1, *des_data2; + + uint16_t ecmLen = SCT_LEN(ecm); + + if (ident == 0) + { + return EMU_CORRUPT_DATA; + } + + memset(work_key, 0, 16); + + if (!get_key(work_key, ident, '0', desKeyIndex, 8, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + if (ecmLen < 11) + { + return EMU_NOT_SUPPORTED; + } + + data = ecm + 9; + des_data1 = dw; + des_data2 = dw + 8; + + msg_pos = 0; + pH = 0; + memset(hashbuffer, 0, sizeof(hashbuffer)); + memcpy(hashkey, work_key, sizeof(hashkey)); + memset(signature, 0, 8); + + while (9 + msg_pos + 2 < ecmLen) + { + switch (data[msg_pos]) + { + case 0xEA: + if (9 + msg_pos + 2 + 15 < ecmLen) + { + encStart = msg_pos + 2; + memcpy(des_data1, &data[msg_pos + 2], 8); + memcpy(des_data2, &data[msg_pos + 2 + 8], 8); + foundData |= 1; + } + break; + + case 0xF0: + if (9 + msg_pos + 2 + 7 < ecmLen) + { + memcpy(signature, &data[msg_pos + 2], 8); + foundData |= 2; + } + break; + } + msg_pos += data[msg_pos + 1] + 2; + } + + if (foundData != 3) + { + return EMU_NOT_SUPPORTED; + } + + pH = i = 0; + + if (data[0] == 0x9F && 10 + data[1] <= ecmLen) + { + via1_do_hash(hashbuffer, &pH, data[i++], hashkey); + via1_do_hash(hashbuffer, &pH, data[i++], hashkey); + + for (hash_start = 0; hash_start < data[1]; hash_start++) + { + via1_do_hash(hashbuffer, &pH, data[i++], hashkey); + } + + while (pH != 0) + { + via1_do_hash(hashbuffer, &pH, 0, hashkey); + } + } + + if (work_key[7] == 0) + { + for (; i < encStart + 16; i++) + { + via1_do_hash(hashbuffer, &pH, data[i], hashkey); + } + memcpy(prepared_key, work_key, 8); + } + else + { + prepared_key[0] = work_key[2]; + prepared_key[1] = work_key[3]; + prepared_key[2] = work_key[4]; + prepared_key[3] = work_key[5]; + prepared_key[4] = work_key[6]; + prepared_key[5] = work_key[0]; + prepared_key[6] = work_key[1]; + prepared_key[7] = work_key[7]; + + memcpy(prepared_key + 8, work_key + 8, 8); + + if (work_key[7] & 1) + { + for (; i < encStart; i++) + { + via1_do_hash(hashbuffer, &pH, data[i], hashkey); + } + + k = ((work_key[7] & 0xF0) == 0) ? 0x5A : 0xA5; + + for (i = 0; i < 8; i++) + { + tmp = des_data1[i]; + des_data1[i] = (k & hashbuffer[pH] ) ^ tmp; + via1_do_hash(hashbuffer, &pH, tmp, hashkey); + } + + for (i = 0; i < 8; i++) + { + tmp = des_data2[i]; + des_data2[i] = (k & hashbuffer[pH] ) ^ tmp; + via1_do_hash(hashbuffer, &pH, tmp, hashkey); + } + } + else + { + for ( ; i < encStart + 16; i++) + { + via1_do_hash(hashbuffer, &pH, data[i], hashkey); + } + } + } + + via1_decode(des_data1, prepared_key); + via1_decode(des_data2, prepared_key); + via1_hash(hashbuffer, hashkey); + + if (memcmp(signature, hashbuffer, 8)) + { + return EMU_CHECKSUM_ERROR; + } + + return EMU_OK; +} + +static int8_t via26_process_dw(uint8_t *indata, uint32_t ident, uint8_t desKeyIndex) +{ + uint8_t pv1,pv2, i; + uint8_t Tmp[8], T1Key[300], P1Key[8], KeyDes1[16], KeyDes2[16], XorKey[8]; + uint32_t ks1[32], ks2[32]; + + if (!get_key(T1Key, ident, 'T', 1, 300, 1)) + { + return 2; + } + + if (!get_key(P1Key, ident, 'P', 1, 8, 1)) + { + return 2; + } + + if (!get_key(KeyDes1, ident, 'D', 1, 16, 1)) + { + return 2; + } + + if (!get_key(KeyDes2, ident, '0', desKeyIndex, 16, 1)) + { + return 2; + } + + if (!get_key(XorKey, ident, 'X', 1, 8, 1)) + { + return 2; + } + + for (i = 0; i < 8; i++) + { + pv1 = indata[i]; + Tmp[i] = T1Key[pv1]; + } + + for (i = 0; i < 8; i++) + { + pv1 = P1Key[i]; + pv2 = Tmp[pv1]; + indata[i] = pv2; + } + + des_set_key(KeyDes1, ks1); + des(indata, ks1, 1); + + for (i = 0; i < 8; i++) + { + indata[i] ^= XorKey[i]; + } + + des_set_key(KeyDes2, ks1); + des_set_key(KeyDes2 + 8, ks2); + des(indata, ks1, 0); + des(indata, ks2, 1); + des(indata, ks1, 0); + + for (i = 0; i < 8; i++) + { + indata[i] ^= XorKey[i]; + } + + des_set_key(KeyDes1, ks1); + des(indata, ks1, 0); + + for (i = 0; i < 8; i++) + { + pv1 = indata[i]; + pv2 = P1Key[i]; + Tmp[pv2] = pv1; + } + + for (i = 0; i < 8; i++) + { + pv1 = Tmp[i]; + pv2 = T1Key[pv1]; + indata[i] = pv2; + } + + return 0; +} + +static int8_t via26_decrypt(uint8_t *source, uint8_t *dw, uint32_t ident, uint8_t desKeyIndex) +{ + uint8_t tmpData[8], C1[8]; + uint8_t *pXorVector; + int32_t i, j; + + if (ident == 0) + { + return EMU_CORRUPT_DATA; + } + + if (!get_key(C1, ident, 'C', 1, 8, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + for (i = 0; i < 2; i++) + { + memcpy(tmpData, source + i * 8, 8); + via26_process_dw(tmpData, ident, desKeyIndex); + + if (i != 0) + { + pXorVector = source; + } + else + { + pXorVector = &C1[0]; + } + + for (j = 0; j < 8; j++) + { + dw[i * 8 + j] = tmpData[j] ^ pXorVector[j]; + } + + // Fix CW checksum bytes + for (j = 3; j < 8; j += 4) + { + dw[i * 8 + j] = (dw[i * 8 + j - 3] + dw[i * 8 + j - 2] + dw[i * 8 + j - 1]) & 0xFF; + } + } + + return EMU_OK; +} + +static void via3_core(uint8_t *data, uint8_t Off, uint32_t ident, uint8_t *XorKey, uint8_t *T1Key) +{ + uint8_t i; + uint32_t lR2, lR3, lR4, lR6, lR7; + + switch (ident) + { + case 0x032820: + { + for (i = 0; i < 4; i++) + { + data[i] ^= XorKey[(Off + i) & 0x07]; + } + + lR2 = (data[0] ^ 0xBD) + data[0]; + lR3 = (data[3] ^ 0xEB) + data[3]; + lR2 = (lR2 - lR3) ^ data[2]; + lR3 = ((0x39 * data[1]) << 2); + data[4] = (lR2 | lR3) + data[2]; + + lR3 = ((((data[0] + 6) ^ data[0]) | (data[2] << 1)) ^ 0x65) + data[0]; + lR2 = (data[1] ^ 0xED) + data[1]; + lR7 = ((data[3] + 0x29) ^ data[3]) * lR2; + data[5] = lR7 + lR3; + + lR2 = ((data[2] ^ 0x33) + data[2]) & 0x0A; + lR3 = (data[0] + 0xAD) ^ data[0]; + lR3 = lR3 + lR2; + lR2 = data[3] * data[3]; + lR7 = (lR2 | 1) + data[1]; + data[6] = (lR3 | lR7) + data[1]; + + lR3 = data[1] & 0x07; + lR2 = (lR3 - data[2]) & (data[0] | lR2 | 0x01); + data[7] = lR2 + data[3]; + + for (i = 0; i < 4; i++) + { + data[i + 4] = T1Key[data[i + 4]]; + } + } + break; + + case 0x030B00: + { + for (i = 0; i < 4; i++) + { + data[i] ^= XorKey[(Off + i) & 0x07]; + } + + lR6 = (data[3] + 0x6E) ^ data[3]; + lR6 = (lR6 * (data[2] << 1)) + 0x17; + lR3 = (data[1] + 0x77) ^ data[1]; + lR4 = (data[0] + 0xD7) ^ data[0]; + data[4] = ((lR4 & lR3) | lR6) + data[0]; + + lR4 = ((data[3] + 0x71) ^ data[3]) ^ 0x90; + lR6 = (data[1] + 0x1B) ^ data[1]; + lR4 = (lR4 * lR6) ^ data[0]; + data[5] = (lR4 ^ (data[2] << 1)) + data[1]; + + lR3 = (data[3] * data[3]) | 0x01; + lR4 = (((data[2] ^ 0x35) + data[2]) | lR3) + data[2]; + lR6 = data[1] ^ (data[0] + 0x4A); + data[6] = lR6 + lR4; + + lR3 = (data[0] * (data[2] << 1)) | data[1]; + lR4 = 0xFE - data[3]; + lR3 = lR4 ^ lR3; + data[7] = lR3 + data[3]; + + for (i = 0; i < 4; i++) + { + data[4 + i] = T1Key[data[4 + i]]; + } + } + break; + + default: + break; + } +} + +static void via3_fct1(uint8_t *data, uint32_t ident, uint8_t *XorKey, uint8_t *T1Key) +{ + uint8_t t; + + via3_core(data, 0, ident, XorKey, T1Key); + + switch (ident) + { + case 0x032820: + { + t = data[4]; + data[4] = data[7]; + data[7] = t; + } + break; + + case 0x030B00: + { + t = data[5]; + data[5] = data[7]; + data[7] = t; + } + break; + + default: + break; + } +} + +static void via3_fct2(uint8_t *data, uint32_t ident, uint8_t *XorKey, uint8_t *T1Key) +{ + uint8_t t; + + via3_core(data, 4, ident, XorKey, T1Key); + + switch (ident) + { + case 0x032820: + { + t = data[4]; + data[4] = data[7]; + data[7] = data[5]; + data[5] = data[6]; + data[6] = t; + } + break; + + case 0x030B00: + { + t = data[6]; + data[6] = data[7]; + data[7] = t; + } + break; + + default: + break; + } +} + +static int8_t via3_process_dw(uint8_t *data, uint32_t ident, uint8_t desKeyIndex) +{ + uint8_t i; + uint8_t tmp[8], T1Key[300], P1Key[8], KeyDes[16], XorKey[8]; + uint32_t ks1[32], ks2[32]; + + if (!get_key(T1Key, ident, 'T', 1, 300, 1)) + { + return 2; + } + + if (!get_key(P1Key, ident, 'P', 1, 8, 1)) + { + return 2; + } + + if (!get_key(KeyDes, ident, '0', desKeyIndex, 16, 1)) + { + return 2; + } + + if (!get_key(XorKey, ident, 'X', 1, 8, 1)) + { + return 2; + } + + for (i = 0; i < 4; i++) + { + tmp[i] = data[i + 4]; + } + + via3_fct1(tmp, ident, XorKey, T1Key); + + for (i = 0; i < 4; i++) + { + tmp[i] = data[i] ^ tmp[i + 4]; + } + + via3_fct2(tmp, ident, XorKey, T1Key); + + for (i = 0; i < 4; i++) + { + tmp[i] ^= XorKey[i + 4]; + } + + for (i = 0; i < 4; i++) + { + data[i] = data[i + 4] ^ tmp[i + 4]; + data[i + 4] = tmp[i]; + } + + des_set_key(KeyDes, ks1); + des_set_key(KeyDes + 8, ks2); + + des(data, ks1, 0); + des(data, ks2, 1); + des(data, ks1, 0); + + for (i = 0; i < 4; i++) + { + tmp[i] = data[i + 4]; + } + + via3_fct2(tmp, ident, XorKey, T1Key); + + for (i = 0; i < 4; i++) + { + tmp[i] = data[i] ^ tmp[i + 4]; + } + + via3_fct1(tmp, ident, XorKey, T1Key); + + for (i = 0; i < 4; i++) + { + tmp[i] ^= XorKey[i]; + } + + for (i = 0; i < 4; i++) + { + data[i] = data[i + 4] ^ tmp[i + 4]; + data[i + 4] = tmp[i]; + } + + return 0; +} + +static void via3_final_mix(uint8_t *dw) +{ + uint8_t tmp[4]; + + memcpy(tmp, dw, 4); + memcpy(dw, dw + 4, 4); + memcpy(dw + 4, tmp, 4); + + memcpy(tmp, dw + 8, 4); + memcpy(dw + 8, dw + 12, 4); + memcpy(dw + 12, tmp, 4); +} + +static int8_t via3_decrypt(uint8_t *source, uint8_t *dw, uint32_t ident, uint8_t desKeyIndex, + uint8_t aesKeyIndex, uint8_t aesMode, int8_t doFinalMix) +{ + int8_t aesAfterCore = 0, needsAES = (aesKeyIndex != 0xFF); + int32_t i, j; + + uint8_t tmpData[8], C1[8]; + uint8_t *pXorVector; + + char aesKey[16]; + + if (ident == 0) + { + return EMU_CORRUPT_DATA; + } + + if (!get_key(C1, ident, 'C', 1, 8, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + if (needsAES && !get_key((uint8_t *)aesKey, ident, 'E', aesKeyIndex, 16, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + if (aesMode == 0x0D || aesMode == 0x11 || aesMode == 0x15) + { + aesAfterCore = 1; + } + + if (needsAES && !aesAfterCore) + { + if (aesMode == 0x0F) + { + hdSurEncPhase1_D2_0F_11(source); + hdSurEncPhase2_D2_0F_11(source); + } + else if (aesMode == 0x13) + { + hdSurEncPhase1_D2_13_15(source); + } + + struct aes_keys aes; + aes_set_key(&aes, aesKey); + aes_decrypt(&aes, source, 16); + + if (aesMode == 0x0F) + { + hdSurEncPhase1_D2_0F_11(source); + } + else if (aesMode == 0x13) + { + hdSurEncPhase2_D2_13_15(source); + } + } + + for (i = 0; i < 2; i++) + { + memcpy(tmpData, source + i * 8, 8); + via3_process_dw(tmpData, ident, desKeyIndex); + + if (i != 0) + { + pXorVector = source; + } + else + { + pXorVector = &C1[0]; + } + + for (j = 0; j < 8; j++) + { + dw[i * 8 + j] = tmpData[j] ^ pXorVector[j]; + } + } + + if (needsAES && aesAfterCore) + { + if (aesMode == 0x11) + { + hdSurEncPhase1_D2_0F_11(dw); + hdSurEncPhase2_D2_0F_11(dw); + } + else if (aesMode == 0x15) + { + hdSurEncPhase1_D2_13_15(dw); + } + + struct aes_keys aes; + aes_set_key(&aes, aesKey); + aes_decrypt(&aes, dw, 16); + + if (aesMode == 0x11) + { + hdSurEncPhase1_D2_0F_11(dw); + } + + if (aesMode == 0x15) + { + hdSurEncPhase2_D2_13_15(dw); + } + } + + if (ident == 0x030B00) + { + if (doFinalMix) + { + via3_final_mix(dw); + } + + if (!is_valid_dcw(dw) || !is_valid_dcw(dw + 8)) + { + return EMU_CHECKSUM_ERROR; + } + } + + return EMU_OK; +} + +int8_t viaccess_ecm(uint8_t *ecm, uint8_t *dw) +{ + int8_t doFinalMix = 0; + + uint8_t nanoCmd = 0, nanoLen = 0, version = 0, providerKeyLen = 0; + uint8_t desKeyIndex = 0, aesMode = 0, aesKeyIndex = 0xFF; + uint16_t i = 0, keySelectPos = 0, ecmLen = SCT_LEN(ecm); + uint32_t currentIdent = 0; + + for (i = 4; i + 2 < ecmLen; ) + { + nanoCmd = ecm[i++]; + nanoLen = ecm[i++]; + + if (i + nanoLen > ecmLen) + { + return EMU_NOT_SUPPORTED; + } + + switch (nanoCmd) + { + case 0x40: + if (nanoLen < 0x03) + { + break; + } + version = ecm[i]; + if (nanoLen == 3) + { + currentIdent = ((ecm[i] << 16) | (ecm[i + 1] << 8)) | (ecm[i + 2] & 0xF0); + desKeyIndex = ecm[i + 2] & 0x0F; + keySelectPos = i + 3; + } + else + { + currentIdent = (ecm[i] << 16) | (ecm[i + 1] << 8) | ((ecm[i + 2] >> 4) & 0x0F); + desKeyIndex = ecm[i + 3]; + keySelectPos = i + 4; + } + providerKeyLen = nanoLen; + break; + + case 0x90: + if (nanoLen < 0x03) + { + break; + } + version = ecm[i]; + currentIdent = ((ecm[i] << 16) | (ecm[i + 1] << 8)) | (ecm[i + 2] & 0xF0); + desKeyIndex = ecm[i + 2] & 0x0F; + keySelectPos = i + 4; + if ((version == 3) && (nanoLen > 3)) + { + desKeyIndex = ecm[i + (nanoLen - 4)] & 0x0F; + } + providerKeyLen = nanoLen; + break; + + case 0x80: + nanoLen = 0; + break; + + case 0xD2: + if (nanoLen < 0x02) + { + break; + } + aesMode = ecm[i]; + aesKeyIndex = ecm[i + 1]; + break; + + case 0xDD: + nanoLen = 0; + break; + + case 0xEA: + if (nanoLen < 0x10) + { + break; + } + if (version < 2) + { + return via1_decrypt(ecm, dw, currentIdent, desKeyIndex); + } + else if (version == 2) + { + return via26_decrypt(ecm + i, dw, currentIdent, desKeyIndex); + } + else if (version == 3) + { + doFinalMix = 0; + if (currentIdent == 0x030B00 && providerKeyLen > 3) + { + if (keySelectPos + 2 >= ecmLen) + { + break; + } + if (ecm[keySelectPos] == 0x05 && ecm[keySelectPos + 1] == 0x67 && + (ecm[keySelectPos + 2] == 0x00 || ecm[keySelectPos + 2] == 0x01)) + { + if (ecm[keySelectPos + 2] == 0x01) + { + doFinalMix = 1; + } + } + else + { + break; + } + } + + return via3_decrypt(ecm + i, dw, currentIdent, desKeyIndex, aesKeyIndex, aesMode, doFinalMix); + } + break; + + default: + break; + } + + i += nanoLen; + } + + return EMU_NOT_SUPPORTED; +} + +// Viaccess EMM EMU + +int8_t viaccess_emm(uint8_t *emm, uint32_t *keysAdded) +{ + uint8_t nanoCmd = 0, subNanoCmd = 0, *tmp; + uint8_t ecmKeyCount = 0, emmKeyIndex = 0, aesMode = 0x0D; + uint8_t nanoLen = 0, subNanoLen = 0, haveEmmXorKey = 0, haveNewD0 = 0; + uint8_t ecmKeys[6][16], keyD0[2], emmKey[16], emmXorKey[16], provName[17]; + + uint16_t i = 0, j = 0, k = 0, emmLen = SCT_LEN(emm); + uint32_t ui1, ui2, ui3, ecmKeyIndex[6], provider = 0, ecmProvider = 0; + + char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36]; + struct aes_keys aes; + + memset(keyD0, 0, 2); + memset(ecmKeyIndex, 0, sizeof(uint32_t) * 6); + + for (i = 3; i + 2 < emmLen; ) + { + nanoCmd = emm[i++]; + nanoLen = emm[i++]; + + if (i + nanoLen > emmLen) + { + return EMU_NOT_SUPPORTED; + } + + switch (nanoCmd) + { + case 0x90: + { + if (nanoLen < 3) + { + break; + } + + ui1 = emm[i + 2]; + ui2 = emm[i + 1]; + ui3 = emm[i]; + provider = (ui1 | (ui2 << 8) | (ui3 << 16)); + + if (provider == 0x00D00040) + { + ecmProvider = 0x030B00; + } + else + { + return EMU_NOT_SUPPORTED; + } + break; + } + + case 0xD2: + { + if (nanoLen < 2) + { + break; + } + + emmKeyIndex = emm[i + 1]; + break; + } + + case 0x41: + { + if (nanoLen < 1) + { + break; + } + + if (!get_key(emmKey, provider, 'M', emmKeyIndex, 16, 1)) + { + return EMU_KEY_NOT_FOUND; + } + + memset(provName, 0, 17); + memset(emmXorKey, 0, 16); + + k = nanoLen < 16 ? nanoLen : 16; + + memcpy(provName, &emm[i], k); + aes_set_key(&aes, (char *)emmKey); + aes_decrypt(&aes, emmXorKey, 16); + + for (j = 0; j < 16; j++) + { + provName[j] ^= emmXorKey[j]; + } + provName[k] = 0; + + if (strcmp((char *)provName, "TNTSAT") != 0 && + strcmp((char *)provName, "TNTSATPRO") != 0 && + strcmp((char *)provName, "CSAT V") != 0) + { + return EMU_NOT_SUPPORTED; + } + + break; + } + + case 0xBA: + { + if (nanoLen < 2) + { + break; + } + + get_key(keyD0, ecmProvider, 'D', 0, 2, 0); + + ui1 = (emm[i] << 8) | emm[i + 1]; + + if( (uint32_t)((keyD0[0] << 8) | keyD0[1]) < ui1 || (keyD0[0] == 0x00 && keyD0[1] == 0x00)) + { + keyD0[0] = emm[i]; + keyD0[1] = emm[i + 1]; + haveNewD0 = 1; + break; + } + + return EMU_OK; + } + + case 0xBC: + { + break; + } + + case 0x43: + { + if (nanoLen < 16) + { + break; + } + + memcpy(emmXorKey, &emm[i], 16); + haveEmmXorKey = 1; + + break; + } + + case 0x44: + { + if (nanoLen < 3) + { + break; + } + + if (!haveEmmXorKey) + { + memset(emmXorKey, 0, 16); + } + + tmp = (uint8_t *)malloc(((nanoLen / 16) + 1) * 16 * sizeof(uint8_t)); + if (tmp == NULL) + { + return EMU_OUT_OF_MEMORY; + } + + memcpy(tmp, &emm[i], nanoLen); + aes_set_key(&aes, (char *)emmKey); + + for (j = 0; j < nanoLen; j += 16) + { + aes_decrypt(&aes, emmXorKey, 16); + + for (k = 0; k < 16; k++) + { + tmp[j + k] ^= emmXorKey[k]; + } + } + + memcpy(&emm[i - 2], tmp, nanoLen); + free(tmp); + nanoLen = 0; + i -= 2; + break; + } + + case 0x68: + { + if (ecmKeyCount > 5) + { + break; + } + + for (j = i; j + 2 < i + nanoLen; ) + { + subNanoCmd = emm[j++]; + subNanoLen = emm[j++]; + + if (j + subNanoLen > i + nanoLen) + { + break; + } + + switch (subNanoCmd) + { + case 0xD2: + { + if (nanoLen < 2) + { + break; + } + + aesMode = emm[j]; + emmKeyIndex = emm[j + 1]; + break; + } + + case 0x01: + { + if(nanoLen < 17) + { + break; + } + + ecmKeyIndex[ecmKeyCount] = emm[j]; + memcpy(&ecmKeys[ecmKeyCount], &emm[j + 1], 16); + + if (!get_key(emmKey, provider, 'M', emmKeyIndex, 16, 1)) + { + break; + } + + if (aesMode == 0x0F || aesMode == 0x11) + { + hdSurEncPhase1_D2_0F_11(ecmKeys[ecmKeyCount]); + hdSurEncPhase2_D2_0F_11(ecmKeys[ecmKeyCount]); + } + else if (aesMode == 0x13 || aesMode == 0x15) + { + hdSurEncPhase1_D2_13_15(ecmKeys[ecmKeyCount]); + } + + aes_set_key(&aes, (char *)emmKey); + aes_decrypt(&aes, ecmKeys[ecmKeyCount], 16); + + if (aesMode == 0x0F || aesMode == 0x11) + { + hdSurEncPhase1_D2_0F_11(ecmKeys[ecmKeyCount]); + } + else if (aesMode == 0x13 || aesMode == 0x15) + { + hdSurEncPhase2_D2_13_15(ecmKeys[ecmKeyCount]); + } + + ecmKeyCount++; + break; + } + + default: + break; + } + + j += subNanoLen; + } + break; + } + + case 0xF0: + { + if (nanoLen != 4) + { + break; + } + + ui1 = ((emm[i + 2] << 8) | (emm[i + 1] << 16) | (emm[i] << 24) | emm[i + 3]); + + if (ccitt32_crc(emm + 3, emmLen - 11) != ui1) + { + return EMU_CHECKSUM_ERROR; + } + + if (haveNewD0) + { + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + emu_set_key('V', ecmProvider, "D0", keyD0, 2, 1, NULL, NULL); + + for (j = 0; j < ecmKeyCount; j++) + { + snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "E%X", ecmKeyIndex[j]); + emu_set_key('V', ecmProvider, keyName, ecmKeys[j], 16, 1, NULL, NULL); + + (*keysAdded)++; + cs_hexdump(0, ecmKeys[j], 16, keyValue, sizeof(keyValue)); + cs_log("Key found in EMM: V %06X %s %s", ecmProvider, keyName, keyValue); + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + } + break; + } + + default: + break; + } + + i += nanoLen; + } + + return EMU_OK; +} + +#endif // WITH_EMU diff --git a/module-emulator-viaccess.h b/module-emulator-viaccess.h new file mode 100644 index 0000000..6f4afca --- /dev/null +++ b/module-emulator-viaccess.h @@ -0,0 +1,11 @@ +#ifndef MODULE_EMULATOR_VIACCESS_H +#define MODULE_EMULATOR_VIACCESS_H + +#ifdef WITH_EMU + +int8_t viaccess_ecm(uint8_t *ecm, uint8_t *dw); +int8_t viaccess_emm(uint8_t *emm, uint32_t *keysAdded); + +#endif // WITH_EMU + +#endif // MODULE_EMULATOR_VIACCESS_H diff --git a/module-emulator.c b/module-emulator.c new file mode 100644 index 0000000..865e668 --- /dev/null +++ b/module-emulator.c @@ -0,0 +1,894 @@ +#define MODULE_LOG_PREFIX "emu" + +#include "globals.h" + +#ifdef WITH_EMU + +#include "module-streamrelay.h" +#include "module-emulator-osemu.h" +#include "module-emulator-biss.h" +#include "module-emulator-irdeto.h" +#include "module-emulator-powervu.h" +#include "oscam-conf-chk.h" +#include "oscam-config.h" +#include "oscam-reader.h" +#include "oscam-string.h" + +/* + * Readers in OSCam consist of 2 basic parts. + * The hardware or the device part. This is where physical smart cards are inserted + * and made available to OSCam. + * The software or the emulation part. This is where the actual card reading is done, + * including ecm and emm processing (i.e emulation of the various cryptosystems). + * In the Emu reader, the device part has no meaning, but we have to create it in + * order to be compatible with OSCam's reader structure. +*/ + +/* + * Create the Emu "emulation" part. This is of type s_cardsystem. + * Similar structures are found in the main sources folder (files reader-xxxxxx.c) + * for every cryptosystem supported by OSCam. + * Here we read keys from our virtual card (aka the SoftCam.Key file) and we inform + * OSCam about them. This is done with the emu_card_info() function. Keep in mind + * that Emu holds all its keys to separate structures for faster access. + * In addition, ECM and EMM requests are processed here, with the emu_do_ecm() and + * emu_do_emm() functions. +*/ + +#define CS_OK 1 +#define CS_ERROR 0 + +extern char cs_confdir[128]; +#ifdef MODULE_STREAMRELAY +static int8_t emu_key_data_mutex_init = 0; +#endif +pthread_mutex_t emu_key_data_mutex; + +static void set_hexserial_to_version(struct s_reader *rdr) +{ + char cVersion[32]; + uint32_t version = EMU_VERSION; + uint8_t hversion[2]; + memset(hversion, 0, 2); + snprintf(cVersion, sizeof(cVersion), "%04d", version); + char_to_bin(hversion, cVersion, 4); + rdr->hexserial[3] = hversion[0]; + rdr->hexserial[4] = hversion[1]; +} + +static void set_prids(struct s_reader *rdr) +{ + int32_t i, j; + + rdr->nprov = 0; + + for (i = 0; (i < rdr->emu_auproviders.nfilts) && (rdr->nprov < CS_MAXPROV); i++) + { + for (j = 0; (j < rdr->emu_auproviders.filts[i].nprids) && (rdr->nprov < CS_MAXPROV); j++) + { + i2b_buf(4, rdr->emu_auproviders.filts[i].prids[j], rdr->prid[i]); + rdr->nprov++; + } + } +} + +static void emu_add_entitlement(struct s_reader *rdr, uint16_t caid, uint32_t provid, uint8_t *key, char *keyName, uint32_t keyLength, uint8_t isData) +{ + if (!rdr->ll_entitlements) + { + rdr->ll_entitlements = ll_create("ll_entitlements"); + } + + S_ENTITLEMENT *item; + if (cs_malloc(&item, sizeof(S_ENTITLEMENT))) + { + // fill item + item->caid = caid; + item->provid = provid; + item->id = 0; + item->class = 0; + item->start = 0; + item->end = 2147472000; + item->type = 0; + item->isKey = 1; + memcpy(item->name, keyName, 8); + item->key = key; + item->keyLength = keyLength; + item->isData = isData; + + // add item + ll_append(rdr->ll_entitlements, item); + } +} + +static void refresh_entitlements(struct s_reader *rdr) +{ + uint32_t i; + uint16_t caid; + KeyData *tmpKeyData; + LL_ITER itr; + biss2_rsa_key_t *item; + + cs_clear_entitlement(rdr); + + for (i = 0; i < StreamKeys.keyCount; i++) + { + emu_add_entitlement(rdr, b2i(2, StreamKeys.EmuKeys[i].key), StreamKeys.EmuKeys[i].provider, StreamKeys.EmuKeys[i].key, + StreamKeys.EmuKeys[i].keyName, StreamKeys.EmuKeys[i].keyLength, 1); + } + + for (i = 0; i < ViKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x0500, ViKeys.EmuKeys[i].provider, ViKeys.EmuKeys[i].key, + ViKeys.EmuKeys[i].keyName, ViKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < IrdetoKeys.keyCount; i++) + { + tmpKeyData = &IrdetoKeys.EmuKeys[i]; + do + { + emu_add_entitlement(rdr, tmpKeyData->provider >> 8, tmpKeyData->provider & 0xFF, + tmpKeyData->key, tmpKeyData->keyName, tmpKeyData->keyLength, 0); + + tmpKeyData = tmpKeyData->nextKey; + } + while (tmpKeyData != NULL); + } + + for (i = 0; i < CwKeys.keyCount; i++) + { + emu_add_entitlement(rdr, CwKeys.EmuKeys[i].provider >> 8, CwKeys.EmuKeys[i].provider & 0xFF, + CwKeys.EmuKeys[i].key, CwKeys.EmuKeys[i].keyName, CwKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < PowervuKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x0E00, PowervuKeys.EmuKeys[i].provider, PowervuKeys.EmuKeys[i].key, + PowervuKeys.EmuKeys[i].keyName, PowervuKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < TandbergKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x1010, TandbergKeys.EmuKeys[i].provider, TandbergKeys.EmuKeys[i].key, + TandbergKeys.EmuKeys[i].keyName, TandbergKeys.EmuKeys[i].keyLength, 0); + } + + for (i = 0; i < NagraKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x1801, NagraKeys.EmuKeys[i].provider, NagraKeys.EmuKeys[i].key, + NagraKeys.EmuKeys[i].keyName, NagraKeys.EmuKeys[i].keyLength, 0); + } + + // Session words for BISS1 mode 1/E (caid 2600) and BISS2 mode 1/E (caid 2602) + for (i = 0; i < BissSWs.keyCount; i++) + { + caid = (BissSWs.EmuKeys[i].keyLength == 8) ? 0x2600 : 0x2602; + emu_add_entitlement(rdr, caid, BissSWs.EmuKeys[i].provider, BissSWs.EmuKeys[i].key, + BissSWs.EmuKeys[i].keyName, BissSWs.EmuKeys[i].keyLength, 0); + } + + // Session keys (ECM keys) for BISS2 mode CA + for (i = 0; i < Biss2Keys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x2610, Biss2Keys.EmuKeys[i].provider, Biss2Keys.EmuKeys[i].key, + Biss2Keys.EmuKeys[i].keyName, Biss2Keys.EmuKeys[i].keyLength, 0); + } + + // RSA keys (EMM keys) for BISS2 mode CA + itr = ll_iter_create(rdr->ll_biss2_rsa_keys); + while ((item = ll_iter_next(&itr))) + { + emu_add_entitlement(rdr, 0x2610, 0, item->ekid, "RSAPRI", 8, 0); + } + + for (i = 0; i < OmnicryptKeys.keyCount; i++) + { + emu_add_entitlement(rdr, 0x00FF, OmnicryptKeys.EmuKeys[i].provider, OmnicryptKeys.EmuKeys[i].key, + OmnicryptKeys.EmuKeys[i].keyName, OmnicryptKeys.EmuKeys[i].keyLength, 0); + } +} + +static int32_t emu_do_ecm(struct s_reader *rdr, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + if (!emu_process_ecm(rdr, er, ea->cw, &ea->cw_ex)) + { + return CS_OK; + } + + return CS_ERROR; +} + +static int32_t emu_do_emm(struct s_reader *rdr, EMM_PACKET *emm) +{ + uint32_t keysAdded = 0; + + if (emm->emmlen < 3) + { + return CS_ERROR; + } + + if (SCT_LEN(emm->emm) > emm->emmlen) + { + return CS_ERROR; + } + + if (!emu_process_emm(rdr, b2i(2, emm->caid), emm->emm, &keysAdded)) + { + if (keysAdded > 0) + { + refresh_entitlements(rdr); + } + + return CS_OK; + } + + return CS_ERROR; +} + +static int32_t emu_card_info(struct s_reader *rdr) +{ + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + + // Delete keys from Emu's memory + emu_clear_keydata(); + + // Delete BISS2 mode CA RSA keys + ll_destroy_data(&rdr->ll_biss2_rsa_keys); + + // Read keys built in the OSCam-Emu binary + emu_read_keymemory(rdr); + + // Read keys from SoftCam.Key file + emu_set_keyfile_path(cs_confdir); + + if (!emu_read_keyfile(rdr, cs_confdir)) + { + if (emu_read_keyfile(rdr, "/var/keys/")) + { + emu_set_keyfile_path("/var/keys/"); + } + } + + // Read BISS2 mode CA RSA keys from PEM files + biss_read_pem(rdr, BISS2_MAX_RSA_KEYS); + + cs_log("Total keys in memory: W:%d V:%d N:%d I:%d F:%d G:%d O:%d P:%d T:%d A:%d", + CwKeys.keyCount, ViKeys.keyCount, NagraKeys.keyCount, IrdetoKeys.keyCount, BissSWs.keyCount, + Biss2Keys.keyCount, OmnicryptKeys.keyCount, PowervuKeys.keyCount, TandbergKeys.keyCount, + StreamKeys.keyCount); + + // Inform OSCam about all available keys. + // This is used for listing the "entitlements" in the webif's reader page. + refresh_entitlements(rdr); + + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + set_prids(rdr); + + set_hexserial_to_version(rdr); + + return CS_OK; +} + +/* +static int32_t emu_card_init(struct s_reader *UNUSED(rdr), struct s_ATR *UNUSED(atr)) +{ + return CS_ERROR; +} +*/ + +int32_t emu_get_via3_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + uint32_t provid = 0; + + if(ep->emm[3] == 0x90 && ep->emm[4] == 0x03) + { + provid = b2i(3, ep->emm + 5); + provid &= 0xFFFFF0; + i2b_buf(4, provid, ep->provid); + } + + switch (ep->emm[0]) + { + case 0x88: + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, 4); + rdr_log_dbg(rdr, D_EMM, "UNIQUE"); + return 1; + + case 0x8A: + case 0x8B: + ep->type = GLOBAL; + rdr_log_dbg(rdr, D_EMM, "GLOBAL"); + return 1; + + case 0x8C: + case 0x8D: + ep->type = SHARED; + rdr_log_dbg(rdr, D_EMM, "SHARED (part)"); + // We need those packets to pass otherwise we would never + // be able to complete EMM reassembly + return 1; + + case 0x8E: + ep->type = SHARED; + rdr_log_dbg(rdr, D_EMM, "SHARED"); + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 3, 3); + return 1; + + default: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + return 1; + } +} + +int32_t emu_get_ird2_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + int32_t l = (ep->emm[3] & 0x07); + int32_t base = (ep->emm[3] >> 3); + char dumprdrserial[l * 3], dumpemmserial[l * 3]; + + switch (l) + { + case 0: + // global emm, 0 bytes addressed + ep->type = GLOBAL; + rdr_log_dbg(rdr, D_EMM, "GLOBAL base = %02x", base); + return 1; + + case 2: + // shared emm, 2 bytes addressed + ep->type = SHARED; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, l); + cs_hexdump(1, rdr->hexserial, l, dumprdrserial, sizeof(dumprdrserial)); + cs_hexdump(1, ep->hexserial, l, dumpemmserial, sizeof(dumpemmserial)); + rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED l = %d ep = {%s} rdr = {%s} base = %02x", + l, dumpemmserial, dumprdrserial, base); + return 1; + + case 3: + // unique emm, 3 bytes addressed + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, l); + cs_hexdump(1, rdr->hexserial, l, dumprdrserial, sizeof(dumprdrserial)); + cs_hexdump(1, ep->hexserial, l, dumpemmserial, sizeof(dumpemmserial)); + rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE l = %d ep = {%s} rdr = {%s} base = %02x", + l, dumpemmserial, dumprdrserial, base); + return 1; + + default: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + return 1; + } +} + +int32_t emu_get_pvu_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + if (ep->emm[0] == 0x82) + { + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 12, 4); + } + else + { + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + } + return 1; +} + +int32_t emu_get_tan_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + if (ep->emm[0] == 0x82 || ep->emm[0] == 0x83) + { + ep->type = GLOBAL; + } + else + { + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + } + return 1; +} + +int32_t emu_get_biss_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + switch (ep->emm[0]) + { + case 0x81: // Spec say this is for EMM, but oscam (and all other crypto systems) use it for ECM + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + ep->type = GLOBAL; + return 1; + + default: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + return 1; + } +} + +static int32_t emu_get_emm_type(struct emm_packet_t *ep, struct s_reader *rdr) +{ + uint16_t caid = b2i(2, ep->caid); + + if (caid_is_viaccess(caid)) return emu_get_via3_emm_type(ep, rdr); + if (caid_is_irdeto(caid)) return emu_get_ird2_emm_type(ep, rdr); + if (caid_is_powervu(caid)) return emu_get_pvu_emm_type(ep, rdr); + if (caid_is_director(caid)) return emu_get_tan_emm_type(ep, rdr); + if (caid_is_biss_dynamic(caid)) return emu_get_biss_emm_type(ep, rdr); + + return CS_ERROR; +} + +FILTER *get_emu_prids_for_caid(struct s_reader *rdr, uint16_t caid) +{ + int32_t i; + + for (i = 0; i < rdr->emu_auproviders.nfilts; i++) + { + if (caid == rdr->emu_auproviders.filts[i].caid) + { + return &rdr->emu_auproviders.filts[i]; + } + } + + return NULL; +} + +static int32_t emu_get_via3_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid)) +{ + if (*emm_filters == NULL) + { + const unsigned int max_filter_count = 1; + if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return CS_ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x8A; + filters[idx].mask[0] = 0xFE; + filters[idx].filter[3] = 0x80; + filters[idx].mask[3] = 0x80; + idx++; + + *filter_count = idx; + } + + return CS_OK; +} + +static int32_t emu_get_ird2_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t caid, uint32_t UNUSED(provid)) +{ + uint8_t hexserial[3], prid[4]; + FILTER *emu_provids; + int8_t have_provid = 0, have_serial = 0; + int32_t i; + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + if(irdeto2_get_hexserial(caid, hexserial)) + { + have_serial = 1; + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + emu_provids = get_emu_prids_for_caid(rdr, caid); + if (emu_provids != NULL && emu_provids->nprids > 0) + { + have_provid = 1; + } + + if (*emm_filters == NULL) + { + const unsigned int max_filter_count = have_serial + (2 * (have_provid ? emu_provids->nprids : 0)); + if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return CS_ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + unsigned int idx = 0; + + if (have_serial) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFB; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], hexserial, 3); + memset(&filters[idx].mask[2], 0xFF, 3); + idx++; + } + + for (i = 0; have_provid && i < emu_provids->nprids; i++) + { + i2b_buf(4, emu_provids->prids[i], prid); + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFB; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], &prid[1], 3); + memset(&filters[idx].mask[2], 0xFF, 3); + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFA; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], &prid[1], 2); + memset(&filters[idx].mask[2], 0xFF, 2); + idx++; + } + + *filter_count = idx; + } + + return CS_OK; +} + +static int32_t emu_get_pvu_emm_filter(struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, + uint16_t caid, uint16_t srvid, uint16_t tsid, uint16_t onid, uint32_t ens) +{ + uint8_t hexserials[32][4]; + uint32_t i, count = 0; + + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + count = powervu_get_hexserials_new(hexserials, 32, caid, tsid, onid, ens); + if (count == 0) + { + count = powervu_get_hexserials(hexserials, 32, srvid); + if (count == 0) + { + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + return CS_ERROR; + } + } + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + if (*emm_filters == NULL) + { + const unsigned int max_filter_count = count; + if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return CS_ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + for (i = 0; i < count; i++) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].filter[10] = hexserials[i][0]; + filters[idx].filter[11] = hexserials[i][1]; + filters[idx].filter[12] = hexserials[i][2]; + filters[idx].filter[13] = hexserials[i][3]; + filters[idx].mask[0] = 0xFF; + filters[idx].mask[10] = 0xFF; + filters[idx].mask[11] = 0xFF; + filters[idx].mask[12] = 0xFF; + filters[idx].mask[13] = 0xFF; + idx++; + } + + *filter_count = idx; + } + + return CS_OK; +} + +static int32_t emu_get_tan_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid)) +{ + if (*emm_filters == NULL) + { + const unsigned int max_filter_count = 2; + uint8_t buf[8]; + + if (!emu_find_key('T', 0x40, 0, "MK", buf, 8, 0, 0, 0, NULL) && + !emu_find_key('T', 0x40, 0, "MK01", buf, 8, 0, 0, 0, NULL)) + { + return CS_ERROR; + } + + if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return CS_ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + idx++; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].mask[0] = 0xFF; + idx++; + + *filter_count = idx; + } + + return CS_OK; +} + +static int32_t emu_get_biss_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid)) +{ + if (*emm_filters == NULL) + { + const unsigned int max_filter_count = 15; + if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return CS_ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + uint8_t i; + + for (i = 0; i < max_filter_count; i++) + { + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x81 + i; // What about table 0x81? + filters[idx].mask[0] = 0xFF; + idx++; + + *filter_count = idx; + } + } + return CS_OK; +} + +static int32_t emu_get_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **UNUSED(emm_filters), unsigned int *UNUSED(filter_count)) +{ + return CS_ERROR; +} + +static int32_t emu_get_emm_filter_adv(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, + uint16_t caid, uint32_t provid, uint16_t srvid, uint16_t tsid, uint16_t onid, uint32_t ens) +{ + if (caid_is_viaccess(caid)) return emu_get_via3_emm_filter(rdr, emm_filters, filter_count, caid, provid); + if (caid_is_irdeto(caid)) return emu_get_ird2_emm_filter(rdr, emm_filters, filter_count, caid, provid); + if (caid_is_powervu(caid)) return emu_get_pvu_emm_filter(emm_filters, filter_count, caid, srvid, tsid, onid, ens); + if (caid_is_director(caid)) return emu_get_tan_emm_filter(rdr, emm_filters, filter_count, caid, provid); + if (caid_is_biss_dynamic(caid)) return emu_get_biss_emm_filter(rdr, emm_filters, filter_count, caid, provid); + + return CS_ERROR; +} + +const struct s_cardsystem reader_emu = +{ + .desc = "emu", + .caids = (uint16_t[]){ 0x05, 0x06, 0x0D, 0x0E, 0x10, 0x18, 0x26, 0 }, + .do_ecm = emu_do_ecm, + .do_emm = emu_do_emm, + .card_info = emu_card_info, + //.card_init = emu_card_init, // apparently this is not needed at all + .get_emm_type = emu_get_emm_type, + .get_emm_filter = emu_get_emm_filter, // needed to pass checks + .get_emm_filter_adv = emu_get_emm_filter_adv, +}; + +/* + * Create the Emu virtual "device" part. This is of type s_cardreader. + * Similar structures are found in the csctapi (Card System Card Terminal API) + * folder for every IFD (InterFace Device), aka smart card reader. + * Since we have no hardware to initialize, we start our Stream Relay server + * with the emu_reader_init() function. + * At Emu shutdown, we remove keys from memory with the emu_close() function. +*/ + +#define CR_OK 0 +#define CR_ERROR 1 + +static int32_t emu_reader_init(struct s_reader *UNUSED(reader)) +{ +#ifdef MODULE_STREAMRELAY + if (cfg.stream_relay_enabled && (stream_server_thread_init == 0)) + { + int32_t i; + stream_server_thread_init = 1; + SAFE_MUTEX_INIT(&emu_fixed_key_srvid_mutex, NULL); + + for (i = 0; i < EMU_STREAM_SERVER_MAX_CONNECTIONS; i++) + { + SAFE_MUTEX_INIT(&emu_fixed_key_data_mutex[i], NULL); + ll_emu_stream_delayed_keys[i] = ll_create("ll_emu_stream_delayed_keys"); + memset(&emu_fixed_key_data[i], 0, sizeof(emu_stream_client_key_data)); + } + + start_thread("stream_key_delayer", stream_key_delayer, NULL, NULL, 1, 1); + cs_log("Stream key delayer initialized"); + } + + // Initialize mutex for exclusive access to key database and key file + if (!emu_key_data_mutex_init) + { + SAFE_MUTEX_INIT(&emu_key_data_mutex, NULL); + emu_key_data_mutex_init = 1; + } +#endif + return CR_OK; +} + +static int32_t emu_close(struct s_reader *UNUSED(reader)) +{ + cs_log("Reader is shutting down"); + + // Delete keys from Emu's memory + SAFE_MUTEX_LOCK(&emu_key_data_mutex); + emu_clear_keydata(); + SAFE_MUTEX_UNLOCK(&emu_key_data_mutex); + + return CR_OK; +} + +static int32_t emu_get_status(struct s_reader *UNUSED(reader), int32_t *in) { *in = 1; return CR_OK; } +static int32_t emu_activate(struct s_reader *UNUSED(reader), struct s_ATR *UNUSED(atr)) { return CR_OK; } +static int32_t emu_transmit(struct s_reader *UNUSED(reader), uint8_t *UNUSED(buffer), uint32_t UNUSED(size), uint32_t UNUSED(expectedlen), uint32_t UNUSED(delay), uint32_t UNUSED(timeout)) { return CR_OK; } +static int32_t emu_receive(struct s_reader *UNUSED(reader), uint8_t *UNUSED(buffer), uint32_t UNUSED(size), uint32_t UNUSED(delay), uint32_t UNUSED(timeout)) { return CR_OK; } +static int32_t emu_write_settings(struct s_reader *UNUSED(reader), struct s_cardreader_settings *UNUSED(s)) { return CR_OK; } +static int32_t emu_card_write(struct s_reader *UNUSED(pcsc_reader), const uint8_t *UNUSED(buf), uint8_t *UNUSED(cta_res), uint16_t *UNUSED(cta_lr), int32_t UNUSED(l)) { return CR_OK; } +static int32_t emu_set_protocol(struct s_reader *UNUSED(rdr), uint8_t *UNUSED(params), uint32_t *UNUSED(length), uint32_t UNUSED(len_request)) { return CR_OK; } + +const struct s_cardreader cardreader_emu = +{ + .desc = "emu", + .typ = R_EMU, + .skip_extra_atr_parsing = 1, + .reader_init = emu_reader_init, + .get_status = emu_get_status, + .activate = emu_activate, + .transmit = emu_transmit, + .receive = emu_receive, + .close = emu_close, + .write_settings = emu_write_settings, + .card_write = emu_card_write, + .set_protocol = emu_set_protocol, +}; + +void add_emu_reader(void) +{ + // This function is called inside oscam.c and creates an emu [reader] with default + // settings in oscam.server file. If an emu [reader] already exists, it uses that. + + LL_ITER itr; + struct s_reader *rdr; + int8_t haveEmuReader = 0; + char emuName[] = "emulator"; + char *ctab, *ftab, *emu_auproviders, *disablecrccws_only_for; + + // Check if emu [reader] entry already exists in oscam.server file and get it + itr = ll_iter_create(configured_readers); + while ((rdr = ll_iter_next(&itr))) + { + if (rdr->typ == R_EMU) + { + haveEmuReader = 1; + break; + } + } + + rdr = NULL; + + // If there's no emu [reader] in oscam.server, create one with default settings + if (!haveEmuReader) + { + if (!cs_malloc(&rdr, sizeof(struct s_reader))) + { + return; + } + + reader_set_defaults(rdr); + + rdr->enable = 1; + rdr->typ = R_EMU; + cs_strncpy(rdr->label, emuName, sizeof(emuName)); + cs_strncpy(rdr->device, emuName, sizeof(emuName)); + + // CAIDs + ctab = strdup("0500,0604,0D00,0E00,1010,1801,2600,2602,2610"); + chk_caidtab(ctab, &rdr->ctab); + NULLFREE(ctab); + + // Idents + ftab = strdup("0500:020A00,021110;" + "0604:000000;" + "0D00:0000C0;" + "0E00:000000;" + "1010:000000;" + "1801:000000,001101,002111,007301;" + "2600:000000;" + "2602:000000;" + "2610:000000;" + ); + chk_ftab(ftab, &rdr->ftab); + NULLFREE(ftab); + + // AU providers + emu_auproviders = strdup("0604:010200;0E00:000000;1010:000000;2610:000000;"); + chk_ftab(emu_auproviders, &rdr->emu_auproviders); + NULLFREE(emu_auproviders); + + // EMM cache + rdr->cachemm = 2; + rdr->rewritemm = 1; + rdr->logemm = 2; + rdr->deviceemm = 1; + + // User group + rdr->grp = 0x1ULL; + + // Add the "device" part to our emu reader + rdr->crdr = &cardreader_emu; + + // Disable CW checksum test for PowerVu + disablecrccws_only_for = strdup("0E00:000000"); + chk_ftab(disablecrccws_only_for, &rdr->disablecrccws_only_for); + NULLFREE(disablecrccws_only_for); + + reader_fixups_fn(rdr); + ll_append(configured_readers, rdr); + } + + // Set DVB Api delayer option +#ifdef HAVE_DVBAPI + if (cfg.dvbapi_enabled && cfg.dvbapi_delayer < 60) + { + cfg.dvbapi_delayer = 60; + } +#endif + + cs_log("OSCam-Emu version %d", EMU_VERSION); +} + +#endif // WITH_EMU diff --git a/module-gbox-cards.c b/module-gbox-cards.c new file mode 100644 index 0000000..ea482fa --- /dev/null +++ b/module-gbox-cards.c @@ -0,0 +1,666 @@ +#define MODULE_LOG_PREFIX "gbox" + +#include "globals.h" + +#ifdef MODULE_GBOX +#include "module-gbox.h" +#include "module-gbox-cards.h" +#include "module-gbox-helper.h" +#include "oscam-lock.h" +#include "oscam-garbage.h" +#include "oscam-files.h" +#include "oscam-chk.h" +#include "oscam-string.h" +#include "oscam-time.h" + +LLIST *gbox_cards; +CS_MUTEX_LOCK gbox_cards_lock; +uint8_t checkcode[7]; +uint8_t last_checkcode[7]; +uint8_t sid_verified = 0; + +GBOX_CARDS_ITER *gbox_cards_iter_create(void) +{ + GBOX_CARDS_ITER *gci; + if(!cs_malloc(&gci, sizeof(GBOX_CARDS_ITER))) + { return NULL; } + cs_readlock(__func__, &gbox_cards_lock); + gci->it = ll_iter_create(gbox_cards); + return gci; +} + +void gbox_cards_iter_destroy(GBOX_CARDS_ITER *gci) +{ + cs_readunlock(__func__, &gbox_cards_lock); + if (gci) { add_garbage(gci); } +} + +struct gbox_card *gbox_cards_iter_next(GBOX_CARDS_ITER *gci) +{ + if (gci) { return ll_iter_next(&gci->it); } + else { return NULL; } +} + +uint8_t gbox_get_crd_dist_lev(uint16_t crd_id) +{ + uint8_t crd_dist = 0; + uint8_t crd_level = 0; + struct gbox_card *card; + cs_readlock(__func__, &gbox_cards_lock); + LL_ITER it = ll_iter_create(gbox_cards); + while((card = ll_iter_next(&it))) + { + if ((card->type == GBOX_CARD_TYPE_GBOX || card->type == GBOX_CARD_TYPE_CCCAM) && card->id.peer == crd_id) + { + crd_dist = card->dist; + crd_level = card->lvl; + break; + } + } + cs_readunlock(__func__, &gbox_cards_lock); + return ((crd_level << 4) | (crd_dist & 0xf)); +} + +void gbox_write_share_cards_info(void) +{ + uint16_t card_count_shared = 0; + char *fext = FILE_SHARED_CARDS_INFO; + char *fname = get_gbox_tmp_fname(fext); + FILE *fhandle_shared; + fhandle_shared = fopen(fname, "w"); + if(!fhandle_shared) + { + cs_log("Couldn't open %s: %s", fname, strerror(errno)); + return; + } + + struct gbox_card *card; + cs_readlock(__func__, &gbox_cards_lock); + LL_ITER it = ll_iter_create(gbox_cards); + while((card = ll_iter_next(&it))) + { + if (card->type == GBOX_CARD_TYPE_GBOX) + { + fprintf(fhandle_shared, "CardID %d at %s Card %08X Sl:%d Lev:%1d dist:%1d id:%04X\n", + card_count_shared, card->origin_peer->hostname, card->caprovid, + card->id.slot, card->lvl, card->dist, card->id.peer); + card_count_shared++; + } + } + cs_readunlock(__func__, &gbox_cards_lock); +//char tmp[32]; +//fprintf(fhandle_shared, "my checkcode: %s",cs_hexdump(1, gbox_get_my_checkcode(), 7, tmp, sizeof(tmp))); + fclose(fhandle_shared); + cs_log_dbg(D_READER,"share.info written"); + //cs_log("share.info written"); + return; +} + +uint16_t gbox_write_local_cards_info(void) +{ + uint16_t card_count_local = 0; + char *fext = FILE_LOCAL_CARDS_INFO; + char *fname = get_gbox_tmp_fname(fext); + FILE *fhandle_local; + fhandle_local = fopen(fname, "w"); + if(!fhandle_local) + { + cs_log("Couldn't open %s: %s", fname, strerror(errno)); + return 0; + } + + struct gbox_card *card; + cs_readlock(__func__, &gbox_cards_lock); + LL_ITER it = ll_iter_create(gbox_cards); + while((card = ll_iter_next(&it))) + { + switch (card->type) + { + case GBOX_CARD_TYPE_GBOX: + break; + case GBOX_CARD_TYPE_LOCAL: + fprintf(fhandle_local, "CardID:%2d %s %08X Sl:%2d id:%04X\n", + card_count_local, "Local_Card", card->caprovid, card->id.slot, card->id.peer); + card_count_local++; + break; + case GBOX_CARD_TYPE_BETUN: + fprintf(fhandle_local, "CardID:%2d %s %08X Sl:%2d id:%04X\n", + card_count_local, "Betun_Card", card->caprovid, card->id.slot, card->id.peer); + card_count_local++; + break; + case GBOX_CARD_TYPE_CCCAM: + fprintf(fhandle_local, "CardID:%2d %s %08X Sl:%2d id:%04X\n", + card_count_local, "CCcam_Card", card->caprovid, card->id.slot, card->id.peer); + card_count_local++; + break; + case GBOX_CARD_TYPE_PROXY: + fprintf(fhandle_local, "CardID:%2d %s %08X Sl:%2d id:%04X\n", + card_count_local, "Proxy_Card", card->caprovid, card->id.slot, card->id.peer); + card_count_local++; + break; + default: + cs_log("Invalid card type: %d in gbox_write_cards_info", card->type); + break; + } + } + cs_readunlock(__func__, &gbox_cards_lock); + fclose(fhandle_local); + cs_log_dbg(D_READER,"sc.info written"); + //cs_log("sc.info written"); + return card_count_local; +} + +void gbox_write_stats(void) +{ + int32_t card_count = 0; + struct gbox_good_srvid *srvid_good = NULL; + struct gbox_bad_srvid *srvid_bad = NULL; + char *fext = FILE_STATS; + char *fname = get_gbox_tmp_fname(fext); + FILE *fhandle; + fhandle = fopen(fname, "w"); + if(!fhandle) + { + cs_log("Couldn't open %s: %s", fname, strerror(errno)); + return; + } + fprintf(fhandle, "Statistics for peer cards received\n"); + struct gbox_card *card; + cs_readlock(__func__, &gbox_cards_lock); + LL_ITER it = ll_iter_create(gbox_cards); + while((card = ll_iter_next(&it))) + { + if (card->type == GBOX_CARD_TYPE_GBOX) + { + fprintf(fhandle, "\nCard# %04d CaProv:%08X ID:%04X #CWs:%d AVGtime:%d ms", + card_count +1, card->caprovid, card->id.peer, card->no_cws_returned, card->average_cw_time); + fprintf(fhandle, "\n Good SID: "); + LL_ITER it2 = ll_iter_create(card->goodsids); + while((srvid_good = ll_iter_next(&it2))) + { fprintf(fhandle, "%04X ", srvid_good->srvid.sid); } + fprintf(fhandle, "\n Bad SID: "); + it2 = ll_iter_create(card->badsids); + while((srvid_bad = ll_iter_next(&it2))) + { fprintf(fhandle, "%04X ", srvid_bad->srvid.sid); } + card_count++; + } + } // end of while ll_iter_next + cs_readunlock(__func__, &gbox_cards_lock); + + fclose(fhandle); + return; +} + +void init_gbox_cards_list(void) +{ + gbox_cards = ll_create("gbox.cards"); + cs_lock_create(__func__, &gbox_cards_lock, "gbox_cards_lock", 5000); +} + +static void gbox_free_card(struct gbox_card *card) +{ + ll_destroy_data(&card->badsids); + ll_destroy_data(&card->goodsids); + add_garbage(card); + return; +} + +uint8_t *gbox_get_my_checkcode(void) +{ + return &checkcode[0]; +} + +uint8_t *gbox_update_my_checkcode(void) +{ + checkcode[0] = 0x15; + checkcode[1] = 0x30; + checkcode[2] = 0x02; + checkcode[3] = 0x04; + checkcode[4] = 0x19; + checkcode[5] = 0x19; + checkcode[6] = 0x66; + + struct gbox_card *card; + cs_readlock(__func__, &gbox_cards_lock); + LL_ITER it = ll_iter_create(gbox_cards); + while((card = ll_iter_next(&it))) + { + if(card->lvl) + { + checkcode[0] ^= (0xFF & (card->caprovid >> 24)); + checkcode[1] ^= (0xFF & (card->caprovid >> 16)); + checkcode[2] ^= (0xFF & (card->caprovid >> 8)); + checkcode[3] ^= (0xFF & (card->caprovid)); + checkcode[4] ^= (0xFF & (card->id.slot)); + checkcode[5] ^= (0xFF & (card->id.peer >> 8)); + checkcode[6] ^= (0xFF & (card->id.peer)); + } + } + cs_readunlock(__func__, &gbox_cards_lock); + + if(memcmp(last_checkcode, checkcode, 7)) + { + memcpy(last_checkcode, checkcode, 7); + //cs_log_dump(gbox_get_my_checkcode(), 7, "my checkcode updated:"); + cs_log_dump_dbg(D_READER, gbox_get_my_checkcode(), 7, "my checkcode updated:"); + } + return &checkcode[0]; +} + +uint16_t gbox_count_cards(void) +{ + return ll_count(gbox_cards); +} + +uint16_t gbox_count_peer_cards(uint16_t peer_id) +{ + uint16_t counter = 0; + struct gbox_card *card; + + cs_readlock(__func__, &gbox_cards_lock); + LL_ITER it = ll_iter_create(gbox_cards); + while((card = ll_iter_next(&it))) + { + if (card->origin_peer && card->origin_peer->gbox.id == peer_id) + { counter++; } + } + cs_readunlock(__func__, &gbox_cards_lock); + + return counter; +} + +void gbox_delete_cards(uint8_t delete_type, uint16_t criteria) +{ + struct gbox_card *card; + uint8_t found; + + cs_writelock(__func__, &gbox_cards_lock); + LL_ITER it = ll_iter_create(gbox_cards); + while((card = ll_iter_next(&it))) + { + found = 0; + switch (delete_type) + { + case GBOX_DELETE_FROM_PEER: + if (card->origin_peer && card->origin_peer->gbox.id == criteria) + { found = 1; } + break; + case GBOX_DELETE_WITH_ID: + if (card->id.peer == criteria) + { found = 1; } + break; + case GBOX_DELETE_WITH_TYPE: + if (card->type == criteria) + { found = 1; } + break; + default: + cs_log("Invalid delete type: %d in gbox_delete_cards", delete_type); + break; + } + if (found) + { + cs_log_dbg(D_READER, "remove card from card_list - peer: %04X %08X dist %d", card->id.peer, card->caprovid, card->dist); + ll_remove(gbox_cards, card); + } + } + cs_writeunlock(__func__, &gbox_cards_lock); + return; +} +static uint8_t check_card_properties(uint32_t caprovid, uint16_t id_peer, uint8_t slot, uint8_t distance, uint8_t type) +{ + uint8_t ret = 1; + + if (!distance) //local card + { return ret; } + + struct gbox_card *card; + cs_writelock(__func__, &gbox_cards_lock); + LL_ITER it = ll_iter_create(gbox_cards); + while((card = ll_iter_next(&it))) + { + if (card->caprovid == caprovid && card->id.peer == id_peer && card->id.slot == slot && type != GBOX_CARD_TYPE_CCCAM) + { + if (distance < card->dist) //better card + { + ll_remove(gbox_cards, card); + ret = 1; //let card pass + break; + } + else + { + ret = 0; //skip card + break; + } + } + if (card->caprovid == caprovid && card->id.peer == id_peer && type == GBOX_CARD_TYPE_CCCAM) + { + if (distance < card->dist) //better card + { + ll_remove(gbox_cards, card); + ret = 1; //let card pass + break; + } + else + { + ret = 0; //skip card + break; + } + } + } + cs_writeunlock(__func__, &gbox_cards_lock); + return ret; +} + +void gbox_add_card(uint16_t id_peer, uint32_t caprovid, uint8_t slot, uint8_t level, uint8_t distance, uint8_t type, struct gbox_peer *origin_peer) +{ + uint16_t caid = gbox_get_caid(caprovid); + uint32_t provid = gbox_get_provid(caprovid); + + if(!caprovid) //skip caprov 00000000 + { return; } + //don't insert 0100:000000 + if(caid_is_seca(caid) && (!provid)) + { return; } + //skip CAID 18XX providers +// if(caid_is_nagra(caid) && (provid)) +// { return; } + + struct gbox_card *card; + if(!cs_malloc(&card, sizeof(struct gbox_card))) + { + cs_log("Card allocation failed"); + return; + } + if (check_card_properties(caprovid, id_peer, slot, distance, type) && !check_peer_ignored(id_peer)) + { + cs_log_dbg(D_READER, "add card to card_list - peer: %04X %08X dist %d", id_peer, caprovid, distance); + card->caprovid = caprovid; + card->id.peer = id_peer; + card->id.slot = slot; + card->dist = distance; + card->lvl = level; + card->badsids = ll_create("badsids"); + card->goodsids = ll_create("goodsids"); + card->no_cws_returned = 0; + card->average_cw_time = 0; + card->type = type; + card->origin_peer = origin_peer; + cs_writelock(__func__, &gbox_cards_lock); + ll_append(gbox_cards, card); + cs_writeunlock(__func__, &gbox_cards_lock); + } + return; +} + +static void gbox_free_list(LLIST *card_list) +{ + if(card_list) + { + cs_writelock(__func__, &gbox_cards_lock); + LL_ITER it = ll_iter_create(card_list); + struct gbox_card *card; + while((card = ll_iter_next_remove(&it))) + { gbox_free_card(card); } + ll_destroy(&gbox_cards); + cs_writeunlock(__func__, &gbox_cards_lock); + } + return; +} + +void gbox_free_cardlist(void) +{ + gbox_free_list(gbox_cards); + return; +} + +void gbox_add_good_sid(uint16_t id_card, uint16_t caid, uint8_t slot, uint16_t sid_ok, uint32_t cw_time) +{ + struct gbox_card *card = NULL; + struct gbox_good_srvid *srvid = NULL; + uint8_t factor = 0; + + cs_writelock(__func__, &gbox_cards_lock); + LL_ITER it = ll_iter_create(gbox_cards); + while((card = ll_iter_next(&it))) + { + if(card->id.peer == id_card && gbox_get_caid(card->caprovid) == caid && card->id.slot == slot) + { + card->no_cws_returned++; + if (!card->no_cws_returned) + { card->no_cws_returned = 10; } // wrap around + if (card->no_cws_returned < 10) + { factor = card->no_cws_returned; } + else + { factor = 10; } + card->average_cw_time = ((card->average_cw_time * (factor-1)) + cw_time) / factor; + LL_ITER it2 = ll_iter_create(card->goodsids); + while((srvid = ll_iter_next(&it2))) + { + if(srvid->srvid.sid == sid_ok) + { + srvid->last_cw_received = time(NULL); + cs_writeunlock(__func__, &gbox_cards_lock); + return; // sid_ok is already in the list of goodsids + } + } + + if(!cs_malloc(&srvid, sizeof(struct gbox_good_srvid))) + { + cs_writeunlock(__func__, &gbox_cards_lock); + cs_log("Good SID allocation failed"); + return; + } + srvid->srvid.sid = sid_ok; + srvid->srvid.provid_id = gbox_get_provid(card->caprovid); + srvid->last_cw_received = time(NULL); + cs_log_dbg(D_READER, "Adding good SID: %04X for CAID: %04X Provider: %04X on CardID: %04X", sid_ok, caid, gbox_get_provid(card->caprovid), id_card); + ll_append(card->goodsids, srvid); + break; + } + } // end of ll_iter_next + // return dist_c; + cs_writeunlock(__func__, &gbox_cards_lock); + return; +} + +void gbox_remove_bad_sid(uint16_t id_peer, uint8_t id_slot, uint16_t sid) +{ + struct gbox_card *card = NULL; + struct gbox_bad_srvid *srvid = NULL; + + cs_writelock(__func__, &gbox_cards_lock); + LL_ITER it2 = ll_iter_create(gbox_cards); + while((card = ll_iter_next(&it2))) + { + if(card->id.peer == id_peer && card->id.slot == id_slot) + { + LL_ITER it3 = ll_iter_create(card->badsids); + while((srvid = ll_iter_next(&it3))) + { + if(srvid->srvid.sid == sid) + { + ll_iter_remove_data(&it3); // remove sid_ok from badsids + break; + } + } + } + } + cs_writeunlock(__func__, &gbox_cards_lock); +} + +uint8_t gbox_next_free_slot(uint16_t id) +{ + struct gbox_card *c; + uint8_t lastslot = 0; + + cs_readlock(__func__, &gbox_cards_lock); + LL_ITER it = ll_iter_create(gbox_cards); + while((c = ll_iter_next(&it))) + { + if(id == c->id.peer && c->id.slot > lastslot) + { lastslot = c->id.slot; } + } + cs_readunlock(__func__, &gbox_cards_lock); + return ++lastslot; +} + +static int8_t is_already_pending(LLIST *pending_cards, uint16_t peer_id, uint8_t slot) +{ + if (!pending_cards) + { return -1; } + + int8_t ret = 0; + struct gbox_card_id *current_id; + LL_LOCKITER *li = ll_li_create(pending_cards, 0); + while ((current_id = ll_li_next(li))) + { + if (current_id->peer == peer_id && current_id->slot == slot) + { + ret = 1; + break; + } + } + ll_li_destroy(li); + return ret; +} + +uint8_t gbox_get_cards_for_ecm(uint8_t *send_buf, int32_t len2, uint8_t max_cards, ECM_REQUEST *er, uint32_t *current_avg_card_time, uint16_t peer_id, uint8_t force_remm) +{ + if (!send_buf || !er) + { return 0; } + + uint8_t nb_matching_crds = 0; + struct gbox_good_srvid *srvid_good = NULL; + struct gbox_bad_srvid *srvid_bad = NULL; + uint8_t enough = 0; + time_t time_since_lastcw; + + // loop over good only + cs_readlock(__func__, &gbox_cards_lock); + LL_ITER it = ll_iter_create(gbox_cards); + LL_ITER it2; + struct gbox_card *card; + + while((card = ll_iter_next(&it))) + { + if(card->origin_peer && card->origin_peer->gbox.id == peer_id && card->type == GBOX_CARD_TYPE_GBOX && + gbox_get_caid(card->caprovid) == er->caid && gbox_get_provid(card->caprovid) == er->prid && !is_already_pending(er->gbox_cards_pending, card->id.peer, card->id.slot)) + { + sid_verified = 0; + + // check if sid is good + it2 = ll_iter_create(card->goodsids); + while((srvid_good = ll_iter_next(&it2))) + { + if(srvid_good->srvid.provid_id == er->prid && srvid_good->srvid.sid == er->srvid) + { + if (!enough || *current_avg_card_time > card->average_cw_time) + { + time_since_lastcw = llabs(srvid_good->last_cw_received - time(NULL)); + *current_avg_card_time = card->average_cw_time; + if (enough) + { len2 = len2 - 3; } + else + { + nb_matching_crds++; + if (time_since_lastcw < GBOX_SID_CONFIRM_TIME && er->gbox_ecm_status == GBOX_ECM_NEW_REQ) + { enough = 1; } + } + i2b_buf(2, card->id.peer, send_buf + len2); + send_buf[len2 + 2] = card->id.slot; + len2 = len2 + 3; + sid_verified = 1; + break; + } + } + } + + if(nb_matching_crds == max_cards) + { break; } + } + } + cs_readunlock(__func__, &gbox_cards_lock); + + // loop over bad and unknown cards + cs_writelock(__func__, &gbox_cards_lock); + it = ll_iter_create(gbox_cards); + while((card = ll_iter_next(&it))) + { + if(card->origin_peer && card->origin_peer->gbox.id == peer_id && card->type == GBOX_CARD_TYPE_GBOX && + gbox_get_caid(card->caprovid) == er->caid && gbox_get_provid(card->caprovid) == er->prid && !is_already_pending(er->gbox_cards_pending, card->id.peer, card->id.slot) && !enough) + { + sid_verified = 0; + + // check if sid is good + it2 = ll_iter_create(card->goodsids); + while((srvid_good = ll_iter_next(&it2))) + { + if(srvid_good->srvid.provid_id == er->prid && srvid_good->srvid.sid == er->srvid) + { + sid_verified = 1; + cs_log_dbg(D_READER, "ID: %04X SL: %02X SID: %04X is good", card->id.peer, card->id.slot, srvid_good->srvid.sid); + } + } + if(!sid_verified) + { + // check if sid is bad + LL_ITER itt = ll_iter_create(card->badsids); + while((srvid_bad = ll_iter_next(&itt))) + { + if(srvid_bad->srvid.provid_id == er->prid && srvid_bad->srvid.sid == er->srvid) + { + if (srvid_bad->bad_strikes < 3) + { + sid_verified = 2; + if(!force_remm) + { + srvid_bad->bad_strikes++; + } + else + { + srvid_bad->bad_strikes = 1; + //cs_log("cards.c - get card for ecm - Block bad SID: %04X - %d bad strikes", srvid_bad->srvid.sid, srvid_bad->bad_strikes); + } + } + else + { sid_verified = 1; } + cs_log_dbg(D_READER, "CRD_ID: %04X Slot: %d SID: %04X failed to relpy %d times", card->id.peer, card->id.slot, srvid_bad->srvid.sid, srvid_bad->bad_strikes); + break; + } + } + + // sid is neither good nor bad + if(sid_verified != 1) + { + i2b_buf(2, card->id.peer, send_buf + len2); + send_buf[len2 + 2] = card->id.slot; + len2 = len2 + 3; + nb_matching_crds++; + + if (!sid_verified) + { + if(!cs_malloc(&srvid_bad, sizeof(struct gbox_bad_srvid))) + { + cs_log("ServID allocation failed"); + cs_writeunlock(__func__, &gbox_cards_lock); + return 0; + } + + srvid_bad->srvid.sid = er->srvid; + srvid_bad->srvid.provid_id = gbox_get_provid(card->caprovid); + srvid_bad->bad_strikes = 1; + ll_append(card->badsids, srvid_bad); + cs_log_dbg(D_READER, "ID: %04X SL: %02X SID: %04X is not checked", card->id.peer, card->id.slot, srvid_bad->srvid.sid); + } + } + } + + if(nb_matching_crds == max_cards) + { break; } + } + } + cs_writeunlock(__func__, &gbox_cards_lock); + return nb_matching_crds; +} + +#endif diff --git a/module-gbox-cards.h b/module-gbox-cards.h new file mode 100644 index 0000000..e4a6112 --- /dev/null +++ b/module-gbox-cards.h @@ -0,0 +1,32 @@ +#ifndef MODULE_GBOX_CARDS_H_ +#define MODULE_GBOX_CARDS_H_ + +#ifdef MODULE_GBOX +typedef struct gboxcardsiter GBOX_CARDS_ITER; +struct gboxcardsiter +{ + LL_ITER it; +}; + +GBOX_CARDS_ITER *gbox_cards_iter_create(void); +void gbox_cards_iter_destroy(GBOX_CARDS_ITER *gci); +struct gbox_card *gbox_cards_iter_next(GBOX_CARDS_ITER *gci); +void gbox_write_share_cards_info(void); +uint16_t gbox_write_local_cards_info(void); +void gbox_write_stats(void); +void init_gbox_cards_list(void); +void gbox_add_card(uint16_t id_peer, uint32_t caprovid, uint8_t slot, uint8_t level, uint8_t distance, uint8_t type, struct gbox_peer *origin_peer); +uint8_t *gbox_get_my_checkcode(void); +uint8_t *gbox_update_my_checkcode(void); +uint16_t gbox_count_peer_cards(uint16_t peer_id); +uint16_t gbox_count_cards(void); +void gbox_delete_cards(uint8_t delete_type, uint16_t criteria); +void gbox_free_cardlist(void); +void gbox_add_good_sid(uint16_t id_card, uint16_t caid, uint8_t slot, uint16_t sid_ok, uint32_t cw_time); +void gbox_remove_bad_sid(uint16_t id_peer, uint8_t id_slot, uint16_t sid); +uint8_t gbox_next_free_slot(uint16_t id); +uint8_t gbox_get_cards_for_ecm(uint8_t *send_buf, int32_t len2, uint8_t max_cards, ECM_REQUEST *er, uint32_t *current_avg_card_time, uint16_t peer_id, uint8_t force_remm); +uint8_t gbox_get_crd_dist_lev(uint16_t crd_id); +#endif + +#endif diff --git a/module-gbox-helper.c b/module-gbox-helper.c new file mode 100644 index 0000000..bd7cc58 --- /dev/null +++ b/module-gbox-helper.c @@ -0,0 +1,283 @@ +#define MODULE_LOG_PREFIX "gbox" + +#include "globals.h" + +#ifdef MODULE_GBOX +#include "minilzo/minilzo.h" +#include "oscam-string.h" + + +uint16_t gbox_get_caid(uint32_t caprovid) +{ + if ((caprovid >> 24) == 0x05) + { return 0x0500; } + else + { return caprovid >> 16; } +} + +uint32_t gbox_get_provid(uint32_t caprovid) +{ + uint32_t provid = 0; + + switch(caprovid >> 24) + { + case 0x05: // ViXS + provid = caprovid & 0xFFFFFF; + break; + + case 0x0D: // Cryptoworx + provid = (caprovid >> 8) & 0xFF; + break; + + default: + provid = caprovid & 0xFFFF; + break; + } + return provid; +} + +uint32_t gbox_get_caprovid(uint16_t caid, uint32_t prid) +{ + uint32_t caprovid = 0; + + switch(caid >> 8) + { + case 0x05: // ViXS + caprovid = (caid >> 8) << 24 | (prid & 0xFFFFFF); + break; + + case 0x0D: // Cryptoworks + caprovid = (caid >> 8) << 24 | (caid & 0xFF) << 16 | ((prid << 8) & 0xFF00); + break; + + case 0x18: // Nagra + caprovid = (caid >> 8) << 24 | (caid & 0xFF) << 16; + break; + + default: + caprovid = (caid >> 8) << 24 | (caid & 0xFF) << 16 | (prid & 0xFFFF); + break; + } + return caprovid; +} + +static void gbox_convert_pw(uint8_t *password, uint32_t pw) +{ + int32_t i; + for(i = 3; i >= 0; i--) + { + password[3 - i] = (pw >> (8 * i)) & 0xff; + } +} + +uint32_t gbox_get_checksum(uint8_t *buf, uint16_t buflen) +{ + uint8_t checksum[4]; + int32_t counter; + + checksum[3] = buf[0]; + checksum[2] = buf[1]; + checksum[1] = buf[2]; + checksum[0] = buf[3]; + + for(counter = 1; counter < (buflen / 4) - 4; counter++) + { + checksum[3] ^= buf[counter * 4]; + checksum[2] ^= buf[counter * 4 + 1]; + checksum[1] ^= buf[counter * 4 + 2]; + checksum[0] ^= buf[counter * 4 + 3]; + } + + return checksum[3] << 24 | checksum[2] << 16 | checksum[1] << 8 | checksum[0]; +} + +//////////////////////////////////////////////////////////////////////////////// +// GBOX BUFFER ENCRYPTION/DECRYPTION (thanks to dvbcrypt@gmail.com) +//////////////////////////////////////////////////////////////////////////////// + +static uint8_t Lookup_Table[0x40] = +{ + 0x25, 0x38, 0xD4, 0xCD, 0x17, 0x7A, 0x5E, 0x6C, 0x52, 0x42, 0xFE, 0x68, 0xAB, 0x3F, 0xF7, 0xBE, + 0x47, 0x57, 0x71, 0xB0, 0x23, 0xC1, 0x26, 0x6C, 0x41, 0xCE, 0x94, 0x37, 0x45, 0x04, 0xA2, 0xEA, + 0x07, 0x58, 0x35, 0x55, 0x08, 0x2A, 0x0F, 0xE7, 0xAC, 0x76, 0xF0, 0xC1, 0xE6, 0x09, 0x10, 0xDD, + 0xC5, 0x8D, 0x2E, 0xD9, 0x03, 0x9C, 0x3D, 0x2C, 0x4D, 0x41, 0x0C, 0x5E, 0xDE, 0xE4, 0x90, 0xAE +}; + +static void gbox_encrypt8(uint8_t *buffer, uint8_t *pass) +{ + int passcounter; + int bufcounter; + uint8_t temp; + + for(passcounter = 0; passcounter < 4; passcounter++) + { + for(bufcounter = 7; bufcounter >= 0; bufcounter--) + { + temp = pass[3]; + pass[3] = (pass[3] / 2) + (pass[2] & 1) * 0x80; + pass[2] = (pass[2] / 2) + (pass[1] & 1) * 0x80; + pass[1] = (pass[1] / 2) + (pass[0] & 1) * 0x80; + pass[0] = (pass[0] / 2) + (temp & 1) * 0x80; + buffer[(bufcounter + 1) & 7] = buffer[(bufcounter + 1) & 7 ] - Lookup_Table[(buffer[bufcounter] >> 2) & 0x3F ]; + buffer[(bufcounter + 1) & 7] = Lookup_Table[(buffer[bufcounter] - pass[(bufcounter + 1) & 3]) & 0x3F ] ^ buffer[(bufcounter + 1) & 7 ]; + buffer[(bufcounter + 1) & 7] = buffer[(bufcounter + 1) & 7 ] - pass[(bufcounter & 3)]; + } + } +} + +static void gbox_decrypt8(uint8_t *buffer, uint8_t *pass) +{ + uint8_t temp; + int bufcounter; + int passcounter; + + for(passcounter = 3; passcounter >= 0; passcounter--) + { + for(bufcounter = 0; bufcounter <= 7; bufcounter++) + { + buffer[(bufcounter + 1) & 7] = pass[bufcounter & 3] + buffer[(bufcounter + 1) & 7]; + temp = buffer[bufcounter] - pass[(bufcounter + 1) & 3]; + buffer[(bufcounter + 1) & 7] = Lookup_Table[temp & 0x3F] ^ buffer[(bufcounter + 1) & 7]; + temp = buffer[bufcounter] >> 2; + buffer[(bufcounter + 1) & 7] = Lookup_Table[temp & 0x3F] + buffer[(bufcounter + 1) & 7]; + temp = pass[0] & 0x80; + pass[0] = ((pass[1] & 0x80) >> 7) + (pass[0] << 1); + pass[1] = ((pass[2] & 0x80) >> 7) + (pass[1] << 1); + pass[2] = ((pass[3] & 0x80) >> 7) + (pass[2] << 1); + pass[3] = (temp >> 7) + (pass[3] << 1); + } + } +} + +static void gbox_decryptB(uint8_t *buffer, int bufsize, uint8_t *localkey) +{ + int counter; + gbox_encrypt8(&buffer[bufsize - 9], localkey); + gbox_decrypt8(buffer, localkey); + for(counter = bufsize - 2; counter >= 0; counter--) + { buffer[counter] = buffer[counter + 1] ^ buffer[counter]; } +} + +static void gbox_encryptB(uint8_t *buffer, int bufsize, uint8_t *key) +{ + int counter; + for(counter = 0; counter < (bufsize - 1); counter++) + { buffer[counter] = buffer[counter + 1] ^ buffer[counter]; } + gbox_encrypt8(buffer, key); + gbox_decrypt8(&buffer[bufsize - 9], key); +} + +static void gbox_encryptA(uint8_t *buffer, uint8_t *pass) +{ + int counter; + uint8_t temp; + + for(counter = 0x1F; counter >= 0; counter--) + { + temp = pass[3] & 1; + pass[3] = ((pass[2] & 1) << 7) + (pass[3] >> 1); + pass[2] = ((pass[1] & 1) << 7) + (pass[2] >> 1); + pass[1] = ((pass[0] & 1) << 7) + (pass[1] >> 1); + pass[0] = (temp << 7) + (pass[0] >> 1); + temp = (pass[(counter + 1) & 3] ^ buffer[counter & 7]) >> 2; + buffer[(counter + 1) & 7] = Lookup_Table[temp & 0x3F] * 2 + buffer[(counter + 1) & 7 ]; + temp = buffer[counter & 7] - pass[(counter + 1) & 3]; + buffer[(counter + 1) & 7] = Lookup_Table[temp & 0x3F] ^ buffer[(counter + 1) & 7]; + buffer[(counter + 1) & 7] = pass[counter & 3] + buffer[(counter + 1) & 7]; + } +} + +static void gbox_decryptA(uint8_t *buffer, uint8_t *pass) +{ + int counter; + uint8_t temp; + + for(counter = 0; counter <= 0x1F; counter++) + { + buffer[(counter + 1) & 7] = buffer[(counter + 1) & 7] - pass[counter & 3]; + temp = buffer[counter & 7] - pass[(counter + 1) & 3]; + buffer[(counter + 1) & 7] = Lookup_Table[temp & 0x3F] ^ buffer[(counter + 1) & 7]; + temp = (pass[(counter + 1) & 3] ^ buffer[counter & 7]) >> 2; + buffer[(counter + 1) & 7] = buffer[(counter + 1) & 7] - Lookup_Table[temp & 0x3F] * 2; + temp = pass[0] & 0x80; + pass[0] = ((pass[1] & 0x80) >> 7) + (pass[0] << 1); + pass[1] = ((pass[2] & 0x80) >> 7) + (pass[1] << 1); + pass[2] = ((pass[3] & 0x80) >> 7) + (pass[2] << 1); + pass[3] = (temp >> 7) + (pass[3] << 1); + } +} + +void gbox_encrypt(uint8_t *buffer, int bufsize, uint32_t key) +{ + uint8_t pass[4]; + gbox_convert_pw(&pass[0], key); + gbox_encryptA(buffer, &pass[0]); + gbox_encryptB(buffer, bufsize, &pass[0]); +} + +void gbox_decrypt(uint8_t *buffer, int bufsize, uint32_t localkey) +{ + uint8_t pass[4]; + gbox_convert_pw(&pass[0], localkey); + gbox_decryptB(buffer, bufsize, &pass[0]); + gbox_decryptA(buffer, &pass[0]); +} + +void gbox_compress(uint8_t *buf, int32_t unpacked_len, int32_t *packed_len) +{ + uint8_t *tmp, *tmp2; + lzo_voidp wrkmem; + + if(!cs_malloc(&tmp, 0x40000)) + { + return; + } + + if(!cs_malloc(&tmp2, 0x40000)) + { + NULLFREE(tmp); + return; + } + + if(!cs_malloc(&wrkmem, unpacked_len * 0x1000)) + { + NULLFREE(tmp); + NULLFREE(tmp2); + return; + } + + unpacked_len -= 12; + memcpy(tmp2, buf + 12, unpacked_len); + lzo_init(); + lzo_uint pl = 0; + + if(lzo1x_1_compress(tmp2, unpacked_len, tmp, &pl, wrkmem) != LZO_E_OK) + { cs_log("compression failed!"); } + + memcpy(buf + 12, tmp, pl); + pl += 12; + NULLFREE(tmp); + NULLFREE(tmp2); + NULLFREE(wrkmem); + *packed_len = pl; +} + +void gbox_decompress(uint8_t *buf, int32_t *unpacked_len) +{ + uint8_t *tmp; + if(!cs_malloc(&tmp, 0x40000)) + { return; } + + int err; + int len = *unpacked_len - 12; + *unpacked_len = 0x40000; + lzo_init(); + + if((err = lzo1x_decompress_safe(buf + 12, len, tmp, (lzo_uint *)unpacked_len, NULL)) != LZO_E_OK) + { cs_log_dbg(D_READER, "gbox: decompression failed! errno=%d", err); } + + memcpy(buf + 12, tmp, *unpacked_len); + *unpacked_len += 12; + NULLFREE(tmp); +} +#endif diff --git a/module-gbox-helper.h b/module-gbox-helper.h new file mode 100644 index 0000000..b1e2ab8 --- /dev/null +++ b/module-gbox-helper.h @@ -0,0 +1,15 @@ +#ifndef MODULE_GBOX_HELPER_H_ +#define MODULE_GBOX_HELPER_H_ + +#ifdef MODULE_GBOX +uint16_t gbox_get_caid(uint32_t caprovid); +uint32_t gbox_get_provid(uint32_t caprovid); +uint32_t gbox_get_caprovid(uint16_t caid, uint32_t prid); +uint32_t gbox_get_checksum(uint8_t *ecm, uint16_t ecmlen); +void gbox_encrypt(uint8_t *buffer, int bufsize, uint32_t key); +void gbox_decrypt(uint8_t *buffer, int bufsize, uint32_t localkey); +void gbox_compress(uint8_t *buf, int32_t unpacked_len, int32_t *packed_len); +void gbox_decompress(uint8_t *buf, int32_t *unpacked_len); +#endif + +#endif diff --git a/module-gbox-remm.c b/module-gbox-remm.c new file mode 100644 index 0000000..21d4173 --- /dev/null +++ b/module-gbox-remm.c @@ -0,0 +1,469 @@ +#define MODULE_LOG_PREFIX "gbox/remm" + +#include "globals.h" + +#ifdef MODULE_GBOX +#include "module-gbox-remm.h" +#include "module-gbox.h" +#include "module-gbox-helper.h" +#include "oscam-string.h" +#include "oscam-client.h" +#include "oscam-lock.h" +#include "oscam-time.h" +#include "oscam-reader.h" +#include "oscam-files.h" +#include "module-dvbapi.h" +#include "oscam-emm.h" + +static void gbox_send_remm_ack_msg(struct s_client *cli, uint16_t caid, uint32_t provider, uint8_t dvbapi_stat, uint8_t ack) +{ + uint8_t outbuf[32]; + struct gbox_peer *peer = cli->gbox; + uint16_t local_gbox_id = gbox_get_local_gbox_id(); + uint32_t local_gbox_pw = gbox_get_local_gbox_password(); + + gbox_message_header(outbuf, MSG_REM_EMM, peer->gbox.password, local_gbox_pw); + outbuf[10] = MSGID_REMM_ACK; + i2b_buf(2, peer->gbox.id, outbuf +11); + i2b_buf(2, local_gbox_id, outbuf + 13); + outbuf[15] = ack; + outbuf[16] = dvbapi_stat; + i2b_buf(2, caid, outbuf +17); + i2b_buf(4, provider, outbuf +19); + outbuf[23] = 0; + outbuf[24] = 0; + outbuf[25] = 0; + outbuf[26] = 0; + + gbox_send(cli, outbuf, 27); + + if (ack == PEER_AU_BLOCKED) + { cs_log("<- send REJECT REMM msg to peer %04X for caid: %04X", peer->gbox.id, caid); } + + if (ack == PEER_AU_READY) + { cs_log("<- send ACCEPT REMM msg to peer %04X for caid: %04X", peer->gbox.id, caid); } + + if (ack == PEER_AU_UNREADY) + { cs_log("<- send WARNING to peer %04X: my dvbapi unready for AU caid: %04X", peer->gbox.id, caid); } + + return; +} + +static void gbox_recvd_remm_ack_msg(struct s_client *cli, uint8_t *buf, int32_t n) +{ + if (!cli || !cli->gbox || !buf || n != 27) { return; } + struct gbox_peer *peer; + peer = cli->gbox; + + uint8_t ack = buf[15]; + uint8_t dvbapi_stat = buf[16]; + uint16_t rpeer = b2i(2, buf +11); + uint16_t rcaid = b2i(2, buf +17); + //uint32_t rprovid = b2i(4, buf +19); + + if (ack == PEER_AU_BLOCKED) + { cs_log("-> Peer %04X %s rejected REMM for caid %04X - requesting peer %04X blocked", peer->gbox.id, cli->reader->label, rcaid, rpeer ); } + + if (ack == PEER_AU_READY) + { cs_log("-> MSG from peer %04X %s: Accept REMM REQ for caid %04X", peer->gbox.id, cli->reader->label, rcaid); } + + if (ack == PEER_AU_UNREADY) + { + cs_log("-> WARNING: Peer %04X %s dvbapi AU unready for caid %04X", peer->gbox.id, cli->reader->label, rcaid); + cs_log_dbg(D_EMM,"Peer %04X dvbapi AU status: dvbapi_au: %1d - dvbapi_usr_autoau: %1d - dvbapi_usr_aulist: %1d", + peer->gbox.id, (dvbapi_stat & 1) ? 1 : 0, (dvbapi_stat & 2) ? 1 : 0, (dvbapi_stat & 4) ? 1 : 0 ); + } +} + +static uint8_t check_dvbapi_au_ready( void) +{ +#ifdef HAVE_DVBAPI + uint8_t dvbapi_stat = 0; + if (module_dvbapi_enabled()) + { + if(cfg.dvbapi_au) + { dvbapi_stat |= 1; } + + struct s_client *cl; + cs_readlock(__func__, &clientlist_lock); + + for(cl = first_client; cl; cl = cl->next) + { + if(cl->typ == 'c' && cl->account && is_dvbapi_usr(cl->account->usr)) + { + if(cl->account->autoau) + { + dvbapi_stat |= 2; + break; + } + + if(ll_count(cl->account->aureader_list)) + { + dvbapi_stat |= 4; + break; + } + } + } + cs_readunlock(__func__, &clientlist_lock); + } + return dvbapi_stat; +#else + return 0; +#endif +} + +uint8_t check_valid_remm_peer(uint16_t peer_id) +{ + if (cfg.accept_remm_peer_num > 0) + { + int i; + for (i = 0; i < cfg.accept_remm_peer_num; i++) + { + if (cfg.accept_remm_peer[i] == peer_id) + { return 1; } + } + } + return 0; +} + +static void gbox_recvd_remm_req(struct s_client *cli, uint8_t *buf, int32_t n) +{ + if (!cli || !cli->gbox || !buf || !cli->reader || n != 122) { return; } + + struct gbox_peer *peer; + peer = cli->gbox; + + uint16_t rcaid = b2i(2, buf +23); + uint32_t rprovid = b2i(4, buf +17); + //uint16_t tcli_peer = b2i(2, buf +11); + //uint16_t tsrv_peer = b2i(2, buf +13); + + uint8_t dvbapi_stat = check_dvbapi_au_ready(); + + if (!check_valid_remm_peer( peer->gbox.id)) + { + gbox_send_remm_ack_msg(cli, rcaid, rprovid, dvbapi_stat, PEER_AU_BLOCKED); + handle_attack(cli, GBOX_ATTACK_REMM_REQ_BLOCKED, peer->gbox.id); + cs_log("Reject REMM REQ for caid %04X) - peer %04X blocked for AU", rcaid, peer->gbox.id); + return; + } + + //if (tcli_peer != local_gbox.id) + // { forward remm req to target client peer} + + struct s_reader *rdr = cli->reader; + rdr->gbox_remm_peer = peer->gbox.id; + rdr->last_g = time(NULL); // last receive is now + + rdr->auprovid = rprovid; + rdr->caid = rcaid; + + memcpy(rdr->hexserial, buf + 29, 6); + rdr->hexserial[6] = 0; + rdr->hexserial[7] = 0; + rdr->nprov = buf[37]; + + int32_t i; + for(i = 0; i < rdr->nprov; i++) + { + if(caid_is_betacrypt(rdr->caid) || caid_is_irdeto(rdr->caid)) + { + rdr->prid[i][0] = buf[38 + (i * 5)]; + memcpy(&rdr->prid[i][1], &buf[40 + (i * 5)], 3); + } + else + { + rdr->prid[i][2] = buf[38 + (i * 5)]; + rdr->prid[i][3] = buf[39 + (i * 5)]; + memcpy(&rdr->sa[i][0], &buf[40 + (i * 5)], 4); + } + } + + rdr->blockemm = 0; + rdr->blockemm |= (buf[117] == 1) ? 0 : 0x80; // remm marker bit + rdr->blockemm |= (buf[118] == 1) ? 0 : EMM_GLOBAL; + rdr->blockemm |= (buf[119] == 1) ? 0 : EMM_SHARED; + rdr->blockemm |= (buf[120] == 1) ? 0 : EMM_UNIQUE; + rdr->blockemm |= (buf[121] == 1) ? 0 : EMM_UNKNOWN; + + cs_log("-> received REMM REQ for type %s%s%s%s caid %04X from peer %04X:%s", + buf[120]==1 ? "UQ ":"", buf[119]==1 ? "SH ":"", buf[118]==1 ? "GL ":"", buf[121]==1 ? "UK":"", + rdr->caid, peer->gbox.id, rdr->label); + + if (dvbapi_stat == 3 || dvbapi_stat == 5) + { + gbox_send_remm_ack_msg(cli, rdr->caid, rdr->auprovid, dvbapi_stat, PEER_AU_READY); + cs_log_dbg(D_EMM,"my dvbapi ready for AU: dvbapi_au: %1d - dvbapi_usr_autoau: %1d - dvbapi_usr_aulist: %1d", + (dvbapi_stat & 1) ? 1 : 0, (dvbapi_stat & 2) ? 1 : 0, (dvbapi_stat & 4) ? 1 : 0 ); + } + else + { + gbox_send_remm_ack_msg(cli, rdr->caid, rdr->auprovid, dvbapi_stat, PEER_AU_UNREADY); + cs_log_dbg(D_EMM,"dvbapi status: dvbapi_au: %1d - dvbapi_usr_autoau: %1d - dvbapi_usr_aulist: %1d", + (dvbapi_stat & 1) ? 1 : 0, (dvbapi_stat & 2) ? 1 : 0, (dvbapi_stat & 4) ? 1 : 0 ); + } + write_msg_info(cli, MSGID_REMM, 0, 1); +} + +static void gbox_recvd_remm_data(struct s_client *cli, uint8_t *buf, int32_t buflen, int32_t emmlen) +{ + if(!cli || !cli->gbox || !buf || buflen < 30 || emmlen +27 > buflen || emmlen < 3 || emmlen + 27 > MAX_EMM_SIZE) + { return; } + + struct gbox_peer *peer; + peer = cli->gbox; + + uint16_t rcaid = b2i(2, buf + 15); + uint32_t recvd_remm_crc = b2i(4, buf + 23); + uint32_t calc_remm_crc = gbox_get_checksum(&buf[0] +27, emmlen); + cs_log_dbg(D_EMM,"received remm from peer: %04X caid: %04X (remm_crc = %08X - calc_remm_crc = %08X)", + peer->gbox.id, rcaid, recvd_remm_crc, calc_remm_crc); + + if(recvd_remm_crc == calc_remm_crc) + { + EMM_PACKET remm; + memset(&remm, 0, sizeof(remm)); + remm.emmlen = emmlen; + memcpy(remm.caid, buf +15, 2); + memcpy(remm.provid, buf +17 , 4); + memcpy(remm.emm, buf +27, remm.emmlen); + do_emm(cur_client(), &remm); + } + else + { + cs_log_dbg(D_EMM,"reject received REMM from peer %04X caid: %04X - crc failed - %08X != %08X", + peer->gbox.id, rcaid, recvd_remm_crc, calc_remm_crc); + } + + return; +} + +void gbox_recvd_remm_cmd_switch(struct s_client *cli, uint8_t *buf, int32_t n) +{ + if (!cli || !cli->gbox || !buf || n < 26) { return; } + + struct gbox_peer *peer; + peer = cli->gbox; + uint8_t cmd_id = buf[10]; + + switch(cmd_id) + { + case MSGID_REMM_REQ: + cs_log_dbg(D_EMM,"-> Incoming REMM request (%d bytes) from %04X %s - %s", + n, peer->gbox.id, username(cli), cli->reader->device); + gbox_recvd_remm_req(cli, buf, n); + break; + + case MSGID_REMM_DATA: + cs_log_dbg(D_EMM,"-> Incoming gbox remote EMM data (%d bytes total - %d bytes emm-len) from %04X %s - %s", + n, buf[21], peer->gbox.id, username(cli), cli->reader->device); + gbox_recvd_remm_data(cli, buf, n, buf[21]); // buf[21]) = emm length + break; + + case MSGID_REMM_ACK: + cs_log_dbg(D_EMM,"-> Incoming REMM ACK (%d bytes) from %04X %s - %s", + n, peer->gbox.id, username(cli), cli->reader->device); + gbox_recvd_remm_ack_msg(cli, buf, n); + break; + + default: + cs_log("received unknown remm cmd_id: %d %d bytes from %04X %s - %s", + cmd_id, n, peer->gbox.id, username(cli), cli->reader->device); + return; + } +} + +void gbox_send_remm_req(struct s_client *cli, ECM_REQUEST *er) +{ + if (!cli || !cli->gbox || !er) { return; } + int32_t i; + uint8_t mbuf[1024]; + struct s_client *cl = cur_client(); + struct gbox_peer *peer = cli->gbox; + struct s_reader *aureader = NULL, *rdr = NULL; + + if(er->selected_reader && !er->selected_reader->audisabled && ll_contains(cl->aureader_list, er->selected_reader)) + { aureader = er->selected_reader; } + + if(!aureader && cl->aureader_list) + { + LL_ITER itr = ll_iter_create(cl->aureader_list); + while((rdr = ll_iter_next(&itr))) + { + if(emm_reader_match(rdr, er->caid, er->prid)) + { + aureader = rdr; + break; + } + } + } + + if(!aureader) + { return; } + + uint16_t au_caid = aureader->caid; + + if(!au_caid && caid_is_bulcrypt(er->caid)) // Bulcrypt has 2 caids and aureader->caid can't be used. Use ECM_REQUEST caid for AU. + { au_caid = er->caid; } + + if(cl->lastcaid != er->caid) + { cl->disable_counter = 0; } + + cl->lastcaid = er->caid; + cl->disable_counter++; + + if (!cli->reader->gbox_force_remm && cl->disable_counter < 6) // delay 6 ecm + { return; } + + if(!memcmp(cl->lastserial, aureader->hexserial, 8)) + { + cl->disable_counter = 0; + return; + } + + memcpy(cl->lastserial, aureader->hexserial, 8); + + if(au_caid) + { cl->disable_counter = 0; } + else + { return; } + + uint8_t total_ent = 0; + uint8_t active_ent = 0; + + if(aureader->ll_entitlements) // check for active entitlements + { + time_t now = time((time_t *)0); + LL_ITER itr = ll_iter_create(aureader->ll_entitlements); + S_ENTITLEMENT *ent; + + while((ent = ll_iter_next(&itr))) + { + total_ent++; + if((ent->end > now) && (ent->type != 7)) + { + active_ent++; + } + } + //cs_log("AU card %s: Total entitlements: %d - active entitlements: %d", aureader->label, total_ent, active_ent); + } + + if(total_ent && cli->reader->gbox_force_remm) + { + if(active_ent >= cli->reader->gbox_force_remm) + { + cs_log("WARNING: Card '%s' got %d active entitlements - consider to disable 'force_remm'", aureader->label, active_ent); + } + } + + memset(mbuf, 0, sizeof(mbuf)); + + uint16_t local_gbox_id = gbox_get_local_gbox_id(); + uint32_t local_gbox_pw = gbox_get_local_gbox_password(); + + gbox_message_header(mbuf, MSG_REM_EMM, peer->gbox.password, local_gbox_pw); + mbuf[10] = MSGID_REMM_REQ; + i2b_buf(2, peer->gbox.id, mbuf + 11); + i2b_buf(2, local_gbox_id, mbuf + 13); + i2b_buf(2, er->srvid, mbuf + 15); + + // override emm provid with auprovid if set in server reader config + if(aureader->auprovid) + { + if(aureader->auprovid != er->prid) + { i2b_buf(4, aureader->auprovid, mbuf +17); } + else + { i2b_buf(4, er->prid, mbuf +17); } + } + else + { + i2b_buf(4, er->prid, mbuf +17); + } + + i2b_buf(2, er->pid, mbuf +21); + i2b_buf(2, au_caid, mbuf +23); + + memcpy(mbuf +29, aureader->hexserial, 6); // serial 6 bytes + mbuf[37] = aureader->nprov; + + for(i = 0; i < aureader->nprov; i++) + { + if(caid_is_betacrypt(au_caid) || caid_is_irdeto(au_caid)) + { + mbuf[38 + (i * 5)] = aureader->prid[i][0]; + memcpy(&mbuf[40 + (i * 5)], &aureader->prid[i][1], 3); + } + else + { + mbuf[38 + (i * 5)] = aureader->prid[i][2]; + mbuf[39 + (i * 5)] = aureader->prid[i][3]; + memcpy(&mbuf[40 + (i * 5)], &aureader->sa[i][0], 4); // for conax we need at least 4 Bytes + } + if(i >= 15) { break; } + } + + mbuf[117] = aureader->blockemm | 0x80; // set remm marker bit + + if(au_caid == 0x0D96 || au_caid == 0x0D98 ) // these caids needs globals + { mbuf[118] = (aureader->blockemm & EMM_GLOBAL && !(aureader->saveemm & EMM_GLOBAL)) ? 0 : 1; } + else + { mbuf[118] = 0; } + + mbuf[119] = (aureader->blockemm & EMM_SHARED && !(aureader->saveemm & EMM_SHARED)) ? 0 : 1; + mbuf[120] = (aureader->blockemm & EMM_UNIQUE && !(aureader->saveemm & EMM_UNIQUE)) ? 0 : 1; + mbuf[121] = (aureader->blockemm & EMM_UNKNOWN && !(aureader->saveemm & EMM_UNKNOWN)) ? 0 : 1; + + cs_log("<- %04X sends REMM REQ for type = %s%s%s%s to %s peer-id=%04X for reader=%s, caid=%04X", local_gbox_id, + mbuf[120] == 1 ? "UQ " : "", mbuf[119] == 1 ? "SH " : "", mbuf[118] == 1 ? "GL " : "", mbuf[121] == 1 ? "UK" : "", + username(cur_client()), peer->gbox.id, aureader->label, au_caid ); + + cs_log_dump_dbg(D_EMM, mbuf, 122, "<- send remm request, (data_len=%d):", 122); + gbox_send(cli, mbuf, 122); + return; +} + +int32_t gbox_send_remm_data(EMM_PACKET *ep) +{ + struct s_client *cli = cur_client(); + struct gbox_peer *peer = cli->gbox; + + if(!cli->gbox || !cli->reader->tcp_connected || !ep || !cli->reader->gbox_remm_peer) + { return 0; } + + uint32_t remm_crc = gbox_get_checksum(&ep->emm[0], ep->emmlen); + + if(remm_crc == peer->last_remm_crc) + { return 0; } + + peer->last_remm_crc = remm_crc; + + uint8_t *buf; + + if(!cs_malloc(&buf, ep->emmlen +27 +15)) + { return -1; } + + memset(buf, 0, 26); + memset(buf +27, 0xff, ep->emmlen + 15); + + uint16_t local_gbox_id = gbox_get_local_gbox_id(); + uint32_t local_gbox_pw = gbox_get_local_gbox_password(); + + gbox_message_header(buf, MSG_REM_EMM, peer->gbox.password, local_gbox_pw); + buf[10] = MSGID_REMM_DATA; + i2b_buf(2, peer->gbox.id, buf +11); + i2b_buf(2, local_gbox_id, buf +13); + memcpy(buf +15, ep->caid, 2); + memcpy(buf +17, ep->provid, 4); + buf[21] = ep->emmlen; + i2b_buf(4, remm_crc, buf +23); + memcpy(buf +27, ep->emm, ep->emmlen); + cs_log("<- send remm to: %s peer: %04X emmlength: %d crc: %08X", + username(cur_client()), peer->gbox.id, ep->emmlen, remm_crc); + cs_log_dump_dbg(D_EMM, buf, 27 + ep->emmlen, "<- gbox send emm, (data-len=%d):", 27 + ep->emmlen); + gbox_send(cli, buf, 27 + ep->emmlen); + + NULLFREE(buf); + return 1; +} +#endif + diff --git a/module-gbox-remm.h b/module-gbox-remm.h new file mode 100644 index 0000000..ab5ea40 --- /dev/null +++ b/module-gbox-remm.h @@ -0,0 +1,23 @@ +#ifndef MODULE_GBOX_REMM_H_ +#define MODULE_GBOX_REMM_H_ + +#ifdef MODULE_GBOX + +#define MSG_REM_EMM 0x49BF + +#define MSGID_REMM_REQ 1 +#define MSGID_REMM_DATA 2 +#define MSGID_REMM_ACK 3 + +#define PEER_AU_BLOCKED 1 +#define PEER_AU_READY 2 +#define PEER_AU_UNREADY 3 + +void gbox_send_remm_req(struct s_client *cli, ECM_REQUEST *er); +void gbox_recvd_remm_cmd_switch(struct s_client *cli, uint8_t *buf, int32_t n); +int32_t gbox_send_remm_data(EMM_PACKET *ep); +uint8_t check_valid_remm_peer(uint16_t peer_id); + +#endif + +#endif diff --git a/module-gbox-sms.c b/module-gbox-sms.c new file mode 100644 index 0000000..0a0fbee --- /dev/null +++ b/module-gbox-sms.c @@ -0,0 +1,478 @@ +#define MODULE_LOG_PREFIX "gbox/sms" + +#include "globals.h" + +#ifdef MODULE_GBOX +#include "module-gbox.h" +#include "module-gbox-sms.h" +#include "oscam-string.h" +#include "oscam-files.h" +#include "oscam-string.h" +#include "oscam-client.h" +#include "oscam-time.h" +#include "oscam-lock.h" + +static int32_t poll_gsms_data(uint16_t *boxid, uint8_t *num, char *text) +{ + char *fext= FILE_GSMS_TXT; + char *fname = get_gbox_tmp_fname(fext); + + FILE *fhandle = fopen(fname, "r"); + if(!fhandle) + { + //cs_log("Couldn't open %s: %s", fname, strerror(errno)); + return -2; + } + + uint32_t length1; + uint8_t length; + char buffer[140]; + char *tail; + + memset(buffer, 0, sizeof(buffer)); + fseek(fhandle, 0, SEEK_END); + length1 = ftell(fhandle); + fseek(fhandle, 0, SEEK_SET); + + if(length1 < 13) + { + cs_log("GSMS failed. Min msg char in %s = 6, actual = %d", fname, length1 - 7); + fclose(fhandle); + unlink(fname); + return -1; + } + + if(fgets(buffer, 140, fhandle) != NULL) + { + *boxid = strtol(buffer, &tail, 16); + *num = atoi(tail); + } + + fclose(fhandle); + unlink(fname); + + if(length1 > 127 + 7) + { + length = 127 + 7; + } + else + { + length = length1; + } + + cs_log_dbg(D_READER, "total msg length taken from %s = %d, limited to %d", fname, length1, length); + cs_strncpy(text, buffer + 7, sizeof(buffer)); + + return 0; +} + +static void write_gsms_to_osd_file(struct s_client *cli, uint8_t *gsms) +{ + char *fext= FILE_OSD_MSG; + char *fname = get_gbox_tmp_fname(fext); + + if(file_exists(fname)) + { + char gsms_buf[150]; + uint8_t i; + + // allow only alphanumerical characters in osd gsms due to safety reasons + for(i = 0; i < cs_strlen((char *)gsms); i++) + { + if(!isalnum(gsms[i]) && gsms[i] != ' ') + { + gsms[i] = '_'; + } + } + + memset(gsms_buf, 0, sizeof(gsms_buf)); + snprintf(gsms_buf, sizeof(gsms_buf), "%s %s:%s %s", fname, username(cli), cli->reader->device, gsms); + cs_log_dbg(D_READER, "found OSD 'driver' %s - write gsms to OSD", fname); + + char *cmd = gsms_buf; + FILE *p; + if((p = popen(cmd, "w")) == NULL) + { + cs_log("Error %s", fname); + return; + } + + pclose(p); + } + + return; +} + +void write_gsms_ack(struct s_client *cli) +{ + char tsbuf[28]; + time_t walltime = cs_time(); + cs_ctime_r(&walltime, tsbuf); + struct gbox_peer *peer = cli->gbox; + char *fext = FILE_GSMS_ACK; + char *fname = get_gbox_tmp_fname(fext); + + FILE *fhandle = fopen(fname, "a+"); + if(!fhandle) + { + cs_log("Couldn't open %s: %s", fname, strerror(errno)); + return; + } + + fprintf(fhandle, "Peer %04X (%s) confirmed receipt of GSMS on %s", peer->gbox.id, cli->reader->device, tsbuf); + fclose(fhandle); + return; +} + +static void write_gsms_nack(struct s_client *cl, uint8_t inf) +{ + char tsbuf[28]; + time_t walltime = cs_time(); + cs_ctime_r(&walltime, tsbuf); + struct gbox_peer *peer = cl->gbox; + char *fext = FILE_GSMS_NACK; + char *fname = get_gbox_tmp_fname(fext); + + FILE *fhandle = fopen(fname, "a+"); + if(!fhandle) + { + cs_log("Couldn't open %s: %s", fname, strerror(errno)); + return; + } + + if(inf) + { + fprintf(fhandle, "INFO: GSMS to all: Peer %04X (%s) was OFFLINE %s", + peer->gbox.id, cl->reader->device,tsbuf); + } + else + { + fprintf(fhandle, "WARNING: Private GSMS to Peer %04X (%s) failed - was OFFLINE %s", + peer->gbox.id, cl->reader->device,tsbuf); + } + + fclose(fhandle); + return; +} + +void write_gsms_msg(struct s_client *cli, uint8_t *gsms, uint16_t type, uint16_t UNUSED(msglen)) +{ + char tsbuf[28]; + time_t walltime = cs_time(); + cs_ctime_r(&walltime, tsbuf); + struct gbox_peer *peer = cli->gbox; + struct s_reader *rdr = cli->reader; + char *fext = FILE_GSMS_MSG; + char *fname = get_gbox_tmp_fname(fext); + + FILE *fhandle = fopen(fname, "a+"); + if(!fhandle) + { + cs_log("Couldn't open %s: %s", fname, strerror(errno)); + return; + } + + if(type == 0x30) + { + fprintf(fhandle, "Normal message received from %04X %s on %s%s\n\n", peer->gbox.id, cli->reader->device, tsbuf, gsms); + rdr->gbox_gsms_peer = peer->gbox.id; + snprintf(rdr->last_gsms, sizeof(rdr->last_gsms), "%s %s", gsms, tsbuf); // for easy handling of gsms by webif + } + else if(type == 0x31) + { + fprintf(fhandle, "OSD message received from %04X %s on %s%s\n\n", peer->gbox.id, cli->reader->device, tsbuf, gsms); + write_gsms_to_osd_file(cli, gsms); + rdr->gbox_gsms_peer = peer->gbox.id; + snprintf(rdr->last_gsms, sizeof(rdr->last_gsms), "%s %s", gsms, tsbuf); // for easy handling of gsms by webif + } + else + { + fprintf(fhandle, "Corrupted message received from %04X %s on %s%s\n\n", peer->gbox.id, cli->reader->device, tsbuf, gsms); + } + + fclose(fhandle); + return; +} + +void gsms_unavail(void) +{ + cs_log("INFO: GSMS feature disabled by conf"); +} + +static void gbox_send_gsms2peer(struct s_client *cl, char *gsms, uint8_t msg_type, int8_t gsms_len) +{ + uint8_t outbuf[150]; + struct gbox_peer *peer = cl->gbox; + uint16_t local_gbox_id = gbox_get_local_gbox_id(); + uint32_t local_gbox_pw = gbox_get_local_gbox_password(); + struct s_reader *rdr = cl->reader; + + gbox_message_header(outbuf, MSG_GSMS, peer->gbox.password, local_gbox_pw); + + outbuf[10] = (peer->gbox.id >> 8) & 0xff; + outbuf[11] = peer->gbox.id & 0xff; + outbuf[12] = (local_gbox_id >> 8) & 0xff; + outbuf[13] = local_gbox_id & 0xff; + outbuf[14] = msg_type; + outbuf[15] = gsms_len; + memcpy(outbuf + 16, gsms, gsms_len); + outbuf[16 + gsms_len] = 0; + + cs_log("<-[gbx] send GSMS to %s:%d id: %04X", rdr->device, rdr->r_port, peer->gbox.id); + gbox_send(cl, outbuf, gsms_len + 17); + return; +} +int gbox_direct_send_gsms(uint16_t boxid, uint8_t num, char *gsms) +{ + uint8_t msg_type = 0, gsms_len = 0; + int peer_found = 0; + char text[GBOX_MAX_MSG_TXT + 1]; + + memset(text, 0, sizeof(text)); + + if(cfg.gsms_dis) + { + gsms_unavail(); + return 0; + } + + gsms_len = cs_strlen(gsms); + if(gsms_len < 6) + { + cs_log("GBOX: message to send to peer is too short 6 chars expected and %d received text[%s]", gsms_len, gsms); + } + else if(gsms_len > GBOX_MAX_MSG_TXT) + { + gsms_len = GBOX_MAX_MSG_TXT; + cs_log("GBOX message is too long so it will be truncated to max. [%d]", GBOX_MAX_MSG_TXT); + } + + cs_strncpy(text, gsms, sizeof(text)); + + switch(num) + { + case 0: + msg_type = 0x30; + break; + + case 1: + msg_type = 0x31; + break; + + //case 2: + // gsms_prot = 2; + // msg_type = 0x30; + // break; + + //case 3: + // gsms_prot = 2; + // msg_type = 0x31; + // break; + + default: + cs_log("ERROR unknown gsms protocol"); + return 0; + } + + cs_log_dbg(D_READER,"init gsms_length=%d msg_type=%02X ", gsms_len, msg_type); + struct s_client *cl; + cs_readlock(__func__, &clientlist_lock); + + for(cl = first_client; cl; cl = cl->next) + { + peer_found=0; + if(cl->gbox && cl->typ == 'p') + { + struct gbox_peer *peer = cl->gbox; + if(peer->online && boxid == 0xFFFF) // send gsms to all peers online + { + gbox_send_gsms2peer(cl, text, msg_type, gsms_len); + peer_found=1; + } + + if(!peer->online && boxid == 0xFFFF) + { + cs_log("GBOX Info: peer %04X is OFFLINE", peer->gbox.id); + write_gsms_nack( cl, 1); + } + + if(peer->online && boxid == peer->gbox.id) + { + gbox_send_gsms2peer(cl, text, msg_type, gsms_len); + peer_found=1; + } + + if(!peer->online && boxid == peer->gbox.id) + { + cs_log("GBOX WARNING: send GSMS failed - peer %04X is OFFLINE", peer->gbox.id); + write_gsms_nack( cl, 0); + } + } + } + + cs_readunlock(__func__, &clientlist_lock); + return peer_found; +} + +void gbox_get_online_peers(void) +{ + int n = 0, i; + struct s_client *cl; + + for(i = 0; i < GBOX_MAX_DEST_PEERS; i++) + { + cfg.gbox_dest_peers[i] = '\0'; + } + + cfg.gbox_dest_peers_num = 0; + cs_readlock(__func__, &clientlist_lock); + + for(cl = first_client; cl; cl = cl->next) + { + if(cl->gbox && cl->typ == 'p' && n < GBOX_MAX_DEST_PEERS) + { + struct gbox_peer *peer = cl->gbox; + if(peer->online) // peer is online + { + cfg.gbox_dest_peers[n++] = peer->gbox.id; + } + } + } + + cs_readunlock(__func__, &clientlist_lock); + cfg.gbox_dest_peers_num = n; + return; +} + +void gbox_init_send_gsms(void) +{ + uint16_t boxid = 0; + uint8_t num = 0; + uint8_t msg_type = 0; + int32_t poll_result = 0; + char text[150]; + memset(text, 0, sizeof(text)); + char *fext = FILE_GSMS_TXT; + char *fname = get_gbox_tmp_fname(fext); + + if(cfg.gsms_dis) + { + unlink(fname); + gsms_unavail(); + return; + } + + poll_result = poll_gsms_data( &boxid, &num, text); + if(poll_result) + { + if(poll_result != -2) + { + cs_log("ERROR polling file %s", fname); + } + + return; + } + + int8_t gsms_len = cs_strlen(text); + cs_log_dbg(D_READER,"got from %s: box_ID = %04X num = %d gsms_length = %d txt = %s", fname, boxid, num, gsms_len, text); + + switch(num) + { + case 0: + msg_type = 0x30; + break; + + case 1: + msg_type = 0x31; + break; + + //case 2: + // gsms_prot = 2; + // msg_type = 0x30; + // break; + + //case 3: + // gsms_prot = 2; + // msg_type = 0x31; + // break; + + default: + cs_log("ERROR unknown gsms protocol"); + return; + } + + cs_log_dbg(D_READER,"init gsms to boxid= %04X length= %d msg_type= %02X ", boxid, gsms_len, msg_type); + + uint8_t id_valid = 0; + struct s_client *cl; + cs_readlock(__func__, &clientlist_lock); + + for(cl = first_client; cl; cl = cl->next) + { + if(cl->gbox && cl->typ == 'p') + { + struct gbox_peer *peer = cl->gbox; + + if(peer->online && boxid == 0xFFFF) // send gsms to all peers online + { + gbox_send_gsms2peer(cl, text, msg_type, gsms_len); + id_valid = 1; + } + + if(!peer->online && boxid == 0xFFFF) + { + cs_log("Info: peer %04X is OFFLINE", peer->gbox.id); + write_gsms_nack( cl, 1); + id_valid = 1; + } + + if(peer->online && boxid == peer->gbox.id) + { + gbox_send_gsms2peer(cl, text, msg_type, gsms_len); + id_valid = 1; + } + + if(!peer->online && boxid == peer->gbox.id) + { + cs_log("WARNING: send GSMS failed - peer %04X is OFFLINE", peer->gbox.id); + write_gsms_nack( cl, 0); + id_valid = 1; + } + } + } + + cs_readunlock(__func__, &clientlist_lock); + + if(!id_valid) + { + cs_log("WARNING: send GSMS failed - peer_id unknown"); + } + + return; +} + +void gbox_send_gsms_ack(struct s_client *cli) +{ + uint8_t outbuf[20]; + struct gbox_peer *peer = cli->gbox; + uint16_t local_gbox_id = gbox_get_local_gbox_id(); + uint32_t local_gbox_pw = gbox_get_local_gbox_password(); + struct s_reader *rdr = cli->reader; + + if(peer->online) + { + gbox_message_header(outbuf, MSG_GSMS_ACK, peer->gbox.password, local_gbox_pw); + + outbuf[10] = 0; + outbuf[11] = 0; + outbuf[12] = (local_gbox_id >> 8) & 0xff; + outbuf[13] = local_gbox_id & 0xff; + outbuf[14] = 0x1; + outbuf[15] = 0; + + cs_log_dbg(D_READER,"<-[gbx] send GSMS_ACK to %s:%d id: %04X", rdr->device, rdr->r_port, peer->gbox.id); + gbox_send(cli, outbuf, 16); + } +} + +#endif diff --git a/module-gbox-sms.h b/module-gbox-sms.h new file mode 100644 index 0000000..ab03cb2 --- /dev/null +++ b/module-gbox-sms.h @@ -0,0 +1,22 @@ +#ifndef MODULE_GBOX_SMS_H_ +#define MODULE_GBOX_SMS_H_ + +#ifdef MODULE_GBOX + +#define FILE_GSMS_TXT "gsms.txt" +#define FILE_GSMS_MSG "gsms.log" +#define FILE_OSD_MSG "gsms.osd" +#define FILE_GSMS_ACK "gsms.ack" +#define FILE_GSMS_NACK "gsms.nack" + +void gbox_init_send_gsms(void); +void write_gsms_msg(struct s_client *cli, uint8_t *gsms, uint16_t type, uint16_t UNUSED(msglen)); +void gbox_send_gsms_ack(struct s_client *cli); +int gbox_direct_send_gsms(uint16_t boxid, uint8_t num, char *gsms); +void gbox_get_online_peers(void); +void write_gsms_ack(struct s_client *cli); +void gsms_unavail(void); + +#endif + +#endif diff --git a/module-gbox.c b/module-gbox.c new file mode 100644 index 0000000..68ee065 --- /dev/null +++ b/module-gbox.c @@ -0,0 +1,2767 @@ +#define MODULE_LOG_PREFIX "gbox" + +#include "globals.h" +#ifdef MODULE_GBOX + +#include "module-gbox.h" +#include "module-gbox-helper.h" +#include "module-gbox-sms.h" +#include "module-gbox-cards.h" +#include "module-cccam.h" +#include "module-cccam-data.h" +#include "oscam-failban.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-chk.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-reader.h" +#include "oscam-files.h" +#include "module-gbox-remm.h" +#include "module-dvbapi.h" +#include "oscam-work.h" + +static struct gbox_data local_gbox; +static int8_t local_gbox_initialized = 0; +static uint8_t local_cards_initialized = 0; +uint8_t local_gbx_rev = 0x30; +uint32_t startup = 0; + +static uint32_t gbox_add_local_cards(void); +static int32_t gbox_send_ecm(struct s_client *cli, ECM_REQUEST *er); +void start_gbx_ticker(void); + +char *get_gbox_tmp_fname(char *fext) +{ + static char gbox_tmpfile_buf[128]; + memset(gbox_tmpfile_buf, 0, sizeof(gbox_tmpfile_buf)); + const char *slash = "/"; + + if(!cfg.gbox_tmp_dir) + { + snprintf(gbox_tmpfile_buf, sizeof(gbox_tmpfile_buf), "%s%s%s",get_tmp_dir(), slash, fext); + } + else + { + if(cfg.gbox_tmp_dir[cs_strlen(cfg.gbox_tmp_dir) - 1] == '/') { slash = ""; } + snprintf(gbox_tmpfile_buf, sizeof(gbox_tmpfile_buf), "%s%s%s", cfg.gbox_tmp_dir, slash, fext); + } + return gbox_tmpfile_buf; +} + +uint16_t gbox_get_local_gbox_id(void) +{ + return local_gbox.id; +} + +uint32_t gbox_get_local_gbox_password(void) +{ + return local_gbox.password; +} + +static uint8_t gbox_get_my_cpu_api (void) +{ + return(cfg.gbox_my_cpu_api); +} + +static void write_attack_file (struct s_client *cli, uint8_t txt_id, uint16_t rcvd_id) +{ + if (cfg.dis_attack_txt) {return;} + char tsbuf[28]; + time_t walltime = cs_time(); + cs_ctime_r(&walltime, tsbuf); + char *fext= FILE_ATTACK_INFO; + char *fname = get_gbox_tmp_fname(fext); + FILE *fhandle = fopen(fname, "a"); + + if(!fhandle) + { + cs_log("Couldn't open %s: %s", fname, strerror(errno)); + return; + } + + if(txt_id == GBOX_ATTACK_UNKWN_HDR) + { + fprintf(fhandle, "ATTACK ALERT FROM %04X %s - peer sends unknown Header CMD - %s", + rcvd_id, cs_inet_ntoa(cli->ip), tsbuf); + } + + if(txt_id == GBOX_ATTACK_LOCAL_PW) + { + fprintf(fhandle, "ATTACK ALERT FROM %04X %s - peer sends wrong local password - %s", + rcvd_id, cs_inet_ntoa(cli->ip), tsbuf); + } + + if(txt_id == GBOX_ATTACK_PEER_IGNORE) + { + fprintf(fhandle, "ATTACK ALERT FROM %04X %s - peer ignored by conf - %s", + rcvd_id, cs_inet_ntoa(cli->ip), tsbuf); + } + + if(txt_id == GBOX_ATTACK_PEER_PW) + { + fprintf(fhandle, "ATTACK ALERT FROM %04X %s - peer sends unknown peer password - %s", + rcvd_id, cs_inet_ntoa(cli->ip), tsbuf); + } + + if(txt_id == GBOX_ATTACK_AUTH_FAIL) + { + fprintf(fhandle, "ATTACK ALERT FROM %04X %s - authentification failed - %s", + rcvd_id, cs_inet_ntoa(cli->ip), tsbuf); + } + + if(txt_id == GBOX_ATTACK_ECM_BLOCKED) + { + fprintf(fhandle, "ATTACK ALERT FROM %04X %s - ECM is blocked - %s", + rcvd_id, cs_inet_ntoa(cli->ip), tsbuf); + } + + if(txt_id == GBOX_ATTACK_REMM_REQ_BLOCKED) + { + fprintf(fhandle, "ATTACK ALERT FROM %04X %s - unaccepted peer sent REMM REQ - %s", + rcvd_id, cs_inet_ntoa(cli->ip), tsbuf); + } + + fclose(fhandle); + return; +} + +void write_msg_info(struct s_client *cli, uint8_t msg_id, uint8_t txt_id, uint16_t misc) +{ + if (msg_id == MSGID_GSMS && misc == 0x31) {return;} + char *fext= FILE_MSG_INFO; + char *fname = get_gbox_tmp_fname(fext); + + if (file_exists(fname)) + { + char buf[120]; + memset(buf, 0, sizeof(buf)); + + if (msg_id == MSGID_ATTACK) + { + snprintf(buf, sizeof(buf), "%s %d %04X %d %s %d", + fname, msg_id, misc, 0, cs_inet_ntoa(cli->ip), txt_id); + + cs_log_dbg(D_READER, "found driver %s - write msg (msg_id = %d - txt-id = %d) Attack Alert from %s %04X", + fname, msg_id, txt_id, cs_inet_ntoa(cli->ip), misc); + } + else + { + snprintf(buf, sizeof(buf), "%.24s %d %.24s %.24s %s %d", + fname, msg_id, username(cli), cli->reader->device, cs_inet_ntoa(cli->ip), misc); + + cs_log_dbg(D_READER, "found driver %s - write msg (id = %d) related to %s %s", + fname, msg_id, username(cli),cli->reader->device); + } + + char *cmd = buf; + FILE *p; + if((p = popen(cmd, "w")) == NULL) + { + cs_log("Error popen: %s",fname); + return; + } + if(pclose(p) == -1) + { + cs_log("Error pclose(): %s",fname); + return; + } + } + return; +} + +void handle_attack(struct s_client *cli, uint8_t txt_id, uint16_t rcvd_id) +{ + write_attack_file(cli, txt_id, rcvd_id); + write_msg_info(cli, MSGID_ATTACK, txt_id, rcvd_id); + return; +} + +void gbox_write_peer_onl(void) +{ + char *fext = FILE_GBOX_PEER_ONL; + char *fname = get_gbox_tmp_fname(fext); + FILE *fhandle = fopen(fname, "w"); + if(!fhandle) + { + cs_log("Couldn't open %s: %s", fname, strerror(errno)); + return; + } + + cs_readlock(__func__, &clientlist_lock); + + struct s_client *cl; + for(cl = first_client; cl; cl = cl->next) + { + if(cl->gbox && cl->typ == 'p') + { + struct gbox_peer *peer = cl->gbox; + if (peer->online) + { + fprintf(fhandle, "1 %s %s %04X 2.%02X %s\n", cl->reader->device, + cs_inet_ntoa(cl->ip), peer->gbox.id, peer->gbox.minor_version, cl->reader->description ? cl->reader->description : ""); + + if (!peer->onlinestat) + { + peer->onlinestat = 1; + cs_log("comeONLINE: %s %s boxid: %04X (%s) v2.%02X cards:%d", cl->reader->device, + cs_inet_ntoa(cl->ip), peer->gbox.id, cl->reader->description ? cl->reader->description : "-", peer->gbox.minor_version, peer->filtered_cards); + write_msg_info(cl, MSGID_COMEONLINE, 0, peer->filtered_cards); + } + } + else + { + fprintf(fhandle, "0 %s %s %04X 0.00 %s\n", cl->reader->device, cs_inet_ntoa(cl->ip),peer->gbox.id, cl->reader->description ? cl->reader->description : ""); + if (peer->onlinestat) + { + peer->onlinestat = 0; + cs_log("goneOFFLINE: %s %s boxid: %04X (%s)",cl->reader->device, cs_inet_ntoa(cl->ip),peer->gbox.id, cl->reader->description ? cl->reader->description : "-"); + write_msg_info(cl, MSGID_GONEOFFLINE, 0, 0); + } + } + } + } + cs_readunlock(__func__, &clientlist_lock); + fclose(fhandle); + return; +} + +void gbox_write_version(void) +{ + char *fext = FILE_GBOX_VERSION; + char *fname = get_gbox_tmp_fname(fext); + FILE *fhandle = fopen(fname, "w"); + if(!fhandle) + { + cs_log("Couldn't open %s: %s", get_gbox_tmp_fname(FILE_GBOX_VERSION), strerror(errno)); + return; + } + fprintf(fhandle, "%02X.%02X my-id: %04X rev: %01X.%01X\n", LOCAL_GBOX_MAJOR_VERSION, cfg.gbox_my_vers, local_gbox.id, local_gbx_rev >> 4, local_gbx_rev & 0xf); + fclose(fhandle); +} + +void hostname2ip(char *hostname, IN_ADDR_T *ip) +{ + cs_resolve(hostname, ip, NULL, NULL); +} + +uint16_t gbox_convert_password_to_id(uint32_t password) +{ + return (((password >> 24) & 0xff) ^ ((password >> 8) & 0xff)) << 8 | (((password >> 16) & 0xff) ^ (password & 0xff)); +} + +static int8_t gbox_remove_all_bad_sids(ECM_REQUEST *er, uint16_t sid) +{ + if (!er) + { + return -1; + } + + struct gbox_card_pending *pending = NULL; + LL_LOCKITER *li = ll_li_create(er->gbox_cards_pending, 0); + + while ((pending = ll_li_next(li))) + { + gbox_remove_bad_sid(pending->id.peer, pending->id.slot, sid); + } + ll_li_destroy(li); + return 0; +} + +void gbox_free_cards_pending(ECM_REQUEST *er) +{ + ll_destroy_free_data(&er->gbox_cards_pending); +} + +void gbox_init_ecm_request_ext(struct gbox_ecm_request_ext *ere) +{ + ere->gbox_slot = 0; + ere->gbox_version = 0; + ere->gbox_rev = 0; + ere->gbox_type = 0; +} + +struct s_client *get_gbox_proxy(uint16_t gbox_id) +{ + struct s_client *cl; + struct s_client *found = NULL; + cs_readlock(__func__, &clientlist_lock); + + for(cl = first_client; cl; cl = cl->next) + { + if(cl->typ == 'p' && cl->gbox && cl->gbox_peer_id == gbox_id) + { + found = cl; + break; + } + } + cs_readunlock(__func__, &clientlist_lock); + return found; +} + +void remove_peer_crd_file(struct s_client *proxy) +{ + char buff[64]; + snprintf(buff, sizeof(buff),"cards_to_%.24s", proxy->reader->label); + char *fname = get_gbox_tmp_fname(buff); + + if(file_exists(fname)) + { + if(unlink(fname) < 0) + { + cs_log("Error removing peer_crd_file %s (errno=%d %s)!", fname, errno, strerror(errno)); + } + } +} + +static int8_t gbox_peer_online(struct gbox_peer *peer, uint8_t online) +{ + if (!peer) { return -1; } + + peer->online = online; + gbox_write_peer_onl(); + return 0; +} + +static int8_t gbox_clear_peer(struct gbox_peer *peer) +{ + if (!peer) + { + return -1; + } + + peer->ecm_idx = 0; + peer->next_hello = 0; + peer->authstat = 0; + peer->crd_crc_change = 1; + gbox_delete_cards(GBOX_DELETE_FROM_PEER, peer->gbox.id); + gbox_peer_online(peer, GBOX_PEER_OFFLINE); + + return 0; +} + +static int8_t gbox_reinit_proxy(struct s_client *proxy) +{ + if (!proxy) + { + return -1; + } + + struct gbox_peer *peer = proxy->gbox; + gbox_clear_peer(peer); + + if (!proxy->reader) + { + return -1; + } + + remove_peer_crd_file(proxy); + proxy->reader->tcp_connected = 0; + proxy->reader->card_status = CARD_NEED_INIT; + proxy->reader->last_s = proxy->reader->last_g = 0; + + return 0; +} + +//gbox.net doesn't accept slots >18 +static uint8_t calc_slot(uint8_t slot) +{ + int8_t i; + uint8_t cslot = slot; + + for(i=0 ; i < 9 ; i++) + { + if(slot > i*18) + { + cslot = slot - i*18; + } + } + return cslot; +} + +uint16_t count_send_cards(struct s_client *proxy) +{ + uint16_t nbcards = 0; + struct gbox_peer *peer = proxy->gbox; + struct gbox_card *card; + if(gbox_count_cards() > 0) + { + GBOX_CARDS_ITER *gci = gbox_cards_iter_create(); + while((card = gbox_cards_iter_next(gci))) + { + if(chk_ctab(gbox_get_caid(card->caprovid), &peer->my_user->account->ctab) && (card->lvl > 0) && +#ifdef MODULE_CCCAM + (card->dist <= peer->my_user->account->cccmaxhops) && +#endif + (!card->origin_peer || (card->origin_peer && card->origin_peer->gbox.id != peer->gbox.id))) + { + if(card->type == GBOX_CARD_TYPE_GBOX) + { + nbcards++; + continue; + } + else if(card->type == GBOX_CARD_TYPE_CCCAM) //&& cfg.cc_gbx_reshare_en + { + if(proxy->reader->gbox_cccam_reshare < 0) + { continue; } + else + { + if(chk_ident_filter(gbox_get_caid(card->caprovid), gbox_get_provid(card->caprovid), &proxy->reader->ccc_gbx_reshare_ident)) + { + nbcards++; + continue; + } + } + } + else if(card->type == GBOX_CARD_TYPE_LOCAL || card->type == GBOX_CARD_TYPE_BETUN || card->type == GBOX_CARD_TYPE_PROXY) + { + if(proxy->reader->gbox_reshare > 0) + { + nbcards++; + continue; + } + else + { + continue; + } + } + + if(nbcards == MAX_GBOX_CARDS ) + { + break; + } + } + } // while cards exist + gbox_cards_iter_destroy(gci); + } + return nbcards; +} + +void gbox_send(struct s_client *cli, uint8_t *buf, int32_t l) +{ + struct gbox_peer *peer = cli->gbox; + + cs_log_dump_dbg(D_READER, buf, l, "<- data to %s (%d bytes):", cli->reader->label, l); + + hostname2ip(cli->reader->device, &SIN_GET_ADDR(cli->udp_sa)); + SIN_GET_FAMILY(cli->udp_sa) = AF_INET; + SIN_GET_PORT(cli->udp_sa) = htons((uint16_t)cli->reader->r_port); + + gbox_encrypt(buf, l, peer->gbox.password); + sendto(cli->udp_fd, buf, l, 0, (struct sockaddr *)&cli->udp_sa, cli->udp_sa_len); + cs_log_dump_dbg(D_READER, buf, l, "<- encrypted data to %s (%d bytes):", cli->reader->label, l); +} + +void gbox_send_hello_packet(struct s_client *cli, int8_t packet, uint8_t *outbuf, uint8_t *ptr, int32_t nbcards, uint8_t hello_stat) +{ + struct gbox_peer *peer = cli->gbox; + int32_t hostname_len = cs_strlen(cfg.gbox_hostname); + int32_t len; + + gbox_message_header(outbuf, MSG_HELLO, peer->gbox.password, local_gbox.password); + + if(hello_stat > GBOX_STAT_HELLOS) // hello_stat == HelloR + { + outbuf[10] = 1; + } + else + { + outbuf[10] = 0; + } + outbuf[11] = packet; + + if((packet & 0x0F) == 0) // first packet + { + memcpy(++ptr, gbox_get_my_checkcode(), 7); + + ptr += 7; + *ptr = local_gbox.minor_version; + *(++ptr) = local_gbox.cpu_api; + memcpy(++ptr, cfg.gbox_hostname, hostname_len); + ptr += hostname_len; + *ptr = hostname_len; + } + len = ptr - outbuf + 1; + + switch(hello_stat) + { + case GBOX_STAT_HELLOL: + if(cfg.log_hello) + { cs_log("<- HelloL to %s", cli->reader->label); } + else + { cs_log_dbg(D_READER,"<- HelloL to %s", cli->reader->label); } + break; + + case GBOX_STAT_HELLOS: + if(cfg.log_hello) + { cs_log("<- HelloS #%d total cards %d to %s", (packet & 0xf) +1, nbcards, cli->reader->label); } + else + { cs_log_dbg(D_READER,"<- HelloS #%d total cards %d to %s", (packet & 0xf) +1, nbcards, cli->reader->label); } + break; + + case GBOX_STAT_HELLOR: + if(cfg.log_hello) + { cs_log("<- HelloR #%d total cards %d to %s", (packet & 0xf) +1, nbcards, cli->reader->label); } + else + { cs_log_dbg(D_READER,"<- HelloR #%d total cards %d to %s", (packet & 0xf) +1, nbcards, cli->reader->label); } + break; + + default: + if(cfg.log_hello) + { cs_log("<- hello #%d total cards %d to %s", (packet & 0xf) +1, nbcards, cli->reader->label); } + else + { cs_log_dbg(D_READER,"<- hello #%d total cards %d to %s", (packet & 0xf) +1, nbcards, cli->reader->label); } + break; + } + cs_log_dump_dbg(D_READER, outbuf, len, "<- hello #%d to %s, (len=%d):", (packet & 0xf) +1, cli->reader->label, len); + + gbox_compress(outbuf, len, &len); + gbox_send(cli, outbuf, len); +} + +void gbox_send_hello(struct s_client *proxy, uint8_t hello_stat) +{ + if(!proxy) + { + cs_log("ERROR: Invalid proxy try to call 'gbox_send_hello'"); + return; + } + + struct gbox_peer *peer = proxy->gbox; + + if(!peer) + { + cs_log("ERROR: Invalid peer try to call 'gbox_send_hello'"); + return; + } + + if(hello_stat > GBOX_STAT_HELLOL && (!peer->my_user || !peer->my_user->account)) + { + cs_log("ERROR: Invalid peer try to call 'gbox_send_hello'"); + return; + } + + uint16_t sendcrds = 0; + uint16_t nbcards = 0; + uint16_t nbcards_cnt = 0; + uint8_t packet = 0; + uint8_t buf[1024]; + uint8_t *ptr = buf + 11; + + struct gbox_card *card; + memset(buf, 0, sizeof(buf)); + if(gbox_count_cards() > 0) + { + if(hello_stat > GBOX_STAT_HELLOL) + { + uint16_t nb_send_cards = count_send_cards(proxy); + + char buff[64]; + snprintf(buff, sizeof(buff),"cards_to_%.24s", proxy->reader->label); + char *fname = get_gbox_tmp_fname(buff); + + FILE *fhandle = fopen(fname, "w"); + if(!fhandle) + { + cs_log("Couldn't open %s: %s", fname, strerror(errno)); + return; + } + + fprintf(fhandle, "Cards forwarded to peer %04X - %s\n\n", peer->gbox.id, proxy->reader->label); + + GBOX_CARDS_ITER *gci = gbox_cards_iter_create(); + while((card = gbox_cards_iter_next(gci))) + { + //send to user only cards which matching CAID from account and lvl > 0 + //and cccmaxhops from account + //do not send peer cards back + if(chk_ctab(gbox_get_caid(card->caprovid), &peer->my_user->account->ctab) && (card->lvl > 0) && +#ifdef MODULE_CCCAM + (card->dist <= peer->my_user->account->cccmaxhops) && +#endif + (!card->origin_peer || (card->origin_peer && card->origin_peer->gbox.id != peer->gbox.id))) + { + if(card->type == GBOX_CARD_TYPE_GBOX) + { + // cs_log_dbg(D_READER,"send to peer gbox-card %04X - level=%d crd-owner=%04X", card->caprovid >> 16, card->lvl, card->id.peer); + *(++ptr) = card->caprovid >> 24; + *(++ptr) = card->caprovid >> 16; + *(++ptr) = card->caprovid >> 8; + *(++ptr) = card->caprovid & 0xff; + *(++ptr) = 1; // note: original gbx is more efficient and sends all cards of one caid as package + *(++ptr) = calc_slot(card->id.slot); + *(++ptr) = ((card->lvl - 1) << 4) + card->dist + 1; + + fprintf(fhandle, "#%03d Peer Crd to %04X - crd %08X - level %d - dist %d - slot %02d - crd owner %04X\n", ++sendcrds, peer->gbox.id, card->caprovid, card->lvl -1, card->dist + 1, calc_slot(card->id.slot), card->id.peer); + } + else if(card->type == GBOX_CARD_TYPE_CCCAM) + { + if(proxy->reader->gbox_cccam_reshare < 0) + { continue; } + else + { + if(chk_ident_filter(gbox_get_caid(card->caprovid), gbox_get_provid(card->caprovid), &proxy->reader->ccc_gbx_reshare_ident)) + { + if(proxy->reader->gbox_cccam_reshare > proxy->reader->gbox_reshare) + { + proxy->reader->gbox_cccam_reshare = proxy->reader->gbox_reshare; + } + // cs_log_dbg(D_READER,"send to peer %04X - ccc-card %04X - level=%d crd-owner=%04X", peer->gbox.id, card->caprovid >> 16, proxy->reader->gbox_cccam_reshare, card->id.peer); + *(++ptr) = card->caprovid >> 24; + *(++ptr) = card->caprovid >> 16; + *(++ptr) = card->caprovid >> 8; + *(++ptr) = card->caprovid & 0xff; + *(++ptr) = 1; + *(++ptr) = calc_slot(card->id.slot); + *(++ptr) = ((proxy->reader->gbox_cccam_reshare) << 4) + card->dist + 1; + + fprintf(fhandle, "#%03d CCCM crd to %04X - crd %08X - level %d - dist %d - slot %02d - crd owner %04X\n", ++sendcrds, peer->gbox.id, card->caprovid, proxy->reader->gbox_cccam_reshare, card->dist + 1, calc_slot(card->id.slot), card->id.peer); + } + else + { continue; } + } + } + else if(card->type == GBOX_CARD_TYPE_LOCAL || card->type == GBOX_CARD_TYPE_BETUN || card->type == GBOX_CARD_TYPE_PROXY) + { + if(proxy->reader->gbox_reshare > 0) + { + //cs_log_dbg(D_READER,"send local crd %04X reshare=%d crd-owner=%04X", card->caprovid >> 16, proxy->reader->gbox_reshare, card->id.peer); + *(++ptr) = card->caprovid >> 24; + *(++ptr) = card->caprovid >> 16; + *(++ptr) = card->caprovid >> 8; + *(++ptr) = card->caprovid & 0xff; + *(++ptr) = 1; + *(++ptr) = calc_slot(card->id.slot); + *(++ptr) = ((proxy->reader->gbox_reshare - 1) << 4) + card->dist + 1; + + fprintf(fhandle, "#%03d Locl Crd to %04X - crd %08X - level %d - dist %d - slot %02d - crd owner %04X\n", ++sendcrds, peer->gbox.id, card->caprovid, proxy->reader->gbox_reshare - 1, card->dist + 1, calc_slot(card->id.slot), card->id.peer); + } + else + { + cs_log_dbg(D_READER,"WARNING: local card %04X NOT be shared - !! reshare=%d !! crd-owner=%04X", card->caprovid >> 16, proxy->reader->gbox_reshare, card->id.peer); + continue; + } + } + + *(++ptr) = card->id.peer >> 8; + *(++ptr) = card->id.peer & 0xff; + nbcards++; + nbcards_cnt++; + + if(nbcards_cnt == MAX_GBOX_CARDS) + { + cs_log("max card limit [%d] send to peer %04X is exceeded", MAX_GBOX_CARDS, peer->gbox.id); + break; + } + + if(nbcards_cnt == nb_send_cards) + { + break; + } + + if(nbcards == 74) + { + gbox_send_hello_packet(proxy, packet, buf, ptr, nbcards, hello_stat); + packet++; + nbcards = 0; + ptr = buf + 11; + memset(buf, 0, sizeof(buf)); + } + } + } // while cards exist + + gbox_cards_iter_destroy(gci); + fclose(fhandle); + } // end if > HelloL + else + { + GBOX_CARDS_ITER *gci = gbox_cards_iter_create(); + while((card = gbox_cards_iter_next(gci))) + { + if(card->lvl > 0 && card->type != GBOX_CARD_TYPE_CCCAM && card->type != GBOX_CARD_TYPE_GBOX) + { + if(proxy->reader->gbox_reshare > 0) + { + *(++ptr) = card->caprovid >> 24; + *(++ptr) = card->caprovid >> 16; + *(++ptr) = card->caprovid >> 8; + *(++ptr) = card->caprovid & 0xff; + *(++ptr) = 1; + *(++ptr) = calc_slot(card->id.slot); + *(++ptr) = ((proxy->reader->gbox_reshare - 1) << 4) + card->dist + 1; + *(++ptr) = card->id.peer >> 8; + *(++ptr) = card->id.peer & 0xff; + } + nbcards++; + if(nbcards >= GBOX_MAX_LOCAL_CARDS ) + { + cs_log("gbox_send_HelloL - local crds = %d - max allowed = %d ", nbcards, GBOX_MAX_LOCAL_CARDS); + break; + } + } + } // end while local cards exist + gbox_cards_iter_destroy(gci); + } // end if HelloL + }// end if gbox_count_cards > 0 + + gbox_send_hello_packet(proxy, 0x80 | packet, buf, ptr, nbcards, hello_stat); //last packet has bit 0x80 set +} + +void gbox_reconnect_peer(struct s_client *cl) +{ + struct gbox_peer *peer = cl->gbox; + hostname2ip(cl->reader->device, &SIN_GET_ADDR(cl->udp_sa)); + SIN_GET_FAMILY(cl->udp_sa) = AF_INET; + SIN_GET_PORT(cl->udp_sa) = htons((uint16_t)cl->reader->r_port); + hostname2ip(cl->reader->device, &(cl->ip)); + gbox_reinit_proxy(cl); + cs_log("reconnect %s peer: %04X", username(cl), peer->gbox.id); + gbox_send_hello(cl, GBOX_STAT_HELLOS); + return; +} + +void restart_gbox_peer(char *rdrlabel, uint8_t allrdr, uint16_t gbox_id) +{ + struct s_client *cl; + cs_readlock(__func__, &clientlist_lock); + + for(cl = first_client; cl; cl = cl->next) + { + if(cl->gbox && cl->typ == 'p' && + ((rdrlabel && !strcmp(rdrlabel, cl->reader->label)) || + allrdr || (gbox_id && cl->gbox_peer_id == gbox_id))) + { gbox_reconnect_peer(cl); } + } + cs_readunlock(__func__, &clientlist_lock); +} + +static void *gbox_server(struct s_client *cli, uint8_t *UNUSED(b), int32_t l) +{ + if(l > 0) + { + cs_log("gbox_server %s/%d", cli->reader->label, cli->port); + //gbox_check_header_recvd(cli, NULL, b, l); + } + return NULL; +} + +char *gbox_username(struct s_client *client) +{ + if(!client) + { + return "anonymous"; + } + + if(client->reader) + { + if(client->reader->r_usr[0]) + { + return client->reader->r_usr; + } + } + return "anonymous"; +} + +static int8_t gbox_disconnect_double_peers(struct s_client *cli) +{ + struct s_client *cl; + cs_writelock(__func__, &clientlist_lock); + + for(cl = first_client; cl; cl = cl->next) + { + if(cl->typ == 'c' && cl->gbox_peer_id == cli->gbox_peer_id && cl != cli) + { + cl->reader = NULL; + cl->gbox = NULL; + cs_log_dbg(D_READER, "disconnected double client %s - %s", username(cl), cs_inet_ntoa(cli->ip)); + //cs_log("disconnected double client %s - %s",username(cl), cs_inet_ntoa(cli->ip)); + cs_disconnect_client(cl); + } + } + cs_writeunlock(__func__, &clientlist_lock); + return 0; +} + +static int8_t gbox_auth_client(struct s_client *cli, uint32_t gbox_password) +{ + if(!cli) { return -1; } + + uint16_t gbox_id = gbox_convert_password_to_id(gbox_password); + struct s_client *cl = get_gbox_proxy(gbox_id); + + if(cl->typ == 'p' && cl->gbox && cl->reader) + { + struct gbox_peer *peer = cl->gbox; + struct s_auth *account = get_account_by_name(gbox_username(cl)); + + if ((peer->gbox.password == gbox_password) && account) + { + cli->crypted = 1; // display as crypted + cli->gbox = cl->gbox; // point to the same gbox as proxy + cli->reader = cl->reader; // point to the same reader as proxy + cli->gbox_peer_id = cl->gbox_peer_id; // signal authenticated + gbox_disconnect_double_peers(cli); + cs_auth_client(cli, account, NULL); + cli->account = account; + cli->grp = account->grp; + cli->lastecm = time(NULL); + peer->my_user = cli; + return 0; + } + } + return -1; +} + +static void gbox_server_init(struct s_client *cl) +{ + cs_writelock(__func__, &clientlist_lock); + if(!cl->init_done) + { + if(IP_ISSET(cl->ip)) + { + cs_log("new connection from %s", cs_inet_ntoa(cl->ip)); + } + // We cannot authenticate here, because we don't know gbox pw + cl->gbox_peer_id = NO_GBOX_ID; + cl->init_done = 1; + cl->last = time((time_t *)0); + start_gbx_ticker(); + } + cs_writeunlock(__func__, &clientlist_lock); + return; +} + +static uint16_t gbox_decode_cmd(uint8_t *buf) +{ + return buf[0] << 8 | buf[1]; +} + +int8_t gbox_message_header(uint8_t *buf, uint16_t cmd, uint32_t peer_password, uint32_t local_password) +{ + if (!buf) { return -1; } + i2b_buf(2, cmd, buf); + i2b_buf(4, peer_password, buf + 2); + if (cmd == MSG_CW) { return 0; } + i2b_buf(4, local_password, buf + 6); + return 0; +} + +// returns number of cards in a hello packet or -1 in case of error +int16_t read_cards_from_hello(uint8_t *ptr, uint8_t *len, CAIDTAB *ctab, uint8_t maxdist, struct gbox_peer *peer) +{ + uint8_t *current_ptr = 0; + uint32_t caprovid; + int16_t ncards_in_msg = 0; + + while(ptr < len) + { + caprovid = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; + + ncards_in_msg += ptr[4]; + //caid check + if(chk_ctab(gbox_get_caid(caprovid), ctab)) + { + current_ptr = ptr; + ptr += 5; + + // for all cards of current caid/provid, + while (ptr < current_ptr + 5 + current_ptr[4] * 4) + { + if ((ptr[1] & 0xf) <= maxdist) + { + gbox_add_card(ptr[2] << 8 | ptr[3], caprovid, ptr[0], ptr[1] >> 4, ptr[1] & 0xf, GBOX_CARD_TYPE_GBOX, peer); + } + ptr += 4; // next card + } // end while cards for provider + } + else + { + ptr += 5 + ptr[4] * 4; // skip cards because caid + } + } // end while < len + return ncards_in_msg; +} + +// returns 1 if checkcode changed / 0 if not +static uint8_t gbox_checkcode_recvd(struct s_client *cli, uint8_t *checkcode, uint8_t updcrc) +{ + struct gbox_peer *peer = cli->gbox; + if(memcmp(peer->checkcode, checkcode, 7)) + { + if (updcrc) + { + cs_log_dump_dbg(D_READER, peer->checkcode, 7, "-> old checkcode from %04X %s:", peer->gbox.id, cli->reader->label); + cs_log_dump_dbg(D_READER, checkcode, 7, "-> new checkcode from %04X %s:", peer->gbox.id, cli->reader->label); + memcpy(peer->checkcode, checkcode, 7); + } + return 1; + } + return 0; +} + +static void disable_remm(struct s_client *cli) +{ + if (cli->reader->blockemm & 0x80) // if remm marker bit set + { + struct gbox_peer *peer = cli->gbox; + cs_log("-> Disable REMM Req for %04X %s %s", peer->gbox.id, cli->reader->label, cli->reader->device); + cli->reader->gbox_remm_peer = 0; + cli->reader->blockemm = 15; + write_msg_info(cli, MSGID_REMM, 0, 0); + } + return; +} + +static void gbox_revd_goodnight(struct s_client *cli) +{ + cs_log("-> Good Night received from %s %s", cli->reader->label, cli->reader->device); + disable_remm(cli); + write_msg_info(cli, MSGID_GOODNIGHT, 0, 0); + gbox_reinit_proxy(cli); + gbox_write_share_cards_info(); + gbox_update_my_checkcode(); + //gbox_send_peer_crd_update(); + cli->last = time((time_t *)0); + return; +} + +static void gbox_send_my_checkcode(struct s_client *cli) +{ + struct gbox_peer *peer = cli->gbox; + uint8_t outbuf[20]; + gbox_message_header(outbuf, MSG_CHECKCODE, peer->gbox.password, local_gbox.password); + memcpy(outbuf + 10, gbox_get_my_checkcode(), 7); + gbox_send(cli, outbuf, 17); + cs_log_dump_dbg(D_READER, gbox_get_my_checkcode(), 7, "<- my checkcode to %s:", cli->reader->label); + if (cfg.log_hello) + { cs_log("<- HelloC my checkcode to %s (%s:%d)", cli->reader->label, cs_inet_ntoa(cli->ip), cli->reader->r_port);} + else + { cs_log_dbg(D_READER,"<- HelloC my checkcode to %s (%s:%d)", cli->reader->label, cs_inet_ntoa(cli->ip), cli->reader->r_port);} + return; +} + +int32_t gbox_cmd_hello_rcvd(struct s_client *cli, uint8_t *data, int32_t n) +{ + if (!cli || !cli->gbox || !cli->reader || !data) { return -1; } + + struct gbox_peer *peer = cli->gbox; + int16_t cards_number = 0; + int32_t payload_len = n; + int32_t hostname_len = 0; + int32_t footer_len = 0; + uint8_t *ptr = 0; + uint8_t diffcheck = 0; + uint8_t is_helloL = 0; + + if(!(gbox_decode_cmd(data) == MSG_HELLO1)) + { + gbox_decompress(data, &payload_len); + cs_log_dump_dbg(D_READER, data, payload_len, "-> data decompressed (%d bytes):", payload_len); + ptr = data + 12; + } + else + { + ptr = data + 11; + cs_log_dump_dbg(D_READER, data, payload_len, "decrypted data (%d bytes):", payload_len); + } + + if ((data[11] & 0xf) != peer->next_hello) // out of sync hellos + { + cs_log("-> out of sync hello from %s %s, expected: %02X, received: %02X", + username(cli), cli->reader->device, peer->next_hello, data[11] & 0xf); + + peer->next_hello = 0; + gbox_send_hello(cli, GBOX_STAT_HELLOL); + return 0; + } + + if (!(data[11] & 0xf)) // is first packet + { + gbox_delete_cards(GBOX_DELETE_FROM_PEER, peer->gbox.id); + hostname_len = data[payload_len - 1]; + footer_len = hostname_len + 2 + 7; + + if(peer->hostname && memcmp(peer->hostname, data + payload_len - 1 - hostname_len, hostname_len)) + { + cs_log("WARNING - Received Hello from Peer %04X - hostname in cfg is different to received hostname", peer->gbox.id); + } + + if(!peer->hostname || memcmp(peer->hostname, data + payload_len - 1 - hostname_len, hostname_len)) + { + NULLFREE(peer->hostname); + if(!cs_malloc(&peer->hostname, hostname_len + 1)) + { + return -1; + } + memcpy(peer->hostname, data + payload_len - 1 - hostname_len, hostname_len); + peer->hostname[hostname_len] = '\0'; + } + + diffcheck=gbox_checkcode_recvd(cli, data + payload_len - footer_len - 1, 1); + if(diffcheck) + { + peer->crd_crc_change = 1; + cs_log_dbg(D_READER,"-> first packet of hello from %04X - diffcheck=1 -> peer-card changed", peer->gbox.id); + } + peer->gbox.minor_version = data[payload_len - footer_len - 1 + 7]; + peer->gbox.cpu_api = data[payload_len - footer_len + 7]; + peer->total_cards = 0; + } + + // read cards from hello + cards_number = read_cards_from_hello(ptr, data + payload_len - footer_len - 1, &cli->reader->ctab, cli->reader->gbox_maxdist, peer); + + if (cards_number < 0) + { return -1; } + else + { + peer->total_cards += cards_number; + cs_log_dbg(D_READER,"-> Hello packet no. %d received - %d unfiltered card(s) - from %s %s", (data[11] & 0xF) + 1, cards_number, username(cli), cli->reader->device); + } + + if(peer->crd_crc_change && cards_number) + { gbox_update_my_checkcode(); } + + if(data[11] & 0x80) // last packet + { + uint8_t tmpbuf[8]; + memset(&tmpbuf[0], 0xff, 7); + + if(data[10] == 0x01 && !memcmp(data + 12, tmpbuf, 7)) // good night message + { + gbox_revd_goodnight(cli); + } + else // last packet of Hello + { + peer->filtered_cards = gbox_count_peer_cards(peer->gbox.id); + + if(!data[10]) + { + memset(&tmpbuf[0], 0, 7); + if(data[11] == 0x80 && !memcmp(data + 12, tmpbuf, 7)) //is HelloL rev < 3.0 + { + gbox_peer_online(peer, GBOX_PEER_ONLINE); + if(cfg.log_hello) + { cs_log("-> HelloL from %s (%s:%d) v2.%02X", cli->reader->label, cs_inet_ntoa(cli->ip), cli->reader->r_port, peer->gbox.minor_version);} + else + { cs_log_dbg(D_READER,"-> HelloL from %s (%s:%d) v2.%02X", cli->reader->label, cs_inet_ntoa(cli->ip), cli->reader->r_port, peer->gbox.minor_version);} + } + else + { + if(peer->crd_crc_change) + { + peer->crd_crc_change = 0; + cs_log_dbg(D_READER,"-> last packet of HelloS from %04X, peer-card changed -> write shared cards.info", peer->gbox.id); + if(peer->filtered_cards) + { + gbox_write_share_cards_info(); + } + if(!peer->online) + { + is_helloL = 1; + gbox_peer_online(peer, GBOX_PEER_ONLINE); + if(cfg.log_hello) + { cs_log("-> HelloL from %s (%s:%d) v2.%02X with %d cards", cli->reader->label, cs_inet_ntoa(cli->ip), cli->reader->r_port, peer->gbox.minor_version, peer->filtered_cards); } + else + {cs_log_dbg(D_READER,"-> HelloL from %s (%s:%d) v2.%02X with %d cards", cli->reader->label, cs_inet_ntoa(cli->ip), cli->reader->r_port, peer->gbox.minor_version, peer->filtered_cards); } + } + } + if(!is_helloL) + { + if(cfg.log_hello) + { cs_log("-> HelloS from %s (%s:%d) v2.%02X with %d cards", cli->reader->label, cs_inet_ntoa(cli->ip), cli->reader->r_port, peer->gbox.minor_version, peer->filtered_cards); } + else + { cs_log_dbg(D_READER,"-> HelloS in %d packets from %s (%s:%d) v2.%02X with %d cards filtered to %d cards", (data[0x0B] & 0x0f)+1, cli->reader->label, cs_inet_ntoa(cli->ip), cli->reader->r_port, peer->gbox.minor_version, peer->total_cards, peer->filtered_cards); } + } + } + cli->last = time((time_t *)0); + gbox_send_hello(cli, GBOX_STAT_HELLOR); + } + else + { + if(peer->crd_crc_change) + { + peer->crd_crc_change = 0; + cs_log_dbg(D_READER,"-> last packet of HelloR from %04X, peer-card changed -> write shared cards.info", peer->gbox.id); + if(peer->filtered_cards) + { + gbox_write_share_cards_info(); + } + if(!peer->online) + { + gbox_peer_online(peer, GBOX_PEER_ONLINE); + } + } + cli->last = time((time_t *)0); + + if (cfg.log_hello) + { cs_log("-> HelloR from %s (%s:%d) v2.%02X with %d cards", cli->reader->label, cs_inet_ntoa(cli->ip), cli->reader->r_port, peer->gbox.minor_version, peer->filtered_cards); } + else + { cs_log_dbg(D_READER,"-> HelloR in %d packets from %s (%s:%d) v2.%02X with %d cards filtered to %d cards", (data[0x0B] & 0x0f)+1, cli->reader->label, cs_inet_ntoa(cli->ip), cli->reader->r_port, peer->gbox.minor_version, peer->total_cards, peer->filtered_cards);} +// cs_sleepms(1000); //add some delay like gbox.net? + gbox_send_my_checkcode(cli); + } + + if(!peer->online) + { + gbox_peer_online(peer, GBOX_PEER_ONLINE); + gbox_send_hello(cli, GBOX_STAT_HELLOS); + } + + cli->reader->tcp_connected = CARD_INSERTED; + + if(!peer->filtered_cards) + { + cli->reader->card_status = NO_CARD; + } + else + { + cli->reader->card_status = CARD_INSERTED; + } + } + peer->crd_crc_change = 0; + peer->next_hello = 0; + cli->last = time((time_t *)0); + } + else + { + peer->next_hello++; + } + + return 0; +} + +uint8_t get_peer_onl_status(uint16_t peer_id) +{ + cs_readlock(__func__, &clientlist_lock); + struct s_client *cl; + for(cl = first_client; cl; cl = cl->next) + { + if(cl->gbox && cl->typ == 'p') + { + struct gbox_peer *peer = cl->gbox; + if((peer->gbox.id == peer_id) && peer->online) + { + cs_readunlock(__func__, &clientlist_lock); + return 1; + } + } + } + cs_readunlock(__func__, &clientlist_lock); + return 0; +} + +static int8_t is_blocked_peer(uint16_t peer_id) +{ + int i; + if (cfg.gbox_block_ecm_num > 0) + { + for (i = 0; i < cfg.gbox_block_ecm_num; i++) + { + if (cfg.gbox_block_ecm[i] == peer_id) + { + return 1; + } + } + } + return 0; +} + +int8_t check_peer_ignored(uint16_t peer_id) +{ + int i; + if (cfg.gbox_ignored_peer_num > 0) + { + for (i = 0; i < cfg.gbox_ignored_peer_num; i++) + { + if (cfg.gbox_ignored_peer[i] == peer_id) + { + return 1; + } + } + } + return 0; +} + +static int8_t validate_peerpass(uint32_t rcvd_peer_pw) +{ + struct s_client *cli; + cs_readlock(__func__, &clientlist_lock); + + for(cli = first_client; cli; cli = cli->next) + { + if(cli->gbox && cli->typ == 'p') + { + struct s_reader *rdr = cli->reader; + + if (rcvd_peer_pw == a2i(rdr->r_pwd, 4)) + { + cs_readunlock(__func__, &clientlist_lock); + return 1; + } // valid peerpass + } + } + cs_readunlock(__func__, &clientlist_lock); + return 0; +} + +static int8_t gbox_incoming_ecm(struct s_client *cli, uint8_t *data, int32_t n) +{ + if(!cli || !cli->gbox || !data || !cli->reader) { return -1; } + + struct gbox_peer *peer; + struct s_client *cl; + uint8_t diffcheck = 0; + + peer = cli->gbox; + if (!peer || !peer->my_user) + { + return -1; + } + cl = peer->my_user; + + if(n < 21) + { + return -1; + } + + // No ECMs with length < MIN_LENGTH expected + if ((((data[19] & 0x0f) << 8) | data[20]) < MIN_ECM_LENGTH) + { + return -1; + } + + // GBOX_MAX_HOPS not violated + if (data[n - 15] + 1 > GBOX_MAXHOPS) + { + cs_log("-> incoming ECM distance: %d > max ECM distance: %d", data[n - 15] + 1, GBOX_MAXHOPS); + return -1; + } + + // ECM must not take more hops than allowed by gbox_reshare + if (data[n - 15] + 1 > cli->reader->gbox_reshare) + { + cs_log("-> incoming ECM dist: %d more than allowed from specified gbox_reshare: %d", data[n - 15] + 1, cli->reader->gbox_reshare); + return -1; + } + + // Check for blocked peers + uint16_t requesting_peer = data[(((data[19] & 0x0f) << 8) | data[20]) + 21] << 8 | + data[(((data[19] & 0x0f) << 8) | data[20]) + 22]; + + if (is_blocked_peer(requesting_peer)) + { + handle_attack(cli, GBOX_ATTACK_ECM_BLOCKED, requesting_peer); + cs_log("ECM from peer %04X blocked by config", requesting_peer); + return -1; + } + + ECM_REQUEST *er; + if(!(er = get_ecmtask())) + { + return -1; + } + + struct gbox_ecm_request_ext *ere; + if(!cs_malloc(&ere, sizeof(struct gbox_ecm_request_ext))) + { + NULLFREE(er); + return -1; + } + + uint8_t *ecm = data + 18; // offset of ECM in gbx message + + er->src_data = ere; + gbox_init_ecm_request_ext(ere); + + if(peer->ecm_idx == 100) + { + peer->ecm_idx = 0; + } + + er->idx = peer->ecm_idx++; + er->ecmlen = SCT_LEN(ecm); + + if(er->ecmlen < 3 || er->ecmlen > MAX_ECM_SIZE || er->ecmlen + 18 > n) + { + NULLFREE(ere); + NULLFREE(er); + return -1; + } + + er->pid = b2i(2, data + 10); + er->srvid = b2i(2, data + 12); + + if(ecm[er->ecmlen + 5] == 0x05) + { + er->caid = (ecm[er->ecmlen + 5] << 8); + } + else + { + er->caid = b2i(2, ecm + er->ecmlen + 5); + } + + memcpy(er->ecm, data + 18, er->ecmlen); + + er->gbox_ecm_src_peer = b2i(2, ecm + er->ecmlen); //boxid which ORIGINALLY broadcasted the ECM + ere->gbox_version = ecm[er->ecmlen + 2]; + ere->gbox_rev = ecm[er->ecmlen + 3]; + ere->gbox_type = ecm[er->ecmlen + 4]; + uint32_t caprovid = b2i(4, ecm + er->ecmlen + 5); + er->gbox_cw_src_peer = b2i(2, ecm + er->ecmlen + 10); //boxid to send ECM to (cw source peer) + ere->gbox_slot = ecm[er->ecmlen + 12]; + diffcheck = gbox_checkcode_recvd(cl, data + n - 14, 0); + er->gbox_crc = gbox_get_checksum(&er->ecm[0], er->ecmlen); + er->gbox_ecm_dist = data[n - 15] + 1; + + memcpy(&ere->gbox_routing_info[0], &data[n - 15 - er->gbox_ecm_dist + 1], er->gbox_ecm_dist - 1); + + er->caid = gbox_get_caid(caprovid); + er->prid = gbox_get_provid(caprovid); + + peer->gbox_rev = ecm[er->ecmlen + 3]; + + cs_log_dbg(D_READER,"-> ECM (->%d) - ecm-requesting-peer: %04X - cw_src_peer: %04X caid: %04X sid: %04X from_peer: %04X rev: %01X.%01X (%s:%d)", + er->gbox_ecm_dist, er->gbox_ecm_src_peer, er->gbox_cw_src_peer, er->caid, er->srvid, peer->gbox.id, peer->gbox_rev >> 4, + peer->gbox_rev & 0xf, peer->hostname, cli->port); + + get_cw(cl, er); + + // checkcode did not match gbox->peer checkcode + if(diffcheck) + { + cs_log_dbg(D_READER,"checkcode in ECM CHANGED - Peer %04X ", peer->gbox.id); + gbox_send_hello(cli, GBOX_STAT_HELLOS); //peer will send back HelloR with cards, new checkcode etc + } + return 0; +} + +static uint32_t gbox_get_pending_time(ECM_REQUEST *er, uint16_t peer_id, uint8_t slot) +{ + if(!er) + { + return 0; + } + + uint32_t ret_time = 0; + struct gbox_card_pending *pending = NULL; + LL_LOCKITER *li = ll_li_create(er->gbox_cards_pending, 0); + + while((pending = ll_li_next(li))) + { + if ((pending->id.peer == peer_id) && (pending->id.slot == slot)) + { + ret_time = pending->pending_time; + er->gbox_cw_src_peer = peer_id; + break; + } + } + ll_li_destroy(li); + return ret_time; +} + +static int32_t gbox_chk_recvd_dcw(struct s_client *cli, uint8_t *dcw, int32_t *rc, uint8_t *data, int32_t n) +{ + if(!cli || gbox_decode_cmd(data) != MSG_CW || n < 44) + { + return -1; + } + + int i; + uint16_t id_card = 0; + struct s_client *proxy; + + if(cli->typ != 'p') + { + proxy = get_gbox_proxy(cli->gbox_peer_id); + } + else + { + proxy = cli; + } + + if (!proxy || !proxy->reader) + { + cs_log("error, gbox_chk_recvd_dcw, proxy not found"); + gbox_send_goodbye(cli); + return -1; + } + + proxy->last = time((time_t *)0); + *rc = 1; + memcpy(dcw, data + 14, 16); + uint32_t crc = b2i(4, data + 30); + char tmp[33]; + cs_log_dbg(D_READER,"-> CW (->%d) received cw: %s from CW-source-peer=%04X, caid=%04X, slot= %d, ecm_pid=%04X, sid=%04X, crc=%08X, cw-src-type=%d, cw-dist=%d, hw-type=%d, rev=%01X.%01X, chid=%04X", data[42] & 0x0f, + cs_hexdump(0, dcw, 16, tmp, sizeof(tmp)), data[10] << 8 | data[11], data[34] << 8 | data[35], data[36], data[6] << 8 | data[7], + data[8] << 8 | data[9], crc, data[41], data[42] & 0x0f, data[42] >> 4, data[43] >> 4, + data[43] & 0x0f, data[37] << 8 | data[38]); + + struct timeb t_now; + cs_ftime(&t_now); + int64_t cw_time = GBOX_DEFAULT_CW_TIME; + + for(i = 0; i < cfg.max_pending; i++) + { + if(proxy->ecmtask[i].gbox_crc == crc) + { + id_card = b2i(2, data + 10); + cw_time = comp_timeb(&t_now, &proxy->ecmtask[i].tps) - gbox_get_pending_time(&proxy->ecmtask[i], id_card, data[36]); + gbox_add_good_sid(id_card, proxy->ecmtask[i].caid, data[36], proxy->ecmtask[i].srvid, cw_time); + gbox_remove_all_bad_sids(&proxy->ecmtask[i], proxy->ecmtask[i].srvid); + + if(proxy->ecmtask[i].gbox_ecm_status == GBOX_ECM_NEW_REQ || proxy->ecmtask[i].gbox_ecm_status == GBOX_ECM_ANSWERED) + { + return -1; + } + + proxy->ecmtask[i].gbox_ecm_status = GBOX_ECM_ANSWERED; + proxy->ecmtask[i].gbox_cw_src_peer = id_card; + proxy->reader->currenthops = gbox_get_crd_dist_lev(id_card) & 0xf; + proxy->reader->gbox_cw_src_peer = id_card; + proxy->reader->gbox_crd_slot_lev = (data[36] << 4) | ((gbox_get_crd_dist_lev(id_card) >> 4) & 0xf); + *rc = 1; + return proxy->ecmtask[i].idx; + } + } + + // late answers from other peers,timing not possible + gbox_add_good_sid(id_card, data[34] << 8 | data[35], data[36], data[8] << 8 | data[9], GBOX_DEFAULT_CW_TIME); + cs_log_dbg(D_READER, "no task found for crc=%08x", crc); + + return -1; +} + +static int8_t gbox_received_dcw(struct s_client *cli, uint8_t *data, int32_t n) +{ + int32_t rc = 0, i = 0, idx = 0; + uint8_t dcw[16]; + + idx = gbox_chk_recvd_dcw(cli, dcw, &rc, data, n); + + if(idx < 0) // no dcw received + { + return -1; + } + + if(!idx) + { + idx = cli->last_idx; + } + + cli->reader->last_g = time((time_t *)0); // for reconnect timeout + + for(i = 0; i < cfg.max_pending; i++) + { + if(cli->ecmtask[i].idx == idx) + { + cli->pending--; + casc_check_dcw(cli->reader, i, rc, dcw); + return 0; + } + } + return -1; +} + +static void gbox_send_peer_crd_update(void) +{ + struct s_client *cl; + cs_readlock(__func__, &clientlist_lock); + + for (cl = first_client; cl; cl = cl->next) + { + if(cl->gbox && cl->typ == 'p' && !check_peer_ignored(cl->gbox_peer_id)) + { + struct gbox_peer *peer = cl->gbox; + if(peer->online) + { + gbox_send_hello(cl, GBOX_STAT_HELLOS); + cl->last = time((time_t *)0); + } + } + } + cs_readunlock(__func__, &clientlist_lock); + return; +} + +int32_t gbox_recv_cmd_switch(struct s_client *proxy, uint8_t *data, int32_t n) +{ + if (!data || !proxy) + { + return -1; + } + + uint16_t cmd = gbox_decode_cmd(data); + uint8_t diffcheck = 0; + //struct gbox_peer *peer = proxy->gbox; + + switch(cmd) + { + case MSG_HERE: + cs_log_dbg(D_READER,"-> HERE? from %s %s - check reader port: %d might be wrong", username(proxy), proxy->reader->device, proxy->reader->r_port); + // todo: what to reply?? + break; + + case MSG_GOODBYE: + cs_log("-> goodbye message from %s %s",username(proxy), proxy->reader->device); + //msg goodbye is an indication from peer that requested ECM failed (not found/rejected...) + //TODO: implement on suitable place - rebroadcast ECM to other peers + write_msg_info(proxy, MSGID_GOODBYE, 0, 0); + break; + + case MSG_GSMS: + if(!cfg.gsms_dis) + { + cs_log("-> MSG_GSMS from %s %s", username(proxy), proxy->reader->device); + gbox_send_gsms_ack(proxy); + write_gsms_msg(proxy, data +16, data[14], data[15]); + write_msg_info(proxy, MSGID_GSMS, 0, data[14]); + } + else + { + gsms_unavail(); + } + break; + + case MSG_GSMS_ACK: + if(!cfg.gsms_dis) + { + cs_log("-> MSG_GSMS_ACK from %s %s", username(proxy), proxy->reader->device); + write_gsms_ack(proxy); + } + else + { + gsms_unavail(); + } + break; + + case MSG_HELLO1: + case MSG_HELLO: + if(gbox_cmd_hello_rcvd(proxy, data, n) < 0) + { + return -1; + } + break; + + case MSG_CW: + gbox_received_dcw(proxy, data, n); + break; + + case MSG_CHECKCODE: + diffcheck = gbox_checkcode_recvd(proxy, data + 10, 0); + + if (cfg.log_hello) + { + cs_log("-> HelloC checkcode from %s - %s %s", username(proxy), proxy->reader->device, diffcheck ? "- crc diff":""); + } + else + { + cs_log_dbg(D_READER,"-> HelloC checkcode from %s - %s %s", username(proxy), proxy->reader->device, diffcheck ? "- crc diff":""); + } + + if(diffcheck) + { + gbox_write_share_cards_info(); //need that for gbox.net peer @ local crd change + cs_log_dbg(D_READER,"peer %s - %s checkcode changed", username(proxy), proxy->reader->device); + } + break; + + case MSG_ECM: + gbox_incoming_ecm(proxy, data, n); + break; + + case MSG_REM_EMM: + //cs_log_dbg(D_EMM,"-> Incoming REMM MSG (%d bytes) from %s - %s", n, username(proxy), proxy->reader->device); + cs_log_dump_dbg(D_EMM, data, n, "-> gbox incoming REMM MSG - (len=%d bytes):", n); + gbox_recvd_remm_cmd_switch(proxy, data, n); + break; + + default: + cs_log("-> unknown command %04X received from %s %s", + cmd, username(proxy), proxy->reader->device); + + write_msg_info(proxy, MSGID_UNKNOWNMSG, 0, 0); + + cs_log_dump_dbg(D_READER, data, n, "unknown data (%d bytes) received from %s %s", + n, username(proxy), proxy->reader->device); + } // end switch + return 0; +} + +uint8_t add_betatunnel_card(uint16_t caid, uint8_t slot) +{ + int32_t i; + struct s_client *cli; + cs_readlock(__func__, &clientlist_lock); + + for(cli = first_client; cli; cli = cli->next) + { + TUNTAB *ttab; + ttab = &cli->ttab; + + for(i = 0; i < ttab->ttnum; i++) + { + // Check for Betatunnel on gbox account in oscam.user + if(cli->gbox && ttab->ttdata && caid == ttab->ttdata[i].bt_caidto) + { + gbox_add_card(local_gbox.id, gbox_get_caprovid(ttab->ttdata[i].bt_caidfrom, i), slot, DEFAULT_GBOX_RESHARE, 0, GBOX_CARD_TYPE_BETUN, NULL); + cs_log_dbg(D_READER, "gbox created betatunnel card for caid: %04X->%04X", ttab->ttdata[i].bt_caidfrom, caid); + cs_readunlock(__func__, &clientlist_lock); + return 1; + } + } + } + cs_readunlock(__func__, &clientlist_lock); + return 0; +} + +static uint32_t gbox_add_local_cards(void) +{ + int32_t i; + uint32_t prid = 0; + uint8_t slot = 0; + uint16_t crdnb = 0; + uint16_t cccrdnb = 0; +#ifdef MODULE_CCCAM + LL_ITER it, it2; + struct cc_card *card = NULL; + struct cc_data *cc; + uint32_t checksum = 0; + uint16_t cc_peer_id = 0; + struct cc_provider *provider; + uint8_t *node1 = NULL; + uint8_t offset = 0; + + gbox_delete_cards(GBOX_DELETE_WITH_TYPE, GBOX_CARD_TYPE_CCCAM); +#endif + gbox_delete_cards(GBOX_DELETE_WITH_ID, local_gbox.id); + struct s_client *cl; + + cs_readlock(__func__, &clientlist_lock); + for(cl = first_client; cl; cl = cl->next) + { + if(cl->typ == 'r' && cl->reader && cl->reader->card_status == CARD_INSERTED && cl->reader->enable) + { + slot = gbox_next_free_slot(local_gbox.id); + + // SECA, Viaccess and Cryptoworks have multiple providers + if(caid_is_seca(cl->reader->caid) || caid_is_cryptoworks(cl->reader->caid)) + { + for(i = 0; i < cl->reader->nprov; i++) + { + prid = cl->reader->prid[i][1] << 16 | cl->reader->prid[i][2] << 8 | cl->reader->prid[i][3]; + gbox_add_card(local_gbox.id, gbox_get_caprovid(cl->reader->caid, prid), slot, DEFAULT_GBOX_RESHARE, 0, GBOX_CARD_TYPE_LOCAL, NULL); + } + } + else if(caid_is_viaccess(cl->reader->caid)) + { + for(i = 1; i < cl->reader->nprov; i++) //skip via issuer + { + prid = cl->reader->prid[i][1] << 16 | cl->reader->prid[i][2] << 8 | cl->reader->prid[i][3]; + gbox_add_card(local_gbox.id, gbox_get_caprovid(cl->reader->caid, prid), slot, DEFAULT_GBOX_RESHARE, 0, GBOX_CARD_TYPE_LOCAL, NULL); + } + } + else + { + gbox_add_card(local_gbox.id, gbox_get_caprovid(cl->reader->caid, 0), slot, DEFAULT_GBOX_RESHARE, 0, GBOX_CARD_TYPE_LOCAL, NULL); + if(chk_is_betatunnel_caid(cl->reader->caid) == 1) // 1702 1722 + { + if(add_betatunnel_card(cl->reader->caid, gbox_next_free_slot(local_gbox.id))) + { crdnb++; } + } + } + crdnb++; + } // end local readers +#ifdef MODULE_CCCAM + if(cfg.cc_gbx_reshare_en && cfg.cc_reshare > -1 && cl->typ == 'p' && cl->reader && cl->reader->typ == R_CCCAM && cl->cc) + { + cc = cl->cc; + it = ll_iter_create(cc->cards); + + while((card = ll_iter_next(&it))) + { + // calculate gbox id from cc node + node1 = ll_has_elements(card->remote_nodes); + checksum = ((node1[0] ^ node1[7]) << 8) | ((node1[1] ^ node1[6]) << 24) | (node1[2] ^ node1[5]) | ((node1[3] ^ node1[4]) << 16); + cc_peer_id = ((((checksum >> 24) & 0xFF) ^ ((checksum >> 8) & 0xFF)) << 8 | (((checksum >> 16) & 0xFF) ^ (checksum & 0xFF))) + offset; + + slot = gbox_next_free_slot(cc_peer_id); + + if(caid_is_seca(card->caid) || caid_is_viaccess(card->caid) || caid_is_cryptoworks(card->caid)) + { + it2 = ll_iter_create(card->providers); + while((provider = ll_iter_next(&it2))) + { + gbox_add_card(cc_peer_id, gbox_get_caprovid(card->caid, provider->prov), slot, DEFAULT_CCC_GBOX_RESHARE, card->hop, GBOX_CARD_TYPE_CCCAM, NULL); + } + } + else + { + gbox_add_card(cc_peer_id, gbox_get_caprovid(card->caid, 0), slot, DEFAULT_CCC_GBOX_RESHARE, card->hop, GBOX_CARD_TYPE_CCCAM, NULL); + } + cccrdnb++; + crdnb++; + + if(slot % 18 == 0) + { + //offset++; + offset += (rand() % 18) +1; + //cs_log("cccrdnum: %d, slot: %d, offset: %d, caid: %04X, peer: %04X", cccrdnb, slot, offset, card->caid, cc_peer_id); + } + } + } // end cccam +#endif + } // end for clients + + cs_readunlock(__func__, &clientlist_lock); + + if (cfg.gbox_proxy_cards_num > 0) + { + for (i = 0; i < cfg.gbox_proxy_cards_num; i++) + { + slot = gbox_next_free_slot(local_gbox.id); + gbox_add_card(local_gbox.id, cfg.gbox_proxy_card[i], slot, DEFAULT_GBOX_RESHARE, 0, GBOX_CARD_TYPE_PROXY, NULL); + cs_log_dbg(D_READER,"add proxy card: slot %d %04X:%06X", slot, gbox_get_caid(cfg.gbox_proxy_card[i]), gbox_get_provid(cfg.gbox_proxy_card[i])); + crdnb++; + } + } //end add proxy reader cards + + gbox_update_my_checkcode(); + gbox_write_local_cards_info(); + if (!local_cards_initialized) + { + local_cards_initialized = 1; + if(cfg.cc_gbx_reshare_en) + { cs_log("Local gbox cards initialized - cards: %d - filtered cccards: %d", crdnb - cccrdnb, cccrdnb); } + else + { cs_log("Local gbox cards initialized - cards: %d", crdnb); } + } + return (cccrdnb << 16) | (crdnb - cccrdnb); +} //end add local gbox cards + +void gbx_local_card_stat(uint8_t crdstat, uint16_t caid) +{ + if(crdstat && local_cards_initialized) + { + if(crdstat == LOCALCARDEJECTED) + { + cs_sleepms(100); + } + else if(crdstat == LOCALCARDUP) + { + cs_sleepms(2000); + cs_log("New local card ready - caid = %04X", caid); + } + else if(crdstat == LOCALCARDDISABLED) + { + cs_log_dbg(D_READER,"Local Gbox Card disabled by WebIF"); + } + else + { + return; + } + + cs_log("Card update send to peer(s) online - Local/Proxy crd(s):%d", gbox_add_local_cards() & 0xffff); + //gbox_write_local_cards_info(); //done by gbox_add_local_cards() + gbox_send_peer_crd_update(); + } + return; +} + +uint8_t chk_gbx_hdr_rcvd(uint16_t rcvd_header_cmd) +{ + switch(rcvd_header_cmd) + { + case MSG_HERE: + case MSG_HELLO1: + case MSG_HELLO: + case MSG_GOODBYE: + case MSG_GSMS: + case MSG_GSMS_ACK: + case MSG_CW: + case MSG_CHECKCODE: + case MSG_ECM: + case MSG_REM_EMM: + return 1; + + default: + return 0; + } +} + +// returns -1 in case of error, 1 if authentication was performed, 0 else +static int8_t gbox_check_header_recvd(struct s_client *cli, struct s_client *proxy, uint8_t *data, int32_t l) +{ + struct gbox_peer *peer = NULL; + if (proxy) { peer = proxy->gbox; } + + char tmp[128]; + int32_t n = l; + uint8_t authentication_done = 0; + uint16_t peer_recvd_id = 0; + uint32_t my_received_pw = 0; + uint32_t peer_received_pw = 0; + uint16_t rcvd_header_cmd; + + cs_log_dump_dbg(D_READER, data, n, "-> crypted data (%d bytes) from %s:", n, cs_inet_ntoa(cli->ip)); + gbox_decrypt(data, n, local_gbox.password); + cs_log_dump_dbg(D_READER, data, n, "-> decrypted data (%d bytes) from %s:", n, cs_inet_ntoa(cli->ip)); + + peer_received_pw = b2i(4, data + 6); + my_received_pw = b2i(4, data + 2); + rcvd_header_cmd = b2i(2, data); + + if(!chk_gbx_hdr_rcvd(rcvd_header_cmd)) + { + cs_log("-> ATTACK ALERT from IP %s - Received unknown Header: %02X", cs_inet_ntoa(cli->ip), b2i(2, data)); + //cs_log_dbg(D_READER,"-> received data: %s", cs_hexdump(1, data, n, tmp, sizeof(tmp))); + cs_log("-> received data: %s", cs_hexdump(1, data, n, tmp, sizeof(tmp))); + handle_attack(cli, GBOX_ATTACK_UNKWN_HDR, 0); + return -1; + } + + if (my_received_pw == local_gbox.password) + { + if (gbox_decode_cmd(data) != MSG_CW) + { + //peer_received_pw = b2i(4, data + 6); + peer_recvd_id = gbox_convert_password_to_id(peer_received_pw); + + //cs_log_dbg(D_READER, "-> data from IP: %s", cs_inet_ntoa(cli->ip)); + cs_log_dbg(D_READER, "-> data from peer: %04X data: %s", peer_recvd_id, cs_hexdump(0, data, l, tmp, sizeof(tmp))); + //cs_log_dbg(D_READER,"my_received pw: %08X - peer_recvd pw: %08X - peer_recvd_id: %04X ", my_received_pw, peer_received_pw, peer_recvd_id); + + if (check_peer_ignored(peer_recvd_id)) + { + handle_attack(cli, GBOX_ATTACK_PEER_IGNORE, peer_recvd_id); + cs_log("Peer blocked by conf - ignoring gbox peer_id: %04X", peer_recvd_id); + return -1; + } + + if (!validate_peerpass(peer_received_pw)) + { + handle_attack(cli, GBOX_ATTACK_PEER_PW, peer_recvd_id); + cs_log("peer: %04X - peerpass: %08X unknown -> enable reader and check oscam.server->[reader]->password", + peer_recvd_id, peer_received_pw); + + return -1; + } + + if (cli->gbox_peer_id == NO_GBOX_ID && gbox_decode_cmd(data) != MSG_HERE) + //if (cli->gbox_peer_id == NO_GBOX_ID) + { + if (gbox_auth_client(cli, peer_received_pw) < 0) + { + handle_attack(cli, GBOX_ATTACK_AUTH_FAIL, peer_recvd_id); + cs_log ("Peer %04X:%s authentication failed. Check user in [account] or {reader] section", peer_recvd_id, cs_inet_ntoa(cli->ip)); + return -1; + } + + authentication_done = 1; + proxy = get_gbox_proxy(cli->gbox_peer_id); + peer = proxy->gbox; + } + + if (!peer) + { + return -1; + } + + if (peer_received_pw != peer->gbox.password) + { + cs_log("gbox peer: %04X sends wrong own password", peer->gbox.id); + return -1; + } + } + else // is MSG_CW + { + cs_log_dbg(D_READER, "-> CW MSG from peer: %04X data: %s", + cli->gbox_peer_id, cs_hexdump(0, data, l, tmp, sizeof(tmp))); + + if((data[39] != ((local_gbox.id >> 8) & 0xff)) || (data[40] != (local_gbox.id & 0xff))) + { + cs_log_dbg(D_READER,"peer: %04X sends CW not to my id: %04X -> forwarding CW to requesting peer %02X%02X ", cli->gbox_peer_id, local_gbox.id, data[39], data[40]); + } + } + } + else // error my passw + { + cs_log("-> ATTACK ALERT from IP %s - received corrupted data - local password: %08X - peer password: %08X", cs_inet_ntoa(cli->ip), my_received_pw, peer_received_pw); + //cs_log_dbg(D_READER,"-> received data: %s", cs_hexdump(1, data, n, tmp, sizeof(tmp))); + cs_log("-> received data: %s", cs_hexdump(1, data, n, tmp, sizeof(tmp))); + handle_attack(cli, GBOX_ATTACK_LOCAL_PW, 0); + return -1; + } + + if(!proxy) + { + return -1; + } + + if(!IP_EQUAL(cli->ip, proxy->ip)) + { + cs_log("IP change received - peer %04X. New IP = %s. Reconnecting...", cli->gbox_peer_id, cs_inet_ntoa(cli->ip)); + restart_gbox_peer(NULL, 0, cli->gbox_peer_id); + //gbox_reconnect_peer(proxy); + write_msg_info(cli, MSGID_IPCHANGE, 0, 0); + return -1; + } + + if(!peer) + { + return -1; + } + + if(!peer->authstat) + { + peer->authstat = 1; + cli->last = time((time_t *)0); + cs_log("peer %04X authenticated successfully", cli->gbox_peer_id); + } + return authentication_done; +} + +static int32_t gbox_recv(struct s_client *cli, uint8_t *buf, int32_t l) +{ + uint8_t data[RECEIVE_BUFFER_SIZE]; + int32_t n = l, chkcmd; + int8_t ret = 0; + + if(!cli->udp_fd || !cli->is_udp || cli->typ != 'c') + { + return -1; + } + + n = recv_from_udpipe(buf); + if (n < MIN_GBOX_MESSAGE_LENGTH || n >= RECEIVE_BUFFER_SIZE) // protect against too short or too long messages + { + return -1; + } + + struct s_client *proxy = get_gbox_proxy(cli->gbox_peer_id); + + memcpy(&data[0], buf, n); + + ret = gbox_check_header_recvd(cli, proxy, &data[0], n); + if (ret < 0) + { + return -1; + } + + // in case of new authentication the proxy gbox can now be found + if (ret) + { + proxy = get_gbox_proxy(cli->gbox_peer_id); + } + + if (!proxy) + { + return -1; + } + + cli->last = time((time_t *)0); + cli->gbox = proxy->gbox; // point to the same gbox as proxy + cli->reader = proxy->reader; // point to the same reader as proxy + struct gbox_peer *peer = proxy->gbox; + cs_writelock(__func__, &peer->lock); + chkcmd = gbox_recv_cmd_switch(proxy, data, n); + cs_writeunlock(__func__, &peer->lock); + + if(chkcmd < 0) + { + return -1; + } + + return 0; +} + +static uint8_t check_setup( void) +{ +#ifdef HAVE_DVBAPI + if (module_dvbapi_enabled()) + { return 0x30; } //stb + else + { return 0x50; } +#else + return 0x50; //server +#endif +} + +static void gbox_send_dcw(struct s_client *cl, ECM_REQUEST *er) +{ + if (!cl || !er) + { + return; + } + + struct s_client *cli = get_gbox_proxy(cl->gbox_peer_id); + if (!cli || !cli->gbox) + { + return; + } + struct gbox_peer *peer = cli->gbox; + + struct gbox_ecm_request_ext *ere = er->src_data; + + if(er->rc == E_NOTFOUND && cli->reader->gbox_force_remm && ere->gbox_rev >> 4) + { + gbox_send_remm_req(cli, er); + return; + } + + if(er->rc >= E_NOTFOUND) + { + cs_log_dbg(D_READER, "unable to decode!"); + gbox_send_goodbye(cli); + return; + } + + uint8_t buf[60]; + memset(buf, 0, sizeof(buf)); + + gbox_message_header(buf, MSG_CW , peer->gbox.password, 0); + i2b_buf(2, er->pid, buf + 6); // PID + i2b_buf(2, er->srvid, buf + 8); // SrvID + i2b_buf(2, er->gbox_cw_src_peer, buf + 10); // From peer - source of cw + buf[12] = (ere->gbox_slot << 4) | (er->ecm[0] & 0x0f); // slot << 4 | even/odd + buf[13] = er->caid >> 8; // CAID first byte + memcpy(buf + 14, er->cw, 16); // CW + i2b_buf(4, er->gbox_crc, buf + 30); // CRC + i2b_buf(2, er->caid, buf + 34); // CAID + buf[36] = ere->gbox_slot; // Slot + + if (buf[34] == 0x06) // if irdeto + { + i2b_buf(2, er->chid, buf + 37); // CHID + } + else + { + if (local_gbox.minor_version == 0x2A) + { + buf[37] = 0xff; // gbox.net sends 0xff + buf[38] = 0xff; // gbox.net sends 0xff + } + else + { + buf[37] = 0; // gbox sends 0 + buf[38] = 0; // gbox sends 0 + } + } + + i2b_buf(2, er->gbox_ecm_src_peer, buf + 39); // Target peer to recv cw + + if(er->rc == E_CACHE1 || er->rc == E_CACHE2 || er->rc == E_CACHEEX) + { buf[41] = 0x03; } // source of cw -> cache + else + { buf[41] = 0x01; } // source of cw -> card, emu + + uint8_t cw_dist = gbox_get_crd_dist_lev(er->gbox_cw_src_peer) & 0xf; + + buf[42] = ((check_setup()) | (cw_dist + 1)); + buf[43] = ere->gbox_rev & 0xf0; + + // This copies the routing info from ECM to cw answer. + memcpy(&buf[44], &ere->gbox_routing_info, er->gbox_ecm_dist - 1); + buf[44 + er->gbox_ecm_dist - 1] = er->gbox_ecm_dist - 1; //act. dist +/* + uint8_t i; + for(i = 0; i < er->gbox_ecm_dist; i++) + { + buf[44 +i] = i; + } +*/ + gbox_send(cli, buf, 44 + er->gbox_ecm_dist); + + /* + char tmp[0x50]; + cs_log("sending dcw to peer : %04x data: %s", er->gbox_ecm_src_peer, cs_hexdump(0, buf, er->gbox_ecm_dist + 44, tmp, sizeof(tmp))); + */ + + if(ere->gbox_rev >> 4) + { gbox_send_remm_req(cli, er); } + + cs_log_dbg(D_READER,"<- CW (<-%d) caid; %04X from cw-source-peer: %04X forward to ecm-requesting-peer: %04X - forwarding peer: %04X %s rev:%01X.%01X port:%d", + er->gbox_ecm_dist, er->caid, er->gbox_cw_src_peer, er->gbox_ecm_src_peer, peer->gbox.id, cli->reader->label, + ere->gbox_rev >> 4, ere->gbox_rev & 0xf, cli->port); +} + +static int32_t gbox_send_ecm(struct s_client *cli, ECM_REQUEST *er) +{ + if(!cli || !cli->reader || !er || !er->ecmlen) + { + return -1; + } + + if(!cli->gbox || !cli->reader->tcp_connected) + { + cs_log_dbg(D_READER, "%s server not init!", cli->reader->label); + write_ecm_answer(cli->reader, er, E_NOTFOUND, 0x27, NULL, NULL, 0, NULL); + return -1; + } + + struct gbox_peer *peer = cli->gbox; + + if(!peer->filtered_cards) + { + cs_log_dbg(D_READER, "Send ECM failed, %s NO CARDS!", cli->reader->label); + write_ecm_answer(cli->reader, er, E_NOTFOUND, E2_CCCAM_NOCARD, NULL, NULL, 0, NULL); + return -1; + } + + if(!peer->online) + { + cs_log_dbg(D_READER, "Send ECM failed, peer is OFFLINE!"); + write_ecm_answer(cli->reader, er, E_NOTFOUND, 0x27, NULL, NULL, 0, NULL); + return -1; + } + + if(er->gbox_ecm_status == GBOX_ECM_ANSWERED) + { + cs_log_dbg(D_READER, "%s replied to this ecm already", cli->reader->label); + } + + if(er->gbox_ecm_status == GBOX_ECM_NEW_REQ) + { + er->gbox_cards_pending = ll_create("pending_gbox_cards"); + } + + uint8_t send_buf[1024]; + int32_t buflen, len1; + + len1 = er->ecmlen + 18; // length till end of ECM + + er->gbox_crc = gbox_get_checksum(&er->ecm[0], er->ecmlen); + + memset(send_buf, 0, sizeof(send_buf)); + + uint8_t nb_matching_crds = 0; + uint32_t current_avg_card_time = 0; + + gbox_message_header(send_buf, MSG_ECM , peer->gbox.password, local_gbox.password); + i2b_buf(2, er->pid, send_buf + 10); + i2b_buf(2, er->srvid, send_buf + 12); + send_buf[14] = 0x00; + send_buf[15] = 0x00; + send_buf[17] = 0x00; + memcpy(send_buf + 18, er->ecm, er->ecmlen); + + if(!er->gbox_ecm_dist) + { + er->gbox_ecm_src_peer = local_gbox.id; + i2b_buf(2, local_gbox.id, send_buf + len1); //local boxid first broadcasted the ECM + send_buf[len1 + 3] = 0x4; + } + else + { + i2b_buf(2, er->gbox_ecm_src_peer, send_buf + len1); //forward boxid that originally broadcasted the ECM + send_buf[len1 + 3] = 0; + } + + send_buf[len1 + 2] = cfg.gbox_my_vers; + + if(check_valid_remm_peer( peer->gbox.id)) + { + send_buf[len1 + 3] = local_gbx_rev; + } + + send_buf[len1 + 4] = gbox_get_my_cpu_api(); + + uint32_t caprovid = gbox_get_caprovid(er->caid, er->prid); + i2b_buf(4, caprovid, send_buf + len1 + 5); + + send_buf[len1 + 9] = 0x00; + buflen = len1 + 10; + + nb_matching_crds = gbox_get_cards_for_ecm(&send_buf[0], len1 + 10, cli->reader->gbox_maxecmsend, er, ¤t_avg_card_time, peer->gbox.id, cli->reader->gbox_force_remm); + + buflen += nb_matching_crds * 3; + + if(!nb_matching_crds && er->gbox_ecm_status == GBOX_ECM_NEW_REQ) + { + cs_log_dbg(D_READER, "no valid card found for CAID: %04X PROV: %06X", er->caid, er->prid); + write_ecm_answer(cli->reader, er, E_NOTFOUND, E2_CCCAM_NOCARD, NULL, NULL, 0, NULL); + return -1; + } + + if(nb_matching_crds) + { + send_buf[16] = nb_matching_crds; // Number of cards the ECM should be forwarded to + + // distance ECM + uint8_t i; + for(i = 0; i < er->gbox_ecm_dist + 1; i++) + { + send_buf[buflen] = i; + buflen++; + } + + memcpy(&send_buf[buflen], gbox_get_my_checkcode(), 7); + buflen = buflen + 7; + memcpy(&send_buf[buflen], peer->checkcode, 7); + buflen = buflen + 7; + + struct gbox_card_pending *pending = NULL; + struct timeb t_now; + cs_ftime(&t_now); + + for (i = 0; i < nb_matching_crds; i++) + { + if(!cs_malloc(&pending, sizeof(struct gbox_card_pending))) + { + cs_log("Can't allocate gbox card pending"); + return -1; + } + pending->id.peer = (send_buf[len1+10+i*3] << 8) | send_buf[len1+11+i*3]; + pending->id.slot = send_buf[len1+12+i*3]; + pending->pending_time = comp_timeb(&t_now, &er->tps); + + ll_append(er->gbox_cards_pending, pending); + cs_log_dbg(D_READER, "matching gbox card(s): %d, ID: %04X, Slot: %02X", + i + 1, (send_buf[len1 + 10 + i * 3] << 8) | send_buf[len1 + 11 + i * 3], send_buf[len1 + 12 + i * 3]); + } + + LL_LOCKITER *li = ll_li_create(er->gbox_cards_pending, 0); + while ((pending = ll_li_next(li))) + { + cs_log_dbg(D_READER, "Pending Card ID: %04X Slot: %02X time: %d", pending->id.peer, pending->id.slot, pending->pending_time); + er->gbox_cw_src_peer = pending->id.peer; + cs_log_dbg(D_READER,"<- ECM (<-%d) - caid: %04X prov: %06X sid: %04X to cw-src-peer: %04X - ecm_src_peer: %04X", + gbox_get_crd_dist_lev(er->gbox_cw_src_peer) & 0xf, er->caid, er->prid, er->srvid, er->gbox_cw_src_peer, er->gbox_ecm_src_peer); + } + ll_li_destroy(li); + + if(er->gbox_ecm_status == GBOX_ECM_NEW_REQ) + { + er->gbox_ecm_status++; + cli->pending++; + } + + gbox_send(cli, send_buf, buflen); + cli->reader->last_s = time((time_t *) 0); + } + return 0; +} + +// init my gbox with id, password etc +static int8_t init_local_gbox(void) +{ + int32_t i; + local_gbox.id = 0; + local_gbox.password = 0; + local_gbox.minor_version = cfg.gbox_my_vers; + local_gbox.cpu_api = gbox_get_my_cpu_api(); + init_gbox_cards_list(); + + if(!cfg.gbox_port[0]) + { + cs_log("error, no/invalid port=%d configured in oscam.conf!", cfg.gbox_port[0] ? cfg.gbox_port[0] : 0); + return -1; + } + + if(!cfg.gbox_hostname || cs_strlen(cfg.gbox_hostname) > 128) + { + cs_log("error, no/invalid hostname '%s' configured in oscam.conf!", + cfg.gbox_hostname ? cfg.gbox_hostname : ""); + return -1; + } + + if(!cfg.gbox_password) + { + cs_log("error, 'my_password' not configured in oscam.conf!"); + return -1; + } + + if(!cfg.gbox_reconnect || cfg.gbox_reconnect > GBOX_MAX_RECONNECT || cfg.gbox_reconnect < GBOX_MIN_RECONNECT) + { + cs_log("Invalid 'gbox_reconnect = %d' Using default: %d sec", cfg.gbox_reconnect, DEFAULT_GBOX_RECONNECT); + cfg.gbox_reconnect = DEFAULT_GBOX_RECONNECT; + } + + local_gbox.password = cfg.gbox_password; + local_gbox.id = gbox_convert_password_to_id(local_gbox.password); + + if(!local_gbox.id) + { + cs_log("invalid 'my_password' %08X -> local gbox id: %04X, choose another 'my_password'", + cfg.gbox_password, local_gbox.id); + return -1; + } + + local_gbox_initialized = 1; + + for(i = 0; i < CS_MAXPORTS; i++) + { + if(!cfg.gbox_port[i]) + { + cs_log("we are online - %d port(s) to monitor", i); + break; + } + } + + gbox_write_version(); + + return local_gbox_initialized; +} + +static int32_t gbox_peer_init(struct s_client *cli) +{ + if(!cli || cli->typ != 'p' || !cli->reader) + { + cs_log("error, wrong call to gbox_peer_init!"); + return -1; + } + + if (local_gbox_initialized < 0) + { + return -1; + } + + int8_t ret; + if(!local_gbox_initialized) + { + local_gbox_initialized = 1; + ret = init_local_gbox(); + if (ret < 0) + { + local_gbox_initialized = -1; + cs_log("local gbox initialization failed"); + write_msg_info(cli, MSGID_GBOXONL, 0, 0); + return -1; + } + write_msg_info(cli, MSGID_GBOXONL, 0, 1); + } + + if(!cs_malloc(&cli->gbox, sizeof(struct gbox_peer))) + { + return -1; + } + + struct s_reader *rdr = cli->reader; + struct gbox_peer *peer = cli->gbox; + + memset(peer, 0, sizeof(struct gbox_peer)); + + peer->gbox.password = a2i(rdr->r_pwd, 4); + //cs_log_dbg(D_READER,"peer-reader-label: %s peer-reader-password: %s", cli->reader->label, rdr->r_pwd); + peer->gbox.id = gbox_convert_password_to_id(peer->gbox.password); + + if (get_gbox_proxy(peer->gbox.id) || peer->gbox.id == NO_GBOX_ID || peer->gbox.id == local_gbox.id) + { + cs_log("error, double/invalid gbox id: %04X", peer->gbox.id); + return -1; + } + cs_lock_create(__func__, &peer->lock, "gbox_lock", 5000); + + gbox_clear_peer(peer); + + cli->gbox_peer_id = peer->gbox.id; + + cli->pfd = 0; + cli->crypted = 1; + + rdr->card_status = CARD_NEED_INIT; + rdr->tcp_connected = 0; + + set_null_ip(&cli->ip); + + if((cli->udp_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + { + cs_log("socket creation failed (errno=%d %s)", errno, strerror(errno)); + cs_disconnect_client(cli); + } + + int32_t opt = 1; + setsockopt(cli->udp_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + set_so_reuseport(cli->udp_fd); + + set_socket_priority(cli->udp_fd, cfg.netprio); + + memset((char *)&cli->udp_sa, 0, sizeof(cli->udp_sa)); + + if(!hostResolve(rdr)) + { + return 0; + } + + cli->port = rdr->r_port; + SIN_GET_FAMILY(cli->udp_sa) = AF_INET; + SIN_GET_PORT(cli->udp_sa) = htons((uint16_t)rdr->r_port); + hostname2ip(cli->reader->device, &SIN_GET_ADDR(cli->udp_sa)); + + cs_log("proxy %s (fd=%d, peer id=%04X, my id=%04X, my hostname=%s, peer's listen port=%d)", + rdr->device, cli->udp_fd, peer->gbox.id, local_gbox.id, cfg.gbox_hostname, rdr->r_port); + + cli->pfd = cli->udp_fd; + + if(!cli->reader->gbox_maxecmsend) + { + cli->reader->gbox_maxecmsend = DEFAULT_GBOX_MAX_ECM_SEND; + } + + if(!cli->reader->gbox_maxdist) + { + cli->reader->gbox_maxdist = DEFAULT_GBOX_MAX_DIST; + } + + // value > GBOX_MAXHOPS not allowed in gbox network + if(cli->reader->gbox_reshare > GBOX_MAXHOPS) + { + cli->reader->gbox_reshare = GBOX_MAXHOPS; + } + + if(cli->reader->gbox_cccam_reshare > GBOX_MAXHOPS) + { + cli->reader->gbox_cccam_reshare = GBOX_MAXHOPS; + } + + return 0; +} + +static void gbox_send_HERE(struct s_client *cli) +{ + struct gbox_peer *peer = cli->gbox; + uint8_t outbuf[64]; + int32_t hostname_len = cs_strlen(cli->reader->device); + gbox_message_header(outbuf, MSG_HERE, peer->gbox.password, local_gbox.password); + outbuf[0xA] = cfg.gbox_my_vers; + outbuf[0xB] = gbox_get_my_cpu_api(); + memcpy(&outbuf[0xC], cli->reader->device, hostname_len); + gbox_send(cli, outbuf, hostname_len + 0xC); + if(cfg.log_hello) + { cs_log("<- send Keep Alive MSG HERE to boxid: %04X - %s", peer->gbox.id, cli->reader->label); } + else + { cs_log_dbg(D_READER,"<- send Keep Alive MSG HERE to boxid: %04X - %s", peer->gbox.id, cli->reader->label); } + cs_log_dump_dbg(D_READER, outbuf, hostname_len + 0xC, "<- send HERE?, (len=%d):", hostname_len + 0xC); +} + +uint8_t k = 0; +void gbox_send_idle_msg(void) +{ + if(k > 8) //10s + { + struct s_client *cl; + cs_readlock(__func__, &clientlist_lock); + + for(cl = first_client; cl; cl = cl->next) + { + struct gbox_peer *peer = cl->gbox; + if(cl->gbox && cl->typ == 'p' && !peer->online && !check_peer_ignored(cl->gbox_peer_id) && cl->reader->send_offline_cmd) + { + gbox_send_HERE(cl); + } + } + cs_readunlock(__func__, &clientlist_lock); + k = 0; + } + else { k++; } +} + +void gbox_send_init_hello(void) +{ + if(local_gbox_initialized) + { + struct s_client *cl; + gbox_add_local_cards(); + cs_sleepms(1000); + cs_readlock(__func__, &clientlist_lock); + + for(cl = first_client; cl; cl = cl->next) + { + if(cl->gbox && cl->typ == 'p') + { + gbox_send_hello(cl, GBOX_STAT_HELLOL); + } + } + cs_readunlock(__func__, &clientlist_lock); + } + else if(cfg.gbox_port[0] || cfg.gbox_hostname) + { cs_log("local gbox failed init"); } +} + +static void gbox_peer_idle (struct s_client *cl) +{ + uint32_t ptime_elapsed, etime_elapsed; + struct s_client *proxy = get_gbox_proxy(cl->gbox_peer_id); + struct gbox_peer *peer; + peer = proxy->gbox; + + if (proxy && proxy->gbox) + { + etime_elapsed = llabs(cl->lastecm - time(NULL)); + + if (llabs(proxy->last - time(NULL)) > etime_elapsed) + { + ptime_elapsed = etime_elapsed; + } + else + { + ptime_elapsed = llabs(proxy->last - time(NULL)); + } + + if (ptime_elapsed > (cfg.gbox_reconnect *2) && cl->gbox_peer_id != NO_GBOX_ID) + { + // gbox peer apparently died without saying goodnight + cs_writelock(__func__, &peer->lock); + + if (peer->online) + { + disable_remm(cl); + cs_log("Lost connection to: %s %s - taking peer %04X %s offline", + proxy->reader->device, cs_inet_ntoa(proxy->ip), cl->gbox_peer_id, username(cl)); + + cs_log_dbg(D_READER, "time since last proxy activity: %d sec > %d => lost connection - taking peer %04X - %s offline", + ptime_elapsed, cfg.gbox_reconnect *2, cl->gbox_peer_id, username(cl)); + + write_msg_info(proxy, MSGID_LOSTCONNECT, 0, 0); + gbox_reinit_proxy(proxy); + gbox_write_share_cards_info(); + gbox_update_my_checkcode(); + } + cs_writeunlock(__func__, &peer->lock); + } + + if (etime_elapsed > cfg.gbox_reconnect && cl->gbox_peer_id != NO_GBOX_ID) + { + cs_writelock(__func__, &peer->lock); + + if (!(check_peer_ignored(cl->gbox_peer_id))) + { + if (!peer->online && ptime_elapsed < cfg.gbox_reconnect *3) + { + cs_log_dbg(D_READER, "%04X - %s -> offline - time since last ecm / proxy_act: %d sec / %d sec => trigger HELLOL", + cl->gbox_peer_id, username(cl), etime_elapsed, ptime_elapsed); + gbox_send_hello(proxy, GBOX_STAT_HELLOL); + } + + if (peer->online) + { + cs_log_dbg(D_READER, "%04X - %s -> online - time since last ecm /proxy activity: %d sec / %d sec => trigger keepalive HELLOS", + cl->gbox_peer_id, username(cl), etime_elapsed, ptime_elapsed); + + gbox_send_hello(proxy, GBOX_STAT_HELLOS); + } + } + cs_writeunlock(__func__, &peer->lock); + } + } + cl->last = time((time_t *)0); +} + +static int8_t gbox_send_peer_good_night(struct s_client *proxy) +{ + uint8_t outbuf[64]; + int32_t hostname_len = 0; + + if (cfg.gbox_hostname) + { + hostname_len = cs_strlen(cfg.gbox_hostname); + } + + int32_t len = hostname_len + 22; + + if(proxy->gbox && proxy->typ == 'p') + { + struct gbox_peer *peer = proxy->gbox; + struct s_reader *rdr = proxy->reader; + + if (peer->online) + { + gbox_message_header(outbuf, MSG_HELLO, peer->gbox.password, local_gbox.password); + outbuf[10] = 0x01; + outbuf[11] = 0x80; + memset(&outbuf[12], 0xff, 7); + outbuf[19] = cfg.gbox_my_vers; + outbuf[20] = gbox_get_my_cpu_api(); + memcpy(&outbuf[21], cfg.gbox_hostname, hostname_len); + outbuf[21 + hostname_len] = hostname_len; + cs_log("<- good night to %s:%d id: %04X", rdr->device, rdr->r_port, peer->gbox.id); + gbox_compress(outbuf, len, &len); + gbox_send(proxy, outbuf, len); + gbox_reinit_proxy(proxy); + } + } + return 0; +} + +void gbox_send_good_night(void) +{ + gbox_free_cardlist(); + struct s_client *cli; + cs_readlock(__func__, &clientlist_lock); + + for(cli = first_client; cli; cli = cli->next) + { + if(cli->gbox && cli->typ == 'p') + { + gbox_send_peer_good_night(cli); + } + } + cs_readunlock(__func__, &clientlist_lock); +} + +void gbox_send_goodbye(struct s_client *cli) // indication that requested ECM failed +{ + if (local_gbox.minor_version != 0x2A) + { + uint8_t outbuf[15]; + struct gbox_peer *peer = cli->gbox; + gbox_message_header(outbuf, MSG_GOODBYE, peer->gbox.password, local_gbox.password); + cs_log_dbg(D_READER,"<- goodbye - requested ecm failed. Send info to requesting boxid: %04X", peer->gbox.id); + gbox_send(cli, outbuf, 10); + } + else + { + return; + } +} + +static void delayed_crd_update(void) +{ + struct s_client *cli; + cs_readlock(__func__, &clientlist_lock); + + for (cli = first_client; cli; cli = cli->next) + { + if(cli->gbox && cli->typ == 'p' && !check_peer_ignored(cli->gbox_peer_id)) + { + uint32_t timediff = llabs(cli->last - time(NULL)); + struct gbox_peer *peer = cli->gbox; + if(peer->online && peer->authstat == 1 && timediff > 3) + { + peer->authstat = 2; + //cs_log("<- send %d sec delayed HelloS to %04X", timediff, cli->gbox_peer_id); + gbox_send_hello(cli, GBOX_STAT_HELLOS); + } + } + } + cs_readunlock(__func__, &clientlist_lock); + return; +} + +static pthread_t gbx_tick_thread; +static int32_t gbx_tick_active = 0; +static pthread_cond_t gbx_tick_sleep_cond; +static pthread_mutex_t gbx_tick_sleep_cond_mutex; +static pthread_mutex_t gbx_tick_mutex; + +static void gbx_tick_mutex_init(void) +{ + static int8_t mutex_init = 0; + if(!mutex_init) + { + SAFE_MUTEX_INIT(&gbx_tick_mutex, NULL); + cs_pthread_cond_init(__func__, &gbx_tick_sleep_cond_mutex, &gbx_tick_sleep_cond); + mutex_init = 1; + } +} + +static void gbx_ticker(void) +{ + char *fext= FILE_GSMS_TXT; + char *fname = get_gbox_tmp_fname(fext); + + while(gbx_tick_active) + { + if(file_exists(fname) && !cfg.gsms_dis) + { + gbox_init_send_gsms(); + } + + startup++; + + if(startup < GBOX_START_TIME) + { + delayed_crd_update(); + } + else if(startup == GBOX_START_TIME -10) + { + gbox_add_local_cards(); + } + else if(startup % STATS_WRITE_TIME == 0) + { + gbox_write_stats(); + } + + gbox_send_idle_msg(); + + sleepms_on_cond(__func__, &gbx_tick_sleep_cond_mutex, &gbx_tick_sleep_cond, 1000); + } + pthread_exit(NULL); +} + +void start_gbx_ticker(void) +{ + int32_t is_active; + + gbx_tick_mutex_init(); + SAFE_MUTEX_LOCK(&gbx_tick_mutex); + + is_active = gbx_tick_active; + if(!gbx_tick_active) + { + gbx_tick_active = 1; + } + + if(is_active) + { + SAFE_MUTEX_UNLOCK(&gbx_tick_mutex); + return; + } + + int32_t ret = start_thread("gbox ticker", (void *)&gbx_ticker, NULL, &gbx_tick_thread, 0, 1); + if(ret) + { + gbx_tick_active = 0; + } + + SAFE_MUTEX_UNLOCK(&gbx_tick_mutex); +} + +void stop_gbx_ticker(void) +{ + gbx_tick_mutex_init(); + SAFE_MUTEX_LOCK(&gbx_tick_mutex); + + if(gbx_tick_active) + { + gbx_tick_active = 0; + SAFE_COND_SIGNAL(&gbx_tick_sleep_cond); + SAFE_THREAD_JOIN(gbx_tick_thread, NULL); + } + + SAFE_MUTEX_UNLOCK(&gbx_tick_mutex); +} + +void module_gbox(struct s_module *ph) +{ + int32_t i; + + for(i = 0; i < CS_MAXPORTS; i++) + { + if(!cfg.gbox_port[i]) + { + break; + } + + ph->ptab.nports++; + ph->ptab.ports[i].s_port = cfg.gbox_port[i]; + } + + ph->desc = "gbox"; + ph->num = R_GBOX; + ph->type = MOD_CONN_UDP; + ph->large_ecm_support = 1; + ph->listenertype = LIS_GBOX; + ph->s_handler = gbox_server; + ph->s_init = gbox_server_init; + ph->send_dcw = gbox_send_dcw; + ph->recv = gbox_recv; + ph->c_init = gbox_peer_init; + ph->c_send_ecm = gbox_send_ecm; + ph->c_send_emm = gbox_send_remm_data; + ph->s_peer_idle = gbox_peer_idle; +} +#endif diff --git a/module-gbox.h b/module-gbox.h new file mode 100644 index 0000000..e14bcd8 --- /dev/null +++ b/module-gbox.h @@ -0,0 +1,196 @@ +#ifndef MODULE_GBOX_H_ +#define MODULE_GBOX_H_ + +#ifdef MODULE_GBOX + +#define NO_GBOX_ID 0 +#define GBOX_MAXHOPS 8 +#define DEFAULT_GBOX_MAX_DIST 2 +#define DEFAULT_GBOX_MAX_ECM_SEND 5 +#define DEFAULT_GBOX_RESHARE 2 +#define DEFAULT_CCC_GBOX_RESHARE 1 +#define DEFAULT_GBOX_RECONNECT 180 +#define GBOX_MIN_RECONNECT 60 +#define GBOX_MAX_RECONNECT 300 +#define GBOX_MAX_LOCAL_CARDS 32 +#define GBOX_SID_CONFIRM_TIME 3600 +#define GBOX_DEFAULT_CW_TIME 500 +#define RECEIVE_BUFFER_SIZE 1024 +#define MIN_GBOX_MESSAGE_LENGTH 10 // CMD + pw + pw. TODO: Check if is really min +#define MIN_ECM_LENGTH 8 +#define STATS_WRITE_TIME 60 // write stats file every 1 min +#define MAX_GBOX_CARDS 1024 // send max. 1024 cards to peer +#define LOCAL_GBOX_MAJOR_VERSION 0x02 +#define GBOX_START_TIME 30 + +#define MSG_ECM 0x445C +#define MSG_CW 0x4844 +#define MSG_HELLO 0xDDAB +#define MSG_HELLO1 0x4849 +#define MSG_CHECKCODE 0x41C0 +#define MSG_GOODBYE 0x9091 +#define MSG_GSMS_ACK 0x9099 +#define MSG_GSMS 0x0FFF +#define MSG_HERE 0xA0A1 + +#define GBOX_ECM_NEW_REQ 0 +#define GBOX_ECM_SENT 1 +#define GBOX_ECM_ANSWERED 2 + +#define GBOX_CARD_TYPE_GBOX 0 +#define GBOX_CARD_TYPE_LOCAL 1 +#define GBOX_CARD_TYPE_BETUN 2 +#define GBOX_CARD_TYPE_CCCAM 3 +#define GBOX_CARD_TYPE_PROXY 4 + +#define FILE_GBOX_VERSION "gbox.ver" +#define FILE_SHARED_CARDS_INFO "share.info" +#define FILE_BACKUP_CARDS_INFO "expired.info" +#define FILE_ATTACK_INFO "attack.txt" +#define FILE_GBOX_PEER_ONL "share.onl" +#define FILE_STATS "stats.info" +#define FILE_MSG_INFO "msg.info" +#define FILE_LOCAL_CARDS_INFO "sc.info" + +#define MSGID_GOODNIGHT 0 +#define MSGID_GSMS 1 +#define MSGID_GONEOFFLINE 2 +#define MSGID_COMEONLINE 3 +#define MSGID_GOODBYE 4 +#define MSGID_LOSTCONNECT 5 +#define MSGID_ATTACK 6 +#define MSGID_IPCHANGE 7 +#define MSGID_GBOXONL 8 +#define MSGID_UNKNOWNMSG 9 +#define MSGID_REMM 12 + +#define GBOX_STAT_HELLOL 0 +#define GBOX_STAT_HELLOS 1 +#define GBOX_STAT_HELLOR 2 + +#define GBOX_DELETE_FROM_PEER 0 +#define GBOX_DELETE_WITH_ID 1 +#define GBOX_DELETE_WITH_TYPE 2 + +#define GBOX_PEER_OFFLINE 0 +#define GBOX_PEER_ONLINE 1 + +#define GBOX_ATTACK_LOCAL_PW 0 +#define GBOX_ATTACK_PEER_IGNORE 1 +#define GBOX_ATTACK_PEER_PW 2 +#define GBOX_ATTACK_AUTH_FAIL 3 +#define GBOX_ATTACK_ECM_BLOCKED 4 +#define GBOX_ATTACK_REMM_REQ_BLOCKED 5 +#define GBOX_ATTACK_UNKWN_HDR 6 + +#define LOCALCARDEJECTED 1 +#define LOCALCARDUP 2 +#define LOCALCARDDISABLED 3 + +struct gbox_srvid +{ + uint16_t sid; + uint32_t provid_id; +}; + +struct gbox_good_srvid +{ + struct gbox_srvid srvid; + time_t last_cw_received; +}; + +struct gbox_bad_srvid +{ + struct gbox_srvid srvid; + uint8_t bad_strikes; +}; + +struct gbox_card_id +{ + uint16_t peer; + uint8_t slot; +}; + +struct gbox_card_pending +{ + struct gbox_card_id id; + uint32_t pending_time; +}; + +struct gbox_card +{ + struct gbox_card_id id; + uint32_t caprovid; + uint8_t dist; + uint8_t lvl; + uint8_t type; + LLIST *badsids; // sids that have failed to decode (struct gbox_srvid) + LLIST *goodsids; // sids that could be decoded (struct gbox_srvid) + uint32_t no_cws_returned; + uint32_t average_cw_time; + struct gbox_peer *origin_peer; +}; + +struct gbox_data +{ + uint16_t id; + uint32_t password; + uint8_t minor_version; + uint8_t cpu_api; +}; + +struct gbox_peer +{ + struct gbox_data gbox; + uint8_t *hostname; + uint8_t checkcode[7]; + int8_t online; + uint8_t onlinestat; + uint8_t authstat; + uint8_t next_hello; + uint8_t gbox_rev; + uint8_t crd_crc_change; + uint8_t ecm_idx; + CS_MUTEX_LOCK lock; + struct s_client *my_user; + uint16_t filtered_cards; + uint16_t total_cards; + uint32_t last_remm_crc; +}; + +struct gbox_ecm_request_ext +{ + uint8_t gbox_slot; + uint8_t gbox_version; + uint8_t gbox_rev; + uint8_t gbox_type; + uint8_t gbox_routing_info[GBOX_MAXHOPS]; +}; + +void handle_attack(struct s_client *cli, uint8_t txt_id, uint16_t rcvd_id); +char *get_gbox_tmp_fname(char *fext); +uint16_t gbox_get_local_gbox_id(void); +uint16_t gbox_convert_password_to_id(uint32_t password); +uint8_t get_peer_onl_status(uint16_t peer_id); +int8_t check_peer_ignored(uint16_t peer_id); +uint32_t gbox_get_local_gbox_password(void); +void gbox_send(struct s_client *cli, uint8_t *buf, int32_t l); +int8_t gbox_message_header(uint8_t *buf, uint16_t cmd, uint32_t peer_password, uint32_t local_password); +void gbox_free_cards_pending(ECM_REQUEST *er); +void gbox_send_good_night(void); +void gbox_send_goodbye(struct s_client *cli); +void restart_gbox_peer(char *rdrlabel, uint8_t all, uint16_t gbox_id); +void write_msg_info(struct s_client *cli, uint8_t msg_id, uint8_t txt_id, uint16_t misc); +extern void gbx_local_card_stat(uint8_t crdstat, uint16_t caid); +extern void gbox_send_init_hello(void); +extern void stop_gbx_ticker(void); +#else +static inline void gbox_free_cards_pending(ECM_REQUEST *UNUSED(er)) { } +static inline void gbox_send_good_night(void) { } +static inline void gbx_local_card_stat(uint8_t UNUSED(crdstat), uint16_t UNUSED(caid) ) { } +static inline void gbox_send_init_hello(void) { } +static inline void stop_gbx_ticker(void) { } + +#endif + +#endif diff --git a/module-ghttp.c b/module-ghttp.c new file mode 100644 index 0000000..fea2db0 --- /dev/null +++ b/module-ghttp.c @@ -0,0 +1,891 @@ +#define MODULE_LOG_PREFIX "ghttp" + +#include "globals.h" +#ifdef MODULE_GHTTP +#include "oscam-client.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-reader.h" +#include "oscam-work.h" +#include "module-dvbapi.h" +#ifdef WITH_SSL +#include +#include +#include +#endif + +typedef struct +{ + uint8_t *session_id; + uint8_t *host_id; + uint8_t *fallback_id; + pthread_mutex_t conn_mutex; + LLIST *post_contexts; + LLIST *ecm_q; +#ifdef WITH_SSL + SSL *ssl_handle; +#endif +} s_ghttp; + +typedef struct +{ + uint16_t onid; + uint16_t tsid; + uint16_t sid; + uint16_t pid; +} s_ca_context; + +static LLIST *ghttp_ignored_contexts; +#ifdef WITH_SSL +static SSL_CTX *ghttp_ssl_context; +#endif + +static int32_t _ghttp_post_ecmdata(struct s_client *client, ECM_REQUEST *er); + +#ifdef WITH_SSL +static bool _ssl_connect(struct s_client *client, int32_t fd) +{ + s_ghttp *context = (s_ghttp *)client->ghttp; + + if(context->ssl_handle) // cleanup previous + { + SSL_shutdown(context->ssl_handle); + SSL_free(context->ssl_handle); + } + + cs_log_dbg(D_CLIENT, "%s: trying ssl...", client->reader->label); + + context->ssl_handle = SSL_new(ghttp_ssl_context); + if(context->ssl_handle == NULL) + { + ERR_print_errors_fp(stderr); +#if OPENSSL_VERSION_NUMBER < 0x1010005fL + ERR_remove_state(0); +#endif + return false; + } + if(!SSL_set_fd(context->ssl_handle, fd)) + { + ERR_print_errors_fp(stderr); +#if OPENSSL_VERSION_NUMBER < 0x1010005fL + ERR_remove_state(0); +#endif + return false; + } + if(SSL_connect(context->ssl_handle) != 1) + { + ERR_print_errors_fp(stderr); +#if OPENSSL_VERSION_NUMBER < 0x1010005fL + ERR_remove_state(0); +#endif + } + + if(context->ssl_handle) + { + cs_log_dbg(D_CLIENT, "%s: ssl established", client->reader->label); + return true; + } + + return false; +} +#endif + +int32_t ghttp_client_init(struct s_client *cl) +{ + int32_t handle; + char *str = NULL; + + ghttp_ignored_contexts = ll_create("ignored contexts"); +#ifdef WITH_SSL + ghttp_ssl_context = SSL_CTX_new(SSLv23_client_method()); + if(ghttp_ssl_context == NULL) + { + ERR_print_errors_fp(stderr); +#if OPENSSL_VERSION_NUMBER < 0x1010005fL + ERR_remove_state(0); +#endif + } +#endif + + if(cl->reader->r_port == 0) + { + cl->reader->r_port = cl->reader->ghttp_use_ssl ? 443 : 80; + } + + str = strstr(cl->reader->device, "."); + if(!str) + { + char host[128]; + cs_strncpy(host, cl->reader->device, sizeof(host)); + snprintf(cl->reader->device, sizeof(cl->reader->device), "%.115s.appspot.com", host); + } + + cs_log("%s: init google cache client %s:%d (fd=%d)", cl->reader->label, cl->reader->device, cl->reader->r_port, cl->udp_fd); + + if(cl->udp_fd) + { + network_tcp_connection_close(cl->reader, "re-init"); + } + + handle = network_tcp_connection_open(cl->reader); + if(handle < 0) + { + return -1; + } + + cl->reader->tcp_connected = 2; + cl->reader->card_status = CARD_INSERTED; + cl->reader->last_g = cl->reader->last_s = time((time_t *)0); + + cl->pfd = cl->udp_fd; + if(!cl->ghttp) + { + if(!cs_malloc(&(cl->ghttp), sizeof(s_ghttp))) + { + return -1; + } + memset(cl->ghttp, 0, sizeof(s_ghttp)); + ((s_ghttp *)cl->ghttp)->post_contexts = ll_create("post contexts"); + ((s_ghttp *)cl->ghttp)->ecm_q = ll_create("ecm queue"); + } + else + { + ll_clear(((s_ghttp *)cl->ghttp)->ecm_q); + } + + if(cl->reader->ghttp_use_ssl) + { +#ifndef WITH_SSL + cs_log("%s: use_ssl set but no ssl support available, aborting...", cl->reader->label); + return -1; +#endif +#ifdef WITH_SSL + if(ghttp_ssl_context == NULL) + { + return -1; + } + + if(_ssl_connect(cl, handle)) + { + cl->crypted = 1; + } + else + { + network_tcp_connection_close(cl->reader, "ssl failed"); + return -1; + } +#endif + } + + return 0; +} + +static uint32_t javastring_hashcode(uint8_t *input, int32_t len) +{ + uint32_t h = 0; + while(/**input &&*/ len--) + { + h = 31 * h + *input++; + } + return h; +} + +static int32_t ghttp_send_int(struct s_client *client, uint8_t *buf, int32_t l) +{ + cs_log_dbg(D_CLIENT, "%s: sending %d bytes", client->reader->label, l); + if(!client->pfd) + { + // disconnected? try reinit. + cs_log_dbg(D_CLIENT, "%s: disconnected?", client->reader->label); + ghttp_client_init(client); + } + +#ifdef WITH_SSL + s_ghttp *context = (s_ghttp *)client->ghttp; + if(client->reader->ghttp_use_ssl) + { + return SSL_write(context->ssl_handle, buf, l); + } +#endif + return send(client->pfd, buf, l, 0); +} + +static int32_t ghttp_send(struct s_client *client, uint8_t *buf, int32_t l) +{ + s_ghttp *context = (s_ghttp *)client->ghttp; + SAFE_MUTEX_LOCK(&context->conn_mutex); + int32_t ret = ghttp_send_int(client, buf, l); + SAFE_MUTEX_UNLOCK(&context->conn_mutex); + return ret; +} + +static int32_t ghttp_recv_int(struct s_client *client, uint8_t *buf, int32_t l) +{ + int32_t n = -1; + s_ghttp *context = (s_ghttp *)client->ghttp; + + if(!client->pfd) + { + ll_clear(context->ecm_q); + return -1; + } + + if(client->reader->ghttp_use_ssl) + { +#ifdef WITH_SSL + n = SSL_read(context->ssl_handle, buf, l); +#endif + } + else + { + n = cs_recv(client->pfd, buf, l, 0); + } + + if(n > 0) + { + cs_log_dbg(D_CLIENT, "%s: received %d bytes from %s", client->reader->label, n, remote_txt()); + client->last = time((time_t *)0); + + if(n > 400) + { + buf[n] = '\0'; + cs_log_dbg(D_CLIENT, "%s: unexpected reply size %d - %s", client->reader->label, n, buf); + return -1; // assumes google error, disconnects + } + } + + if(n < 5) + { + cs_log_dbg(D_CLIENT, "%s: read %d bytes, disconnecting", client->reader->label, n); + n = -1; + } + return n; +} + +static int32_t ghttp_recv(struct s_client *client, uint8_t *buf, int32_t l) +{ + s_ghttp *context = (s_ghttp *)client->ghttp; + SAFE_MUTEX_LOCK(&context->conn_mutex); + int32_t ret = ghttp_recv_int(client, buf, l); + SAFE_MUTEX_UNLOCK(&context->conn_mutex); + return ret; +} + +static bool _is_post_context(LLIST *ca_contexts, ECM_REQUEST *er, bool remove_data) +{ + s_ca_context *ctx; + s_ca_context *existing = NULL; + + if(cs_malloc(&ctx, sizeof(s_ca_context))) + { + ctx->onid = er->onid; + ctx->tsid = er->tsid; + ctx->sid = er->srvid; + ctx->pid = 0; + + existing = (s_ca_context *)ll_contains_data(ca_contexts, ctx, sizeof(s_ca_context)); + + if(remove_data) + { + ll_remove_data(ca_contexts, existing); + } + NULLFREE(ctx); + } + return existing != NULL; +} + +static void _add_context(LLIST *ca_contexts, s_ca_context *context) +{ + if(!ll_contains_data(ca_contexts, context, sizeof(s_ca_context))) + { + ll_append(ca_contexts, context); + } + else + { + NULLFREE(context); + } + + while(ll_count(ca_contexts) > 64) + { + ll_remove_first_data(ca_contexts); + } + + cs_log_dbg(D_CLIENT, "ca contexts size %d", ll_count(ca_contexts)); +} + +static void _set_pid_status(LLIST *ca_contexts, uint16_t onid, uint16_t tsid, uint16_t sid, uint16_t pid) +{ + s_ca_context *ctx; + if(cs_malloc(&ctx, sizeof(s_ca_context))) + { + ctx->onid = onid; + ctx->tsid = tsid; + ctx->sid = sid; + ctx->pid = pid; + _add_context(ca_contexts, ctx); + } +} + +static void _set_pids_status(LLIST *ca_contexts, uint16_t onid, uint16_t tsid, uint16_t sid, uint8_t *buf, int len) +{ + int8_t offs = 0; + uint16_t pid = 0; + + while(offs < len) + { + pid = b2i(2, buf + offs); + offs += 2; + _set_pid_status(ca_contexts, onid, tsid, sid, pid); + } +} + +static bool _swap_hosts(s_ghttp *context) +{ + if(!context->fallback_id) + { + return false; + } + + uint8_t *tmp = context->host_id; + context->host_id = context->fallback_id; + context->fallback_id = tmp; + NULLFREE(context->session_id); + ll_clear(context->ecm_q); + ll_clear_data(ghttp_ignored_contexts); + return true; +} + +static char *_get_header_substr(uint8_t *buf, const char *start, const char *end) +{ + char *data = strstr((char *)buf, start); + if(!data) + { + return NULL; + } + + data += cs_strlen(start); + int len = strstr(data, end) - data; + if(len <= 0) + { + return NULL; + } + + char tmp = data[len]; + data[len] = '\0'; + char *value = cs_strdup(data); + data[len] = tmp; + return value; +} + +static int _get_int_header(uint8_t *buf, const char *start) +{ + char *data = strstr((char *)buf, start); + if(!data) { return -1; } + data += cs_strlen(start); + return atoi(data); +} + +static char *_get_header(uint8_t *buf, const char *start) +{ + return _get_header_substr(buf, start, "\r\n"); +} + +static int32_t ghttp_recv_chk(struct s_client *client, uint8_t *dcw, int32_t *rc, uint8_t *buf, int32_t n) +{ + char *data; + char *hdrstr; + uint8_t *content; + int rcode, len, clen = 0; + s_ghttp *context = (s_ghttp *)client->ghttp; + ECM_REQUEST *er = NULL; + + if(n < 5) + { + return -1; + } + + data = strstr((char *)buf, "HTTP/1.1 "); + if(!data || ll_count(context->ecm_q) > 6) + { + cs_log_dbg(D_CLIENT, "%s: non http or otherwise corrupt response: %s", client->reader->label, buf); + cs_log_dump_dbg(D_CLIENT, buf, n, "%s: ", client->reader->label); + network_tcp_connection_close(client->reader, "receive error"); + NULLFREE(context->session_id); + ll_clear(context->ecm_q); + return -1; + } + + LL_ITER itr = ll_iter_create(context->ecm_q); + er = (ECM_REQUEST *)ll_iter_next(&itr); + + rcode = _get_int_header(buf, "HTTP/1.1 "); + clen = _get_int_header(buf, "Content-Length: "); + + content = (uint8_t *)(strstr(data, "\r\n\r\n") + 4); + + hdrstr = _get_header_substr(buf, "ETag: \"", "\"\r\n"); + if(hdrstr) + { + NULLFREE(context->host_id); + context->host_id = (uint8_t *)hdrstr; + cs_log_dbg(D_CLIENT, "%s: new name: %s", client->reader->label, context->host_id); + + len = b64decode(context->host_id); + if(len == 0 || len >= 64) + { + NULLFREE(context->host_id); + } + else + { + cs_log_dbg(D_CLIENT, "%s: redirected...", client->reader->label); + NULLFREE(context->session_id); + ll_clear_data(ghttp_ignored_contexts); + ll_clear(context->ecm_q); + return -1; + } + } + + hdrstr = _get_header_substr(buf, "ETag: W/\"", "\"\r\n"); + if(hdrstr) + { + NULLFREE(context->fallback_id); + context->fallback_id = (uint8_t *)hdrstr; + cs_log_dbg(D_CLIENT, "%s: new fallback name: %s", client->reader->label, context->fallback_id); + + len = b64decode(context->fallback_id); + if(len == 0 || len >= 64) + { + NULLFREE(context->fallback_id); + } + } + + hdrstr = _get_header(buf, "Set-Cookie: GSSID="); + if(hdrstr) + { + NULLFREE(context->session_id); + context->session_id = (uint8_t *)hdrstr; + cs_log_dbg(D_CLIENT, "%s: set session_id to: %s", client->reader->label, context->session_id); + } + + // buf[n] = '\0'; + // cs_log_dump_dbg(D_TRACE, content, clen, "%s: reply\n%s", client->reader->label, buf); + + if(rcode < 200 || rcode > 204) + { + cs_log_dbg(D_CLIENT, "%s: http error code %d", client->reader->label, rcode); + data = strstr((char *)buf, "Content-Type: application/octet-stream"); // if not octet-stream, google error. need reconnect? + if(data) // we have error info string in the post content + { + if(clen > 0) + { + content[clen] = '\0'; + cs_log_dbg(D_CLIENT, "%s: http error message: %s", client->reader->label, content); + } + } + + if(rcode == 503) + { + if(er && _is_post_context(context->post_contexts, er, false)) + { + if(_swap_hosts(context)) + { + cs_log_dbg(D_CLIENT, "%s: switching to fallback", client->reader->label); + } + else + { + cs_log_dbg(D_CLIENT, "%s: recv_chk got 503 despite post, trying reconnect", client->reader->label); + network_tcp_connection_close(client->reader, "reconnect"); + ll_clear(context->ecm_q); + } + } + else + { + // on 503 cache timeout, retry with POST immediately (and switch to POST for subsequent) + if(er) + { + _set_pid_status(context->post_contexts, er->onid, er->tsid, er->srvid, 0); + cs_log_dbg(D_CLIENT, "%s: recv_chk got 503, trying direct post", client->reader->label); + _ghttp_post_ecmdata(client, er); + } + } + } + else if(rcode == 401) + { + NULLFREE(context->session_id); + if(er) + { + cs_log_dbg(D_CLIENT, "%s: session expired, trying direct post", client->reader->label); + _ghttp_post_ecmdata(client, er); + } + } + else if(rcode == 403) + { + client->reader->enable = 0; + network_tcp_connection_close(client->reader, "login failure"); + ll_clear(context->ecm_q); + cs_log("%s: invalid username/password, disabling reader.", client->reader->label); + } + + // not sure if this is needed on failure, copied from newcamd + *rc = 0; + memset(dcw, 0, 16); + + return -1; + } + + // successful http reply (200 ok or 204 no content) + + hdrstr = _get_header(buf, "Pragma: context-ignore="); + if(hdrstr) + { + if(clen > 1) + { + cs_log_dump_dbg(D_CLIENT, content, clen, "%s: pmt ignore reply - %s (%d pids)", + client->reader->label, hdrstr, clen / 2); + + uint32_t onid = 0, tsid = 0, sid = 0; + + if(sscanf(hdrstr, "%4x-%4x-%4x", &onid, &tsid, &sid) == 3) + { + _set_pids_status(ghttp_ignored_contexts, onid, tsid, sid, content, clen); + } + NULLFREE(hdrstr); + return -1; + } + NULLFREE(hdrstr); + } + + data = strstr((char *)buf, "Pragma: context-ignore-clear"); + if(data) + { + cs_log_dbg(D_CLIENT, "%s: clearing local ignore list (size %d)", + client->reader->label, ll_count(ghttp_ignored_contexts)); + + ll_clear_data(ghttp_ignored_contexts); + } + + // switch back to cache get after rapid ecm response (arbitrary atm), only effect is a slight bw save for client + if(!er || _is_post_context(context->post_contexts, er, false)) + { + data = strstr((char *)buf, "Pragma: cached"); + if(data || (client->cwlastresptime > 0 && client->cwlastresptime < 640)) + { + cs_log_dbg(D_CLIENT, "%s: probably cached cw (%d ms), switching back to cache get for next req", + client->reader->label, client->cwlastresptime); + + if(er) + { + _is_post_context(context->post_contexts, er, true); + } + } + } + + if(clen == 16) // cw in content + { + memcpy(dcw, content, 16); + *rc = 1; + er = ll_remove_first(context->ecm_q); + if(!er) + { + return -1; + } + + cs_log_dump_dbg(D_TRACE, dcw, 16, "%s: cw recv chk for idx %d", client->reader->label, er->idx); + return er->idx; + } + else + { + if(clen != 0) + { + cs_log_dump_dbg(D_CLIENT, content, clen, "%s: recv_chk fail, clen = %d", client->reader->label, clen); + } + } + return -1; +} + +static char *_ghttp_basic_auth(struct s_client *client) +{ + uint8_t auth[64]; + char *encauth = NULL; + int32_t ret; + s_ghttp *context = (s_ghttp *)client->ghttp; + + if(!context->session_id && cs_strlen(client->reader->r_usr) > 0) + { + cs_log_dbg(D_CLIENT, "%s: username specified and no existing session, adding basic auth", client->reader->label); + ret = snprintf((char *)auth, sizeof(auth), "%s:%s", client->reader->r_usr, client->reader->r_pwd); + b64encode((char *)auth, ret, &encauth); + } + return encauth; +} + +static int32_t _ghttp_http_get(struct s_client *client, uint32_t hash, int odd) +{ + uint8_t req[128]; + char *encauth = NULL; + int32_t ret; + s_ghttp *context = (s_ghttp *)client->ghttp; + + encauth = _ghttp_basic_auth(client); + + if(encauth) // basic auth login + { + ret = snprintf((char *)req, sizeof(req), "GET /api/c/%d/%x HTTP/1.1\r\nHost: %s\r\nAuthorization: Basic %s\r\n\r\n", + odd ? 81 : 80, hash, context->host_id, encauth); + + NULLFREE(encauth); + } + else + { + if(context->session_id) // session exists + { + ret = snprintf((char *)req, sizeof(req), "GET /api/c/%s/%d/%x HTTP/1.1\r\nHost: %s\r\n\r\n", + context->session_id, odd ? 81 : 80, hash, context->host_id); + } + else // no credentials configured, assume no session required + { + ret = snprintf((char *)req, sizeof(req), "GET /api/c/%d/%x HTTP/1.1\r\nHost: %s\r\n\r\n", + odd ? 81 : 80, hash, context->host_id); + } + } + + ret = ghttp_send(client, req, ret); + + return ret; +} + +static int32_t _ghttp_post_ecmdata(struct s_client *client, ECM_REQUEST *er) +{ + uint8_t req[640]; + uint8_t *end; + char *encauth = NULL; + int32_t ret; + s_ghttp *context = (s_ghttp *)client->ghttp; + + encauth = _ghttp_basic_auth(client); + + if(encauth) // basic auth login + { + ret = snprintf((char *)req, sizeof(req), "POST /api/e/%x/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s\r\nAuthorization: Basic %s\r\nContent-Length: %d\r\n\r\n", + er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid, context->host_id, encauth, er->ecmlen); + + NULLFREE(encauth); + } + else + { + if(context->session_id) // session exists + { + ret = snprintf((char *)req, sizeof(req), "POST /api/e/%s/%x/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n", + context->session_id, er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid, context->host_id, er->ecmlen); + } + else // no credentials configured, assume no session required + { + ret = snprintf((char *)req, sizeof(req), "POST /api/e/%x/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n", + er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid, context->host_id, er->ecmlen); + } + } + end = req + ret; + memcpy(end, er->ecm, er->ecmlen); + + cs_log_dbg(D_CLIENT, "%s: sending full ecm - /api/e/%x/%x/%x/%x/%x/%x", + client->reader->label, er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid); + + ret = ghttp_send(client, req, ret + er->ecmlen); + + return ret; +} + +static bool _is_pid_ignored(ECM_REQUEST *er) +{ + s_ca_context *ignore; + if(cs_malloc(&ignore, sizeof(s_ca_context))) + { + ignore->onid = er->onid; + ignore->tsid = er->tsid; + ignore->sid = er->srvid; + ignore->pid = er->pid; + + if(ll_contains_data(ghttp_ignored_contexts, ignore, sizeof(s_ca_context))) + { + NULLFREE(ignore); + return true; + } + else + { + NULLFREE(ignore); + } + } + return false; +} + +static int32_t ghttp_send_ecm(struct s_client *client, ECM_REQUEST *er) +{ + uint32_t hash; + s_ghttp *context = (s_ghttp *)client->ghttp; + + if(_is_pid_ignored(er)) + { + cs_log_dbg(D_CLIENT, "%s: ca context found in ignore list, ecm blocked: %x-%x-%x pid %x", + client->reader->label, er->onid, er->tsid, er->srvid, er->pid); + return -1; + } + + if(!context->host_id) { context->host_id = (uint8_t *)cs_strdup(client->reader->device); } + + ll_append(context->ecm_q, er); + if(ll_count(context->ecm_q) > 1) + { + cs_log_dbg(D_CLIENT, "%s: %d simultaneous ecms...", client->reader->label, ll_count(context->ecm_q)); + } + + if(_is_post_context(context->post_contexts, er, false)) + { + _ghttp_post_ecmdata(client, er); + } + else + { + hash = javastring_hashcode(er->ecm + 3, er->ecmlen - 3); + _ghttp_http_get(client, hash, er->ecm[0] == 0x81); + } + + return 0; +} + +static void ghttp_cleanup(struct s_client *client) +{ + s_ghttp *context = (s_ghttp *)client->ghttp; + + ll_destroy_data(&ghttp_ignored_contexts); + + if(context) + { + NULLFREE(context->session_id); + NULLFREE(context->host_id); + NULLFREE(context->fallback_id); + ll_destroy(&context->ecm_q); + ll_destroy_data(&context->post_contexts); + +#ifdef WITH_SSL + if(context->ssl_handle) + { + SSL_shutdown(context->ssl_handle); + SSL_free(context->ssl_handle); + } + SSL_CTX_free(ghttp_ssl_context); +#endif + NULLFREE(context); + } +} + +#ifdef HAVE_DVBAPI +static int32_t ghttp_capmt_notify(struct s_client *client, struct demux_s *demux) +{ + uint8_t req[640], lenhdr[64] = ""; + uint8_t *pids = NULL; + uint8_t *end; + char *encauth = NULL; + int32_t ret; + int8_t i, pids_len = 0, offs = 0; + s_ghttp *context = (s_ghttp *)client->ghttp; + + if(!context) + { + return -1; + } + + cs_log_dbg(D_CLIENT, "%s: capmt %x-%x-%x %d pids on adapter %d mask %x dmx index %d", + client->reader->label, demux->onid, demux->tsid, demux->program_number, demux->ECMpidcount, + demux->adapter_index, demux->ca_mask, demux->demux_index); + + if(demux->ECMpidcount > 0) + { + if(cs_malloc(&pids, demux->ECMpidcount * 8)) + { + pids_len = demux->ECMpidcount * 8; + + for(i = 0; i < demux->ECMpidcount; i++) + { + i2b_buf(2, demux->ECMpids[i].ECM_PID, pids + offs); + i2b_buf(2, demux->ECMpids[i].CAID, pids + (offs += 2)); + i2b_buf(4, demux->ECMpids[i].PROVID, pids + (offs += 2)); + offs += 4; + } + snprintf((char *)lenhdr, sizeof(lenhdr), "\r\nContent-Length: %d", pids_len); + } + else + { + return -1; + } + } + + if(!context->host_id) + { + context->host_id = (uint8_t *)cs_strdup(client->reader->device); + } + + encauth = _ghttp_basic_auth(client); + + if(encauth) // basic auth login + { + ret = snprintf((char *)req, sizeof(req), "%s /api/p/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s\r\nAuthorization: Basic %s%s\r\n\r\n", + ((pids_len > 0) ? "POST" : "GET"), demux->onid, demux->tsid, demux->program_number, + demux->ECMpidcount, demux->ens, context->host_id, encauth, lenhdr); + + NULLFREE(encauth); + } + else + { + if(context->session_id) // session exists + { + ret = snprintf((char *)req, sizeof(req), "%s /api/p/%s/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s%s\r\n\r\n", + ((pids_len > 0) ? "POST" : "GET"), context->session_id, demux->onid, demux->tsid, + demux->program_number, demux->ECMpidcount, demux->ens, context->host_id, lenhdr); + } + else // no credentials configured, assume no session required + { + ret = snprintf((char *)req, sizeof(req), "%s /api/p/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s%s\r\n\r\n", + ((pids_len > 0) ? "POST" : "GET"), demux->onid, demux->tsid, demux->program_number, + demux->ECMpidcount, demux->ens, context->host_id, lenhdr); + } + } + end = req + ret; + if(pids_len > 0) + { + memcpy(end, pids, pids_len); + cs_log_dbg(D_CLIENT, "%s: new unscrambling detected, switching to post", client->reader->label); + _set_pid_status(context->post_contexts, demux->onid, demux->tsid, demux->program_number, 0); + } + cs_log_dump_dbg(D_CLIENT, pids, pids_len, "%s: sending capmt ecm pids - %s /api/p/%x/%x/%x/%x/%x", + client->reader->label, (pids_len > 0) ? "POST" : "GET", demux->onid, demux->tsid, + demux->program_number, demux->ECMpidcount, demux->ens); + + ghttp_send(client, req, ret + pids_len); + + if(pids_len > 0) + { + NULLFREE(pids); + } + + return 0; +} +#endif + +void module_ghttp(struct s_module *ph) +{ + ph->ptab.nports = 0; + // ph->ptab.ports[0].s_port = cfg.ghttp_port; + ph->desc = "ghttp"; + ph->type = MOD_CONN_TCP; + // ph->listenertype = LIS_GHTTP; + ph->large_ecm_support = 1; + ph->recv = ghttp_recv; + ph->c_init = ghttp_client_init; + ph->c_recv_chk = ghttp_recv_chk; + ph->c_send_ecm = ghttp_send_ecm; + ph->cleanup = ghttp_cleanup; +#ifdef HAVE_DVBAPI + ph->c_capmt = ghttp_capmt_notify; +#endif + ph->num = R_GHTTP; +} +#endif diff --git a/module-lcd.c b/module-lcd.c new file mode 100644 index 0000000..9338f2d --- /dev/null +++ b/module-lcd.c @@ -0,0 +1,268 @@ +#define MODULE_LOG_PREFIX "lcd" + +#include "globals.h" + +#ifdef LCDSUPPORT +/* + * module-lcd.c + * + * Created on: 24.05.2011 + * Author: alno + */ + +#include "module-cccam.h" +#include "oscam-client.h" +#include "oscam-files.h" +#include "oscam-string.h" +#include "oscam-time.h" + +static int8_t running; +int32_t seconds, secs, fullmins, mins, hours, days, fullhours; + +static void refresh_lcd_file(void) +{ + char targetfile[256]; + char temp_file[256]; + char channame[CS_SERVICENAME_SIZE]; + + set_thread_name(__func__); + + if(cfg.lcd_output_path == NULL) + { + get_tmp_dir_filename(targetfile, sizeof(targetfile), "oscam.lcd"); + get_tmp_dir_filename(temp_file, sizeof(temp_file), "oscam.lcd.tmp"); + } + else + { + snprintf(targetfile, sizeof(targetfile), "%s%s", cfg.lcd_output_path, "/oscam.lcd"); + snprintf(temp_file, sizeof(temp_file), "%s%s.tmp", cfg.lcd_output_path, "/oscam.lcd"); + } + + int8_t iscccam = 0; + fullhours = 0; + fullmins = 0; + seconds = 0; + hours = 0; + mins = 0; + secs = 0; + days = 0; + time_t now; + + while(running) + { + now = time((time_t *)0); + int16_t idx = 0, count_r = 0, count_p = 0, count_u = 0; + FILE *fpsave; + + if((fpsave = fopen(temp_file, "w"))) + { + + idx = 0; + int16_t i; + char *type; + char *label; + char *status; + + // Statuslines start + secs = 0; + fullmins = 0; + mins = 0; + fullhours = 0; + hours = 0; + days = 0; + + seconds = now - first_client->login; + secs = seconds % 60; + if(seconds > 60) + { + fullmins = seconds / 60; + mins = fullmins % 60; + if(fullmins > 60) + { + fullhours = fullmins / 60; + hours = fullhours % 24; + days = fullhours / 24; + } + } + + fprintf(fpsave, "Version: %s\n", CS_VERSION); + if(days == 0) + { fprintf(fpsave, "up: %02d:%02d:%02d\n", hours, mins, secs); } + else + { fprintf(fpsave, "up: %02dd %02d:%02d:%02d\n", days, hours, mins, secs); } + fprintf(fpsave, "totals: %d/%d/%d/%d/%d/%d\n", first_client->cwfound, first_client->cwnot, first_client->cwignored, first_client->cwtout, first_client->cwcache, first_client->cwtun); + fprintf(fpsave, "uptime: %d\n", seconds); + // Statuslines end + + // Readertable head + fprintf(fpsave, "Typ| Label | Idle | w | s | b | e | St\n"); + fprintf(fpsave, "---+------------+--------------+---+---+---+---+----\n"); + + struct s_client *cl; + + // Reader/Proxy table start + for(i = 0, cl = first_client; cl ; cl = cl->next, i++) + { + + if((cl->typ == 'r' || cl->typ == 'p') && ((now - cl->last) < 20 || !cfg.lcd_hide_idle)) + { + type = ""; + label = ""; + status = "OFF"; + secs = 0; + fullmins = 0; + mins = 0; + fullhours = 0; + hours = 0; + days = 0; + + seconds = now - cl->last; + + if(cl->typ == 'r') + { + type = "R"; + idx = count_r; + label = cl->reader->label; + if(cl->reader->card_status == CARD_INSERTED) + { status = "OK"; } + count_r++; + } + + else if(cl->typ == 'p') + { + type = "P"; + iscccam = strncmp(client_get_proto(cl), "cccam", 5) == 0; + idx = count_p; + label = cl->reader->label; + + if(cl->reader->card_status == CARD_INSERTED) + { status = "CON"; } + + count_p++; + } + + + secs = seconds % 60; + if(seconds > 60) + { + fullmins = seconds / 60; + mins = fullmins % 60; + if(fullmins > 60) + { + fullhours = fullmins / 60; + hours = fullhours % 24; + days = fullhours / 24; + } + } + + uint16_t written = 0, skipped = 0, blocked = 0, error = 0; + + char emmtext[16] = " "; + if(cl->typ == 'r' || !iscccam) + { + for(i = 0; i < 4; i++) + { + error += cl->reader->emmerror[i]; + blocked += cl->reader->emmblocked[i]; + skipped += cl->reader->emmskipped[i]; + written += cl->reader->emmwritten[i]; + } + snprintf(emmtext, 16, "%3d|%3d|%3d|%3d", + written > 999 ? 999 : written, + skipped > 999 ? 999 : skipped, + blocked > 999 ? 999 : blocked, + error > 999 ? 999 : error); + } + else if(cl->typ == 'p' && iscccam) + { + if(!cccam_snprintf_cards_stat(cl, emmtext, 16)) + { snprintf(emmtext, 16, " No cards "); } + } + + if(days == 0) + { + fprintf(fpsave, "%s%d | %-10.10s | %02d:%02d:%02d |%s| %s\n", + type, idx, label, hours, mins, + secs, emmtext, status); + } + else + { + fprintf(fpsave, "%s%d | %-10.10s |% 3dd %02d:%02d:%02d |%s| %s\n", + type, idx, label, days, hours, mins, + secs, emmtext, status); + } + } + } + + fprintf(fpsave, "---+------------+--------------+---+---+---+--++----\n"); + // Reader/Proxy table end + + + // Usertable start + fprintf(fpsave, "Typ| Label | Channel | Time\n"); + fprintf(fpsave, "---+------------+-----------------------------+-----\n"); + + /* + //Testclient + fprintf(fpsave,"%s%d | %-10.10s | %-10.10s:%-17.17s| % 4d\n", + "U", + 1, + "test", + "Sky De", + "Discovery Channel", + 568); + + */ + + for(i = 0, cl = first_client; cl ; cl = cl->next, i++) + { + + seconds = now - cl->lastecm; + + if(cl->typ == 'c' && seconds < 15) + { + type = "U"; + idx = count_u; + label = cl->account->usr; + count_u++; + + get_servicename(cl, cl->last_srvid, cl->last_provid, cl->last_caid, channame, sizeof(channame)); + fprintf(fpsave, "%s%d | %-10.10s | %-10.10s:%-17.17s| % 4d\n", + type, + idx, + label, + get_cl_lastprovidername(cl), + cl->last_srvidptr && cl->last_srvidptr->name ? cl->last_srvidptr->name : "", + cl->cwlastresptime); + + } + } + fprintf(fpsave, "---+------------+-----------------------------+-----\n"); + // Usertable end + fclose(fpsave); + } + + cs_sleepms(cfg.lcd_write_intervall * 1000); + + if(rename(temp_file, targetfile) < 0) + { cs_log("An error occured while writing oscam.lcd file %s.", targetfile); } + + } + +} + +void lcd_thread_start(void) +{ + if(cfg.enablelcd) + { + running = 1; + start_thread("LCD", (void *) &refresh_lcd_file, NULL, NULL, 1, 1); + } +} + +void lcd_thread_stop(void) +{ + running = 0; +} + +#endif diff --git a/module-lcd.h b/module-lcd.h new file mode 100644 index 0000000..aba388e --- /dev/null +++ b/module-lcd.h @@ -0,0 +1,12 @@ +#ifndef MODULE_LCD_H_ +#define MODULE_LCD_H_ + +#ifdef LCDSUPPORT +extern void lcd_thread_start(void); +extern void lcd_thread_stop(void); +#else +static inline void lcd_thread_start(void) { } +static inline void lcd_thread_stop(void) { } +#endif + +#endif diff --git a/module-led.c b/module-led.c new file mode 100644 index 0000000..a3ebcb8 --- /dev/null +++ b/module-led.c @@ -0,0 +1,389 @@ +#define MODULE_LOG_PREFIX "led" + +#include "globals.h" + +#ifdef LEDSUPPORT + +#include "module-led.h" +#include "oscam-string.h" +#include "oscam-time.h" + +#if defined(__arm__) +struct s_arm_led +{ + int32_t led; + int32_t action; + time_t start_time; +}; + +static pthread_t arm_led_thread; +static LLIST *arm_led_actions; + +#define ARM_LED_TYPES 3 +#define ARM_LED_FILES 4 + +struct arm_leds +{ + char *machine; + struct led_file + { + uint8_t id; + char *file; + } leds[ARM_LED_FILES]; +}; + +static const struct arm_leds arm_leds[ARM_LED_TYPES] = +{ + { + "nslu2", { + { LED1A, "red:status" }, + { LED1B, "green:ready" }, + { LED2, "green:disk-1" }, + { LED3, "green:disk-2" } + }, + }, + { + "dockstar", { + { LED1A, "orange:misc" }, + { LED1B, "green:health" }, + { LED2, "green:health" }, + { LED3, "orange:misc" } + }, + }, + { + "wrt350nv2", { + { LED1A, "orange:power" }, + { LED1B, "green:power" }, + { LED2, "green:wireless" }, + { LED3, "green:security" } + }, + } +}; + +static int32_t arm_init_led_file(uint8_t led_type, uint8_t led_no, char *buf, int32_t buflen) +{ + uint8_t i; + if(led_type >= ARM_LED_TYPES) { return 0; } + if(led_no >= ARM_LED_FILES) { return 0; } + for(i = 0; i < ARM_LED_FILES; i++) + { + if(arm_leds[led_type].leds[i].id == led_no) + { + return snprintf(buf, buflen, "/sys/class/leds/%s:%s/brightness", + arm_leds[led_type].machine, arm_leds[led_type].leds[i].file); + } + } + return 0; +} + +#define LED_TYPE_UNKNOWN 0xff +static uint8_t arm_led_type = LED_TYPE_UNKNOWN; + +static void arm_detect_led_type(void) +{ + uint8_t i; + char led_file[256]; + for(i = 0; i < ARM_LED_TYPES; i++) + { + if(!arm_init_led_file(i, 0, led_file, sizeof(led_file))) + { break; } + if(access(led_file, W_OK) == 0) + { + arm_led_type = i; + cs_log("LED support for %s is activated.", arm_leds[arm_led_type].machine); + break; + } + } + if(arm_led_type == LED_TYPE_UNKNOWN) + { cs_log("LED support is not active. Can't detect machine type."); } +} + +static void arm_switch_led_from_thread(int32_t led, int32_t action) +{ + if(action < 2) // only LED_ON and LED_OFF + { + char led_file[256]; + if(!arm_init_led_file(arm_led_type, led, led_file, sizeof(led_file))) + { return; } + FILE *f = fopen(led_file, "w"); + if(!f) + { return; } + fprintf(f, "%d", action); + fclose(f); + } + else // LED Macros + { + switch(action) + { + case LED_DEFAULT: + arm_switch_led_from_thread(LED1A, LED_OFF); + arm_switch_led_from_thread(LED1B, LED_OFF); + arm_switch_led_from_thread(LED2, LED_ON); + arm_switch_led_from_thread(LED3, LED_OFF); + break; + case LED_BLINK_OFF: + arm_switch_led_from_thread(led, LED_OFF); + cs_sleepms(100); + arm_switch_led_from_thread(led, LED_ON); + break; + case LED_BLINK_ON: + arm_switch_led_from_thread(led, LED_ON); + cs_sleepms(300); + arm_switch_led_from_thread(led, LED_OFF); + break; + } + } +} + +static void *arm_led_thread_main(void *UNUSED(thread_data)) +{ + uint8_t running = 1; + set_thread_name(__func__); + while(running) + { + LL_ITER iter = ll_iter_create(arm_led_actions); + struct s_arm_led *arm_led; + while((arm_led = ll_iter_next(&iter))) + { + int32_t led, action; + time_t now, start; + led = arm_led->led; + action = arm_led->action; + now = time(0); + start = arm_led->start_time; + ll_iter_remove_data(&iter); + if(action == LED_STOP_THREAD) + { + running = 0; + break; + } + if(now - start < ARM_LED_TIMEOUT) + { + arm_switch_led_from_thread(led, action); + } + } + if(running) + { + sleep(60); + } + } + ll_clear_data(arm_led_actions); + pthread_exit(NULL); + return NULL; +} + +static void arm_led_start_thread(void) +{ + arm_detect_led_type(); + if(!cfg.enableled || arm_led_type == LED_TYPE_UNKNOWN) + { return; } + // call this after signal handling is done + if(!arm_led_actions) + { + arm_led_actions = ll_create("arm_led_actions"); + } + + start_thread("arm led", arm_led_thread_main, NULL, &arm_led_thread, 1, 1); +} + +static void arm_led(int32_t led, int32_t action) +{ + struct s_arm_led *data; + if(!cfg.enableled || arm_led_type == LED_TYPE_UNKNOWN) + { return; } + if(!arm_led_actions) + { + arm_led_actions = ll_create("arm_led_actions"); + } + if(cs_malloc(&data, sizeof(struct s_arm_led))) + { + data->start_time = time(0); + data->led = led; + data->action = action; + ll_append(arm_led_actions, (void *)data); + } + if(arm_led_thread) + { + // arm_led_thread_main is not started at oscam startup + // when first arm_led calls happen + pthread_kill(arm_led_thread, OSCAM_SIGNAL_WAKEUP); + } +} + +static void arm_led_stop_thread(void) +{ + if(!cfg.enableled || arm_led_type == LED_TYPE_UNKNOWN) + { return; } + arm_led(0, LED_STOP_THREAD); +} +#else +static inline void arm_led_start_thread(void) { } +static inline void arm_led_stop_thread(void) { } +static inline void arm_led(int32_t UNUSED(led), int32_t UNUSED(action)) { } +#endif + + +#ifdef QBOXHD +static void qboxhd_led_blink(int32_t color, int32_t duration) +{ + int32_t f; + if(cfg.enableled != 2) + { return; } + // try QboxHD-MINI first + if((f = open(QBOXHDMINI_LED_DEVICE, O_RDWR | O_NONBLOCK)) > -1) + { + qboxhdmini_led_color_struct qbminiled; + uint32_t qboxhdmini_color = 0x000000; + if(color != QBOXHD_LED_COLOR_OFF) + { + switch(color) + { + case QBOXHD_LED_COLOR_RED: + qboxhdmini_color = QBOXHDMINI_LED_COLOR_RED; + break; + case QBOXHD_LED_COLOR_GREEN: + qboxhdmini_color = QBOXHDMINI_LED_COLOR_GREEN; + break; + case QBOXHD_LED_COLOR_BLUE: + qboxhdmini_color = QBOXHDMINI_LED_COLOR_BLUE; + break; + case QBOXHD_LED_COLOR_YELLOW: + qboxhdmini_color = QBOXHDMINI_LED_COLOR_YELLOW; + break; + case QBOXHD_LED_COLOR_MAGENTA: + qboxhdmini_color = QBOXHDMINI_LED_COLOR_MAGENTA; + break; + } + // set LED on with color + qbminiled.red = (uint8_t)((qboxhdmini_color & 0xFF0000) >> 16); // R + qbminiled.green = (uint8_t)((qboxhdmini_color & 0x00FF00) >> 8); // G + qbminiled.blue = (uint8_t)(qboxhdmini_color & 0x0000FF); // B + ioctl(f, QBOXHDMINI_IOSET_RGB, &qbminiled); + cs_sleepms(duration); + } + // set LED off + qbminiled.red = 0; + qbminiled.green = 0; + qbminiled.blue = 0; + ioctl(f, QBOXHDMINI_IOSET_RGB, &qbminiled); + close(f); + } + else if((f = open(QBOXHD_LED_DEVICE, O_RDWR | O_NONBLOCK)) > -1) + { + qboxhd_led_color_struct qbled; + if(color != QBOXHD_LED_COLOR_OFF) + { + // set LED on with color + qbled.H = color; + qbled.S = 99; + qbled.V = 99; + ioctl(f, QBOXHD_SET_LED_ALL_PANEL_COLOR, &qbled); + cs_sleepms(duration); + } + // set LED off + qbled.H = 0; + qbled.S = 0; + qbled.V = 0; + ioctl(f, QBOXHD_SET_LED_ALL_PANEL_COLOR, &qbled); + close(f); + } +} +#else +static inline void qboxhd_led_blink(int32_t UNUSED(color), int32_t UNUSED(duration)) { } +#endif + +void led_status_stopping(void) +{ + if(cfg.enableled == 1) + { + arm_led(LED1B, LED_OFF); + arm_led(LED2, LED_OFF); + arm_led(LED3, LED_OFF); + arm_led(LED1A, LED_ON); + } + if(cfg.enableled == 2) + { + qboxhd_led_blink(QBOXHD_LED_COLOR_YELLOW, QBOXHD_LED_BLINK_FAST); + qboxhd_led_blink(QBOXHD_LED_COLOR_RED, QBOXHD_LED_BLINK_FAST); + qboxhd_led_blink(QBOXHD_LED_COLOR_GREEN, QBOXHD_LED_BLINK_FAST); + qboxhd_led_blink(QBOXHD_LED_COLOR_BLUE, QBOXHD_LED_BLINK_FAST); + qboxhd_led_blink(QBOXHD_LED_COLOR_MAGENTA, QBOXHD_LED_BLINK_FAST); + } +} + +void led_status_cw_not_found(ECM_REQUEST *er) +{ + if(!er->rc) + { arm_led(LED2, LED_BLINK_OFF); } + if(er->rc < E_NOTFOUND) + { + qboxhd_led_blink(QBOXHD_LED_COLOR_GREEN, QBOXHD_LED_BLINK_MEDIUM); + } + else if(er->rc <= E_STOPPED) + { + qboxhd_led_blink(QBOXHD_LED_COLOR_RED, QBOXHD_LED_BLINK_MEDIUM); + } +} + +void led_status_default(void) +{ + arm_led(LED1A, LED_DEFAULT); + arm_led(LED1A, LED_ON); +} + +void led_status_starting(void) +{ + arm_led(LED1A, LED_OFF); + arm_led(LED1B, LED_ON); + qboxhd_led_blink(QBOXHD_LED_COLOR_YELLOW, QBOXHD_LED_BLINK_FAST); + qboxhd_led_blink(QBOXHD_LED_COLOR_RED, QBOXHD_LED_BLINK_FAST); + qboxhd_led_blink(QBOXHD_LED_COLOR_GREEN, QBOXHD_LED_BLINK_FAST); + qboxhd_led_blink(QBOXHD_LED_COLOR_BLUE, QBOXHD_LED_BLINK_FAST); + qboxhd_led_blink(QBOXHD_LED_COLOR_MAGENTA, QBOXHD_LED_BLINK_FAST); +} + +void led_status_card_activation_error(void) +{ + qboxhd_led_blink(QBOXHD_LED_COLOR_MAGENTA, QBOXHD_LED_BLINK_MEDIUM); +} + +void led_status_found_cardsystem(void) +{ + qboxhd_led_blink(QBOXHD_LED_COLOR_YELLOW, QBOXHD_LED_BLINK_MEDIUM); + qboxhd_led_blink(QBOXHD_LED_COLOR_GREEN, QBOXHD_LED_BLINK_MEDIUM); + qboxhd_led_blink(QBOXHD_LED_COLOR_YELLOW, QBOXHD_LED_BLINK_MEDIUM); + qboxhd_led_blink(QBOXHD_LED_COLOR_GREEN, QBOXHD_LED_BLINK_MEDIUM); +} + +void led_status_unsupported_card_system(void) +{ + qboxhd_led_blink(QBOXHD_LED_COLOR_MAGENTA, QBOXHD_LED_BLINK_MEDIUM); +} + +void led_status_card_detected(void) +{ + qboxhd_led_blink(QBOXHD_LED_COLOR_YELLOW, QBOXHD_LED_BLINK_SLOW); +} + +void led_status_card_ejected(void) +{ + qboxhd_led_blink(QBOXHD_LED_COLOR_RED, QBOXHD_LED_BLINK_SLOW); +} + +void led_status_emm_ok(void) +{ + arm_led(LED3, LED_BLINK_ON); + qboxhd_led_blink(QBOXHD_LED_COLOR_BLUE, QBOXHD_LED_BLINK_MEDIUM); +} + +void led_init(void) +{ + arm_led_start_thread(); +} + +void led_stop(void) +{ + arm_led_stop_thread(); +} + +#endif diff --git a/module-led.h b/module-led.h new file mode 100644 index 0000000..26231f1 --- /dev/null +++ b/module-led.h @@ -0,0 +1,83 @@ +#ifndef MODULE_LED_H_ +#define MODULE_LED_H_ + +#define LED1A 0 +#define LED1B 1 +#define LED2 2 +#define LED3 3 +#define LED_OFF 0 +#define LED_ON 1 +#define LED_BLINK_ON 2 +#define LED_BLINK_OFF 3 +#define LED_DEFAULT 10 +#define LED_STOP_THREAD 100 +#define ARM_LED_TIMEOUT 3 // Don't blink for actions which are < ARM_LED_TIMEOUT seconds ago + +// QBOX led structures +typedef struct +{ + uint16_t H; // range 0-359 + uint8_t S; // range 0-99 + uint8_t V; // range 0-99 +} qboxhd_led_color_struct; + +typedef struct +{ + uint8_t red; // first 5 bit used (&0x1F) + uint8_t green; // first 5 bit used (&0x1F) + uint8_t blue; // first 5 bit used (&0x1F) +} qboxhdmini_led_color_struct; + +#define QBOXHD_LED_DEVICE "/dev/sw0" +#define QBOXHD_SET_LED_ALL_PANEL_COLOR _IO(0xBC, 13) // payload = 3byte [H][S][V] + +#define QBOXHD_LED_COLOR_RED 359 // only H value, S and V values are always == 99 +#define QBOXHD_LED_COLOR_GREEN 120 +#define QBOXHD_LED_COLOR_BLUE 230 +#define QBOXHD_LED_COLOR_YELLOW 55 +#define QBOXHD_LED_COLOR_MAGENTA 290 + +#define QBOXHDMINI_LED_DEVICE "/dev/lpc_0" +#define QBOXHDMINI_IOSET_RGB _IOWR('L', 6, qboxhdmini_led_color_struct) + +#define QBOXHDMINI_LED_COLOR_RED 0x1F0000 // 3 bytes RGB, 5 bit used for each color +#define QBOXHDMINI_LED_COLOR_GREEN 0x001F00 +#define QBOXHDMINI_LED_COLOR_BLUE 0x00001F +#define QBOXHDMINI_LED_COLOR_YELLOW 0x1F1F00 +#define QBOXHDMINI_LED_COLOR_MAGENTA 0x1F001F + +#define QBOXHD_LED_COLOR_OFF -1 // all colors H, S, V and/or R, G, B == 0, 0, 0 + +#define QBOXHD_LED_BLINK_FAST 100 // blink milliseconds +#define QBOXHD_LED_BLINK_MEDIUM 200 +#define QBOXHD_LED_BLINK_SLOW 400 + +#ifdef LEDSUPPORT +extern void led_init(void); +extern void led_stop(void); +extern void led_status_stopping(void); +extern void led_status_cw_not_found(ECM_REQUEST *er); +extern void led_status_default(void); +extern void led_status_starting(void); +extern void led_status_card_activation_error(void); +extern void led_status_found_cardsystem(void); +extern void led_status_unsupported_card_system(void); +extern void led_status_emm_ok(void); +extern void led_status_card_detected(void); +extern void led_status_card_ejected(void); +#else +static inline void led_init(void) { } +static inline void led_stop(void) { } +static inline void led_status_stopping(void) { } +static inline void led_status_cw_not_found(ECM_REQUEST *UNUSED(er)) { } +static inline void led_status_default(void) { } +static inline void led_status_starting(void) { } +static inline void led_status_card_activation_error(void) { } +static inline void led_status_found_cardsystem(void) { } +static inline void led_status_unsupported_card_system(void) { } +static inline void led_status_emm_ok(void) { } +static inline void led_status_card_detected(void) { } +static inline void led_status_card_ejected(void) { } +#endif + +#endif diff --git a/module-monitor.c b/module-monitor.c new file mode 100644 index 0000000..10be173 --- /dev/null +++ b/module-monitor.c @@ -0,0 +1,999 @@ +#define MODULE_LOG_PREFIX "monitor" + +#include "globals.h" +#ifdef MODULE_MONITOR +#include "cscrypt/md5.h" +#include "module-monitor.h" +#include "oscam-aes.h" +#include "oscam-array.h" +#include "oscam-client.h" +#include "oscam-config.h" +#include "oscam-conf-chk.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-work.h" + +extern char *entitlement_type[]; + +struct monitor_data +{ + bool auth; + uint8_t ucrc[4]; + struct aes_keys aes_keys; + int32_t seq; + int32_t counter; + char btxt[256]; +}; + +static int8_t monitor_check_ip(void) +{ + int32_t ok = 0; + struct s_client *cur_cl = cur_client(); + struct monitor_data *module_data = cur_cl->module_data; + + if(module_data->auth) { return 0; } + ok = check_ip(cfg.mon_allowed, cur_cl->ip); + if(!ok) + { + cs_auth_client(cur_cl, (struct s_auth *)0, "invalid ip"); + return -1; + } + return 0; +} + +static int8_t monitor_auth_client(char *usr, char *pwd) +{ + struct s_auth *account; + struct s_client *cur_cl = cur_client(); + struct monitor_data *module_data = cur_cl->module_data; + + if(module_data->auth) { return 0; } + if((!usr) || (!pwd)) + { + cs_auth_client(cur_cl, (struct s_auth *)0, NULL); + return -1; + } + for(account = cfg.account; account; account = account->next) + { + if(account->monlvl && streq(usr, account->usr) && streq(pwd, account->pwd)) + { + module_data->auth = 1; + break; + } + } + if(!module_data->auth) + { + cs_auth_client(cur_cl, (struct s_auth *)0, "invalid account"); + return -1; + } + if(cs_auth_client(cur_cl, account, NULL)) + { return -1; } + return 0; +} + +static int32_t secmon_auth_client(uint8_t *ucrc) +{ + uint32_t crc; + struct s_auth *account; + struct s_client *cur_cl = cur_client(); + struct monitor_data *module_data = cur_cl->module_data; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + + if(module_data->auth) + { + int32_t s = memcmp(module_data->ucrc, ucrc, 4); + if(s) + { cs_log("wrong user-crc or garbage !?"); } + return !s; + } + + cur_cl->crypted = 1; + crc = (ucrc[0] << 24) | (ucrc[1] << 16) | (ucrc[2] << 8) | ucrc[3]; + + for(account = cfg.account; (account) && (!module_data->auth); account = account->next) + { + if((account->monlvl) && + (crc == crc32(0L, MD5((uint8_t *)account->usr, cs_strlen(account->usr), md5tmp), MD5_DIGEST_LENGTH))) + { + memcpy(module_data->ucrc, ucrc, 4); + aes_set_key(&module_data->aes_keys, (char *)MD5((uint8_t *)ESTR(account->pwd), cs_strlen(ESTR(account->pwd)), md5tmp)); + if(cs_auth_client(cur_cl, account, NULL)) + { return -1; } + module_data->auth = 1; + } + } + + if(!module_data->auth) + { + cs_auth_client(cur_cl, (struct s_auth *)0, "invalid user"); + return -1; + } + return module_data->auth; +} + +int32_t monitor_send_idx(struct s_client *cl, char *txt) +{ + struct monitor_data *module_data = cl->module_data; + int32_t l; + uint8_t buf[256 + 32]; + if(!cl->udp_fd) + { return -1; } + struct timespec req_ts; + req_ts.tv_sec = 0; + req_ts.tv_nsec = 500000; + nanosleep(&req_ts, NULL); // avoid lost udp-pakkets + if(!cl->crypted) + { return sendto(cl->udp_fd, txt, cs_strlen(txt), 0, (struct sockaddr *)&cl->udp_sa, cl->udp_sa_len); } + l = cs_strlen(txt); + if(l > 255) + { l = 255; } + buf[0] = '&'; + buf[9] = l; + l = boundary(4, l + 5) + 5; + memcpy(buf + 1, module_data->ucrc, 4); + cs_strncpy((char *)buf + 10, txt, sizeof(buf) - 10); + memset(buf+10+buf[9], 0, l-10-buf[9]); + uint8_t tmp[10]; + memcpy(buf + 5, i2b_buf(4, crc32(0L, buf + 10, l - 10), tmp), 4); + aes_encrypt_idx(&module_data->aes_keys, buf + 5, l - 5); + return sendto(cl->udp_fd, buf, l, 0, (struct sockaddr *)&cl->udp_sa, cl->udp_sa_len); +} + +#define monitor_send(t) monitor_send_idx(cur_client(), t) + +static int32_t monitor_recv(struct s_client *client, uint8_t *buf, int32_t UNUSED(buflen)) +{ + int32_t n = recv_from_udpipe(buf); + if(!n) + { return buf[0] = 0; } + if(!client->module_data && !cs_malloc(&client->module_data, sizeof(struct monitor_data))) + { return 0; } + if(buf[0] == '&') + { + int32_t bsize; + if(n < 21) // 5+16 is minimum + { + cs_log("packet too small!"); + return buf[0] = 0; + } + int32_t res = secmon_auth_client(buf + 1); + if(res == -1) + { + cs_disconnect_client(client); + return 0; + } + if(!res) + { + return buf[0] = 0; + } + struct monitor_data *module_data = client->module_data; + aes_decrypt(&module_data->aes_keys, buf + 5, 16); + bsize = boundary(4, buf[9] + 5) + 5; + if(n < bsize) + { + cs_log("packet-size mismatch !"); + return buf[0] = 0; + } + aes_decrypt(&module_data->aes_keys, buf + 21, n - 21); + uint8_t tmp[10]; + if(memcmp(buf + 5, i2b_buf(4, crc32(0L, buf + 10, n - 10), tmp), 4)) + { + cs_log("CRC error ! wrong password ?"); + return buf[0] = 0; + } + n = buf[9]; + memmove(buf, buf + 10, n); + } + else + { + if(monitor_check_ip() == -1) + { + cs_disconnect_client(client); + return 0; + } + } + buf[n] = '\0'; + n = cs_strlen(trim((char *)buf)); + if(n) { client->last = time((time_t *) 0); } + return n; +} + +static void monitor_send_info(char *txt, int32_t last) +{ + struct s_client *cur_cl = cur_client(); + struct monitor_data *module_data = cur_cl->module_data; + char buf[16]; + if(txt) + { + if(!module_data->btxt[0]) + { + module_data->counter = 0; + txt[2] = 'B'; + } + else + { module_data->counter++; } + snprintf(buf, sizeof(buf), "%03d", module_data->counter); + memcpy(txt + 4, buf, 3); + txt[3] = '0' + module_data->seq; + } + else if(!last) + { return; } + + if(!last) + { + if(module_data->btxt[0]) { monitor_send(module_data->btxt); } + cs_strncpy(module_data->btxt, txt, sizeof(module_data->btxt)); + return; + } + + if(txt && module_data->btxt[0]) + { + monitor_send(module_data->btxt); + txt[2] = 'E'; + cs_strncpy(module_data->btxt, txt, sizeof(module_data->btxt)); + } + else + { + if(txt) + { cs_strncpy(module_data->btxt, txt, sizeof(module_data->btxt)); } + module_data->btxt[2] = (module_data->btxt[2] == 'B') ? 'S' : 'E'; + } + + if(module_data->btxt[0]) + { + monitor_send(module_data->btxt); + module_data->seq = (module_data->seq + 1) % 10; + } + module_data->btxt[0] = 0; +} + +static char *monitor_client_info(char id, struct s_client *cl, char *sbuf) +{ + char channame[CS_SERVICENAME_SIZE]; + sbuf[0] = '\0'; + + if(cl) + { + char ldate[20], ltime[16]; + const char *usr; + int32_t lsec, isec, con, cau, lrt = - 1; + time_t now; + struct tm lt; + now = time((time_t *)0); + + if((cfg.hideclient_to <= 0) || + (now - cl->lastecm < cfg.hideclient_to) || + (now - cl->lastemm < cfg.hideclient_to) || + (cl->typ != 'c')) + { + lsec = now - cl->login; + isec = now - cl->last; + usr = username(cl); + if(cl->dup) + { con = 2; } + else if((cl->tosleep) && (now - cl->lastswitch > cl->tosleep)) + { con = 1; } + else + { con = 0; } + + // no AU reader == 0 / AU ok == 1 / Last EMM > aulow == -1 + if(cl->typ == 'c' || cl->typ == 'p' || cl->typ == 'r') + { + if((cl->typ == 'c' && ll_count(cl->aureader_list) == 0) || + ((cl->typ == 'p' || cl->typ == 'r') && cl->reader->audisabled)) + { cau = 0; } + + else if((now - cl->lastemm) / 60 > cfg.aulow) + { cau = (-1); } + + else + { cau = 1; } + } + else + { + cau = 0; + } + + if(cl->typ == 'r') + { + int32_t i; + struct s_reader *rdr; + for(i = 0, rdr = first_active_reader; rdr ; rdr = rdr->next, i++) + if(cl->reader == rdr) + { lrt = i; } + + if(lrt >= 0) + { + lrt = 10 + cl->reader->card_status; + } + } + else + { + lrt = cl->cwlastresptime; + } + localtime_r(&cl->login, <); + int32_t cnr = get_threadnum(cl); + snprintf(ldate, sizeof(ldate), " %02d.%02d.%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100); + snprintf(ltime, sizeof(ltime), "%02d:%02d:%02d", lt.tm_hour, lt.tm_min, lt.tm_sec); + snprintf(sbuf, 256, "[%c--CCC]%8X|%c|%d|%s|%d|%d|%s|%d|%s|%s|%s|%d|%04X@%06X:%04X|%s|%d|%d|%d|%d|%d|%d|%d|%d|%d|%d\n", + id, cl->tid, cl->typ, cnr, usr, cau, cl->crypted, + cs_inet_ntoa(cl->ip), cl->port, client_get_proto(cl), + ldate, ltime, lsec, cl->last_caid, cl->last_provid, cl->last_srvid, + get_servicename_or_null(cl, cl->last_srvid, cl->last_provid, cl->last_caid, channame, sizeof(channame)), isec, con, + cl->cwfound, cl->cwnot, cl->cwcache, cl->cwignored, + cl->cwtout, cl->emmok, cl->emmnok, lrt); + } + } + return sbuf; +} + +static void monitor_process_info(void) +{ + time_t now = time((time_t *)0); + char sbuf[256]; + struct s_client *cl, *cur_cl = cur_client(); + + for(cl = first_client; cl ; cl = cl->next) + { + if((cfg.hideclient_to <= 0) || + (now - cl->lastecm < cfg.hideclient_to) || + (now - cl->lastemm < cfg.hideclient_to) || + (cl->typ != 'c')) + { + if((cur_cl->monlvl < 2) && (cl->typ != 's')) + { + if((cur_cl->account && cl->account && strcmp(cur_cl->account->usr, cl->account->usr)) || + ((cl->typ != 'c') && (cl->typ != 'm'))) + { continue; } + } + monitor_send_info(monitor_client_info('I', cl, sbuf), 0); + } + } + monitor_send_info(NULL, 1); +} + +static void monitor_send_details(char *txt, uint32_t tid) +{ + char buf[512]; + snprintf(buf, sizeof(buf), "[D-----]%8X|%s\n", tid, txt); + monitor_send_info(buf, 0); +} + +static void monitor_send_details_version(void) +{ + char buf[256]; + snprintf(buf, sizeof(buf), "[V-0000]version=%s, system=%s\n", CS_VERSION, CS_TARGET); + monitor_send_info(buf, 1); +} + +static void monitor_send_keepalive_ack(void) +{ + char buf[32]; + snprintf(buf, sizeof(buf), "[K-0000]keepalive_ack\n"); + monitor_send_info(buf, 1); +} + +static void monitor_process_details_master(char *buf, uint32_t pid) +{ + snprintf(buf, 256, "Version=%s", CS_VERSION); + monitor_send_details(buf, pid); + snprintf(buf, 256, "System=%s", CS_TARGET); + monitor_send_details(buf, pid); + snprintf(buf, 256, "DebugLevel=%d", cs_dblevel); + monitor_send_details(buf, pid); + snprintf(buf, 256, "MaxClients=UNLIMITED"); + monitor_send_details(buf, pid); + snprintf(buf, 256, "ClientMaxIdle=%d sec", cfg.cmaxidle); + monitor_send_details(buf, pid); + if(cfg.max_log_size) + { + snprintf(buf, 256, "MaxLogsize=%d Kb", cfg.max_log_size); + } + else + { + snprintf(buf, 256, "MaxLogsize=unlimited"); + } + monitor_send_details(buf, pid); + snprintf(buf, 256, "ClientTimeout=%u ms", cfg.ctimeout); + monitor_send_details(buf, pid); + snprintf(buf, 256, "CacheDelay=%d ms", cfg.delay); + monitor_send_details(buf, pid); + if(cfg.cwlogdir) + { + snprintf(buf, 256, "CwlogDir=%s", cfg.cwlogdir); + monitor_send_details(buf, pid); + } + if(cfg.preferlocalcards) + { + snprintf(buf, 256, "PreferlocalCards=%d", cfg.preferlocalcards); + monitor_send_details(buf, pid); + } + if(cfg.waitforcards) + { + snprintf(buf, 256, "WaitforCards=%d", cfg.waitforcards); + monitor_send_details(buf, pid); + } + snprintf(buf, 256, "LogFile=%s", cfg.logfile); + monitor_send_details(buf, pid); + if(cfg.mailfile) + { + snprintf(buf, 256, "MailFile=%s", cfg.mailfile); + monitor_send_details(buf, pid); + } + if(cfg.usrfile) + { + snprintf(buf, 256, "UsrFile=%s", cfg.usrfile); + monitor_send_details(buf, pid); + } + monitor_send_details(buf, pid); + snprintf(buf, 256, "Sleep=%d", cfg.tosleep); + monitor_send_details(buf, pid); + snprintf(buf, 256, "Monitorport=%d", cfg.mon_port); + monitor_send_details(buf, pid); + snprintf(buf, 256, "Nice=%d", cfg.nice); + monitor_send_details(buf, pid); +#ifdef WEBIF + snprintf(buf, 256, "Restartmode=%d", cs_get_restartmode()); + monitor_send_details(buf, pid); +#else + snprintf(buf, 256, "Restartmode=%s", "no"); + monitor_send_details(buf, pid); +#endif + + // monitor_send_details(buf, pid); +} + + +static void monitor_process_details_reader(struct s_client *cl) +{ + char tbuffer1[64], tbuffer2[64], buf[256] = { 0 }, tmpbuf[256] = { 0 }, valid_to[32] = { 0 }; + struct s_reader *rdr = cl->reader; + if(!rdr) + { + monitor_send_details("Reader do not exist or it is not started.", cl->tid); + return; + } + + if(rdr->card_valid_to) + { + struct tm vto_t; + localtime_r(&rdr->card_valid_to, &vto_t); + strftime(valid_to, sizeof(valid_to) - 1, "%Y-%m-%d", &vto_t); + } + else + { + cs_strncpy(valid_to, "n/a", sizeof(valid_to)); + } + + snprintf(tmpbuf, sizeof(tmpbuf) - 1, "Cardsystem: %s Reader: %s ValidTo: %s HexSerial: %s ATR: %s", + rdr->csystem ? rdr->csystem->desc : "-", + rdr->label, + valid_to, + cs_hexdump(1, rdr->hexserial, 8, tbuffer2, sizeof(tbuffer2)), + rdr->card_atr_length + ? cs_hexdump(1, rdr->card_atr, rdr->card_atr_length, buf, sizeof(buf)) + : "" + ); + monitor_send_details(tmpbuf, cl->tid); + + if(!rdr->ll_entitlements) + { + monitor_send_details("No entitlements for the reader.", cl->tid); + return; + } + + S_ENTITLEMENT *item; + LL_ITER itr = ll_iter_create(rdr->ll_entitlements); + time_t now = (time(NULL) / 86400) * 86400; + + while((item = ll_iter_next(&itr))) + { + struct tm start_t, end_t; + + localtime_r(&item->start, &start_t); + localtime_r(&item->end , &end_t); + + strftime(tbuffer1, sizeof(tbuffer1) - 1, "%Y-%m-%d %H:%M %z", &start_t); + strftime(tbuffer2, sizeof(tbuffer2) - 1, "%Y-%m-%d %H:%M %z", &end_t); + + char *entresname = get_tiername(item->id & 0xFFFF, item->caid, buf); + if(!entresname[0]) + { entresname = get_provider(item->provid, item->caid, buf, sizeof(buf)); } + + snprintf(tmpbuf, sizeof(tmpbuf) - 1, "%s Type: %s CAID: %04X Provid: %06X ID: %08X%08X Class: %08X StartDate: %s ExpireDate: %s Name: %s", + item->end > now ? "active " : "expired", + entitlement_type[item->type], + item->caid, + item->provid, + (uint32_t)(item->id >> 32), + (uint32_t)(item->id), + item->class, + tbuffer1, + tbuffer2, + entresname + ); + monitor_send_details(tmpbuf, cl->tid); + } +} + + +static void monitor_process_details(char *arg) +{ + uint32_t tid = 0; //using threadid 8 positions hex see oscam-log.c //FIXME untested but pid isnt working anyway with threading + struct s_client *cl = NULL, *cl1; + char sbuf[256]; + + if(!arg) + { cl = first_client; } // no arg - show master + else + { + if(sscanf(arg, "%X", &tid) == 1) + { + for(cl1 = first_client; cl1 ; cl1 = cl1->next) + if(cl1->tid == tid) + { + cl = cl1; + break; + } + } + } + + if(!cl) + { monitor_send_details("Invalid TID", tid); } + else + { + //monitor_send_info(monitor_client_info('D', idx), 0); // FIXME + switch(cl->typ) + { + case 's': + monitor_process_details_master(sbuf, cl->tid); + break; + case 'c': + case 'm': + monitor_send_details(monitor_client_info(1, cl, sbuf), cl->tid); + break; + case 'r': + monitor_process_details_reader(cl); // with client->typ='r' client->ridx is always filled and valid, so no need checking + break; + case 'p': + monitor_send_details(monitor_client_info(1, cl, sbuf), cl->tid); + break; + } + } + monitor_send_info(NULL, 1); +} + +static void monitor_send_login(void) +{ + char buf[64]; + struct s_client *cur_cl = cur_client(); + struct monitor_data *module_data = cur_cl->module_data; + if(module_data->auth && cur_cl->account) + { snprintf(buf, sizeof(buf), "[A-0000]1|%.42s logged in\n", cur_cl->account->usr); } + else + { cs_strncpy(buf, "[A-0000]0|not logged in\n", sizeof(buf)); } + monitor_send_info(buf, 1); +} + +static void monitor_login(char *usr) +{ + char *pwd = NULL; + int8_t res = 0; + if((usr) && (pwd = strchr(usr, ' '))) + { * pwd++ = 0; } + if(pwd) + { res = monitor_auth_client(trim(usr), trim(pwd)); } + else + { res = monitor_auth_client(NULL, NULL); } + + if(res == -1) + { + cs_disconnect_client(cur_client()); + return; + } + monitor_send_login(); +} + +static void monitor_logsend(char *flag) +{ + if(!flag) { return; } //no arg + + struct s_client *cur_cl = cur_client(); + if(strcmp(flag, "on")) + { + if(strcmp(flag, "onwohist")) + { + cur_cl->log = 0; + return; + } + } + + if(cur_cl->log) // already on + { return; } + + if(!strcmp(flag, "on") && cfg.loghistorylines) + { + if(cfg.loghistorylines && log_history) + { + LL_ITER it = ll_iter_create(log_history); + struct s_log_history *hist; + + while((hist = (struct s_log_history*)ll_iter_next(&it))) + { + char p_usr[32], p_txt[512]; + size_t pos1 = strcspn(hist->txt, "\t") + 1; + + cs_strncpy(p_usr, hist->txt , pos1 > sizeof(p_usr) ? sizeof(p_usr) : pos1); + + if((p_usr[0]) && ((cur_cl->monlvl > 1) || (cur_cl->account && !strcmp(p_usr, cur_cl->account->usr)))) + { + snprintf(p_txt, sizeof(p_txt), "[LOG%03d]%s", cur_cl->logcounter, hist->txt + pos1); + cur_cl->logcounter = (cur_cl->logcounter + 1) % 1000; + monitor_send(p_txt); + } + } + } + } + + cur_cl->log = 1; +} + +static void monitor_set_debuglevel(char *flag) +{ + if(flag) + { + cs_dblevel = atoi(flag); +#ifndef WITH_DEBUG + cs_log("*** Warning: Debug Support not compiled in ***"); +#else + cs_log("%s debug_level=%d", "all", cs_dblevel); +#endif + } +} + +static void monitor_get_account(void) +{ + struct s_auth *account; + char buf[256]; + int32_t count = 0; + + for(account = cfg.account; (account); account = account->next) + { + count++; + snprintf(buf, sizeof(buf), "[U-----]%s\n", account->usr); + monitor_send_info(buf, 0); + } + snprintf(buf, sizeof(buf), "[U-----] %i User registered\n", count); + monitor_send_info(buf, 1); + return; +} + +static void monitor_set_account(char *args) +{ + struct s_auth *account; + char delimiter[] = " ="; + char *ptr, *saveptr1 = NULL; + int32_t argidx, i, found; + char *argarray[3]; + static const char *token[] = {"au", "sleep", "uniq", "monlevel", "group", "services", "betatunnel", "ident", "caid", "chid", "class", "hostname", "expdate", "keepalive", "disabled"}; + int32_t tokencnt = sizeof(token) / sizeof(char *); + char buf[256], tmp[64]; + + argidx = 0; + found = 0; + + snprintf(tmp, sizeof(tmp), "%s", args); + snprintf(buf, sizeof(buf), "[S-0000]setuser: %s check\n", tmp); + monitor_send_info(buf, 0); + + ptr = strtok_r(args, delimiter, &saveptr1); + + // resolve arguments + while(ptr != NULL) + { + argarray[argidx] = trim(ptr); + ptr = strtok_r(NULL, delimiter, &saveptr1); + argidx++; + } + + if(argidx != 3) + { + snprintf(buf, sizeof(buf), "[S-0000]setuser: %s failed - wrong number of parameters (%d)\n", tmp, argidx); + monitor_send_info(buf, 0); + snprintf(buf, sizeof(buf), "[S-0000]setuser: %s end\n", tmp); + monitor_send_info(buf, 1); + return; + } + + // search account + for(account = cfg.account; (account) ; account = account->next) + { + if(!strcmp(argarray[0], account->usr)) + { + found = 1; + break; + } + } + + if(found != 1) + { + snprintf(buf, sizeof(buf), "[S-0000]setuser: %s failed - user %s not found\n", tmp , argarray[0]); + monitor_send_info(buf, 0); + snprintf(buf, sizeof(buf), "[S-0000]setuser: %s end\n", tmp); + monitor_send_info(buf, 1); + return; + } + + found = -1; + for(i = 0; i < tokencnt; i++) + { + if(!strcmp(argarray[1], token[i])) + { + // preparing the parameters before re-load + switch(i) + { + case 6: + tuntab_clear(&account->ttab); + break; // betatunnel + + case 8: + caidtab_clear(&account->ctab); + break; // Caid + } + found = i; + } + } + + if(found < 0) + { + snprintf(buf, sizeof(buf), "[S-0000]setuser: parameter %s not exist. possible values:\n", argarray[1]); + monitor_send_info(buf, 0); + for(i = 0; i < tokencnt; i++) + { + snprintf(buf, sizeof(buf), "[S-0000]%s\n", token[i]); + monitor_send_info(buf, 0); + } + snprintf(buf, sizeof(buf), "[S-0000]setuser: %s end\n", tmp); + monitor_send_info(buf, 1); + return; + } + else + { + chk_account(token[found], argarray[2], account); + } + + if(write_userdb() == 0) + { cs_reinit_clients(cfg.account); } + + snprintf(buf, sizeof(buf), "[S-0000]setuser: %s done - param %s set to %s\n", tmp, argarray[1], argarray[2]); + monitor_send_info(buf, 1); +} + +static void monitor_set_server(char *args) +{ + char delimiter[] = "="; + char *ptr, *saveptr1; + int32_t argidx, i; + char *argarray[3]; + static const char *token[] = {"clienttimeout", "fallbacktimeout", "clientmaxidle", "cachedelay", "bindwait", "netprio", "sleep", "unlockparental", "serialreadertimeout", "maxlogsize", "showecmdw", "waitforcards", "preferlocalcards"}; + char buf[256]; + + argidx = 0; + ptr = strtok_r(args, delimiter, &saveptr1); + + // resolve arguments + while(ptr != NULL) + { + argarray[argidx] = trim(ptr); + ptr = strtok_r(NULL, delimiter, &saveptr1); + argidx++; + } + + if(argidx != 2) + { + snprintf(buf, sizeof(buf), "[S-0000]setserver failed - wrong number of parameters (%d)\n", argidx); + monitor_send_info(buf, 1); + return; + } + + trim(argarray[0]); + trim(argarray[1]); + strtolower(argarray[0]); + + for(i = 0; i < 13; i++) + if(!strcmp(argarray[0], token[i])) { break; } + + if(i < 13) + { + config_set("global", token[i], argarray[1]); + snprintf(buf, sizeof(buf), "[S-0000]setserver done - param %s set to %s\n", argarray[0], argarray[1]); + monitor_send_info(buf, 1); + } + else + { + snprintf(buf, sizeof(buf), "[S-0000]setserver failed - parameter %s not exist\n", argarray[0]); + monitor_send_info(buf, 1); + return; + } + + /*Hide by blueven. Introduce new fallbacktimeout_percaid. + * + * if (cfg.ftimeout>=cfg.ctimeout) { + cfg.ftimeout = cfg.ctimeout - 100; + snprintf(buf, sizeof(buf), "[S-0000]setserver WARNING: fallbacktimeout adjusted to %u ms\n", cfg.ftimeout); + monitor_send_info(buf, 1); + }*/ + //kill(first_client->pid, SIGUSR1); +} + +#ifdef WEBIF +static void monitor_restart_server(void) +{ + cs_restart_oscam(); +} +#endif + +static void monitor_list_commands(const char *args[], int32_t cmdcnt) +{ + int32_t i; + for(i = 0; i < cmdcnt; i++) + { + char buf[64]; + snprintf(buf, sizeof(buf), "[S-0000]commands: %s\n", args[i]); + if(i < cmdcnt - 1) + { monitor_send_info(buf, 0); } + else + { monitor_send_info(buf, 1); } + } +} + +static int32_t monitor_process_request(char *req) +{ + int32_t i, rc; + static const char *cmd[] = {"login", + "exit", + "log", + "status", + "shutdown", + "reload", + "details", + "version", + "debug", + "getuser", + "setuser", + "setserver", + "commands", + "keepalive", + "reread" +#ifdef WEBIF + , "restart" +#endif + }; + + int32_t cmdcnt = sizeof(cmd) / sizeof(char *); // Calculate the amount of items in array + char *arg; + struct s_client *cur_cl = cur_client(); + struct monitor_data *module_data = cur_cl->module_data; + + if((arg = strchr(req, ' '))) + { + *arg++ = 0; + trim(arg); + } + //trim(req); + + if(!module_data->auth && strcmp(req, cmd[0]) != 0) + { monitor_login(NULL); } + + for(rc = 1, i = 0; i < cmdcnt; i++) + { + if(!strcmp(req, cmd[i])) + { + switch(i) + { + case 0: + monitor_login(arg); + break; // login + + case 1: + cs_disconnect_client(cur_cl); + break; // exit + + case 2: + monitor_logsend(arg); + break; // log + + case 3: + monitor_process_info(); + break; // status + + case 4: + if(cur_cl->monlvl > 3) { cs_exit_oscam(); } + break; // shutdown + + case 5: + if(cur_cl->monlvl > 2) { cs_accounts_chk(); } + break; // reload + + case 6: + monitor_process_details(arg); + break; // details + + case 7: + monitor_send_details_version(); + break; // version + + case 8: + if(cur_cl->monlvl > 3) { monitor_set_debuglevel(arg); } + break; // debuglevel + + case 9: + if(cur_cl->monlvl > 3) { monitor_get_account(); } + break; // getuser + + case 10: + if(cur_cl->monlvl > 3) { monitor_set_account(arg); } + break; // setuser + + case 11: + if(cur_cl->monlvl > 3) { monitor_set_server(arg); } + break; // setserver + + case 12: + if(cur_cl->monlvl > 3) { monitor_list_commands(cmd, cmdcnt); } + break; // list commands + + case 13: + if(cur_cl->monlvl > 3) { monitor_send_keepalive_ack(); } + break; // keepalive + + case 14: + { + char buf[64]; // reread + snprintf(buf, sizeof(buf), "[S-0000]reread\n"); + monitor_send_info(buf, 1); + cs_card_info(); + break; + } +#ifdef WEBIF + case 15: + if(cur_cl->monlvl > 3) { monitor_restart_server(); } + break; // keepalive +#endif + default: + continue; + } + break; + } + } + return rc; +} + +static void *monitor_server(struct s_client *client, uint8_t *mbuf, int32_t UNUSED(n)) +{ + client->typ = 'm'; + monitor_process_request((char *)mbuf); + + return NULL; +} + +static void monitor_cleanup(struct s_client *client) +{ + NULLFREE(client->module_data); +} + +void module_monitor(struct s_module *ph) +{ + ph->ptab.nports = 1; + ph->ptab.ports[0].s_port = cfg.mon_port; + ph->desc = "monitor"; + ph->type = MOD_CONN_UDP; + IP_ASSIGN(ph->s_ip, cfg.mon_srvip); + ph->s_handler = monitor_server; + ph->recv = monitor_recv; + ph->cleanup = monitor_cleanup; + //ph->send_dcw=NULL; +} +#endif diff --git a/module-monitor.h b/module-monitor.h new file mode 100644 index 0000000..c9d73b2 --- /dev/null +++ b/module-monitor.h @@ -0,0 +1,13 @@ +#ifndef MODULE_MONITOR_H_ +#define MODULE_MONITOR_H_ + +#ifdef MODULE_MONITOR +int32_t monitor_send_idx(struct s_client *cl, char *txt); +#else +int32_t monitor_send_idx(struct s_client *UNUSED(cl), char *UNUSED(txt)) +{ + return 0; +} +#endif + +#endif diff --git a/module-newcamd-des.c b/module-newcamd-des.c new file mode 100644 index 0000000..8ce6ae3 --- /dev/null +++ b/module-newcamd-des.c @@ -0,0 +1,500 @@ +#include "globals.h" +#include "module-newcamd-des.h" +#include "oscam-string.h" + +#define CRYPT 0 +#define HASH 1 + +extern const int32_t CWS_NETMSGSIZE; + +static const uint8_t PC2[8][6] = +{ + { 14, 17, 11, 24, 1, 5 }, + { 3, 28, 15, 6, 21, 10 }, + { 23, 19, 12, 4, 26, 8 }, + { 16, 7, 27, 20, 13, 2 }, + { 41, 52, 31, 37, 47, 55 }, + { 30, 40, 51, 45, 33, 48 }, + { 44, 49, 39, 56, 34, 53 }, + { 46, 42, 50, 36, 29, 32 } +}; + + +static const uint8_t E[8][6] = +{ + { 32, 1, 2, 3, 4, 5 }, + { 4, 5, 6, 7, 8, 9 }, + { 8, 9, 10, 11, 12, 13 }, + { 12, 13, 14, 15, 16, 17 }, + { 16, 17, 18, 19, 20, 21 }, + { 20, 21, 22, 23, 24, 25 }, + { 24, 25, 26, 27, 28, 29 }, + { 28, 29, 30, 31, 32, 1 } +}; + +static const uint8_t P[32] = +{ + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 +}; + +static const uint8_t SBOXES[4][64] = +{ + { + 0x2e, 0xe0, 0xc4, 0xbf, 0x4d, 0x27, 0x11, 0xc4, + 0x72, 0x4e, 0xaf, 0x72, 0xbb, 0xdd, 0x68, 0x11, + 0x83, 0x5a, 0x5a, 0x06, 0x36, 0xfc, 0xfc, 0xab, + 0xd5, 0x39, 0x09, 0x95, 0xe0, 0x83, 0x97, 0x68, + 0x44, 0xbf, 0x21, 0x8c, 0x1e, 0xc8, 0xb8, 0x72, + 0xad, 0x14, 0xd6, 0xe9, 0x72, 0x21, 0x8b, 0xd7, + 0xff, 0x65, 0x9c, 0xfb, 0xc9, 0x03, 0x57, 0x9e, + 0x63, 0xaa, 0x3a, 0x40, 0x05, 0x56, 0xe0, 0x3d + }, + { + 0xcf, 0xa3, 0x11, 0xfd, 0xa8, 0x44, 0xfe, 0x27, + 0x96, 0x7f, 0x2b, 0xc2, 0x63, 0x98, 0x84, 0x5e, + 0x09, 0x6c, 0xd7, 0x10, 0x32, 0xd1, 0x4d, 0xea, + 0xec, 0x06, 0x70, 0xb9, 0x55, 0x3b, 0xba, 0x85, + 0x90, 0x4d, 0xee, 0x38, 0xf7, 0x2a, 0x5b, 0xc1, + 0x2a, 0x93, 0x84, 0x5f, 0xcd, 0xf4, 0x31, 0xa2, + 0x75, 0xbb, 0x08, 0xe6, 0x4c, 0x17, 0xa6, 0x7c, + 0x19, 0x60, 0xd3, 0x05, 0xb2, 0x8e, 0x6f, 0xd9 + }, + { + 0x4a, 0xdd, 0xb0, 0x07, 0x29, 0xb0, 0xee, 0x79, + 0xf6, 0x43, 0x03, 0x94, 0x8f, 0x16, 0xd5, 0xaa, + 0x31, 0xe2, 0xcd, 0x38, 0x9c, 0x55, 0x77, 0xce, + 0x5b, 0x2c, 0xa4, 0xfb, 0x62, 0x8f, 0x18, 0x61, + 0x1d, 0x61, 0x46, 0xba, 0xb4, 0xdd, 0xd9, 0x80, + 0xc8, 0x16, 0x3f, 0x49, 0x73, 0xa8, 0xe0, 0x77, + 0xab, 0x94, 0xf1, 0x5f, 0x62, 0x0e, 0x8c, 0xf3, + 0x05, 0xeb, 0x5a, 0x25, 0x9e, 0x32, 0x27, 0xcc + }, + { + 0xd7, 0x1d, 0x2d, 0xf8, 0x8e, 0xdb, 0x43, 0x85, + 0x60, 0xa6, 0xf6, 0x3f, 0xb9, 0x70, 0x1a, 0x43, + 0xa1, 0xc4, 0x92, 0x57, 0x38, 0x62, 0xe5, 0xbc, + 0x5b, 0x01, 0x0c, 0xea, 0xc4, 0x9e, 0x7f, 0x29, + 0x7a, 0x23, 0xb6, 0x1f, 0x49, 0xe0, 0x10, 0x76, + 0x9c, 0x4a, 0xcb, 0xa1, 0xe7, 0x8d, 0x2d, 0xd8, + 0x0f, 0xf9, 0x61, 0xc4, 0xa3, 0x95, 0xde, 0x0b, + 0xf5, 0x3c, 0x32, 0x57, 0x58, 0x62, 0x84, 0xbe + } +}; + +static const uint8_t PC1[][8] = +{ + {57, 49, 41, 33, 25, 17, 9, 1}, + {58, 50, 42, 34, 26, 18, 10, 2}, + {59, 51, 43, 35, 27, 19, 11, 3}, + {60, 52, 44, 36, 63, 55, 47, 39}, + {31, 23, 15, 7, 62, 54, 46, 38}, + {30, 22, 14, 6, 61, 53, 45, 37}, + {29, 21, 13, 5, 28, 20, 12, 4} +}; + +void doPC1(uint8_t data[]) +{ + uint8_t buf[8]; + uint8_t i, j; + + memset(buf, 0, 8); + + for(j = 0; j < 7; j++) + { + for(i = 0; i < 8; i++) + { + uint8_t lookup = PC1[j][i]; + buf[j] |= ((data[(lookup >> 3)] >> (8 - (lookup & 7))) & 1) << (7 - i); + } + } + + memcpy(data, buf, 8); +} + +static void doIp(uint8_t data[]) +{ + uint8_t j, k; + uint8_t val; + uint8_t buf[8]; + uint8_t *p; + uint8_t i = 8; + memset(buf, 0, sizeof(buf)); + + for(i = 0; i < 8; i++) + { + val = data[i]; + p = &buf[3]; + j = 4; + + do + { + for(k = 0; k <= 4; k += 4) + { + p[k] >>= 1; + if(val & 1) { p[k] |= 0x80; } + val >>= 1; + } + p--; + } + while(--j); + } + + memcpy(data, buf, 8); +} + +static void doIp_1(uint8_t data[]) +{ + uint8_t j, k; + uint8_t r = 0; + uint8_t buf[8]; + uint8_t *p; + uint8_t i = 8; + + for(i = 0; i < 8; i++) + { + p = &data[3]; + j = 4; + + do + { + for(k = 0; k <= 4; k += 4) + { + r >>= 1; + if(p[k] & 1) { r |= 0x80; } + p[k] >>= 1; + } + p--; + } + while(--j); + buf[i] = r; + } + + memcpy(data, buf, 8); +} + +static void makeK(uint8_t *left, uint8_t *right, uint8_t *K) +{ + uint8_t i, j; + uint8_t bit, val; + uint8_t *p; + + for(i = 0; i < 8; i++) + { + val = 0; + for(j = 0; j < 6; j++) + { + bit = PC2[i][j]; + if(bit < 29) + { + bit = 28 - bit; + p = left; + } + else + { + bit = 56 - bit; + p = right; + } + val <<= 1; + if(p[bit >> 3] & (1 << (bit & 7))) { val |= 1; } + } + *K = val; + K++; + } +} + +static void rightRot(uint8_t key[]) { + uint8_t carry = (key[0] & 1) ? 0x08 : 0; + key[0] = (key[0] >> 1) | ((key[1] & 1) ? 0x80 : 0); + key[1] = (key[1] >> 1) | ((key[2] & 1) ? 0x80 : 0); + key[2] = (key[2] >> 1) | ((key[3] & 1) ? 0x80 : 0); + key[3] = (key[3] >> 1) | carry; +} + +static void rightRotKeys(uint8_t left[], uint8_t right[]) +{ + rightRot(left); + rightRot(right); +} + +static void leftRot(uint8_t key[]) +{ + uint8_t carry = key[3] >> 3; + key[3] = 0x0F & ((key[3] << 1) | !!(key[2] & 0x80)); + key[2] = (key[2] << 1) | !!(key[1] & 0x80); + key[1] = (key[1] << 1) | !!(key[0] & 0x80); + key[0] = (key[0] << 1) | carry; +} + +static void leftRotKeys(uint8_t left[], uint8_t right[]) +{ + leftRot(left); + leftRot(right); +} + +static void desCore(uint8_t data[], uint8_t K[], uint8_t result[]) +{ + uint8_t i, j; + uint8_t bit, val; + + memset(result, 0, 4); + + for(i = 0; i < 8; i++) + { + val = 0; + for(j = 0; j < 6; j++) + { + bit = 32 - E[i][j]; + val <<= 1; + if(data[3 - (bit >> 3)] & (1 << (bit & 7))) { val |= 1; } + } + val ^= K[i]; + val = SBOXES[i & 3][val]; + if(i > 3) + { + val >>= 4; + } + val &= 0x0f; + result[i >> 1] |= (i & 1) ? val : (val << 4); + } +} + +static void permut32(uint8_t data[]) +{ + uint8_t i, j; + uint8_t bit; + uint8_t r[4] = {0}; // init to keep Valgrind happy + uint8_t *p; + + for(i = 0; i < 32; i++) + { + bit = 32 - P[i]; + p = r; + for(j = 0; j < 3; j++) + { + *p = (*p << 1) | ((p[1] & 0x80) ? 1 : 0); + p++; + } + *p <<= 1; + if(data[3 - (bit >> 3)] & (1 << (bit & 7))) { *p |= 1; } + } + + memcpy(data, r, 4); +} + +static void swap(uint8_t left[], uint8_t right[]) +{ + uint8_t x[4]; + + memcpy(x, right, 4); + memcpy(right, left, 4); + memcpy(left, x, 4); +} + +static void desRound(uint8_t left[], uint8_t right[], uint8_t data[], uint8_t mode, uint8_t k8) +{ + uint8_t i; + uint8_t K[8]; + uint8_t r[4]; + uint8_t tempr[4]; + unsigned short temp; + + memcpy(tempr, data + 4, 4); + + /* Viaccess */ + temp = (short)k8 * (short)tempr[0] + (short)k8 + (short)tempr[0]; + tempr[0] = (temp & 0xff) - ((temp >> 8) & 0xff); + if((temp & 0xff) - (temp >> 8) < 0) + { tempr[0]++; } + + makeK(left, right, K); + desCore(tempr, K, r); + permut32(r); + + if(mode & DES_HASH) + { + i = r[0]; + r[0] = r[1]; + r[1] = i; + } + + for(i = 0; i < 4; i++) + { + *data ^= r[i]; + data++; + } + + swap(data - 4, data); +} + +void nc_des(uint8_t key[], uint8_t mode, uint8_t data[]) +{ + uint8_t i; + uint8_t left[8]; + uint8_t right[8]; + uint8_t *p = left; + + short DESShift = (mode & DES_RIGHT) ? 0x8103 : 0xc081; + + for(i = 3; i > 0; i--) + { + *p = (key[i - 1] << 4) | (key[i] >> 4); + p++; + } + left[3] = key[0] >> 4; + right[0] = key[6]; + right[1] = key[5]; + right[2] = key[4]; + right[3] = key[3] & 0x0f; + + if(mode & DES_IP) { doIp(data); } + + do + { + if(!(mode & DES_RIGHT)) + { + leftRotKeys(left, right); + if(!(DESShift & 0x8000)) { leftRotKeys(left, right); } + } + desRound(left, right, data, mode, key[7]); + + if(mode & DES_RIGHT) + { + rightRotKeys(left, right); + if(!(DESShift & 0x8000)) { rightRotKeys(left, right); } + } + DESShift <<= 1; + } + while(DESShift); + + swap(data, data + 4); + if(mode & DES_IP_1) { doIp_1(data); } + +} + +/*------------------------------------------------------------------------*/ +static void des_key_parity_adjust(uint8_t *key, uint8_t len) +{ + uint8_t i, j, parity; + + for(i = 0; i < len; i++) + { + parity = 1; + for(j = 1; j < 8; j++) if((key[i] >> j) & 0x1) { parity = ~parity & 0x01; } + key[i] |= parity; + } +} + +static uint8_t *des_key_spread(uint8_t *normal, uint8_t *spread) +{ + spread[ 0] = normal[ 0] & 0xfe; + spread[ 1] = ((normal[ 0] << 7) | (normal[ 1] >> 1)) & 0xfe; + spread[ 2] = ((normal[ 1] << 6) | (normal[ 2] >> 2)) & 0xfe; + spread[ 3] = ((normal[ 2] << 5) | (normal[ 3] >> 3)) & 0xfe; + spread[ 4] = ((normal[ 3] << 4) | (normal[ 4] >> 4)) & 0xfe; + spread[ 5] = ((normal[ 4] << 3) | (normal[ 5] >> 5)) & 0xfe; + spread[ 6] = ((normal[ 5] << 2) | (normal[ 6] >> 6)) & 0xfe; + spread[ 7] = normal[ 6] << 1; + spread[ 8] = normal[ 7] & 0xfe; + spread[ 9] = ((normal[ 7] << 7) | (normal[ 8] >> 1)) & 0xfe; + spread[10] = ((normal[ 8] << 6) | (normal[ 9] >> 2)) & 0xfe; + spread[11] = ((normal[ 9] << 5) | (normal[10] >> 3)) & 0xfe; + spread[12] = ((normal[10] << 4) | (normal[11] >> 4)) & 0xfe; + spread[13] = ((normal[11] << 3) | (normal[12] >> 5)) & 0xfe; + spread[14] = ((normal[12] << 2) | (normal[13] >> 6)) & 0xfe; + spread[15] = normal[13] << 1; + + des_key_parity_adjust(spread, 16); + return spread; +} + +static void des_random_get(uint8_t *buffer, uint8_t len) +{ + uint8_t idx = 0; + int randomNo = 0; + + for(idx = 0; idx < len; idx++) + { + if(!(idx % 3)) { randomNo = rand(); } + buffer[idx] = (randomNo >> ((idx % 3) << 3)) & 0xff; + } +} + +static void EuroDes(uint8_t key[], uint8_t operatingMode, uint8_t data[]) +{ + /* Eurocrypt 3-DES */ + uint8_t mode = (operatingMode == HASH) ? 0 : DES_RIGHT; + nc_des(key, (uint8_t)(DES_IP | mode), data); + + mode ^= DES_RIGHT; + nc_des(key + 8, mode, data); + + mode ^= DES_RIGHT; + nc_des(key, (uint8_t)(mode | DES_IP_1), data); +} + +int nc_des_encrypt(uint8_t *buffer, int len, uint8_t *deskey) +{ + uint8_t checksum = 0; + uint8_t noPadBytes; + uint8_t padBytes[7]; + char ivec[8]; + short i; + + if(!deskey) { return len; } + noPadBytes = (8 - ((len - 1) % 8)) % 8; + if(len + noPadBytes + 1 >= CWS_NETMSGSIZE - 8) { return -1; } + des_random_get(padBytes, noPadBytes); + for(i = 0; i < noPadBytes; i++) { buffer[len++] = padBytes[i]; } + for(i = 2; i < len; i++) { checksum ^= buffer[i]; } + buffer[len++] = checksum; + des_random_get((uint8_t *)ivec, 8); + memcpy(buffer + len, ivec, 8); + for(i = 2; i < len; i += 8) + { + uint8_t j; + for(j = 0; j < 8; j++) { buffer[i + j] ^= ivec[j]; } + EuroDes(deskey, HASH, buffer + i); + memcpy(ivec, buffer + i, 8); + } + len += 8; + return len; +} + +int nc_des_decrypt(uint8_t *buffer, int len, uint8_t *deskey) +{ + char ivec[8]; + char nextIvec[8]; + int i; + uint8_t checksum = 0; + + if(!deskey) { return len; } + if((len - 2) % 8 || (len - 2) < 16) { return -1; } + len -= 8; + memcpy(nextIvec, buffer + len, 8); + for(i = 2; i < len; i += 8) + { + uint8_t j; + + memcpy(ivec, nextIvec, 8); + memcpy(nextIvec, buffer + i, 8); + EuroDes(deskey, CRYPT, buffer + i); + for(j = 0; j < 8; j++) + { buffer[i + j] ^= ivec[j]; } + } + for(i = 2; i < len; i++) { checksum ^= buffer[i]; } + if(checksum) { return -1; } + return len; +} + +void nc_des_login_key_get(uint8_t *key1, uint8_t *key2, int len, uint8_t *des16) +{ + uint8_t des14[14]; + int i; + + memcpy(des14, key1, sizeof(des14)); + for(i = 0; i < len; i++) { des14[i % 14] ^= key2[i]; } + des16 = des_key_spread(des14, des16); + doPC1(des16); + doPC1(des16 + 8); +} diff --git a/module-newcamd-des.h b/module-newcamd-des.h new file mode 100644 index 0000000..29f96e4 --- /dev/null +++ b/module-newcamd-des.h @@ -0,0 +1,17 @@ +#ifndef MODULE_NEWCAMD_DES_H_ +#define MODULE_NEWCAMD_DES_H_ + +#define DES_IP 1 +#define DES_IP_1 2 +#define DES_RIGHT 4 +#define DES_HASH 8 + +#define DES_ECM_CRYPT 0 +#define DES_ECM_HASH DES_HASH + +void nc_des(uint8_t key[], uint8_t mode, uint8_t data[]); +int nc_des_encrypt(uint8_t *buffer, int len, uint8_t *deskey); +int nc_des_decrypt(uint8_t *buffer, int len, uint8_t *deskey); +void nc_des_login_key_get(uint8_t *key1, uint8_t *key2, int len, uint8_t *des16); + +#endif diff --git a/module-newcamd.c b/module-newcamd.c new file mode 100644 index 0000000..6495a30 --- /dev/null +++ b/module-newcamd.c @@ -0,0 +1,1922 @@ +#define MODULE_LOG_PREFIX "newcamd" + +#include "globals.h" + +#ifdef MODULE_NEWCAMD + +#include "cscrypt/des.h" +#include "cscrypt/md5.h" +#include "module-newcamd.h" +#include "module-newcamd-des.h" +#include "oscam-array.h" +#include "oscam-conf-chk.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-emm.h" +#include "oscam-net.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-time.h" + +const int32_t CWS_NETMSGSIZE = 1024; // csp 0.8.9 (default: 400). This is CWS_NETMSGSIZE. The old default was 240 +int32_t portion_sid_num = 0; + +#define NCD_CLIENT_ID 0x8888 +#define CWS_FIRSTCMDNO 0xE0 + +typedef enum +{ + MSG_CLIENT_2_SERVER_LOGIN = CWS_FIRSTCMDNO, + MSG_CLIENT_2_SERVER_LOGIN_ACK, + MSG_CLIENT_2_SERVER_LOGIN_NAK, + MSG_CARD_DATA_REQ, + MSG_CARD_DATA, + MSG_SERVER_2_CLIENT_NAME, + MSG_SERVER_2_CLIENT_NAME_ACK, + MSG_SERVER_2_CLIENT_NAME_NAK, + MSG_SERVER_2_CLIENT_LOGIN, + MSG_SERVER_2_CLIENT_LOGIN_ACK, + MSG_SERVER_2_CLIENT_LOGIN_NAK, + MSG_ADMIN, + MSG_ADMIN_ACK, + MSG_ADMIN_LOGIN, + MSG_ADMIN_LOGIN_ACK, + MSG_ADMIN_LOGIN_NAK, + MSG_ADMIN_COMMAND, + MSG_ADMIN_COMMAND_ACK, + MSG_ADMIN_COMMAND_NAK, + MSG_KEEPALIVE = CWS_FIRSTCMDNO + 0x1D, + MSG_SERVER_2_CLIENT_OSD = 0xD1, + MSG_SERVER_2_CLIENT_ALLCARDS = 0xD2, + MSG_SERVER_2_CLIENT_ADDCARD = 0xD3, + MSG_SERVER_2_CLIENT_REMOVECARD = 0xD4, + MSG_SERVER_2_CLIENT_CHANGE_KEY = 0xD5, + MSG_SERVER_2_CLIENT_GET_VERSION = 0xD6, + MSG_SERVER_2_CLIENT_ADDSID = 0xD7, + MSG_CLIENT_2_SERVER_CARDDISCOVER = 0xD8 +} net_msg_type_t; + +typedef enum +{ + COMMTYPE_CLIENT, + COMMTYPE_SERVER +} comm_type_t; + +typedef struct custom_data +{ + uint16_t sid; + uint16_t caid; + int32_t provid; + uint8_t x; +} custom_data_t; + +#define REQ_SIZE 2 + +static int32_t network_message_send(int32_t handle, uint16_t *netMsgId, uint8_t *buffer, int32_t len, + uint8_t *deskey, comm_type_t commType, uint16_t sid, custom_data_t *cd) +{ + uint8_t netbuf[CWS_NETMSGSIZE]; + int32_t head_size; + struct s_client *cl = cur_client(); + + head_size = (cl->ncd_proto == NCD_524) ? 8 : 12; + + if(len < 3 || len + head_size > CWS_NETMSGSIZE || handle < 0) + { + return -1; + } + + buffer[1] = (buffer[1] & 0xF0) | (((len - 3) >> 8) & 0x0F); + buffer[2] = (len - 3) & 0xFF; + + memcpy(netbuf + head_size, buffer, len); + len += head_size; + + if(netMsgId) + { + if(commType == COMMTYPE_CLIENT) + { + (*netMsgId)++; + } + + netbuf[2] = (*netMsgId) >> 8; + netbuf[3] = (*netMsgId) & 0xFF; + } + else + { + netbuf[2] = 0; + netbuf[3] = 0; + } + + memset(netbuf + 4, 0, (cl->ncd_proto == NCD_524) ? 4 : 8); + + if(sid) + { + if(cl->reader && cl->reader->ncd_disable_server_filt + && sid != NCD_CLIENT_ID && cl->ncd_proto != NCD_524) // mgclient send header + { + memcpy(netbuf + 4, cl->ncd_header + 4, 7); + } + + netbuf[(cl->ncd_proto == NCD_524) ? 6 : 4] = (uint8_t)(sid >> 8); // sid + netbuf[(cl->ncd_proto == NCD_524) ? 7 : 5] = (uint8_t)(sid); + } + + //if((!ncd_proto == NCD_524) && (buffer[0] >= 0xD1) && (buffer[0] <= 0xD8)) // extended proto for mg + //{ + // cs_log_dbg(D_CLIENT, "newcamd: extended: msg"); + + if(cd) + { + cs_log_dbg(D_CLIENT, "newcamd: has cd"); + + netbuf[4] = cd->sid >> 8; + netbuf[5] = cd->sid & 0xFF; + netbuf[6] = cd->caid >> 8; + netbuf[7] = cd->caid & 0xFF; + netbuf[8] = (cd->provid >> 16) & 0xFF; + netbuf[9] = (cd->provid >> 8) & 0xFF; + netbuf[10] = cd->provid & 0xFF; + } + //} + + if(NCD_525 == cl->ncd_proto && cfg.ncd_mgclient && MSG_CLIENT_2_SERVER_LOGIN_ACK == buffer[0]) + { + netbuf[4] = 0x6E; // From ExtNewcamSession.java CSP line 65-66 getLoginOkMsg() + netbuf[5] = 0x73; + netbuf[11] = 0x14; + } + + if(NCD_525 == cl->ncd_proto && MSG_SERVER_2_CLIENT_ADDSID == buffer[0]) + { + netbuf[11] = 0x14; + } + + //if(buffer[0] == MSG_CLIENT_2_SERVER_LOGIN && cur_client()->reader->ncd_disable_server_filt) + //{ + // netbuf[11] = 0x11; // From ChameleonCwsConnector.java CSP line 59-61 run() + //} + + netbuf[0] = (len - 2) >> 8; + netbuf[1] = (len - 2) & 0xFF; + + cs_log_dump_dbg(D_CLIENT, netbuf, len, "send %d bytes to %s", len, remote_txt()); + + if((len = nc_des_encrypt(netbuf, len, deskey)) < 0) + { + return -1; + } + + netbuf[0] = (len - 2) >> 8; + netbuf[1] = (len - 2) & 0xFF; + + return send(handle, netbuf, len, 0); +} + +static int32_t send_sid_list(void) +{ + struct s_client *cl = cur_client(); + + if(1 != cl->ftab.nfilts || !cl->sidtabs.no || !cfg.ncd_ptab.ports[cl->port_idx].ncd) + { + cs_log("SID list will not be send to mgcamd client."); + return 0; + } + + uint8_t mbuf[CWS_NETMSGSIZE]; + int32_t n = 0, nr = 0, i = 0, sid_num = 0, portion_num = 0; + SIDTAB *sidtab = 0; + custom_data_t cd; + + cs_log_dbg(D_TRACE, "Send SID list to mgcamd client."); + + memset(&cd, 0, sizeof(cd)); + FILTER *pfilts = cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts; + + //memset(mbuf, 0, sizeof(mbuf)); // not nessesery + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if((cl->sidtabs.no & ((SIDTABBITS)1 << nr)) && (sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid)) + { + for(n = 0; n < pfilts[0].nprids; n++) + { + if(chk_srvid_match_by_caid_prov(pfilts[0].caid, pfilts[0].prids[n], sidtab)) + { + for(i = 0; i < sidtab->num_srvid; i++) + { + // First SID goes to header + if(0 == portion_sid_num) + { + cd.sid = sidtab->srvid[i]; // first sid + cd.caid = cfg.ncd_ptab.ports[cl->port_idx].s_port; // assigned port + cd.provid = 0x1; // mark as deny + } + + mbuf[portion_sid_num * 3] = (uint8_t)(sidtab->srvid[i] >> 8); + mbuf[portion_sid_num * 3 + 1] = (uint8_t)(sidtab->srvid[i] & 0xFF); + mbuf[portion_sid_num * 3 + 2] = 0x1; // mark as deny + + ++sid_num; + ++portion_sid_num; + + if(portion_sid_num >= 50) + { + ++portion_num; + + cs_log_dump_dbg(0x0800, mbuf, (portion_sid_num) * 3, "Portion %d contains %d SIDs", + portion_num, portion_sid_num); + + mbuf[0] = MSG_SERVER_2_CLIENT_ADDSID; + mbuf[1] = 0x00; + mbuf[2] = 0x00; + + network_message_send(cl->udp_fd, &cl->ncd_msgid, mbuf, portion_sid_num * 3, + cl->ncd_skey, COMMTYPE_SERVER, 0, &cd); + portion_sid_num = 0; + } + } + + break; + } + } + } + } + + if(portion_sid_num) + { + ++portion_num; + + cs_log_dump_dbg(0x0800, mbuf, (portion_sid_num) * 3, "Portion %d contains %d SIDs", + portion_num, portion_sid_num); + + mbuf[0] = MSG_SERVER_2_CLIENT_ADDSID; + mbuf[1] = 0x0; + mbuf[2] = 0x0; + + network_message_send(cl->udp_fd, &cl->ncd_msgid, mbuf, portion_sid_num * 3, + cl->ncd_skey, COMMTYPE_SERVER, 0, &cd); + portion_sid_num = 0; + } + + cs_log("%d deny SIDs in the %d messages were sent to the client.", sid_num, portion_num); + + return sid_num; +} + +static int32_t network_message_receive(int32_t handle, uint16_t *netMsgId, uint8_t *buffer, + uint8_t *deskey, comm_type_t commType) +{ + int32_t len, ncd_off, msgid; + uint8_t netbuf[CWS_NETMSGSIZE]; + int32_t returnLen; + struct s_client *cl = cur_client(); + + if(!buffer || handle < 0) + { + return -1; + } + + len = cs_recv(handle, netbuf, 2, 0); + + cs_log_dbg(D_CLIENT, "nmr(): len=%d, errno=%d", len, (len == -1) ? errno : 0); + + if(!len) + { + cs_log_dbg(D_CLIENT, "nmr: 1 return 0"); + + if(commType == COMMTYPE_CLIENT) + { + network_tcp_connection_close(cl->reader, "receive error1"); + } + else + { + cs_disconnect_client(cl); + } + + return 0; + } + + if(len != 2) + { + cs_log_dbg(D_CLIENT, "nmr: len!=2"); + + if(commType == COMMTYPE_CLIENT) + { + network_tcp_connection_close(cl->reader, "receive error2"); + } + else + { + cs_disconnect_client(cl); + } + + return -1; + } + + if(((netbuf[0] << 8) | netbuf[1]) > CWS_NETMSGSIZE - 2) + { + cs_log_dbg(D_CLIENT, "nmr: received data len=%d longer than CWS_NETMSGSIZE=%d", + ((netbuf[0] << 8) | netbuf[1]), CWS_NETMSGSIZE); + cs_log_dbg(D_CLIENT, "nmr: 1 return -1"); + return -1; + } + + len = cs_recv(handle, netbuf + 2, (netbuf[0] << 8) | netbuf[1], 0); + if(!len) + { + cs_log_dbg(D_CLIENT, "nmr: 2 return 0"); + return 0; + } + + if(len != ((netbuf[0] << 8) | netbuf[1])) + { + cs_log_dbg(D_CLIENT, "nmr: 2 return -1"); + return -1; + } + + len += 2; + + if((len = nc_des_decrypt(netbuf, len, deskey)) < 11) // 15(newcamd525) or 11 ??? + { + cs_log_dbg(D_CLIENT, "nmr: can't decrypt, invalid des key?"); + cs_sleepms(2000); + return -1; + } + + //cs_log_dump_dbg(D_CLIENT, netbuf, len, "nmr: decrypted data, len=%d", len); + + msgid = (netbuf[2] << 8) | netbuf[3]; + + if(cl->ncd_proto == NCD_AUTO) + { + // auto detect + int32_t l5 = (((netbuf[13] & 0x0F) << 8) | netbuf[14]) + 3; + int32_t l4 = (((netbuf[9] & 0x0F) << 8) | netbuf[10]) + 3; + + if((l5 <= len - 12) && ((netbuf[12] & 0xF0) == 0xE0 || (netbuf[12] & 0xF0) == 0x80)) + { + cl->ncd_proto = NCD_525; + } + else if((l4 <= len - 8) && ((netbuf[8] & 0xF0) == 0xE0 || (netbuf[9] & 0xF0) == 0x80)) + { + cl->ncd_proto = NCD_524; + } + else + { + cs_log_dbg(D_CLIENT, "nmr: 4 return -1"); + return -1; + } + + cs_log_dbg(D_CLIENT, "nmr: autodetect: newcamd52%d used", (cl->ncd_proto == NCD_525) ? 5 : 4); + } + + ncd_off = (cl->ncd_proto == NCD_525) ? 4 : 0; + + returnLen = (((netbuf[9 + ncd_off] & 0x0F) << 8) | netbuf[10 + ncd_off]) + 3; + if(returnLen > (len - (8 + ncd_off))) + { + cs_log_dbg(D_CLIENT, "nmr: 4 return -1"); + return -1; + } + + //cs_log_dump_dbg(D_CLIENT, netbuf, len, "nmr: decrypted data"); + + if(netMsgId) + { + switch(commType) + { + case COMMTYPE_SERVER: + *netMsgId = msgid; + break; + + case COMMTYPE_CLIENT: + //if(*netMsgId != ((netbuf[2] << 8) | netbuf[3])) + //{ + cs_log_dbg(D_CLIENT, "nmr: netMsgId=%d, from server=%d, ", *netMsgId, msgid); + //return -2; + //} + break; + + default: + cs_log_dbg(D_CLIENT, "nmr: 5 return -1"); + return -1; + break; + } + } + + switch(commType) + { + case COMMTYPE_SERVER: + memcpy(cl->ncd_header, netbuf, (8 + ncd_off)); + buffer[0] = (cl->ncd_proto == NCD_525) ? netbuf[4] : netbuf[6]; // sid + buffer[1] = (cl->ncd_proto == NCD_525) ? netbuf[5] : netbuf[7]; + break; + + case COMMTYPE_CLIENT: + memcpy(cl->ncd_header, netbuf, (8 + ncd_off)); + buffer[0] = netbuf[2]; // msgid + buffer[1] = netbuf[3]; + break; + } + + memcpy(buffer + 2, netbuf + (8 + ncd_off), returnLen); + return returnLen + 2; +} + +static void network_cmd_no_data_send(int32_t handle, uint16_t *netMsgId, net_msg_type_t cmd, + uint8_t *deskey, comm_type_t commType) +{ + uint8_t buffer[3]; + + buffer[0] = cmd; + buffer[1] = 0; + buffer[2] = 0; + + network_message_send(handle, netMsgId, buffer, 3, deskey, commType, 0, NULL); +} + +static int32_t network_cmd_no_data_receive(int32_t handle, uint16_t *netMsgId, uint8_t *deskey, + comm_type_t commType) +{ + uint8_t buffer[CWS_NETMSGSIZE]; + + if(network_message_receive(handle, netMsgId, buffer, deskey, commType) != 3 + 2) + { + return -1; + } + + return buffer[2]; +} + +void newcamd_reply_ka(void) +{ + struct s_client *cl = cur_client(); + + if(!cl) + { + return; + } + + if(!cl->udp_fd) + { + cs_log_dbg(D_CLIENT, "invalid client fd=%d", cl->udp_fd); + return; + } + + cs_log_dbg(D_CLIENT, "send keepalive to client fd=%d", cl->udp_fd); + + if(cl->reader) + { + cl->reader->last_s = time((time_t *)0); + } + + network_cmd_no_data_send(cl->udp_fd, &cl->ncd_msgid, MSG_KEEPALIVE, cl->ncd_skey, COMMTYPE_SERVER); +} + +static int32_t connect_newcamd_server(void) +{ + int32_t i; + uint8_t buf[CWS_NETMSGSIZE]; + uint8_t keymod[14]; + uint8_t key[16]; + int32_t handle = 0; + + uint32_t idx; + uint8_t passwdcrypt[120]; + uint8_t login_answer; + int32_t bytes_received; + struct s_client *cl = cur_client(); + + if(cl->reader->device[0] == 0 || cl->reader->r_pwd[0] == 0 + || cl->reader->r_usr[0] == 0 || cl->reader->r_port == 0) + { + return -5; + } + + // 1. Connect + handle = network_tcp_connection_open(cl->reader); + if(handle < 0) + { + return -1; + } + + // 2. Get init sequence + cl->ncd_msgid = 0; + if(read(handle, keymod, sizeof(keymod)) != sizeof(keymod)) + { + cs_log("server does not return 14 bytes"); + network_tcp_connection_close(cl->reader, "connect error"); + return -2; + } + + cs_log_dump_dbg(D_CLIENT, keymod, sizeof(cl->reader->ncd_key), "server init sequence:"); + nc_des_login_key_get(keymod, cl->reader->ncd_key, sizeof(cl->reader->ncd_key), key); + + // 3. Send login info + idx = 3; + buf[0] = MSG_CLIENT_2_SERVER_LOGIN; + buf[1] = 0; + + cs_strncpy((char *)buf + idx, cl->reader->r_usr, sizeof(buf) - idx); + __md5_crypt(cl->reader->r_pwd, "$1$abcdefgh$", (char *)passwdcrypt); + idx += cs_strlen(cl->reader->r_usr) + 1; + cs_strncpy((char *)buf + idx, (const char *)passwdcrypt, sizeof(buf) - idx); + + network_message_send(handle, 0, buf, idx + cs_strlen((char *)passwdcrypt) + 1, key, + COMMTYPE_CLIENT, NCD_CLIENT_ID, NULL); + + // 3.1 Get login answer + login_answer = network_cmd_no_data_receive(handle, &cl->ncd_msgid, key, COMMTYPE_CLIENT); + if(login_answer == MSG_CLIENT_2_SERVER_LOGIN_NAK) + { + cs_log("login failed for user '%s'", cl->reader->r_usr); + network_tcp_connection_close(cl->reader, "login error1"); + return -3; + } + + if(login_answer != MSG_CLIENT_2_SERVER_LOGIN_ACK) + { + cs_log("expected MSG_CLIENT_2_SERVER_LOGIN_ACK (%02X), received %02X", + MSG_CLIENT_2_SERVER_LOGIN_ACK, login_answer); + + network_tcp_connection_close(cl->reader, "login error2"); + return -3; + } + + // 3.2 Set connection info + cl->reader->tcp_connected = 1; + cl->crypted = 1; + + // 4. Send MSG_CARD_DATE_REQ + nc_des_login_key_get(cl->reader->ncd_key, passwdcrypt, cs_strlen((char *)passwdcrypt), key); + network_cmd_no_data_send(handle, &cl->ncd_msgid, MSG_CARD_DATA_REQ, key, COMMTYPE_CLIENT); + + bytes_received = network_message_receive(handle, &cl->ncd_msgid, buf, key, COMMTYPE_CLIENT); + if(bytes_received < 16 || buf[2] != MSG_CARD_DATA) + { + cs_log("expected MSG_CARD_DATA (%02X), received %02X", MSG_CARD_DATA, buf[2]); + + network_tcp_connection_close(cl->reader, "receive error"); + return -4; + } + + // 5. Parse CAID and PROVID(s) + cl->reader->caid = (uint16_t)((buf[6] << 8) | buf[7]); + + // handle special serial format in newcamd. See newcamd_auth_client + newcamd_to_hexserial(buf + 10, cl->reader->hexserial, cl->reader->caid); + + cs_log("Newcamd Server: %s:%d - UserID: %i", cl->reader->device, cl->reader->r_port, buf[3 + 2]); + + cs_log("CAID: %04X - UA: %02X%02X%02X%02X%02X%02X%02X%02X - Provider # %i", + cl->reader->caid, cl->reader->hexserial[0], cl->reader->hexserial[1], cl->reader->hexserial[2], + cl->reader->hexserial[3], cl->reader->hexserial[4], cl->reader->hexserial[5], cl->reader->hexserial[6], + cl->reader->hexserial[7], buf[14 + 2]); + + cl->reader->nprov = buf[14 + 2]; + memset(cl->reader->prid, 0x00, sizeof(cl->reader->prid)); + + for(i = 0; i < cl->reader->nprov; i++) + { + if(caid_is_betacrypt(cl->reader->caid) || caid_is_irdeto(cl->reader->caid)) + { + memcpy(&cl->reader->prid[i], buf + 22 + 2 + 11 * i, 4); + } + else + { + cl->reader->prid[i][1] = buf[15 + 2 + 11 * i]; + cl->reader->prid[i][2] = buf[16 + 2 + 11 * i]; + cl->reader->prid[i][3] = buf[17 + 2 + 11 * i]; + } + + memcpy(&cl->reader->sa[i], buf + 22 + 2 + 11 * i, 4); // the 4 first bytes are not read + + cs_log("Provider ID: %02X%02X%02X - SA: %02X%02X%02X%02X", + cl->reader->prid[i][1], cl->reader->prid[i][2], cl->reader->prid[i][3], cl->reader->sa[i][0], + cl->reader->sa[i][1], cl->reader->sa[i][2], cl->reader->sa[i][3]); + } + + memcpy(cl->reader->ncd_skey, key, 16); + + // 6. Set card inserted + cl->reader->tcp_connected = 2; + cl->reader->card_status = CARD_INSERTED; + cl->reader->last_g = cl->reader->last_s = time((time_t *)0); + + // Only after connect() on cl->udp_fd (Linux) + cl->pfd = cl->udp_fd; + + if(cl->reader->ncd_disable_server_filt) // act like mgclient + { + network_cmd_no_data_send(handle, &cl->ncd_msgid, MSG_SERVER_2_CLIENT_GET_VERSION, key, COMMTYPE_CLIENT); + } + + return 0; +} + +static int32_t newcamd_connect(void) +{ + struct s_client *cl = cur_client(); + + if(cl->reader->tcp_connected < 2 && connect_newcamd_server() < 0) + { + return 0; + } + + if(!cl->udp_fd) + { + return 0; + } + + return 1; +} + +static int32_t newcamd_send(uint8_t *buf, int32_t ml, uint16_t sid) +{ + struct s_client *cl = cur_client(); + + if(!newcamd_connect()) + { + return -1; + } + + return network_message_send(cl->udp_fd, &cl->ncd_msgid, buf, ml, + cl->reader->ncd_skey, COMMTYPE_CLIENT, sid, NULL); +} + +static int32_t newcamd_recv(struct s_client *client, uint8_t *buf, int32_t UNUSED(l)) +{ + int32_t rc, rs; + + if(client->typ == 'c') + { + rs = network_message_receive(client->udp_fd, &client->ncd_msgid, buf, + client->ncd_skey, COMMTYPE_SERVER); + } + else + { + if(!client->udp_fd) + { + return (-1); + } + + rs = network_message_receive(client->udp_fd, &client->ncd_msgid, buf, + client->reader->ncd_skey, COMMTYPE_CLIENT); + } + + if(rs < 5) + { + rc = -1; + } + else + { + rc = rs; + } + + cs_log_dump_dbg(D_CLIENT, buf, rs, "received %d bytes from %s", rs, remote_txt()); + + client->last = time((time_t *) 0); + + if(rc == -1) + { + if(rs > 0) + { + cs_log("packet is too small (%d bytes)", rs); + } + else + { + cs_log("Connection closed to %s", remote_txt()); + } + } + + return (rc); +} + +static void mk_user_au_ftab(struct s_reader *aureader, FILTER *filt) +{ + int32_t i, j, found; + FILTER old_filter; + + memcpy(&old_filter, filt, sizeof(old_filter)); + memset(filt, 0, sizeof(*filt)); + + filt->caid = aureader->caid; + if(filt->caid == 0) + { + filt->caid = old_filter.caid; + } + + for(i = 0; i < aureader->nprov && filt->nprids < CS_MAXPROV; i++) + { + filt->prids[filt->nprids++] = b2i(3, &aureader->prid[i][1]); + } + + for(i = 0; i < old_filter.nprids && filt->nprids < CS_MAXPROV; i++) + { + for(j = found = 0; (!found) && (j < filt->nprids); j++) + { + if(old_filter.prids[i] == filt->prids[j]) + { + found = 1; + } + } + + if(!found) + { + filt->prids[filt->nprids++] = old_filter.prids[i]; + } + } +} + +static void mk_user_ftab(FILTER *filt) +{ + int32_t port_idx, i, j, k, c; + struct s_client *cl = cur_client(); + + memset(filt, 0, sizeof(*filt)); + + port_idx = cl->port_idx; + if(!cfg.ncd_ptab.ports[port_idx].ncd) + { + return; + } + + FILTER *psfilt = &cfg.ncd_ptab.ports[port_idx].ncd->ncd_ftab.filts[0]; + + // 1. CAID + // search server CAID in client CAID + for(c = i = 0; i < cl->ctab.ctnum; i++) + { + CAIDTAB_DATA *d = &cl->ctab.ctdata[i]; + + int32_t ctab_caid = d->caid & d->mask; + if(ctab_caid) + { + c++; + } + + if(psfilt->caid == ctab_caid) + { + filt->caid = ctab_caid; + break; + } + } + + if(c && !filt->caid) + { + cs_log("no valid CAID found in CAID for user '%s'", cl->account->usr); + return; + } + + // search CAID in client IDENT + cs_log_dbg(D_CLIENT, "client[%8lX].%s nfilts=%d, filt.caid=%04X", (unsigned long)pthread_self(), + cl->account->usr, cl->ftab.nfilts, filt->caid); + + if(!filt->caid && cl->ftab.nfilts) + { + int32_t fcaids; + for(i = fcaids = 0; i < cl->ftab.nfilts; i++) + { + uint16_t ucaid = cl->ftab.filts[i].caid; + if(ucaid) + { + fcaids++; + } + + if(ucaid && psfilt->caid == ucaid) + { + filt->caid = ucaid; + break; + } + } + + if(fcaids == cl->ftab.nfilts && !filt->caid) + { + cs_log("no valid CAID found in IDENT for user '%s'", cl->account->usr); + //cs_disconnect_client(); + return; + } + } + + // empty client CAID - use server CAID + if(!filt->caid) + { + filt->caid = psfilt->caid; + } + + // 2. PROVID + if(!cl->ftab.nfilts) + { + int32_t add; + for(i = 0; i < psfilt->nprids && filt->nprids < CS_MAXPROV; i++) + { + // use server PROVID(s) (and only those which are in user's groups) + add = 0; + struct s_reader *rdr; + + for(rdr = first_active_reader; rdr ; rdr = rdr->next) + { + if(rdr->grp & cl->grp) + { + if(!rdr->ftab.nfilts) + { + if(is_network_reader(rdr)) + { + add = 1; + } + + for(j = 0; !add && j < rdr->nprov; j++) + { + if(b2i(3, &rdr->prid[j][1]) == psfilt->prids[i]) + { + add = 1; + } + } + } + else + { + for(j = 0; !add && j < rdr->ftab.nfilts; j++) + { + uint32_t rcaid = rdr->ftab.filts[j].caid; + if(!rcaid || rcaid == filt->caid) + { + for(k = 0; !add && k < rdr->ftab.filts[j].nprids; k++) + { + if(rdr->ftab.filts[j].prids[k] == psfilt->prids[i]) + { + add = 1; + } + } + } + } + } + } + } + + if(add) + { + filt->prids[filt->nprids++] = psfilt->prids[i]; + } + } + + memcpy(filt, psfilt, sizeof(*filt)); + return; + } + + // search in client IDENT + for(j = 0; j < cl->ftab.nfilts; j++) + { + uint32_t ucaid = cl->ftab.filts[j].caid; + + cs_log_dbg(D_CLIENT, "client caid %d: %04X", j, ucaid); + + if(!ucaid || ucaid == filt->caid) + { + for(i = 0; i < psfilt->nprids && filt->nprids < CS_MAXPROV; i++) + { + cs_log_dbg(D_CLIENT, "search server provid %d: %06X", i, psfilt->prids[i]); + + if(cl->ftab.filts[j].nprids) + { + for(k = 0; k < cl->ftab.filts[j].nprids && filt->nprids < CS_MAXPROV; k++) + { + if(cl->ftab.filts[j].prids[k] == psfilt->prids[i]) + { + filt->prids[filt->nprids++] = cl->ftab.filts[j].prids[k]; + } + } + } + else + { + filt->prids[filt->nprids++] = psfilt->prids[i]; + // allow server PROVID(s) if no PROVID(s) specified in IDENT + } + } + } + } + + if(!filt->nprids) + { + cs_log("no valid PROVID(s) found in CAID for user '%s'", cl->account->usr); + //cs_disconnect_client(); + } +} + +static int8_t newcamd_auth_client(IN_ADDR_T ip, uint8_t *deskey) +{ + int32_t i, ok, rc, sid_list; + uint8_t *usr = NULL, *pwd = NULL; + struct s_auth *account; + uint8_t buf[14]; + uint8_t key[16]; + uint8_t passwdcrypt[120]; + struct s_reader *aureader = NULL, *rdr = NULL; + struct s_client *cl = cur_client(); + uint8_t mbuf[CWS_NETMSGSIZE]; + + sid_list = 0; + + ok = cfg.ncd_allowed ? check_ip(cfg.ncd_allowed, ip) : 1; + + if(!ok) + { + cs_auth_client(cl, (struct s_auth *)0, NULL); + return -1; + } + + // make random 14 bytes + get_random_bytes(buf, 14); + + // send init sequence + send(cl->udp_fd, buf, 14, 0); + nc_des_login_key_get(buf, deskey, 14, key); + memcpy(cl->ncd_skey, key, 16); + cl->ncd_msgid = 0; + + i = process_input(mbuf, sizeof(mbuf), cfg.cmaxidle); + if(i > 0) + { + if(mbuf[2] != MSG_CLIENT_2_SERVER_LOGIN) + { + cs_log_dbg(D_CLIENT, "expected MSG_CLIENT_2_SERVER_LOGIN (%02X), received %02X", + MSG_CLIENT_2_SERVER_LOGIN, mbuf[2]); + return -1; + } + + usr = mbuf + 5; + pwd = usr + cs_strlen((char *)usr) + 1; + } + else + { + cs_log_dbg(D_CLIENT, "bad client login request"); + return -1; + } + + cl->ncd_client_id = (mbuf[0] << 8) | mbuf[1]; + const char *client_name = newcamd_get_client_name(cl->ncd_client_id); + +#if defined(TCP_KEEPIDLE) + if(cl->ncd_client_id == 0x4453) // DiabloWifi has problems with TCPKeepAlive + { + int32_t flag = 600; + + // send first keepalive packet after 600 seconds of + // last package received (keepalive packets included) + if(setsockopt(cl->udp_fd, IPPROTO_TCP, TCP_KEEPIDLE, &flag, sizeof(flag)) && errno != EBADF) + { + cs_log("Setting TCP_KEEPIDLE failed, errno=%d, %s", errno, strerror(errno)); + } + else + { + cs_log("WARNING: Setting TCP_KEEPIDLE to 10 minutes for bugged DiabloWifi. Note that this might lead to not detected broken connections or multiple connections."); + } + } +#endif + + if(cl->ncd_proto == NCD_525 && 0x6D == mbuf[0] && 0x67 == mbuf[1] && 0x11 == cl->ncd_header[11]) + { + sid_list = 1; + } + + for(ok = 0, account = cfg.account; usr && account && (!ok); account = account->next) + { + cs_log_dbg(D_CLIENT, "account->usr=%s", account->usr); + + if(strcmp((char *)usr, account->usr) == 0) + { + __md5_crypt(ESTR(account->pwd), "$1$abcdefgh$", (char *)passwdcrypt); + cs_log_dbg(D_CLIENT, "account->pwd=%s", passwdcrypt); + + if(strcmp((char *)pwd, (const char *)passwdcrypt) == 0) + { + cl->crypted = 1; + char e_txt[20]; + + snprintf(e_txt, 20, "%s:%d", "newcamd", cfg.ncd_ptab.ports[cl->port_idx].s_port); + + if((rc = cs_auth_client(cl, account, e_txt)) == 2) + { + cs_log("hostname or ip mismatch for user %s (%s)", usr, client_name); + break; + } + else if(rc != 0) + { + cs_log("account is invalid for user %s (%s)", usr, client_name); + break; + } + else + { + cs_log("user %s authenticated successfully (%s)", usr, client_name); + ok = 1; + break; + } + } + else + { + cs_log("user %s is providing a wrong password (%s)", usr, client_name); + } + } + } + + if(!ok && !account) + { + cs_log("user %s is trying to connect but doesnt exist ! (%s)", usr, client_name); + usr = 0; + } + + // check for non ready reader and reject client + for(rdr = first_active_reader; rdr ; rdr = rdr->next) + { + if(!cfg.ncd_ptab.ports[cl->port_idx].ncd) + { + continue; + } + + if(rdr->caid == cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts[0].caid) + { + if(rdr->card_status == CARD_NEED_INIT) + { + cs_log("init for reader %s not finished -> reject client", rdr->label); + ok = 0; + } + break; + } + } + + if(ok) + { + LL_ITER itr = ll_iter_create(cl->aureader_list); + while((rdr = ll_iter_next(&itr))) + { + int32_t n; + + if(!cfg.ncd_ptab.ports[cl->port_idx].ncd) + { + continue; + } + + if(cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts[0].caid == 0 + && !rdr->audisabled && (is_network_reader(rdr) || rdr->card_status == CARD_INSERTED)) + { + aureader = rdr; + break; + } + + for(n = 0; n < cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts[0].nprids; n++) + { + if(emm_reader_match(rdr, cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts[0].caid, + cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts[0].prids[n])) + { + aureader = rdr; + break; + } + } + + if(aureader) + { + break; + } + } + + if(aureader) + { + cs_log("AU enabled for user %s on reader %s", usr, aureader->label); + } + else + { + cs_log("AU disabled for user %s", usr); + } + } + + network_cmd_no_data_send(cl->udp_fd, &cl->ncd_msgid, + (ok) ? MSG_CLIENT_2_SERVER_LOGIN_ACK : MSG_CLIENT_2_SERVER_LOGIN_NAK, + cl->ncd_skey, COMMTYPE_SERVER); + + if(ok) + { + FILTER usr_filter; + FILTER *pufilt = &usr_filter; + + nc_des_login_key_get(deskey, passwdcrypt, cs_strlen((char *)passwdcrypt), key); + memcpy(cl->ncd_skey, key, 16); + + i = process_input(mbuf, sizeof(mbuf), cfg.cmaxidle); + if(i > 0) + { + int32_t j, len = 15; + + if(mbuf[2] != MSG_CARD_DATA_REQ) + { + cs_log_dbg(D_CLIENT, "expected MSG_CARD_DATA_REQ (%02X), received %02X", + MSG_CARD_DATA_REQ, mbuf[2]); + return -1; + } + + mk_user_ftab(&usr_filter); + + // set userfilter for au enabled clients + if(aureader) + { +#ifdef WITH_EMU + if(aureader->typ == R_EMU) + { + usr_filter = *get_emu_prids_for_caid(aureader, cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts[0].caid); + } + else +#endif + mk_user_au_ftab(aureader, &usr_filter); + } + + ftab_clear(&cl->ftab); + + if(!cfg.ncd_mgclient) + { + ftab_add(&cl->ftab, &usr_filter); + } + + mbuf[0] = MSG_CARD_DATA; + mbuf[1] = 0x00; + mbuf[2] = 0x00; + + if(aureader) + { + mbuf[3] = 1; + } + else + { + mbuf[3] = get_threadnum(cl) + 10; // Unique user number + } + + mbuf[4] = (uint8_t)(pufilt->caid >> 8); + mbuf[5] = (uint8_t)(pufilt->caid); + mbuf[6] = 0x00; + mbuf[7] = 0x00; + + if(aureader) + { + hexserial_to_newcamd(aureader->hexserial, mbuf + 8, pufilt->caid); + } + else + { + memset(&mbuf[8], 0, 6); // mbuf[8] - mbuf[13] + } + + mbuf[14] = pufilt->nprids; + + for(j = 0; j < pufilt->nprids; j++) + { + if(caid_is_betacrypt(pufilt->caid) || caid_is_irdeto(pufilt->caid)) + { + mbuf[15 + 11 * j] = 0; + mbuf[16 + 11 * j] = 0; + mbuf[17 + 11 * j] = j; + } + else + { + mbuf[15 + 11 * j] = (uint8_t)(pufilt->prids[j] >> 16); + mbuf[16 + 11 * j] = (uint8_t)(pufilt->prids[j] >> 8); + mbuf[17 + 11 * j] = (uint8_t)(pufilt->prids[j]); + } + + mbuf[18 + 11 * j] = 0x00; + mbuf[19 + 11 * j] = 0x00; + mbuf[20 + 11 * j] = 0x00; + mbuf[21 + 11 * j] = 0x00; + + if(aureader) + { + // check if user provid from IDENT exists on card + int32_t k, found; + uint32_t rprid; + found = 0; + + if(pufilt->caid == aureader->caid && aureader->typ != R_EMU) + { + for(k = 0; k < aureader->nprov; k++) + { + rprid = b2i(3, &aureader->prid[k][1]); + if(rprid == pufilt->prids[j]) + { + if(caid_is_betacrypt(pufilt->caid) || caid_is_irdeto(pufilt->caid)) + { + mbuf[22 + 11 * j] = aureader->prid[k][0]; + mbuf[23 + 11 * j] = aureader->prid[k][1]; + mbuf[24 + 11 * j] = aureader->prid[k][2]; + mbuf[25 + 11 * j] = aureader->prid[k][3]; + } + else + { + mbuf[22 + 11 * j] = aureader->sa[k][0]; + mbuf[23 + 11 * j] = aureader->sa[k][1]; + mbuf[24 + 11 * j] = aureader->sa[k][2]; + mbuf[25 + 11 * j] = aureader->sa[k][3]; + } + + found = 1; + break; + } + } + } + + if(!found) + { + mbuf[22 + 11 * j] = 0x00; + mbuf[23 + 11 * j] = 0x00; + mbuf[24 + 11 * j] = 0x00; + mbuf[25 + 11 * j] = 0x00; + } + } + else + { + if(caid_is_betacrypt(pufilt->caid) || caid_is_irdeto(pufilt->caid)) + { + mbuf[22 + 11 * j] = 0x00; + mbuf[23 + 11 * j] = (uint8_t)(pufilt->prids[j] >> 16); + mbuf[24 + 11 * j] = (uint8_t)(pufilt->prids[j] >> 8); + mbuf[25 + 11 * j] = (uint8_t)(pufilt->prids[j]); + } + else + { + mbuf[22 + 11 * j] = 0x00; + mbuf[23 + 11 * j] = 0x00; + mbuf[24 + 11 * j] = 0x00; + mbuf[25 + 11 * j] = 0x00; + } + } + + len += 11; + } + + custom_data_t cd; + memset(&cd, 0, sizeof(cd)); + + if(aureader) + { + if((aureader->blockemm & EMM_GLOBAL) && !(aureader->saveemm & EMM_GLOBAL)) + { + cd.sid |= 4; + } + + if((aureader->blockemm & EMM_SHARED) && !(aureader->saveemm & EMM_SHARED)) + { + cd.sid |= 2; + } + + if((aureader->blockemm & EMM_UNIQUE) && !(aureader->saveemm & EMM_UNIQUE)) + { + cd.sid |= 1; + } + } + + if(network_message_send(cl->udp_fd, &cl->ncd_msgid, mbuf, len, key, COMMTYPE_SERVER, 0, &cd) < 0) + { + return -1; + } + } + + // send SID list + if(sid_list) + { + send_sid_list(); + } + } + else + { + cs_auth_client(cl, 0, usr ? "login failure" : "no such user"); + return -1; + } + + return 0; +} + +static void newcamd_send_dcw(struct s_client *client, ECM_REQUEST *er) +{ + int32_t len; + uint16_t cl_msgid; + uint8_t mbuf[19]; + + if(!client->udp_fd) + { + cs_log_dbg(D_CLIENT, "ncd_send_dcw: error: client->udp_fd=%d", client->udp_fd); + return; + } + + cl_msgid = er->msgid; + mbuf[0] = er->ecm[0]; + + if(er->rc >= E_NOTFOUND) // not found + { + len = 3; + mbuf[1] = mbuf[2] = 0x00; + } + else + { + len = 19; + mbuf[1] = mbuf[2] = 0x10; + memcpy(mbuf + 3, er->cw, 16); + } + + cs_log_dbg(D_CLIENT, "ncd_send_dcw: er->msgid=%d, cl_msgid=%d, %02X", er->msgid, cl_msgid, mbuf[0]); + + network_message_send(client->udp_fd, &cl_msgid, mbuf, len, client->ncd_skey, COMMTYPE_SERVER, 0, NULL); +} + +static void newcamd_process_ecm(struct s_client *cl, uint8_t *buf, int32_t len) +{ + int32_t pi; + ECM_REQUEST *er; + uint16_t ecmlen; + + if(len < 5) + { + return; + } + + ecmlen = SCT_LEN((&buf[2])); + if(ecmlen < 4 || ecmlen > MAX_ECM_SIZE || ecmlen + 2 > len) + { + return; + } + + if(!(er = get_ecmtask())) + { + return; + } + + // save client ncd_msgid + er->msgid = cl->ncd_msgid; + er->ecmlen = ecmlen; + + cs_log_dbg(D_CLIENT, "ncd_process_ecm: er->msgid=%d len=%d ecmlen=%d", er->msgid, len, er->ecmlen); + + er->srvid = cl->ncd_header[4] << 8 | cl->ncd_header[5]; + er->caid = cl->ncd_header[6] << 8 | cl->ncd_header[7]; + er->prid = cl->ncd_header[8] << 16 | cl->ncd_header[9] << 8 | cl->ncd_header[10]; + + if(!er->caid) + { + pi = cl->port_idx; + if(cfg.ncd_ptab.nports && cfg.ncd_ptab.nports >= pi && cfg.ncd_ptab.ports[pi].ncd) + { + er->caid = cfg.ncd_ptab.ports[pi].ncd->ncd_ftab.filts[0].caid; + } + } + + memcpy(er->ecm, buf + 2, er->ecmlen); + get_cw(cl, er); +} + +static void newcamd_process_emm(uint8_t *buf, int32_t len) +{ + int32_t ok = 1, provid; + uint16_t caid = 0; + struct s_client *cl = cur_client(); + EMM_PACKET epg; + + if(len < 3) + { + return; + } + + memset(&epg, 0, sizeof(epg)); + + epg.emmlen = SCT_LEN(buf); + if(epg.emmlen > MAX_EMM_SIZE || epg.emmlen > len) + { + return; + } + + caid = cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts[0].caid; + + epg.caid[0] = (uint8_t)(caid >> 8); + epg.caid[1] = (uint8_t)(caid); + + provid = cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts[0].prids[0]; + + epg.provid[0] = (uint8_t)(provid >> 24); + epg.provid[1] = (uint8_t)(provid >> 16); + epg.provid[2] = (uint8_t)(provid >> 8); + epg.provid[3] = (uint8_t)(provid); + + /*if(caid == 0x0500) + { + uint16_t emm_head; + + emm_head = (buf[0]<<8) | buf[1]; + switch( emm_head ) + { + case 0x8e70: // EMM-S + memcpy(epg.hexserial+1, buf+3, 4); + epg.hexserial[4]=aureader->hexserial[4]; + break; + case 0x8870: // EMM-U + case 0x8c70: // confidential? + default: + cs_log("unsupported emm type: %04X", emm_head); + ok=0; + } + + if(!ok) cs_log("only EMM-S supported"); + } + else*/ + + memcpy(epg.emm, buf, epg.emmlen); + + if(ok) + { + do_emm(cl, &epg); + } + + // Should always send an answer to client (also if au is disabled), + // some clients will disconnect if they get no answer + buf[1] = 0x10; + buf[2] = 0x00; + + network_message_send(cl->udp_fd, &cl->ncd_msgid, buf, 3, cl->ncd_skey, COMMTYPE_SERVER, 0, NULL); +} + +static void newcamd_report_cards(struct s_client *client) +{ + int32_t j, k, l; + uint8_t buf[512]; + custom_data_t *cd; + + if(!cs_malloc(&cd, sizeof(struct custom_data))) + { + return; + } + + memset(buf, 0, sizeof(buf)); + + cd->sid = cfg.ncd_ptab.ports[client->port_idx].s_port; + buf[0] = MSG_SERVER_2_CLIENT_ADDCARD; + + struct s_reader *rdr; + for(rdr = first_active_reader; rdr ; rdr = rdr->next) + { + int32_t flt = 0; + + if(!(rdr->grp & client->grp)) + { + continue; // test - skip unaccesible readers + } + + if(rdr->ftab.filts) + { + for(j = 0; j < rdr->ftab.nfilts; j++) + { + if(rdr->ftab.filts[j].caid) + { + cd->caid = rdr->ftab.filts[j].caid; + + if(!rdr->ftab.filts[j].nprids) + { + cd->provid = 0; + cs_log_dbg(D_CLIENT, "newcamd: extended: report card %04X@%06X svc", cd->caid, cd->provid); + + network_message_send(client->udp_fd, &client->ncd_msgid, buf, 3, + client->ncd_skey, COMMTYPE_SERVER, 0, cd); + } + + for(k = 0; k < rdr->ftab.filts[j].nprids; k++) + { + cd->provid = rdr->ftab.filts[j].prids[k]; + cs_log_dbg(D_CLIENT, "newcamd: extended: report card %04X@%06X svc", cd->caid, cd->provid); + + network_message_send(client->udp_fd, &client->ncd_msgid, buf, 3, + client->ncd_skey, COMMTYPE_SERVER, 0, cd); + flt = 1; + } + } + } + } + + if(rdr->caid && !flt) + { + if((rdr->tcp_connected || rdr->card_status == CARD_INSERTED)) + { + cd->caid = rdr->caid; + if(!rdr->nprov) + { + cd->provid = 0; + cs_log_dbg(D_CLIENT, "newcamd: extended: report card %04X@%06X caid", cd->caid, cd->provid); + + network_message_send(client->udp_fd, &client->ncd_msgid, buf, 3, + client->ncd_skey, COMMTYPE_SERVER, 0, cd); + } + + for(j = 0; j < rdr->nprov; j++) + { + cd->provid = (rdr->prid[j][1]) << 16 | (rdr->prid[j][2] << 8) | rdr->prid[j][3]; + cs_log_dbg(D_CLIENT, "newcamd: extended: report card %04X@%06X caid", cd->caid, cd->provid); + + network_message_send(client->udp_fd, &client->ncd_msgid, buf, 3, + client->ncd_skey, COMMTYPE_SERVER, 0, cd); + } + } + } + } + + if(cfg.sidtab && client->account) + { + struct s_sidtab *ptr; + for(j = 0, ptr = cfg.sidtab; ptr; ptr = ptr->next, j++) + { + if(client->account->sidtabs.ok & ((SIDTABBITS)1 << j)) + { + for(k = 0; k < ptr->num_caid; k++) + { + cd->caid = ptr->caid[k]; + + if(!ptr->num_provid) + { + cd->provid = 0; + cs_log_dbg(D_CLIENT, "newcamd: extended: report card %04X@%06X acs", cd->caid, cd->provid); + + network_message_send(client->udp_fd, &client->ncd_msgid, buf, 3, + client->ncd_skey, COMMTYPE_SERVER, 0, cd); + } + + for(l = 0; l < ptr->num_provid; l++) + { + cd->provid = ptr->provid[l]; + cs_log_dbg(D_CLIENT, "newcamd: extended: report card %04X@%06X acs", cd->caid, cd->provid); + + network_message_send(client->udp_fd, &client->ncd_msgid, buf, 3, + client->ncd_skey, COMMTYPE_SERVER, 0, cd); + } + } + } + } + } + + NULLFREE(cd); +} + +static void newcamd_server_init(struct s_client *client) +{ + int8_t res = 0; + + client->ncd_server = 1; + cs_log("client connected to %d port", cfg.ncd_ptab.ports[client->port_idx].s_port); + + if(cfg.ncd_ptab.ports[client->port_idx].ncd && cfg.ncd_ptab.ports[client->port_idx].ncd->ncd_key_is_set) + { + // port has a des key specified + res = newcamd_auth_client(client->ip, cfg.ncd_ptab.ports[client->port_idx].ncd->ncd_key); + } + else + { + // default global des key + res = newcamd_auth_client(client->ip, cfg.ncd_key); + } + + if(res == -1) + { + cs_disconnect_client(client); + return; + } + + // report all cards if using extended mg proto + if(cfg.ncd_mgclient) + { + cs_log_dbg(D_CLIENT, "newcamd: extended: report all available cards"); + newcamd_report_cards(client); + } + +} + +#define EXT_VERSION_STR "1.67" +#define EXT_VERSION_LEN 4 + +static void newcamd_send_version(struct s_client *client) +{ + uint8_t buf[30]; + memset(buf, 0, sizeof(buf)); + + buf[0] = MSG_SERVER_2_CLIENT_GET_VERSION; + buf[1] = EXT_VERSION_LEN >> 8; + buf[2] = EXT_VERSION_LEN & 0xFF; + memcpy(buf + 3, EXT_VERSION_STR, EXT_VERSION_LEN); + + network_message_send(client->udp_fd, &client->ncd_msgid, buf, EXT_VERSION_LEN + 3, + client->ncd_skey, COMMTYPE_SERVER, 0, NULL); +} + +static void *newcamd_server(struct s_client *client, uint8_t *mbuf, int32_t len) +{ + // check for clienttimeout, if timeout occurs try to send keepalive / wait for answer + // befor client was disconnected. If keepalive was disabled, exit after clienttimeout + + if(len < 3) + { + return NULL; + } + + cs_log_dbg(D_CLIENT, "newcamd: got cmd %d", mbuf[2]); + + switch(mbuf[2]) + { + case 0x80: + case 0x81: + { + newcamd_process_ecm(client, mbuf, len); + break; + } + + case MSG_SERVER_2_CLIENT_GET_VERSION: + { + cs_log_dbg(D_CLIENT, "newcamd: extended: send Version 1.67"); + newcamd_send_version(client); + break; + } + + case MSG_KEEPALIVE: + { + newcamd_reply_ka(); + break; + } + + default: + { + if(mbuf[2] > 0x81 && mbuf[2] < 0x92) + { + newcamd_process_emm(mbuf + 2, len - 2); + } + else + { + cs_log_dbg(D_CLIENT, "unknown newcamd command! (%d)", mbuf[2]); + } + } + } + + return NULL; +} + +void newcamd_idle(void) +{ + struct s_client *client = cur_client(); + struct s_reader *rdr = client->reader; + + if(!rdr) + { + return; + } + + if(rdr->tcp_ito > 0) + { + // inactivitytimeout > 0 enables protocol keepalive packages + time_t now; + int32_t time_diff; + + time(&now); + time_diff = llabs(now - rdr->last_s); + + if(time_diff > (rdr->tcp_ito)) + { + if(client->ncd_keepalive) + { + newcamd_reply_ka(); + } + else + { + network_tcp_connection_close(client->reader, "inactivity"); + } + } + } + else if(rdr->tcp_ito == -1) + { + // idle reconnect + newcamd_connect(); + } +} + +/* +* client functions +*/ + +int32_t newcamd_client_init(struct s_client *client) +{ + client->ncd_proto = client->reader->ncd_proto; + + cs_log("proxy %s:%d newcamd52%d (fd=%d)", client->reader->device, client->reader->r_port, + (client->reader->ncd_proto == NCD_525) ? 5 : 4, client->udp_fd); + + // try to connect. ignore possible failures + // idle reconnect (tcp_ito = -1) will trigger an additional connect anyway + if(client->reader->ncd_connect_on_init && client->reader->tcp_ito != -1) + { + newcamd_connect(); + } + + return 0; +} + +static int32_t newcamd_send_ecm(struct s_client *client, ECM_REQUEST *er) +{ + struct s_reader *rdr = client->reader; + + if(!newcamd_connect()) + { + return -1; + } + + // check server filters + if(!chk_rsfilter(rdr, er)) + { + return -1; + } + + uint8_t *buf; + if(!cs_malloc(&buf, er->ecmlen)) + { + return -1; + } + + memcpy(buf, er->ecm, er->ecmlen); + + client->ncd_header[4] = er->srvid >> 8; + client->ncd_header[5] = er->srvid & 0xFF; + client->ncd_header[6] = er->caid >> 8; + client->ncd_header[7] = er->caid & 0xFF; + client->ncd_header[8] = er->prid >> 16; + client->ncd_header[9] = er->prid >> 8; + client->ncd_header[10] = er->prid & 0xFF; + + int32_t rc = (newcamd_send(buf, er->ecmlen, er->srvid) < 1) ? -1 : 0; + + NULLFREE(buf); + return (rc); +} + +static int32_t newcamd_send_emm(EMM_PACKET *ep) +{ + uint8_t buf[ep->emmlen]; + + if(!newcamd_connect()) + { + return -1; + } + + memcpy(buf, ep->emm, ep->emmlen); + + return (newcamd_send(buf, ep->emmlen, 0) < 1) ? 0 : 1; +} + +static int32_t newcamd_recv_chk(struct s_client *client, uint8_t *dcw, int32_t *rc, uint8_t *buf, int32_t n) +{ + uint16_t idx = -1; + + if(n < 5) + { + return -1; + } + + switch(buf[2]) + { + case 0x80: + case 0x81: + { + idx = (buf[0] << 8) | buf[1]; + + if(n == 5) // not found on server + { + *rc = 0; + memset(dcw, 0, 16); + break; + } + + if(n < 21) + { + cs_log_dbg(D_CLIENT, "invalid newcamd answer"); + return (-1); + } + + *rc = 1; + memcpy(dcw, buf + 5, 16); + break; + } + + case MSG_KEEPALIVE: + { + return -1; + } + + case MSG_SERVER_2_CLIENT_ADDCARD: + { + if(client->reader) + { + client->reader->ncd_disable_server_filt = 1; + } + + return -1; + } + + default: + { + if(buf[2] > 0x81 && buf[2] < 0x92) // answer to emm + { + return -1; + } + + cs_log_dbg(D_CLIENT, "unknown newcamd command from server"); + return -1; + } + } + + return idx; +} + +/* + * resolve client type for newcamd protocol + */ +const char *newcamd_get_client_name(uint16_t client_id) +{ + // When adding new entries keep the list sorted! + static const struct + { + uint16_t id; + const char *client; + } ncd_service_ids[] = + { + { 0x0000, "generic" }, + { 0x02C2, "Opticum" }, + { 0x0665, "rq-sssp-client-CS" }, + { 0x0666, "rqcamd" }, + { 0x0667, "rq-echo-client" }, + { 0x0669, "rq-sssp-client-CW" }, + { 0x0769, "JlsRq" }, + { 0x414C, "AlexCS" }, + { 0x4333, "camd3" }, + { 0x4343, "CCcam" }, + { 0x434C, "Cardlink" }, + { 0x4453, "DiabloCam-UW" }, + { 0x4543, "eyetvCamd" }, + { 0x4765, "Octagon" }, + { 0x4C43, "LCE" }, + { 0x4E58, "NextYE2k" }, + { 0x5342, "SBCL" }, + { 0x5456, "Tecview" }, + { 0x5644, "vdr-sc" }, + { 0x5743, "WiCard" }, + { 0x6378, "cx" }, + { 0x6502, "Tvheadend" }, + { 0x6576, "evocamd" }, + { 0x6762, "gbox2CS" }, + { 0x6B61, "Kaffeine" }, + { 0x6B63, "kpcs" }, + { 0x6D63, "mpcs" }, + { 0x6D67, "mgcamd" }, + { 0x6E65, "NextYE2k" }, + { 0x6E73, "NewCS" }, + { 0x7264, "radegast" }, + { 0x7363, "Scam" }, + { 0x7763, "WinCSC" }, + { 0x7878, "tsdecrypt" }, + { 0x8888, "OSCam" }, + { 0x9911, "ACamd" }, + { 0x9922, "DVBplug" }, + { 0xFFFF, NULL } + }; + + int i = 0; + + while(1) + { + if(!ncd_service_ids[i].client) + { + break; + } + + if(ncd_service_ids[i].id == client_id) + { + return ncd_service_ids[i].client; + } + + i++; + } + + return "unknown - please report"; +} + +void module_newcamd(struct s_module *ph) +{ + ph->desc = "newcamd"; + ph->type = MOD_CONN_TCP; + ph->large_ecm_support = 1; + ph->listenertype = LIS_NEWCAMD; + IP_ASSIGN(ph->s_ip, cfg.ncd_srvip); + ph->s_handler = newcamd_server; + ph->s_init = newcamd_server_init; + ph->recv = newcamd_recv; + ph->send_dcw = newcamd_send_dcw; + ph->ptab = cfg.ncd_ptab; + ph->c_init = newcamd_client_init; + ph->c_recv_chk = newcamd_recv_chk; + ph->c_send_ecm = newcamd_send_ecm; + ph->c_send_emm = newcamd_send_emm; + ph->c_idle = newcamd_idle; + ph->num = R_NEWCAMD; +} +#endif diff --git a/module-newcamd.h b/module-newcamd.h new file mode 100644 index 0000000..e4921c6 --- /dev/null +++ b/module-newcamd.h @@ -0,0 +1,6 @@ +#ifndef MODULE_NEWCAMD_H_ +#define MODULE_NEWCAMD_H_ + +const char *newcamd_get_client_name(uint16_t client_id); + +#endif diff --git a/module-pandora.c b/module-pandora.c new file mode 100644 index 0000000..8a81f1f --- /dev/null +++ b/module-pandora.c @@ -0,0 +1,321 @@ +#define MODULE_LOG_PREFIX "pandora" + +#include "globals.h" + +#ifdef MODULE_PANDORA + +#include "cscrypt/md5.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-net.h" +#include "oscam-string.h" + +#define CWS_NETMSGSIZE 320 +#define START_TIME 150000 +#define MAX_TIME 500000 + +static void simple_crypt(uint8_t *buf, int len, uint8_t *key, int key_len) +{ + int i, x; + for(i = 0, x = 0; i < len; i++) + { + buf[i] ^= key[x++]; + if(x >= key_len) + { x = 0; } + } +} + +static void pandora_process_request(struct s_client *cl, uint8_t *buf, int32_t l) +{ + int ecmlen; + ECM_REQUEST *er; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + + if(l < 10 + CS_ECMSTORESIZE + 2) + { return; } + + if(!(er = get_ecmtask())) + { return; } + er->caid = b2i(2, buf + 1); + er->srvid = b2i(2, buf + 3); + er->prid = b2i(4, buf + 5); + //er->ecmcrc32 = crc32(0L, buf+10, CS_ECMSTORESIZE); + er->chid = b2i(2, buf + 10 + CS_ECMSTORESIZE); + + if(!cl->pand_ignore_ecm && (l >= 10 + CS_ECMSTORESIZE + 2 + 2)) + { + ecmlen = b2i(2, buf + 10 + CS_ECMSTORESIZE + 2); + + if(ecmlen < 0 || ecmlen > MAX_ECM_SIZE + || ((10 + CS_ECMSTORESIZE + 2 + 2 + ecmlen) > CWS_NETMSGSIZE) + || ((10 + CS_ECMSTORESIZE + 2 + 2 + ecmlen) > l)) + { + er->ecmlen = 0; + } + else + { + if(!memcmp(buf + 10, MD5(buf + 14 + CS_ECMSTORESIZE, ecmlen, md5tmp), CS_ECMSTORESIZE)) + { + er->ecmlen = ecmlen; + memcpy(er->ecm, buf + 14 + CS_ECMSTORESIZE, ecmlen); + //set_ecmhash(cl, er); + } + else + { er->ecmlen = 0; } + } + } + else + { er->ecmlen = 0; } + + if(!er->ecmlen) + { usleep(cl->pand_autodelay); } + get_cw(cl, er); +} + +static int pandora_recv(struct s_client *cl, uint8_t *buf, int32_t l) +{ + int ret; + + if(!cl->udp_fd) + { return (-9); } + if(cl->typ != 'c') + { ret = recv_from_udpipe(buf); } + else + { + ret = recvfrom(cl->udp_fd, buf, l, 0, (struct sockaddr *)&cl->udp_sa, &cl->udp_sa_len); + } + if(ret < 1) + { return (-1); } + + simple_crypt(buf, ret, cl->pand_md5_key, 16); + cl->last = time((time_t *) 0); + + if(cl->typ != 'c') + { pandora_process_request(cl, buf, ret); } + return (ret); +} + +static void pandora_send_dcw(struct s_client *cl, ECM_REQUEST *er) +{ + uint8_t msgbuf[CWS_NETMSGSIZE], len; + if(cfg.pand_skip_send_dw) + { return; } + if(er->rc < E_NOTFOUND) + { + msgbuf[0] = 2; // DW_FOUND + memcpy(&msgbuf[1], er->cw, 16); + len = 1 + 16; + cl->pand_autodelay = START_TIME; + } + else + { + msgbuf[0] = 0xFF; // DW_NOT_FOUND + len = 1; + if(cl->pand_autodelay < MAX_TIME) + { cl->pand_autodelay += 100000; } + } + simple_crypt(msgbuf, len, cl->pand_md5_key, 16); + sendto(cl->udp_fd, msgbuf, len, 0, (struct sockaddr *) &cl->udp_sa, cl->udp_sa_len); +} + +int pandora_auth_client(struct s_client *cl, IN_ADDR_T ip) +{ + int ok; + struct s_auth *account; + +#ifdef IPV6SUPPORT + // FIXME: Add IPv6 support + (void)ip; // Prevent warning about unused var "ip" +#else + if(!cl->pand_ignore_ecm && cfg.pand_allowed) + { + struct s_ip *p_ip; + for(ok = 0, p_ip = cfg.pand_allowed; (p_ip) && (!ok); p_ip + = p_ip->next) + { ok = ((ip >= p_ip->ip[0]) && (ip <= p_ip->ip[1])); } + + if(!ok) + { + cs_auth_client(cl, (struct s_auth *) 0, "IP not allowed"); + return 0; + } + } +#endif + + for(ok = 0, account = cfg.account; cfg.pand_usr && account && !ok; account = account->next) + { + ok = streq(cfg.pand_usr, account->usr); + if(ok && cs_auth_client(cl, account, NULL)) + { cs_disconnect_client(cl); } + } + if(!ok) + { cs_auth_client(cl, (struct s_auth *)(-1), NULL); } + return ok; +} + +static void *pandora_server(struct s_client *cl, uint8_t *UNUSED(mbuf), int32_t UNUSED(len)) +{ + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + if(!cl->init_done) + { + if(cfg.pand_pass) + { + cl->pand_autodelay = 150000; + memcpy(cl->pand_md5_key, MD5((uint8_t *)cfg.pand_pass, cs_strlen(cfg.pand_pass), md5tmp), 16); + cl->pand_ignore_ecm = (cfg.pand_ecm) ? 0 : 1; + cl->crypted = 1; + pandora_auth_client(cl, cl->ip); + cl->init_done = 1; + } + else + { + cs_log("Password for Pandora share MUST be set !!!"); + } + } + return NULL; +} + +/************************************************************************************************************************ + * client functions + *************************************************************************************************************************/ +int pandora_client_init(struct s_client *cl) +{ + static struct sockaddr_in loc_sa; + int16_t p_proto; + char ptxt[16]; + struct s_reader *rdr = cl->reader; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + + cl->pfd = 0; + if(rdr->r_port <= 0) + { + cs_log("invalid port %d for server %s", rdr->r_port, rdr->device); + return (1); + } + p_proto = IPPROTO_UDP; + + set_null_ip(&cl->ip); + memset((char *) &loc_sa, 0, sizeof(loc_sa)); + loc_sa.sin_family = AF_INET; + + if(IP_ISSET(cfg.srvip)) + { IP_ASSIGN(SIN_GET_ADDR(loc_sa), cfg.srvip); } + else + { loc_sa.sin_addr.s_addr = INADDR_ANY; } + loc_sa.sin_port = htons(rdr->l_port); + + if((cl->udp_fd = socket(PF_INET, SOCK_DGRAM, p_proto)) < 0) + { + cs_log("Socket creation failed (errno=%d)", errno); + return 1; + } + + int32_t opt = 1; + setsockopt(cl->udp_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + set_so_reuseport(cl->udp_fd); + + set_socket_priority(cl->udp_fd, cfg.netprio); + + if(rdr->l_port > 0) + { + if(bind(cl->udp_fd, (struct sockaddr *) &loc_sa, sizeof(loc_sa)) < 0) + { + cs_log("bind failed (errno=%d)", errno); + close(cl->udp_fd); + return (1); + } + snprintf(ptxt, sizeof(ptxt), ", port=%d", rdr->l_port); + } + else + { ptxt[0] = '\0'; } + + memcpy(cl->pand_md5_key, MD5((uint8_t *)rdr->r_pwd, cs_strlen(rdr->r_pwd), md5tmp), 16); + cl->crypted = 1; + + //cl->grp = 0xFFFFFFFF; + //rdr->caid[0] = rdr->ctab.caid[0]; + + cl->pand_send_ecm = rdr->pand_send_ecm; + memset((char *) &cl->udp_sa, 0, sizeof(cl->udp_sa)); +#ifdef IPV6SUPPORT + ((struct sockaddr_in *)(&cl->udp_sa))->sin_family = AF_INET; + ((struct sockaddr_in *)(&cl->udp_sa))->sin_port = htons((u_short) rdr->r_port); +#else + cl->udp_sa.sin_family = AF_INET; + cl->udp_sa.sin_port = htons((u_short) rdr->r_port); +#endif + + cs_log("proxy %s:%d pandora %s (%s)", rdr->device, rdr->r_port, rdr->pand_send_ecm ? "with ECM support" : "", ptxt); + + cl->pfd = cl->udp_fd; + //set_nonblock(cl->udp_fd, true); //!!!!! + return (0); +} + +static int pandora_send_ecm(struct s_client *cl, ECM_REQUEST *er) +{ + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + uint8_t msgbuf[CWS_NETMSGSIZE]; + int ret, len; + uint8_t adel; + adel = (cfg.ctimeout > 7) ? 7 : cfg.ctimeout; + + msgbuf[0] = 1; + msgbuf[1] = er->caid >> 8; + msgbuf[2] = er->caid & 0xFF; + msgbuf[3] = er->srvid >> 8; + msgbuf[4] = er->srvid & 0xFF; + msgbuf[5] = er->prid >> 24; + msgbuf[6] = er->prid >> 16; + msgbuf[7] = er->prid >> 8; + msgbuf[8] = er->prid & 0xFF; + msgbuf[9] = adel; + memcpy(&msgbuf[10], MD5(er->ecm, er->ecmlen, md5tmp), CS_ECMSTORESIZE); + msgbuf[10 + CS_ECMSTORESIZE] = er->chid >> 8; + msgbuf[11 + CS_ECMSTORESIZE] = er->chid & 0xFF; + len = 12 + CS_ECMSTORESIZE; + if(cl->pand_send_ecm) + { + if(len+2+er->ecmlen > CWS_NETMSGSIZE) + { return -1; } + msgbuf[12 + CS_ECMSTORESIZE] = er->ecmlen >> 8; + msgbuf[13 + CS_ECMSTORESIZE] = er->ecmlen & 0xFF; + memcpy(&msgbuf[14 + CS_ECMSTORESIZE], er->ecm, er->ecmlen); + len += er->ecmlen + 2; + } + simple_crypt(msgbuf, len, cl->pand_md5_key, 16); + ret = sendto(cl->pfd, msgbuf, len, 0, (struct sockaddr *) &cl->udp_sa, cl->udp_sa_len); + return ((ret < len) ? (-1) : 0); +} + +static int pandora_recv_chk(struct s_client *UNUSED(cl), uint8_t *dcw, int *rc, uint8_t *buf, int UNUSED(n)) +{ + if(buf[0] != 0x2) + { return (-1); } + *rc = 1; + memcpy(dcw, buf + 1, 16); + return (0); +} + +void module_pandora(struct s_module *ph) +{ + ph->ptab.nports = 1; + ph->ptab.ports[0].s_port = cfg.pand_port; + ph->num = R_PANDORA; + + ph->desc = "pandora"; + ph->type = MOD_CONN_UDP; + ph->large_ecm_support = 1; + //ph->watchdog = 1; + IP_ASSIGN(ph->s_ip, cfg.pand_srvip); + ph->s_handler = pandora_server; + ph->recv = pandora_recv; + ph->send_dcw = pandora_send_dcw; + + ph->c_init = pandora_client_init; + ph->c_recv_chk = pandora_recv_chk; + ph->c_send_ecm = pandora_send_ecm; +} + +#endif diff --git a/module-radegast.c b/module-radegast.c new file mode 100644 index 0000000..c982b0a --- /dev/null +++ b/module-radegast.c @@ -0,0 +1,407 @@ +#define MODULE_LOG_PREFIX "radegast" + +#include "globals.h" +#ifdef MODULE_RADEGAST +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-reader.h" +#ifdef MODULE_STREAMRELAY +#include "module-streamrelay.h" +#include "oscam-chk.h" +#endif + +static int32_t radegast_connect(void); + +static int32_t radegast_send(struct s_client *client, uint8_t *buf) +{ + int32_t l = buf[1] + 2; + return (send(client->pfd, buf, l, 0)); +} + +static int32_t radegast_recv(struct s_client *client, uint8_t *buf, int32_t l) +{ + int32_t n; + if(!client->pfd) { return (-1); } + if(client->typ == 'c') // server code + { + if((n = cs_recv(client->pfd, buf, l, 0)) > 0) + { client->last = time((time_t *) 0); } + } + else // client code + { + if((n = cs_recv(client->pfd, buf, l, 0)) > 0) + { + cs_log_dump_dbg(D_CLIENT, buf, n, "radegast: received %d bytes from %s", n, remote_txt()); + client->last = time((time_t *) 0); + if((buf[0] == 0x02) && (buf[1] == 0x12) && (buf[2] == 0x05) && (buf[3] == 0x10)) { return (n); } // dcw received + else if((buf[0] == 0x02) && (buf[1] == 0x02) && (buf[2] == 0x04) && (buf[3] == 0x00)) { return (n); } // dcw no found + else if((buf[0] == 0x81) && (buf[1] == 0x00)) { return (n); } // cmd unknown + else { n = -1; }// no cmd radegast disconnect + } + } + return (n); +} + +static int32_t radegast_recv_chk(struct s_client *client, uint8_t *dcw, int32_t *rc, uint8_t *buf, int32_t UNUSED(n)) +{ + if((buf[0] == 2) && (buf[1] == 0x12)) + { + char tmp_dbg[33]; + memcpy(dcw, buf + 4, 16); + cs_log_dbg(D_CLIENT, "radegast: recv chk - %s", cs_hexdump(0, dcw, 16, tmp_dbg, sizeof(tmp_dbg))); + *rc = 1; + return (client->reader->msg_idx); + } + + return (-1); +} + +static void radegast_auth_client(IN_ADDR_T ip) +{ + int32_t ok; + struct s_auth *account; + struct s_client *cl = cur_client(); + + ok = check_ip(cfg.rad_allowed, ip); + + if(!ok) + { + cs_log("radegast: IP not allowed"); + cs_auth_client(cl, (struct s_auth *)0, NULL); + cs_disconnect_client(cl); + } + + for(ok = 0, account = cfg.account; cfg.rad_usr && account && !ok; account = account->next) + { + ok = streq(cfg.rad_usr, account->usr); + if(ok && cs_auth_client(cl, account, NULL)) + { cs_disconnect_client(cl); } + } + + if(!ok) + { cs_auth_client(cl, ok ? account : (struct s_auth *)(-1), "radegast"); } +} + +static void radegast_send_dcw(struct s_client *client, ECM_REQUEST *er) +{ + uint8_t mbuf[1024]; + mbuf[0] = 0x02; // DCW + if(er->rc < E_NOTFOUND) + { +#ifdef MODULE_STREAMRELAY + if((cfg.stream_relay_ctab.ctnum == 0 || chk_ctab_ex(er->caid, &cfg.stream_relay_ctab)) && cfg.stream_relay_enabled) + { + stream_write_cw(er); + } +#endif + mbuf[1] = 0x12; // len (overall) + mbuf[2] = 0x05; // ACCESS + mbuf[3] = 0x10; // len + memcpy(mbuf + 4, er->cw, 16); + } + else + { + mbuf[1] = 0x02; // len (overall) + mbuf[2] = 0x04; // NO ACCESS + mbuf[3] = 0x00; // len + } + radegast_send(client, mbuf); +} + +static void radegast_process_ecm(uint8_t *buf, int32_t l) +{ + int32_t i, n, sl; + ECM_REQUEST *er; + struct s_client *cl = cur_client(); + + if(!(er = get_ecmtask())) + { return; } + + for(i = 0; i+1 < l; i += (sl + 2)) + { + sl = buf[i + 1]; + + switch(buf[i]) + { + case 2: // CAID (upper byte only, oldstyle) + if(i+2 >= l) + { break; } + er->caid = buf[i + 2] << 8; + break; + + case 10: // CAID + if(i+3 >= l) + { break; } + er->caid = b2i(2, buf + i + 2); + break; + + case 3: // ECM DATA + if(i+4 >= l) + { break; } + er->ecmlen = (((buf[i + 1 + 2] & 0x0F) << 8) | buf[i + 2 + 2]) + 3; + if(er->ecmlen < 3 || er->ecmlen > MAX_ECM_SIZE || i+2+er->ecmlen > l) + { break; } + memcpy(er->ecm, buf + i + 2, er->ecmlen); + break; + + case 6: // PROVID (ASCII) + if(i+2+sl > l) + { break; } + n = (sl > 6) ? 3 : (sl >> 1); + er->prid = cs_atoi((char *) buf + i + 2 + sl - (n << 1), n, 0); + break; + + case 7: // KEYNR (ASCII), not needed + break; + + case 8: // ECM PROCESS PID ?? don't know, not needed + break; + + case 9: // Adding srvid because ECM contains 0000 + if(i+2 >= l) + { break; } + er->srvid = (buf[i + 4] << 8 ) | (buf[i + 2]); + break; + } + } + + if(l != i) + { cs_log("WARNING: ECM-request corrupt"); } + else + { get_cw(cl, er); } +} + +static void radegast_process_unknown(uint8_t *buf) +{ + uint8_t answer[2] = {0x81, 0x00}; + radegast_send(cur_client(), answer); + cs_log("unknown request %02X, len=%d", buf[0], buf[1]); +} + +static void *radegast_server(struct s_client *client, uint8_t *mbuf, int32_t n) +{ + if(n < 3) + { return NULL; } + + if(!client->init_done) + { + radegast_auth_client(cur_client()->ip); + client->init_done = 1; + } + + switch(mbuf[0]) + { + case 1: + radegast_process_ecm(mbuf + 2, mbuf[1]); + break; + + default: + radegast_process_unknown(mbuf); + } + + return NULL; +} + +static int32_t radegast_send_ecm(struct s_client *client, ECM_REQUEST *er) +{ + uint8_t provid_buf[8]; + uint8_t header[22] +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L //gcc-15+ + __attribute__((nonstring)) +#endif + = "\x02\x01\x00\x06\x08\x30\x30\x30\x30\x30\x30\x30\x30\x07\x04\x30\x30\x30\x38\x08\x01\x02"; + uint8_t *ecmbuf; + + uint8_t *SubECMp; + uint8_t *via_ecm_mod; + uint32_t n, k, Len, pos = 0; + + if(!radegast_connect()) + { return (-1); } + + if(!cs_malloc(&ecmbuf, er->ecmlen + 30)) + { return -1; } + + // Quickfix to suppress SubECMs with CWsSwap set to 01 + // Applied only on Viaccess (CAID: 0x0500) + // this reduce the size of the ECM from long to short + // 40 07 03 0B 00 08 07 01 00 ... -> to keep + // 40 07 03 0B 00 08 07 01 01 ... -> to delete + // Thanks to luffy for the tip and the code. + + if(er->caid == 0x500) + { + cs_log_dump_dbg(D_ATR, er->ecm, er->ecmlen, "%s: ecm dump BEFORE suppressing SubECMs with CWsSwap set to 01", __func__); + Len = er->ecmlen; + if(cs_malloc (&via_ecm_mod, Len+4)) + { + if(er->ecm[4] == 0x80) + { + memcpy(via_ecm_mod, er->ecm, 4); + via_ecm_mod[1] = 0x70; + via_ecm_mod[2] = 0x01; + pos = 0x04; + k = 4; + while(k < Len) + { + SubECMp = (uint8_t *)&er->ecm[k]; + if(((pos + SubECMp[1] + 2) > 0xE0) || (pos + SubECMp[1] + 2) > Len) + { + break; + } + + if (SubECMp[2] == 0xD2) + { + if(SubECMp[0x0E] == 0x00) + { + memcpy(via_ecm_mod+pos, SubECMp, SubECMp[1] + 2); + via_ecm_mod[2] += SubECMp[1] + 2; + pos += SubECMp[1] + 2; + } + } + else if ((SubECMp[2] == 0x90 || SubECMp[2] == 0x40) && SubECMp[3] == 0x07) + { + if(SubECMp[0x0A] == 0x00 || SubECMp[0x0A] == 0xFF) + { + memcpy(via_ecm_mod + pos, SubECMp, SubECMp[1] + 2); + via_ecm_mod[2] += SubECMp[1] + 2; + pos += SubECMp[1] + 2; + } + } + k += SubECMp[1] + 2; + } + Len = via_ecm_mod[2] + 3; + er->ecmlen = Len; + memcpy(er->ecm, via_ecm_mod, Len); + cs_log_dump_dbg(D_ATR, er->ecm, er->ecmlen, "%s: ecm dump AFTER suppressing SubECMs with CWsSwap set to 01", __func__); + } + NULLFREE(via_ecm_mod); + } + + } + + ecmbuf[0] = 1; + ecmbuf[1] = (er->ecmlen + 30 - 2) & 0xff; + memcpy(ecmbuf + 2, header, sizeof(header)); + for(n = 0; n < 4; n++) + { + snprintf((char *)provid_buf + (n * 2), sizeof(provid_buf) - (n * 2), "%02X", ((uint8_t *)(&er->prid))[4 - 1 - n]); + } + ecmbuf[7] = provid_buf[0]; + ecmbuf[8] = provid_buf[1]; + ecmbuf[9] = provid_buf[2]; + ecmbuf[10] = provid_buf[3]; + ecmbuf[11] = provid_buf[4]; + ecmbuf[12] = provid_buf[5]; + ecmbuf[13] = provid_buf[6]; + ecmbuf[14] = provid_buf[7]; + ecmbuf[2 + sizeof(header)] = 0xa; + ecmbuf[3 + sizeof(header)] = 2; + ecmbuf[4 + sizeof(header)] = er->caid >> 8; + ecmbuf[5 + sizeof(header)] = er->caid & 0xff; + ecmbuf[6 + sizeof(header)] = 3; + ecmbuf[7 + sizeof(header)] = er->ecmlen & 0xff; + memcpy(ecmbuf + 8 + sizeof(header), er->ecm, er->ecmlen); + ecmbuf[4] = er->caid >> 8; + + client->reader->msg_idx = er->idx; + n = send(client->pfd, ecmbuf, er->ecmlen + 30, 0); + + cs_log_dbg(D_TRACE, "radegast: sending ecm"); + cs_log_dump_dbg(D_CLIENT, ecmbuf, er->ecmlen + 30, "ecm:"); + NULLFREE(ecmbuf); + return ((n < 1) ? (-1) : 0); +} + +int32_t radegast_cli_init(struct s_client *cl) +{ + int32_t handle; + + handle = network_tcp_connection_open(cl->reader); + if(handle < 0) { return -1; } + + cs_log("radegast: proxy %s:%d (fd=%d)", cl->reader->device, cl->reader->r_port, cl->udp_fd); + + cl->reader->tcp_connected = 2; + cl->reader->card_status = CARD_INSERTED; + cl->reader->last_g = cl->reader->last_s = time((time_t *)0); + + cs_log_dbg(D_CLIENT, "radegast: last_s=%" PRId64 ", last_g=%" PRId64, (int64_t)cl->reader->last_s, (int64_t)cl->reader->last_g); + + cl->pfd = cl->udp_fd; + + return (0); +} + +static void radegast_server_init(struct s_client *cl) +{ + if(!cl->init_done) + { + if(IP_ISSET(cl->ip)) + { cs_log("radegast: new connection from %s", cs_inet_ntoa(cl->ip)); } + radegast_auth_client(cur_client()->ip); + cl->init_done = 1; + } + return; +} + +static int32_t radegast_connect(void) +{ + struct s_client *cl = cur_client(); + + if(cl->reader->tcp_connected < 2 && radegast_cli_init(cl) < 0) + { return 0; } + + if(!cl->udp_fd) + { return 0; } + + return 1; +} + +void radegast_idle(void) +{ + struct s_client *client = cur_client(); + struct s_reader *rdr = client->reader; + time_t now = time(NULL); + if(!rdr) { return; } + + if(rdr->tcp_ito > 0) + { + int32_t time_diff; + time_diff = llabs(now - rdr->last_s); + if(time_diff > (rdr->tcp_ito)) + { + network_tcp_connection_close(rdr, "inactivity"); + return; + } + } + else if(rdr->tcp_ito == -1) + { + radegast_connect(); + return; + } +} + +void module_radegast(struct s_module *ph) +{ + ph->ptab.nports = 1; + ph->ptab.ports[0].s_port = cfg.rad_port; + + ph->desc = "radegast"; + ph->type = MOD_CONN_TCP; + ph->large_ecm_support = 1; + ph->listenertype = LIS_RADEGAST; + IP_ASSIGN(ph->s_ip, cfg.rad_srvip); + ph->s_handler = radegast_server; + ph->s_init = radegast_server_init; + ph->c_idle = radegast_idle; + ph->recv = radegast_recv; + ph->send_dcw = radegast_send_dcw; + ph->c_init = radegast_cli_init; + ph->c_recv_chk = radegast_recv_chk; + ph->c_send_ecm = radegast_send_ecm; + ph->num = R_RADEGAST; +} +#endif diff --git a/module-scam.c b/module-scam.c new file mode 100644 index 0000000..f874367 --- /dev/null +++ b/module-scam.c @@ -0,0 +1,1197 @@ +#define MODULE_LOG_PREFIX "scam" + +#include "globals.h" +#ifdef MODULE_SCAM +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-reader.h" +#include "oscam-lock.h" +#include "oscam-time.h" +#include "oscam-chk.h" +#include "cscrypt/des.h" + +struct scam_data +{ + uint8_t enckey[8]; + uint8_t deckey[8]; + uint8_t enc_xor_offset; + uint8_t dec_xor_offset; + uint8_t login_pending; + char login_username[64]; + uint16_t version; +}; + +static inline void xxor(uint8_t *data, int32_t len, const uint8_t *v1, const uint8_t *v2) +{ + uint32_t i; + switch(len) + { + case 16: + for(i = 0; i < 16; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + + case 8: + for(i = 0; i < 8; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + + case 4: + for(i = 0; i < 4; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + + default: + while(len--) + { + *data++ = *v1++ ^ *v2++; + } + break; + } +} + +static void scam_generate_deskey(char *keyString, uint8_t *desKey) +{ + uint8_t iv[8], *tmpKey; + uint32_t i, passLen, alignedPassLen; + uint32_t key_schedule[32]; + + memset(iv, 0, 8); + memset(desKey, 0, 8); + + passLen = keyString == NULL ? 0 : cs_strlen(keyString); + if(passLen > 1024) { + passLen = 1024; + } + + alignedPassLen = (passLen + 7) & -8; + if(alignedPassLen == 0) alignedPassLen = 8; + + if(!cs_malloc(&tmpKey, alignedPassLen)) + { + return; + } + + if(passLen == 0) + { + memset(tmpKey, 0xAA, 8); + passLen = 8; + } + else + { + memcpy(tmpKey, keyString, passLen); + } + + for(i = 0; i < alignedPassLen - passLen; i++) + { + tmpKey[passLen + i] = (uint8_t)i; + } + + xxor(desKey, 8, tmpKey, iv); + + for(i = 0; i < alignedPassLen; i += 8) + { + des_set_key(&tmpKey[i], key_schedule); + des(&tmpKey[i], key_schedule, 1); + xxor(desKey, 8, desKey, &tmpKey[i]); + } + + NULLFREE(tmpKey); +} + +static void scam_encrypt_packet(uint8_t *packet, uint32_t packetLength, uint8_t *key, uint32_t dataLength, uint32_t dataOffset, uint8_t *xorOffset) +{ + uint8_t iv[8]; + uint32_t i; + memset(iv, 0, 8); + + des_cbc_encrypt(packet + dataOffset, iv, key, dataLength); + + for(i = 0; i < packetLength; i++) + { + key[*xorOffset] ^= packet[i]; + *xorOffset = (*xorOffset + 1) & 7; + } +} + +static void scam_decrypt_packet(uint8_t *packet, uint32_t packetLength, uint8_t *key, uint32_t dataLength, uint32_t dataOffset, uint8_t *xorOffset) +{ + uint8_t tmpKey[8], iv[8]; + uint32_t i; + memcpy(tmpKey, key, 8); + memset(iv, 0, 8); + + for(i = 0; i < packetLength; i++) + { + tmpKey[*xorOffset] ^= packet[i]; + *xorOffset = (*xorOffset + 1) & 7; + } + + des_cbc_decrypt(packet + dataOffset, iv, key, dataLength); + memcpy(key, tmpKey, 8); +} + +static void scam_decode_length(uint8_t *packet, uint32_t *dataLength, uint32_t *dataOffset) +{ + uint32_t i, n; + + if(packet[1] & 0x80) + { + n = packet[1] & ~0x80; + *dataLength = 0; + + for(i = 0; i < n; i++) + { + *dataLength = (*dataLength << 8) | packet[2 + i]; + } + *dataOffset = 2 + n; + } + else + { + *dataLength = packet[1]; + *dataOffset = 2; + } +} + +static uint32_t scam_get_length_data_length(uint8_t *packet) +{ + if(packet[1] & 0x80) + { + return packet[1] & ~0x80; + } + else + { + return 1; + } +} + +static void scam_encode_length(uint32_t len, uint8_t *data, uint8_t *dataLen) +{ + if(len < 128) + { + data[0] = (uint8_t)len; + *dataLen = 1; + } + else if(len < 256) + { + data[0] = 0x81; + data[1] = (uint8_t)len; + *dataLen = 2; + } + else if(len < 65536) + { + data[0] = 0x82; + data[1] = (uint8_t)(len >> 8); + data[2] = (uint8_t)(len & 0xFF); + *dataLen = 3; + } + else if(len < 16777216) + { + data[0] = 0x83; + data[1] = (uint8_t)(len >> 16); + data[2] = (uint8_t)((len >> 8) & 0xFF); + data[3] = (uint8_t)(len & 0xFF); + *dataLen = 4; + } + else + { + data[0] = 0x84; + data[1] = (uint8_t)(len >> 24); + data[2] = (uint8_t)((len >> 16) & 0xFF); + data[3] = (uint8_t)((len >> 8) & 0xFF); + data[4] = (uint8_t)(len & 0xFF); + *dataLen = 5; + } +} + + +static void scam_client_close(struct s_client *cl, int32_t call_conclose) +{ + struct s_reader *rdr = cl->reader; + if(!rdr) { return; } + + if(rdr) { rdr->tcp_connected = 0; } + if(rdr) { rdr->card_status = NO_CARD; } + if(rdr) { rdr->last_s = rdr->last_g = 0; } + if(cl) { cl->last = 0; } + + if(call_conclose) // clears also pending ecms! + { network_tcp_connection_close(rdr, "close"); } + else + { + if(cl->udp_fd) + { + close(cl->udp_fd); + cl->udp_fd = 0; + cl->pfd = 0; + } + } +} + +static int32_t scam_send(struct s_client *cl, uint8_t *buf, uint32_t len) +{ + uint8_t *mbuf, lenData[5]; + uint8_t lenDataLen = 0, paddingLen = 0; + uint16_t crc = 0; + int32_t result, packetLen; + struct scam_data *scam = cl->scam; + + if(scam == NULL) { return 0; } + if(len == 0) { return 0; } + + paddingLen = 8 - ((4 + len) % 8); + if(paddingLen == 8) + { + paddingLen = 0; + } + else if(paddingLen > 0 && paddingLen < 3) + { + paddingLen += 8; + } + + scam_encode_length(4 + len + paddingLen, lenData, &lenDataLen); + + if(lenDataLen == 0) { return -1; } + + packetLen = 1 + lenDataLen + 4 + len + paddingLen; + + if(!cs_malloc(&mbuf, packetLen)) { return -1; } + + mbuf[0] = 0x0F; + memcpy(&mbuf[1], lenData, lenDataLen); + mbuf[1 + lenDataLen] = 0x10; + mbuf[1 + lenDataLen + 1] = 0x02; + memcpy(&mbuf[1 + lenDataLen + 4], buf, len); + + if(paddingLen > 0) + { + mbuf[1 + lenDataLen + 4 + len] = 0x7F; + mbuf[1 + lenDataLen + 4 + len + 1] = paddingLen - 2; + get_random_bytes(mbuf + 1 + lenDataLen + 4 + len + 2, paddingLen - 2); + } + + crc = ccitt_crc(mbuf + 1 + lenDataLen + 4, len + paddingLen, 0xFFFF, 0); + i2b_buf(2, crc, &mbuf[1 + lenDataLen + 2]); + + scam_encrypt_packet(mbuf, packetLen, scam->enckey, 4 + len + paddingLen, 1 + lenDataLen, &scam->enc_xor_offset); + result = send(cl->pfd, mbuf, packetLen, 0); + NULLFREE(mbuf); + + return (result); +} + +static int32_t scam_msg_recv(struct s_client *cl, uint8_t *buf, int32_t maxlen) +{ + int32_t len; + int32_t handle = cl->udp_fd; + struct scam_data *scam = cl->scam; + + if(scam == NULL) { return 0; } + if(handle <= 0 || maxlen < 3) + { cs_log("scam_msg_recv: fd is 0"); return -1; } + + len = cs_recv(handle, buf, 2, MSG_WAITALL); + if(len != 2) // invalid header length read + { + if(len <= 0) + { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "disconnected by remote server"); } + else + { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "invalid header length (expected 2, read %d)", len); } + return -1; + } + + if(buf[0] != 0x0F) + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "invalid packet tag"); + return 0; + } + + int32_t headerSize = buf[1] & 0x80 ? (2 + (buf[1] & ~0x80)) : 2; + if(headerSize > 2) + { + if(maxlen < headerSize + 1) { return -1; } + + len = cs_recv(handle, buf + 2, headerSize - 2, MSG_WAITALL); + if(len != headerSize - 2) // invalid header length read + { + if(len <= 0) + { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "disconnected by remote server"); } + else + { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "invalid header length (expected %d, read %d)", headerSize, 2+len); } + return -1; + } + } + + uint32_t dataLength, dataOffset; + scam_decode_length(buf, &dataLength, &dataOffset); + + if(dataLength) // check if any data is expected in msg + { + if(dataLength % 8 != 0) + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "message data has invalid size (size=%d)", dataLength); + return 0; + } + + if(headerSize + dataLength > (uint32_t)maxlen) + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "message too big (size=%d max=%d)", headerSize+dataLength, maxlen); + return 0; + } + + len = cs_recv(handle, buf + dataOffset, dataLength, MSG_WAITALL); + if((uint32_t)len != dataLength) + { + if(len <= 0) + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "disconnected by remote"); + } + else + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "invalid message length read (expected %d, read %d)", dataLength, len); + } + return -1; + } + + scam_decrypt_packet(buf, headerSize + dataLength, scam->deckey, dataLength, dataOffset, &scam->dec_xor_offset); + } + + return headerSize+dataLength; +} + +static int32_t scam_recv(struct s_client *cl, uint8_t *buf, int32_t len) +{ + int32_t n; + struct s_reader *rdr = (cl->typ == 'c') ? NULL : cl->reader; + + if(buf == NULL || len <= 0) + { return -1; } + + n = scam_msg_recv(cl, buf, len); // recv and decrypt msg + if(n <= 0) + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "connection closed by %s, n=%d.", remote_txt(), n); + if(rdr) + { + scam_client_close(cl, 1); + } + else + { + cs_disconnect_client(cl); + } + cs_sleepms(150); + n = -1; + } + else + { + cl->last = time(NULL); // last client action is now + if(rdr) { rdr->last_g = time(NULL); } // last reader receive is now + } + + return n; +} + +// scam client functions + +static int32_t scam_client_init(struct s_client *cl); + +static int32_t scam_client_connect(void) +{ + struct s_client *cl = cur_client(); + + if(cl->reader->tcp_connected < 2 && scam_client_init(cl) < 0) + { return 0; } + + if(!cl->udp_fd) + { return 0; } + + return 1; +} + +static void scam_client_idle(void) +{ + struct s_client *client = cur_client(); + struct s_reader *rdr = client->reader; + time_t now = time(NULL); + if(!rdr) { return; } + + if(rdr->tcp_ito > 0) + { + int32_t time_diff; + time_diff = llabs(now - rdr->last_s); + if(time_diff > (rdr->tcp_ito)) + { + network_tcp_connection_close(rdr, "inactivity"); + return; + } + } + else if(rdr->tcp_ito == -1) + { + scam_client_connect(); + return; + } +} + +static void scam_client_recv_caid(uint8_t *buf, uint32_t len) +{ + uint16_t caid; + + if(len < 3) + { + return; + } + + caid = buf[1] << 8 | buf[2]; + + if(buf[0]) + { + cs_log("scam server has card: %04X", caid); + } + else + { + cs_log("scam server no longer has card: %04X", caid); + } +} + +static void scam_client_recv_server_version(uint8_t *buf, uint32_t len) +{ + uint32_t pos = 0, dataLength = 0, dataOffset = 0, usedLen = 0; + char versionString[128]; + uint16_t versionShort = 0; + versionString[0] = 0; + + scam_decode_length(buf, &dataLength, &dataOffset); + + while(pos + dataOffset + dataLength - 1 < len) + { + switch(buf[pos]) + { + case 0x01: // version string + usedLen = dataLength; + if(usedLen > 127) + { + usedLen = 127; + } + memcpy(versionString, buf + dataOffset, usedLen); + versionString[usedLen] = 0; + break; + + case 0x0A: // version short + if(dataLength != 2) break; + versionShort = (buf[pos + dataOffset] << 8) | buf[pos + dataOffset + 1]; + break; + + default: + cs_log_dbg(D_READER, "unknown server version packet tag %X", buf[pos]); + break; + } + + pos += dataOffset + dataLength; + if((pos + 2 < len) && (pos + 1 + scam_get_length_data_length(buf + pos)) < len) + { + scam_decode_length(buf + pos, &dataLength, &dataOffset); + } + else + { + break; + } + } + + cs_log("scam server version: %s (%d)", versionString, versionShort); +} + +static void scam_client_recv_dcw(struct s_client *cl, uint8_t *buf, uint32_t len, uint8_t *dcw, int32_t *ecm_task_idx, int32_t *rc) +{ + // 00C00000 enigma namespace + // 0455 tsid + // 0001 onid + // 151A srvid + // 200081 ??? + // 943E85577035C469 dcw1 + // C73882811721E31B dcw2 + + if(len != 29) + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "unknown server dcw packet length %d", len); + return; + } + + *ecm_task_idx = b2i(4, &buf[0]); // we store idx here instead of ens + memcpy(dcw, &buf[13], 16); + *rc = 1; +} + +static void scam_client_send_hello(struct s_client *cl) +{ + uint8_t mbuf[70]; + uint32_t usernameLen, i = 0; + struct s_reader *rdr = cl->reader; + struct scam_data *scam = cl->scam; + + if(scam == NULL) { return; } + if(!rdr) { return; } + + usernameLen = cs_strlen(rdr->r_usr); + if(usernameLen > 63) // because rdr->r_usr is max. 63+1 chars + { + usernameLen = 63; + } + + mbuf[i++] = 0x46; // client hello data type + mbuf[i++] = 6 + usernameLen; // will never exceed 63+6 = 69 bytes (<127 bytes) + + // client version + mbuf[i++] = 0xA0; // client version data type + mbuf[i++] = 0x02; // data length (2) + mbuf[i++] = 0x00; // version ( 0x0007) + mbuf[i++] = 0x07; + + // username + mbuf[i++] = 0xA1; // username data type + mbuf[i++] = (uint8_t)usernameLen; + memcpy(mbuf + i, rdr->r_usr, usernameLen); + mbuf[i + usernameLen] = 0; + + scam_send(cl, mbuf, 8 + usernameLen); + + scam_generate_deskey(rdr->r_pwd, scam->enckey); + scam_generate_deskey(rdr->r_pwd, scam->deckey); + scam->enc_xor_offset = 0; + scam->dec_xor_offset = 0; +} + +static int32_t scam_client_send_ecm(struct s_client *cl, ECM_REQUEST *er) +{ + // 2481A5 310A + // 00C00000 enigma namespace + // 0455 tsid + // 0001 onid + // 151A srvid + // 3002 + // 1843 caid + // 3304 + // 66A1AE16 pat/pmt crc? we currently fill it with chid + // 348189 + // 8130.. ecm + // 3501 + // 02 needed dcws? + + uint8_t *mbuf, packetLenData[5], ecmLenData[5]; + uint32_t i = 0, ret = 0, dataLength = 0, packetLength = 0; + uint8_t pLenDataLen = 0, eLenDataLen = 0; + + if(!scam_client_connect()) + { return (-1); } + + scam_encode_length(er->ecmlen, ecmLenData, &eLenDataLen); + dataLength = 23 + eLenDataLen + er->ecmlen + 3; + scam_encode_length(dataLength, packetLenData, &pLenDataLen); + packetLength = 1 + pLenDataLen + dataLength; + + if(!cs_malloc(&mbuf, packetLength)) + { return -1; } + + mbuf[i++] = 0x24; // ecm request data type + memcpy(mbuf + i, packetLenData, pLenDataLen); i += pLenDataLen; + + mbuf[i++] = 0x31; // channel info data type + mbuf[i++] = 0x0A; // size is always 0x0A + + //i2b_buf(4, er->ens, mbuf+i); i += 4; + i2b_buf(4, er->idx, mbuf+i); i += 4; // we store idx instead of ens here + + i2b_buf(2, er->tsid, mbuf + i); i += 2; + i2b_buf(2, er->onid, mbuf + i); i += 2; + i2b_buf(2, er->srvid, mbuf + i); i += 2; + + mbuf[i++] = 0x30; // caid data type + mbuf[i++] = 0x02; // size is always 0x02 + i2b_buf(2, er->caid, mbuf + i); i += 2; + + mbuf[i++] = 0x33; // ??? data type + mbuf[i++] = 0x04; // size is always 0x04 + i2b_buf(2, er->chid, mbuf + i); i += 4; + + mbuf[i++] = 0x34; // ecm data type + memcpy(mbuf + i, ecmLenData, eLenDataLen); i += eLenDataLen; + memcpy(mbuf + i, er->ecm, er->ecmlen); i += er->ecmlen; + + mbuf[i++] = 0x35; // ??? data type + mbuf[i++] = 0x01; // size is always 0x01 + mbuf[i++] = 0x02; // unknown value + + ret = scam_send(cl, mbuf, packetLength); + + cs_log_dbg(D_TRACE, "scam: sending ecm"); + cs_log_dump_dbg(D_CLIENT, mbuf, packetLength, "ecm:"); + NULLFREE(mbuf); + return ((ret < 1) ? (-1) : 0); +} + +static int32_t scam_client_init(struct s_client *cl) +{ + int32_t handle; + + handle = network_tcp_connection_open(cl->reader); + if(handle < 0) { + cl->reader->last_s = 0; // set last send to zero + cl->reader->last_g = 0; // set last receive to zero + cl->last = 0; // set last client action to zero + return (0); + } + + if(cl->scam) { + memset(cl->scam, 0, sizeof(struct scam_data)); + } + + if(!cl->scam && !cs_malloc(&cl->scam, sizeof(struct scam_data))) { + return 0; + } + + cs_log("scam: proxy %s:%d (fd=%d)", + cl->reader->device, cl->reader->r_port, cl->udp_fd); + + cl->reader->tcp_connected = 2; + cl->reader->card_status = CARD_INSERTED; + cl->reader->last_g = cl->reader->last_s = time((time_t *)0); + + cs_log_dbg(D_CLIENT, "scam: last_s=%" PRId64 ", last_g=%" PRId64, (int64_t)cl->reader->last_s, (int64_t)cl->reader->last_g); + + cl->pfd = cl->udp_fd; + + scam_client_send_hello(cl); + + return (0); +} + +static int32_t scam_client_handle(struct s_client *cl, uint8_t *dcw, int32_t *rc, uint8_t *buf, int32_t n) +{ + uint32_t pos = 0, packetLength = 0, packetOffset = 0, dataLength = 0, dataOffset = 0; + int32_t ret = -1; + + if(n < 3) + { + return (-1); + } + + scam_decode_length(buf, &packetLength, &packetOffset); + pos += packetOffset; + + if((pos + 2 < (uint32_t)n) && (pos + 1 + scam_get_length_data_length(buf + pos) < (uint32_t)n)) + { + scam_decode_length(buf + pos, &dataLength, &dataOffset); + } + else + { + return (-1); + } + + while(pos + dataOffset + dataLength - 1 < (uint32_t)n) + { + switch(buf[pos]) + { + case 0x10: // checksum + if(dataLength != 2) { break; } + if(b2i(2, &buf[pos + dataOffset]) != ccitt_crc(buf + pos + dataOffset + 2, n - pos - dataOffset - 2, 0xFFFF, 0)) + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "sent packet with invalid checksum"); + return (-1); + } + break; + + case 0x20: // caid list + scam_client_recv_caid(buf + pos + dataOffset, dataLength); + break; + + case 0x45: // server version + scam_client_recv_server_version(buf + pos + dataOffset, dataLength); + break; + + case 0x63: // dcw + scam_client_recv_dcw(cl, buf + pos + dataOffset, dataLength, dcw, &ret, rc); + break; + + case 0x7F: // padding + break; + + default: + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "unknown scam server packet %X", buf[pos]); + break; + } + + pos += dataOffset + dataLength; + if((pos + 2 < (uint32_t)n) && (pos + 1 + scam_get_length_data_length(buf + pos) < (uint32_t)n)) + { + scam_decode_length(buf + pos, &dataLength, &dataOffset); + } + else + { + break; + } + } + + return ret; +} + +// scam server functions +static uint8_t scam_server_authip_client(struct s_client *cl) +{ + if(cfg.scam_allowed && !check_ip(cfg.scam_allowed, cl->ip)) + { + cs_log("scam: IP not allowed"); + cs_auth_client(cl, (struct s_auth *)0, NULL); + cs_disconnect_client(cl); + return 0; + } + + return 1; +} + +static void scam_server_init(struct s_client *cl) +{ + if(!cl->init_done) + { + if(IP_ISSET(cl->ip)) + { cs_log("scam: new connection from %s", cs_inet_ntoa(cl->ip)); } + + if(scam_server_authip_client(cl)) + { + if(cl->scam) + { + memset(cl->scam, 0, sizeof(struct scam_data)); + } + + if(cl->scam || cs_malloc(&cl->scam, sizeof(struct scam_data))) + { + cl->init_done = 1; + } + } + } + return; +} + +static void scam_server_recv_ecm(struct s_client *cl, uint8_t *buf, int32_t len) +{ + uint32_t pos = 0, dataLength = 0, dataOffset = 0, usedLen = 0; + ECM_REQUEST *er; + uint8_t gotCaid = 0, gotEcm = 0; + + if(len < 1) + { + return; + } + + if(!(er = get_ecmtask())) + { return; } + + scam_decode_length(buf, &dataLength, &dataOffset); + + while(pos + dataOffset + dataLength - 1 < (uint32_t)len) + { + switch(buf[pos]) { + + case 0x31: // channel data + if(dataLength != 0x0A) break; + er->ens = b2i(4, buf + pos + dataOffset); + er->tsid = b2i(2, buf + pos + dataOffset + 4); + er->onid = b2i(2, buf + pos + dataOffset + 6); + er->srvid = b2i(2, buf + pos + dataOffset + 8); + break; + + case 0x30: // caid + if(dataLength != 0x02) break; + er->caid = b2i(2, buf + pos + dataOffset); + gotCaid = 1; + break; + + case 0x33: // unknown + break; + + case 0x34: // ecm + usedLen = dataLength; + if(usedLen > MAX_ECM_SIZE) + { + break; + } + er->ecmlen = usedLen; + memcpy(er->ecm, buf + pos + dataOffset, usedLen); + gotEcm = 1; + break; + + case 0x35: // unknown + break; + + default: + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "sent unknown scam client ecm tag %X", buf[pos]); + break; + } + + pos += dataOffset + dataLength; + if((pos + 2 < (uint32_t)len) && (pos + 1 + scam_get_length_data_length(buf + pos) < (uint32_t)len)) + { + scam_decode_length(buf + pos, &dataLength, &dataOffset); + } + else + { + break; + } + } + + if(gotCaid && gotEcm) + { + get_cw(cl, er); + } + else + { + NULLFREE(er); + cs_log("WARNING: ECM-request corrupt"); + } +} + +static void scam_caidlist_add(uint16_t *caidlist, uint32_t listsize, uint32_t *count, uint16_t caid) +{ + uint32_t i; + uint8_t exists = 0; + + if(*count >= listsize) + { + return; + } + + for(i = 0; i < *count; i++) + { + if(caidlist[i] == caid) + { + exists = 1; + break; + } + } + + if(!exists) + { + caidlist[*count] = caid; + (*count)++; + } +} + +static void scam_server_send_caidlist(struct s_client *cl) +{ + uint8_t mbuf[5]; + int32_t j; + uint32_t i = 0; + uint16_t caids[55]; + uint32_t cardcount = 0; + struct s_reader *rdr = NULL; + + cs_readlock(__func__, &readerlist_lock); + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + if(rdr->caid && chk_ctab(rdr->caid, &cl->ctab)) + { + scam_caidlist_add(caids, ARRAY_SIZE(caids), &cardcount, rdr->caid); + } + + for(j = 0; j < rdr->ctab.ctnum; j++) + { + CAIDTAB_DATA *d = &rdr->ctab.ctdata[j]; + if(d->caid && chk_ctab(d->caid, &cl->ctab)) + { + scam_caidlist_add(caids, ARRAY_SIZE(caids), &cardcount, d->caid); + } + } + } + cs_readunlock(__func__, &readerlist_lock); + + for(j = 0; j < (int32_t)cardcount; j++) + { + i = 0; + mbuf[i++] = 0x20; // caid data type + mbuf[i++] = 0x03; // length + mbuf[i++] = 0x01; // active card + i2b_buf(2, caids[j], mbuf + i); + scam_send(cl, mbuf, 5); + } +} + +static void scam_server_send_serverversion(struct s_client *cl) +{ + uint8_t mbuf[64]; + uint32_t i = 0; + char *version = "scam/3.60 oscam"; + uint8_t vlen = cs_strlen(version); + + mbuf[i++] = 0x45; // server version data type + mbuf[i++] = 2 + vlen + 4; // will never exceed 127 bytes + + mbuf[i++] = 0x01; // server version string data type + mbuf[i++] = vlen; // will never exceed 127 bytes + memcpy(mbuf + i, version, vlen); i += vlen; + + mbuf[i++] = 0x0A; // server version short data type + mbuf[i++] = 0x02; // is always 0x02 + i2b_buf(2, 0x7, mbuf + i); + + scam_send(cl, mbuf, 2 + 2 + vlen + 4); +} + +static void scam_server_recv_auth(struct s_client *cl, uint8_t *buf, int32_t len) +{ + uint32_t pos = 0, dataLength = 0, dataOffset = 0, usedLen = 0; + uint8_t userok = 0; + struct s_auth *account; + struct scam_data *scam = cl->scam; + + if(scam == NULL) { return; } + scam->login_username[0] = 0; + + if(len < 1) + { + return; + } + + scam_decode_length(buf, &dataLength, &dataOffset); + + while(pos + dataOffset + dataLength - 1 < (uint32_t)len) + { + switch(buf[pos]) + { + case 0xA0: // version short + if(dataLength != 2) break; + scam->version = (buf[pos + dataOffset] << 8) | buf[pos + dataOffset + 1]; + break; + + case 0xA1: // username string + usedLen = dataLength; + if(usedLen > 64) { + usedLen = 63; + } + memcpy(scam->login_username, buf + pos + dataOffset, usedLen); + scam->login_username[usedLen] = 0; + break; + + default: + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "unknown client auth packet tag %X", buf[pos]); + break; + } + + pos += dataOffset + dataLength; + if((pos + 2 < (uint32_t)len) && (pos + 1 + scam_get_length_data_length(buf + pos) < (uint32_t)len)) + { + scam_decode_length(buf + pos, &dataLength, &dataOffset); + } + else + { + break; + } + } + + for(account = cfg.account; account; account = account->next) + { + if(streq(scam->login_username, account->usr)) + { + userok = 1; + break; + } + } + + if(!userok) + { + cs_auth_client(cl, (struct s_auth *)0, NULL); + cs_disconnect_client(cl); + return; + } + + scam->login_pending = 1; + scam_generate_deskey(account->pwd, scam->enckey); + scam_generate_deskey(account->pwd, scam->deckey); + scam->enc_xor_offset = 0; + scam->dec_xor_offset = 0; + + scam_server_send_caidlist(cl); + scam_server_send_serverversion(cl); +} + +static void scam_server_send_dcw(struct s_client *cl, ECM_REQUEST *er) +{ + uint8_t mbuf[31]; + uint32_t i = 0; + + if(!(er->rc < E_NOTFOUND)) + { + return; + } + + mbuf[i++] = 0x63; // dcw data type + mbuf[i++] = 0x1D; // fixed sized < 127 + + i2b_buf(4, er->ens, mbuf + i); i += 4; + i2b_buf(2, er->tsid, mbuf + i); i += 2; + i2b_buf(2, er->onid, mbuf + i); i += 2; + i2b_buf(2, er->srvid, mbuf + i); i += 2; + + mbuf[i++] = 0x20; // unknown + mbuf[i++] = 0x00; // unknown + mbuf[i++] = 0x81; // unknown + memcpy(mbuf + i, er->cw, 16); + + scam_send(cl, mbuf, 31); +} + +static void *scam_server_handle(struct s_client *cl, uint8_t *buf, int32_t n) +{ + uint32_t pos = 0, packetLength = 0, packetOffset = 0, dataLength = 0, dataOffset = 0; + struct s_auth *account; + struct scam_data *scam; + + if(n < 3) + { return NULL; } + + if(!cl->init_done) + { + if(!scam_server_authip_client(cl)) { return NULL; } + + if(cl->scam) + { + memset(cl->scam, 0, sizeof(struct scam_data)); + } + + if(cl->scam == NULL && !cs_malloc(&cl->scam, sizeof(struct scam_data))) + { + return NULL; + } + cl->init_done = 1; + } + + scam = cl->scam; + if(scam == NULL) + { + return NULL; + } + + scam_decode_length(buf, &packetLength, &packetOffset); + pos += packetOffset; + + if(scam->login_pending && packetLength > 1 && (buf[pos] != 0x10 || buf[pos + 1] != 0x02)) + { + scam->login_pending = 0; + cs_auth_client(cl, (struct s_auth *)0, NULL); + cs_disconnect_client(cl); + return NULL; + } + + if((pos + 2 < (uint32_t)n) && (pos + 1 + scam_get_length_data_length(buf + pos) < (uint32_t)n)) + { + scam_decode_length(buf + pos, &dataLength, &dataOffset); + } + else + { + return NULL; + } + + while(pos + dataOffset + dataLength - 1 < (uint32_t)n) + { + switch(buf[pos]) + { + case 0x10: // checksum + if(dataLength != 2) { break; } + if(b2i(2, &buf[pos + dataOffset]) != ccitt_crc(buf + pos + dataOffset + 2, n - pos - dataOffset - 2, 0xFFFF, 0)) + { + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "sent packet with invalid checksum"); + return NULL; + } + if(scam->login_pending) + { + for(account = cfg.account; account; account = account->next) + { + if(streq(scam->login_username, account->usr)) + { + scam->login_pending = 0; + if(!cs_auth_client(cl, account, NULL)) + { + cs_log("scam client login: %s version: %d", scam->login_username, scam->version); + } + else + { + cs_disconnect_client(cl); + } + break; + } + } + if(scam->login_pending) + { + scam->login_pending = 0; + cs_auth_client(cl, (struct s_auth *)0, NULL); + cs_disconnect_client(cl); + return NULL; + } + } + break; + + case 0x46: // client auth + scam_server_recv_auth(cl, buf + pos + dataOffset, dataLength); + break; + + case 0x24: // ecm request + scam_server_recv_ecm(cl, buf + pos + dataOffset, dataLength); + break; + + case 0x7F: // padding + break; + + default: + cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "sent unknown scam client packet %X", buf[pos]); + break; + } + + pos += dataOffset + dataLength; + if((pos + 2 < (uint32_t)n) && (pos + 1 + scam_get_length_data_length(buf + pos) < (uint32_t)n)) + { + scam_decode_length(buf + pos, &dataLength, &dataOffset); + } + else + { + break; + } + } + + return NULL; +} + +void scam_cleanup(struct s_client *cl) +{ + NULLFREE(cl->scam); +} + +void module_scam(struct s_module *ph) +{ + ph->desc = "scam"; + ph->type = MOD_CONN_TCP; + ph->listenertype = LIS_SCAM; + ph->num = R_SCAM; + ph->large_ecm_support = 1; + IP_ASSIGN(ph->s_ip, cfg.scam_srvip); + ph->ptab.nports = 1; + ph->ptab.ports[0].s_port = cfg.scam_port; + // server + client + ph->recv = scam_recv; + ph->cleanup = scam_cleanup; + // server + ph->s_init = scam_server_init; + ph->s_handler = scam_server_handle; + ph->send_dcw = scam_server_send_dcw; + // client + ph->c_init = scam_client_init; + ph->c_idle = scam_client_idle; + ph->c_recv_chk = scam_client_handle; + ph->c_send_ecm = scam_client_send_ecm; +} + +#endif diff --git a/module-serial.c b/module-serial.c new file mode 100644 index 0000000..afe6f26 --- /dev/null +++ b/module-serial.c @@ -0,0 +1,1521 @@ +#define MODULE_LOG_PREFIX "serial" + +#include "globals.h" +#ifdef MODULE_SERIAL +#include "oscam-config.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-reader.h" + +extern int32_t exit_oscam; + +#define HSIC_CRC 0xA5 +#define SSSP_MAX_PID 8 + +#define P_HSIC 1 // Humax Sharing Interface Client +#define P_SSSP 2 // Simple Serial Sharing Protocol +#define P_BOMBA 3 // This is not really a Protocol +#define P_DSR95 4 // DSR9500 with SID +#define P_GS 5 // GS7001 +#define P_ALPHA 6 // AlphaStar Receivers +#define P_DSR95_OLD 7 // DSR9500 without SID +#define P_GBOX 8 // Arion with gbox +#define P_TWIN 9 // Twin Protocol +#define P_MAX P_TWIN +#define P_AUTO 0xFF + +#define P_DSR_AUTO 0 +#define P_DSR_GNUSMAS 1 +#define P_DSR_OPEN 2 +#define P_DSR_PIONEER 3 +#define P_DSR_WITHSID 4 +#define P_DSR_UNKNOWN 5 + +#define IS_ECM 0 // incoming data is ECM +#define IS_DCW 1 // incoming data is DCW +#define IS_PMT 2 // incoming data is PMT +#define IS_LGO 3 // incoming data is client logon +#define IS_ECHO 4 // incoming data is DCW echo from Samsung +#define IS_CAT 5 // incoming data is CAT +#define IS_BAD 0xFF // incoming data is unknown + +static const char *const proto_txt[] = { "unknown", "hsic", "sssp", "bomba", "dsr9500", "gs", + "alpha", "dsr9500old", "gbox", "twin" }; + +static const char *const dsrproto_txt[] = {"unknown", "samsung", "openbox", "pioneer", "extended", "unknown" }; + +typedef struct s_gbox +{ + int32_t cat_len; + int32_t pmt_len; + int32_t ecm_len; +} GBOX_LENS; + +typedef struct s_sssp +{ + uint16_t caid; + uint16_t pid; + uint32_t prid; +} SSSP_TAB; + +// added to support multiple instances with thread +struct s_serial_client +{ + int32_t connected; + struct timeb tps; + struct timeb tpe; + char oscam_ser_usr[32]; + char oscam_ser_device[64]; + int32_t oscam_ser_port; + speed_t oscam_ser_baud; + int32_t oscam_ser_delay; + int32_t oscam_ser_timeout; + int32_t oscam_ser_proto; + int32_t serial_errors; + int32_t dsr9500type; + int32_t samsung_0a; // number of 0A in ALL dcw sent into samsung + int32_t samsung_dcw; // number of dcw sent into samsung before echo or ecm is received + + GBOX_LENS gbox_lens; + SSSP_TAB sssp_tab[SSSP_MAX_PID]; + uint16_t sssp_srvid; + int32_t sssp_num; + int32_t sssp_fix; +}; + +static pthread_mutex_t mutex; +static pthread_cond_t cond; +static int32_t bcopy_end = -1; +static struct s_module *serial_ph = NULL; + +struct s_thread_param +{ + uint8_t module_idx; + struct s_serial_client serialdata; +}; + +static int32_t chk_ser_srvid_match(uint16_t caid, uint16_t sid, uint32_t provid, SIDTAB *sidtab) +{ + int32_t i, rc = 0; + + if(!sidtab->num_caid) + { rc |= 1; } + else + for(i = 0; (i < sidtab->num_caid) && (!(rc & 1)); i++) + if(caid == sidtab->caid[i]) { rc |= 1; } + + if(!sidtab->num_provid) + { rc |= 2; } + else + for(i = 0; (i < sidtab->num_provid) && (!(rc & 2)); i++) + if(provid == sidtab->provid[i]) { rc |= 2; } + + if(!sidtab->num_srvid) + { rc |= 4; } + else + for(i = 0; (i < sidtab->num_srvid) && (!(rc & 4)); i++) + if(sid == sidtab->srvid[i]) { rc |= 4; } + + return (rc == 7); +} + +static int32_t chk_ser_srvid(struct s_client *cl, uint16_t caid, uint16_t sid, uint32_t provid) +{ + int32_t nr, rc = 0; + SIDTAB *sidtab; + + if(!cl->sidtabs.ok) + { + if(!cl->sidtabs.no) { return (1); } + rc = 1; + } + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + if(sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) + { + if((cl->sidtabs.no & ((SIDTABBITS)1 << nr)) && + (chk_ser_srvid_match(caid, sid, provid, sidtab))) + { return (0); } + if((cl->sidtabs.ok & ((SIDTABBITS)1 << nr)) && + (chk_ser_srvid_match(caid, sid, provid, sidtab))) + { rc = 1; } + } + return (rc); +} + +static void oscam_wait_ser_fork(void) +{ + SAFE_MUTEX_LOCK(&mutex); + do + { + if(bcopy_end) + { + bcopy_end = 0; + break; + } + else + { SAFE_COND_WAIT(&cond, &mutex); } + } + while(1); + SAFE_MUTEX_UNLOCK(&mutex); +} + +static int32_t oscam_ser_alpha_convert(uint8_t *buf, int32_t l) +{ + int32_t i; + if(buf[0] == 0x7E) // normalize + { + l -= 2; + memmove(buf, buf + 1, l); // remove BOT/EOT + for(i = 0; i < l; i++) + if(buf[i] == 0x20) + { + memmove(buf + i, buf + i + 1, --l); + buf[i] ^= 0x20; + } + } + else // to alphastar + { + memmove(buf + 1, buf, l++); // insert BOT + buf[0] = 0x7E; + for(i = 1; i < l; i++) + if((buf[i] == 0x20) || (buf[i] == 0x7E) || (buf[i] == 0x7F)) + { + buf[i] ^= 0x20; + memmove(buf + i + 1, buf + i, l++); + buf[i++] = 0x20; + } + buf[l++] = 0x7F; // insert EOT + } + return (l); +} + +static void oscam_ser_disconnect(void); + +static int32_t oscam_ser_parse_url(char *url, struct s_serial_client *serialdata, char *pcltype) +{ + char *service, *usr, *dev, *baud = NULL, *dummy, *para; + char cltype; + + cltype = pcltype ? (*pcltype) : cur_client()->typ; + + serialdata->oscam_ser_proto = P_AUTO; + if((dummy = strstr(url, "://"))) + { + int32_t i; + service = url; + url = dummy + 3; + *dummy = 0; + for(i = 1; i <= P_MAX; i++) + if(!strcmp(service, proto_txt[i])) + { serialdata->oscam_ser_proto = i; } + } + if(!(cltype == 'c') && (serialdata->oscam_ser_proto == P_AUTO)) { return (0); } + + switch(serialdata->oscam_ser_proto) // set the defaults + { + case P_GS: + serialdata->oscam_ser_timeout = 500; + serialdata->oscam_ser_baud = B19200; + break; + + default: + serialdata->oscam_ser_timeout = 50; +#ifdef B115200 + serialdata->oscam_ser_baud = B115200; +#else + serialdata->oscam_ser_baud = B9600; +#endif + } + + switch(serialdata->oscam_ser_proto) + { + case P_DSR95: + serialdata->dsr9500type = (cltype == 'c') ? P_DSR_AUTO : P_DSR_WITHSID; + break; + + case P_DSR95_OLD: + serialdata->dsr9500type = P_DSR_AUTO; + serialdata->oscam_ser_proto = P_DSR95; + } + + usr = url; + if((dev = strchr(usr, '@'))) + { + *dev++ = '\0'; + if((dummy = strchr(usr, ':'))) // fake pwd + { *dummy++ = '\0'; } + if((cltype == 'c') && (!usr[0])) { return (0); } + } + else + { + if(cltype == 'c') { return (0); } // user needed in server-mode + dev = usr; + } + + if((baud = strchr(dev, ':'))) // port = baud + { *baud++ = '\0'; } + + dummy = baud ? baud : dev; + if((para = strchr(dummy, '?'))) + { + char *ptr1, *ptr2, *saveptr1 = NULL; + *para++ = '\0'; + for(ptr1 = strtok_r(para, "&", &saveptr1); ptr1; ptr1 = strtok_r(NULL, "&", &saveptr1)) + { + if(!(ptr2 = strchr(ptr1, '='))) { continue; } + *ptr2++ = '\0'; + strtolower(ptr1); + if(!strcmp("delay" , ptr1)) { serialdata->oscam_ser_delay = atoi(ptr2); } + if(!strcmp("timeout", ptr1)) { serialdata->oscam_ser_timeout = atoi(ptr2); } + } + } + + if(baud) + { + trim(baud); +#ifdef B115200 + if(!strcmp(baud, "115200")) + { serialdata->oscam_ser_baud = B115200; } + else +#endif +#ifdef B57600 + if(!strcmp(baud, "57600")) + { serialdata->oscam_ser_baud = B57600; } + else +#endif + if(!strcmp(baud, "38400")) + { serialdata->oscam_ser_baud = B38400; } + else if(!strcmp(baud, "19200")) + { serialdata->oscam_ser_baud = B19200; } + else if(!strcmp(baud, "9600")) + { serialdata->oscam_ser_baud = B9600; } + } + + if((para = strchr(dev, ','))) // device = ip/hostname and port + { + *para++ = '\0'; + serialdata->oscam_ser_port = atoi(para); + } + else + { serialdata->oscam_ser_port = 0; } + cs_strncpy(serialdata->oscam_ser_usr, usr, sizeof(serialdata->oscam_ser_usr)); + cs_strncpy(serialdata->oscam_ser_device, dev, sizeof(serialdata->oscam_ser_device)); + return (serialdata->oscam_ser_baud); +} + +static void oscam_ser_set_baud(struct termios *tio, speed_t baud) +{ + cfsetospeed(tio, baud); + cfsetispeed(tio, baud); +} + +static int32_t oscam_ser_set_serial_device(int32_t fd, speed_t baud) +{ + struct termios tio; + + memset(&tio, 0, sizeof(tio)); + //tio.c_cflag = (CS8 | CREAD | HUPCL | CLOCAL); + tio.c_cflag = (CS8 | CREAD | CLOCAL); + tio.c_iflag = IGNPAR; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + //#if !defined(__CYGWIN__) + oscam_ser_set_baud(&tio, B1200); + tcsetattr(fd, TCSANOW, &tio); + cs_sleepms(500); + //#endif + oscam_ser_set_baud(&tio, baud); + return (tcsetattr(fd, TCSANOW, &tio)); +} + +static int32_t oscam_ser_poll(int32_t event, struct s_client *client) +{ + int64_t msec; + struct pollfd pfds; + struct timeb tpc; + cs_ftime(&tpc); + msec = comp_timeb(&client->serialdata->tpe, &tpc); + if(msec < 0) + { return (0); } + pfds.fd = cur_client()->pfd; + pfds.events = event; + pfds.revents = 0; + if(poll(&pfds, 1, msec) != 1) + { return (0); } + else + { return (((pfds.revents)&event) == event); } +} + +static int32_t oscam_ser_write(struct s_client *client, const uint8_t *const buf, int32_t n) +{ + int32_t i; + for(i = 0; (i < n) && (oscam_ser_poll(POLLOUT, client)); i++) + { + if(client->serialdata->oscam_ser_delay) + { cs_sleepms(client->serialdata->oscam_ser_delay); } + if(write(client->pfd, buf + i, 1) < 1) + { break; } + } + return (i); +} + +static int32_t oscam_ser_send(struct s_client *client, const uint8_t *const buf, int32_t l) +{ + int32_t n; + struct s_serial_client *serialdata = client->serialdata ; + if(!client->pfd) { return (0); } + cs_ftime(&serialdata->tps); + serialdata->tpe = client->serialdata->tps; + add_ms_to_timeb(&serialdata->tpe, serialdata->oscam_ser_timeout); + add_ms_to_timeb(&serialdata->tpe, (l * (serialdata->oscam_ser_delay + 1))); + n = oscam_ser_write(client, buf, l); + cs_ftime(&serialdata->tpe); + cs_log_dump_dbg(D_CLIENT, buf, l, "send %d of %d bytes to %s in %"PRId64" ms", n, l, remote_txt(), + comp_timeb(&serialdata->tpe, &serialdata->tps)); + if(n != l) + { cs_log("transmit error. send %d of %d bytes only !", n, l); } + return (n); +} + +static int32_t oscam_ser_selrec(uint8_t *buf, int32_t n, int32_t l, int32_t *c) +{ + int32_t i; + if(*c + n > l) + { n = l - *c; } + if(n <= 0) { return (0); } + for(i = 0; (i < n) && (oscam_ser_poll(POLLIN, cur_client())); i++) + if(read(cur_client()->pfd, buf + *c, 1) < 1) + { return (0); } + else + { (*c)++; } + return (i == n); +} + +static int32_t oscam_ser_recv(struct s_client *client, uint8_t *xbuf, int32_t l) +{ + int32_t s, p, n, r; + uint8_t job = IS_BAD; + static uint8_t lb; + static int32_t have_lb = 0; + uint8_t *buf = xbuf + 1; + struct s_serial_client *serialdata = client->serialdata; + + if(!client->pfd) { return (-1); } + cs_ftime(&serialdata->tps); + serialdata->tpe = serialdata->tps; + add_ms_to_timeb(&serialdata->tpe, serialdata->oscam_ser_timeout); + buf[0] = lb; + for(s = p = r = 0, n = have_lb; (s < 4) && (p >= 0); s++) + { + switch(s) + { + case 0: // STAGE 0: skip known garbage from DSR9500 + if(oscam_ser_selrec(buf, 2 - n, l, &n)) + { + if((buf[0] == 0x0A) && (buf[1] == 0x0D)) + { p = (-4); } + if((buf[0] == 0x0D) && (buf[1] == 0x0A)) + { p = (-4); } + } + else + { p = (-3); } + have_lb = 0; + break; + + case 1: // STAGE 1: identify protocol + p = (-3); + if(oscam_ser_selrec(buf, 1, l, &n)) // now we have 3 bytes in buf + { + // skip unsupported Advanced Serial Sharing Protocol HF 8900 + if((buf[0] == 0x04) && (buf[1] == 0x00) && (buf[2] == 0x02)) + { + oscam_ser_selrec(buf, 2, l, &n); // get rest 2 bytes to buffor + p = (-4); + have_lb = 0; + break; + } + else + { + p = (-2); + if(client->typ == 'c') // HERE IS SERVER + { + job = IS_ECM; // assume ECM + switch(buf[0]) + { + case 0x00: + if((buf[1] == 0x01) && (buf[2] == 0x00)) + { + p = P_GS; + job = IS_LGO; + serialdata->tpe.time++; + } + break; + + case 0x01: + if((buf[1] & 0xf0) == 0xb0) { p = P_GBOX; } + else + { + p = P_SSSP; + job = IS_PMT; + } + break; // pmt-request + + case 0x02: + p = P_HSIC; + break; + + case 0x03: + switch(serialdata->oscam_ser_proto) + { + case P_SSSP: + case P_GS: + case P_DSR95: + p = serialdata->oscam_ser_proto; + break; + + case P_AUTO: + p = (buf[1] < 0x30) ? P_SSSP : P_DSR95; + break; // auto for GS is useless!! + } + break; + + case 0x04: + p = P_DSR95; + job = IS_ECHO; + serialdata->dsr9500type = P_DSR_GNUSMAS; + break; + + case 0x7E: + p = P_ALPHA; + if(buf[1] != 0x80) { job = IS_BAD; } + break; + + case 0x80: + case 0x81: + p = P_BOMBA; + break; + } + } + else // HERE IS CLIENT + { + job = IS_DCW; // assume DCW + switch(serialdata->oscam_ser_proto) + { + case P_HSIC : + if((buf[0] == 4) && (buf[1] == 4)) { p = P_HSIC; } + break; + + case P_BOMBA: + p = P_BOMBA; + break; + + case P_DSR95: + if(buf[0] == 4) { p = P_DSR95; } + break; + + case P_ALPHA: + if(buf[0] == 0x88) { p = P_ALPHA; } + break; + + case P_TWIN: + if((buf[0] == 0xF7) && (buf[1] == 0x00) && (buf[2] == 0x16)) { p = P_TWIN; } + break; + } + } + + if((serialdata->oscam_ser_proto != p) && (serialdata->oscam_ser_proto != P_AUTO)) + { p = (-2); } + } + } + break; + + case 2: // STAGE 2: examine length + if(client->typ == 'c') + { + switch(p) + { + case P_SSSP: + r = (buf[1] << 8) | buf[2]; + break; + + case P_BOMBA: + r = buf[2]; + break; + + case P_HSIC: + if(oscam_ser_selrec(buf, 12, l, &n)) { r = buf[14]; } + else { p = (-1); } + break; + + case P_DSR95: + if(job == IS_ECHO) + { + r = 17 * serialdata->samsung_dcw - 3 + serialdata->samsung_0a; + serialdata->samsung_dcw = serialdata->samsung_0a = 0; + } + else + { + if(oscam_ser_selrec(buf, 16, l, &n)) + { + uint8_t b; + if(cs_atob(&b, (char *)buf + 17, 1) < 0) + { p = (-2); } + else + { + r = (b << 1); + r += (serialdata->dsr9500type == P_DSR_WITHSID) ? 4 : 0; + } + } + else { p = (-1); } + } + break; + + case P_GS: + if(job == IS_LGO) { r = 5; } + else + { + if(oscam_ser_selrec(buf, 1, l, &n)) + { r = (buf[3] << 8) | buf[2]; } + else { p = (-1); } + } + break; + + case P_ALPHA: + r = -0x7F; // char specifying EOT + break; + + case P_GBOX: + r = ((buf[1] & 0xf) << 8) | buf[2]; + serialdata->gbox_lens.cat_len = r; + break; + + default: + serialdata->dsr9500type = P_DSR_AUTO; + } + } + else + { + switch(p) + { + case P_HSIC: + r = (buf[2] == 0x3A) ? 20 : 0; + break; // 3A=DCW / FC=ECM was wrong + + case P_BOMBA: + r = 13; + break; + + case P_DSR95: + r = 14; + break; + + case P_ALPHA: + r = (buf[1] << 8) | buf[2]; + break; // should be 16 always + + case P_TWIN: + r = 16; + break; + } + } + break; + + case 3: // STAGE 3: get the rest... + if(r > 0) // read r additional bytes + { + int32_t all = n + r; + if(!oscam_ser_selrec(buf, r, l, &n)) + { + cs_log_dbg(D_CLIENT, "not all data received, waiting another 50 ms"); + add_ms_to_timeb(&serialdata->tpe, 50); + if(!oscam_ser_selrec(buf, all - n, l, &n)) + { p = (-1); } + } + // auto detect DSR9500 protocol + if(client->typ == 'c' && p == P_DSR95 && serialdata->dsr9500type == P_DSR_AUTO) + { + add_ms_to_timeb(&serialdata->tpe, 20); + if(oscam_ser_selrec(buf, 2, l, &n)) + { + if(cs_atoi((char *)buf + n - 2, 1, 1) == 0xFFFFFFFF) + { + switch((buf[n - 2] << 8) | buf[n - 1]) + { + case 0x0A0D: + serialdata->dsr9500type = P_DSR_OPEN; + break; + + case 0x0D0A: + serialdata->dsr9500type = P_DSR_PIONEER; + break; + + default: + serialdata->dsr9500type = P_DSR_UNKNOWN; + break; + } + } + else + { + if(oscam_ser_selrec(buf, 2, l, &n)) + if(cs_atoi((char *)buf + n - 2, 1, 1) == 0xFFFFFFFF) + { serialdata->dsr9500type = P_DSR_UNKNOWN; } + else + { serialdata->dsr9500type = P_DSR_WITHSID; } + else + { + serialdata->dsr9500type = P_DSR_UNKNOWN; + p = (-1); + } + } + } + else + { serialdata->dsr9500type = P_DSR_GNUSMAS; } + if(p) + cs_log("detected dsr9500-%s type receiver", + dsrproto_txt[serialdata->dsr9500type]); + } + // gbox + if(client->typ == 'c' && p == P_GBOX) + { + int32_t j; + for(j = 0; (j < 3) && (p > 0); j++) + { + switch(j) + { + case 0: // PMT head + if(!oscam_ser_selrec(buf, 3, l, &n)) + { p = (-1); } + else if(!(buf[n - 3] == 0x02 && (buf[n - 2] & 0xf0) == 0xb0)) + { p = (-2); } + break; + + case 1: // PMT + ECM header + serialdata->gbox_lens.pmt_len = ((buf[n - 2] & 0xf) << 8) | buf[n - 1]; + if(!oscam_ser_selrec(buf, serialdata->gbox_lens.pmt_len + 3, l, &n)) + { p = (-1); } + break; + + case 2: // ECM + ECM PID + serialdata->gbox_lens.ecm_len = ((buf[n - 2] & 0xf) << 8) | buf[n - 1]; + if(!oscam_ser_selrec(buf, serialdata->gbox_lens.ecm_len + 4, l, &n)) + { p = (-1); } + } + } + } // gbox + } + else if(r < 0) // read until specified char (-r) + { + while((buf[n - 1] != (-r)) && (p > 0)) + if(!oscam_ser_selrec(buf, 1, l, &n)) + { p = (-1); } + } + break; + } + } + + if(p == (-2) || p == (-1)) + { + oscam_ser_selrec(buf, l - n, l, &n); // flush buffer + serialdata->serial_errors++; + } + + cs_ftime(&serialdata->tpe); + cs_log_dump_dbg(D_CLIENT, buf, n, "received %d bytes from %s in %"PRId64" ms", n, remote_txt(), comp_timeb(&serialdata->tpe, &serialdata->tps)); + client->last = serialdata->tpe.time; + + switch(p) + { + case (-1): + if(client->typ == 'c' && (n > 2) && (buf[0] == 2) && (buf[1] == 2) && (buf[2] == 2)) + { + oscam_ser_disconnect(); + cs_log("humax powered on"); // this is nice ;) + } + else + { + if(client->typ == 'c' && buf[0] == 0x1 && buf[1] == 0x08 && buf[2] == 0x20 && buf[3] == 0x08) + { + oscam_ser_disconnect(); + cs_log("ferguson powered on"); // this is nice to ;) + } + else + cs_log("incomplete request (%d bytes)", n); + } + break; + + case (-2): + cs_log_dbg(D_CLIENT, "unknown request or garbage"); + break; + } + + xbuf[0] = (uint8_t)((job << 4) | p); + return ((p < 0) ? 0 : n + 1); +} + +/* + * server functions + */ + +static void oscam_ser_disconnect_client(void) +{ + uint8_t mbuf[1024]; + struct s_serial_client *serialdata = cur_client()->serialdata; + + switch(serialdata->connected ? serialdata->connected : serialdata->oscam_ser_proto) + { + case P_GS: + mbuf[0] = 0x01; + mbuf[1] = 0x00; + mbuf[2] = 0x00; + mbuf[3] = 0x00; + oscam_ser_send(cur_client(), mbuf, 4); + break; + } + serialdata->dsr9500type = P_DSR_AUTO; + serialdata->serial_errors = 0; +} + +static void oscam_ser_init_client(void) +{ + uint8_t mbuf[4]; + switch(cur_client()->serialdata->oscam_ser_proto) // sure, does not work in auto-mode + { + case P_GS: + oscam_ser_disconnect_client(); // send disconnect first + cs_sleepms(300); // wait a little bit + mbuf[0] = 0x00; + mbuf[1] = 0x00; + mbuf[2] = 0x00; + mbuf[3] = 0x00; + oscam_ser_send(cur_client(), mbuf, 4); // send connect + break; + } +} + +static void oscam_ser_disconnect(void) +{ + oscam_ser_disconnect_client(); + if(cur_client()->serialdata->connected) + { cs_log("%s disconnected (%s)", username(cur_client()), proto_txt[cur_client()->serialdata->connected]); } + cur_client()->serialdata->connected = 0; +} + +static void oscam_ser_auth_client(int32_t proto) +{ + int32_t ok = 0; + struct s_serial_client *serialdata = cur_client()->serialdata; + // After reload base account ptrs may be placed in other address, + // and we may can't find it in this process. + // Simply save valid account. + struct s_auth *account = 0; + + if(serialdata->connected == proto) + { return; } + if(serialdata->connected) + { oscam_ser_disconnect(); } + serialdata->connected = proto; + + for(ok = 0, account = cfg.account; (account) && (!ok); account = account->next) + if((ok = !strcmp(serialdata->oscam_ser_usr, account->usr))) + { break; } + cs_auth_client(cur_client(), ok ? account : (struct s_auth *)(-1), proto_txt[serialdata->connected]); +} + +static void oscam_ser_send_dcw(struct s_client *client, ECM_REQUEST *er) +{ + uint8_t mbuf[23]; + int32_t i; + uint8_t crc; + struct s_serial_client *serialdata = cur_client()->serialdata; + + if(er->rc < E_NOTFOUND) // found + { + switch(serialdata->connected) + { + case P_HSIC: + for(i = 0, crc = HSIC_CRC; i < 16; i++) + { crc ^= er->cw[i]; } + memset(mbuf, 0x04, 2); + memset(mbuf + 2, 0x3a, 2); + memcpy(mbuf + 4, er->cw, 16); + memcpy(mbuf + 20, &crc, 1); + memset(mbuf + 21, 0x1b, 2); + oscam_ser_send(client, mbuf, 23); + break; + + case P_SSSP: + mbuf[0] = 0xF2; + mbuf[1] = 0; + mbuf[2] = 16; + memcpy(mbuf + 3, er->cw, 16); + oscam_ser_send(client, mbuf, 19); + if(!serialdata->sssp_fix) + { + mbuf[0] = 0xF1; + mbuf[1] = 0; + mbuf[2] = 2; + i2b_buf(2, er->pid, mbuf + 3); + oscam_ser_send(client, mbuf, 5); + serialdata->sssp_fix = 1; + } + break; + + case P_GBOX: + case P_BOMBA: + oscam_ser_send(client, er->cw, 16); + break; + + case P_DSR95: + mbuf[0] = 4; + memcpy(mbuf + 1, er->cw, 16); + oscam_ser_send(client, mbuf, 17); + if(serialdata->dsr9500type == P_DSR_GNUSMAS) + { + serialdata->samsung_0a = 0; + for(i = 1; i < 17; i++) + if(mbuf[i] == 0x0A) + { serialdata->samsung_0a++; } + serialdata->samsung_dcw++; + } + break; + + case P_GS: + mbuf[0] = 0x03; + mbuf[1] = 0x08; + mbuf[2] = 0x10; + mbuf[3] = 0x00; + memcpy(mbuf + 4, er->cw, 16); + oscam_ser_send(client, mbuf, 20); + break; + + case P_ALPHA: + mbuf[0] = 0x88; + mbuf[1] = 0x00; + mbuf[2] = 0x10; + memcpy(mbuf + 3, er->cw, 16); + oscam_ser_send(client, mbuf, 19); + break; + } + } + else // not found + { + switch(serialdata->connected) + { + case P_GS: + mbuf[0] = 0x03; + mbuf[1] = 0x09; + mbuf[2] = 0x00; + mbuf[3] = 0x00; + oscam_ser_send(client, mbuf, 4); + break; + } + } + serialdata->serial_errors = 0; // clear error counter +} + +static void oscam_ser_process_pmt(uint8_t *buf, int32_t l) +{ + int32_t i; + uint8_t sbuf[32]; + struct s_serial_client *serialdata = cur_client()->serialdata; + + switch(serialdata->connected) + { + case P_SSSP: + serialdata->sssp_fix = 0; + memset(serialdata->sssp_tab, 0, sizeof(serialdata->sssp_tab)); + serialdata->sssp_srvid = b2i(2, buf + 3); + serialdata->sssp_num = 0; + + for(i = 9; (i < l) && (serialdata->sssp_num < SSSP_MAX_PID); i += 7) + { + // check support for pid (caid, sid and provid in oscam.services) + if(chk_ser_srvid(cur_client(), b2i(2, buf + i), b2i(2, buf + 3), b2i(3, buf + i + 4))) + { + memcpy(sbuf + 3 + (serialdata->sssp_num << 1), buf + i + 2, 2); + serialdata->sssp_tab[serialdata->sssp_num].caid = b2i(2, buf + i); + serialdata->sssp_tab[serialdata->sssp_num].pid = b2i(2, buf + i + 2); + serialdata->sssp_tab[serialdata->sssp_num].prid = b2i(3, buf + i + 4); + serialdata->sssp_num++; + } + } + + sbuf[0] = 0xF1; + sbuf[1] = 0; + sbuf[2] = (serialdata->sssp_num << 1); + oscam_ser_send(cur_client(), sbuf, sbuf[2] + 3); + break; + } +} + +static void oscam_ser_client_logon(uint8_t *buf, int32_t l) +{ + uint8_t gs_logon[] = {0, 1, 0, 0, 2, 1, 0, 0}; + + switch(cur_client()->serialdata->connected) + { + case P_GS: + if((l >= 8) && (!memcmp(buf, gs_logon, 8))) + { + buf[0] = 0x02; + buf[1] = 0x04; + buf[2] = 0x00; + buf[3] = 0x00; + oscam_ser_send(cur_client(), buf, 4); + } + break; + } +} + +static int32_t oscam_ser_check_ecm(ECM_REQUEST *er, uint8_t *buf, int32_t l) +{ + int32_t i; + struct s_serial_client *serialdata = cur_client()->serialdata; + + if(l < 16) + { + cs_log("incomplete request (%d bytes)", l); + return (1); + } + + switch(serialdata->connected) + { + case P_HSIC: + er->ecmlen = l - 12; + if(er->ecmlen < 0 || er->ecmlen > MAX_ECM_SIZE) + { return (3); } + er->caid = b2i(2, buf + 1); + er->prid = b2i(3, buf + 3); + er->pid = b2i(2, buf + 6); + er->srvid = b2i(2, buf + 10); + memcpy(er->ecm, buf + 12, er->ecmlen); + break; + + case P_SSSP: + er->pid = b2i(2, buf + 3); + + for(i = 0; (i < 8) && (serialdata->sssp_tab[i].pid != er->pid); i++) { ; } + + if(i >= serialdata->sssp_num) + { + cs_log_dbg(D_CLIENT, "illegal request, unknown pid=%04X", er->pid); + return (2); + } + er->ecmlen = l - 5; + if(er->ecmlen < 0 || er->ecmlen > MAX_ECM_SIZE) + { return (3); } + er->srvid = serialdata->sssp_srvid; + er->caid = serialdata->sssp_tab[i].caid; + er->prid = serialdata->sssp_tab[i].prid; + memcpy(er->ecm, buf + 5, er->ecmlen); + break; + + case P_BOMBA: + er->ecmlen = l; + if(er->ecmlen < 0 || er->ecmlen > MAX_ECM_SIZE) + { return (3); } + memcpy(er->ecm, buf, er->ecmlen); + break; + + case P_DSR95: + buf[l] = '\0'; // prepare for trim + trim((char *)buf + 13); // strip spc, nl, cr ... + er->ecmlen = cs_strlen((char *)buf + 13) >> 1; + if(er->ecmlen < 0 || er->ecmlen > MAX_ECM_SIZE) + { return (3); } + er->prid = cs_atoi((char *)buf + 3, 3, 0); // ignore errors + er->caid = cs_atoi((char *)buf + 9, 2, 0); // ignore errors + if(cs_atob(er->ecm, (char *)buf + 13, er->ecmlen) < 0) + { + cs_log("illegal characters in ecm-request"); + return (1); + } + if(serialdata->dsr9500type == P_DSR_WITHSID) + { + er->ecmlen -= 2; + if(er->ecmlen < 0) + { return (3); } + er->srvid = cs_atoi((char *)buf + 13 + (er->ecmlen << 1), 2, 0); + } + break; + + case P_GS: + er->ecmlen = ((buf[3] << 8) | buf[2]) - 6; + er->srvid = (buf[5] << 8) | buf[4]; // sid + er->caid = (buf[7] << 8) | buf[6]; + er->prid = 0; + + if(er->ecmlen < 0 || er->ecmlen > MAX_ECM_SIZE || (10 + er->ecmlen > l)) + { return (3); } + memcpy(er->ecm, buf + 10, er->ecmlen); + break; + + case P_ALPHA: + l = oscam_ser_alpha_convert(buf, l); + er->ecmlen = b2i(2, buf + 1) - 2; + er->caid = b2i(2, buf + 3); + if((er->ecmlen != l - 5) || (er->ecmlen > MAX_ECM_SIZE) || (er->ecmlen < 0)) + { + cs_log("incomplete request (%d bytes)", l); + return (1); + } + memcpy(er->ecm, buf + 5, er->ecmlen); + break; + + case P_GBOX: + if(((serialdata->gbox_lens.cat_len + 3 + 3 + 1) > l) + || ((serialdata->gbox_lens.ecm_len + 3) > l)) + { + return (3); + } + er->srvid = b2i(2, buf + serialdata->gbox_lens.cat_len + 3 + 3); + er->ecmlen = serialdata->gbox_lens.ecm_len + 3; + + if(er->ecmlen < 0 || er->ecmlen > MAX_ECM_SIZE) + { return (3); } + if(serialdata->gbox_lens.cat_len + 3 + serialdata->gbox_lens.pmt_len + 3 + er->ecmlen > l) + { return (3); } + + memcpy(er->ecm, buf + serialdata->gbox_lens.cat_len + 3 + serialdata->gbox_lens.pmt_len + 3, er->ecmlen); + break; + } + return (0); +} + +static void oscam_ser_process_ecm(uint8_t *buf, int32_t l) +{ + ECM_REQUEST *er; + + if(!(er = get_ecmtask())) + { return; } + + switch(oscam_ser_check_ecm(er, buf, l)) + { + default: + case 3: + case 2: + //er->rc = E_CORRUPT; + NULLFREE(er); + return; // error without log + case 1: + er->rc = E_CORRUPT; // error with log + break; + } + get_cw(cur_client(), er); +} + + +static void oscam_ser_server(void) +{ + int32_t n; + uint8_t mbuf[1024]; + int32_t *pserial_errors = &cur_client()->serialdata->serial_errors; + + cur_client()->serialdata->connected = 0; + oscam_ser_init_client(); + + while((n = process_input(mbuf, sizeof(mbuf), INT_MAX)) > 0) + { + if((*pserial_errors) > 3) + { + cs_log("too many errors, reiniting..."); + break; + } + + oscam_ser_auth_client(mbuf[0] & 0xF); + switch(mbuf[0] >> 4) + { + case IS_ECM: + oscam_ser_process_ecm(mbuf + 1, n - 1); + break; + case IS_PMT: + oscam_ser_process_pmt(mbuf + 1, n - 1); + break; + case IS_LGO: + oscam_ser_client_logon(mbuf + 1, n - 1); + break; + } + } + + if(cur_client()->serialdata->oscam_ser_port > 0) + { network_tcp_connection_close(cur_client()->reader, "error reading from socket"); } + oscam_ser_disconnect(); +} + +static int32_t init_oscam_ser_device(struct s_client *cl) +{ + char *device = cl->serialdata->oscam_ser_device; + speed_t baud = cl->serialdata->oscam_ser_baud; + int32_t port = cl->serialdata->oscam_ser_port; + int32_t fd; + + // network connection to a TCP-exposed serial port + if(port > 0) + { + cs_strncpy(cl->reader->device, device, sizeof(cl->reader->device)); + cl->reader->r_port = cl->port = port; + fd = network_tcp_connection_open(cl->reader); + if(fd < 0) + { return 0; } + else + { return fd; } + } + else // standard serial port connection + { + fd = open(device, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK); + if(fd > 0) + { + fcntl(fd, F_SETFL, 0); + if(oscam_ser_set_serial_device(fd, baud) < 0) { cs_log("ERROR ioctl"); } + if(tcflush(fd, TCIOFLUSH) < 0) { cs_log("ERROR flush"); } + } + else + { + fd = 0; + cs_log("ERROR opening %s (errno=%d %s)", device, errno, strerror(errno)); + } + return (fd); + } +} + +static void oscam_copy_serialdata(struct s_serial_client *dest, struct s_serial_client *src) +{ + if(dest && src) + { + dest->connected = src->connected; + memcpy(&dest->tps, &src->tps, sizeof(dest->tps)); + memcpy(&dest->tpe, &src->tpe, sizeof(dest->tpe)); + memcpy(&dest->oscam_ser_usr, &src->oscam_ser_usr, sizeof(dest->oscam_ser_usr)); + memcpy(&dest->oscam_ser_device, &src->oscam_ser_device, sizeof(dest->oscam_ser_device)); + dest->oscam_ser_port = src->oscam_ser_port; + dest->oscam_ser_baud = src->oscam_ser_baud; + dest->oscam_ser_delay = src->oscam_ser_delay; + dest->oscam_ser_timeout = src->oscam_ser_timeout; + dest->oscam_ser_proto = src->oscam_ser_proto; + dest->serial_errors = src->serial_errors; + dest->dsr9500type = src->dsr9500type; + dest->samsung_0a = src->samsung_0a; // number of 0A in ALL dcw sent into samsung + dest->samsung_dcw = src->samsung_dcw; // number of dcw sent into samsung before echo or ecm is received + + dest->gbox_lens = src->gbox_lens; + memcpy(&dest->sssp_tab, &src->sssp_tab, sizeof(dest->sssp_tab)); + dest->sssp_srvid = src->sssp_srvid; + dest->sssp_num = src->sssp_num; + dest->sssp_fix = src->sssp_fix; + } +} + +static void oscam_init_serialdata(struct s_serial_client *dest) +{ + if(dest) + { + memset(dest, 0, sizeof(struct s_serial_client)); + dest->oscam_ser_timeout = 50; + dest->dsr9500type = P_DSR_AUTO; + } +} + +static void *oscam_ser_fork(void *pthreadparam) +{ + struct s_thread_param *pparam = (struct s_thread_param *) pthreadparam; + struct s_client *cl = create_client(get_null_ip()); + SAFE_SETSPECIFIC(getclient, cl); + cl->thread = pthread_self(); + cl->typ = 'c'; + cl->module_idx = pparam->module_idx; + cl->account = first_client->account; + + if(!cl->serialdata && !cs_malloc(&cl->serialdata, sizeof(struct s_serial_client))) + { return NULL; } + + set_thread_name(__func__); + oscam_init_serialdata(cl->serialdata); + oscam_copy_serialdata(cl->serialdata, &pparam->serialdata); + + if(cl->serialdata->oscam_ser_port > 0) + { + // reader struct for serial network connection + struct s_reader *newrdr; + if(!cs_malloc(&newrdr, sizeof(struct s_reader))) + { return NULL; } + memset(newrdr, 0, sizeof(struct s_reader)); + newrdr->client = cl; + newrdr->ph = *serial_ph; + cl->reader = newrdr; + cs_strncpy(cl->reader->label, "network-socket", sizeof(cl->reader->label)); + } + + cs_log("serial: initialized (%s@%s)", cl->serialdata->oscam_ser_proto > P_MAX ? + "auto" : proto_txt[cl->serialdata->oscam_ser_proto], cl->serialdata->oscam_ser_device); + + SAFE_MUTEX_LOCK(&mutex); + bcopy_end = 1; + SAFE_MUTEX_UNLOCK(&mutex); + SAFE_COND_SIGNAL(&cond); + + while(!exit_oscam) + { + cl->login = time((time_t *)0); + cl->pfd = init_oscam_ser_device(cl); + if(cl->pfd) + { oscam_ser_server(); } + else + { cs_sleepms(60000); } // retry in 1 min. (USB-Device ?) + if(cl->pfd) { close(cl->pfd); } + } + NULLFREE(cl->serialdata); + NULLFREE(cl->reader); + return NULL; +} + +void *init_oscam_ser(struct s_client *UNUSED(cl), uint8_t *UNUSED(mbuf), int32_t module_idx) +{ + char sdevice[512]; + int32_t ret; + struct s_thread_param param; + oscam_init_serialdata(¶m.serialdata); + + if(cfg.ser_device) + { cs_strncpy(sdevice, cfg.ser_device, sizeof(sdevice)); } + else + { memset(sdevice, 0, sizeof(sdevice)); } + + param.module_idx = module_idx; + char *p, *q; + char cltype = 'c'; // now auto should work + + if(bcopy_end == -1) // mutex should be initialized only once + { + cs_pthread_cond_init(__func__, &mutex, &cond); + bcopy_end = 0; + } + + while((p = strrchr(sdevice, ';'))) + { + *p = 0; + q = p; + q = q + 1; + if(!(q) || (!(q)[0])) { return NULL; } + if(!oscam_ser_parse_url(p + 1, ¶m.serialdata, &cltype)) { return NULL; } + + ret = start_thread("oscam_ser_fork", oscam_ser_fork, (void *) ¶m, NULL, 1, 1); + if(ret) + { + return NULL; + } + else + { + oscam_wait_ser_fork(); + } + } + + if(!sdevice[0]) { return NULL; } + if(!oscam_ser_parse_url(sdevice, ¶m.serialdata, &cltype)) { return NULL; } + + ret = start_thread("oscam_ser_fork", oscam_ser_fork, (void *) ¶m, NULL, 1, 1); + if(ret) + { + return NULL; + } + else + { + oscam_wait_ser_fork(); + } + return NULL; +} + +/* + * client functions + */ + +static int32_t oscam_ser_client_init(struct s_client *client) +{ + if(!client->serialdata && !cs_malloc(&client->serialdata, sizeof(struct s_serial_client))) + { return 1; } + + oscam_init_serialdata(client->serialdata); + + if((!client->reader->device[0])) { cs_disconnect_client(client); } + if(!oscam_ser_parse_url(client->reader->device, client->serialdata, NULL)) { cs_disconnect_client(client); } + client->pfd = init_oscam_ser_device(client); + + if(client->serialdata->oscam_ser_proto == P_TWIN) + { + if(client->pfd > 0) + { + client->reader->tcp_connected = 1; + client->reader->card_status = CARD_INSERTED; + } + } + + return ((client->pfd > 0) ? 0 : 1); +} + +static int32_t oscam_ser_twin_send(struct s_client *client, ECM_REQUEST *er) +{ + struct ecmtw tw; + tw = get_twin(er); + cs_debug_mask(D_CLIENT, "found channel: %04X:%06X:%04X:%04X:%04X", tw.caid, tw.provid, tw.deg, tw.freq, tw.srvid); + uint8_t wbuf[32]; + wbuf[0] = 7; + wbuf[1] = 6; + wbuf[2] = tw.deg >> 8; + wbuf[3] = tw.deg & 0xff; + wbuf[4] = tw.freq >> 8; + wbuf[5] = tw.freq & 0xff; + wbuf[6] = tw.srvid >> 8; + wbuf[7] = tw.srvid & 0xff; + wbuf[8] = wbuf[0] ^ wbuf[1] ^ wbuf[2] ^ wbuf[3] ^ wbuf[4] ^ wbuf[5] ^ wbuf[6] ^ wbuf[7]; + oscam_ser_send(client, wbuf, 9); + return(0); +} + +static int32_t oscam_ser_send_ecm(struct s_client *client, ECM_REQUEST *er) +{ + char *tmp; + uint8_t *buf; + if(!cs_malloc(&buf, er->ecmlen + 12)) + { + return(-1); + } + + switch(client->serialdata->oscam_ser_proto) + { + case P_HSIC: + memset(buf, 0, 12); + buf[0] = 2; + i2b_buf(2, er->caid, buf + 1); + i2b_buf(3, er->prid, buf + 3); + i2b_buf(2, er->pid, buf + 6); + i2b_buf(2, er->srvid, buf + 10); + memcpy(buf + 12, er->ecm, er->ecmlen); + oscam_ser_send(client, buf, 12 + er->ecmlen); + break; + + case P_BOMBA: + oscam_ser_send(client, er->ecm, er->ecmlen); + break; + + case P_DSR95: + if(cs_malloc(&tmp, er->ecmlen * 2 + 1)) + { + if(client->serialdata->dsr9500type == P_DSR_WITHSID) + { + snprintf((char *)buf, 512, "%c%08X%04X%s%04X\n\r", + 3, er->prid, er->caid, cs_hexdump(0, er->ecm, er->ecmlen, tmp, er->ecmlen * 2 + 1), er->srvid); + oscam_ser_send(client, buf, (er->ecmlen << 1) + 19); // 1 + 8 + 4 + l*2 + 4 + 2 + } + else + { + snprintf((char *)buf, 512, "%c%08X%04X%s\n\r", + 3, er->prid, er->caid, cs_hexdump(0, er->ecm, er->ecmlen, tmp, er->ecmlen * 2 + 1)); + oscam_ser_send(client, buf, (er->ecmlen << 1) + 15); // 1 + 8 + 4 + l*2 + 2 + } + NULLFREE(tmp); + } + break; + + case P_ALPHA: + buf[0] = 0x80; + i2b_buf(2, 2 + er->ecmlen, buf + 1); + i2b_buf(2, er->caid, buf + 3); + memcpy(buf + 5, er->ecm, er->ecmlen); + oscam_ser_send(client, buf, oscam_ser_alpha_convert(buf, 5 + er->ecmlen)); + break; + + case P_TWIN: + oscam_ser_twin_send(client, er); + break; + } + NULLFREE(buf); + return (0); +} + +static void oscam_ser_process_dcw(uint8_t *dcw, int32_t *rc, uint8_t *buf, int32_t l, struct s_client *client) +{ + switch(client->serialdata->oscam_ser_proto) + { + case P_HSIC: + if((l >= 23) && (buf[2] == 0x3A) && (buf[3] == 0x3A)) + { + int32_t i; + uint8_t crc; + for(i = 4, crc = HSIC_CRC; i < 20; i++) + { crc ^= buf[i]; } + if(crc == buf[20]) + { + memcpy(dcw, buf + 4, 16); + *rc = 1; + } + } + break; + + case P_BOMBA: + if(l >= 16) + { + memcpy(dcw, buf, 16); + *rc = 1; + } + break; + + case P_DSR95: + if((l >= 17) && (buf[0] == 4)) + { + memcpy(dcw, buf + 1, 16); + *rc = 1; + } + break; + + case P_ALPHA: + if((l >= 19) && (buf[0] == 0x88)) + { + memcpy(dcw, buf + 3, 16); + *rc = 1; + } + break; + + case P_TWIN: + if ((l >= 19) && (buf[0] == 0xF7)) + { + memcpy(dcw, buf + 3, 16); + *rc = 1; + } + } +} + +static int32_t oscam_ser_recv_chk(struct s_client *client, uint8_t *dcw, int32_t *rc, uint8_t *buf, int32_t n) +{ + *rc = (-1); + + switch(buf[0] >> 4) + { + case IS_DCW: + oscam_ser_process_dcw(dcw, rc, buf + 1, n - 1, client); + break; + } + return ((*rc < 0) ? (-1) : 0); // idx not supported in serial module +} + +/* + * protocol structure + */ + +void module_serial(struct s_module *ph) +{ + ph->desc = "serial"; + ph->type = MOD_CONN_SERIAL; + ph->large_ecm_support = 1; + ph->listenertype = LIS_SERIAL; + ph->s_handler = init_oscam_ser; + ph->recv = oscam_ser_recv; + ph->send_dcw = oscam_ser_send_dcw; + ph->c_init = oscam_ser_client_init; + ph->c_recv_chk = oscam_ser_recv_chk; + ph->c_send_ecm = oscam_ser_send_ecm; + ph->num = R_SERIAL; + serial_ph = ph; +} +#endif diff --git a/module-stat.c b/module-stat.c new file mode 100644 index 0000000..741c66f --- /dev/null +++ b/module-stat.c @@ -0,0 +1,2119 @@ +#define MODULE_LOG_PREFIX "stat" + +#include "globals.h" + +#ifdef WITH_LB +#include "cscrypt/md5.h" +#include "module-cacheex.h" +#include "module-cccam.h" +#include "oscam-array.h" +#include "oscam-cache.h" +#include "oscam-conf-chk.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-files.h" +#include "oscam-lock.h" +#include "oscam-string.h" +#include "oscam-time.h" + +#define UNDEF_AVG_TIME 99999 // NOT set here 0 or small value! Could cause there reader get selected +#define MAX_ECM_SEND_CACHE 16 + +#define LB_NONE 0 +#define LB_FASTEST_READER_FIRST 1 +#define LB_OLDEST_READER_FIRST 2 +#define LB_LOWEST_USAGELEVEL 3 + +#define DEFAULT_LOCK_TIMEOUT 1000000 + +extern CS_MUTEX_LOCK ecmcache_lock; +extern struct ecm_request_t *ecmcwcache; + +static int32_t stat_load_save; + +static struct timeb last_housekeeping; + +void init_stat(void) +{ + stat_load_save = -100; + + //checking config + if(cfg.lb_nbest_readers < 2) + { cfg.lb_nbest_readers = DEFAULT_NBEST; } + if(cfg.lb_nfb_readers < 0) + { cfg.lb_nfb_readers = DEFAULT_NFB; } + if(cfg.lb_min_ecmcount < 2) + { cfg.lb_min_ecmcount = DEFAULT_MIN_ECM_COUNT; } + if(cfg.lb_max_ecmcount < 3) + { cfg.lb_max_ecmcount = DEFAULT_MAX_ECM_COUNT; } + if(cfg.lb_reopen_seconds < 10) + { cfg.lb_reopen_seconds = DEFAULT_REOPEN_SECONDS; } + if(cfg.lb_retrylimit < 0) + { cfg.lb_retrylimit = DEFAULT_RETRYLIMIT; } + if(cfg.lb_stat_cleanup <= 0) + { cfg.lb_stat_cleanup = DEFAULT_LB_STAT_CLEANUP; } +} + +#define LINESIZE 1024 + +static uint32_t get_prid(uint16_t caid, uint32_t prid) +{ + int32_t i; + for(i = 0; i < cfg.lb_noproviderforcaid.ctnum; i++) + { + CAIDTAB_DATA *d = &cfg.lb_noproviderforcaid.ctdata[i]; + uint16_t tcaid = d->caid; + if(!tcaid) { break; } + if((tcaid == caid) || (tcaid < 0x0100 && (caid >> 8) == tcaid)) + { + prid = 0; + break; + } + + } + return prid; +} + +static void get_stat_query(ECM_REQUEST *er, STAT_QUERY *q) +{ + memset(q, 0, sizeof(STAT_QUERY)); + + q->caid = er->caid; + q->prid = get_prid(er->caid, er->prid); + q->srvid = er->srvid; + q->chid = er->chid; + q->ecmlen = er->ecmlen; +} + +void load_stat_from_file(void) +{ + stat_load_save = 0; + char buf[256]; + char *line; + char *fname; + FILE *file; + + if(!cfg.lb_savepath) + { + get_tmp_dir_filename(buf, sizeof(buf), "stat"); + fname = buf; + } + else + { fname = cfg.lb_savepath; } + + file = fopen(fname, "r"); + if(!file) + { + cs_log_dbg(D_LB, "loadbalancer: could not open %s for reading (errno=%d %s)", fname, errno, strerror(errno)); + return; + } + + if(!cs_malloc(&line, LINESIZE)) + { + fclose(file); + return; + } + + cs_log_dbg(D_LB, "loadbalancer: load statistics from %s", fname); + + struct timeb ts, te; + cs_ftime(&ts); + + struct s_reader *rdr = NULL; + READER_STAT *s; + + int32_t i = 1; + int32_t valid = 0; +#ifdef WITH_DEBUG + int32_t count = 0; +#endif + int32_t type = 0; + char *ptr, *saveptr1 = NULL; + char *split[12]; + + while(fgets(line, LINESIZE, file)) + { + if(!line[0] || line[0] == '#' || line[0] == ';') + { continue; } + + if(!cs_malloc(&s, sizeof(READER_STAT))) + { continue; } + + //get type by evaluating first line: + if(type == 0) + { + if(strstr(line, " rc ")) { type = 2; } + else { type = 1; } + } + + if(type == 1) // New format - faster parsing: + { + for(i = 0, ptr = strtok_r(line, ",", &saveptr1); ptr && i < 12 ; ptr = strtok_r(NULL, ",", &saveptr1), i++) + { split[i] = ptr; } + valid = (i == 11); + if(valid) + { + cs_strncpy(buf, split[0], sizeof(buf)); + s->rc = atoi(split[1]); + s->caid = a2i(split[2], 4); + s->prid = a2i(split[3], 6); + s->srvid = a2i(split[4], 4); + s->chid = a2i(split[5], 4); + s->time_avg = atoi(split[6]); + s->ecm_count = atoi(split[7]); + s->last_received.time = atol(split[8]); + s->fail_factor = atoi(split[9]); + s->ecmlen = a2i(split[10], 2); + } + } + else // Old format - keep for compatibility: + { + i = sscanf(line, "%255s rc %04d caid %04hX prid %06X srvid %04hX time avg %d ms ecms %d last %" SCNd64 " fail %d len %02hX\n", + buf, &s->rc, &s->caid, &s->prid, &s->srvid, + &s->time_avg, &s->ecm_count, (int64_t *)&s->last_received.millitm, &s->fail_factor, &s->ecmlen); + valid = i > 5; + } + + if(valid && s->ecmlen > 0) + { + if(rdr == NULL || strcmp(buf, rdr->label) != 0) + { + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(strcmp(rdr->label, buf) == 0) + { + break; + } + } + } + + if(rdr != NULL && strcmp(buf, rdr->label) == 0) + { + if(!rdr->lb_stat) + { + rdr->lb_stat = ll_create("lb_stat"); + cs_lock_create(__func__, &rdr->lb_stat_lock, rdr->label, DEFAULT_LOCK_TIMEOUT); + } + + ll_append(rdr->lb_stat, s); +#ifdef WITH_DEBUG + count++; +#endif + } + else + { + cs_log("loadbalancer: statistics could not be loaded for %s", buf); + NULLFREE(s); + } + } + else + { + cs_log_dbg(D_LB, "loadbalancer: statistics ERROR: %s rc=%d i=%d", buf, s->rc, i); + NULLFREE(s); + } + } + fclose(file); + NULLFREE(line); + + cs_ftime(&te); +#ifdef WITH_DEBUG + int64_t load_time = comp_timeb(&te, &ts); + + cs_log_dbg(D_LB, "loadbalancer: statistics loaded %d records in %"PRId64" ms", count, load_time); +#endif +} + +void lb_destroy_stats(struct s_reader *rdr) +{ + if(!rdr->lb_stat) + return; + cs_lock_destroy(__func__, &rdr->lb_stat_lock); + ll_destroy_data(&rdr->lb_stat); +} + +/** + * get statistic values for reader ridx and caid/prid/srvid/ecmlen + **/ +static READER_STAT *get_stat_lock(struct s_reader *rdr, STAT_QUERY *q, int8_t lock) +{ + if(!rdr->lb_stat) + { + rdr->lb_stat = ll_create("lb_stat"); + cs_lock_create(__func__, &rdr->lb_stat_lock, rdr->label, DEFAULT_LOCK_TIMEOUT); + } + + if(lock) { cs_readlock(__func__, &rdr->lb_stat_lock); } + + LL_ITER it = ll_iter_create(rdr->lb_stat); + READER_STAT *s; + int32_t i = 0; + while((s = ll_iter_next(&it))) + { + i++; + if(s->caid == q->caid && s->prid == q->prid && s->srvid == q->srvid && s->chid == q->chid) + { + if(s->ecmlen == q->ecmlen) + { break; } + if(!s->ecmlen) + { + s->ecmlen = q->ecmlen; + break; + } + if(!q->ecmlen) // Query without ecmlen from dvbapi + { break; } + } + } + if(lock) { cs_readunlock(__func__, &rdr->lb_stat_lock); } + + // Move stat to list start for faster access: + if (i > 10 && s && !rdr->lb_stat_busy) { + if (lock) cs_writelock(__func__, &rdr->lb_stat_lock); + ll_iter_move_first(&it); + if (lock) cs_writeunlock(__func__, &rdr->lb_stat_lock); + } + + return s; +} + +/** + * get statistic values for reader ridx and caid/prid/srvid/ecmlen + **/ +static READER_STAT *get_stat(struct s_reader *rdr, STAT_QUERY *q) +{ + return get_stat_lock(rdr, q, 1); +} + +/** + * Calculates average time + */ +static void calc_stat(READER_STAT *s) +{ + int32_t i, c = 0, t = 0; + for(i = 0; i < LB_MAX_STAT_TIME; i++) + { + if(s->time_stat[i] > 0) + { + t += (int32_t)s->time_stat[i]; + c++; + } + } + if(!c) + { s->time_avg = UNDEF_AVG_TIME; } + else + { s->time_avg = t / c; } +} + +/** + * Saves statistik to /tmp/.oscam/stat.n where n is reader-index + */ +static void save_stat_to_file_thread(void) +{ + stat_load_save = 0; + char buf[256]; + + set_thread_name(__func__); + + char *fname; + if(!cfg.lb_savepath) + { + get_tmp_dir_filename(buf, sizeof(buf), "stat"); + fname = buf; + } + else + { fname = cfg.lb_savepath; } + + FILE *file = fopen(fname, "w"); + + if(!file) + { + cs_log("can't write to file %s", fname); + return; + } + + struct timeb ts, te; + cs_ftime(&ts); + + int32_t cleanup_timeout = (cfg.lb_stat_cleanup * 60 * 60 * 1000); + + int32_t count = 0; + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + + if(rdr->lb_stat) + { + rdr->lb_stat_busy = 1; + + cs_writelock(__func__, &rdr->lb_stat_lock); + LL_ITER it = ll_iter_create(rdr->lb_stat); + READER_STAT *s; + while((s = ll_iter_next(&it))) + { + int64_t gone = comp_timeb(&ts, &s->last_received); + if(gone > cleanup_timeout || !s->ecmlen) // cleanup old stats + { + ll_iter_remove_data(&it); + continue; + } + + //Old version, too slow to parse: + //fprintf(file, "%s rc %d caid %04hX prid %06X srvid %04hX time avg %d ms ecms %d last %ld fail %d len %02hX\n", + // rdr->label, s->rc, s->caid, s->prid, + // s->srvid, s->time_avg, s->ecm_count, s->last_received, s->fail_factor, s->ecmlen); + + //New version: + fprintf(file, "%s,%d,%04hX,%06X,%04hX,%04hX,%d,%d,%" PRId64 ",%d,%02hX\n", + rdr->label, s->rc, s->caid, s->prid, + s->srvid, (uint16_t)s->chid, s->time_avg, s->ecm_count, (int64_t)s->last_received.time, s->fail_factor, s->ecmlen); + + count++; + //if(count % 500 == 0) { // Saving stats is using too much cpu and causes high file load. so we need a break + // cs_readunlock(__func__, &rdr->lb_stat_lock); + // cs_sleepms(100); + // cs_readlock(__func__, &rdr->lb_stat_lock); + //} + } + cs_writeunlock(__func__, &rdr->lb_stat_lock); + + rdr->lb_stat_busy = 0; + } + } + + fclose(file); + + cs_ftime(&te); + int64_t load_time = comp_timeb(&te, &ts); + + + cs_log("loadbalancer: statistic saved %d records to %s in %"PRId64" ms", count, fname, load_time); +} + +void save_stat_to_file(int32_t thread) +{ + stat_load_save = 0; + if(thread) + { start_thread("save lb stats", (void *)&save_stat_to_file_thread, NULL, NULL, 1, 1); } + else + { save_stat_to_file_thread(); } +} + +/** + * fail_factor is multiplied to the reopen_time. This function increases the fail_factor + **/ +static void inc_fail(READER_STAT *s) +{ + if(s->fail_factor <= 0) + { s->fail_factor = 1; } + else + { s->fail_factor++; } // inc by one at the time +} + +static READER_STAT *get_add_stat(struct s_reader *rdr, STAT_QUERY *q) +{ + if (rdr->lb_stat_busy) + return NULL; + + if(!rdr->lb_stat) + { + rdr->lb_stat = ll_create("lb_stat"); + cs_lock_create(__func__, &rdr->lb_stat_lock, rdr->label, DEFAULT_LOCK_TIMEOUT); + } + + cs_writelock(__func__, &rdr->lb_stat_lock); + + READER_STAT *s = get_stat_lock(rdr, q, 0); + if(!s) + { + if(cs_malloc(&s, sizeof(READER_STAT))) + { + s->caid = q->caid; + s->prid = q->prid; + s->srvid = q->srvid; + s->chid = q->chid; + s->ecmlen = q->ecmlen; + s->time_avg = UNDEF_AVG_TIME; // dummy placeholder + s->rc = E_FOUND; // set to found--> do not change! + cs_ftime(&s->last_received); + s->fail_factor = 0; + s->ecm_count = 0; + ll_prepend(rdr->lb_stat, s); + } + } + cs_writeunlock(__func__, &rdr->lb_stat_lock); + + return s; +} + +static void housekeeping_stat(int32_t force); + +void readerinfofix_get_stat_query(ECM_REQUEST *er, STAT_QUERY *q) +{ + get_stat_query(er, q); +} + +void readerinfofix_inc_fail(READER_STAT *s) +{ + inc_fail(s); +} + +READER_STAT *readerinfofix_get_add_stat(struct s_reader *rdr, STAT_QUERY *q) +{ + return get_add_stat(rdr, q); +} + +static int32_t get_reopen_seconds(READER_STAT *s) +{ + int32_t max = (INT_MAX / cfg.lb_reopen_seconds); + if(max > 9999) { max = 9999; } + if(s->fail_factor > max) + { s->fail_factor = max; } + if(!s->fail_factor) + { return cfg.lb_reopen_seconds; } + return s->fail_factor * cfg.lb_reopen_seconds; +} + +/** + * Adds caid/prid/srvid/ecmlen to stat-list for reader ridx with time/rc + */ +static void add_stat(struct s_reader *rdr, ECM_REQUEST *er, int32_t ecm_time, int32_t rc, uint8_t rcEx) +{ + //inc ecm_count if found, drop to 0 if not found: + // rc codes: + // 0 = found + + // 1 = cache1 # + // 2 = cache2 # + // 3 = cacheex # + // 4 = not found - + // 5 = timeout - + // 6 = sleeping # + // 7 = fake - + // 8 = invalid - + // 9 = corrupt # + // 10= no card # + // 11= expdate # + // 12= disabled # + // 13= stopped # + // 100= unhandled # + // + = adds statistic values + // # = ignored because of duplicate values, temporary failures or softblocks + // - = causes loadbalancer to block this reader for this caid/prov/sid + + + if(!rdr || !er || !cfg.lb_mode || !er->ecmlen || !er->client || rdr->lb_stat_busy) + { return; } + + struct s_client *cl = rdr->client; + if(!check_client(cl)) + { return; } + + + // IGNORE stats for fallback reader with lb_force_fallback parameter + if(chk_is_fixed_fallback(rdr, er) && rdr->lb_force_fallback) + { return; } + + + // IGNORE fails for ratelimit check + if(rc == E_NOTFOUND && rcEx == E2_RATELIMIT) + { +#ifdef WITH_DEBUG + if((D_LB & cs_dblevel)) + { + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_log_dbg(D_LB, "loadbalancer: NOT adding stat (blocking) for reader %s because fails ratelimit checks!", rdr->label); + } +#endif + return; + } + + + // IGNORE fails when reader has positive services defined in new lb_whitelist_services parameter! See ticket #3310,#3311 + if(rc >= E_NOTFOUND && has_lb_srvid(cl, er)) + { +#ifdef WITH_DEBUG + if((D_LB & cs_dblevel)) + { + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_log_dbg(D_LB, "loadbalancer: NOT adding stat (blocking) for reader %s because has positive srvid: rc %d %s time %d ms", + rdr->label, rc, buf, ecm_time); + } +#endif + return; + } + + + // IGNORE fails for sleep CMD08 + if(rc == E_NOTFOUND && rdr->client->stopped==2) + { +#ifdef WITH_DEBUG + if((D_LB & cs_dblevel)) + { + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_log_dbg(D_LB, "loadbalancer: NOT adding stat (no block) for reader %s because CMD08 sleep command!", rdr->label); + } +#endif + return; + } + + // IGNORE timeouts on local readers (they could be busy handling an emm or entitlement refresh) + if(rc == E_TIMEOUT && !is_network_reader(rdr)) + { +#ifdef WITH_DEBUG + if((D_LB & cs_dblevel)) + { + cs_log_dbg(D_LB, "loadbalancer: NOT adding stat (no block) for reader %s because timeout on local reader", rdr->label); + } +#endif + return; + } + + // IGNORE unhandled ecmresponses + if(rc == E_UNHANDLED) + { +#ifdef WITH_DEBUG + if((D_LB & cs_dblevel)) + { + cs_log_dbg(D_LB, "loadbalancer: NOT adding stat (no block) for reader %s because unhandled reponse", rdr->label); + } +#endif + return; + } + + // ignore too old ecms + if((uint32_t)ecm_time >= 3 * cfg.ctimeout) + { return; } + + if((uint32_t)ecm_time >= cfg.ctimeout) + { rc = E_TIMEOUT;} + + STAT_QUERY q; + get_stat_query(er, &q); + READER_STAT *s; + s = get_add_stat(rdr, &q); + if (!s) return; + + struct timeb now; + cs_ftime(&now); + + cs_ftime(&s->last_received); + + if(rc == E_FOUND) // found + { + + s->rc = E_FOUND; + s->ecm_count++; + s->fail_factor = 0; + + // FASTEST READER: + s->time_idx++; + if(s->time_idx >= LB_MAX_STAT_TIME) + { s->time_idx = 0; } + s->time_stat[s->time_idx] = ecm_time; + calc_stat(s); + + // OLDEST READER now set by get best reader! + + + // USAGELEVEL: + /* Assign a value to rdr->lb_usagelevel_ecmcount, + because no determined value was assigned before. */ + if(rdr->lb_usagelevel_ecmcount < 0) + { rdr->lb_usagelevel_ecmcount = 0; } + + rdr->lb_usagelevel_ecmcount++; /* ecm is found so counter should increase */ + if((rdr->lb_usagelevel_ecmcount % cfg.lb_min_ecmcount) == 0) //update every MIN_ECM_COUNT usagelevel: + { + int64_t t = comp_timeb(&now, &rdr->lb_usagelevel_time) / 1000; + rdr->lb_usagelevel = cfg.lb_min_ecmcount * 1000 / (t < 1 ? 1 : t); + /* Reset of usagelevel time and counter */ + rdr->lb_usagelevel_time = now; + rdr->lb_usagelevel_ecmcount = 0; + } + + } + else if(rc == E_NOTFOUND || rc == E_TIMEOUT || rc == E_FAKE) // not found / timeout /fake + { + inc_fail(s); + s->rc = rc; + } + else if(rc == E_INVALID) // invalid + { + s->rc = rc; + } + else + { +#ifdef WITH_DEBUG + if(rc >= E_FOUND && (D_LB & cs_dblevel)) + { + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_log_dbg(D_LB, "loadbalancer: not handled stat for reader %s: rc %d %s time %d ms", + rdr->label, rc, buf, ecm_time); + } +#endif + return; + } + + housekeeping_stat(0); + +#ifdef WITH_DEBUG + if(D_LB & cs_dblevel) + { + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_log_dbg(D_LB, "loadbalancer: adding stat for reader %s: rc %d %s time %d ms fail %d", + rdr->label, rc, buf, ecm_time, s->fail_factor); + } +#endif + + if(cfg.lb_save) + { + stat_load_save++; + if(stat_load_save > cfg.lb_save) + { save_stat_to_file(1); } + } + +} + +int32_t clean_stat_by_rc(struct s_reader *rdr, int8_t rc, int8_t inverse) +{ + int32_t count = 0; + if(rdr && rdr->lb_stat) + { + if (rdr->lb_stat_busy) return 0; + rdr->lb_stat_busy = 1; + cs_writelock(__func__, &rdr->lb_stat_lock); + READER_STAT *s; + LL_ITER itr = ll_iter_create(rdr->lb_stat); + while((s = ll_iter_next(&itr))) + { + if((!inverse && s->rc == rc) || (inverse && s->rc != rc)) + { + ll_iter_remove_data(&itr); + count++; + } + } + cs_writeunlock(__func__, &rdr->lb_stat_lock); + rdr->lb_stat_busy = 0; + } + return count; +} + +int32_t clean_all_stats_by_rc(int8_t rc, int8_t inverse) +{ + int32_t count = 0; + LL_ITER itr = ll_iter_create(configured_readers); + struct s_reader *rdr; + while((rdr = ll_iter_next(&itr))) + { + count += clean_stat_by_rc(rdr, rc, inverse); + } + save_stat_to_file(0); + return count; +} + +int32_t clean_stat_by_id(struct s_reader *rdr, uint16_t caid, uint32_t prid, uint16_t srvid, uint16_t chid, uint16_t ecmlen) +{ + int32_t count = 0; + if(rdr && rdr->lb_stat) + { + if (rdr->lb_stat_busy) return 0; + + rdr->lb_stat_busy = 1; + cs_writelock(__func__, &rdr->lb_stat_lock); + READER_STAT *s; + LL_ITER itr = ll_iter_create(rdr->lb_stat); + while((s = ll_iter_next(&itr))) + { + if(s->caid == caid && + s->prid == prid && + s->srvid == srvid && + s->chid == chid && + s->ecmlen == ecmlen) + { + ll_iter_remove_data(&itr); + count++; + break; // because the entry should unique we can left here + } + } + cs_writeunlock(__func__, &rdr->lb_stat_lock); + rdr->lb_stat_busy = 0; + } + return count; +} + +/* +static int32_t has_ident(FTAB *ftab, ECM_REQUEST *er) { + + if (!ftab || !ftab->filts) + return 0; + + int32_t j, k; + + for (j = 0; j < ftab->nfilts; j++) { + if (ftab->filts[j].caid) { + if (ftab->filts[j].caid==er->caid) { //caid matches! + int32_t nprids = ftab->filts[j].nprids; + if (!nprids) // No Provider ->Ok + return 1; + + for (k = 0; k < nprids; k++) { + uint32_t prid = ftab->filts[j].prids[k]; + if (prid == er->prid) { //Provider matches + return 1; + } + } + } + } + } + return 0; //No match! +}*/ + +static int32_t get_retrylimit(ECM_REQUEST *er) +{ + return caidvaluetab_get_value(&cfg.lb_retrylimittab, er->caid, cfg.lb_retrylimit); +} + +static int32_t get_nfb_readers(ECM_REQUEST *er) +{ + int32_t nfb_readers = er->client->account->lb_nfb_readers == -1 ? cfg.lb_nfb_readers : er->client->account->lb_nfb_readers; + + if(nfb_readers <= 0) { nfb_readers = 0; } + + return nfb_readers; +} + +static int32_t get_nbest_readers(ECM_REQUEST *er) +{ + int32_t nbest_readers = er->client->account->lb_nbest_readers == -1 ? cfg.lb_nbest_readers : er->client->account->lb_nbest_readers; + CAIDVALUETAB *nbest_readers_tab = er->client->account->lb_nbest_readers_tab.cvnum == 0 ? &cfg.lb_nbest_readers_tab : &er->client->account->lb_nbest_readers_tab; + if(nbest_readers <= 0) { nbest_readers = 1; } + return caidvaluetab_get_value(nbest_readers_tab, er->caid, nbest_readers); +} + +static void convert_to_beta_int(ECM_REQUEST *er, uint16_t caid_to) +{ + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + convert_to_beta(er->client, er, caid_to); + // update ecmd5 for store ECM in cache + memcpy(er->ecmd5, MD5(er->ecm + 13, er->ecmlen - 13, md5tmp), CS_ECMSTORESIZE); + cacheex_update_hash(er); + er->btun = 2; // marked as auto-betatunnel converted. Also for fixing recursive lock in get_cw +} + +static void convert_to_nagra_int(ECM_REQUEST *er, uint16_t caid_to) +{ + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + convert_to_nagra(er->client, er, caid_to); + // update ecmd5 for store ECM in cache + memcpy(er->ecmd5, MD5(er->ecm + 3, er->ecmlen - 3, md5tmp), CS_ECMSTORESIZE); + cacheex_update_hash(er); + er->btun = 2; // marked as auto-betatunnel converted. Also for fixing recursive lock in get_cw +} + +static int32_t lb_valid_btun(ECM_REQUEST *er, uint16_t caidto) +{ + STAT_QUERY q; + READER_STAT *s; + struct s_reader *rdr; + + get_stat_query(er, &q); + q.caid = caidto; + + cs_readlock(__func__, &readerlist_lock); + for(rdr = first_active_reader; rdr ; rdr = rdr->next) + { + if(rdr->lb_stat && rdr->client) + { + s = get_stat(rdr, &q); + if(s && s->rc == E_FOUND) + { + cs_readunlock(__func__, &readerlist_lock); + return 1; + } + } + } + cs_readunlock(__func__, &readerlist_lock); + return 0; +} + +static uint16_t __lb_get_betatunnel_caid_to(uint16_t caid) +{ + int32_t lbbm = cfg.lb_auto_betatunnel_mode; + if(lbbm <= 3) + { + if(caid == 0x1801) { return 0x1722; } + if(caid == 0x1833) { return 0x1702; } + if(caid == 0x1834) { return 0x1722; } + if(caid == 0x1835) { return 0x1722; } + } + if(lbbm >= 1) + { + if(caid == 0x1702) { return 0x1833; } + } + if(lbbm == 1 || lbbm == 4) + { + if(caid == 0x1722) { return 0x1801; } + } + else if(lbbm == 2 || lbbm == 5) + { + if(caid == 0x1722) { return 0x1834; } + } + else if(lbbm == 3 || lbbm == 6) + { + if(caid == 0x1722) { return 0x1835; } + } + return 0; +} + +uint16_t lb_get_betatunnel_caid_to(ECM_REQUEST *er) +{ + if(!cfg.lb_auto_betatunnel) + return 0; + uint16_t caidto = __lb_get_betatunnel_caid_to(er->caid); + if(lb_valid_btun(er, caidto)) + return caidto; + return 0; +} + +void check_lb_auto_betatunnel_mode(ECM_REQUEST *er) +{ + int32_t lbbm = cfg.lb_auto_betatunnel_mode; + if(lbbm == 1 || lbbm == 4) + { + er->caid = 0x1801; + } + else if(lbbm == 2 || lbbm == 5) + { + er->caid = 0x1834; + } + else if(lbbm == 3 || lbbm == 6) + { + er->caid = 0x1835; + } + // no other way to autodetect 1801, 1834 or 1835 +} + +uint16_t get_rdr_caid(struct s_reader *rdr) +{ + if(is_network_reader(rdr) || rdr->typ == R_EMU) + { + return 0; // reader caid is not real caid + } + else + { + return rdr->caid; + } +} + +static void reset_ecmcount_reader(READER_STAT *s, struct s_reader *rdr) +{ + cs_readlock(__func__, &rdr->lb_stat_lock); + if(rdr->lb_stat && rdr->client) + { + if(s) + { + s->ecm_count = 0; + } + } + cs_readunlock(__func__, &rdr->lb_stat_lock); +} + +static void reset_avgtime_reader(READER_STAT *s, struct s_reader *rdr) +{ + cs_readlock(__func__, &rdr->lb_stat_lock); + if(rdr->lb_stat && rdr->client) + { + if(!s) { return; } + int32_t i; + for(i = 0; i < LB_MAX_STAT_TIME; i++) + { + if(s->time_stat[i] > 0) { s->time_stat[i] = 0; } + } + s->time_avg = UNDEF_AVG_TIME; + } + cs_readunlock(__func__, &rdr->lb_stat_lock); +} + +/* force_reopen=1 -> force opening of block readers + * force_reopen=0 -> no force opening of block readers, use reopen_seconds + */ +static void try_open_blocked_readers(ECM_REQUEST *er, STAT_QUERY *q, int32_t *max_reopen, int32_t force_reopen) +{ + struct s_ecm_answer *ea; + READER_STAT *s; + struct s_reader *rdr; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if((ea->status & READER_FALLBACK) || (ea->status & READER_ACTIVE)) { continue; } + rdr = ea->reader; + s = get_stat(rdr, q); + if(!s) { continue; } + + if(!cfg.lb_reopen_invalid && s->rc == E_INVALID){ + cs_log_dbg(D_LB, "loadbalancer: reader %s blocked because INVALID sent! It will be blocked until stats cleaned!", rdr->label); + continue; + } + + // if force_reopen we must active the "valid" reader + if(s->rc != E_FOUND && force_reopen && cfg.lb_force_reopen_always) + { + cs_log_dbg(D_LB, "loadbalancer: force opening reader %s and reset fail_factor! --> ACTIVE", rdr->label); + ea->status |= READER_ACTIVE; + s->fail_factor = 0; + continue; + } + + //active readers reach get_reopen_seconds(s) + struct timeb now; + cs_ftime(&now); + int64_t gone = comp_timeb(&now, &s->last_received); + int32_t reopenseconds = get_reopen_seconds(s); + if(s->rc != E_FOUND && gone > reopenseconds*1000 ) + { + if(*max_reopen) + { + cs_log_dbg(D_LB, "loadbalancer: reader %s reaches %d seconds for reopening (fail_factor %d) --> ACTIVE", rdr->label, reopenseconds, s->fail_factor); + ea->status |= READER_ACTIVE; + (*max_reopen)--; + } + else + { + cs_log_dbg(D_LB, "loadbalancer: reader %s reaches %d seconds for reopening (fail_factor %d), but max_reopen reached!", rdr->label, reopenseconds, s->fail_factor); + } + continue; + } + + if(s->rc != E_FOUND) // for debug output + { + cs_log_dbg(D_LB, "loadbalancer: reader %s blocked for %d seconds (fail_factor %d), retrying in %d seconds", rdr->label, get_reopen_seconds(s), s->fail_factor, (uint) (reopenseconds - (gone/1000))); + continue; + } + + if(s->rc == E_FOUND) // for debug output + { cs_log_dbg(D_LB, "loadbalancer: reader %s \"e_found\" but not selected for lbvalue check", rdr->label); } + + } +} + +/** + * Gets best reader for caid/prid/srvid/ecmlen. + * Best reader is evaluated by lowest avg time but only if ecm_count > cfg.lb_min_ecmcount (5) + * Also the reader is asked if he is "available" + * returns ridx when found or -1 when not found + */ +void stat_get_best_reader(ECM_REQUEST *er) +{ + if(!cfg.lb_mode || cfg.lb_mode > 3) + { return; } + + if(!er->reader_avail) + { return; } + + struct s_reader *rdr; + struct s_ecm_answer *ea; + + //preferred card forwarding (CCcam client): + if(cccam_forward_origin_card(er)) + { return; } + + STAT_QUERY q; + get_stat_query(er, &q); + + // auto-betatunnel: The trick is: "let the loadbalancer decide"! + if(cfg.lb_auto_betatunnel && caid_is_nagra(er->caid) && er->ecmlen) // nagra + { + uint16_t caid_to = __lb_get_betatunnel_caid_to(er->caid); + if(caid_to) + { + int8_t needs_stats_nagra = 1, needs_stats_beta = 1; + + // Clone query parameters for beta: + STAT_QUERY qbeta = q; + qbeta.caid = caid_to; + qbeta.prid = 0; + qbeta.ecmlen = er->ecm[2] + 3 + 10; + + int32_t time_nagra = 0; + int32_t time_beta = 0; + int32_t weight; + int32_t ntime; + + READER_STAT *stat_nagra = NULL; + READER_STAT *stat_beta = NULL; + + // What is faster? nagra or beta? + int8_t isn; + int8_t isb; + int8_t overall_valid = 0; + int8_t overall_nvalid = 0; + for(ea = er->matching_rdr; ea; ea = ea->next) + { + isn = 0; + isb = 0; + rdr = ea->reader; + weight = rdr->lb_weight; + if(weight <= 0) { weight = 1; } + + // Check if betatunnel is allowed on this reader: + int8_t valid = chk_ctab(caid_to, &rdr->ctab) //Check caid + && chk_rfilter2(caid_to, 0, rdr) //Ident + && chk_srvid_by_caid_prov_rdr(rdr, caid_to, 0) //Services + && (!get_rdr_caid(rdr) || chk_caid_rdr(rdr, caid_to)); //rdr-caid + if(valid) + { + stat_beta = get_stat(rdr, &qbeta); + overall_valid = 1; + } + //else + //stat_beta = NULL; + + // Check if nagra is allowed on this reader: + int8_t nvalid = chk_ctab(er->caid, &rdr->ctab)//Check caid + && chk_rfilter2(er->caid, 0, rdr) //Ident + && chk_srvid_by_caid_prov_rdr(rdr, er->caid, 0) //Services + && (!get_rdr_caid(rdr) || chk_caid_rdr(rdr, er->caid)); //rdr-caid + if(nvalid) + { + stat_nagra = get_stat(rdr, &q); + overall_nvalid = 1; + } + + // calculate nagra data: + if(stat_nagra && stat_nagra->rc == E_FOUND) + { + ntime = stat_nagra->time_avg * 100 / weight; + if(!time_nagra || ntime < time_nagra) + { time_nagra = ntime; } + } + + // calculate beta data: + if(stat_beta && stat_beta->rc == E_FOUND) + { + ntime = stat_beta->time_avg * 100 / weight; + if(!time_beta || ntime < time_beta) + { time_beta = ntime; } + } + + // Uncomplete reader evaluation, we need more stats! + if(stat_nagra) + { + needs_stats_nagra = 0; + isn = 1; + } + if(stat_beta) + { + needs_stats_beta = 0; + isb = 1; + } + cs_log_dbg(D_LB, "loadbalancer-betatunnel valid %d, stat_nagra %d, stat_beta %d, (%04X,%04X)", valid, isn, isb , get_rdr_caid(rdr), caid_to); + } + + if(!overall_valid) // we have no valid betatunnel reader also we don't needs stats (converted) + { needs_stats_beta = 0; } + + if(!overall_nvalid) // we have no valid reader also we don't needs stats (unconverted) + { needs_stats_nagra = 0; } + + if(cfg.lb_auto_betatunnel_prefer_beta && time_beta) + { + time_beta = time_beta * cfg.lb_auto_betatunnel_prefer_beta / 100; + if(time_beta <= 0) + { time_beta = 1; } + } + + if(needs_stats_nagra || needs_stats_beta) + { + cs_log_dbg(D_LB, "loadbalancer-betatunnel %04X:%04X (%d/%d) needs more statistics...", er->caid, caid_to, + needs_stats_nagra, needs_stats_beta); + if(needs_stats_beta) // try beta first + { + + convert_to_beta_int(er, caid_to); + get_stat_query(er, &q); + } + } + else if(time_beta && (!time_nagra || time_beta <= time_nagra)) + { + cs_log_dbg(D_LB, "loadbalancer-betatunnel %04X:%04X selected beta: n%d ms > b%d ms", er->caid, caid_to, time_nagra, time_beta); + convert_to_beta_int(er, caid_to); + get_stat_query(er, &q); + } + else + { + cs_log_dbg(D_LB, "loadbalancer-betatunnel %04X:%04X selected nagra: n%d ms < b%d ms", er->caid, caid_to, time_nagra, time_beta); + } + // else nagra is faster or no beta, so continue unmodified + } + } + else + { + if(cfg.lb_auto_betatunnel && (er->caid == 0x1702 || er->caid == 0x1722) && er->ocaid == 0x0000 && er->ecmlen) // beta + { + uint16_t caid_to = __lb_get_betatunnel_caid_to(er->caid); + if(caid_to) + { + int8_t needs_stats_nagra = 1, needs_stats_beta = 1; + + // Clone query parameters for beta: + STAT_QUERY qnagra = q; + qnagra.caid = caid_to; + qnagra.prid = 0; + qnagra.ecmlen = er->ecm[2] - 7; + + int32_t time_nagra = 0; + int32_t time_beta = 0; + int32_t weight; + int32_t avg_time; + + READER_STAT *stat_nagra = NULL; + READER_STAT *stat_beta = NULL; + //What is faster? nagra or beta? + int8_t isb; + int8_t isn; + int8_t overall_valid = 0; + int8_t overall_bvalid = 0; + for(ea = er->matching_rdr; ea; ea = ea->next) + { + isb = 0; + isn = 0; + rdr = ea->reader; + weight = rdr->lb_weight; + if(weight <= 0) { weight = 1; } + + //Check if reverse betatunnel is allowed on this reader: + int8_t valid = chk_ctab(caid_to, &rdr->ctab)//, rdr->typ) //Check caid + && chk_rfilter2(caid_to, 0, rdr) //Ident + && chk_srvid_by_caid_prov_rdr(rdr, caid_to, 0) //Services + && (!get_rdr_caid(rdr) || chk_caid_rdr(rdr, caid_to)); //rdr-caid + if(valid) + { + stat_nagra = get_stat(rdr, &qnagra); + overall_valid = 1; + } + //else + //stat_nagra = NULL; + + // Check if beta is allowed on this reader: + int8_t bvalid = chk_ctab(er->caid, &rdr->ctab)//, rdr->typ) //Check caid + && chk_rfilter2(er->caid, 0, rdr) //Ident + && chk_srvid_by_caid_prov_rdr(rdr, er->caid, 0) //Services + && (!get_rdr_caid(rdr) || chk_caid_rdr(rdr, er->caid)); //rdr-caid + if(bvalid) + { + stat_beta = get_stat(rdr, &q); + overall_bvalid = 1; + } + + // calculate nagra data: + if(stat_nagra && stat_nagra->rc == E_FOUND) + { + avg_time = stat_nagra->time_avg * 100 / weight; + if(!time_nagra || avg_time < time_nagra) + { time_nagra = avg_time; } + } + + // calculate beta data: + if(stat_beta && stat_beta->rc == E_FOUND) + { + avg_time = stat_beta->time_avg * 100 / weight; + if(!time_beta || avg_time < time_beta) + { time_beta = avg_time; } + } + + // Uncomplete reader evaluation, we need more stats! + if(stat_beta) + { + needs_stats_beta = 0; + isb = 1; + } + if(stat_nagra) + { + needs_stats_nagra = 0; + isn = 1; + } + cs_log_dbg(D_LB, "loadbalancer-betatunnel valid %d, stat_beta %d, stat_nagra %d, (%04X,%04X)", valid, isb, isn , get_rdr_caid(rdr), caid_to); + } + + if(!overall_valid) // we have no valid reverse betatunnel reader also we don't needs stats (converted) + { needs_stats_nagra = 0; } + + if(!overall_bvalid) // we have no valid reader also we don't needs stats (unconverted) + { needs_stats_beta = 0; } + + if(cfg.lb_auto_betatunnel_prefer_beta && time_beta) + { + time_beta = time_beta * cfg.lb_auto_betatunnel_prefer_beta / 100; + if(time_beta < 0) + { time_beta = 0; } + } + + // if we needs stats, we send 2 ecm requests: 18xx and 17xx: + if(needs_stats_nagra || needs_stats_beta) + { + cs_log_dbg(D_LB, "loadbalancer-betatunnel %04X:%04X (%d/%d) needs more statistics...", er->caid, caid_to, + needs_stats_beta, needs_stats_nagra); + if(needs_stats_nagra) // try nagra frist + { + + convert_to_nagra_int(er, caid_to); + get_stat_query(er, &q); + + } + } + else if(time_nagra && (!time_beta || time_nagra <= time_beta)) + { + cs_log_dbg(D_LB, "loadbalancer-betatunnel %04X:%04X selected nagra: b%d ms > n%d ms", er->caid, caid_to, time_beta, time_nagra); + convert_to_nagra_int(er, caid_to); + get_stat_query(er, &q); + } + else + { + cs_log_dbg(D_LB, "loadbalancer-betatunnel %04X:%04X selected beta: b%d ms < n%d ms", er->caid, caid_to, time_beta, time_nagra); + } + + } + } + } + + if(cfg.lb_auto_betatunnel && chk_is_betatunnel_caid(er->caid)) + { + // check again is caid valied to reader + // with both caid on local readers or with proxy + // (both caid will setup to reader for make tunnel caid in share (ccc) visible) + // make sure dosn't send a beta ecm to nagra reader (or reverse) + struct s_ecm_answer *prv = NULL; + for(ea = er->matching_rdr; ea; ea = ea->next) + { + rdr = ea->reader; + if(is_network_reader(rdr) || rdr->typ == R_EMU) // reader caid is not real caid + { + prv = ea; + continue; // proxy can convert or reject + } + cs_log_dbg(D_LB, "check again caid %04X on reader %s", er->caid, rdr->label); + + if(!get_rdr_caid(ea->reader) || chk_caid_rdr(ea->reader, er->caid)) + { + prv = ea; + } + else + { + if(!chk_is_fixed_fallback(rdr, er)) { er->reader_avail--; } + cs_log_dbg(D_LB, "caid %04X not found in caidlist, reader %s removed from request reader list", er->caid, rdr->label); + if(prv) + { + prv->next = ea->next; + } + else + { er->matching_rdr = ea->next; } + } + } + + if(!er->reader_avail) + { return; } + } + + struct timeb check_time; + cs_ftime(&check_time); + int64_t current = -1; + READER_STAT *s = NULL; + int32_t retrylimit = get_retrylimit(er); + int32_t nlocal_readers = 0; + + int32_t nbest_readers = get_nbest_readers(er); // Number of NON fallback readers ecm requests go (minimum 1) + int32_t nfb_readers = get_nfb_readers(er); // Number of fallback readers ecm requests go (minimum 1) + int32_t nreaders = cfg.lb_max_readers; // lb_max_readers is limit lb uses while learning + + + if(!nreaders) // if is configured zero -> replace it by -1 (default means unlimited!) + { nreaders = -1; } + else if(nreaders <= nbest_readers) + { nreaders = nbest_readers + 1; } // nreaders must cover nbest more 1 reader for try to unblock/add stats + + int32_t reader_active = 0; + int32_t max_reopen = nreaders - nbest_readers; // if nreaders=-1, we try to reopen all readers + + +#ifdef WITH_DEBUG + if(cs_dblevel & D_LB) + { + //loadbalancer debug output: + int32_t nr = 0; + char buf[512]; + int n, l = 512; + char *rptr = buf; + *rptr = 0; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + nr++; + if(nr > 5) { continue; } + + if(!(ea->status & READER_FALLBACK)) + { n = snprintf(rptr, l, "%s%s%s ", ea->reader->label, (ea->status & READER_CACHEEX) ? "*" : "", (ea->status & READER_LOCAL) ? "L" : ""); } + else + { n = snprintf(rptr, l, "[%s%s%s] ", ea->reader->label, (ea->status & READER_CACHEEX) ? "*" : "", (ea->status & READER_LOCAL) ? "L" : ""); } + rptr += n; + l -= n; + } + + if(nr > 5) + { snprintf(rptr, l, "...(%d more)", nr - 5); } + + char ecmbuf[ECM_FMT_LEN]; + format_ecm(er, ecmbuf, ECM_FMT_LEN); + + cs_log_dbg(D_LB, "loadbalancer: client %s for %s: n=%d valid readers: %s", + username(er->client), ecmbuf, nr, buf); + } +#endif + + //Deactive all matching readers and set ea->value = 0; + for(ea = er->matching_rdr; ea; ea = ea->next) + { + ea->status &= ~(READER_ACTIVE | READER_FALLBACK); + ea->value = 0; + } + + cs_log_dbg(D_LB, "loadbalancer: --------------------------------------------"); + if(max_reopen < 1) { cs_log_dbg(D_LB, "loadbalancer: mode %d, nbest %d, nfb %d, max_reopen ALL, retrylimit %d ms", cfg.lb_mode, nbest_readers, nfb_readers, retrylimit); } + else { cs_log_dbg(D_LB, "loadbalancer: mode %d, nbest %d, nfb %d, max_reopen %d, retrylimit %d ms", cfg.lb_mode, nbest_readers, nfb_readers, max_reopen, retrylimit); } + + + // Here evaluate lbvalue for readers with valid statistics + for(ea = er->matching_rdr; ea; ea = ea->next) + { + rdr = ea->reader; + s = get_stat(rdr, &q); + + int32_t weight = rdr->lb_weight <= 0 ? 100 : rdr->lb_weight; + //struct s_client *cl = rdr->client; + + if(s && s->rc == E_FOUND + && s->ecm_count >= cfg.lb_min_ecmcount + && (s->ecm_count <= cfg.lb_max_ecmcount || (retrylimit && s->time_avg <= retrylimit))) + { + // Reader can decode this service (rc==0) and has lb_min_ecmcount ecms: + if(er->preferlocalcards && (ea->status & READER_LOCAL)) + { nlocal_readers++; } // Prefer local readers! + + switch(cfg.lb_mode) + { + case LB_FASTEST_READER_FIRST: + current = s->time_avg * 100 / weight; + break; + + case LB_OLDEST_READER_FIRST: + if(!rdr->lb_last.time) + { rdr->lb_last = check_time; } + + //current is negative here! + current = comp_timeb(&rdr->lb_last, &check_time); + current = current * weight / 100; + if(!current) { current = -1; } + + //handle retrylimit + if(retrylimit) + { + if(s->time_avg > retrylimit) // set lowest value for reader with time-avg>retrylimit + { + current = s->time_avg; // in this way, it will choose best time-avg reader among the worst ones + } + else + { + current = current - 1; // so when all have same current, it prioritizes the one with s->time_avg<=retrylimit! This avoid a loop! + } + } + break; + + case LB_LOWEST_USAGELEVEL: + current = rdr->lb_usagelevel * 100 / weight; + + //handle retrylimit + if(retrylimit) + { + if(s->time_avg > retrylimit) + { current = 1000; } //set lowest value for reader with time-avg>retrylimit + else + { current = current - 1; } //so when all reaches retrylimit (all have lb_value=1000) or all have same current, it prioritizes the one with s->time_avg<=retrylimit! This avoid a loop! + } + break; + } + + if(cfg.lb_mode != LB_OLDEST_READER_FIRST) // Adjust selection to reader load: + { + /*if(rdr->ph.c_available && !rdr->ph.c_available(rdr, AVAIL_CHECK_LOADBALANCE, er)) + { + current=current*2; + } + + if(cl && cl->pending) + current=current*cl->pending; + */ + if(current < 1) + { current = 1; } + } + + cs_log_dbg(D_LB, "loadbalancer: reader %s lbvalue = %d (time-avg %d)", rdr->label, (int) llabs(current), s->time_avg); + +#if defined(WEBIF) || defined(LCDSUPPORT) + rdr->lbvalue = llabs(current); +#endif + ea->value = current; + ea->time = s->time_avg; + } + } + + // check for local readers + if(nlocal_readers > nbest_readers) // if we have local readers, we prefer them! + { + nlocal_readers = nbest_readers; + nbest_readers = 0; + } + else + { + nbest_readers = nbest_readers - nlocal_readers; + } + + struct s_reader *best_rdr = NULL; + struct s_reader *best_rdri = NULL; + int32_t best_time = 0; + + // Here choose nbest readers. We evaluate only readers with valid stats (they have ea->value>0, calculated above) + while(1) + { + struct s_ecm_answer *best = NULL; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if(nlocal_readers && !(ea->status & READER_LOCAL)) + { continue; } + + if(ea->value && (!best || ea->value < best->value)) + { best = ea; } + } + + if(!best) + { break; } + + best_rdri = best->reader; + if(!best_rdr) + { + best_rdr = best_rdri; + best_time = best->time; + } + + if(nlocal_readers) // primary readers, local + { + nlocal_readers--; + reader_active++; + best->status |= READER_ACTIVE; + best->value = 0; + cs_log_dbg(D_LB, "loadbalancer: reader %s --> ACTIVE", best_rdri->label); + } + else if(nbest_readers) // primary readers, other + { + nbest_readers--; + reader_active++; + best->status |= READER_ACTIVE; + best->value = 0; + cs_log_dbg(D_LB, "loadbalancer: reader %s --> ACTIVE", best_rdri->label); + } + else + { break; } + } + + /* Here choose nfb_readers + * Select fallbacks reader until nfb_readers reached using this priority: + * 1. forced (lb_force_fallback=1) fixed fallback + * 2. "normal" fixed fallback + * 3. best ea->value remaining reader; + */ + //check for fixed fallbacks + int32_t n_fixed_fb = chk_has_fixed_fallback(er); + if(n_fixed_fb) + { + //check before for lb_force_fallback=1 readers + for(ea = er->matching_rdr; ea && nfb_readers; ea = ea->next) + { + rdr = ea->reader; + if(chk_is_fixed_fallback(rdr, er) && rdr->lb_force_fallback && !(ea->status & READER_ACTIVE)){ + nfb_readers--; + ea->status |= (READER_ACTIVE | READER_FALLBACK); + cs_log_dbg(D_LB, "loadbalancer: reader %s --> FALLBACK (FIXED with force)", rdr->label); + } + } + + //check for "normal" fixed fallback with valid stats + for(ea = er->matching_rdr; ea && nfb_readers; ea = ea->next) + { + rdr = ea->reader; + if(chk_is_fixed_fallback(rdr, er) && !rdr->lb_force_fallback && !(ea->status & READER_ACTIVE)){ + + s = get_stat(rdr, &q); + if(s && s->rc == E_FOUND + && s->ecm_count >= cfg.lb_min_ecmcount + && (s->ecm_count <= cfg.lb_max_ecmcount || (retrylimit && s->time_avg <= retrylimit))) + { + nfb_readers--; + ea->status |= (READER_ACTIVE | READER_FALLBACK); + cs_log_dbg(D_LB, "loadbalancer: reader %s --> FALLBACK (FIXED)", rdr->label); + } + } + } + } + + //check for remaining best ea->value readers as fallbacks + while(nfb_readers) + { + struct s_ecm_answer *best = NULL; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if((ea->status & READER_ACTIVE)) + { continue; } + + if(ea->value && (!best || ea->value < best->value)) + { best = ea; } + } + if(!best) + { break; } + + nfb_readers--; + best->status |= (READER_ACTIVE | READER_FALLBACK); + best->value = 0; + cs_log_dbg(D_LB, "loadbalancer: reader %s --> FALLBACK", best->reader->label); + } + // end fallback readers + + // ACTIVE readers with no stats, or with no lb_min_ecmcount, or lb_max_ecmcount reached --> NO use max_reopen for these readers, always open! + for(ea = er->matching_rdr; ea; ea = ea->next) + { + rdr = ea->reader; + s = get_stat(rdr, &q); + +#ifdef CS_CACHEEX + // if cacheex reader, always active and no stats + if(rdr->cacheex.mode == 1) + { + ea->status |= READER_ACTIVE; + continue; + } +#endif + // ignore fixed fallback with lb_force_fallback=1: no need stats, always used as fallaback! + if(chk_is_fixed_fallback(rdr, er) && rdr->lb_force_fallback) + continue; + + // active readers with no stats + if(!s) + { + cs_log_dbg(D_LB, "loadbalancer: reader %s need starting statistics --> ACTIVE", rdr->label); + ea->status |= READER_ACTIVE; + reader_active++; + continue; + } + + // active readers with no lb_min_ecmcount reached + if(s->rc == E_FOUND && s->ecm_count < cfg.lb_min_ecmcount) + { + cs_log_dbg(D_LB, "loadbalancer: reader %s needs to reach lb_min_ecmcount(%d), now %d --> ACTIVE", rdr->label, cfg.lb_min_ecmcount, s->ecm_count); + ea->status |= READER_ACTIVE; + reader_active++; + continue; + } + + // reset stats and active readers reach cfg.lb_max_ecmcount and time_avg > retrylimit. + if(s->rc == E_FOUND && s->ecm_count > cfg.lb_max_ecmcount && (!retrylimit || s->time_avg > retrylimit)) + { + cs_log_dbg(D_LB, "loadbalancer: reader %s reaches max ecms (%d), resetting statistics --> ACTIVE", rdr->label, cfg.lb_max_ecmcount); + reset_ecmcount_reader(s, rdr); // ecm_count=0 + reset_avgtime_reader(s, rdr); // time_avg=0 + ea->status |= READER_ACTIVE; + reader_active++; + continue; + } + + struct timeb now; + cs_ftime(&now); + int64_t gone = comp_timeb(&now, &s->last_received); + // reset avg-time and active reader with s->last_received older than 5 min and avg-time>retrylimit + if(retrylimit && s->rc == E_FOUND && (gone >= 300*1000) && s->time_avg > retrylimit) + { + cs_log_dbg(D_LB, "loadbalancer: reader %s has time-avg>retrylimit and last received older than 5 minutes, resetting avg-time --> ACTIVE", rdr->label); + reset_avgtime_reader(s, rdr); // time_avg=0 + ea->status &= ~(READER_ACTIVE | READER_FALLBACK); // It could be activated as fallback above because has lb_vlaue>0, so remove fallback state! + ea->status |= READER_ACTIVE; + reader_active++; + continue; + } + } + + int32_t force_reopen = 0; + + //no reader active --> force to reopen matching readers + if(reader_active == 0) + { + cs_log_dbg(D_LB, "loadbalancer: NO VALID MATCHING READER FOUND!"); + force_reopen = 1; + } + else if(reader_active == 1 && cfg.double_check && is_double_check_caid(er, &cfg.double_check_caid)) + { + cs_log_dbg(D_LB, "loadbalancer: ONLY 1 READER MATCHES AND DOUBLECHECK IS ENABLED!"); + force_reopen = 1; + } + else if(retrylimit) + { + /* + * check for lbretrylimit! + * + * if best_time > retrylimit we need to reset avg times of all computed above matching readers, so we can re-evaluated lbvalue! + * More, we force open blocked reader! + */ + int32_t retrylimit_reached = best_time && best_time > retrylimit; + if(retrylimit_reached) + { + cs_log_dbg(D_LB, "loadbalancer: best reader %s (avg_time %d ms) reaches RETRYLIMIT (%d ms), resetting avg times and ACTIVE all (valid and blocked) matching readers!", best_rdr->label, best_time, retrylimit); + for(ea = er->matching_rdr; ea; ea = ea->next) + { + rdr = ea->reader; +#ifdef CS_CACHEEX + if(rdr->cacheex.mode == 1) { continue; } +#endif + s = get_stat(rdr, &q); + + //reset avg time and ACTIVE all valid lbvalue readers + if(s && s->rc == E_FOUND + && s->ecm_count >= cfg.lb_min_ecmcount + && (s->ecm_count <= cfg.lb_max_ecmcount || s->time_avg <= retrylimit)) + { + if((ea->status & READER_FALLBACK)) { cs_log_dbg(D_LB, "loadbalancer: reader %s selected as FALLBACK --> ACTIVE", rdr->label); } + else if(!(ea->status & READER_ACTIVE)) { cs_log_dbg(D_LB, "loadbalancer: reader %s --> ACTIVE", rdr->label); } + ea->status &= ~(READER_ACTIVE | READER_FALLBACK); //remove active and fallback + ea->status |= READER_ACTIVE; //add active + reset_avgtime_reader(s, rdr); + } + + //reset avg time all blocked "valid" readers. We active them by force_reopen=1 + if(s && s->rc != E_FOUND) + { + reset_avgtime_reader(s, rdr); + } + + } + force_reopen = 1; //force reopen blocked readers + } + } + + //try to reopen max_reopen blocked readers (readers with last ecm not "e_found"); if force_reopen=1, force reopen valid blocked readers! + try_open_blocked_readers(er, &q, &max_reopen, force_reopen); + + cs_log_dbg(D_LB, "loadbalancer: --------------------------------------------"); + +#ifdef WITH_DEBUG + if(cs_dblevel & D_LB) + { + //loadbalancer debug output: + int32_t nr = 0; + char buf[512]; + int32_t l = 512; + char *rptr = buf; + *rptr = 0; + int32_t n = 0; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if(!(ea->status & READER_ACTIVE)) + { continue; } + + nr++; + if(nr > 5) { continue; } + + if(!(ea->status & READER_FALLBACK)) + { n = snprintf(rptr, l, "%s%s%s ", ea->reader->label, (ea->status & READER_CACHEEX) ? "*" : "", (ea->status & READER_LOCAL) ? "L" : ""); } + else + { n = snprintf(rptr, l, "[%s%s%s] ", ea->reader->label, (ea->status & READER_CACHEEX) ? "*" : "", (ea->status & READER_LOCAL) ? "L" : ""); } + rptr += n; + l -= n; + } + + if(nr > 5) + { snprintf(rptr, l, "...(%d more)", nr - 5); } + + if(cs_dblevel & D_LB) + { + char ecmbuf[ECM_FMT_LEN]; + format_ecm(er, ecmbuf, ECM_FMT_LEN); + + cs_log_dbg(D_LB, "loadbalancer: client %s for %s: n=%d selected readers: %s", + username(er->client), ecmbuf, nr, buf); + } + } +#endif + return; +} + +/** + * clears statistic of reader ridx. + **/ +void clear_reader_stat(struct s_reader *rdr) +{ + if(!rdr->lb_stat) + { return; } + + ll_clear_data(rdr->lb_stat); +} + +void clear_all_stat(void) +{ + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + clear_reader_stat(rdr); + } +} + +static void housekeeping_stat_thread(void) +{ + struct timeb now; + cs_ftime(&now); + int32_t cleanup_timeout = cfg.lb_stat_cleanup * 60 * 60 * 1000; + int32_t cleaned = 0; + struct s_reader *rdr; + set_thread_name(__func__); + LL_ITER itr = ll_iter_create(configured_readers); + cs_readlock(__func__, &readerlist_lock); // this avoids cleaning a reading during writing + while((rdr = ll_iter_next(&itr))) + { + if(rdr->lb_stat) + { + rdr->lb_stat_busy = 1; + cs_writelock(__func__, &rdr->lb_stat_lock); + LL_ITER it = ll_iter_create(rdr->lb_stat); + READER_STAT *s; + + while((s = ll_iter_next(&it))) + { + int64_t gone = comp_timeb(&now, &s->last_received); + if(gone > cleanup_timeout) + { + ll_iter_remove_data(&it); + cleaned++; + } + } + cs_writeunlock(__func__, &rdr->lb_stat_lock); + rdr->lb_stat_busy = 0; + } + } + cs_readunlock(__func__, &readerlist_lock); + cs_log_dbg(D_LB, "loadbalancer cleanup: removed %d entries", cleaned); +} + +static void housekeeping_stat(int32_t force) +{ + struct timeb now; + cs_ftime(&now); + int64_t gone = comp_timeb(&now, &last_housekeeping); + if(!force && (gone < 60 * 60 * 1000)) // only clean once in an hour + { return; } + + last_housekeeping = now; + start_thread("housekeeping lb stats", (void *)&housekeeping_stat_thread, NULL, NULL, 1, 1); +} + +static int compare_stat(READER_STAT **ps1, READER_STAT **ps2) +{ + READER_STAT *s1 = (*ps1), *s2 = (*ps2); + int64_t res = s1->rc - s2->rc; + if(res) { return res; } + res = s1->caid - s2->caid; + if(res) { return res; } + res = s1->prid - s2->prid; + if(res) { return res; } + res = s1->srvid - s2->srvid; + if(res) { return res; } + res = s1->chid - s2->chid; + if(res) { return res; } + res = s1->ecmlen - s2->ecmlen; + if(res) { return res; } + res = comp_timeb(&s1->last_received, &s2->last_received); + return res; +} + +static int compare_stat_r(READER_STAT **ps1, READER_STAT **ps2) +{ + return -compare_stat(ps1, ps2); +} + +READER_STAT **get_sorted_stat_copy(struct s_reader *rdr, int32_t reverse, int32_t *size) +{ + if(reverse) + { return (READER_STAT **)ll_sort(rdr->lb_stat, compare_stat_r, size); } + else + { return (READER_STAT **)ll_sort(rdr->lb_stat, compare_stat, size); } +} + +static int8_t stat_in_ecmlen(struct s_reader *rdr, READER_STAT *s) +{ + int32_t i; + for (i = 0; i < rdr->ecm_whitelist.ewnum; i++) + { + ECM_WHITELIST_DATA *d = &rdr->ecm_whitelist.ewdata[i]; + if ((d->caid == 0 || d->caid == s->caid) && (d->ident == 0 || d->ident == s->prid) && (d->len == s->ecmlen)) + return 1; + } + return 0; +} + +static int8_t add_to_ecmlen(struct s_reader *rdr, READER_STAT *s) +{ + int32_t i; + for (i = 0; i < rdr->ecm_whitelist.ewnum; i++) + { + ECM_WHITELIST_DATA *d = &rdr->ecm_whitelist.ewdata[i]; + if ((d->caid == s->caid) && (d->ident == s->prid) && (d->len == s->ecmlen)) + return 1; + } + ECM_WHITELIST_DATA d = { .caid = s->caid, .ident = s->prid, .len = s->ecmlen }; + ecm_whitelist_add(&rdr->ecm_whitelist, &d); + return 0; +} + +void update_ecmlen_from_stat(struct s_reader *rdr) +{ + if(!rdr || !rdr->lb_stat) + { return; } + + cs_readlock(__func__, &rdr->lb_stat_lock); + LL_ITER it = ll_iter_create(rdr->lb_stat); + READER_STAT *s; + while((s = ll_iter_next(&it))) + { + if(s->rc == E_FOUND) + { + if(!stat_in_ecmlen(rdr, s)) + { add_to_ecmlen(rdr, s); } + } + } + cs_readunlock(__func__, &rdr->lb_stat_lock); +} + +/** + * mark as last reader after checked for cache requests: + **/ +void lb_mark_last_reader(ECM_REQUEST *er) +{ + //OLDEST_READER: set lb_last + struct s_ecm_answer *ea; + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if((ea->status & (READER_ACTIVE | READER_FALLBACK)) == READER_ACTIVE) + { cs_ftime(&ea->reader->lb_last); } + } +} + +/** + * Automatic timeout feature depending on statistik values + **/ +static uint32_t __lb_auto_timeout(ECM_REQUEST *er, uint32_t ctimeout) +{ + STAT_QUERY q; + READER_STAT *s = NULL; + + struct s_reader *rdr = NULL; + struct s_ecm_answer *ea; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if((ea->status & (READER_ACTIVE | READER_FALLBACK)) == READER_ACTIVE) + { + rdr = ea->reader; + get_stat_query(er, &q); + s = get_stat(rdr, &q); + if(s) { break; } + } + } + + if(!s) { return ctimeout; } + + uint32_t t; + if(s->rc == E_TIMEOUT) + { t = ctimeout / 2; } //timeout known, early timeout! + else + { + if(s->ecm_count < cfg.lb_min_ecmcount) { return ctimeout; } + + t = s->time_avg * (100 + cfg.lb_auto_timeout_p) / 100; + if((int32_t)(t - s->time_avg) < cfg.lb_auto_timeout_t) { t = s->time_avg + cfg.lb_auto_timeout_t; } + } + + if(t > ctimeout) { t = ctimeout; } + +#ifdef WITH_DEBUG + if(D_TRACE & cs_dblevel) + { + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_log_dbg(D_TRACE, "auto-timeout for %s %s set rdr %s to %d", username(er->client), buf, rdr->label, t); + } +#endif + return t; +} + +uint32_t lb_auto_timeout(ECM_REQUEST *er, uint32_t timeout) +{ + if(cfg.lb_auto_timeout) + return __lb_auto_timeout(er, timeout); + return timeout; +} + +bool lb_check_auto_betatunnel(ECM_REQUEST *er, struct s_reader *rdr) +{ + if(!cfg.lb_auto_betatunnel) + return 0; + + bool match = 0; + uint16_t caid = __lb_get_betatunnel_caid_to(er->caid); + if(caid) + { + uint16_t save_caid = er->caid; + er->caid = caid; + match = matching_reader(er, rdr); // matching + er->caid = save_caid; + } + return match; +} + +/** + * search for same ecm hash with same readers + **/ +static struct ecm_request_t *check_same_ecm(ECM_REQUEST *er) +{ + struct ecm_request_t *ecm; + time_t timeout; + struct s_ecm_answer *ea_ecm = NULL, *ea_er = NULL; + uint8_t rdrs = 0; + + cs_readlock(__func__, &ecmcache_lock); + for(ecm = ecmcwcache; ecm; ecm = ecm->next) + { + timeout = time(NULL) - ((cfg.ctimeout + 500) / 1000); + + if(ecm->tps.time <= timeout) + { break; } + + if(ecm == er) { continue; } + + if(er->caid != ecm->caid || memcmp(ecm->ecmd5, er->ecmd5, CS_ECMSTORESIZE)) + { continue; } + + if(!er->readers || !ecm->readers || er->readers != ecm->readers) + { continue; } + + ea_ecm = ecm->matching_rdr; + ea_er = er->matching_rdr; + rdrs = er->readers; + + while(rdrs && ea_ecm && ea_er) + { + if(ea_ecm->reader != ea_er->reader) + { break; } + ea_ecm = ea_ecm->next; + ea_er = ea_er->next; + rdrs--; + } + + if(!rdrs) + { + cs_readunlock(__func__, &ecmcache_lock); + return ecm; + } + } + cs_readunlock(__func__, &ecmcache_lock); + return NULL; // nothing found so return null +} + +static void use_same_readers(ECM_REQUEST *er_new, ECM_REQUEST *er_cache) +{ + struct s_ecm_answer *ea_new = er_new->matching_rdr; + struct s_ecm_answer *ea_cache = er_cache->matching_rdr; + uint8_t rdrs = er_new->readers; + while(rdrs) + { + ea_new->status &= ~(READER_ACTIVE | READER_FALLBACK); + if((ea_cache->status & READER_ACTIVE)) + { + if(!(ea_cache->status & READER_FALLBACK)) + { + ea_new->status |= READER_ACTIVE; + } + else + { + ea_new->status |= (READER_ACTIVE | READER_FALLBACK); + } + } + + ea_new = ea_new->next; + ea_cache = ea_cache->next; + rdrs--; + } +} + +void lb_set_best_reader(ECM_REQUEST *er) +{ + if(!cfg.lb_mode) + return; + + // cache2 is handled by readers queue, so, if a same ecm hash with same readers, use these same readers to get cache2 from them! Not ask other readers! + struct ecm_request_t *ecm_eq = NULL; + ecm_eq = check_same_ecm(er); + if(ecm_eq) + { + // set all readers used by ecm_eq, so we get cache2 from them! + use_same_readers(er, ecm_eq); + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] found same ecm with same readers from client %s, use them!", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, (check_client(ecm_eq->client) ? ecm_eq->client->account->usr : "-")); + } + else + { + // FILTER readers by loadbalancing + stat_get_best_reader(er); + } +} + +void lb_update_last(struct s_ecm_answer *ea_er, struct s_reader *reader) +{ + // for lb oldest reader mode - not use for fallback readers + if (!(ea_er->status & READER_FALLBACK)) + cs_ftime(&reader->lb_last); +} + +void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc, int32_t ecm_time) +{ + if(rc >= E_99 || cacheex_reader(rdr)) + { return; } + + if(!ecm_time) + ecm_time = cfg.ctimeout; + + add_stat(rdr, er, ecm_time, rc, ea->rcEx); +} + +void stat_finish(void) +{ + if(cfg.lb_mode && cfg.lb_save) + { + save_stat_to_file(0); + if(cfg.lb_savepath) + { cs_log("stats saved to file %s", cfg.lb_savepath); } + cfg.lb_save = 0; //this is for avoiding duplicate saves + } +} + +#endif diff --git a/module-stat.h b/module-stat.h new file mode 100644 index 0000000..9fc64ae --- /dev/null +++ b/module-stat.h @@ -0,0 +1,46 @@ +#ifndef MODULE_STAT_H_ +#define MODULE_STAT_H_ + +void save_stat_to_file(int32_t thread); +int32_t clean_stat_by_rc(struct s_reader *rdr, int8_t rc, int8_t inverse); +int32_t clean_all_stats_by_rc(int8_t rc, int8_t inverse); +int32_t clean_stat_by_id(struct s_reader *rdr, uint16_t caid, uint32_t prid, uint16_t srvid, uint16_t chid, uint16_t ecmlen); +void clear_reader_stat(struct s_reader *rdr); +void clear_all_stat(void); +READER_STAT **get_sorted_stat_copy(struct s_reader *rdr, int32_t reverse, int32_t *size); +void update_ecmlen_from_stat(struct s_reader *rdr); + +#ifdef WITH_LB +void init_stat(void); +void stat_finish(void); +void load_stat_from_file(void); +void lb_destroy_stats(struct s_reader *rdr); +void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc, int32_t ecm_time); +void stat_get_best_reader(ECM_REQUEST *er); +void lb_mark_last_reader(ECM_REQUEST *er); +void check_lb_auto_betatunnel_mode(ECM_REQUEST *er); +uint32_t lb_auto_timeout(ECM_REQUEST *er, uint32_t ctimeout); +bool lb_check_auto_betatunnel(ECM_REQUEST *er, struct s_reader *rdr); +void lb_set_best_reader(ECM_REQUEST *er); +void lb_update_last(struct s_ecm_answer *ea_er, struct s_reader *reader); +uint16_t lb_get_betatunnel_caid_to(ECM_REQUEST *er); +void readerinfofix_get_stat_query(ECM_REQUEST *er, STAT_QUERY *q); +void readerinfofix_inc_fail(READER_STAT *s); +READER_STAT *readerinfofix_get_add_stat(struct s_reader *rdr, STAT_QUERY *q); +#else +static inline void init_stat(void) { } +static inline void stat_finish(void) { } +static inline void load_stat_from_file(void) { } +static inline void lb_destroy_stats(struct s_reader *UNUSED(rdr)) { } +static inline void send_reader_stat(struct s_reader *UNUSED(rdr), ECM_REQUEST *UNUSED(er), struct s_ecm_answer *UNUSED(ea), int8_t UNUSED(rc), int32_t UNUSED(ecm_time)) { } +static inline void stat_get_best_reader(ECM_REQUEST *UNUSED(er)) { } +static inline void lb_mark_last_reader(ECM_REQUEST *UNUSED(er)) { } +static inline void check_lb_auto_betatunnel_mode(ECM_REQUEST *UNUSED(er)) { } +static inline uint32_t lb_auto_timeout(ECM_REQUEST *UNUSED(er), uint32_t ctimeout) { return ctimeout; } +static inline bool lb_check_auto_betatunnel(ECM_REQUEST *UNUSED(er), struct s_reader *UNUSED(rdr)) { return 0; } +static inline void lb_set_best_reader(ECM_REQUEST *UNUSED(er)) { } +static inline void lb_update_last(struct s_ecm_answer *UNUSED(ea_er), struct s_reader *UNUSED(reader)) { } +static inline uint16_t lb_get_betatunnel_caid_to(ECM_REQUEST *UNUSED(er)) { return 0; } +#endif + +#endif diff --git a/module-streamrelay.c b/module-streamrelay.c new file mode 100644 index 0000000..17fb28f --- /dev/null +++ b/module-streamrelay.c @@ -0,0 +1,2230 @@ +#define MODULE_LOG_PREFIX "relay" + +#include "globals.h" + +#ifdef MODULE_STREAMRELAY + +#if !STATIC_LIBDVBCSA +#include +#endif +#include "module-streamrelay.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-config.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-time.h" + +#ifdef WITH_EMU +#include "cscrypt/des.h" +#include "module-emulator-osemu.h" +#include "module-emulator-powervu.h" +#endif + +#define STREAM_UNDEFINED 0x00 +#define STREAM_VIDEO 0x01 +#define STREAM_AUDIO 0x02 +#define STREAM_SUBTITLE 0x03 +#define STREAM_TELETEXT 0x04 + +extern int32_t exit_oscam; + +typedef struct +{ + int32_t connfd; + int32_t connid; + char stream_host[256]; +} stream_client_conn_data; + +char *stream_source_auth = NULL; +uint32_t cluster_size = 50; +bool has_dvbcsa_ecm = 0, is_dvbcsa_static = 1; +#if DVBCSA_KEY_ECM +static uint8_t (*dvbcsa_get_ecm_table_fn)(void) = NULL; +#endif + +static uint8_t stream_server_mutex_init = 0; +static pthread_mutex_t stream_server_mutex; +static int32_t glistenfd, mod_idx, gconncount = 0, gconnfd[STREAM_SERVER_MAX_CONNECTIONS], stream_resptime[STREAM_SERVER_MAX_CONNECTIONS]; +static char ecm_src[STREAM_SERVER_MAX_CONNECTIONS][9]; +static IN_ADDR_T client_ip[STREAM_SERVER_MAX_CONNECTIONS], stream_host_ip[STREAM_SERVER_MAX_CONNECTIONS]; +#ifdef WITH_EMU +#define STATIC /* none */ +#else +#define STATIC static +#endif +static in_port_t client_port[STREAM_SERVER_MAX_CONNECTIONS]; +struct s_client *streamrelay_client[STREAM_SERVER_MAX_CONNECTIONS]; + +STATIC pthread_mutex_t fixed_key_srvid_mutex; +STATIC uint16_t stream_cur_srvid[STREAM_SERVER_MAX_CONNECTIONS]; +STATIC stream_client_key_data key_data[STREAM_SERVER_MAX_CONNECTIONS]; + +#ifdef WITH_EMU +int8_t stream_server_thread_init = 0; +int8_t emu_stream_emm_enabled = 0; +uint8_t emu_stream_server_mutex_init = 0; +int8_t stream_server_has_ecm[EMU_STREAM_SERVER_MAX_CONNECTIONS]; +pthread_mutex_t emu_fixed_key_data_mutex[EMU_STREAM_SERVER_MAX_CONNECTIONS]; +emu_stream_client_key_data emu_fixed_key_data[EMU_STREAM_SERVER_MAX_CONNECTIONS]; +LLIST *ll_emu_stream_delayed_keys[EMU_STREAM_SERVER_MAX_CONNECTIONS]; +#endif + +#if DVBCSA_KEY_ECM +static uint16_t last_srvid[STREAM_SERVER_MAX_CONNECTIONS]; +#endif + +#ifdef MODULE_RADEGAST +static int32_t gRadegastFd = 0; + +static bool connect_to_radegast(void) +{ + struct SOCKADDR cservaddr; + + if (gRadegastFd == 0) + { gRadegastFd = socket(DEFAULT_AF, SOCK_STREAM, 0); } + + if (gRadegastFd < 0) + { + gRadegastFd = 0; + return false; + } + + int32_t flags = fcntl(gRadegastFd, F_GETFL); + fcntl(gRadegastFd, F_SETFL, flags | O_NONBLOCK); + + bzero(&cservaddr, sizeof(cservaddr)); + SIN_GET_FAMILY(cservaddr) = DEFAULT_AF; + SIN_GET_PORT(cservaddr) = htons(cfg.rad_port); + SIN_GET_ADDR(cservaddr) = cfg.rad_srvip; + + if (connect(gRadegastFd, (struct sockaddr *)&cservaddr, sizeof(cservaddr)) == -1) + { return false; } + + return true; +} + +static void close_radegast_connection(void) +{ + close(gRadegastFd); + gRadegastFd = 0; +} + +static bool send_to_radegast(uint8_t* data, int len) +{ + if (send(gRadegastFd, data, len, 0) < 0) + { + cs_log("send_to_radegast: Send failure! (errno=%d %s)", errno, strerror(errno)); + return false; + } + return true; +} + +static void radegast_client_ecm(stream_client_data *cdata) +{ + uint16_t section_length = SCT_LEN(cdata->ecm_data); + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + MD5(cdata->ecm_data, section_length, md5tmp); + + if (!memcmp(cdata->ecm_md5, md5tmp, MD5_DIGEST_LENGTH)) + { return; } + memcpy(cdata->ecm_md5, md5tmp, MD5_DIGEST_LENGTH); + + uint16_t packet_len; + static uint8_t header_len = 2; + static uint8_t payload_static_len = 12; + + if (gRadegastFd <= 0) + { + cs_log("HTTP stream including ECM header. Connecting radegast server to parse ECM data"); + connect_to_radegast(); + } + + packet_len = header_len + payload_static_len + section_length; + uint8_t outgoing_data[packet_len]; + outgoing_data[0] = 1; + outgoing_data[1] = payload_static_len + section_length; + outgoing_data[2] = 10; // caid + outgoing_data[3] = 2; + outgoing_data[4] = cdata->caid >> 8; + outgoing_data[5] = cdata->caid & 0xFF; + outgoing_data[6] = 9; // srvid + outgoing_data[7] = 4; + outgoing_data[8] = cdata->srvid & 0xFF; + outgoing_data[10] = cdata->srvid >> 8; + outgoing_data[12] = 3; + outgoing_data[13] = section_length; + + memcpy(outgoing_data + header_len + payload_static_len, cdata->ecm_data, section_length); + + if (!send_to_radegast(outgoing_data, packet_len)) + { + close_radegast_connection(); + if (connect_to_radegast()) + { send_to_radegast(outgoing_data, packet_len); } + } +} +#endif // MODULE_RADEGAST + +#ifdef WITH_EMU +void ParseEcmData(stream_client_data *cdata) +{ + uint8_t *data = cdata->ecm_data; + uint8_t dcw[16]; + uint16_t section_length = SCT_LEN(data); + + if (section_length < 11) + { return; } + + if (caid_is_powervu(cdata->caid)) + { + if (data[11] > cdata->ecm_nb || (cdata->ecm_nb == 255 && data[11] == 0) || ((cdata->ecm_nb - data[11]) > 5)) + { + cdata->ecm_nb = data[11]; + cdata->key.connid = cdata->connid; + powervu_ecm(data, dcw, NULL, cdata->srvid, cdata->caid, cdata->tsid, cdata->onid, cdata->ens, &cdata->key); + } + } +#ifdef MODULE_RADEGAST + else + { + cs_strncpy(ecm_src[cdata->connid], "radegast", sizeof(ecm_src[cdata->connid])); + radegast_client_ecm(cdata); + } +#endif +} +#else +#define ParseEcmData radegast_client_ecm +#endif + +#if DVBCSA_KEY_ECM +#define DVBCSA_HEADER_ECM 1 +#define dvbcsa_bs_key_set(a,b) dvbcsa_bs_key_set_ecm(ecm,a,b) +#else +#define DVBCSA_HEADER_ECM 0 +#endif +#ifndef STATIC_LIBDVBCSA +#define STATIC_LIBDVBCSA 0 +#endif + +static void write_cw(ECM_REQUEST *er, int32_t connid) +{ +#if DVBCSA_KEY_ECM + const uint8_t ecm = (cfg.stream_relay_ctab.ctnum == 0 && !select_csa_alt(er)) + ? 0 + : get_ecm_mode(er); +#endif + if (memcmp(er->cw, "\x00\x00\x00\x00\x00\x00\x00\x00", 8) != 0) + { + if (has_dvbcsa_ecm) + { +#ifdef WITH_EMU + dvbcsa_bs_key_set(er->cw, key_data[connid].key[0][EVEN]); +#else + dvbcsa_bs_key_set(er->cw, key_data[connid].key[EVEN]); +#endif + } + } + + if (memcmp(er->cw + 8, "\x00\x00\x00\x00\x00\x00\x00\x00", 8) != 0) + { + if (has_dvbcsa_ecm) + { +#ifdef WITH_EMU + dvbcsa_bs_key_set(er->cw + 8, key_data[connid].key[0][ODD]); +#else + dvbcsa_bs_key_set(er->cw + 8, key_data[connid].key[ODD]); +#endif + } + } +#if DVBCSA_KEY_ECM + if (er->srvid != last_srvid[connid] && ecm != 0 && has_dvbcsa_ecm) + { + cs_log(dvbcsa_get_ecm_table_fn + ? "Using ecm_mode=0x%02X, libdvbcsa table 0x%02X" + : "Using ecm_mode=0x%02X", + ecm, + dvbcsa_get_ecm_table_fn ? dvbcsa_get_ecm_table_fn() : 0); + last_srvid[connid] = er->srvid; + } +#endif +#ifdef WITH_EMU + SAFE_MUTEX_LOCK(&emu_fixed_key_data_mutex[connid]); + emu_fixed_key_data[connid].csa_used = 1; + SAFE_MUTEX_UNLOCK(&emu_fixed_key_data_mutex[connid]); +#endif +} + +static void update_client_info(ECM_REQUEST *er, int32_t connid) +{ + time_t now; + time(&now); + if (cfg.stream_display_client) + { + streamrelay_client[connid]->ip = stream_host_ip[connid]; + streamrelay_client[connid]->port = cfg.stream_source_port; + } + else + { + streamrelay_client[connid]->ip = client_ip[connid]; + streamrelay_client[connid]->port = client_port[connid]; + } + streamrelay_client[connid]->last_srvid = er->srvid; + streamrelay_client[connid]->last_provid = er->prid; + streamrelay_client[connid]->last_caid = er->caid; +#ifdef WEBIF + snprintf(streamrelay_client[connid]->lastreader, sizeof(streamrelay_client[connid]->lastreader), "⇆ %.*s", 11, ecm_src[connid]); +#endif + streamrelay_client[connid]->cwlastresptime = stream_resptime[connid]; + streamrelay_client[connid]->lastecm = now; + streamrelay_client[connid]->lastswitch = streamrelay_client[connid]->last = time((time_t *)0); // reset idle-Time & last switch +} + +bool stream_write_cw(ECM_REQUEST *er) +{ + int32_t i; + bool cw_written = false; + //SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + if (stream_cur_srvid[i] == er->srvid) + { + write_cw(er, i); + update_client_info(er, i); + cw_written = true; + // don't return as there might be more connections for the same channel (e.g. recordings) + } + } + //SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); + return cw_written; +} + +static void SearchTsPackets(const uint8_t *buf, const uint32_t bufLength, uint16_t *packetSize, uint16_t *startOffset) +{ + uint32_t i; + + for (i = 0; i < bufLength; i++) + { + if (buf[i] == 0x47) + { + // if three packets align, probably safe to assume correct size + if ((buf[i + 188] == 0x47) & (buf[i + 376] == 0x47)) + { + (*packetSize) = 188; + (*startOffset) = i; + return; + } + else if ((buf[i + 204] == 0x47) & (buf[i + 408] == 0x47)) + { + (*packetSize) = 204; + (*startOffset) = i; + return; + } + else if ((buf[i + 208] == 0x47) & (buf[i + 416] == 0x47)) + { + (*packetSize) = 208; + (*startOffset) = i; + return; + } + } + } + + (*packetSize) = 0; + (*startOffset) = 0; +} + +typedef void (*ts_data_callback)(stream_client_data *cdata); + +static void ParseTsData(const uint8_t table_id, const uint8_t table_mask, const uint8_t min_table_length, int8_t *flag, + uint8_t *data, const uint16_t data_length, uint16_t *data_pos, const int8_t payloadStart, + const uint8_t *buf, const int32_t len, ts_data_callback func, stream_client_data *cdata) +{ + int32_t i; + uint16_t offset = 0; + bool found_start = 0; + + if (len < 1) + { return; } + + if (*flag == 0 && !payloadStart) + { return; } + + if (*flag == 0) + { + *data_pos = 0; + offset = 1 + buf[0]; + } + else if (payloadStart) + { offset = 1; } + + if ((len - offset) < 1) + { return; } + + const int32_t free_data_length = (data_length - *data_pos); + const int32_t copySize = (len - offset) > free_data_length ? free_data_length : (len - offset); + + memcpy(data + *data_pos, buf + offset, copySize); + *data_pos += copySize; + + for (i = 0; i < *data_pos; i++) + { + if ((data[i] & table_mask) == table_id) + { + if (i != 0) + { + if (*data_pos - i > i) + { memmove(data, &data[i], *data_pos - i); } + else + { memcpy(data, &data[i], *data_pos - i); } + + *data_pos -= i; + } + found_start = 1; + break; + } + } + + const uint16_t section_length = SCT_LEN(data); + + if (!found_start || (section_length > data_length) || (section_length < min_table_length)) + { + *flag = 0; + return; + } + + if ((*data_pos < section_length) || (*data_pos < 3)) + { + *flag = 2; + return; + } + + func(cdata); + + found_start = 0; + for (i = section_length; i < *data_pos; i++) + { + if ((data[i] & table_mask) == table_id) + { + if (*data_pos - i > i) + { memmove(data, &data[i], *data_pos - i); } + else + { memcpy(data, &data[i], *data_pos - i); } + + *data_pos -= i; + found_start = 1; + break; + } + } + + if (!found_start || (data_length < *data_pos + copySize + 1)) + { *data_pos = 0; } + + *flag = 1; +} + +static void ParsePatData(stream_client_data *cdata) +{ + int32_t i; + uint16_t srvid; +#ifdef __BISS__ + cdata->STREAMpidcount = 0; +#endif + for (i = 8; i + 7 < SCT_LEN(cdata->pat_data); i += 4) + { + srvid = b2i(2, cdata->pat_data + i); + if (srvid == 0) + { continue; } + + if (cdata->srvid == srvid) + { + cdata->pmt_pid = b2i(2, cdata->pat_data + i + 2) & 0x1FFF; + cs_log_dbg(D_READER, "Stream client %i found pmt pid: 0x%04X (%i)", + cdata->connid, cdata->pmt_pid, cdata->pmt_pid); + break; + } + } +} + +#ifdef WITH_EMU +static int8_t stream_client_get_caid(stream_client_data *cdata) +{ + uint32_t tmp1 = (cdata->srvid << 16) | cdata->pmt_pid; + uint8_t tmp2[2]; + + if (emu_find_key('A', tmp1, 0, "FAKE", tmp2, 2, 0, 0, 0, NULL)) + { + cdata->caid = b2i(2, tmp2); + return 1; + } + return 0; +} +#endif + +static void ParseDescriptors(const uint8_t *buffer, const uint16_t info_length, uint8_t *type) +{ + uint32_t i; + uint8_t j, descriptor_length = 0; + + if (info_length < 1) + { return; } + + for (i = 0; i + 1 < info_length; i += descriptor_length + 2) + { + descriptor_length = buffer[i + 1]; + switch (buffer[i]) // descriptor tag + { + case 0x05: // Registration descriptor + { + // "HDMV" format identifier is removed + // Cam does not need to know about Blu-ray + const char format_identifiers_audio[10][5] = + { + "AC-3", "BSSD", "dmat", "DRA1", "DTS1", + "DTS2", "DTS3", "EAC3", "mlpa", "Opus", + }; + for (j = 0; j < 10; j++) + { + if (memcmp(buffer + i + 2, format_identifiers_audio[j], 4) == 0) + { + *type = STREAM_AUDIO; + break; + } + } + break; + } + //case 0x09: // CA descriptor + //{ + // break; + //} + case 0x46: // VBI teletext descriptor (DVB) + case 0x56: // teletext descriptor (DVB) + { + *type = STREAM_TELETEXT; + break; + } + case 0x59: // subtitling descriptor (DVB) + { + *type = STREAM_SUBTITLE; + break; + } + case 0x6A: // AC-3 descriptor (DVB) + case 0x7A: // enhanced AC-3 descriptor (DVB) + case 0x7B: // DTS descriptor (DVB) + case 0x7C: // AAC descriptor (DVB) + case 0x81: // AC-3 descriptor (ATSC) + case 0xCC: // Enhanced AC-3 descriptor (ATSC) + { + *type = STREAM_AUDIO; + break; + } + case 0x7F: // extension descriptor (DVB) + { + switch (buffer[i + 2]) // extension descriptor tag + { + case 0x0E: // DTS-HD descriptor (DVB) + case 0x0F: // DTS Neural descriptor (DVB) + case 0x15: // AC-4 descriptor (DVB) + *type = STREAM_AUDIO; + break; + + case 0x20: // TTML subtitling descriptor (DVB) + *type = STREAM_SUBTITLE; + break; + + default: + *type = STREAM_UNDEFINED; + break; + } + break; + } + default: + break; + } + } +} + +static void stream_parse_pmt_ca_descriptor(const uint8_t *data, const int32_t data_pos, const int32_t offset, const uint16_t info_length, stream_client_data *cdata) +{ + if (cdata->ecm_pid) + { return; } + + // parse program descriptors (we are looking only for CA descriptor here) + int32_t i; + uint16_t caid; + uint8_t descriptor_tag, descriptor_length = 0; + + for (i = offset; i + 1 < offset + info_length; i += descriptor_length + 2) + { + descriptor_tag = data[i + data_pos]; + descriptor_length = data[i + 1 + data_pos]; + if (descriptor_length < 1) + { break; } + + if (i + 1 + descriptor_length >= offset + info_length) + { break; } + + if (descriptor_tag == 0x09 && descriptor_length >= 4) + { + caid = b2i(2, data + i + 2 + data_pos); + if (cfg.stream_relay_ctab.ctnum == 0 || chk_ctab_ex(caid, &cfg.stream_relay_ctab)) + { + if (cdata->caid == NO_CAID_VALUE) + { cdata->caid = caid; } + + if (cdata->caid != caid) + { continue; } + cdata->ecm_pid = b2i(2, data + i + 4 + data_pos) & 0x1FFF; + cs_log_dbg(D_READER, "Stream client %i found ecm pid: 0x%04X (%i)", + cdata->connid, cdata->ecm_pid, cdata->ecm_pid); + } + else if (cdata->blocked_caid == NO_CAID_VALUE) + { + // Remember first blocked CAID for error message + cdata->blocked_caid = caid; + } + } + } +} + +static void ParsePmtData(stream_client_data *cdata) +{ + int32_t i; + uint16_t program_info_length = 0, es_info_length = 0, elementary_pid; + const uint16_t section_length = SCT_LEN(cdata->pmt_data); + uint8_t offset = 0; + + cdata->ecm_pid = 0; + cdata->pcr_pid = b2i(2, cdata->pmt_data + 8) & 0x1FFF; + + if (cdata->pcr_pid != 0x1FFF) + { cs_log_dbg(D_READER, "Stream client %i found pcr pid: 0x%04X (%i)", + cdata->connid, cdata->pcr_pid, cdata->pcr_pid); } + program_info_length = b2i(2, cdata->pmt_data + 10) & 0xFFF; + if (!program_info_length) + { + offset = 5; + program_info_length = (b2i(2, cdata->pmt_data + 10 + offset) & 0xFFF); + } + if (12 + offset + program_info_length >= section_length) + { return; } + stream_parse_pmt_ca_descriptor(cdata->pmt_data, 0, 12 + offset, program_info_length, cdata); + + offset = offset == 5 ? 0 : program_info_length; + for (i = 12 + offset; i + 4 < section_length; i += 5 + es_info_length) + { + elementary_pid = b2i(2, cdata->pmt_data + i + 1) & 0x1FFF; + es_info_length = b2i(2, cdata->pmt_data + i + 3) & 0xFFF; + switch (cdata->pmt_data[i]) // stream type + { + case 0x01: + case 0x02: + case 0x10: + case 0x1B: + case 0x20: + case 0x24: + case 0x25: + case 0x42: + case 0xD1: + case 0xEA: + { +#ifdef WITH_EMU + cdata->video_pid = elementary_pid; +#endif + cs_log_dbg(D_READER, "Stream client %i found video pid: 0x%04X (%i)", + cdata->connid, elementary_pid, elementary_pid); + stream_parse_pmt_ca_descriptor(cdata->pmt_data, i, 5, es_info_length, cdata); + break; + } + case 0x03: + case 0x04: + case 0x0F: + case 0x11: + case 0x1C: + case 0x2D: + case 0x2E: + case 0x81: + { +#ifdef WITH_EMU + if (cdata->audio_pid_count >= EMU_STREAM_MAX_AUDIO_SUB_TRACKS) + { + continue; + } + + cdata->audio_pids[cdata->audio_pid_count] = elementary_pid; + cdata->audio_pid_count++; +#endif + cs_log_dbg(D_READER, "Stream client %i found audio pid: 0x%04X (%i)", + cdata->connid, elementary_pid, elementary_pid); + break; + } + case 0x06: + //case 0x81: // some ATSC AC-3 streams do not contain the AC-3 descriptor! + case 0x87: + { + uint8_t type = STREAM_UNDEFINED; + ParseDescriptors(cdata->pmt_data + i + 5, es_info_length, &type); + if (type == STREAM_AUDIO) + { +#ifdef WITH_EMU + if (cdata->audio_pid_count >= EMU_STREAM_MAX_AUDIO_SUB_TRACKS) + { + continue; + } + + cdata->audio_pids[cdata->audio_pid_count] = elementary_pid; + cdata->audio_pid_count++; +#endif + cs_log_dbg(D_READER, "Stream client %i found audio pid: 0x%04X (%i)", + cdata->connid, elementary_pid, elementary_pid); + } + else if (type == STREAM_TELETEXT) + { +#ifdef WITH_EMU + cdata->teletext_pid = elementary_pid; +#endif + cs_log_dbg(D_READER, "Stream client %i found teletext pid: 0x%04X (%i)", + cdata->connid, elementary_pid, elementary_pid); + } + break; + } + } +#ifdef __BISS__ + cdata->STREAMpids[cdata->STREAMpidcount] = elementary_pid; + cdata->STREAMpidcount++; +#endif + } +#ifdef WITH_EMU + // If we haven't found a CAID for this service, + // search the keyDB for a fake one + if (cdata->caid == NO_CAID_VALUE && stream_client_get_caid(cdata) == 1) + { + cs_log_dbg(D_READER, "Stream client %i found fake caid: 0x%04X (%i)", + cdata->connid, cdata->caid, cdata->caid); + } +#endif +} + +#ifdef WITH_EMU +static void ParseCatData(stream_client_data *cdata) +{ + uint8_t *data = cdata->cat_data; + uint32_t i; + + for (i = 8; i < (b2i(2, data + 1) & 0xFFF) - 1; i += data[i + 1] + 2) + { + if (data[i] != 0x09) + { + continue; + } + + uint16_t caid = b2i(2, data + i + 2); + + if (caid_is_powervu(caid)) // add all supported caids here + { + if (cdata->caid == NO_CAID_VALUE) + { + cdata->caid = caid; + } + cdata->emm_pid = b2i(2, data + i + 4) & 0x1FFF;; + cs_log_dbg(D_READER, "Stream client %i found emm pid: 0x%04X (%i)", + cdata->connid, cdata->emm_pid, cdata->emm_pid); + break; + } + } +} + +static void ParseEmmData(stream_client_data *cdata) +{ + uint32_t keysAdded = 0; + + emu_process_emm(NULL, cdata->caid, cdata->emm_data, &keysAdded); + + if (keysAdded) + { + //refresh_entitlements(rdr); + cs_log("Stream client %i found %i keys", cdata->connid, keysAdded); + } +} +#endif + +static void ParseTsPackets(stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) +{ + uint8_t payloadStart; + uint16_t pid, offset; + uint32_t i, tsHeader; + + for (i = 0; i < bufLength; i += packetSize) + { + tsHeader = b2i(4, stream_buf + i); + pid = (tsHeader & 0x1FFF00) >> 8; + payloadStart = (tsHeader & 0x400000) >> 22; + + if (tsHeader & 0x20) + { offset = 4 + stream_buf[i + 4] + 1; } + else + { offset = 4; } + + if (packetSize - offset < 1) + { continue; } + + if (pid == 0x0000 && data->have_pat_data != 1) // Search the PAT for the PMT pid + { + ParseTsData(0x00, 0xFF, 16, &data->have_pat_data, data->pat_data, sizeof(data->pat_data), + &data->pat_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParsePatData, data); + continue; + } + + if (pid == data->pmt_pid && data->have_pmt_data != 1) // Search the PMT for PCR, ECM, Video and Audio pids + { + ParseTsData(0x02, 0xFF, 21, &data->have_pmt_data, data->pmt_data, sizeof(data->pmt_data), + &data->pmt_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParsePmtData, data); + continue; + } + + // We have bot PAT and PMT data - No need to search the rest of the packets + if (data->have_pat_data == 1 && data->have_pmt_data == 1) + { break; } + } +} + +#ifdef WITH_EMU +static void decrypt_csa(struct dvbcsa_bs_batch_s *tsbbatch, uint16_t fill[2], const uint8_t oddeven, const int32_t connid, uint8_t cw_type) +#else +static void decrypt_csa(struct dvbcsa_bs_batch_s *tsbbatch, uint16_t fill[2], const uint8_t oddeven, const int32_t connid) +#endif +{ + if (fill[oddeven] > 0) + { +#if 0 + uint16_t i; + for (i = fill[oddeven]; i <= cluster_size; i++) + { + tsbbatch[i].data = NULL; + tsbbatch[i].len = 0; + } +#else + tsbbatch[fill[oddeven]].data = NULL; +#endif + cs_log_dbg(D_READER, "dvbcsa (%s), batch=%d", oddeven == ODD ? "odd" : "even", fill[oddeven]); + + fill[oddeven] = 0; + +#ifdef WITH_EMU + dvbcsa_bs_decrypt(key_data[connid].key[cw_type][oddeven], tsbbatch, 184); +#else + dvbcsa_bs_decrypt(key_data[connid].key[oddeven], tsbbatch, 184); +#endif + } +} +#ifndef WITH_EMU +#define decrypt(a) decrypt_csa(tsbbatch, fill, a, data->connid) +#else // multiple cw +#define decrypt(a) decrypt_csa(tsbbatch, fill, a, data->connid, 0) +#define decrypt_pvu(a, b) decrypt_csa(tsbbatch, fill, a, data->connid, b) +#endif + +#ifdef WITH_EMU +static void DescrambleTsPacketsPowervu(stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize, struct dvbcsa_bs_batch_s *tsbbatch) +{ + uint32_t i, j, tsHeader, *deskey; + uint16_t pid, offset, fill[2] = {0,0}; + uint8_t *pdata, payloadStart, oddeven = 0, cw_type = 0; + int8_t oddKeyUsed; + + for (i = 0; i < bufLength; i += packetSize) + { + tsHeader = b2i(4, stream_buf + i); + pid = (tsHeader & 0x1FFF00) >> 8; + payloadStart = (tsHeader & 0x400000) >> 22; + offset = (tsHeader & 0x20) ? 4 + stream_buf[i + 4] + 1 : 4; + + if (packetSize - offset < 1) + { + continue; + } + + if (emu_stream_emm_enabled && pid == 0x0001 && data->have_cat_data != 1) // Search the CAT for EMM pids + { + // set to null pid + stream_buf[i + 1] |= 0x1F; + stream_buf[i + 2] = 0xFF; + + ParseTsData(0x01, 0xFF, 8, &data->have_cat_data, data->cat_data, sizeof(data->cat_data), + &data->cat_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParseCatData, data); + continue; + } + + if (emu_stream_emm_enabled && data->emm_pid && pid == data->emm_pid) // Process the EMM data + { + // set to null pid + stream_buf[i + 1] |= 0x1F; + stream_buf[i + 2] = 0xFF; + + ParseTsData(0x80, 0xF0, 3, &data->have_emm_data, data->emm_data, sizeof(data->emm_data), + &data->emm_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParseEmmData, data); + continue; + } + + if (data->ecm_pid && pid == data->ecm_pid) // Process the ECM data + { + stream_server_has_ecm[data->connid] = 1; + // set to null pid + stream_buf[i + 1] |= 0x1F; + stream_buf[i + 2] = 0xFF; + ParseTsData(0x80, 0xFE, 3, &data->have_ecm_data, data->ecm_data, sizeof(data->ecm_data), + &data->ecm_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParseEcmData, data); + continue; + } + + if ((tsHeader & 0xC0) == 0) + { + continue; + } + + if (data->key.csa_used) + { + stream_buf[i + 3] &= 0x3f; // consider it decrypted now + oddeven = (tsHeader & 0xC0) == 0xC0 ? ODD: EVEN; + decrypt_pvu(oddeven == ODD ? EVEN : ODD, cw_type); + + if (pid == data->video_pid) // start with video pid, since it is most dominant + { + cw_type = PVU_CW_VID; + tsbbatch[fill[oddeven]].data = &stream_buf[i + offset]; + tsbbatch[fill[oddeven]].len = packetSize - offset; + fill[oddeven]++; + if (fill[oddeven] > cluster_size - 1) + { + decrypt_pvu(oddeven, cw_type); + } + } + else + { + if (cw_type != PVU_CW_VID) + { + decrypt_pvu(oddeven, PVU_CW_VID); + } + for (j = 0; j < data->audio_pid_count; j++) + { + if (pid == data->audio_pids[j]) + { + if (cw_type != PVU_CW_A1 + j) + { + decrypt_pvu(oddeven, PVU_CW_A1 + j); + } + cw_type = PVU_CW_A1 + j; + tsbbatch[fill[oddeven]].data = &stream_buf[i + offset]; + tsbbatch[fill[oddeven]].len = packetSize - offset; + fill[oddeven]++; + if (fill[oddeven] > cluster_size - 1) + { + decrypt_pvu(oddeven, cw_type); + } + } + } + } + } + else + { + oddKeyUsed = (tsHeader & 0xC0) == 0xC0 ? 1 : 0; + deskey = NULL; + + if (pid == data->video_pid) + { + deskey = data->key.pvu_des_ks[PVU_CW_VID][oddKeyUsed]; + } + else + { + for (j = 0; j < data->audio_pid_count; j++) + { + if (pid == data->audio_pids[j]) + { + deskey = data->key.pvu_des_ks[PVU_CW_A1 + j][oddKeyUsed]; + } + } + } + + if (deskey == NULL) + { + deskey = data->key.pvu_des_ks[PVU_CW_HSD][oddKeyUsed]; + } + + for (j = offset; j + 7 < 188; j += 8) + { + pdata = stream_buf + i + j; + des(pdata, deskey, 0); + } + + stream_buf[i + 3] &= 0x3F; + } + } + if (data->key.csa_used) { decrypt_pvu(oddeven, cw_type); } +} +static void DescrambleTsPacketsRosscrypt1(emu_stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) +{ + int8_t is_av_pid; + int32_t j; + + uint8_t scramblingControl; + uint16_t pid, offset; + uint32_t i, tsHeader; + + for (i = 0; i < bufLength; i += packetSize) + { + tsHeader = b2i(4, stream_buf + i); + pid = (tsHeader & 0x1FFF00) >> 8; + scramblingControl = tsHeader & 0xC0; + + if (tsHeader & 0x20) + { + offset = 4 + stream_buf[i + 4] + 1; + } + else + { + offset = 4; + } + + if (packetSize - offset < 1) + { + continue; + } + + if (scramblingControl == 0) + { + continue; + } + + if (!(stream_buf[i + 3] & 0x10)) + { + stream_buf[i + 3] &= 0x3F; + continue; + } + + is_av_pid = 0; + + if (pid == data->video_pid) + { + is_av_pid = 1; + } + else + { + for (j = 0; j < data->audio_pid_count; j++) + { + if (pid == data->audio_pids[j]) + { + is_av_pid = 1; + break; + } + } + } + + if (is_av_pid) + { + static uint8_t dyn_key[184]; + static uint8_t last_packet[184]; + + // Reset key on channel change + if (data->reset_key_data == 1) + { + memset(dyn_key, 0x00, 184); + memset(last_packet, 0x00, 184); + data->reset_key_data = 0; + } + + if (memcmp(last_packet, stream_buf + i + 4, 184) == 0) + { + if (memcmp(dyn_key, stream_buf + i + 4, 184) != 0) + { + memcpy(dyn_key, stream_buf + i + 4, 184); + } + } + else + { + memcpy(last_packet, stream_buf + i + 4, 184); + } + + for (j = 0; j < 184; j++) + { + stream_buf[i + 4 + j] ^= dyn_key[j]; + } + + stream_buf[i + 3] &= 0x3F; + } + } +} + +static void DescrambleTsPacketsCompel(emu_stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) +{ + int8_t is_pes_pid; // any PES pid + int32_t j; + const int8_t limit = 4; + + uint8_t scramblingControl; + uint16_t pid, offset; + uint32_t i, tsHeader; + + for (i = 0; i < bufLength; i += packetSize) + { + tsHeader = b2i(4, stream_buf + i); + pid = (tsHeader & 0x1FFF00) >> 8; + scramblingControl = tsHeader & 0xC0; + + if (tsHeader & 0x20) + { + offset = 4 + stream_buf[i + 4] + 1; + } + else + { + offset = 4; + } + + if (packetSize - offset < 1) + { + continue; + } + + if (scramblingControl == 0) + { + continue; + } + + if (!(stream_buf[i + 3] & 0x10)) + { + stream_buf[i + 3] &= 0x3F; + continue; + } + + is_pes_pid = 0; + + if (pid == data->video_pid) + { + is_pes_pid = 1; + } + else if (pid == data->teletext_pid) + { + is_pes_pid = 1; + } + else + { + for (j = 0; j < data->audio_pid_count; j++) + { + if (pid == data->audio_pids[j]) + { + is_pes_pid = 1; + break; + } + } + } + + if (is_pes_pid) + { + static uint8_t dyn_key[184]; + static uint8_t found_key_bytes[184]; + static uint8_t found_key_bytes_count = 8; + static uint8_t lastScramblingControl = 0xFF; + + int8_t matches00 = 0; + int8_t matchesFF = 0; + int8_t last00_was_good = 0; + int8_t lastFF_was_good = 0; + + // Reset key when scrambling control changes from odd to even + // and vice versa (every ~53 seconds) or when we change channel + if (lastScramblingControl != scramblingControl) + { + memset(dyn_key, 0x00, 184); + memset(found_key_bytes, 0, 184); + found_key_bytes_count = 8; + lastScramblingControl = scramblingControl; + + //cs_log_dbg(D_READER, "resetting key data (scrambling control: %02X)", scramblingControl); + } + + for (j = 8; j < 184; j++) + { + if (found_key_bytes_count == 184) + { + break; + } + + if (stream_buf[i + 4 + j] == 0x00) + { + last00_was_good = 1; + matches00++; + + if (matches00 > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x00; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else if (stream_buf[i + 4 + j] == 0x3F) + { + last00_was_good = 1; + matches00++; + + if (matches00 > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x3F; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else + { + if (last00_was_good == 1) + { + last00_was_good = 0; + matches00--; + } + else + { + matches00 -= 2; + } + + if (matches00 < 0) + { + matches00 = 0; + } + } + + if (stream_buf[i + 4 + j] == 0xC0) + { + lastFF_was_good = 1; + matchesFF++; + + if (matchesFF > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x3F; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else if (stream_buf[i + 4 + j] == 0xFF) + { + lastFF_was_good = 1; + matchesFF++; + + if (matchesFF > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x00; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else + { + if (lastFF_was_good == 1) + { + lastFF_was_good = 0; + matchesFF--; + } + else + { + matchesFF -= 2; + } + + if (matchesFF < 0) + { + matchesFF = 0; + } + } + } + + for (j = 183; j >= 8; j--) + { + if (found_key_bytes_count == 184) + { + break; + } + + if (stream_buf[i + 4 + j] == 0x00) + { + last00_was_good = 1; + matches00++; + + if (matches00 > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x00; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else if (stream_buf[i + 4 + j] == 0x3F) + { + last00_was_good = 1; + matches00++; + + if (matches00 > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x3F; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else + { + if (last00_was_good == 1) + { + last00_was_good = 0; + matches00--; + } + else + { + matches00 -= 2; + } + + if (matches00 < 0) + { + matches00 = 0; + } + } + + if (stream_buf[i + 4 + j] == 0xC0) + { + lastFF_was_good = 1; + matchesFF++; + + if (matchesFF > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x3F; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else if (stream_buf[i + 4 + j] == 0xFF) + { + lastFF_was_good = 1; + matchesFF++; + + if (matchesFF > limit && found_key_bytes[j] == 0) + { + dyn_key[j] = 0x00; + found_key_bytes[j] = 1; + found_key_bytes_count++; + } + } + else + { + if (lastFF_was_good == 1) + { + lastFF_was_good = 0; + matchesFF--; + } + else + { + matchesFF -= 2; + } + + if (matchesFF < 0) + { + matchesFF = 0; + } + } + } + + for (j = 8; j < 184; j++) + { + stream_buf[i + 4 + j] ^= dyn_key[j]; + } + } + + stream_buf[i + 3] &= 0x3F; // Clear scrambling bits + } +} +#endif // WITH_EMU + +static void DescrambleTsPackets(stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize, struct dvbcsa_bs_batch_s *tsbbatch) +{ + uint32_t i, tsHeader; + uint16_t offset, fill[2] = {0,0}; + uint8_t oddeven = 0; +#ifdef MODULE_RADEGAST + uint16_t pid; + uint8_t payloadStart; +#endif + + for (i = 0; i < bufLength; i += packetSize) + { + tsHeader = b2i(4, stream_buf + i); +#ifdef MODULE_RADEGAST + pid = (tsHeader & 0x1FFF00) >> 8; + payloadStart = (tsHeader & 0x400000) >> 22; +#endif + offset = (tsHeader & 0x20) ? 4 + stream_buf[i + 4] + 1 : 4; + if (packetSize - offset < 1) + { continue; } +#ifdef MODULE_RADEGAST +#ifdef __BISS__ + if(data->ecm_pid == 0x1FFF && caid_is_biss_fixed(data->caid)) + { + uint32_t j, n; + uint16_t ecm_len = 7; + data->ecm_data[0] = 0x80; // to pass the cache check it must be 0x80 or 0x81 + data->ecm_data[1] = 0x00; + data->ecm_data[2] = 0x04; + i2b_buf(2, data->srvid, data->ecm_data + 3); + i2b_buf(2, data->pmt_pid, data->ecm_data + 5); + for(j = 0, n = 7; j < data->STREAMpidcount; j++, n += 2) + { + i2b_buf(2, data->STREAMpids[j], data->ecm_data + n); + data->ecm_data[2] += 2; + ecm_len += 2; + } + data->ens &= 0x0FFFFFFF; // clear top 4 bits (in case of DVB-T/C or garbage), prepare for flagging + data->ens |= 0xA0000000; // flag to emu: this is the namespace, not a pid + i2b_buf(2, data->tsid, data->ecm_data + ecm_len); // place tsid after the last stream pid + i2b_buf(2, data->onid, data->ecm_data + ecm_len + 2); // place onid right after tsid + i2b_buf(4, data->ens, data->ecm_data + ecm_len + 4); // place namespace at the end of the ecm + data->ecm_data[2] += 8; + ParseEcmData(data); + } else +#endif // __BISS__ + if (data->ecm_pid && pid == data->ecm_pid) // Process the ECM data + { + // set to null pid + stream_buf[i + 1] |= 0x1F; + stream_buf[i + 2] = 0xFF; + ParseTsData(0x80, 0xFE, 3, &data->have_ecm_data, data->ecm_data, sizeof(data->ecm_data), + &data->ecm_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParseEcmData, data); + continue; + } +#endif // MODULE_RADEGAST + if ((tsHeader & 0xC0) == 0) + { continue; } + + stream_buf[i + 3] &= 0x3f; // consider it decrypted now + oddeven = (tsHeader & 0xC0) == 0xC0 ? ODD: EVEN; + decrypt(oddeven == ODD ? EVEN : ODD); + tsbbatch[fill[oddeven]].data = &stream_buf[i + offset]; + tsbbatch[fill[oddeven]].len = packetSize - offset; + fill[oddeven]++; + + if (fill[oddeven] > cluster_size - 1) + { decrypt(oddeven); } + } + + decrypt(oddeven); +} + +static void http_log(char *log_txt, int32_t status, const char *msg) +{ + char *msg_stripped = remove_newline_chars(msg); + cs_log_dbg(D_CLIENT, log_txt, status, msg_stripped); + NULLFREE(msg_stripped); +} + +static int32_t switch_stream_source_host(stream_client_conn_data *conndata, int8_t *errorCount, const char *msg) +{ + if (cfg.stream_client_source_host && !streq(conndata->stream_host, cfg.stream_source_host) && *errorCount > 0) + { + cs_strncpy(conndata->stream_host, cfg.stream_source_host, sizeof(conndata->stream_host)); + cs_log("FALLBACK: stream client %i - try using stream source host from config. host=%s reason=%s", conndata->connid, conndata->stream_host, msg); + (*errorCount)--; + return 1; + } + return 0; +} + +static int32_t connect_to_stream(char *http_buf, int32_t http_buf_len, char *stream_path, char *stream_source_host, IN_ADDR_T *in_addr) +{ + struct SOCKADDR cservaddr; + struct utsname buffer; uname(&buffer); + int32_t streamfd, status; + + if ((streamfd = socket(DEFAULT_AF, SOCK_STREAM, 0)) == -1) + { return streamfd; } + + struct timeval tv; + tv.tv_sec = 2; + tv.tv_usec = 0; + if (setsockopt(streamfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv)) + { + cs_log("ERROR: setsockopt() failed for SO_RCVTIMEO!"); + close(streamfd); + return -1; + } + + bzero(&cservaddr, sizeof(cservaddr)); + SIN_GET_FAMILY(cservaddr) = DEFAULT_AF; + SIN_GET_PORT(cservaddr) = htons(cfg.stream_source_port); + cs_resolve(stream_source_host, in_addr, NULL, NULL); + SIN_GET_ADDR(cservaddr) = *in_addr; + + if ((status = connect(streamfd, (struct sockaddr *)&cservaddr, sizeof(cservaddr))) == -1) + { + cs_log("WARNING: Connect to stream source port %d failed.", cfg.stream_source_port); + close(streamfd); + return status; + } + + snprintf(http_buf, http_buf_len, + "GET %s HTTP/1.1\nHost: %s:%u\n" + "User-Agent: Mozilla/5.0 (%s %s %s; rv:133.0) Gecko/20100101 Firefox/133.0\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\n" + "Accept-Language: en-US" + "%s%s\n" + "Connection: keep-alive\n\n", + stream_path, + cs_inet_ntoa(*in_addr), + cfg.stream_source_port, +#if defined(__linux__) + "X11;", buffer.sysname, buffer.machine, +#else + "Windows NT 10.0;", "Win64;", "x64", +#endif + (stream_source_auth) ? "\nAuthorization: Basic " : "", + (stream_source_auth) ? stream_source_auth : ""); + + status = send(streamfd, http_buf, cs_strlen(http_buf), 0); + http_log("HTTP (send) (%i): %s", status, http_buf); + return status == -1 ? status : streamfd; +} + +static void stream_client_disconnect(stream_client_conn_data *conndata) +{ + int32_t i; + + SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); + stream_cur_srvid[conndata->connid] = NO_SRVID_VALUE; +#if DVBCSA_KEY_ECM + last_srvid[conndata->connid] = NO_SRVID_VALUE; +#endif +#ifdef WITH_EMU + stream_server_has_ecm[conndata->connid] = 0; +#endif + SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); + + SAFE_MUTEX_LOCK(&stream_server_mutex); + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + if (gconnfd[i] == conndata->connfd) + { + gconnfd[i] = -1; + gconncount--; + } + } + SAFE_MUTEX_UNLOCK(&stream_server_mutex); + + if (conndata->connfd >= 0) + { + shutdown(conndata->connfd, 2); + close(conndata->connfd); + } + + cs_log("Stream client %i disconnected. ip=%s port=%d", conndata->connid, cs_inet_ntoa(client_ip[conndata->connid]), client_port[conndata->connid]); + + if (streamrelay_client[conndata->connid] && !cfg.stream_reuse_client && !streamrelay_client[conndata->connid]->kill_started) + { + cs_disconnect_client(streamrelay_client[conndata->connid]); + free_client(streamrelay_client[conndata->connid]); + } + + NULLFREE(conndata); +} + +static void streamrelay_auth_client(struct s_client *cl) +{ + int32_t ok = 0; + struct s_auth *account; + + for (account = cfg.account; cfg.stream_relay_user && account; account = account->next) + { + if ((ok = streq(cfg.stream_relay_user, account->usr))) + { break; } + } + + cs_auth_client(cl, ok ? account : (struct s_auth *)(-1), "streamrelay"); +} + +static void create_streamrelay_client(stream_client_conn_data *conndata) +{ + int32_t i, exists = 0; + + if (cfg.stream_reuse_client) + { + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + if (streamrelay_client[i]) + { + if (!streamrelay_client[i]->kill) + { + streamrelay_client[conndata->connid] = streamrelay_client[i]; + exists = 1; + break; + } + } + } + } + + if (!exists) + { streamrelay_client[conndata->connid] = create_client(client_ip[conndata->connid]); } + + streamrelay_client[conndata->connid]->typ = 'c'; + streamrelay_client[conndata->connid]->module_idx = mod_idx; + streamrelay_client[conndata->connid]->thread = pthread_self(); + streamrelay_client[conndata->connid]->ip = client_ip[conndata->connid]; + streamrelay_client[conndata->connid]->port = client_port[conndata->connid]; +#ifdef WEBIF + streamrelay_client[conndata->connid]->wihidden = cfg.stream_hide_client; +#endif +} + +static void *stream_client_handler(void *arg) +{ + stream_client_conn_data *conndata = (stream_client_conn_data *)arg; + stream_client_data *data = NULL; + + char *http_buf = NULL, stream_path[255], stream_path_copy[255]; + char *saveptr, *token, http_version[4], http_host[256]; + + int8_t streamConnectErrorCount = 0, streamDataErrorCount = 0, streamReconnectCount = 0; + int32_t bytesRead = 0, http_status_code = 0; + int32_t i, clientStatus, streamStatus, streamfd, last_streamfd = 0; + + uint8_t *stream_buf = NULL; + uint16_t packetCount = 0, packetSize = 0, startOffset = 0; + uint32_t remainingDataPos, remainingDataLength, tmp_pids[4]; + uint8_t descrambling = 0; + + struct timeb start, end; +#ifdef WITH_EMU + int32_t cur_dvb_buffer_size, cur_dvb_buffer_wait; +#else + const int32_t cur_dvb_buffer_size = DVB_BUFFER_SIZE_CSA; + const int32_t cur_dvb_buffer_wait = DVB_BUFFER_WAIT_CSA; +#endif + struct dvbcsa_bs_batch_s *tsbbatch = NULL; + + create_streamrelay_client(conndata); + SAFE_SETSPECIFIC(getclient, streamrelay_client[conndata->connid]); + set_thread_name(__func__); + + if (!streamrelay_client[conndata->connid]->init_done) + { + streamrelay_auth_client(streamrelay_client[conndata->connid]); + streamrelay_client[conndata->connid]->init_done = 1; + } + + cs_log("Stream client %i connected. ip=%s port=%d", conndata->connid, cs_inet_ntoa(client_ip[conndata->connid]), client_port[conndata->connid]); + + do + { + if (!cs_malloc(&http_buf, 1024) || + !cs_malloc(&stream_buf, DVB_BUFFER_SIZE) || + !cs_malloc(&data, sizeof(stream_client_data))) + { break; } + + clientStatus = recv(conndata->connfd, http_buf, 1024, 0); + http_log("HTTP (recv) (%i): %s", clientStatus, http_buf); + + if (clientStatus < 1) + { break; } + + http_buf[1023] = '\0'; + if (sscanf(http_buf, "GET %254s HTTP/%3s Host: %255s ", stream_path, http_version, http_host) < 1) + { break; } + else + { + // use stream_source_host variable from config if host discovery is disabled + if (!cfg.stream_client_source_host) + { cs_strncpy(conndata->stream_host, cfg.stream_source_host, sizeof(conndata->stream_host)); } + // use host from stream client http request, if 'Host: host:port' header was send + else if ((token = strchr(http_host,':'))) + { + size_t len = (size_t)(token - http_host); + if (len >= sizeof(conndata->stream_host)) + { len = sizeof(conndata->stream_host) - 1; } + memcpy(conndata->stream_host, http_host, len); + conndata->stream_host[len] = '\0'; + } + // use the IP address of the stream client itself + else + { cs_strncpy(conndata->stream_host, cs_inet_ntoa(client_ip[conndata->connid]), sizeof(conndata->stream_host)); } + } + + cs_strncpy(stream_path_copy, stream_path, sizeof(stream_path)); + + token = strtok_r(stream_path_copy, ":", &saveptr); // token 0 + for (i = 1; token != NULL && i < 7; i++) // tokens 1 to 6 + { + token = strtok_r(NULL, ":", &saveptr); + if (token == NULL) + { break; } + + if (i >= 3) // We olny need token 3 (srvid), 4 (tsid), 5 (onid) and 6 (ens) + { + if (sscanf(token, "%x", &tmp_pids[i - 3]) != 1) + { tmp_pids[i - 3] = 0; } + } + } + + data->srvid = tmp_pids[0] & 0xFFFF; + data->tsid = tmp_pids[1] & 0xFFFF; + data->onid = tmp_pids[2] & 0xFFFF; + data->ens = tmp_pids[3]; + + if (data->srvid == 0) // We didn't get a srvid - Exit + { break; } + +#ifndef WITH_EMU + key_data[conndata->connid].key[ODD] = dvbcsa_bs_key_alloc(); + key_data[conndata->connid].key[EVEN] = dvbcsa_bs_key_alloc(); +#else + for (i = 0; i < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; i++) + { + key_data[conndata->connid].key[i][ODD] = dvbcsa_bs_key_alloc(); + key_data[conndata->connid].key[i][EVEN] = dvbcsa_bs_key_alloc(); + } +#endif + + if (!cs_malloc(&tsbbatch, (cluster_size + 1) * sizeof(struct dvbcsa_bs_batch_s))) + { break; } + + SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); + stream_cur_srvid[conndata->connid] = data->srvid; +#ifdef WITH_EMU + stream_server_has_ecm[conndata->connid] = 0; +#endif + SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); + + cs_log("Stream client %i request. host=%s port=%d path=%s (%s)", conndata->connid, conndata->stream_host, cfg.stream_source_port, stream_path, !cfg.stream_client_source_host ? "config" : strchr(http_host,':') ? "http header" : "client ip"); + + cs_log_dbg(D_READER, "Stream client %i received srvid: %04X tsid: %04X onid: %04X ens: %08X", + conndata->connid, data->srvid, data->tsid, data->onid, data->ens); + + snprintf(http_buf, 1024, + "HTTP/1.0 200 OK\n" + "Connection: Close\n" + "Content-Type: video/mpeg\n" + "Server: stream_enigma2\n\n"); + clientStatus = send(conndata->connfd, http_buf, cs_strlen(http_buf), 0); + http_log("HTTP (send) (%i): %s", clientStatus, http_buf); + + data->connid = conndata->connid; + data->caid = NO_CAID_VALUE; + data->blocked_caid = NO_CAID_VALUE; + data->have_pat_data = 0; + data->have_pmt_data = 0; + data->have_cat_data = 0; + data->have_ecm_data = 0; + data->have_emm_data = 0; +#ifdef WITH_EMU + data->reset_key_data = 1; +#endif + + while (!exit_oscam && clientStatus != -1 && streamConnectErrorCount < 3 + && streamDataErrorCount < 15) + { + streamfd = connect_to_stream(http_buf, 1024, stream_path, conndata->stream_host, &stream_host_ip[conndata->connid]); + if (streamfd == -1) + { + if (switch_stream_source_host(conndata, &streamConnectErrorCount, "no connection")) // fallback + { continue; } + cs_log("WARNING: stream client %i - cannot connect to stream source host. ip=%s port=%d path=%s", conndata->connid, cs_inet_ntoa(stream_host_ip[conndata->connid]), cfg.stream_source_port, stream_path); + streamConnectErrorCount++; + cs_sleepms(500); + continue; + } + + streamStatus = 0; + bytesRead = 0; + while (!exit_oscam && clientStatus != -1 && streamStatus != -1 +#if 0 + && streamConnectErrorCount < 3 && streamDataErrorCount < 15) +#else + && (streamConnectErrorCount < 3 || streamDataErrorCount < 15)) +#endif + { +#ifdef WITH_EMU + if (data->key.csa_used) + { + cur_dvb_buffer_size = EMU_DVB_BUFFER_SIZE_CSA; + cur_dvb_buffer_wait = EMU_DVB_BUFFER_WAIT_CSA; + } + else + { + cur_dvb_buffer_size = EMU_DVB_BUFFER_SIZE_DES; + cur_dvb_buffer_wait = EMU_DVB_BUFFER_WAIT_DES; + } +#endif + if (!streamrelay_client[conndata->connid] || streamrelay_client[conndata->connid]->kill) + { + clientStatus = -1; + break; + } + + cs_ftime(&start); + streamStatus = recv(streamfd, stream_buf + bytesRead, cur_dvb_buffer_size - bytesRead, MSG_WAITALL); + if (streamStatus == 0) // socket closed + { + cs_log_dbg(D_CLIENT, "STATUS: streamStatus=%i, streamfd=%i, last_streamfd=%i, streamConnectErrorCount=%i, streamDataErrorCount=%i, bytesRead=%i", + streamStatus, streamfd, last_streamfd, streamConnectErrorCount, streamDataErrorCount, bytesRead); + if (streamfd == last_streamfd) + { + cs_log("WARNING: stream client %i - stream source closed connection.", conndata->connid); + switch_stream_source_host(conndata, &streamDataErrorCount, "connection closed"); // fallback + } + http_log("HTTP (recv) (%i): %s", streamStatus, (const char*)stream_buf); + streamConnectErrorCount++; + cs_sleepms(100); + break; + } + + if (streamStatus < 0) // error + { + http_log("HTTP (recv) (%i): %s", streamStatus, (const char*)stream_buf); + if ((errno == EWOULDBLOCK) | (errno == EAGAIN)) + { + if (cfg.stream_relay_reconnect_count > 0) + { + streamReconnectCount++; // 2 sec timeout * cfg.stream_relay_reconnect_count = seconds no data -> close + cs_log("WARNING: stream client %i no data from stream source. Trying to reconnect (%i/%i)", conndata->connid, streamReconnectCount, cfg.stream_relay_reconnect_count); + if (streamReconnectCount >= cfg.stream_relay_reconnect_count) + { + clientStatus = -1; + break; + } + } + else + { + cs_log("WARNING: stream client %i no data from stream source.", conndata->connid); + } + streamDataErrorCount++; // 2 sec timeout * 15 = seconds no data -> close + cs_sleepms(100); + continue; + } + cs_log("WARNING: stream client %i error receiving data from stream source.", conndata->connid); + streamConnectErrorCount++; + cs_sleepms(100); + break; + } + + if (streamStatus < cur_dvb_buffer_size - bytesRead) // probably just received header but no stream + { + if (!bytesRead && streamStatus > 13 && + sscanf((const char*)stream_buf, "HTTP/%3s %d ", http_version , &http_status_code) == 2 && + http_status_code != 200) + { + if (switch_stream_source_host(conndata, &streamConnectErrorCount, "bad response")) // fallback + { break; } + http_log("HTTP (recv) (%i): %s", streamStatus, (const char*)stream_buf); + cs_log("ERROR: stream client %i got %d response from stream source!", conndata->connid, http_status_code); + streamConnectErrorCount++; + cs_sleepms(100); + break; + } + else + { + cs_log_dbg(0, "WARNING: stream client %i non-full buffer from stream source.", conndata->connid); + streamDataErrorCount++; + cs_sleepms(100); + } + } + else + { + streamDataErrorCount = 0; + } + + streamConnectErrorCount = 0; + bytesRead += streamStatus; + + if (bytesRead >= cur_dvb_buffer_wait) + { + startOffset = 0; + + // only search if not starting on ts packet or unknown packet size + if (stream_buf[0] != 0x47 || packetSize == 0) + { SearchTsPackets(stream_buf, bytesRead, &packetSize, &startOffset); } + + if (packetSize == 0) + { bytesRead = 0; } + else + { + packetCount = ((bytesRead - startOffset) / packetSize); + + // We have both PAT and PMT data - We can start descrambling + if (data->have_pat_data == 1 && data->have_pmt_data == 1) + { + if (data->caid == NO_CAID_VALUE && cfg.stream_relay_ctab.ctnum > 0) + { + // No allowed CAID found but CAID filter is configured, disconnect client + cs_log("Stream client %i caid %04X not enabled in stream relay config", + conndata->connid, data->blocked_caid); + clientStatus = -1; + break; + } + if (cfg.stream_relay_ctab.ctnum == 0 || chk_ctab_ex(data->caid, &cfg.stream_relay_ctab)) + { +#ifdef WITH_EMU + if (caid_is_powervu(data->caid)) + { + DescrambleTsPacketsPowervu(data, stream_buf + startOffset, packetCount * packetSize, packetSize, tsbbatch); + } + else if (data->caid == 0xA101) // Rosscrypt1 + { + DescrambleTsPacketsRosscrypt1(data, stream_buf + startOffset, packetCount * packetSize, packetSize); + } + else if (data->caid == NO_CAID_VALUE) // Compel + { + DescrambleTsPacketsCompel(data, stream_buf + startOffset, packetCount * packetSize, packetSize); + } + else +#endif // WITH_EMU + { + DescrambleTsPackets(data, stream_buf + startOffset, packetCount * packetSize, packetSize, tsbbatch); + } + if (!descrambling) + { + if (cfg.stream_relay_buffer_time) + { cs_sleepms(cfg.stream_relay_buffer_time); } + descrambling = 1; + } + } + } + else // Search PAT and PMT packets for service information + { + ParseTsPackets(data, stream_buf + startOffset, packetCount * packetSize, packetSize); + } + + clientStatus = send(conndata->connfd, stream_buf + startOffset, packetCount * packetSize, 0); + + remainingDataPos = startOffset + (packetCount * packetSize); + remainingDataLength = bytesRead - remainingDataPos; + + if (remainingDataPos < remainingDataLength) + { memmove(stream_buf, stream_buf + remainingDataPos, remainingDataLength); } + else + { memcpy(stream_buf, stream_buf + remainingDataPos, remainingDataLength); } + + bytesRead = remainingDataLength; + } + } + cs_ftime(&end); + stream_resptime[conndata->connid] = comp_timeb(&end, &start); + } + + last_streamfd = streamfd; + close(streamfd); + } + } while (0); + + NULLFREE(http_buf); + NULLFREE(stream_buf); +#ifndef WITH_EMU + dvbcsa_bs_key_free(key_data[conndata->connid].key[ODD]); + dvbcsa_bs_key_free(key_data[conndata->connid].key[EVEN]); + key_data[conndata->connid].key[ODD] = NULL; + key_data[conndata->connid].key[EVEN] = NULL; +#else + for (i = 0; i < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; i++) + { + dvbcsa_bs_key_free(key_data[conndata->connid].key[i][ODD]); + dvbcsa_bs_key_free(key_data[conndata->connid].key[i][EVEN]); + key_data[conndata->connid].key[i][ODD] = NULL; + key_data[conndata->connid].key[i][EVEN] = NULL; + } +#endif + NULLFREE(tsbbatch); + NULLFREE(data); + + stream_client_disconnect(conndata); + return NULL; +} + +static void *stream_server(void) +{ +#ifdef IPV6SUPPORT + struct sockaddr_in6 servaddr, cliaddr; +#else + struct sockaddr_in servaddr, cliaddr; +#endif + socklen_t clilen; + int32_t connfd, reuse = 1, i; + int8_t connaccepted; + stream_client_conn_data *conndata; + + struct s_client *client = first_client; + client->thread = pthread_self(); + SAFE_SETSPECIFIC(getclient, client); + set_thread_name(__func__); + + cluster_size = dvbcsa_bs_batch_size(); + has_dvbcsa_ecm = (DVBCSA_HEADER_ECM); +#if !DVBCSA_KEY_ECM +#pragma message "WARNING: Streamrelay is compiled without dvbcsa ecm headers! ECM processing via Streamrelay will not work!" +#endif +#if !STATIC_LIBDVBCSA + has_dvbcsa_ecm = (dlsym(RTLD_DEFAULT, "dvbcsa_bs_key_set_ecm")); + is_dvbcsa_static = 0; +#if DVBCSA_KEY_ECM + dvbcsa_get_ecm_table_fn = dlsym(RTLD_DEFAULT, "dvbcsa_get_ecm_table"); +#endif +#endif + + do + { + cs_log("%s: (%s) %s dvbcsa parallel mode = %d (relay buffer time: %d ms)%s%s", + (!DVBCSA_HEADER_ECM || !has_dvbcsa_ecm) ? "WARNING" : "INFO", + (!has_dvbcsa_ecm) ? "wrong" : "ecm", + (!is_dvbcsa_static) ? "dynamic" : "static", + cluster_size, + cfg.stream_relay_buffer_time, + (!DVBCSA_HEADER_ECM || !has_dvbcsa_ecm) ? "! ECM processing via Streamrelay does not work!" : "", + (!DVBCSA_HEADER_ECM) ? " Missing dvbcsa ecm headers during build!" : ""); + + if (!stream_server_mutex_init) + { + SAFE_MUTEX_INIT(&stream_server_mutex, NULL); + stream_server_mutex_init = 1; + } + + SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + stream_cur_srvid[i] = NO_SRVID_VALUE; +#ifdef WITH_EMU + stream_server_has_ecm[i] = 0; +#endif + } + SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); + + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + gconnfd[i] = -1; + } +#ifdef IPV6SUPPORT + if ((glistenfd = socket(AF_INET6, SOCK_STREAM, 0)) == -1) + { + cs_log("ERROR: cannot create stream server socket! (errno=%d %s)", errno, strerror(errno)); + break; + } + + bzero(&servaddr,sizeof(servaddr)); + servaddr.sin6_family = AF_INET6; + servaddr.sin6_addr = in6addr_any; + servaddr.sin6_port = htons(cfg.stream_relay_port); +#else + if ((glistenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + cs_log("ERROR: cannot create stream server socket! (errno=%d %s)", errno, strerror(errno)); + break; + } + + bzero(&servaddr,sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = htonl(INADDR_ANY); + servaddr.sin_port = htons(cfg.stream_relay_port); +#endif + setsockopt(glistenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + if (bind(glistenfd,(struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) + { + cs_log("ERROR: cannot bind to stream server socket! (errno=%d %s)", errno, strerror(errno)); + break; + } + + if (listen(glistenfd, 3) == -1) + { + cs_log("ERROR: cannot listen to stream server socket! (errno=%d %s)", errno, strerror(errno)); + break; + } + + cs_log("Stream Relay server initialized. ip=%s port=%d", cs_inet_ntoa(SIN_GET_ADDR(servaddr)), ntohs(SIN_GET_PORT(servaddr))); + + while (!exit_oscam) + { + clilen = sizeof(cliaddr); + if ((connfd = accept(glistenfd,(struct sockaddr *)&cliaddr, &clilen)) == -1) + { + cs_log("ERROR: Calling accept() failed! (errno=%d %s)", errno, strerror(errno)); + break; + } + + connaccepted = 0; + + if (cs_malloc(&conndata, sizeof(stream_client_conn_data))) + { + SAFE_MUTEX_LOCK(&stream_server_mutex); + if (gconncount < STREAM_SERVER_MAX_CONNECTIONS) + { + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + if (gconnfd[i] == -1) + { + cs_strncpy(ecm_src[i], "dvbapi", sizeof(ecm_src[i])); + gconnfd[i] = connfd; + gconncount++; + connaccepted = 1; + + conndata->connfd = connfd; + conndata->connid = i; + client_ip[i] = SIN_GET_ADDR(cliaddr); + client_port[i] = ntohs(SIN_GET_PORT(cliaddr)); + break; + } + } + } + SAFE_MUTEX_UNLOCK(&stream_server_mutex); + } + + if (connaccepted) + { + int on = 1; + if (setsockopt(connfd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) + { cs_log("ERROR: stream client %i setsockopt() failed for TCP_NODELAY!", conndata->connid); } + + start_thread("stream client", stream_client_handler, (void*)conndata, NULL, 1, 0); + } + else + { + shutdown(connfd, 2); + close(connfd); + cs_log("ERROR: stream server client dropped because of too many connections (%i)!", STREAM_SERVER_MAX_CONNECTIONS); + } + + cs_sleepms(20); + } + } while (0); + + if (glistenfd >= 0) + { close(glistenfd); } + return NULL; +} + +void *streamrelay_handler(struct s_client *UNUSED(cl), uint8_t *UNUSED(mbuf), int32_t module_idx) +{ + char authtmp[128]; + + if (cfg.stream_relay_enabled) + { + + if (cfg.stream_source_auth_user && cfg.stream_source_auth_password) + { + snprintf(authtmp, sizeof(authtmp), "%s:%s", cfg.stream_source_auth_user, cfg.stream_source_auth_password); + b64encode(authtmp, cs_strlen(authtmp), &stream_source_auth); + } + +#ifdef WITH_EMU + emu_stream_emm_enabled = cfg.emu_stream_emm_enabled; +#endif + + mod_idx = module_idx; + start_thread("stream_server", stream_server, NULL, NULL, 1, 1); + } + return NULL; +} + +#if WITH_EMU +void *stream_key_delayer(void *UNUSED(arg)) +{ + int32_t i, j; + const uint8_t ecm = 0; + emu_stream_client_key_data *cdata; + LL_ITER it; + emu_stream_cw_item *item; + struct timeb t_now; + + while (!exit_oscam) + { + cs_ftime(&t_now); + + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + it = ll_iter_create(ll_emu_stream_delayed_keys[i]); + + while ((item = ll_iter_next(&it))) + { + if (comp_timeb(&t_now, &item->write_time) < 0) + { + break; + } + + SAFE_MUTEX_LOCK(&emu_fixed_key_data_mutex[i]); + + cdata = &emu_fixed_key_data[i]; + + for (j = 0; j < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; j++) + { + if (item->csa_used) + { + if (item->is_even) + { + dvbcsa_bs_key_set(item->cw[j], key_data[cdata->connid].key[j][EVEN]); + } + else + { + dvbcsa_bs_key_set(item->cw[j], key_data[cdata->connid].key[j][ODD]); + } + + cdata->csa_used = 1; + } + else + { + if (item->is_even) + { + des_set_key(item->cw[j], cdata->pvu_des_ks[j][0]); + } + else + { + des_set_key(item->cw[j], cdata->pvu_des_ks[j][1]); + } + + cdata->csa_used = 0; + } + } + SAFE_MUTEX_UNLOCK(&emu_fixed_key_data_mutex[i]); + + ll_iter_remove_data(&it); + } + } + + cs_sleepms(25); + } + + return NULL; +} +#endif + +void stop_stream_server(void) +{ + int32_t i; + + SAFE_MUTEX_LOCK(&stream_server_mutex); + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + { + if (gconnfd[i] != -1) + { + shutdown(gconnfd[i], 2); + close(gconnfd[i]); + gconnfd[i] = -1; + } + } + + gconncount = 0; + SAFE_MUTEX_UNLOCK(&stream_server_mutex); + +#ifdef MODULE_RADEGAST + close_radegast_connection(); +#endif + + if (glistenfd >= 0) + { + shutdown(glistenfd, 2); + close(glistenfd); + } + + NULLFREE(stream_source_auth); +} + +/* + * protocol structure + */ +void module_streamrelay(struct s_module *ph) +{ + ph->desc = "streamrelay"; + ph->type = MOD_CONN_SERIAL; + ph->s_handler = streamrelay_handler; +} +#endif // MODULE_STREAMRELAY diff --git a/module-streamrelay.h b/module-streamrelay.h new file mode 100644 index 0000000..2f0e1b1 --- /dev/null +++ b/module-streamrelay.h @@ -0,0 +1,129 @@ +#ifndef MODULE_STREAMRELAY_H_ +#define MODULE_STREAMRELAY_H_ + +#ifdef MODULE_STREAMRELAY + +#define STREAM_SERVER_MAX_CONNECTIONS 16 + +#define DVB_MAX_TS_PACKETS 278 +#define DVB_BUFFER_SIZE_CSA 188*DVB_MAX_TS_PACKETS +#define DVB_BUFFER_WAIT_CSA 188*(DVB_MAX_TS_PACKETS-128) +#define DVB_BUFFER_SIZE DVB_BUFFER_SIZE_CSA + +#ifdef WITH_EMU +#define EMU_STREAM_MAX_AUDIO_SUB_TRACKS 4 +#define EMU_DVB_BUFFER_SIZE_CSA DVB_BUFFER_SIZE_CSA +#define EMU_DVB_BUFFER_WAIT_CSA DVB_BUFFER_WAIT_CSA +#define EMU_DVB_BUFFER_SIZE_DES 188*32 +#define EMU_DVB_BUFFER_WAIT_DES 188*29 +#define EMU_STREAM_SERVER_MAX_CONNECTIONS STREAM_SERVER_MAX_CONNECTIONS +#define emu_fixed_key_srvid_mutex fixed_key_srvid_mutex +#define emu_stream_cur_srvid stream_cur_srvid +#define emu_stream_client_data stream_client_data +#endif + +//#define __BISS__ +#ifdef __BISS__ +#define MAX_STREAM_PIDS 32 +#endif + +#include "cscrypt/md5.h" +#include + +#define EVEN 0 +#define ODD 1 + +typedef struct +{ +#ifdef WITH_EMU + struct dvbcsa_bs_key_s *key[EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2][2]; +#else + struct dvbcsa_bs_key_s *key[2]; +#endif +} stream_client_key_data; + +#ifdef WITH_EMU +typedef struct +{ + uint32_t pvu_des_ks[EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2][2][32]; + int8_t csa_used; + int32_t connid; +} emu_stream_client_key_data; +#endif + +typedef struct +{ + int32_t connid; + int8_t have_cat_data; + int8_t have_pat_data; + int8_t have_pmt_data; + int8_t have_ecm_data; + int8_t have_emm_data; + uint16_t blocked_caid; + uint8_t cat_data[1024+208]; + uint8_t pat_data[1024+208]; + uint8_t pmt_data[1024+208]; + uint8_t ecm_data[1024+208]; + uint8_t emm_data[1024+208]; + uint16_t cat_data_pos; + uint16_t pat_data_pos; + uint16_t pmt_data_pos; + uint16_t ecm_data_pos; + uint16_t emm_data_pos; + uint16_t srvid; + uint16_t caid; + uint16_t tsid; + uint16_t onid; + uint32_t ens; + uint16_t pmt_pid; + uint16_t ecm_pid; + uint16_t emm_pid; + uint16_t pcr_pid; +#ifdef __BISS__ + uint8_t STREAMpidcount; + uint16_t STREAMpids[MAX_STREAM_PIDS]; +#endif + uint8_t ecm_md5[MD5_DIGEST_LENGTH]; +#ifdef WITH_EMU + int16_t ecm_nb; + int8_t reset_key_data; + uint16_t video_pid; + uint16_t teletext_pid; + uint16_t audio_pids[EMU_STREAM_MAX_AUDIO_SUB_TRACKS]; + uint8_t audio_pid_count; + emu_stream_client_key_data key; +#endif +} stream_client_data; + +void init_stream_server(void); +void stop_stream_server(void); + +bool stream_write_cw(ECM_REQUEST *er); + +#ifdef WITH_EMU +extern int8_t stream_server_thread_init; +extern pthread_mutex_t fixed_key_srvid_mutex; +extern uint16_t stream_cur_srvid[STREAM_SERVER_MAX_CONNECTIONS]; +extern int8_t stream_server_has_ecm[STREAM_SERVER_MAX_CONNECTIONS]; +extern uint8_t emu_stream_server_mutex_init; +extern bool has_dvbcsa_ecm; + +typedef struct +{ + struct timeb write_time; + int8_t csa_used; + int8_t is_even; + uint8_t cw[8][8]; +} emu_stream_cw_item; + +extern pthread_mutex_t emu_fixed_key_data_mutex[EMU_STREAM_SERVER_MAX_CONNECTIONS]; +extern stream_client_key_data key_data[STREAM_SERVER_MAX_CONNECTIONS]; +extern emu_stream_client_key_data emu_fixed_key_data[EMU_STREAM_SERVER_MAX_CONNECTIONS]; + +extern LLIST *ll_emu_stream_delayed_keys[EMU_STREAM_SERVER_MAX_CONNECTIONS]; +void *stream_key_delayer(void *arg); +#endif // WITH_EMU + +#endif // MODULE_STREAMRELAY + +#endif // MODULE_STREAMRELAY_H_ diff --git a/module-webif-lib.c b/module-webif-lib.c new file mode 100644 index 0000000..253adba --- /dev/null +++ b/module-webif-lib.c @@ -0,0 +1,1146 @@ +#define MODULE_LOG_PREFIX "webif" + +#include "globals.h" + +#ifdef WEBIF +#include "cscrypt/md5.h" +#include "module-webif-lib.h" +#include "module-webif-tpl.h" +#include "oscam-config.h" +#include "oscam-files.h" +#include "oscam-lock.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-net.h" +#if defined(__linux__) + #include +#elif defined(__APPLE__) + #include +#endif + +extern int32_t ssl_active; +extern pthread_key_t getkeepalive; +extern pthread_key_t getssl; +extern CS_MUTEX_LOCK *lock_cs; +extern char noncekey[33]; + +static struct s_nonce *nonce_first[AUTHNONCEHASHBUCKETS]; +static CS_MUTEX_LOCK nonce_lock[AUTHNONCEHASHBUCKETS]; + +/* Parses a value in an authentication string by removing all quotes/whitespace. Note that the original array is modified. */ +static char *parse_auth_value(char *value) +{ + char *pch = value; + char *pch2; + value = strstr(value, "="); + if(value != NULL) + { + do + { + ++value; + } + while(value[0] == ' ' || value[0] == '"'); + pch = value; + for(pch2 = value + cs_strlen(value) - 1; pch2 >= value && (pch2[0] == ' ' || pch2[0] == '"' || pch2[0] == '\r' || pch2[0] == '\n'); --pch2) { pch2[0] = '\0'; } + } + return pch; +} + +/* Parses the date out of a "If-Modified-Since"-header. Note that the original string is modified. */ +time_t parse_modifiedsince(char *value) +{ + int32_t day = -1, month = -1, year = -1, hour = -1, minutes = -1, seconds = -1; + char months[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + char *str, *saveptr1 = NULL; + time_t modifiedheader = 0; + value += 18; + // Parse over weekday at beginning... + while(value[0] == ' ' && value[0] != '\0') { ++value; } + while(value[0] != ' ' && value[0] != '\0') { ++value; } + // According to http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1 three different timeformats are allowed so we need a bit logic to parse all of them... + if(value[0] != '\0') + { + ++value; + for(month = 0; month < 12; ++month) + { + if(strstr(value, months[month])) { break; } + } + if(month > 11) { month = -1; } + for(str = strtok_r(value, " ", &saveptr1); str; str = strtok_r(NULL, " ", &saveptr1)) + { + switch(cs_strlen(str)) + { + case 1: + case 2: + day = atoi(str); + break; + + case 4: + if(str[0] != 'G') + { year = atoi(str); } + break; + + case 8: + if(str[2] == ':' && str[5] == ':') + { + hour = atoi(str); + minutes = atoi(str + 3); + seconds = atoi(str + 6); + } + break; + + case 9: + if(str[2] == '-' && str[6] == '-') + { + day = atoi(str); + year = atoi(str + 7) + 2000; + } + break; + } + } + if(day > 0 && day < 32 && month > 0 && year > 0 && year < 9999 && hour > -1 && hour < 24 && minutes > -1 && minutes < 60 && seconds > -1 && seconds < 60) + { + struct tm timeinfo; + memset(&timeinfo, 0, sizeof(timeinfo)); + timeinfo.tm_mday = day; + timeinfo.tm_mon = month; + timeinfo.tm_year = year - 1900; + timeinfo.tm_hour = hour; + timeinfo.tm_min = minutes; + timeinfo.tm_sec = seconds; + modifiedheader = cs_timegm(&timeinfo); + } + } + return modifiedheader; +} + +/* Calculates a new opaque value. Please note that opaque needs to be at least (MD5_DIGEST_LENGTH * 2) + 1 large. */ +void calculate_opaque(IN_ADDR_T addr, char *opaque) +{ + char noncetmp[128]; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + snprintf(noncetmp, sizeof(noncetmp), "%d:%s:%d", (int32_t)time(NULL), cs_inet_ntoa(addr), (int16_t)rand()); + char_to_hex(MD5((uint8_t *)noncetmp, cs_strlen(noncetmp), md5tmp), MD5_DIGEST_LENGTH, (uint8_t *)opaque); +} + +void init_noncelocks(void) +{ + int32_t i; + for(i = 0; i < AUTHNONCEHASHBUCKETS; ++i) + { + cs_lock_create(__func__, &nonce_lock[i], "nonce_lock", 5000); + nonce_first[i] = NULL; + } +} + +/* Calculates the currently valid nonce value and copies it to result. Please note that nonce (may be NULL), opaque and result needs to be at least (MD5_DIGEST_LENGTH * 2) + 1 large. */ +void calculate_nonce(char *nonce, char *result, char *opaque) +{ + struct s_nonce *noncelist, *prev, *foundnonce = NULL, *foundopaque = NULL, *foundexpired = NULL; + int32_t bucket = opaque[0] % AUTHNONCEHASHBUCKETS; + time_t now = time(NULL); + cs_writelock(__func__, &nonce_lock[bucket]); + for(noncelist = nonce_first[bucket], prev = NULL; noncelist; prev = noncelist, noncelist = noncelist->next) + { + if(now > noncelist->expirationdate) + { + if(prev) { prev->next = NULL; } + else + { + nonce_first[bucket] = NULL; + } + foundexpired = noncelist; + break; + } + if(nonce && !memcmp(noncelist->nonce, nonce, (MD5_DIGEST_LENGTH * 2) + 1)) + { + memcpy(result, noncelist->nonce, (MD5_DIGEST_LENGTH * 2) + 1); + foundnonce = noncelist; + if(!noncelist->firstuse) { noncelist->firstuse = now; } + else if(now - foundnonce->firstuse > AUTHNONCEVALIDSECS) + { + if(prev) { prev->next = noncelist->next; } + else + { + nonce_first[bucket] = noncelist->next; + } + } + break; + } + else if(!noncelist->firstuse && !memcmp(noncelist->opaque, opaque, (MD5_DIGEST_LENGTH * 2) + 1)) + { + foundopaque = noncelist; + } + } + if(foundnonce && now - foundnonce->firstuse > AUTHNONCEVALIDSECS) + { + NULLFREE(foundnonce); + foundnonce = NULL; + } + if(!foundnonce && foundopaque) + { memcpy(result, foundopaque->nonce, (MD5_DIGEST_LENGTH * 2) + 1); } + if(!foundnonce && !foundopaque) + { + char noncetmp[128], randstr[16]; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + get_random_bytes((uint8_t *)randstr, sizeof(randstr) - 1); + randstr[sizeof(randstr) - 1] = '\0'; + snprintf(noncetmp, sizeof(noncetmp), "%d:%s:%s", (int32_t)now, randstr, noncekey); + char_to_hex(MD5((uint8_t *)noncetmp, cs_strlen(noncetmp), md5tmp), MD5_DIGEST_LENGTH, (uint8_t *)result); + if(cs_malloc(&noncelist, sizeof(struct s_nonce))) + { + noncelist->expirationdate = now + AUTHNONCEEXPIRATION; + memcpy(noncelist->nonce, result, (MD5_DIGEST_LENGTH * 2) + 1); + memcpy(noncelist->opaque, opaque, (MD5_DIGEST_LENGTH * 2) + 1); + noncelist->next = nonce_first[bucket]; + nonce_first[bucket] = noncelist; + } + } + cs_writeunlock(__func__, &nonce_lock[bucket]); + while(foundexpired) + { + prev = foundexpired; + foundexpired = foundexpired->next; + NULLFREE(prev); + } +} + +/* Checks if authentication is correct. Returns -1 if not correct, 1 if correct and 2 if nonce isn't valid anymore. + Note that authstring will be modified. */ +int32_t check_auth(char *authstring, char *method, char *path, IN_ADDR_T addr, char *expectednonce, char *opaque) +{ + int32_t authok = 0, uriok = 0; + char authnonce[(MD5_DIGEST_LENGTH * 2) + 1]; + memset(authnonce, 0, sizeof(authnonce)); + char *authnc = ""; + char *authcnonce = ""; + char *authresponse = ""; + char *uri = ""; + char *username = ""; + char *expectedPassword = cfg.http_pwd; + char *pch = authstring + 22; + char *pch2; + char *saveptr1 = NULL; + memset(opaque, 0, (MD5_DIGEST_LENGTH * 2) + 1); + + for(pch = strtok_r(pch, ",", &saveptr1); pch; pch = strtok_r(NULL, ",", &saveptr1)) + { + pch2 = pch; + while(pch2[0] == ' ' && pch2[0] != '\0') { ++pch2; } + if(strncmp(pch2, "nonce", 5) == 0) + { + cs_strncpy(authnonce, parse_auth_value(pch2), sizeof(authnonce)); + } + else if(strncmp(pch2, "nc", 2) == 0) + { + authnc = parse_auth_value(pch2); + } + else if(strncmp(pch2, "cnonce", 6) == 0) + { + authcnonce = parse_auth_value(pch2); + } + else if(strncmp(pch2, "response", 8) == 0) + { + authresponse = parse_auth_value(pch2); + } + else if(strncmp(pch2, "uri", 3) == 0) + { + uri = parse_auth_value(pch2); + } + else if(strncmp(pch2, "username", 8) == 0) + { + username = parse_auth_value(pch2); + } + else if(strncmp(pch2, "opaque", 6) == 0) + { + char *tmp = parse_auth_value(pch2); + cs_strncpy(opaque, tmp, (MD5_DIGEST_LENGTH * 2) + 1); + } + } + + if(strncmp(uri, path, cs_strlen(path)) == 0) { uriok = 1; } + else + { + pch2 = uri; + for(pch = uri; pch[0] != '\0'; ++pch) + { + if(pch[0] == '/') { pch2 = pch; } + if(strncmp(pch2, path, cs_strlen(path)) == 0) { uriok = 1; } + } + } + if(uriok == 1 && streq(username, cfg.http_user)) + { + char A1tmp[3 + cs_strlen(username) + cs_strlen(AUTHREALM) + cs_strlen(expectedPassword)]; + char A1[(MD5_DIGEST_LENGTH * 2) + 1], A2[(MD5_DIGEST_LENGTH * 2) + 1], A3[(MD5_DIGEST_LENGTH * 2) + 1]; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + snprintf(A1tmp, sizeof(A1tmp), "%s:%s:%s", username, AUTHREALM, expectedPassword); + char_to_hex(MD5((uint8_t *)A1tmp, cs_strlen(A1tmp), md5tmp), MD5_DIGEST_LENGTH, (uint8_t *)A1); + + char A2tmp[2 + cs_strlen(method) + cs_strlen(uri)]; + snprintf(A2tmp, sizeof(A2tmp), "%s:%s", method, uri); + char_to_hex(MD5((uint8_t *)A2tmp, cs_strlen(A2tmp), md5tmp), MD5_DIGEST_LENGTH, (uint8_t *)A2); + + char A3tmp[10 + cs_strlen(A1) + cs_strlen(A2) + cs_strlen(authnonce) + cs_strlen(authnc) + cs_strlen(authcnonce)]; + snprintf(A3tmp, sizeof(A3tmp), "%s:%s:%s:%s:auth:%s", A1, authnonce, authnc, authcnonce, A2); + char_to_hex(MD5((uint8_t *)A3tmp, cs_strlen(A3tmp), md5tmp), MD5_DIGEST_LENGTH, (uint8_t *)A3); + + if(strcmp(A3, authresponse) == 0) + { + if(cs_strlen(opaque) != MD5_DIGEST_LENGTH * 2) { calculate_opaque(addr, opaque); } + calculate_nonce(authnonce, expectednonce, opaque); + if(strcmp(expectednonce, authnonce) == 0) { authok = 1; } + else + { + authok = 2; + cs_log_dbg(D_TRACE, "WebIf: Received stale header from %s (nonce=%s, expectednonce=%s, opaque=%s).", cs_inet_ntoa(addr), authnonce, expectednonce, opaque); + } + } + } + if(!authok) + { cs_log("unauthorized access from %s - invalid credentials", cs_inet_ntoa(addr)); } + return authok; +} + +int32_t webif_write_raw(char *buf, FILE *f, int32_t len) +{ + errno = 0; +#ifdef WITH_SSL + if(ssl_active) + { + return SSL_write((SSL *)f, buf, len); + } + else +#endif + return fwrite(buf, 1, len, f); +} + +int32_t webif_write(char *buf, FILE *f) +{ + return webif_write_raw(buf, f, cs_strlen(buf)); +} + +int32_t webif_read(char *buf, int32_t num, FILE *f) +{ + errno = 0; +#ifdef WITH_SSL + if(ssl_active) + { + return SSL_read((SSL *)f, buf, num); + } + else +#endif + return read(fileno(f), buf, num); +} + +void send_headers(FILE *f, int32_t status, char *title, char *extra, char *mime, int32_t cache, int32_t length, char *content, int8_t forcePlain) +{ + time_t now; + char timebuf[32]; + char buf[sizeof(PROTOCOL) + sizeof(SERVER) + cs_strlen(title) + (extra == NULL ? 0 : cs_strlen(extra) + 2) + (mime == NULL ? 0 : cs_strlen(mime) + 2) + 350]; + char *pos = buf; + struct tm timeinfo; + + pos += snprintf(pos, sizeof(buf) - (pos - buf), "%s %d %s\r\n", PROTOCOL, status, title); + pos += snprintf(pos, sizeof(buf) - (pos - buf), "Server: %s\r\n", SERVER); + + now = time(NULL); + cs_gmtime_r(&now, &timeinfo); + strftime(timebuf, sizeof(timebuf), RFC1123FMT, &timeinfo); + pos += snprintf(pos, sizeof(buf) - (pos - buf), "Date: %s\r\n", timebuf); + + if(extra) + { pos += snprintf(pos, sizeof(buf) - (pos - buf), "%s\r\n", extra); } + + if(mime) + { pos += snprintf(pos, sizeof(buf) - (pos - buf), "Content-Type: %s\r\n", mime); } + + if(status != 304) + { + if(!cache) + { + pos += snprintf(pos, sizeof(buf) - (pos - buf), "Cache-Control: no-store, no-cache, must-revalidate\r\n"); + pos += snprintf(pos, sizeof(buf) - (pos - buf), "Expires: Sat, 10 Jan 2000 05:00:00 GMT\r\n"); + } + else + { + pos += snprintf(pos, sizeof(buf) - (pos - buf), "Cache-Control: public, max-age=7200\r\n"); + } + pos += snprintf(pos, sizeof(buf) - (pos - buf), "Content-Length: %d\r\n", length); + pos += snprintf(pos, sizeof(buf) - (pos - buf), "Last-Modified: %s\r\n", timebuf); + if(content) + { + uint32_t checksum = (uint32_t)crc32(0L, (uint8_t *)content, length); + pos += snprintf(pos, sizeof(buf) - (pos - buf), "ETag: \"%u\"\r\n", checksum == 0 ? 1 : checksum); + } + } + if(*(int8_t *)pthread_getspecific(getkeepalive)) + { pos += snprintf(pos, sizeof(buf) - (pos - buf), "Connection: Keep-Alive\r\n"); } + else + { pos += snprintf(pos, sizeof(buf) - (pos - buf), "Connection: close\r\n"); } + snprintf(pos, sizeof(buf) - (pos - buf), "\r\n"); + if(forcePlain == 1) { fwrite(buf, 1, cs_strlen(buf), f); } + else { webif_write(buf, f); } +} + +void send_error(FILE *f, int32_t status, char *title, char *extra, char *text, int8_t forcePlain) +{ + char buf[(2 * cs_strlen(title)) + cs_strlen(text) + 128]; + char *pos = buf; + pos += snprintf(pos, sizeof(buf) - (pos - buf), "%d %s\r\n", status, title); + pos += snprintf(pos, sizeof(buf) - (pos - buf), "

%d %s

\r\n", status, title); + pos += snprintf(pos, sizeof(buf) - (pos - buf), "%s\r\n", text); + snprintf(pos, sizeof(buf) - (pos - buf), "\r\n"); + send_headers(f, status, title, extra, "text/html", 0, cs_strlen(buf), NULL, forcePlain); + if(forcePlain == 1) { fwrite(buf, 1, cs_strlen(buf), f); } + else { webif_write(buf, f); } +} + +void send_error500(FILE *f) +{ + send_error(f, 500, "Internal Server Error", NULL, "The server encountered an internal error that prevented it from fulfilling this request.", 0); +} + +void send_header304(FILE *f, char *extraheader) +{ + send_headers(f, 304, "Not Modified", extraheader, NULL, 1, 0, NULL, 0); +} + +/* + * function for sending files. + */ +void send_file(FILE *f, char *filename, char *subdir, time_t modifiedheader, uint32_t etagheader, char *extraheader) +{ + int8_t filen = 0; + int32_t size = 0; + char *mimetype = ""; + char *result = " "; + char *allocated = NULL; + time_t moddate; + char path[255]; + char *CSS = NULL; + char *JSCRIPT = NULL; + char *JQUERY = NULL; + + if(!strcmp(filename, "CSS")) + { + filename = cfg.http_css ? cfg.http_css : ""; + if(subdir && cs_strlen(subdir) > 0) + { + filename = tpl_getFilePathInSubdir(cfg.http_tpl ? cfg.http_tpl : "", subdir, "site", ".css", path, 255); + } + mimetype = "text/css"; + filen = 1; + } + else if(!strcmp(filename, "JS")) + { + filename = cfg.http_jscript ? cfg.http_jscript : ""; + if(subdir && cs_strlen(subdir) > 0) + { + filename = tpl_getFilePathInSubdir(cfg.http_tpl ? cfg.http_tpl : "", subdir, "oscam", ".js", path, 255); + } + mimetype = "text/javascript"; + filen = 2; + } + else if(!strcmp(filename, "JQ")) + { + if(subdir && cs_strlen(subdir) > 0) + { + filename = tpl_getFilePathInSubdir(cfg.http_tpl ? cfg.http_tpl : "", subdir, "jquery", ".js", path, 255); + } + mimetype = "text/javascript"; + filen = 3; + } + + if(cs_strlen(filename) > 0 && file_exists(filename)) + { + struct stat st; + FILE *fp = NULL; + int32_t readen = 0; + uint32_t CSS_sz = 0; + char separator[255]; + + stat(filename, &st); + moddate = st.st_mtime; + memset(separator, 0, sizeof(separator)); + + if(filen == 1 && cfg.http_prepend_embedded_css) // Prepend Embedded CSS + { + CSS = tpl_getUnparsedTpl("CSS", 1, ""); + snprintf(separator, sizeof(separator), "\n/* Begin embedded CSS File: %s */\n", cfg.http_css); + } + + // We need at least size 1 or keepalive gets problems on some browsers... + if(st.st_size > 0) + { + if((fp = fopen(filename, "r")) == NULL) + return; + + if (CSS) + CSS_sz += cs_strlen(CSS); + + if(!cs_malloc(&allocated, st.st_size + CSS_sz + cs_strlen(separator) + 1)) + { + send_error500(f); + fclose(fp); + return; + } + + if((readen = fread(allocated + CSS_sz + cs_strlen(separator), 1, st.st_size, fp)) == st.st_size) + { + allocated[readen + CSS_sz + cs_strlen(separator)] = '\0'; + } + + fclose(fp); + } + + if(filen == 1 && cfg.http_prepend_embedded_css) // Prepend Embedded CSS + { + if (CSS && allocated) + { + memcpy(allocated, CSS, CSS_sz); + memcpy(allocated + CSS_sz, separator, cs_strlen(separator)); + allocated[readen + CSS_sz + cs_strlen(separator)] = '\0'; + } + } + + if(allocated) { result = allocated; } + } + else + { + CSS = tpl_getUnparsedTpl("CSS", 1, ""); + JSCRIPT = tpl_getUnparsedTpl("JSCRIPT", 1, ""); + JQUERY = tpl_getUnparsedTpl("JQUERY", 1, ""); + if(filen == 1 && cs_strlen(CSS) > 0){ result = CSS;} + else if(filen == 2 && cs_strlen(JSCRIPT) > 0){result = JSCRIPT;} + else if(filen == 3 && cs_strlen(JQUERY) > 0){result = JQUERY;} + moddate = first_client->login; + } + + size = cs_strlen(result); + + if((etagheader == 0 && moddate < modifiedheader) || (etagheader > 0 && (uint32_t)crc32(0L, (uint8_t *)result, size) == etagheader)) + { + send_header304(f, extraheader); + } + else + { + send_headers(f, 200, "OK", NULL, mimetype, 1, size, result, 0); + webif_write(result, f); + } + if(allocated) { NULLFREE(allocated); } + NULLFREE(CSS); + NULLFREE(JSCRIPT); + NULLFREE(JQUERY); +} + +/* Parse url parameters and save them to params array. The pch pointer is increased to the position where parsing stopped. */ +void parseParams(struct uriparams *params, char *pch) +{ + char *pch2; + // parsemode = 1 means parsing next param, parsemode = -1 parsing next + //value; pch2 points to the beginning of the currently parsed string, pch is the current position + int32_t parsemode = 1; + + pch2 = pch; + while(pch[0] != '\0') + { + if((parsemode == 1 && pch[0] == '=') || (parsemode == -1 && pch[0] == '&')) + { + pch[0] = '\0'; + urldecode(pch2); + if(parsemode == 1) + { + if(params->paramcount >= MAXGETPARAMS) { break; } + ++params->paramcount; + params->params[params->paramcount - 1] = pch2; + } + else + { + params->values[params->paramcount - 1] = pch2; + } + parsemode = -parsemode; + pch2 = pch + 1; + } + ++pch; + } + /* last value wasn't processed in the loop yet... */ + if(parsemode == -1 && params->paramcount <= MAXGETPARAMS) + { + urldecode(pch2); + params->values[params->paramcount - 1] = pch2; + } +} + +/* Returns the value of the parameter called name or an empty string if it doesn't exist. */ +char *getParam(struct uriparams *params, char *name) +{ + int32_t i; + for(i = (*params).paramcount - 1; i >= 0; --i) + { + if(strcmp((*params).params[i], name) == 0) { return (*params).values[i]; } + } + return ""; +} + +/* + * returns uptime in sec on success, -1 on error +*/ +int32_t oscam_get_uptime(void) +{ +#if defined(__linux__) + struct sysinfo uptime; + if(!sysinfo(&uptime)){ + return (int32_t)uptime.uptime; + } + else{ + return -1; + } +#elif defined(__APPLE__) + struct timeval boottime; + size_t len = sizeof(boottime); + int mib[2] = { CTL_KERN, KERN_BOOTTIME }; + if(sysctl(mib, 2, &boottime, &len, NULL, 0) < 0 ){ + return -1; + } + time_t bsec = boottime.tv_sec, csec = time(NULL); + + return difftime(csec, bsec); +#else + return -1; +#endif +} + +#if defined(__linux__) +/* + * read /proc data into the passed struct pstat + * returns 0 on success, -1 on error +*/ +int8_t get_stats_linux(const pid_t pid, struct pstat* result) +{ + // convert pid to string + char pid_s[20]; + snprintf(pid_s, sizeof(pid_s), "%d", pid); + char stat_filepath[30] = "/proc/"; + + if (!cs_strncat(stat_filepath, pid_s, sizeof(stat_filepath))) { + return -1; + } + + if (!cs_strncat(stat_filepath, "/stat", sizeof(stat_filepath))) { + return -1; + } + + FILE *f_pstat = fopen(stat_filepath, "r"); + if (f_pstat == NULL) { + cs_log("FOPEN ERROR %s",stat_filepath); + return -1; + } + + FILE *f_stat = fopen("/proc/stat", "r"); + if (!f_stat) { + fclose(f_pstat); + cs_log("ERROR: Can't open /proc/stat for reading: %s", strerror(errno)); + return -1; + } + + // read values from /proc/pid/stat + uint64_t rss; + if (fscanf(f_pstat, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %" SCNd64 + "%" SCNd64 "%" SCNd64 "%" SCNd64 "%*d %*d %*d %*d %*u %" SCNu64 "%" SCNu64, + &result->utime_ticks,&result->stime_ticks, + &result->cutime_ticks,&result->cstime_ticks,&result->vsize, + &rss) == EOF) + { + fclose(f_pstat); + fclose(f_stat); + return -1; + } + fclose(f_pstat); + result->rss = rss * getpagesize(); + + // read+calc cpu total time from /proc/stat + int64_t cpu_time[10] = {0,0,0,0,0,0,0,0,0,0}; + if (fscanf(f_stat, "%*s %" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64, + &cpu_time[0], &cpu_time[1], &cpu_time[2], &cpu_time[3], + &cpu_time[4], &cpu_time[5], &cpu_time[6], &cpu_time[7], + &cpu_time[8], &cpu_time[9]) == EOF) + { + fclose(f_stat); + return -1; + } + fclose(f_stat); + int8_t i; + result->cpu_total_time = 0; + // FIXME: On 32 Bit platforms, the single cpu times can overflow quite easily (clock_t from /proc/stat normally refers to a int32 here) resulting in useless calculation results! + for(i = 0; i < 10; i++) { + result->cpu_total_time += cpu_time[i]; + } + + // read cached from /proc/meminfo + uint64_t meminfo_cached = 0; + FILE *fd = fopen("/proc/meminfo", "r"); + if (fd ) { + char line[256]; + while(fgets(line, sizeof(line), fd)) { + if(sscanf(line, "Cached: %" PRId64" \n kB", &meminfo_cached) == 1){ + break; + } + } + fclose(fd); + } + + // read processes from /proc + uint info_procs = 0; + struct dirent **entries; + int n = scandir("/proc", &entries, NULL, NULL); + n = MAX(n, 0); + while(n--) + { + if (entries[n]->d_name[0] > '0' && entries[n]->d_name[0] <= '9') { info_procs++; } + free(entries[n]); + } + free(entries); + + // read cpu/meminfo from sysinfo() + struct sysinfo info; + float shiftfloat = (float)(1 << SI_LOAD_SHIFT); + if (!sysinfo(&info)) { + // processes + result->info_procs = info_procs; + // cpu load + result->cpu_avg[0] = (float) info.loads[0] / shiftfloat; + result->cpu_avg[1] = (float) info.loads[1] / shiftfloat; + result->cpu_avg[2] = (float) info.loads[2] / shiftfloat; + // meminfo + result->mem_total = info.totalram * info.mem_unit; + result->mem_free = info.freeram * info.mem_unit; + result->mem_used = result->mem_total - result->mem_free; + result->mem_buff = info.bufferram * info.mem_unit; + result->mem_cached = meminfo_cached * 1024; + result->mem_freem = result->mem_free + result->mem_buff + result->mem_cached; + result->mem_share = info.sharedram * info.mem_unit; + result->mem_total_swap = info.totalswap * info.mem_unit; + result->mem_free_swap = info.freeswap * info.mem_unit; + result->mem_used_swap = result->mem_total_swap - result->mem_free_swap; + } + + // set timestamp for function call + cs_ftime(&result->time_started); + + return 0; +} + +/* +* calculates the elapsed CPU usage between 2 measuring points. in percent and stores to cur_usage +*/ +void calc_cpu_usage_pct(struct pstat* cur_usage, struct pstat* last_usage) +{ + const double total_time_diff = cur_usage->cpu_total_time - last_usage->cpu_total_time; + + //time difference between cur_usage/last_usage when created / in sec + cur_usage->gone_refresh = comp_timeb(&cur_usage->time_started, &last_usage->time_started)/1000; + + if(cur_usage->gone_refresh < 1){ + //set to N/A since result may provide wrong results (/proc not updated) + cur_usage->check_available |= (1 << 9); + cur_usage->check_available |= (1 << 10); + cur_usage->check_available |= (1 << 11); + } + else{ + int64_t cur_ticks = cur_usage->utime_ticks + cur_usage->cutime_ticks; + int64_t last_ticks = last_usage->utime_ticks + last_usage->cutime_ticks; + //reset flags if set bevore + cur_usage->check_available &= ~(1 << 9); + cur_usage->check_available &= ~(1 << 10); + cur_usage->check_available &= ~(1 << 11); + + cur_usage->cpu_usage_user = 100.0 * llabs(cur_ticks - last_ticks) / total_time_diff; + + cur_ticks = cur_usage->stime_ticks + cur_usage->cstime_ticks; + last_ticks = last_usage->stime_ticks + last_usage->cstime_ticks; + + cur_usage->cpu_usage_sys = 100.0 * llabs(cur_ticks - last_ticks) / total_time_diff; + } +} +#endif + +#ifdef WITH_SSL +SSL *cur_ssl(void) +{ + return (SSL *) pthread_getspecific(getssl); +} + +/* Locking functions for SSL multithreading */ +struct CRYPTO_dynlock_value +{ + pthread_mutex_t mutex; +}; + +/* function really needs unsigned long to prevent compiler warnings... */ +unsigned long SSL_id_function(void) +{ + return ((unsigned long) pthread_self()); +} + +void SSL_locking_function(int32_t mode, int32_t type, const char *file, int32_t line) +{ + if(mode & CRYPTO_LOCK) + { + cs_writelock(__func__, &lock_cs[type]); + } + else + { + cs_writeunlock(__func__, &lock_cs[type]); + } + // just to remove compiler warnings... + if(file || line) { return; } +} + +struct CRYPTO_dynlock_value *SSL_dyn_create_function(const char *file, int32_t line) +{ + struct CRYPTO_dynlock_value *l; + if(!cs_malloc(&l, sizeof(struct CRYPTO_dynlock_value))) + { return NULL; } + + if(pthread_mutex_init(&l->mutex, NULL)) + { + // Initialization of mutex failed. + NULLFREE(l); + return (NULL); + } + + // just to remove compiler warnings... + if(file || line) { return l; } + return l; +} + +void SSL_dyn_lock_function(int32_t mode, struct CRYPTO_dynlock_value *l, const char *file, int32_t line) +{ + if(mode & CRYPTO_LOCK) + { + SAFE_MUTEX_LOCK(&l->mutex); + } + else + { + SAFE_MUTEX_UNLOCK(&l->mutex); + } + // just to remove compiler warnings... + if(file || line) { return; } +} + +void SSL_dyn_destroy_function(struct CRYPTO_dynlock_value *l, const char *file, int32_t line) +{ + pthread_mutex_destroy(&l->mutex); + NULLFREE(l); + // just to remove compiler warnings... + if(file || line) { return; } +} + +#if defined(OPENSSL_NO_EC) +#pragma message "WARNING: OpenSSL was built without support for elliptic curve cryptography (no-ec). Webserver certificate generation at runtime will not work!" +static bool create_certificate(const char *path) +{ + cs_log("generating webserver ssl certificate file %s (%s)", path, "SKIPPED"); + return false; +} +#else +/* add X.509 V3 extensions */ +static bool add_ext(X509 *cert, int nid, char *value) +{ + X509_EXTENSION *ex; + X509V3_CTX ctx; + + X509V3_set_ctx_nodb(&ctx); + X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0); + ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); + if (!ex) + return false; + + X509_add_ext(cert, ex, -1); + X509_EXTENSION_free(ex); + return true; +} + +/* Create a self-signed certificate for basic https webif usage */ +static bool create_certificate(const char *path) +{ + X509 *pcert = NULL; + X509_NAME * subject_name; + X509_NAME * issuer_name; + EVP_PKEY *pkey = NULL; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + RSA * rsa_key = NULL; +#elif OPENSSL_VERSION_NUMBER < 0x30000000L + EC_KEY *ec_key = NULL; +#endif + ASN1_INTEGER *asn1_serial_number; + BIGNUM *serial_number = NULL; + char san[256]; + struct utsname buffer; + bool ret = false; + + const char *cn = !uname(&buffer) ? buffer.nodename : "localhost"; + size_t cn_len = MIN(strlen(cn), 63); + + cs_log("generating webserver ssl certificate file %s (%s)", path, +#if OPENSSL_VERSION_NUMBER < 0x10100000L + "RSA" +#else + "ECDSA" +#endif + ); + if ((pkey = EVP_PKEY_new())) + { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + if (!(rsa_key = RSA_generate_key(4096, RSA_F4, NULL, NULL))) + { + goto err; + } + if (!EVP_PKEY_assign_RSA(pkey, rsa_key)) + { + goto err; + } + rsa_key = NULL; +#elif OPENSSL_VERSION_NUMBER < 0x30000000L + if (!(ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1))) //prime256v1 + { + goto err; + } + if (!EC_KEY_generate_key(ec_key)) + { + goto err; + } + if (!EVP_PKEY_assign_EC_KEY(pkey, ec_key)) + { + goto err; + } + ec_key = NULL; +#else + pkey = EVP_EC_gen(SN_X9_62_prime256v1); //prime256v1 +#endif + if ((pcert = X509_new())) + { + X509_set_pubkey(pcert, pkey); + // serialNumber + if (!X509_set_version(pcert, 2L)) + { + goto err; + } + + // serialNumber + if ((serial_number = BN_new())) + { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + if (!BN_pseudo_rand(serial_number, 64, 0, 0)) + { + goto err; + } +#else + if (!BN_rand(serial_number, 64, 0, 0)) + { + goto err; + } +#endif + asn1_serial_number = X509_get_serialNumber(pcert); + if (!asn1_serial_number) + { + goto err; + } + if (!BN_to_ASN1_INTEGER(serial_number, asn1_serial_number)) + { + goto err; + } + + // subject + issuer + if ((subject_name = X509_NAME_new()) && (issuer_name = X509_NAME_new())) + { + if (!X509_NAME_add_entry_by_NID(subject_name, NID_commonName, MBSTRING_UTF8, (unsigned char *) cn, cn_len, -1, 0)) + { + goto err; + } + if (!X509_set_subject_name(pcert, subject_name)) + { + goto err; + } + + if (!X509_NAME_add_entry_by_NID(issuer_name, NID_commonName, MBSTRING_UTF8, (unsigned char *) cn, cn_len, -1, 0)) + { + goto err; + } + if (!X509_set_issuer_name(pcert, issuer_name)) + { + goto err; + } + + // expiration + X509_gmtime_adj(X509_getm_notBefore(pcert), 0); + X509_gmtime_adj(X509_getm_notAfter(pcert), CERT_EXPIRY_TIME); + + // X.509 V3 extensions + add_ext(pcert, NID_basic_constraints, "CA:FALSE" ); + add_ext(pcert, NID_key_usage, "nonRepudiation, digitalSignature, keyEncipherment" ); + add_ext(pcert, NID_ext_key_usage, "clientAuth, serverAuth" ); + snprintf(san, sizeof(san), "DNS:%s, DNS:%s.local, IP:127.0.0.1, IP:::1", cn, cn); + add_ext(pcert, NID_subject_alt_name, san); + + // sign certificate with private key + X509_sign(pcert, pkey, EVP_sha256()); + + // write private key and certificate to file + FILE * pemfile; + if ((pemfile = fopen(path, "w"))) + { + PEM_write_PrivateKey(pemfile, pkey, NULL, NULL, 0, NULL, NULL); + PEM_write_X509(pemfile, pcert); + fclose(pemfile); + ret = true; + } + else + { + cs_log("can't write to file %s", path); + goto err; + } + } + else + { + cs_log("Error: X509_NAME_new() failed"); + } + } + else + { + cs_log("Error: BN_new() failed"); + } + } + else + { + cs_log("Error: X509_new() failed"); + } + } + else + { + cs_log("Error: EVP_PKEY_new() failed"); + } + +err: + ERR_print_errors_fp(stderr); + if (pkey) + { + EVP_PKEY_free(pkey); + } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && OPENSSL_VERSION_NUMBER < 0x30000000L + if (ec_key) + { + EC_KEY_free(ec_key); + } +#endif + if (pcert) + { + X509_free(pcert); + } + if (serial_number) + { + BN_free(serial_number); + } + + return ret; +} +#endif + +/* Init necessary structures for SSL in WebIf*/ +SSL_CTX *SSL_Webif_Init(void) +{ + SSL_CTX *ctx; + + static const char *cs_cert = "oscam.pem"; + + // set locking callbacks for SSL + int32_t i, num = CRYPTO_num_locks(); + lock_cs = (CS_MUTEX_LOCK *) OPENSSL_malloc(num * sizeof(CS_MUTEX_LOCK)); + + for(i = 0; i < num; ++i) + { + cs_lock_create(__func__, &lock_cs[i], "ssl_lock_cs", 10000); + } + /* static lock callbacks */ + CRYPTO_set_id_callback(SSL_id_function); + CRYPTO_set_locking_callback(SSL_locking_function); + /* dynamic lock callbacks */ + CRYPTO_set_dynlock_create_callback(SSL_dyn_create_function); + CRYPTO_set_dynlock_lock_callback(SSL_dyn_lock_function); + CRYPTO_set_dynlock_destroy_callback(SSL_dyn_destroy_function); + + ctx = SSL_CTX_new(SSLv23_server_method()); + +#if defined(SSL_CTX_set_ecdh_auto) + (void) SSL_CTX_set_ecdh_auto(ctx, 1); +#elif defined(EC_PKEY_NO_PARAMETERS) && defined(NID_X9_62_prime256v1) + EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if(ecdh) + { + SSL_CTX_set_tmp_ecdh(ctx, ecdh); + EC_KEY_free(ecdh); + } +#endif + + if(cfg.https_force_secure_mode) + { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined SSL_CTX_clear_options // makro removed in OpenSSL 1.1.0+ + SSL_CTX_clear_options(ctx, SSL_OP_ALL); //we CLEAR all bug workarounds! This is for security reason +#else + cs_log("WARNING: You enabled to force secure HTTPS but your system does not support to clear the ssl workarounds! SSL security will be reduced!"); +#endif + } + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION); +#elif defined SSL_OP_NO_TLSv1_1 + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); +#else + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); +#endif + + char path[128]; + + if(!cfg.http_cert) + { get_config_filename(path, sizeof(path), cs_cert); } + else + { cs_strncpy(path, cfg.http_cert, sizeof(path)); } + + if(!file_exists(path) && cfg.https_auto_create_cert) //generate a ready-to-use SSL certificate if no certificate file is available + { + if(!create_certificate(path)) + { + goto out_err; + } + } + + if(!ctx) + goto out_err; + + if(SSL_CTX_use_certificate_chain_file(ctx, path) <=0) + goto out_err; + + if(SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM) <= 0) + goto out_err; + + if(!SSL_CTX_check_private_key(ctx)) + { + cs_log("SSL: Private key does not match the certificate public key"); + goto out_err; + } + + cs_log("loading ssl certificate file %s", path); + return ctx; + +out_err: + ERR_print_errors_fp(stderr); +#if OPENSSL_VERSION_NUMBER < 0x1010005fL + // fix build "OpenSSL 1.1.0e 16 Feb 2017" + ERR_remove_state(0); +#endif + SSL_CTX_free(ctx); + return NULL; +} +#endif + +#endif diff --git a/module-webif-lib.h b/module-webif-lib.h new file mode 100644 index 0000000..600ce02 --- /dev/null +++ b/module-webif-lib.h @@ -0,0 +1,119 @@ +#ifndef MODULE_WEBIF_LIB_H_ +#define MODULE_WEBIF_LIB_H_ + +#ifdef WITH_SSL +#include +#include +#include +#include +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#define X509_getm_notBefore X509_get_notBefore +#define X509_getm_notAfter X509_get_notAfter +#endif +#endif + +#include "cscrypt/md5.h" + +/* The server string in the http header */ +#define SERVER "webserver/1.0" +/* The protocol that gets output. Currently only 1.0 is possible as 1.1 requires many features we don't have. */ +#define PROTOCOL "HTTP/1.0" +/* The RFC1123 time format which is used in http headers. */ +#define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT" +/* The realm for http digest authentication. Gets displayed to browser. */ +#define AUTHREALM "Forbidden" +/* How long a nonce is valid in seconds after a first request with this nonce has been received. If the nonce isn't valid anymore, the browser gets a "stale=true" message and must resubmit with the current nonce. */ +#define AUTHNONCEVALIDSECS 15 +/* When a nonce gets expired after it has been first given to the client. */ +#define AUTHNONCEEXPIRATION 120 +/* The amount of hash buckets (based on opaque string) for better performance. */ +#define AUTHNONCEHASHBUCKETS 4 +/* The maximum amount of GET parameters the webserver will parse. */ +#define MAXGETPARAMS 300 +/* The refresh delay (in seconds) when stopping OSCam via http. */ +#define SHUTDOWNREFRESH 30 +/* The expiry of the certificate; 365 days */ +#define CERT_EXPIRY_TIME (60*60*24*365) + +struct s_connection +{ + int32_t socket; + struct s_client *cl; + IN_ADDR_T remote; +#ifdef WITH_SSL + SSL *ssl; +#endif +}; + +struct uriparams +{ + int32_t paramcount; + char *params[MAXGETPARAMS]; + char *values[MAXGETPARAMS]; +}; + +struct s_nonce +{ + char nonce[(MD5_DIGEST_LENGTH * 2) + 1]; + char opaque[(MD5_DIGEST_LENGTH * 2) + 1]; + time_t expirationdate; + time_t firstuse; + struct s_nonce *next; +}; + +// should be filled with informations for stats block +struct pstat +{ + uint32_t info_procs; // running procs + int64_t utime_ticks; + int64_t cutime_ticks; + int64_t stime_ticks; + int64_t cstime_ticks; + int64_t cpu_total_time; + uint64_t vsize; // virtual memory size in bytes + uint64_t rss; // Resident Set Size in bytes + uint64_t mem_total; // Total Memory in bytes + uint64_t mem_free; // Free Memory in bytes + uint64_t mem_used; // Used Memory in bytes + uint64_t mem_buff; // Buffered Memory in bytes + uint64_t mem_cached; // Cached Memory in bytes + uint64_t mem_freem; // Buffered Memory in bytes + uint64_t mem_share; // Shared Memory in bytes + uint64_t mem_total_swap; // Total Swap Memory in bytes + uint64_t mem_free_swap; // Free Swap Memory in bytes + uint64_t mem_used_swap; // Used Swap Memory in bytes + float cpu_avg[3]; // CPU load from "load average" + struct timeb time_started; // needed for calculating time between function call + int64_t gone_refresh; // time difference between CPU usage calculations in sec + double cpu_usage_user; // user_CPU usage to display in % + double cpu_usage_sys; // sys_CPU usage to display in % + uint16_t check_available; // default is 0, if value x is not available, + // set corresponding bit to 1 --> module-webif.c / set_status_info() +}; + +extern time_t parse_modifiedsince(char *value); +extern void calculate_opaque(IN_ADDR_T addr, char *opaque); +extern void init_noncelocks(void); +extern void calculate_nonce(char *nonce, char *result, char *opaque); +extern int32_t check_auth(char *authstring, char *method, char *path, IN_ADDR_T addr, char *expectednonce, char *opaque); +extern int32_t webif_write_raw(char *buf, FILE *f, int32_t len); +extern int32_t webif_write(char *buf, FILE *f); +extern int32_t webif_read(char *buf, int32_t num, FILE *f); +extern void send_headers(FILE *f, int32_t status, char *title, char *extra, char *mime, int32_t cache, int32_t length, char *content, int8_t forcePlain); +extern void send_error(FILE *f, int32_t status, char *title, char *extra, char *text, int8_t forcePlain); +extern void send_error500(FILE *f); +extern void send_header304(FILE *f, char *extraheader); +extern void send_file(FILE *f, char *filename, char *subdir, time_t modifiedheader, uint32_t etagheader, char *extraheader); +extern void urldecode(char *s); +extern void parseParams(struct uriparams *params, char *pch); +extern char *getParam(struct uriparams *params, char *name); +extern int32_t oscam_get_uptime(void); +extern int8_t get_stats_linux(const pid_t pid, struct pstat* result); +extern void calc_cpu_usage_pct(struct pstat* cur_usage, struct pstat* last_usage); + +#ifdef WITH_SSL +extern SSL *cur_ssl(void); +extern SSL_CTX *SSL_Webif_Init(void); +#endif + +#endif diff --git a/module-webif-tpl.c b/module-webif-tpl.c new file mode 100644 index 0000000..bd13862 --- /dev/null +++ b/module-webif-tpl.c @@ -0,0 +1,892 @@ +#define MODULE_LOG_PREFIX "webif" + +#include "globals.h" + +#ifdef WEBIF +#include "webif/pages.h" +#include "module-webif-tpl.h" +#include "oscam-files.h" +#include "oscam-string.h" +#ifdef COMPRESSED_TEMPLATES +#include "minilzo/minilzo.h" +#endif + +/* struct template templates[] that comes from webif/pages.c is recreated as + struct tpl tpls[] because we need to add additional fields such as tpl_name_hash + and possibly preprocess templates[] struct before using it. */ + +struct tpl +{ + uint32_t tpl_name_hash; + const char *tpl_name; + const char *tpl_data; + const char *tpl_deps; + char *extra_data; + uint32_t tpl_data_len; + uint8_t tpl_type; +}; + +static struct tpl *tpls; +static char *tpls_data; +static int tpls_count; + +static void tpl_init_base64(struct tpl *tpl) +{ + // The rest of OSCam expects images to be base64 encoded and contain mime type. + if(!template_is_image(tpl->tpl_type)) + { return; } + size_t b64_buf_len = 32 + BASE64_LENGTH(tpl->tpl_data_len); // Enough for base64 and 32 for header (data:XXX;base64,) + char *b64_buf; + if(!cs_malloc(&b64_buf, b64_buf_len)) + { + tpl->tpl_data = ""; + tpl->tpl_data_len = 0; + return; + } + int hdr_len = snprintf(b64_buf, b64_buf_len, "data:%s;base64,", template_get_mimetype(tpl->tpl_type)); + base64_encode(tpl->tpl_data, tpl->tpl_data_len, b64_buf + hdr_len, b64_buf_len - hdr_len); + tpl->tpl_data = tpl->extra_data = b64_buf; + tpl->tpl_data_len = cs_strlen(b64_buf); +} + +void webif_tpls_prepare(void) +{ + int i; + const struct template *templates = templates_get(); + tpls_count = templates_count(); + if(!cs_malloc(&tpls, tpls_count * sizeof(struct tpl))) + { + tpls_count = 0; + return; + } +#ifdef COMPRESSED_TEMPLATES + const char *templates_cdata; + size_t tpls_data_len, tpls_data_olen; + templates_get_data(&templates_cdata, &tpls_data_len, &tpls_data_olen); + if(!cs_malloc(&tpls_data, tpls_data_olen)) + { + tpls_count = 0; + return; + } + + lzo_uint new_len = tpls_data_olen; + int r = lzo1x_decompress_safe((uint8_t *)templates_cdata, tpls_data_len, (uint8_t *)tpls_data, &new_len, NULL); + if(r == LZO_E_OK && new_len == tpls_data_olen) + { + cs_log("webif: decompressed %zu bytes back into %zu bytes", tpls_data_len, tpls_data_olen); + } + else + { + /* this should NEVER happen */ + cs_log("internal error - decompression failed: %d\n", r); + NULLFREE(tpls); + tpls_count = 0; + } + + for(i = 0; i < tpls_count; ++i) + { + tpls[i].tpl_name = tpls_data + templates[i].tpl_name_ofs; + tpls[i].tpl_data = tpls_data + templates[i].tpl_data_ofs; + tpls[i].tpl_deps = tpls_data + templates[i].tpl_deps_ofs; + tpls[i].tpl_data_len = templates[i].tpl_data_len; + tpls[i].tpl_type = templates[i].tpl_type; + tpls[i].tpl_name_hash = jhash(tpls[i].tpl_name, cs_strlen(tpls[i].tpl_name)); + tpl_init_base64(&tpls[i]); + } +#else + for(i = 0; i < tpls_count; ++i) + { + tpls[i].tpl_name_hash = jhash(templates[i].tpl_name, cs_strlen(templates[i].tpl_name)); + tpls[i].tpl_name = templates[i].tpl_name; + tpls[i].tpl_data = templates[i].tpl_data; + tpls[i].tpl_deps = templates[i].tpl_deps; + tpls[i].tpl_data_len = templates[i].tpl_data_len; + tpls[i].tpl_type = templates[i].tpl_type; + tpl_init_base64(&tpls[i]); + } +#endif +} + +void webif_tpls_free(void) +{ + int32_t i, tmp; + + tmp = tpls_count; + tpls_count = 0; + + for(i = 0; i < tmp; ++i) + { + NULLFREE(tpls[i].extra_data); + } + NULLFREE(tpls_data); + NULLFREE(tpls); +} + +/* Adds a name->value-mapping or appends to it. You will get a reference back which you may freely + use (but you should not call free/realloc on this!)*/ +void tpl_addVar(struct templatevars *vars, uint8_t addmode, const char *name, const char *value) +{ + if(name == NULL) { return; } + if(value == NULL) { value = ""; } + int32_t i; + char *tmp = NULL, *result = NULL; + for(i = (*vars).varscnt - 1; i >= 0; --i) + { + if(strcmp((*vars).names[i], name) == 0) + { + result = (*vars).values[i]; + break; + } + } + if(result == NULL) + { + if((*vars).varsalloc <= (*vars).varscnt) + { + if(!cs_realloc(&(*vars).names, (*vars).varsalloc * 2 * sizeof(char **))) { return; } + if(!cs_realloc(&(*vars).values, (*vars).varsalloc * 2 * sizeof(char **))) { return; } + if(!cs_realloc(&(*vars).vartypes, (*vars).varsalloc * 2 * sizeof(uint8_t *))) { return; } + (*vars).varsalloc = (*vars).varscnt * 2; + } + int32_t len = cs_strlen(name) + 1; + if(!cs_malloc(&tmp, len)) { return; } + memcpy(tmp, name, len); + (*vars).names[(*vars).varscnt] = tmp; + len = cs_strlen(value) + 1; + if(!cs_malloc(&tmp, len)) + { + NULLFREE((*vars).names[(*vars).varscnt]); + return; + } + memcpy(tmp, value, len); + (*vars).values[(*vars).varscnt] = tmp; + (*vars).vartypes[(*vars).varscnt] = addmode; + (*vars).varscnt++; + } + else + { + int32_t oldlen = 0, newlen = cs_strlen(value); + if(addmode == TPLAPPEND || addmode == TPLAPPENDONCE) { oldlen = cs_strlen((*vars).values[i]); } + if(!cs_realloc(&((*vars).values[i]), oldlen + newlen + 1)) { return; } + memcpy((*vars).values[i] + oldlen, value, newlen + 1); + (*vars).vartypes[i] = addmode; + } + return; +} + +/* Adds a message to be output on the page using the TPLMESSAGE template. */ +void tpl_addMsg(struct templatevars *vars, const char *value) +{ + tpl_addVar(vars, TPLADDONCE, "MESSAGE", value); + (*vars).messages++; + tpl_addVar(vars, TPLAPPEND, "MESSAGES", tpl_getTpl(vars, "MESSAGEBIT")); +} + +/* Allows to add a char array which has been allocated by malloc. It will automatically get + freed when calling tpl_clear(). Please do NOT free the memory yourself or realloc + it after having added the array here! */ +static char *tpl_addTmp(struct templatevars *vars, char *value) +{ + if(value == NULL) { return ""; } + if((*vars).tmpalloc <= (*vars).tmpcnt) + { + if(!cs_realloc(&(*vars).tmp, (*vars).tmpalloc * 2 * sizeof(char **))) { return value; } + (*vars).tmpalloc = (*vars).tmpcnt * 2; + } + (*vars).tmp[(*vars).tmpcnt] = value; + (*vars).tmpcnt++; + return value; +} + +/* Allows to do a dynamic printf without knowing and defining the needed memory size. If you specify + varname, the printf-result will be added/appended to the varlist, if varname=NULL it will only be returned. + In either case you will always get a reference back which you may freely use (but you should not call + free/realloc on this as it will be automatically cleaned!)*/ +void tpl_printf(struct templatevars *vars, uint8_t addmode, const char *varname, const char *fmtstring, ...) +{ + uint32_t needed; + char test[1]; + va_list argptr; + + va_start(argptr, fmtstring); + needed = vsnprintf(test, 1, fmtstring, argptr); + va_end(argptr); + + char *result; + if(!cs_malloc(&result, needed + 1)) { return; } + va_start(argptr, fmtstring); + vsnprintf(result, needed + 1, fmtstring, argptr); + va_end(argptr); + + if(varname == NULL) { tpl_addTmp(vars, result); } + else + { + tpl_addVar(vars, addmode, varname, result); + free(result); + } + return; +} + +/* Returns the value for a name or an empty string if nothing was found. */ +char *tpl_getVar(struct templatevars *vars, const char *name) +{ + int32_t i; + char *result = NULL; + for(i = (*vars).varscnt - 1; i >= 0; --i) + { + if(strcmp((*vars).names[i], name) == 0) + { + result = (*vars).values[i]; + break; + } + } + if(result == NULL) { return ""; } + else + { + if((*vars).vartypes[i] == TPLADDONCE || (*vars).vartypes[i] == TPLAPPENDONCE) + { + // This is a one-time-use variable which gets cleaned up automatically after retrieving it + if(!cs_malloc(&(*vars).values[i], 1)) + { + (*vars).values[i] = result; + result[0] = '\0'; + return result; + } + else + { + (*vars).values[i][0] = '\0'; + return tpl_addTmp(vars, result); + } + } + else { return result; } + } +} + +/* Initializes all variables for a templatevar-structure and returns a pointer to it. Make + sure to call tpl_clear() when you are finished or you'll run into a memory leak! */ +struct templatevars *tpl_create(void) +{ + struct templatevars *vars; + if(!cs_malloc(&vars, sizeof(struct templatevars))) { return NULL; } + (*vars).varsalloc = 64; + (*vars).varscnt = 0; + (*vars).tmpalloc = 64; + (*vars).tmpcnt = 0; + if(!cs_malloc(&(*vars).names, (*vars).varsalloc * sizeof(char **))) + { + NULLFREE(vars); + return NULL; + } + if(!cs_malloc(&(*vars).values, (*vars).varsalloc * sizeof(char **))) + { + NULLFREE((*vars).names); + NULLFREE(vars); + return NULL; + } + if(!cs_malloc(&(*vars).vartypes, (*vars).varsalloc * sizeof(uint8_t *))) + { + NULLFREE((*vars).names); + NULLFREE((*vars).values); + NULLFREE(vars); + return NULL; + } + if(!cs_malloc(&(*vars).tmp, (*vars).tmpalloc * sizeof(char **))) + { + NULLFREE((*vars).names); + NULLFREE((*vars).values); + NULLFREE((*vars).vartypes); + NULLFREE(vars); + return NULL; + } + return vars; +} + +/* Clears all allocated memory for the specified templatevar-structure. */ +void tpl_clear(struct templatevars *vars) +{ + int32_t i; + for(i = (*vars).varscnt - 1; i >= 0; --i) + { + NULLFREE((*vars).names[i]); + NULLFREE((*vars).values[i]); + } + NULLFREE((*vars).names); + NULLFREE((*vars).values); + NULLFREE((*vars).vartypes); + for(i = (*vars).tmpcnt - 1; i >= 0; --i) + { + NULLFREE((*vars).tmp[i]); + } + NULLFREE((*vars).tmp); + NULLFREE(vars); +} + +/* Creates a path to a template file. You need to set the resultsize to the correct size of result. */ +char *tpl_getFilePathInSubdir(const char *path, const char *subdir, const char *name, const char *ext, char *result, uint32_t resultsize) +{ + int path_len = cs_strlen(path); + const char *path_fixup = ""; + if(path_len && path[path_len - 1] != '/') + { path_fixup = "/"; } + if(path_len + cs_strlen(path_fixup) + cs_strlen(name) + cs_strlen(subdir) + cs_strlen(ext) < resultsize) + { + snprintf(result, resultsize, "%s%s%s%s%s", path, path_fixup, subdir, name, ext); + } + else { result[0] = '\0'; } + return result; +} + +char *tpl_getTplPath(const char *name, const char *path, char *result, uint32_t resultsize) +{ + return tpl_getFilePathInSubdir(path, "", name, ".tpl", result, resultsize); +} + +#define check_conf(CONFIG_VAR, text) \ + if (config_enabled(CONFIG_VAR) && strncmp(#CONFIG_VAR, text, len) == 0) { ok = 1; break; } + +/* Returns an unparsed template either from disk or from internal templates. + Note: You must free() the result after using it and you may get NULL if an error occured!*/ +char *tpl_getUnparsedTpl(const char *name, int8_t removeHeader, const char *subdir) +{ + int32_t i; + char *result; + char *tpl_path; + + tpl_path = (cfg.http_piconpath && cs_strlen(name) > 3 && name[0] == 'I' && name[1] == 'C' && name[2] == '_') ? cfg.http_piconpath : cfg.http_tpl; + + if(tpl_path) + { + char path[255]; + if((cs_strlen(tpl_getFilePathInSubdir(tpl_path, subdir, name, ".tpl", path, 255)) > 0 && file_exists(path)) + || (cs_strlen(subdir) > 0 + && cs_strlen(tpl_getFilePathInSubdir(tpl_path, "" , name, ".tpl", path, 255)) > 0 && file_exists(path))) + { + FILE *fp; + char buffer[1025]; + memset(buffer, 0, sizeof(buffer)); + int32_t readen, allocated = 1025, offset, size = 0; + if(!cs_malloc(&result, allocated)) { return NULL; } + if((fp = fopen(path, "r")) != NULL) + { + // Use as read size sizeof(buffer) - 1 to ensure that buffer is + // zero terminated otherwise strstr can segfault! + while((readen = fread(buffer, 1, sizeof(buffer) - 1, fp)) > 0) + { + offset = 0; + if(size == 0 && removeHeader) + { + /* Remove version string from output and check if it is valid for output */ + char *pch1 = strstr(buffer, ""); + if(pch2 != NULL) + { + offset = pch2 - buffer + 4; + readen -= offset; + pch2[0] = '\0'; + char *ptr1, *ptr2, *saveptr1 = NULL, *saveptr2 = NULL; + for(i = 0, ptr1 = strtok_r(pch1 + 10, ";", &saveptr1); (ptr1) && i < 4 ; ptr1 = strtok_r(NULL, ";", &saveptr1), i++) + { + if(i == 3 && cs_strlen(ptr1) > 2) + { + int8_t ok = 0; + for(ptr2 = strtok_r(ptr1, ",", &saveptr2); (ptr2) && ok == 0 ; ptr2 = strtok_r(NULL, ",", &saveptr2)) + { + size_t len = cs_strlen(ptr2); + check_conf(WITH_CARDREADER, ptr2); + check_conf(CARDREADER_PHOENIX, ptr2); + check_conf(CARDREADER_DRECAS, ptr2); + check_conf(CARDREADER_INTERNAL_AZBOX, ptr2); + check_conf(CARDREADER_INTERNAL_COOLAPI, ptr2); + check_conf(CARDREADER_INTERNAL_AMSMC, ptr2); + check_conf(CARDREADER_INTERNAL_SCI, ptr2); + check_conf(CARDREADER_SC8IN1, ptr2); + check_conf(CARDREADER_MP35, ptr2); + check_conf(CARDREADER_SMARGO, ptr2); + check_conf(CARDREADER_PCSC, ptr2); + check_conf(CARDREADER_SMART, ptr2); + check_conf(CARDREADER_DB2COM, ptr2); + check_conf(CARDREADER_STAPI, ptr2); + check_conf(CARDREADER_STAPI5, ptr2); + check_conf(WEBIF_LIVELOG, ptr2); + check_conf(WEBIF_JQUERY, ptr2); + check_conf(WITH_COMPRESS_WEBIF, ptr2); + check_conf(CS_ANTICASC, ptr2); + check_conf(CS_CACHEEX, ptr2); + check_conf(CS_CACHEEX_AIO, ptr2); + check_conf(HAVE_DVBAPI, ptr2); + check_conf(WITH_EXTENDED_CW, ptr2); + check_conf(WITH_NEUTRINO, ptr2); + check_conf(READ_SDT_CHARSETS, ptr2); + check_conf(CLOCKFIX, ptr2); + check_conf(IPV6SUPPORT, ptr2); + check_conf(LCDSUPPORT, ptr2); + check_conf(LEDSUPPORT, ptr2); + check_conf(MODULE_CAMD33, ptr2); + check_conf(MODULE_CAMD35, ptr2); + check_conf(MODULE_CAMD35_TCP, ptr2); + check_conf(MODULE_CCCAM, ptr2); + check_conf(MODULE_CCCSHARE, ptr2); + check_conf(MODULE_CONSTCW, ptr2); + check_conf(MODULE_GBOX, ptr2); + check_conf(MODULE_GHTTP, ptr2); + check_conf(MODULE_MONITOR, ptr2); + check_conf(MODULE_NEWCAMD, ptr2); + check_conf(MODULE_PANDORA, ptr2); + check_conf(MODULE_RADEGAST, ptr2); + check_conf(MODULE_SERIAL, ptr2); + check_conf(MODULE_CW_CYCLE_CHECK, ptr2); + check_conf(READER_BULCRYPT, ptr2); + check_conf(READER_CONAX, ptr2); + check_conf(READER_CRYPTOWORKS, ptr2); + check_conf(READER_GRIFFIN, ptr2); + check_conf(READER_DGCRYPT, ptr2); + check_conf(READER_DRE, ptr2); + check_conf(READER_IRDETO, ptr2); + check_conf(READER_NAGRA, ptr2); + check_conf(READER_NAGRA_MERLIN, ptr2); + check_conf(READER_SECA, ptr2); + check_conf(READER_TONGFANG, ptr2); + check_conf(READER_VIACCESS, ptr2); + check_conf(READER_VIDEOGUARD, ptr2); + check_conf(WITH_CARDREADER, ptr2); + check_conf(WITH_DEBUG, ptr2); + check_conf(WITH_LB, ptr2); + check_conf(WITH_LIBCRYPTO, ptr2); + check_conf(WITH_SSL, ptr2); + check_conf(WITH_STAPI, ptr2); + check_conf(WITH_STAPI5, ptr2); + check_conf(WITH_EMU, ptr2); + } // for + if(ok == 0) + { + fclose(fp); + return result; + } + break; + } // if + } // for + } // if + } // if + } // if + if(allocated < size + readen + 1) + { + allocated += size + 1024; + if(!cs_realloc(&result, allocated)) + { + fclose(fp); + return NULL; + } + } + memcpy(result + size, buffer + offset, readen); + size += readen; + } // while + result[size] = '\0'; + fclose(fp); + return result; + } // if + } // if + } // if + + bool found = 0; + uint32_t name_hash = jhash(name, cs_strlen(name)); + for(i = 0; i < tpls_count; i++) + { + if(tpls[i].tpl_name_hash == name_hash) + { + found = 1; + break; + } + } + + if(found) + { + const struct tpl *tpl = &tpls[i]; + if(!cs_malloc(&result, tpl->tpl_data_len + 1)) { return NULL; } // +1 to accomodate \0 at the end + memcpy(result, tpl->tpl_data, tpl->tpl_data_len); + } + else + { + if(!cs_malloc(&result, 1)) { return NULL; } // Return empty string + } + return result; +} + +/* Returns the specified template with all variables/other templates replaced or an + empty string if the template doesn't exist. Do not free the result yourself, it + will get automatically cleaned up! */ +char *tpl_getTpl(struct templatevars *vars, const char *name) +{ + char *tplorg = tpl_getUnparsedTpl(name, 1, tpl_getVar(vars, "SUBDIR")); + if(!tplorg) { return ""; } + char *tplend = tplorg + cs_strlen(tplorg); + char *pch, *pch2, *tpl = tplorg; + char varname[33]; + + int32_t tmp, respos = 0; + int32_t allocated = 2 * cs_strlen(tpl) + 1; + char *result; + if(!cs_malloc(&result, allocated)) { return ""; } + + while(tpl < tplend) + { + if(tpl[0] == '#' && tpl[1] == '#' && tpl[2] != '#') + { + pch2 = tpl; + pch = tpl + 2; + while(pch[0] != '\0' && (pch[0] != '#' || pch[1] != '#')) { ++pch; } + if(pch - pch2 < 32 && pch[0] == '#' && pch[1] == '#') + { + memcpy(varname, pch2 + 2, pch - pch2 - 2); + varname[pch - pch2 - 2] = '\0'; + if(strncmp(varname, "TPL", 3) == 0) + { + if((*vars).messages > 0 || strncmp(varname, "TPLMESSAGE", 10) != 0) + { pch2 = tpl_getTpl(vars, varname + 3); } + else { pch2 = ""; } + } + else + { + pch2 = tpl_getVar(vars, varname); + } + tmp = cs_strlen(pch2); + if(tmp + respos + 2 >= allocated) + { + allocated = tmp + respos + 256; + if(!cs_realloc(&result, allocated)) { return ""; } + } + memcpy(result + respos, pch2, tmp); + respos += tmp; + tpl = pch + 2; + } + } + else + { + if(respos + 2 >= allocated) + { + allocated = respos + 256; + if(!cs_realloc(&result, allocated)) { return ""; } + } + result[respos] = tpl[0]; + ++respos; + ++tpl; + } + } + NULLFREE(tplorg); + result[respos] = '\0'; + tpl_addTmp(vars, result); + return result; +} + +/* Saves all templates to the specified paths. Existing files will be overwritten! */ +int32_t tpl_saveIncludedTpls(const char *path) +{ + int32_t i, cnt = 0; + char tmp[256]; + FILE *fp; + for(i = 0; i < tpls_count; ++i) + { + const struct tpl *tpl = &tpls[i]; + if(cs_strlen(tpl_getTplPath(tpl->tpl_name, path, tmp, 256)) > 0 && (fp = fopen(tmp, "w")) != NULL) + { + if(strncmp(tpl->tpl_name, "IC", 2) != 0) + { + fprintf(fp, "\n", crc32(0, (uint8_t *)tpl->tpl_data, tpl->tpl_data_len), CS_VERSION, tpl->tpl_deps); + } + fwrite(tpl->tpl_data, tpl->tpl_data_len, 1, fp); + fclose(fp); + ++cnt; + } + } + return cnt; +} + +/* Checks all disk templates in a directory if they are still current or may need upgrade! */ +void tpl_checkOneDirDiskRevisions(const char *subdir) +{ + char dirpath[255] = "\0"; + snprintf(dirpath, 255, "%s%s", cfg.http_tpl ? cfg.http_tpl : "", subdir); + int32_t i; + char path[255]; + for(i = 0; i < tpls_count; ++i) + { + const struct tpl *tpl = &tpls[i]; + if(strncmp(tpl->tpl_name, "IC", 2) != 0 && cs_strlen(tpl_getTplPath(tpl->tpl_name, dirpath, path, 255)) > 0 && file_exists(path)) + { + int8_t error = 1; + char *tplorg = tpl_getUnparsedTpl(tpl->tpl_name, 0, subdir); + unsigned long checksum = 0, curchecksum = crc32(0L, (uint8_t *)tpl->tpl_data, tpl->tpl_data_len); + char *ifdefs = "", *pch1 = strstr(tplorg, ""); + if(pch2 != NULL) + { + pch2[0] = '\0'; + int32_t j; + char *ptr1, *saveptr1 = NULL; + for(j = 0, ptr1 = strtok_r(pch1 + 10, ";", &saveptr1); (ptr1) && j < 4 ; ptr1 = strtok_r(NULL, ";", &saveptr1), j++) + { + if(j == 0) { checksum = strtoul(ptr1, NULL, 10); } + else if(j == 1) { version = ptr1; } + else if(j == 2) { revision = ptr1; } + else if(j == 3) { ifdefs = ptr1; } + } + } + if(checksum != curchecksum) + { + cs_log("WARNING: Your http disk template %s was created for an older revision of OSCam and was changed in original OSCam (%s,r%s). Please consider upgrading it!", path, version, revision); + } + else { error = 0; } + } + else { cs_log("WARNING: Your http disk template %s is in the old template format without revision info. Please consider upgrading it!", path); } + if(error) { cs_log("If you are sure that it is current, add the following line at the beginning of the template to suppress this warning: ", curchecksum, CS_VERSION, ifdefs); } + NULLFREE(tplorg); + } + } +} + +/* Checks whether disk templates need upgrade - including sub-directories */ +void tpl_checkDiskRevisions(void) +{ + char subdir[255]; + char dirpath[255]; + int n; + if(cfg.http_tpl) + { + tpl_checkOneDirDiskRevisions(""); + struct stat s; + struct dirent **entries; + n = scandir(cfg.http_tpl, &entries, NULL, NULL); + if(n==-1) + return; + while(n--) + { + if(strcmp(".", entries[n]->d_name) == 0 || strcmp("..", entries[n]->d_name) == 0) + { + free(entries[n]); + continue; + } + snprintf(dirpath, 255, "%.31s%.31s", cfg.http_tpl, entries[n]->d_name); + if(stat(dirpath, &s) == 0) + { + if(s.st_mode & S_IFDIR) + { + snprintf(subdir, 255, +#ifdef WIN32 + "%s\\" +#else + "%.253s/" +#endif + , entries[n]->d_name); + tpl_checkOneDirDiskRevisions(subdir); + } + } + free(entries[n]); + } + free(entries); + } +} + +/* Helper function for urldecode.*/ +static int32_t x2i(int32_t i) +{ + i = toupper(i); + i = i - '0'; + if(i > 9) { i = i - 'A' + '9' + 1; } + return i; +} + +/* Decodes values in a http url. Note: The original value is modified! */ +void urldecode(char *s) +{ + int32_t c, c1, n; + char *t; + t = s; + n = cs_strlen(s); + while(n > 0) + { + c = *s++; + if(c == '+') { c = ' '; } + else if(c == '%' && n > 2) + { + c = *s++; + c1 = c; + c = *s++; + c = 16 * x2i(c1) + x2i(c); + n -= 2; + } + *t++ = c; + n--; + } + *t = 0; +} + +/* Encode values in a http url. Do not call free() or realloc on the returned reference or you will get memory corruption! */ +char *urlencode(struct templatevars *vars, const char *str) +{ + char *buf; + if(!cs_malloc(&buf, cs_strlen(str) * 3 + 1)) { return ""; } + const char *pstr = str; + char *pbuf = buf; + + while(*pstr) + { + if(isalnum((uint8_t)*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') { *pbuf++ = *pstr; } + else if(*pstr == ' ') { *pbuf++ = '+'; } + else + { + *pbuf++ = '%'; + *pbuf++ = to_hex(*pstr >> 4); + *pbuf++ = to_hex(*pstr & 15); + } + ++pstr; + } + *pbuf = '\0'; + /* Allocate the needed memory size and store it in the templatevars */ + if(!cs_realloc(&buf, cs_strlen(buf) + 1)) { return ""; } + return tpl_addTmp(vars, buf); +} + +/* XML-Escapes a char array. The returned reference will be automatically cleaned through the templatevars-mechanism tpl_clear(). + Do not call free() or realloc on the returned reference or you will get memory corruption! */ +char *xml_encode(struct templatevars *vars, const char *chartoencode) +{ + if(!chartoencode) { return ""; } + int32_t i, pos = 0, len = cs_strlen(chartoencode); + char *encoded; + char buffer[7]; + /* In worst case, every character could get converted to 6 chars (we only support ASCII, for Unicode it would be 7)*/ + if(!cs_malloc(&encoded, len * 6 + 1)) { return ""; } + for(i = 0; i < len; ++i) + { + uint8_t tmp = chartoencode[i]; + switch(tmp) + { + case '&' : + memcpy(encoded + pos, "&", 5); + pos += 5; + break; + case '<' : + memcpy(encoded + pos, "<", 4); + pos += 4; + break; + case '>' : + memcpy(encoded + pos, ">", 4); + pos += 4; + break; + case '"' : + memcpy(encoded + pos, """, 6); + pos += 6; + break; + case '\'': + memcpy(encoded + pos, "'", 6); + pos += 6; + break; + case '\n': + memcpy(encoded + pos, "\n", 1); + pos += 1; + break; + default: + if(tmp < 32) + { + snprintf(buffer, 7, "&#%d;", tmp); + memcpy(encoded + pos, buffer, cs_strlen(buffer)); + pos += cs_strlen(buffer); + } + else + { + encoded[pos] = tmp; + ++pos; + } + } + } + /* Reduce to the really needed memory size and store it in the templatevars */ + if(!cs_realloc(&encoded, pos + 1)) { return ""; } + encoded[pos] = '\0'; + return tpl_addTmp(vars, encoded); +} + +char *json_encode(struct templatevars *vars, const char *chartoencode) +{ + if(!chartoencode) { return ""; } + int32_t i, pos = 0, len = cs_strlen(chartoencode); + char *encoded; + char buffer[7]; + /* In worst case, every character could get converted to 6 chars (\uXXXX) */ + if(!cs_malloc(&encoded, len * 6 + 1)) { return ""; } + for(i = 0; i < len; ++i) + { + uint8_t tmp = chartoencode[i]; + switch(tmp) + { + case '"' : + memcpy(encoded + pos, "\\\"", 2); + pos += 2; + break; + case '\\': + memcpy(encoded + pos, "\\\\", 2); + pos += 2; + break; + case '\n': + memcpy(encoded + pos, "\\n", 2); + pos += 2; + break; + case '\r': + memcpy(encoded + pos, "\\r", 2); + pos += 2; + break; + case '\t': + memcpy(encoded + pos, "\\t", 2); + pos += 2; + break; + default: + if(tmp < 32) + { + snprintf(buffer, 7, "\\u%04x", tmp); + memcpy(encoded + pos, buffer, 6); + pos += 6; + } + else + { + encoded[pos] = tmp; + ++pos; + } + } + } + /* Reduce to the really needed memory size and store it in the templatevars */ + if(!cs_realloc(&encoded, pos + 1)) { return ""; } + encoded[pos] = '\0'; + return tpl_addTmp(vars, encoded); +} + +/* Format a seconds integer to hh:mm:ss or dd hh:mm:ss depending hrs >24 */ +char *sec2timeformat(struct templatevars *vars, int32_t seconds) +{ + char *value; + if(seconds <= 0) + { return "00:00:00"; } + if(!cs_malloc(&value, 16)) + { return "00:00:00"; } + int32_t secs = 0, fullmins = 0, mins = 0, fullhours = 0, hours = 0, days = 0; + secs = seconds % 60; + if(seconds >= 60) + { + fullmins = seconds / 60; + mins = fullmins % 60; + if(fullmins >= 60) + { + fullhours = fullmins / 60; + hours = fullhours % 24; + days = fullhours / 24; + } + } + if(days == 0) + { snprintf(value, 16, "%02d:%02d:%02d", hours, mins, secs); } + else + { snprintf(value, 16, "%02dd %02d:%02d:%02d", days, hours, mins, secs); } + return tpl_addTmp(vars, value); +} + +#endif diff --git a/module-webif-tpl.h b/module-webif-tpl.h new file mode 100644 index 0000000..7e10444 --- /dev/null +++ b/module-webif-tpl.h @@ -0,0 +1,62 @@ +#ifndef MODULE_WEBIF_TPL_H_ +#define MODULE_WEBIF_TPL_H_ + +#ifdef WEBIF + +/* Templates: Adds a variable. The variable can be used as often as wanted. */ +#define TPLADD 0 +/* Templates: Appends a variable or adds it if doesn't exist yet. The variable can be used as often as wanted. */ +#define TPLAPPEND 1 +/* Templates: Adds a variable which will be reset to "" after being used once, either through tpl_getVar or when used in a template. + tpl_addVar/tpl_printf don't do a reset and will overwrite the appendmode with a new value. */ +#define TPLADDONCE 2 +/* Templates: Appends a variable or adds it if doesn't exist yet. The variable will be reset to "" after being used once. See TPLADDONCE for details. */ +#define TPLAPPENDONCE 3 + +struct templatevars +{ + uint32_t varscnt; + uint32_t varsalloc; + uint32_t tmpcnt; + uint32_t tmpalloc; + char **names; + char **values; + uint8_t *vartypes; + char **tmp; + uint8_t messages; +}; + +void webif_tpls_prepare(void); +void webif_tpls_free(void); + +struct templatevars *tpl_create(void); +void tpl_clear(struct templatevars *vars); + +void tpl_addVar(struct templatevars *vars, uint8_t addmode, const char *name, const char *value); +void tpl_addMsg(struct templatevars *vars, const char *value); +void tpl_printf(struct templatevars *vars, uint8_t addmode, const char *varname, const char *fmtstring, ...) __attribute__((format(printf, 4, 5))); + +char *tpl_getVar(struct templatevars *vars, const char *name); +char *tpl_getFilePathInSubdir(const char *path, const char *subdir, const char *name, const char *ext, char *result, uint32_t resultsize); +char *tpl_getTplPath(const char *name, const char *path, char *result, uint32_t resultsize); +char *tpl_getTpl(struct templatevars *vars, const char *name); +char *tpl_getUnparsedTpl(const char *name, int8_t removeHeader, const char *subdir); + +int32_t tpl_saveIncludedTpls(const char *path); + +void tpl_checkOneDirDiskRevisions(const char *subdir); +void tpl_checkDiskRevisions(void); + +char *urlencode(struct templatevars *vars, const char *str); +char *xml_encode(struct templatevars *vars, const char *chartoencode); +char *json_encode(struct templatevars *vars, const char *chartoencode); +char *sec2timeformat(struct templatevars *vars, int32_t seconds); + +#else +static inline void webif_tpls_free(void) +{ + return; +} +#endif + +#endif diff --git a/module-webif.c b/module-webif.c new file mode 100644 index 0000000..252f1c2 --- /dev/null +++ b/module-webif.c @@ -0,0 +1,10369 @@ +#define MODULE_LOG_PREFIX "webif" + +#include "globals.h" + +#ifdef WEBIF +// +// OSCam HTTP server module +// +#include +#include "cscrypt/md5.h" +#include "module-anticasc.h" +#include "module-cacheex.h" +#include "module-cccam.h" +#include "module-cccam-data.h" +#include "module-dvbapi.h" +#include "module-newcamd.h" +#include "module-stat.h" +#include "module-webif.h" +#include "module-webif-lib.h" +#include "module-webif-tpl.h" +#include "oscam-conf-mk.h" +#include "oscam-config.h" +#include "oscam-files.h" +#include "oscam-garbage.h" +#include "oscam-cache.h" +#include "oscam-client.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" +#include "oscam-ecm.h" + +#define WEBIF_MAX_NODES 512 +static uint64_t webif_last_nodeid[WEBIF_MAX_NODES]; + + +#ifdef MODULE_GBOX +#include "module-gbox-sms.h" +#include "module-gbox.h" +#include "module-gbox-cards.h" +#endif + + + + +#ifdef WEBIF_WIKI +#include "webif/pages_wiki.h" +#endif + +extern const struct s_cardreader *cardreaders[]; +extern char cs_confdir[]; +extern uint32_t ecmcwcache_size; +extern uint32_t cfg_sidtab_generation; +extern int32_t exit_oscam; +extern uint8_t cacheex_peer_id[8]; + +extern char *entitlement_type[]; +extern char *RDR_CD_TXT[]; +int8_t isactive; +int32_t ssl_active = 0; +char noncekey[33]; +pthread_key_t getkeepalive; +static pthread_key_t getip; +pthread_key_t getssl; +static CS_MUTEX_LOCK http_lock; +CS_MUTEX_LOCK *lock_cs; + +static uint8_t useLocal = 1; +#define PRINTF_LOCAL_D useLocal ? "%'d" : "%d" +#define PRINTF_LOCAL_F useLocal ? "%'.0f" : "%.0f" +#define PRINTF_LOCAL_MB useLocal ? "%'.2f MB" : "%.2f MB" + +static int8_t httpthread_running = 0; +static pthread_t httpthread; +static int32_t sock; +enum refreshtypes { REFR_ACCOUNTS, REFR_READERS, REFR_CLIENTS, REFR_SERVER, REFR_ANTICASC, REFR_SERVICES }; + +//initialize structs for calculating cpu-usage depending on time between refresh of status_page +static struct pstat p_stat_cur; +static struct pstat p_stat_old; + +static bool use_srvid2 = false; + +/* constants for menuactivating */ +#define MNU_STATUS 0 +#define MNU_LIVELOG 1 +#define MNU_CONFIG 2 +#define MNU_READERS 3 +#define MNU_USERS 4 +#define MNU_SERVICES 5 +#define MNU_FILES 6 +#define MNU_FAILBAN 7 +#define MNU_CACHEEX 8 +#define MNU_SCRIPT 9 +#define MNU_SHUTDOWN 10 +#define MNU_TOTAL_ITEMS 11 // sum of items above + +/* constants for config.html submenuactivating */ +#define MNU_CFG_GLOBAL 0 +#define MNU_CFG_ANTICASC 1 +#define MNU_CFG_CACHE 2 +#define MNU_CFG_LOADBAL 3 +#define MNU_CFG_CAMD33 4 +#define MNU_CFG_CAMD35 5 +#define MNU_CFG_CAMD35TCP 6 +#define MNU_CFG_CCCAM 7 +#define MNU_CFG_NEWCAMD 8 +#define MNU_CFG_GBOX 9 +#define MNU_CFG_RADEGAST 10 +#define MNU_CFG_SCAM 11 +#define MNU_CFG_SERIAL 12 +#define MNU_CFG_DVBAPI 13 +#define MNU_CFG_LCD 14 +#define MNU_CFG_MONITOR 15 +#define MNU_CFG_WEBIF 16 +#define MNU_CFG_STREAMRELAY 17 + +/* constants for files.html submenuactivating */ +#define MNU_CFG_FVERSION 0 +#define MNU_CFG_FCONF 1 +#define MNU_CFG_FUSER 2 +#define MNU_CFG_FSERVER 3 +#define MNU_CFG_FSRVID 4 +#define MNU_CFG_FDVBAPI 5 +#define MNU_CFG_FACLOG 6 +#define MNU_CFG_FLOGFILE 7 +#define MNU_CFG_FUSERFILE 8 +#define MNU_CFG_FSERVICES 9 +#define MNU_CFG_FPROVID 10 +#define MNU_CFG_FTIERS 11 +#define MNU_CFG_FRATELIMIT 12 +#define MNU_CFG_FWHITELIST 13 +#define MNU_CFG_FSRVID2 14 +#define MNU_CFG_FFAKECWS 15 +#define MNU_CFG_FCSS 16 +#define MNU_CFG_FTWIN 17 +#define MNU_CFG_FKEYCW 18 + +/* constants for files.html for GBOX submenuactivating */ +#define MNU_GBX_FSCINF 19 +#define MNU_GBX_FSHRINF 20 +#define MNU_GBX_FSHRONL 21 +#define MNU_GBX_FVERS 22 +#define MNU_GBX_FATTACK 23 +#define MNU_GBX_FSMSLOG 24 +#define MNU_GBX_FSMSACK 25 +#define MNU_GBX_FSMSNACK 26 +#define MNU_GBX_FSTAINF 27 +#define MNU_GBX_FEXPINF 28 +#define MNU_GBX_INFOLOG 29 +#define MNU_CFG_FSOFTCAMKEY 30 + +#define MNU_CFG_TOTAL_ITEMS 31 // sum of items above. Use it for "All inactive" in function calls too. + +static void set_status_info_var(struct templatevars *vars, char *varname, int no_data, char *fmt, double value) +{ + if (no_data) + tpl_addVar(vars, TPLADD, varname, "N/A"); + else + tpl_printf(vars, TPLADD, varname, fmt, value); +} + +/* +* Creates vars Memory/CPU/OSCAM info in for status_page +* if check_available == 0 N/A will be displayed +* Bit mapping +* mem 0 total, 1 used & free, 2 buff, cached & free incl. buff, 3 share +* swap 4 total, 5 used & free, +* proc 6 count +* cpu 7 load +* oscam 8 vsize & rssize, 9 cpu user, 10 cpu sys, 11 cpu sum, 12 cpu refreshtime +* unused 13 - 15 +*/ +static void set_status_info(struct templatevars *vars, struct pstat stats){ + set_status_info_var(vars, "MEM_CUR_TOTAL", stats.check_available & (1 << 0), PRINTF_LOCAL_MB , (double)stats.mem_total/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_FREE", stats.check_available & (1 << 1), PRINTF_LOCAL_MB , (double)stats.mem_free/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_USED", stats.check_available & (1 << 1), PRINTF_LOCAL_MB , (double)stats.mem_used/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_BUFF", stats.check_available & (1 << 2), PRINTF_LOCAL_MB , (double)stats.mem_buff/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_CACHED", stats.check_available & (1 << 2), PRINTF_LOCAL_MB , (double)stats.mem_cached/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_FREEM", stats.check_available & (1 << 2), PRINTF_LOCAL_MB , (double)stats.mem_freem/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_SHARE", stats.check_available & (1 << 3), PRINTF_LOCAL_MB , (double)stats.mem_share/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_TOTSW", stats.check_available & (1 << 4), PRINTF_LOCAL_MB , (double)stats.mem_total_swap/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_FRESW", stats.check_available & (1 << 5), PRINTF_LOCAL_MB , (double)stats.mem_free_swap/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_USESW", stats.check_available & (1 << 5), PRINTF_LOCAL_MB , (double)stats.mem_used_swap/(1024.0*1024.0)); + + set_status_info_var(vars, "SERVER_PROCS", stats.check_available & (1 << 6), PRINTF_LOCAL_F , stats.info_procs); + + set_status_info_var(vars, "CPU_LOAD_0", stats.check_available & (1 << 7), "%.2f" , stats.cpu_avg[0]); + set_status_info_var(vars, "CPU_LOAD_1", stats.check_available & (1 << 7), "%.2f" , stats.cpu_avg[1]); + set_status_info_var(vars, "CPU_LOAD_2", stats.check_available & (1 << 7), "%.2f" , stats.cpu_avg[2]); + + set_status_info_var(vars, "OSCAM_VMSIZE", stats.check_available & (1 << 8), PRINTF_LOCAL_MB , (double)stats.vsize/(1024.0*1024.0)); + set_status_info_var(vars, "OSCAM_RSSSIZE", stats.check_available & (1 << 8), PRINTF_LOCAL_MB , (double)stats.rss/(1024.0*1024.0)); + set_status_info_var(vars, "OSCAM_CPU_USER", stats.check_available & (1 << 9), "%.2f %%" , stats.cpu_usage_user); + set_status_info_var(vars, "OSCAM_CPU_SYS", stats.check_available & (1 << 10), "%.2f %%" , stats.cpu_usage_sys); + double sum_cpu = stats.cpu_usage_sys + stats.cpu_usage_user; + set_status_info_var(vars, "OSCAM_CPU_SUM", stats.check_available & (1 << 11), "%.2f %%" , sum_cpu); + + if (stats.check_available & (1 << 12)) + { + tpl_addVar(vars, TPLADD, "OSCAM_REFRESH" , "N/A"); + } + else + { + tpl_printf(vars, TPLADD, "OSCAM_REFRESH" , "%02"PRId64":%02"PRId64":%02"PRId64"h", + stats.gone_refresh / 3600, + (stats.gone_refresh / 60) % 60, + stats.gone_refresh % 60); + } +} + +static void clear_account_stats(struct s_auth *account) +{ + account->cwfound = 0; + account->cwcache = 0; + account->cwnot = 0; + account->cwtun = 0; + account->cwignored = 0; + account->cwtout = 0; + account->emmok = 0; + account->emmnok = 0; +#ifdef CW_CYCLE_CHECK + account->cwcycledchecked = 0; + account->cwcycledok = 0; + account->cwcyclednok = 0; + account->cwcycledign = 0; +#endif + cacheex_clear_account_stats(account); +} + +static void clear_all_account_stats(void) +{ + struct s_auth *account = cfg.account; + while(account) + { + clear_account_stats(account); + account = account->next; + } +} + +#ifdef CS_CACHEEX +static void cacheex_clear_all_stats(void) +{ + struct s_auth *account = cfg.account; + while(account) + { + cacheex_clear_account_stats(account); + account = account->next; + } + struct s_client *cl; + for(cl = first_client->next; cl ; cl = cl->next) + { + cacheex_clear_client_stats(cl); + ll_clear_data(cl->ll_cacheex_stats); + } + cacheex_clear_client_stats(first_client); +} +#endif + +static void clear_info_clients_stats(void) +{ + first_client->cwfound = 0; + first_client->cwcache = 0; + first_client->cwnot = 0; + first_client->cwtun = 0; + first_client->cwignored = 0; + first_client->cwtout = 0; + first_client->emmok = 0; + first_client->emmnok = 0; + cacheex_clear_client_stats(first_client); +} + +static void clear_info_readers_stats(void) +{ + int8_t i; + cs_writelock(__func__, &readerlist_lock); + LL_ITER itr = ll_iter_create(configured_readers); + struct s_reader *rdr; + while((rdr = ll_iter_next(&itr))) + { + rdr->webif_ecmsok = 0; + rdr->webif_ecmsnok = 0; + rdr->webif_ecmstout = 0; + rdr->webif_ecmsfilteredhead = 0; + rdr->webif_ecmsfilteredlen = 0; + + for(i = 0; i < 4; i++) + { + rdr->webif_emmerror[i] = 0; + rdr->webif_emmwritten[i] = 0; + rdr->webif_emmskipped[i] = 0; + rdr->webif_emmblocked[i] = 0; + } + } + cs_writeunlock(__func__, &readerlist_lock); +} + +static void set_ecm_info(struct templatevars * vars) +{ + //if one of the stats overloaded, reset all stats! + if(first_client->cwfound<0 + || first_client->cwnot<0 + || first_client->cwignored<0 + || first_client->cwtout<0 + || first_client->cwcache<0 + || first_client->cwtun<0 + || first_client->emmok<0 + || first_client->emmnok<0 +#ifdef CS_CACHEEX + || first_client->cwcacheexgot<0 + || first_client->cwcacheexpush<0 + || first_client->cwcacheexhit<0 +#ifdef CS_CACHEEX_AIO + || first_client->cwcacheexgotlg<0 + || first_client->cwcacheexpushlg<0 +#endif +#endif + ){ + clear_info_clients_stats(); + } + //end reset stats + + int ecm = 0, emm = 0; + double ecmsum = first_client->cwfound + first_client->cwcache + first_client->cwnot + first_client->cwtout; //dont count TUN its included + if(ecmsum < 1) {ecmsum = 1; ecm = 1;} + double ecmpos = first_client->cwfound + first_client->cwcache; // dont count TUN its included + if(ecmpos < 1) {ecmpos = 1;} + double ecmneg = first_client->cwnot + first_client->cwtout; //dont count IGN its not part of neagtiv + if(ecmneg < 1) {ecmneg = 1;} + double emmsum = first_client->emmok + first_client->emmnok; + if(emmsum < 1) {emmsum = 1; emm = 1;} + + tpl_printf(vars, TPLADD, "TOTAL_ECM_MIN", "%d", first_client->n_request[0]); + tpl_printf(vars, TPLADD, "TOTAL_CW", PRINTF_LOCAL_F, !ecm ? ecmsum : 0); + tpl_printf(vars, TPLADD, "TOTAL_CWOK", PRINTF_LOCAL_F, (double)first_client->cwfound); + tpl_printf(vars, TPLADD, "TOTAL_CWNOK", PRINTF_LOCAL_F, (double)first_client->cwnot); + tpl_printf(vars, TPLADD, "TOTAL_CWIGN", PRINTF_LOCAL_F, (double)first_client->cwignored); + tpl_printf(vars, TPLADD, "TOTAL_CWTOUT", PRINTF_LOCAL_F, (double)first_client->cwtout); + tpl_printf(vars, TPLADD, "TOTAL_CWCACHE", PRINTF_LOCAL_F, (double)first_client->cwcache); + tpl_printf(vars, TPLADD, "TOTAL_CWTUN", PRINTF_LOCAL_F, (double)first_client->cwtun); + tpl_printf(vars, TPLADD, "TOTAL_CWPOS", PRINTF_LOCAL_F, (double)first_client->cwfound + (double)first_client->cwcache); + tpl_printf(vars, TPLADD, "TOTAL_CWNEG", PRINTF_LOCAL_F, (double)first_client->cwnot + (double)first_client->cwtout); + tpl_printf(vars, TPLADD, "TOTAL_EM", PRINTF_LOCAL_F, !emm ? emmsum : 0); + tpl_printf(vars, TPLADD, "TOTAL_EMOK", PRINTF_LOCAL_F, (double)first_client->emmok); + tpl_printf(vars, TPLADD, "TOTAL_EMNOK", PRINTF_LOCAL_F, (double)first_client->emmnok); + tpl_printf(vars, TPLADD, "REL_CWOK", "%.2f", (double)first_client->cwfound * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWNOK", "%.2f", (double)first_client->cwnot * 100 / ecmsum); + //tpl_printf(vars, TPLADD, "REL_CWIGN", "%.2f", (double)first_client->cwignored * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWTOUT", "%.2f", (double)first_client->cwtout * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWCACHE", "%.2f", (double)first_client->cwcache * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWTUN", "%.2f", (double)first_client->cwtun * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWPOS", "%.2f", (double)(first_client->cwfound + first_client->cwcache) * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWNEG", "%.2f", (double)(first_client->cwnot + first_client->cwtout) * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_EMOK", "%.2f", (double)first_client->emmok * 100 / emmsum); + tpl_printf(vars, TPLADD, "REL_EMNOK", "%.2f", (double)first_client->emmnok * 100 / emmsum); + tpl_printf(vars, TPLADD, "REL_CWPOSOK", "%.2f", (double)first_client->cwfound * 100 / ecmpos); + tpl_printf(vars, TPLADD, "REL_CWPOSCACHE", "%.2f", (double)first_client->cwcache * 100 / ecmpos); + tpl_printf(vars, TPLADD, "REL_CWNEGNOK", "%.2f", (double)first_client->cwnot * 100 / ecmneg); + //tpl_printf(vars, TPLADD, "REL_CWNEGIGN", "%.2f", (double)first_client->cwignored * 100 / ecmneg); + tpl_printf(vars, TPLADD, "REL_CWNEGTOUT", "%.2f", (double)first_client->cwtout * 100 / ecmneg); + + double totalrdrneg = 0, totalrdrpos = 0; + double totalrdrok = 0, totalrdrnok = 0, totalrdrtout = 0; + double flen = 0, fhead = 0; + double teruk = 0, terg = 0, ters = 0, teruq = 0; + double twruk = 0, twrg = 0, twrs = 0, twruq = 0; + double tskuk = 0, tskg = 0, tsks = 0, tskuq = 0; + double tbluk = 0, tblg = 0, tbls = 0, tbluq = 0; + + cs_readlock(__func__, &readerlist_lock); + LL_ITER itr = ll_iter_create(configured_readers); + struct s_reader *rdr; + while((rdr = ll_iter_next(&itr))) + { + if (rdr->webif_ecmsok) { totalrdrok += rdr->webif_ecmsok; } + if (rdr->webif_ecmsnok) { totalrdrnok += rdr->webif_ecmsnok; } + if (rdr->webif_ecmstout) { totalrdrtout += rdr->webif_ecmstout; } + + if (rdr->webif_ecmsfilteredlen) { flen += rdr->webif_ecmsfilteredlen; } + if (rdr->webif_ecmsfilteredhead) { fhead += rdr->webif_ecmsfilteredhead; } + + if (rdr->webif_emmerror[0]) { teruk += rdr->webif_emmerror[0]; } + if (rdr->webif_emmerror[1]) { teruq += rdr->webif_emmerror[1]; } + if (rdr->webif_emmerror[2]) { ters += rdr->webif_emmerror[2]; } + if (rdr->webif_emmerror[3]) { terg += rdr->webif_emmerror[3]; } + + if (rdr->webif_emmwritten[0]) { twruk += rdr->webif_emmwritten[0]; } + if (rdr->webif_emmwritten[1]) { twruq += rdr->webif_emmwritten[1]; } + if (rdr->webif_emmwritten[2]) { twrs += rdr->webif_emmwritten[2]; } + if (rdr->webif_emmwritten[3]) { twrg += rdr->webif_emmwritten[3]; } + + if (rdr->webif_emmskipped[0]) { tskuk += rdr->webif_emmskipped[0]; } + if (rdr->webif_emmskipped[1]) { tskuq += rdr->webif_emmskipped[1]; } + if (rdr->webif_emmskipped[2]) { tsks += rdr->webif_emmskipped[2]; } + if (rdr->webif_emmskipped[3]) { tskg += rdr->webif_emmskipped[3]; } + + if (rdr->webif_emmblocked[0]) { tbluk += rdr->webif_emmblocked[0]; } + if (rdr->webif_emmblocked[1]) { tbluq += rdr->webif_emmblocked[1]; } + if (rdr->webif_emmblocked[2]) { tbls += rdr->webif_emmblocked[2]; } + if (rdr->webif_emmblocked[3]) { tblg += rdr->webif_emmblocked[3]; } + } + cs_readunlock(__func__, &readerlist_lock); + + totalrdrneg = totalrdrnok + totalrdrtout; + totalrdrpos = totalrdrok; + ecmsum = totalrdrok + totalrdrnok + totalrdrtout; + + tpl_printf(vars, TPLADD, "TOTAL_CWOK_READERS", PRINTF_LOCAL_F, totalrdrok); + tpl_printf(vars, TPLADD, "TOTAL_CWNOK_READERS", PRINTF_LOCAL_F, totalrdrnok); + tpl_printf(vars, TPLADD, "TOTAL_CWTOUT_READERS", PRINTF_LOCAL_F, totalrdrtout); + tpl_printf(vars, TPLADD, "REL_CWOK_READERS", "%.2f", ecmsum ? totalrdrok * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "REL_CWNOK_READERS", "%.2f", ecmsum ? totalrdrnok * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "REL_CWTOUT_READERS", "%.2f", ecmsum ? totalrdrtout * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "TOTAL_CWPOS_READERS", PRINTF_LOCAL_F, totalrdrpos); + tpl_printf(vars, TPLADD, "TOTAL_CWNEG_READERS", PRINTF_LOCAL_F, totalrdrneg); + tpl_printf(vars, TPLADD, "REL_CWPOS_READERS", "%.2f", ecmsum ? totalrdrpos * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "REL_CWNEG_READERS", "%.2f", ecmsum ? totalrdrneg * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "TOTAL_ELENR", PRINTF_LOCAL_F, flen); + tpl_printf(vars, TPLADD, "TOTAL_EHEADR", PRINTF_LOCAL_F, fhead); + tpl_printf(vars, TPLADD, "TOTAL_SUM_ALL_READERS_ECM", PRINTF_LOCAL_F, ecmsum); + + tpl_printf(vars, TPLADD, "TOTAL_EMMERRORUK_READERS", PRINTF_LOCAL_F, teruk); + tpl_printf(vars, TPLADD, "TOTAL_EMMERRORG_READERS", PRINTF_LOCAL_F, terg); + tpl_printf(vars, TPLADD, "TOTAL_EMMERRORS_READERS", PRINTF_LOCAL_F, ters); + tpl_printf(vars, TPLADD, "TOTAL_EMMERRORUQ_READERS", PRINTF_LOCAL_F, teruq); + tpl_printf(vars, TPLADD, "TOTAL_EMMWRITTENUK_READERS", PRINTF_LOCAL_F, twruk); + tpl_printf(vars, TPLADD, "TOTAL_EMMWRITTENG_READERS", PRINTF_LOCAL_F, twrg); + tpl_printf(vars, TPLADD, "TOTAL_EMMWRITTENS_READERS", PRINTF_LOCAL_F, twrs); + tpl_printf(vars, TPLADD, "TOTAL_EMMWRITTENUQ_READERS", PRINTF_LOCAL_F, twruq); + tpl_printf(vars, TPLADD, "TOTAL_EMMSKIPPEDUK_READERS", PRINTF_LOCAL_F, tskuk); + tpl_printf(vars, TPLADD, "TOTAL_EMMSKIPPEDG_READERS", PRINTF_LOCAL_F, tskg); + tpl_printf(vars, TPLADD, "TOTAL_EMMSKIPPEDS_READERS", PRINTF_LOCAL_F, tsks); + tpl_printf(vars, TPLADD, "TOTAL_EMMSKIPPEDUQ_READERS", PRINTF_LOCAL_F, tskuq); + tpl_printf(vars, TPLADD, "TOTAL_EMMBLOCKEDUK_READERS", PRINTF_LOCAL_F, tbluk); + tpl_printf(vars, TPLADD, "TOTAL_EMMBLOCKEDG_READERS", PRINTF_LOCAL_F, tblg); + tpl_printf(vars, TPLADD, "TOTAL_EMMBLOCKEDS_READERS", PRINTF_LOCAL_F, tbls); + tpl_printf(vars, TPLADD, "TOTAL_EMMBLOCKEDUQ_READERS", PRINTF_LOCAL_F, tbluq); + + emmsum = teruk + terg + ters + teruq + twruk + twrg + twrs + twruq + tskuk + tskg + tsks + tskuq + tbluk + tblg + tbls + tbluq; + + tpl_printf(vars, TPLADD, "TOTAL_SUM_ALL_READERS_EMM", PRINTF_LOCAL_F, emmsum); +} + +static void refresh_oscam(enum refreshtypes refreshtype) +{ + + switch(refreshtype) + { + case REFR_ACCOUNTS: + cs_log("Refresh Accounts requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + cs_accounts_chk(); + break; + + case REFR_READERS: + cs_log("Refresh Readers requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + reload_readerdb(); + break; + + case REFR_CLIENTS: + cs_log("Refresh Clients requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + cs_reinit_clients(cfg.account); + break; + + case REFR_SERVER: + cs_log("Refresh Server requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + reload_global_config(); // Wczytaj ponownie globalną konfigurację + break; + + case REFR_SERVICES: + cs_log("Refresh Services requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + //init_sidtab(); + cs_accounts_chk(); + break; + +#ifdef CS_ANTICASC + case REFR_ANTICASC: + cs_log("Refresh Anticascading requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + ac_init_stat(); + struct s_client *cl; + struct s_auth *account; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->typ == 'c' && (account = cl->account)) + { + cl->ac_limit = (account->ac_users * 100 + 80) * cfg.ac_stime; + } + } + break; +#endif + default: + break; + } +} +/* + * load historical values from ringbuffer and return it in the right order + * as string. Value should be freed with free_mk_t() + */ +static char *get_ecm_historystring(struct s_client *cl) +{ + + if(cl) + { + int32_t k, i, pos = 0, needed = 1, v; + char *value, *dot = ""; + int32_t ptr = cl->cwlastresptimes_last; + + needed = CS_ECM_RINGBUFFER_MAX * 6; //5 digits + delimiter + if(!cs_malloc(&value, needed)) { return ""; } + + k = ptr + 1; + for(i = 0; i < CS_ECM_RINGBUFFER_MAX; i++) + { + if(k >= CS_ECM_RINGBUFFER_MAX) + { k = 0; } + v = cl->cwlastresptimes[k].duration; + if(v > 0 && v < (int32_t)cfg.ctimeout * 5) + { + pos += snprintf(value + pos, needed - pos, "%s%d", dot, v); + dot = ","; + } + k++; + } + if(cs_strlen(value) == 0) + { + NULLFREE(value); + return ""; + } + else { return value; } + + } + else + { + return ""; + } +} + +static char *get_ecm_fullhistorystring(struct s_client *cl) +{ + + if(cl) + { + int32_t k, i, pos = 0, needed = 1, v; + char *value, *dot = ""; + int32_t ptr = cl->cwlastresptimes_last; + + needed = CS_ECM_RINGBUFFER_MAX * 20; //5 digits + : + returncode(2) + : + time(10) + delimiter + if(!cs_malloc(&value, needed)) { return ""; } + + k = ptr + 1; + for(i = 0; i < CS_ECM_RINGBUFFER_MAX; i++) + { + if(k >= CS_ECM_RINGBUFFER_MAX) + { k = 0; } + v = cl->cwlastresptimes[k].duration; + if(v > 0 && v < (int32_t)cfg.ctimeout * 5) + { + pos += snprintf(value + pos, needed - pos, "%s%d:%d:%" PRId64, dot, cl->cwlastresptimes[k].duration, cl->cwlastresptimes[k].rc, (int64_t)cl->cwlastresptimes[k].timestamp); + dot = ","; + } + k++; + } + + return (value); + + } + else + { + return ""; + } +} + +/* + * Set the active menu to a different CSS class + */ +static void setActiveMenu(struct templatevars *vars, int8_t active) +{ + int8_t i; + for(i = 0; i < MNU_TOTAL_ITEMS; i++) + { + tpl_printf(vars, TPLADD, "TMP", "MENUACTIVE%d", i); + if(i == active) + { tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "menu_selected"); } + else + { tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "menu"); } + } + #ifdef WEBIF_LIVELOG + tpl_addVar(vars, TPLADD, "LOGPAGEMENU", tpl_getTpl(vars, "LOGMENU")); + #endif +} + +/* + * Set the active submenu to a different CSS class + */ +static void setActiveSubMenu(struct templatevars *vars, int8_t active) +{ + int8_t i; + for(i = 0; i < MNU_CFG_TOTAL_ITEMS; i++) + { + tpl_printf(vars, TPLADD, "TMP", "CMENUACTIVE%d", i); + if(i == active) + { tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "configmenu_selected"); } + else + { tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "configmenu"); } + } +} + +static void webif_save_config(char *section, struct templatevars *vars, struct uriparams *params) +{ + if(!streq(getParam(params, "action"), "execute")) + { return; } + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. No changes are possible!"); + return; + } + int i; + int cnt = (*params).paramcount; + // First pass: check for checkbox values (value=1) to identify which checkboxes are checked + // This is needed because hidden fields (value=0) should not override checkbox values + bool *checkbox_checked = NULL; + if(cnt > 0 && !cs_malloc(&checkbox_checked, cnt * sizeof(bool))) + { + return; // Allocation failed, proceed with original behavior + } + for(i = 0; i < cnt; i++) + { + char *token = (*params).params[i]; + char *value = (*params).values[i]; + if(!streq(token, "part") && !streq(token, "action")) + { + // Check if this is a checkbox with value=1 + if(strcmp(value, "1") == 0) + { + checkbox_checked[i] = true; + } + } + } + // Second pass: apply settings, skip value=0 if checkbox is checked (hidden field) + for(i = 0; i < cnt; i++) + { + char *token = (*params).params[i]; + char *value = (*params).values[i]; + if(!streq(token, "part") && !streq(token, "action")) + { + // Skip hidden field value=0 if checkbox is checked (value=1 exists) + if(strcmp(value, "0") == 0) + { + bool checkbox_will_be_checked = false; + int j; + for(j = i + 1; j < cnt; j++) + { + if(strcmp((*params).params[j], token) == 0 && strcmp((*params).values[j], "1") == 0) + { + checkbox_will_be_checked = true; + break; + } + } + if(checkbox_will_be_checked) + { + continue; // Skip this hidden field, checkbox will set it to 1 + } + } + config_set(section, token, value); + } + } + free(checkbox_checked); + if(write_config() == 0) + { + tpl_addMsg(vars, "Configuration was saved."); + enum refreshtypes ref_type = REFR_SERVER; + if(streq(getParam(params, "part"), "anticasc")) + { ref_type = REFR_ANTICASC; } + refresh_oscam(ref_type); + } + else + { + tpl_addMsg(vars, "ERROR: Failed to write config file!!!"); + } +} + +static char *send_oscam_config_global(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_GLOBAL); + + webif_save_config("global", vars, params); + + if(IP_ISSET(cfg.srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.srvip)); } + tpl_printf(vars, TPLADD, "NICE", "%d", cfg.nice); + tpl_printf(vars, TPLADD, "BINDWAIT", "%d", cfg.bindwait); + + tpl_printf(vars, TPLADD, "TMP", "NETPRIO%d", cfg.netprio); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "PIDFILE", "%s", ESTR(cfg.pidfile)); + + + if(cfg.usrfile != NULL) { tpl_addVar(vars, TPLADD, "USERFILE", cfg.usrfile); } + if(cfg.disableuserfile == 0) { tpl_addVar(vars, TPLADD, "DISABLEUSERFILECHECKED", "checked"); } + if(cfg.usrfileflag == 1) { tpl_addVar(vars, TPLADD, "USERFILEFLAGCHECKED", "selected"); } + if(cfg.mailfile != NULL) { tpl_addVar(vars, TPLADD, "MAILFILE", cfg.mailfile); } + if(cfg.disablemail == 0) { tpl_addVar(vars, TPLADD, "DISABLEMAILCHECKED", "checked"); } + + char *value = mk_t_logfile(); + tpl_addVar(vars, TPLADD, "LOGFILE", value); + free_mk_t(value); + if(cfg.disablelog == 0) { tpl_addVar(vars, TPLADD, "DISABLELOGCHECKED", "checked"); } + tpl_printf(vars, TPLADD, "MAXLOGSIZE", "%d", cfg.max_log_size); + + tpl_addVar(vars, TPLADD, "LOGDUPSCHECKED", (cfg.logduplicatelines == 1) ? "checked" : ""); + tpl_printf(vars, TPLADD, "INITIALDEBUGLEVEL", "%u", cfg.initial_debuglevel); + + if(cfg.cwlogdir != NULL) { tpl_addVar(vars, TPLADD, "CWLOGDIR", cfg.cwlogdir); } + if(cfg.emmlogdir != NULL) { tpl_addVar(vars, TPLADD, "EMMLOGDIR", cfg.emmlogdir); } + tpl_addVar(vars, TPLADD, "ECMFMT", cfg.ecmfmt); + tpl_printf(vars, TPLADD, "LOGHISTORYLINES", "%u", cfg.loghistorylines); + if(cfg.sysloghost != NULL) { tpl_addVar(vars, TPLADD, "SYSLOGHOST", cfg.sysloghost); } + tpl_printf(vars, TPLADD, "SYSLOGPORT", "%u", cfg.syslogport); + + + tpl_printf(vars, TPLADD, "CLIENTTIMEOUT", "%u", cfg.ctimeout); + value = mk_t_caidvaluetab(&cfg.ctimeouttab); + tpl_addVar(vars, TPLADD, "CLIENTTIMEOUT_PERCAID", value); + free_mk_t(value); + tpl_printf(vars, TPLADD, "FALLBACKTIMEOUT", "%u", cfg.ftimeout); + tpl_printf(vars, TPLADD, "CLIENTMAXIDLE", "%u", cfg.cmaxidle); + + + value = mk_t_caidvaluetab(&cfg.ftimeouttab); + tpl_addVar(vars, TPLADD, "FALLBACKTIMEOUT_PERCAID", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "SLEEP", "%d", cfg.tosleep); + tpl_addVar(vars, TPLADD, "UNLOCKPARENTALCHECKED", (cfg.ulparent == 1) ? "checked" : ""); + + if(cfg.reload_useraccounts) { tpl_addVar(vars, TPLADD, "RELOADUSERACCOUNTSCHECKED", "checked"); } + if(cfg.reload_readers) { tpl_addVar(vars, TPLADD, "RELOADREADERSCHECKED", "checked"); } + if(cfg.reload_provid) { tpl_addVar(vars, TPLADD, "RELOADPROVIDCHECKED", "checked"); } + if(cfg.reload_services_ids) { tpl_addVar(vars, TPLADD, "RELOADSERVICESIDSCHECKED", "checked"); } + if(cfg.reload_tier_ids) { tpl_addVar(vars, TPLADD, "RELOADTIERUDSCHECKED", "checked"); } + if(cfg.reload_fakecws) { tpl_addVar(vars, TPLADD, "RELOADFAKECWSCHECKED", "checked"); } + if(cfg.reload_ac_stat) { tpl_addVar(vars, TPLADD, "RELOADACSTATCHECKED", "checked"); } + if(cfg.reload_log) { tpl_addVar(vars, TPLADD, "RELOADLOGCHECKED", "checked"); } + + if(cfg.block_same_ip) { tpl_addVar(vars, TPLADD, "BLOCKSAMEIPCHECKED", "checked"); } + if(cfg.block_same_name) { tpl_addVar(vars, TPLADD, "BLOCKSAMENAMECHECKED", "checked"); } + + if(cfg.waitforcards == 1) { tpl_addVar(vars, TPLADD, "WAITFORCARDSCHECKED", "checked"); } + tpl_printf(vars, TPLADD, "EXTRADELAY", "%d", cfg.waitforcards_extra_delay); + if(cfg.preferlocalcards == 1) + { + tpl_addVar(vars, TPLADD, "PREFERCACHEEX", "selected"); + } + else if(cfg.preferlocalcards == 2) + { + tpl_addVar(vars, TPLADD, "PREFERLOCALCARDS", "selected"); + } + + if(cfg.c35_suppresscmd08) + { tpl_addVar(vars, TPLADD, "SUPPRESSCMD08", "checked"); } + + if(cfg.getblockemmauprovid > 0) + { + tpl_addVar(vars, TPLADD, "GETBLOCKEMMAUPROVID", "checked"); + } + + if(cfg.reader_restart_seconds) + { tpl_printf(vars, TPLADD, "READERRESTARTSECONDS", "%d", cfg.reader_restart_seconds); } + + tpl_addVar(vars, TPLADD, "DROPDUPSCHECKED", (cfg.dropdups == 1) ? "checked" : ""); + + if(cfg.resolve_gethostbyname == 1) + { tpl_addVar(vars, TPLADD, "RESOLVER1", "selected"); } + else + { tpl_addVar(vars, TPLADD, "RESOLVER0", "selected"); } + + tpl_printf(vars, TPLADD, "FAILBANTIME", "%d", cfg.failbantime); + tpl_printf(vars, TPLADD, "FAILBANCOUNT", "%d", cfg.failbancount); + + tpl_addVar(vars, TPLADD, "DCHECKCSELECTED", (cfg.double_check == 1) ? "checked" : ""); + + value = mk_t_ftab(&cfg.double_check_caid); + tpl_addVar(vars, TPLADD, "DOUBLECHECKCAID", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "DISABLECRCCWSCHECKEDGLOBAL", (cfg.disablecrccws == 1) ? "checked" : ""); + + value = mk_t_ftab(&cfg.disablecrccws_only_for); + tpl_addVar(vars, TPLADD, "IGNCHKSUMONLYFORGLOBAL", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "CWVOTEENABLEDCHECKED", (cfg.cwvote_enabled == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "CWVOTELOGENABLEDCHECKED", (cfg.cwvote_log_enabled == 1) ? "checked" : ""); + tpl_printf(vars, TPLADD, "CWVOTETIMEOUT", "%d", cfg.cwvote_timeout); + tpl_printf(vars, TPLADD, "CWVOTEMINVOTES", "%d", cfg.cwvote_min_votes); + tpl_printf(vars, TPLADD, "CWVOTELOCALWEIGHT", "%.1f", cfg.cwvote_local_weight); + tpl_printf(vars, TPLADD, "CWVOTEMAXCANDIDATES", "%d", cfg.cwvote_max_candidates); + tpl_addVar(vars, TPLADD, "CWVOTECOMPARE8", (cfg.cwvote_compare_len == 8) ? "selected" : ""); + tpl_addVar(vars, TPLADD, "CWVOTECOMPARE16", (cfg.cwvote_compare_len == 16) ? "selected" : ""); + tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK0", (cfg.cwvote_fallback == 0) ? "selected" : ""); + tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK1", (cfg.cwvote_fallback == 1) ? "selected" : ""); + tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK2", (cfg.cwvote_fallback == 2) ? "selected" : ""); + value = mk_t_cwvote_caidtab(&cfg.cwvote_caids); + tpl_addVar(vars, TPLADD, "CWVOTECAIDS", value); + free_mk_t(value); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "CACHEEXSRCNAME", (cfg.cacheex_srcname_webif == 1) ? "checked" : ""); +#endif + +#ifdef LEDSUPPORT + if(cfg.enableled == 1) + { tpl_addVar(vars, TPLADD, "ENABLELEDSELECTED1", "selected"); } + else if(cfg.enableled == 2) + { tpl_addVar(vars, TPLADD, "ENABLELEDSELECTED2", "selected"); } +#endif + + return tpl_getTpl(vars, "CONFIGGLOBAL"); +} + +#ifdef WITH_LB +static char *send_oscam_config_loadbalancer(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_LOADBAL); + + if(cs_strlen(getParam(params, "button")) > 0) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. No changes are possible!"); + } + else + { + if(strcmp(getParam(params, "button"), "Load Stats") == 0) + { + clear_all_stat(); + load_stat_from_file(); + tpl_addMsg(vars, "Stats loaded from file"); + } + + if(strcmp(getParam(params, "button"), "Save Stats") == 0) + { + save_stat_to_file(1); + tpl_addMsg(vars, "Stats saved to file"); + } + + if(strcmp(getParam(params, "button"), "Clear Stats") == 0) + { + clear_all_stat(); + tpl_addMsg(vars, "Stats cleared completly"); + } + + if(strcmp(getParam(params, "button"), "Clear Timeouts") == 0) + { + clean_all_stats_by_rc(E_TIMEOUT, 0); + tpl_addMsg(vars, "Timeout cleared from Stats"); + } + + if(strcmp(getParam(params, "button"), "Clear Not Found") == 0) + { + clean_all_stats_by_rc(E_NOTFOUND, 0); + tpl_addMsg(vars, "Not Found cleared from Stats"); + } + + if(strcmp(getParam(params, "button"), "Clear Invalid") == 0) + { + clean_all_stats_by_rc(E_INVALID, 0); + tpl_addMsg(vars, "Invalid cleared from Stats"); + } + } + } + + webif_save_config("global", vars, params); + + tpl_printf(vars, TPLADD, "TMP", "LBMODE%d", cfg.lb_mode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "LBSAVE", "%d", cfg.lb_save); + if(cfg.lb_savepath) { tpl_addVar(vars, TPLADD, "LBSAVEPATH", cfg.lb_savepath); } + + tpl_printf(vars, TPLADD, "LBNBESTREADERS", "%d", cfg.lb_nbest_readers); + char *value = mk_t_caidvaluetab(&cfg.lb_nbest_readers_tab); + tpl_addVar(vars, TPLADD, "LBNBESTPERCAID", value); + free_mk_t(value); + tpl_printf(vars, TPLADD, "LBNFBREADERS", "%d", cfg.lb_nfb_readers); + tpl_printf(vars, TPLADD, "LBMAXREADERS", "%d", cfg.lb_max_readers); + tpl_printf(vars, TPLADD, "LBMINECMCOUNT", "%d", cfg.lb_min_ecmcount); + tpl_printf(vars, TPLADD, "LBMAXECEMCOUNT", "%d", cfg.lb_max_ecmcount); + tpl_printf(vars, TPLADD, "LBRETRYLIMIT", "%d", cfg.lb_retrylimit); + + value = mk_t_caidvaluetab(&cfg.lb_retrylimittab); + tpl_addVar(vars, TPLADD, "LBRETRYLIMITS", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "LBREOPENSECONDS", "%d", cfg.lb_reopen_seconds); + tpl_printf(vars, TPLADD, "LBCLEANUP", "%d", cfg.lb_stat_cleanup); + + tpl_addVar(vars, TPLADD, "LBREOPENINVALID", (cfg.lb_reopen_invalid == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LBFORCEALWAYS", (cfg.lb_force_reopen_always == 1) ? "checked" : ""); + + value = mk_t_caidtab(&cfg.lb_noproviderforcaid); + tpl_addVar(vars, TPLADD, "LBNOPROVIDERFORCAID", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LBAUTOBETATUNNEL", (cfg.lb_auto_betatunnel == 1) ? "checked" : ""); + + if(cfg.lb_auto_betatunnel_mode) + { + tpl_printf(vars, TPLADD, "TMP", "LBAUTOBETATUNNELMODE%d", cfg.lb_auto_betatunnel_mode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + + tpl_printf(vars, TPLADD, "LBPREFERBETA", "%d", cfg.lb_auto_betatunnel_prefer_beta); + + tpl_addVar(vars, TPLADD, "LBAUTOTIMEOUT", (cfg.lb_auto_timeout == 1) ? "checked" : ""); + + tpl_printf(vars, TPLADD, "LBAUTOTIMEOUTP", "%d", cfg.lb_auto_timeout_p); + tpl_printf(vars, TPLADD, "LBAUTOTIMEOUTT", "%d", cfg.lb_auto_timeout_t); + + tpl_addVar(vars, TPLADDONCE, "CONFIG_CONTROL", tpl_getTpl(vars, "CONFIGLOADBALANCERCTRL")); + + return tpl_getTpl(vars, "CONFIGLOADBALANCER"); +} +#endif + +#ifdef MODULE_CAMD33 +static char *send_oscam_config_camd33(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + + setActiveSubMenu(vars, MNU_CFG_CAMD33); + + webif_save_config("camd33", vars, params); + + if(cfg.c33_port) + { + tpl_printf(vars, TPLADD, "PORT", "%d", cfg.c33_port); + if(IP_ISSET(cfg.c33_srvip)) { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.c33_srvip)); } + tpl_addVar(vars, TPLADD, "PASSIVECHECKED", (cfg.c33_passive == 1) ? "checked" : ""); + + for(i = 0; i < (int) sizeof(cfg.c33_key); ++i) { tpl_printf(vars, TPLAPPEND, "KEY", "%02X", cfg.c33_key[i]); } + char *value = mk_t_iprange(cfg.c33_plain); + tpl_addVar(vars, TPLADD, "NOCRYPT", value); + free_mk_t(value); + } + + return tpl_getTpl(vars, "CONFIGCAMD33"); +} +#endif + +#ifdef MODULE_CAMD35 +static char *send_oscam_config_camd35(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_CAMD35); + + webif_save_config("cs357x", vars, params); + + if(cfg.c35_port) + { + tpl_printf(vars, TPLADD, "PORT", "%d", cfg.c35_port); + if(IP_ISSET(cfg.c35_srvip)) + { tpl_addVar(vars, TPLAPPEND, "SERVERIP", cs_inet_ntoa(cfg.c35_srvip)); } + + if(cfg.c35_udp_suppresscmd08) + { tpl_addVar(vars, TPLADD, "SUPPRESSCMD08UDP", "checked"); } + + } + return tpl_getTpl(vars, "CONFIGCAMD35"); +} +#endif + +#ifdef MODULE_CAMD35_TCP +static char *send_oscam_config_camd35tcp(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_CAMD35TCP); + + webif_save_config("cs378x", vars, params); + + if((cfg.c35_tcp_ptab.nports > 0) && (cfg.c35_tcp_ptab.ports[0].s_port > 0)) + { + + char *value = mk_t_camd35tcp_port(); + tpl_addVar(vars, TPLADD, "PORT", value); + free_mk_t(value); + + if(IP_ISSET(cfg.c35_tcp_srvip)) + { tpl_addVar(vars, TPLAPPEND, "SERVERIP", cs_inet_ntoa(cfg.c35_tcp_srvip)); } + + if(cfg.c35_tcp_suppresscmd08) + { tpl_addVar(vars, TPLADD, "SUPPRESSCMD08TCP", "checked"); } + } + return tpl_getTpl(vars, "CONFIGCAMD35TCP"); +} +#endif + +static char *send_oscam_config_cache(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_CACHE); + + webif_save_config("cache", vars, params); + + tpl_printf(vars, TPLADD, "CACHEDELAY", "%u", cfg.delay); + + tpl_printf(vars, TPLADD, "MAXCACHETIME", "%d", cfg.max_cache_time); + +#ifdef CS_CACHEEX + char *value = NULL; + +#ifdef CS_CACHEEX_AIO + value = mk_t_cacheex_cwcheck_valuetab(&cfg.cw_cache_settings); + tpl_addVar(vars, TPLADD, "CWCACHESETTINGS", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "CWCACHESIZE", "%d", cfg.cw_cache_size); + + tpl_printf(vars, TPLADD, "CWCACHEMEMORY", "%d", cfg.cw_cache_memory); + + tpl_printf(vars, TPLADD, "ECMCACHESIZE", "%d", cfg.ecm_cache_size); + + tpl_printf(vars, TPLADD, "ECMCACHEMEMORY", "%d", cfg.ecm_cache_memory); + + tpl_printf(vars, TPLADD, "ECMDROPTIME", "%d", cfg.ecm_cache_droptime); +#endif + + value = mk_t_cacheex_valuetab(&cfg.cacheex_wait_timetab); + tpl_addVar(vars, TPLADD, "WAIT_TIME", value); + free_mk_t(value); + +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "WAITTIME_BLOCK_START", "%d", cfg.waittime_block_start); + + tpl_printf(vars, TPLADD, "WAITTIME_BLOCK_TIME", "%d", cfg.waittime_block_time); +#endif + + value = mk_t_caidvaluetab(&cfg.cacheex_mode1_delay_tab); + tpl_addVar(vars, TPLADD, "CACHEEXMODE1DELAY", value); + free_mk_t(value); + +#ifdef CS_CACHEEX_AIO + value = mk_t_caidvaluetab(&cfg.cacheex_nopushafter_tab); + tpl_addVar(vars, TPLADD, "CACHEEXNOPUSHAFTER", value); + free_mk_t(value); +#endif + + tpl_printf(vars, TPLADD, "MAX_HIT_TIME", "%d", cfg.max_hitcache_time); + + tpl_addVar(vars, TPLADD, "CACHEEXSTATSSELECTED", (cfg.cacheex_enable_stats == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "WTTCHECKED", (cfg.wait_until_ctimeout == 1) ? "checked" : ""); + +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "CACHEEXDROPDIFFS", (cfg.cacheex_dropdiffs == 1) ? "checked" : ""); + + value = mk_t_group(cfg.cacheex_push_lg_groups); + tpl_addVar(vars, TPLADD, "CACHEEXPUSHLGGRPS", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LGONLYREMOTESETTINGSCHECKED", (cfg.cacheex_lg_only_remote_settings == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYCHECKED", (cfg.cacheex_localgenerated_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&cfg.cacheex_lg_only_tab); + tpl_addVar(vars, TPLADD, "LGONLYTAB", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYINCHECKED", (cfg.cacheex_localgenerated_only_in == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "LGONLYINAIOONLYCHECKED", (cfg.cacheex_lg_only_in_aio_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&cfg.cacheex_lg_only_in_tab); + tpl_addVar(vars, TPLADD, "LGONLYINTAB", value); + free_mk_t(value); + + value = mk_t_cacheex_hitvaluetab(&cfg.cacheex_filter_caidtab); + tpl_addVar(vars, TPLADD, "CACHEEXECMFILTER", value); + free_mk_t(value); + + value = mk_t_cacheex_hitvaluetab(&cfg.cacheex_filter_caidtab_aio); + tpl_addVar(vars, TPLADD, "CACHEEXECMFILTERAIO", value); + free_mk_t(value); +#endif + + if(cfg.csp_port) + { tpl_printf(vars, TPLADD, "PORT", "%d", cfg.csp_port); } + + if(IP_ISSET(cfg.csp_srvip)) + { tpl_addVar(vars, TPLAPPEND, "SERVERIP", cs_inet_ntoa(cfg.csp_srvip)); } + + value = mk_t_cacheex_hitvaluetab(&cfg.csp.filter_caidtab); + tpl_addVar(vars, TPLADD, "CSP_ECM_FILTER", value); + free_mk_t(value); + + value = mk_t_cacheex_cwcheck_valuetab(&cfg.cacheex_cwcheck_tab); + tpl_addVar(vars, TPLADD, "CACHEEXCWCHECK", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "ARCHECKED", (cfg.csp.allow_request == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "ARFCHECKED", (cfg.csp.allow_reforward == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "BLOCKFAKECWSCHECKED", (cfg.csp.block_fakecws == 1) ? "checked" : ""); +#endif + +#ifdef CW_CYCLE_CHECK +#ifndef CS_CACHEEX + char *value = NULL; +#endif + + tpl_addVar(vars, TPLADD, "CWCYCLECHECK", (cfg.cwcycle_check_enable == 1) ? "checked" : ""); + + value = mk_t_caidtab(&cfg.cwcycle_check_caidtab); + tpl_addVar(vars, TPLADD, "CWCYCLECHECKCAID", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "MAXCYCLELIST", "%d", cfg.maxcyclelist); + tpl_printf(vars, TPLADD, "KEEPCYCLETIME", "%d", cfg.keepcycletime); + + if(cfg.onbadcycle) + { + tpl_printf(vars, TPLADD, "TMP", "ONBADCYCLE%d", cfg.onbadcycle); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + + tpl_addVar(vars, TPLADD, "DROPOLD", (cfg.cwcycle_dropold == 1) ? "checked" : ""); + + if(cfg.cwcycle_sensitive) + { + tpl_printf(vars, TPLADD, "TMP", "CWCSEN%d", cfg.cwcycle_sensitive); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + + tpl_addVar(vars, TPLADD, "ALLOWBADFROMFFB", (cfg.cwcycle_allowbadfromffb == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "USECWCFROMCE", (cfg.cwcycle_usecwcfromce == 1) ? "checked" : ""); + + +#endif + +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "CONFIGCACHEAIO"); +#else + return tpl_getTpl(vars, "CONFIGCACHE"); +#endif +} + +#ifdef MODULE_NEWCAMD +static char *send_oscam_config_newcamd(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + + setActiveSubMenu(vars, MNU_CFG_NEWCAMD); + + webif_save_config("newcamd", vars, params); + + if((cfg.ncd_ptab.nports > 0) && (cfg.ncd_ptab.ports[0].s_port > 0)) + { + + char *value = mk_t_newcamd_port(); + tpl_addVar(vars, TPLADD, "PORT", value); + free_mk_t(value); + + if(IP_ISSET(cfg.ncd_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.ncd_srvip)); } + + for(i = 0; i < (int32_t)sizeof(cfg.ncd_key); i++) + { tpl_printf(vars, TPLAPPEND, "KEY", "%02X", cfg.ncd_key[i]); } + + value = mk_t_iprange(cfg.ncd_allowed); + tpl_addVar(vars, TPLADD, "ALLOWED", value); + free_mk_t(value); + + if(cfg.ncd_keepalive) + { tpl_addVar(vars, TPLADD, "KEEPALIVE", "checked"); } + if(cfg.ncd_mgclient) + { tpl_addVar(vars, TPLADD, "MGCLIENTCHK", "checked"); } + } + return tpl_getTpl(vars, "CONFIGNEWCAMD"); +} +#endif + +#ifdef MODULE_GBOX +static char *send_oscam_config_gbox(struct templatevars *vars, struct uriparams *params) +{ + uint8_t i=0; + char local_gbox_save_gsms[2],local_gbox_msg_type[3], local_gbox_dest_peers[GBOX_MAX_DEST_PEERS*5], tmp_gbox_dest_peers[GBOX_MAX_DEST_PEERS*5] ; + int n=0, len_gbox_save_gsms=0, len_gbox_msg_type=0, len_gbox_dest_peers=0, len_gbox_msg_txt=0; + char *ptr1, *saveptr1, *isbroadcast = NULL; + const char *s; + uint16_t gbox_dest_peers_tmp; + + setActiveSubMenu(vars, MNU_CFG_GBOX); + webif_save_config("gbox", vars, params); + /* + * Action when GetOnlinePeers is pressed + */ + if(streq(getParam(params, "action"), "Online peers")) + { + gbox_get_online_peers(); + // init var + len_gbox_save_gsms=cs_strlen(getParam(params, "gbox_msg_type")); + len_gbox_msg_type=cs_strlen(getParam(params, "gbox_msg_type")); + len_gbox_msg_txt=cs_strlen(getParam(params, "gbox_msg_txt")); + if(len_gbox_msg_txt>GBOX_MAX_MSG_TXT) { len_gbox_msg_txt=GBOX_MAX_MSG_TXT; } + // retrieve value from Webif + cs_strncpy(local_gbox_save_gsms, getParam(params, "gbox_save_gsms"), len_gbox_save_gsms+1); + cfg.gbox_save_gsms=atoi(local_gbox_save_gsms); + cs_strncpy(local_gbox_msg_type, getParam(params, "gbox_msg_type"), len_gbox_msg_type+1); + cfg.gbox_msg_type=atoi(local_gbox_msg_type); + cs_strncpy(cfg.gbox_msg_txt,getParam(params, "gbox_msg_txt"), len_gbox_msg_txt+1); + } + /* + * Action when ResetGSMS button is pressed + */ + if(streq(getParam(params, "action"), "resetallgsms")) + { + cfg.gbox_save_gsms = 0; + cfg.gbox_msg_type = 0; + for(i = 0; i < GBOX_MAX_DEST_PEERS; i++) + { + cfg.gbox_dest_peers[i]='\0'; + } + cfg.gbox_dest_peers_num=0; + for(i = 0; i < GBOX_MAX_MSG_TXT; i++) + { + cfg.gbox_msg_txt[i]='\0'; + } + tpl_addMsg(vars, "GBOX: Reset GSMS datas done!"); + } + /* + * Action when Send GSMS is pressed + */ + if(streq(getParam(params, "action"), "Send GSMS")) + { + // init var + len_gbox_msg_type=cs_strlen(getParam(params, "gbox_msg_type")); + len_gbox_dest_peers=cs_strlen(trim(getParam(params, "gbox_dest_peers"))); + len_gbox_msg_txt=cs_strlen(getParam(params, "gbox_msg_txt")); + if(len_gbox_msg_txt>GBOX_MAX_MSG_TXT) { len_gbox_msg_txt=GBOX_MAX_MSG_TXT; } + // retrieve value from Webif + cs_strncpy(local_gbox_msg_type, getParam(params, "gbox_msg_type"), len_gbox_msg_type+1); + cfg.gbox_msg_type=atoi(local_gbox_msg_type); + cs_strncpy(local_gbox_dest_peers, strtoupper(trim(getParam(params, "gbox_dest_peers"))), len_gbox_dest_peers+1); + cs_strncpy(tmp_gbox_dest_peers, strtoupper(trim(getParam(params, "gbox_dest_peers"))), len_gbox_dest_peers+1); + cs_strncpy(cfg.gbox_msg_txt,getParam(params, "gbox_msg_txt"), len_gbox_msg_txt+1); + n=0; + for (ptr1 = strtok_r(tmp_gbox_dest_peers, ",", &saveptr1); (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s=trim(ptr1); + if ((n < GBOX_MAX_DEST_PEERS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.gbox_dest_peers[n++] = a2i(trim(ptr1), cs_strlen(trim(ptr1))); } + } + cfg.gbox_dest_peers_num = n; + /* + Start sending GBox SMS + */ + if((cs_strlen(cfg.gbox_msg_txt) > 5)) + { + isbroadcast=strstr(local_gbox_dest_peers, "FFFF"); + if(isbroadcast == NULL) + { + n =0; + for (i = 0, ptr1 = strtok_r(local_gbox_dest_peers, ",", &saveptr1); (i < 4) && (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s=ptr1; + if ((n < GBOX_MAX_DEST_PEERS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { + gbox_dest_peers_tmp = a2i(ptr1, 4); + if(gbox_direct_send_gsms(gbox_dest_peers_tmp, cfg.gbox_msg_type, cfg.gbox_msg_txt)) { cs_log("GBOX message sent to[%04X] type[%d] text[%s] ", gbox_dest_peers_tmp, cfg.gbox_msg_type, cfg.gbox_msg_txt);} + n++; + } + } + tpl_addMsg(vars, "GBOX Send SMS: individual messages started."); + } + else + { + if(gbox_direct_send_gsms(0xFFFF, cfg.gbox_msg_type, cfg.gbox_msg_txt)) { cs_log("GBOX broadcast message sent type[%d] text[%s] ", cfg.gbox_msg_type, cfg.gbox_msg_txt);} + tpl_addMsg(vars, "GBOX Send SMS: broadcast started."); + } + } + else + { + cs_log("GBox SMS: destination peers or message text not specified or too short"); + tpl_addMsg(vars, "GBOX: Send SMS failed - error in input fields: dest peers or text message."); + } + } + + tpl_addVar(vars, TPLADD, "HOSTNAME", xml_encode(vars, cfg.gbox_hostname)); + char *value0 = mk_t_gbox_port(); + tpl_addVar(vars, TPLAPPEND, "PORT", value0); + free_mk_t(value0); + tpl_printf(vars, TPLADD, "MYGBOXPASSWORD", "%08X", cfg.gbox_password); + tpl_printf(vars, TPLADD, "MYGBOXID", "%04X", gbox_get_local_gbox_id()); + tpl_printf(vars, TPLADD, "GBOXRECONNECT", "%d", cfg.gbox_reconnect); + tpl_printf(vars, TPLADD, "GBOXMYVERS", "%02X", cfg.gbox_my_vers); + tpl_printf(vars, TPLAPPEND, "GBOXMYCPUAPI", "%02X", cfg.gbox_my_cpu_api); +#ifdef MODULE_CCCAM + if(cfg.cc_gbx_reshare_en == 1) { tpl_addVar(vars, TPLADD, "GBOXCCCRESHARE", "checked"); } + tpl_addVar(vars, TPLAPPEND, "CCCDEPENDINGCONFIG", tpl_getTpl(vars, "CCCAMRESHAREBIT")); +#endif + if(cfg.log_hello == 1) { tpl_addVar(vars, TPLADD, "GBOXLOGHELLO", "checked"); } + if(cfg.gsms_dis == 1) { tpl_addVar(vars, TPLADD, "GBOXGSMSDISABLE", "checked"); } + if(cfg.dis_attack_txt == 1) { tpl_addVar(vars, TPLADD, "GBOXDISATTACKTXT", "checked"); } + if(cfg.gbox_tmp_dir != NULL) { tpl_addVar(vars, TPLADD, "GBOXTMPDIR", cfg.gbox_tmp_dir); } + char *value1 = mk_t_gbox_proxy_card(); + tpl_addVar(vars, TPLAPPEND, "GBOXPROXYCARD", value1); + free_mk_t(value1); + char *value2 = mk_t_gbox_ignored_peer(); + tpl_addVar(vars, TPLAPPEND, "GBOXIGNOREDPEER", value2); + free_mk_t(value2); + char *value3 = mk_t_gbox_block_ecm(); + tpl_addVar(vars, TPLAPPEND, "GBOXBLOCKECM", value3); + free_mk_t(value3); + char *value4 = mk_t_accept_remm_peer(); + tpl_addVar(vars, TPLAPPEND, "GBOXACCEPTREMM", value4); + free_mk_t(value4); +/* + * GBOX SMS +*/ + tpl_addVar(vars, TPLADD, "GBOXSAVEGSMS", (cfg.gbox_save_gsms == 1) ? "checked" : ""); + if(cfg.gbox_msg_type == 0) + { + tpl_addVar(vars, TPLADD, "GBOXMSGTYPENORMAL", "selected"); + } + else if(cfg.gbox_msg_type == 1) + { + tpl_addVar(vars, TPLADD, "GBOXMSGTYPEOSD", "selected"); + } + char *gmsg_dest_peers = mk_t_gbox_dest_peers(); + tpl_addVar(vars, TPLADD, "GBOXMSGDESTPEERS", gmsg_dest_peers); + free_mk_t(gmsg_dest_peers); + tpl_addVar(vars, TPLADD, "GBOXMSGTXT", cfg.gbox_msg_txt); + + return tpl_getTpl(vars, "CONFIGGBOX"); +} +#endif + +#ifdef MODULE_RADEGAST +static char *send_oscam_config_radegast(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_RADEGAST); + + webif_save_config("radegast", vars, params); + + tpl_printf(vars, TPLADD, "PORT", "%d", cfg.rad_port); + if(IP_ISSET(cfg.rad_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.rad_srvip)); } + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, cfg.rad_usr)); + + char *value = mk_t_iprange(cfg.rad_allowed); + tpl_addVar(vars, TPLADD, "ALLOWED", value); + free_mk_t(value); + + return tpl_getTpl(vars, "CONFIGRADEGAST"); +} +#endif + +#ifdef MODULE_SCAM +static char *send_oscam_config_scam(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_SCAM); + + webif_save_config("scam", vars, params); + + tpl_printf(vars, TPLADD, "PORT", "%d", cfg.scam_port); + if(IP_ISSET(cfg.scam_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.scam_srvip)); } + + char *value = mk_t_iprange(cfg.scam_allowed); + tpl_addVar(vars, TPLADD, "ALLOWED", value); + free_mk_t(value); + + return tpl_getTpl(vars, "CONFIGSCAM"); +} +#endif + +#ifdef MODULE_STREAMRELAY +static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uriparams *params) +{ + char *value; + + setActiveSubMenu(vars, MNU_CFG_STREAMRELAY); + + webif_save_config("streamrelay", vars, params); + + tpl_printf(vars, TPLADD, "TMP", "STREAMRELAYENABLEDSELECTED%d", cfg.stream_relay_enabled); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_printf(vars, TPLADD, "STREAM_RELAY_PORT", "%d", cfg.stream_relay_port); + if(cfg.stream_relay_user) + { tpl_printf(vars, TPLADD, "STREAM_RELAY_USER", "%s", cfg.stream_relay_user); } + value = mk_t_caidtab(&cfg.stream_relay_ctab); + tpl_addVar(vars, TPLADD, "STREAM_RELAY_CTAB", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "STREAM_SOURCE_HOST", "%s", cfg.stream_source_host); + tpl_addVar(vars, TPLADD, "STREAM_CLIENT_SOURCE_HOST", (cfg.stream_client_source_host == 1) ? "checked" : ""); + tpl_printf(vars, TPLADD, "STREAM_SOURCE_PORT", "%d", cfg.stream_source_port); + if(cfg.stream_source_auth_user) + { tpl_printf(vars, TPLADD, "STREAM_SOURCE_AUTH_USER", "%s", cfg.stream_source_auth_user); } + if(cfg.stream_source_auth_password) + { tpl_printf(vars, TPLADD, "STREAM_SOURCE_AUTH_PASSWORD", "%s", cfg.stream_source_auth_password); } + + tpl_printf(vars, TPLADD, "STREAM_RELAY_BUFFER_TIME", "%d", cfg.stream_relay_buffer_time); + tpl_printf(vars, TPLADD, "STREAM_RELAY_RECONNECT_COUNT", "%d", cfg.stream_relay_reconnect_count); +#ifdef WITH_EMU + tpl_printf(vars, TPLADD, "TMP", "STREAMEMMENABLEDSELECTED%d", cfg.emu_stream_emm_enabled); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_printf(vars, TPLADD, "STREAM_ECM_DELAY", "%d", cfg.emu_stream_ecm_delay); +#endif + + tpl_printf(vars, TPLADD, "TMP", "STREAMCONFIGCLIENTSELECTED%d", cfg.stream_display_client); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_addVar(vars, TPLADD, "STREAM_REUSE_CLIENT", (cfg.stream_reuse_client == 1) ? "checked" : ""); +#ifdef WEBIF + tpl_addVar(vars, TPLADD, "STREAM_HIDE_CLIENT", (cfg.stream_hide_client == 1) ? "checked" : ""); +#endif + + return tpl_getTpl(vars, "CONFIGSTREAMRELAY"); +} +#endif + +#ifdef MODULE_CCCAM +static char *send_oscam_config_cccam(struct templatevars *vars, struct uriparams *params) +{ + + setActiveSubMenu(vars, MNU_CFG_CCCAM); + + if(strcmp(getParam(params, "button"), "Refresh list") == 0) + { + cs_log_dbg(D_TRACE, "Entitlements: Refresh Shares start"); +#ifdef MODULE_CCCSHARE + refresh_shares(); +#endif + cs_log_dbg(D_TRACE, "Entitlements: Refresh Shares finished"); + tpl_addMsg(vars, "Refresh Shares started"); + } + + webif_save_config("cccam", vars, params); + + if(streq(getParam(params, "action"), "execute") && !cfg.http_readonly) + { cc_update_nodeid(); } + + char *value = mk_t_cccam_port(); + tpl_addVar(vars, TPLAPPEND, "PORT", value); + free_mk_t(value); + + if(IP_ISSET(cfg.cc_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.cc_srvip)); } + + tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare); + + if(!strcmp((char *)cfg.cc_version, "2.0.11")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED0", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.1.1")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED1", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.1.2")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED2", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.1.3")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED3", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.1.4")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED4", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.2.0")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED5", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.2.1")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED6", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.3.0")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED7", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.3.1")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED8", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.3.2")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED9", "selected"); + } + + tpl_printf(vars, TPLADD, "UPDATEINTERVAL", "%d", cfg.cc_update_interval); + tpl_printf(vars, TPLADD, "RECV_TIMEOUT", "%u", cfg.cc_recv_timeout); + + tpl_addVar(vars, TPLADD, "STEALTH", (cfg.cc_stealth == 1) ? "checked" : ""); + + tpl_printf(vars, TPLADD, "NODEID", "%02X%02X%02X%02X%02X%02X%02X%02X", + cfg.cc_fixed_nodeid[0], cfg.cc_fixed_nodeid[1], cfg.cc_fixed_nodeid[2], cfg.cc_fixed_nodeid[3], + cfg.cc_fixed_nodeid[4], cfg.cc_fixed_nodeid[5], cfg.cc_fixed_nodeid[6], cfg.cc_fixed_nodeid[7]); + + tpl_printf(vars, TPLADD, "TMP", "MINIMIZECARDSELECTED%d", cfg.cc_minimize_cards); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "TMP", "RESHAREMODE%d", cfg.cc_reshare_services); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "TMP", "IGNRSHRSELECTED%d", cfg.cc_ignore_reshare); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_addVar(vars, TPLADD, "FORWARDORIGINCARD", (cfg.cc_forward_origin_card == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "KEEPCONNECTED", (cfg.cc_keep_connected == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADDONCE, "CONFIG_CONTROL", tpl_getTpl(vars, "CONFIGCCCAMCTRL")); + + return tpl_getTpl(vars, "CONFIGCCCAM"); +} +#endif + +static bool is_ext(const char *path, const char *ext) +{ + size_t lenpath = cs_strlen(path); + size_t lenext = cs_strlen(ext); + if(lenext > lenpath) + { return 0; } + return memcmp(path + lenpath - lenext, ext, lenext) == 0; +} + +static char *send_oscam_config_webif(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + + setActiveSubMenu(vars, MNU_CFG_WEBIF); + + webif_save_config("webif", vars, params); + + tpl_printf(vars, TPLADD, "HTTPPORT", "%s%d", cfg.http_use_ssl ? "+" : "", cfg.http_port); + if(IP_ISSET(cfg.http_srvip)) + { tpl_addVar(vars, TPLAPPEND, "SERVERIP", cs_inet_ntoa(cfg.http_srvip)); } + + tpl_addVar(vars, TPLADD, "HTTPUSER", cfg.http_user); + tpl_addVar(vars, TPLADD, "HTTPPASSWORD", cfg.http_pwd); + tpl_addVar(vars, TPLADD, "HTTPOSCAMLABEL", cfg.http_oscam_label); + + // css style selector + tpl_printf(vars, TPLADD, "CSSOPTIONS", "\t\t\t\t\t\t\n", + !cfg.http_css ? " selected" : ""); + + if(cfg.http_tpl) + { + char path[255]; + tpl_getFilePathInSubdir(cfg.http_tpl, "", "style", ".css", path, 255); + if(file_exists(path)) + tpl_printf(vars, TPLAPPEND, "CSSOPTIONS", "\t\t\t\t\t\t\n", + path, + cfg.http_css && strstr(cfg.http_css, path) ? " selected" : "", + path); + } + + struct dirent **namelist; + int count = scandir(cs_confdir, &namelist, 0, NULL); + + if( count >= 0 ) + { + for( i = 0 ; i < count; i++ ) + { + if(is_ext(namelist[i]->d_name, ".css")) + { + tpl_printf(vars, TPLAPPEND, "CSSOPTIONS", "\t\t\t\t\t\t\n", + cs_confdir, + namelist[i]->d_name, + cfg.http_css && strstr(cfg.http_css, namelist[i]->d_name) ? " selected" : "", + cs_confdir, namelist[i]->d_name); + } + free( namelist[i] ); + } + free(namelist); + } + + if(cfg.http_prepend_embedded_css) + { tpl_addVar(vars, TPLADD, "HTTPPREPENDEMBEDDEDCSS", "checked"); } + + tpl_addVar(vars, TPLADD, "HTTPHELPLANG", cfg.http_help_lang); + tpl_addVar(vars, TPLADD, "HTTPLOCALE", cfg.http_locale); + tpl_printf(vars, TPLADD, "HTTPEMMUCLEAN", "%d", cfg.http_emmu_clean); + tpl_printf(vars, TPLADD, "HTTPEMMSCLEAN", "%d", cfg.http_emms_clean); + tpl_printf(vars, TPLADD, "HTTPEMMGCLEAN", "%d", cfg.http_emmg_clean); + tpl_printf(vars, TPLADD, "HTTPREFRESH", "%d", cfg.http_refresh); + tpl_printf(vars, TPLADD, "HTTPPOLLREFRESH", "%d", cfg.poll_refresh); + tpl_addVar(vars, TPLADD, "HTTPTPL", cfg.http_tpl); + tpl_addVar(vars, TPLADD, "HTTPPICONPATH", cfg.http_piconpath); + tpl_addVar(vars, TPLADD, "HTTPSCRIPT", cfg.http_script); + tpl_addVar(vars, TPLADD, "HTTPJSCRIPT", cfg.http_jscript); +#ifndef WEBIF_JQUERY + tpl_addVar(vars, TPLADD, "HTTPEXTERNJQUERY", cfg.http_extern_jquery); +#endif + tpl_printf(vars, TPLADD, "HTTPPICONSIZE", "%d", cfg.http_picon_size); + + if(cfg.http_hide_idle_clients > 0) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + tpl_addVar(vars, TPLADD, "HTTPHIDETYPE", cfg.http_hide_type); + if(cfg.http_status_log > 0) { tpl_addVar(vars, TPLADD, "SHOWLOGCHECKED", "checked"); } + if(cfg.http_showpicons > 0) { tpl_addVar(vars, TPLADD, "SHOWPICONSCHECKED", "checked"); } + if(cfg.http_showmeminfo > 0) { tpl_addVar(vars, TPLADD, "SHOWMEMINFOCHECKED", "checked"); } + if(cfg.http_showuserinfo > 0) { tpl_addVar(vars, TPLADD, "SHOWUSERINFOCHECKED", "checked"); } + if(cfg.http_showreaderinfo > 0) { tpl_addVar(vars, TPLADD, "SHOWREADERINFOCHECKED", "checked"); } + if(cfg.http_showcacheexinfo > 0) { tpl_addVar(vars, TPLADD, "SHOWCACHEEXINFOCHECKED", "checked"); } + if(cfg.http_showloadinfo > 0) { tpl_addVar(vars, TPLADD, "SHOWLOADINFOCHECKED", "checked"); } + if(cfg.http_showecminfo > 0) { tpl_addVar(vars, TPLADD, "SHOWECMINFOCHECKED", "checked"); } + + char *value = mk_t_iprange(cfg.http_allowed); + tpl_addVar(vars, TPLADD, "HTTPALLOW", value); + free_mk_t(value); + + for(i = 0; i < MAX_HTTP_DYNDNS; i++) + { + if(cfg.http_dyndns[i][0]) + { + tpl_addVar(vars, TPLAPPEND, "HTTPDYNDNS", i > 0 ? "," : ""); + tpl_addVar(vars, TPLAPPEND, "HTTPDYNDNS", (char *)cfg.http_dyndns[i]); + } + } + + tpl_addVar(vars, TPLADD, "HTTPSAVEFULLSELECT", (cfg.http_full_cfg == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "HTTPOVERWRITEBAKFILE", (cfg.http_overwrite_bak_file == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "HTTPREADONLY", (cfg.http_readonly == 1) ? "checked" : ""); + + +#ifdef WITH_SSL + if(cfg.http_cert != NULL) { tpl_addVar(vars, TPLADD, "HTTPCERT", cfg.http_cert); } + tpl_addVar(vars, TPLADD, "HTTPFORCESECUREMODESELECT", (cfg.https_force_secure_mode == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "HTTPAUTOCREATECERTSELECT", (cfg.https_auto_create_cert == 1) ? "checked" : ""); +#endif + +#ifndef WEBIF_JQUERY + tpl_addVar(vars, TPLADDONCE, "CONFIGWEBIFJQUERY", tpl_getTpl(vars, "CONFIGWEBIFJQUERYBIT")); +#endif + + tpl_printf(vars, TPLADD, "AULOW", "%d", cfg.aulow); + tpl_printf(vars, TPLADD, "HIDECLIENTTO", "%d", cfg.hideclient_to); + + return tpl_getTpl(vars, "CONFIGWEBIF"); +} + +#ifdef LCDSUPPORT +static char *send_oscam_config_lcd(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_LCD); + + webif_save_config("lcd", vars, params); + + tpl_addVar(vars, TPLADD, "ENABLELCDSELECTED", (cfg.enablelcd == 1) ? "checked" : ""); + + if(cfg.lcd_output_path != NULL) + { tpl_addVar(vars, TPLADD, "LCDOUTPUTPATH", cfg.lcd_output_path); } + + tpl_addVar(vars, TPLADD, "LCDHIDEIDLE", (cfg.lcd_hide_idle == 1) ? "checked" : ""); + + tpl_printf(vars, TPLADD, "LCDREFRESHINTERVAL", "%d", cfg.lcd_write_intervall); + + return tpl_getTpl(vars, "CONFIGLCD"); +} +#endif + +#ifdef MODULE_MONITOR +static char *send_oscam_config_monitor(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_MONITOR); + + webif_save_config("monitor", vars, params); + + tpl_printf(vars, TPLADD, "MONPORT", "%d", cfg.mon_port); + if(IP_ISSET(cfg.mon_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.mon_srvip)); } + + tpl_printf(vars, TPLADD, "AULOW", "%d", cfg.aulow); + tpl_printf(vars, TPLADD, "HIDECLIENTTO", "%d", cfg.hideclient_to); + + char *value = mk_t_iprange(cfg.mon_allowed); + tpl_addVar(vars, TPLADD, "NOCRYPT", value); + free_mk_t(value); + + //Monlevel selector + tpl_printf(vars, TPLADD, "TMP", "MONSELECTED%d", cfg.mon_level); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + return tpl_getTpl(vars, "CONFIGMONITOR"); +} +#endif + +#ifdef MODULE_SERIAL +static char *send_oscam_config_serial(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_SERIAL); + + webif_save_config("serial", vars, params); + + if(cfg.ser_device) + { + char sdevice[cs_strlen(cfg.ser_device)]; + cs_strncpy(sdevice, cfg.ser_device, sizeof(sdevice)); + char *ptr, *saveptr1 = NULL; + char delimiter[2]; + delimiter[0] = 1; + delimiter[1] = '\0'; + for(ptr = strtok_r(sdevice, delimiter, &saveptr1); ptr; ptr = strtok_r(NULL, delimiter, &saveptr1)) + { + tpl_addVar(vars, TPLADD, "SERIALDEVICE", xml_encode(vars, ptr)); + tpl_addVar(vars, TPLAPPEND, "DEVICES", tpl_getTpl(vars, "CONFIGSERIALDEVICEBIT")); + } + } + + tpl_addVar(vars, TPLADD, "SERIALDEVICE", ""); + tpl_addVar(vars, TPLAPPEND, "DEVICES", tpl_getTpl(vars, "CONFIGSERIALDEVICEBIT")); + + return tpl_getTpl(vars, "CONFIGSERIAL"); +} +#endif + +#ifdef HAVE_DVBAPI +extern const char *boxdesc[]; + +static char *send_oscam_config_dvbapi(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + + setActiveSubMenu(vars, MNU_CFG_DVBAPI); + + webif_save_config("dvbapi", vars, params); + + if(cfg.dvbapi_enabled > 0) + { tpl_addVar(vars, TPLADD, "ENABLEDCHECKED", "checked"); } + + if(cfg.dvbapi_au > 0) + { tpl_addVar(vars, TPLADD, "AUCHECKED", "checked"); } + + if(cfg.dvbapi_delayer > 0) + { tpl_printf(vars, TPLADD, "DELAYER", "%d", cfg.dvbapi_delayer); } + + tpl_printf(vars, TPLADD, "BOXTYPE", "\n", cfg.dvbapi_boxtype == 0 ? " selected" : ""); + for(i = 1; i <= BOXTYPES; i++) + { + tpl_printf(vars, TPLAPPEND, "BOXTYPE", "%s\n", cfg.dvbapi_boxtype == i ? " selected" : "", boxdesc[i]); + } + + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, cfg.dvbapi_usr)); + + //PMT Mode + tpl_printf(vars, TPLADD, "TMP", "PMTMODESELECTED%d", cfg.dvbapi_pmtmode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + //Request Mode + tpl_printf(vars, TPLADD, "TMP", "REQMODESELECTED%d", cfg.dvbapi_requestmode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + //ecminfo_file + if(cfg.dvbapi_ecminfo_file > 0) + { tpl_addVar(vars, TPLADD, "ECMINFOFILECHECKED", "checked"); } + + //ecminfo_type + tpl_printf(vars, TPLADD, "TMP", "ECMINFOTYPESELECTED%d", cfg.dvbapi_ecminfo_type); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + //read_sdt + tpl_printf(vars, TPLADD, "TMP", "READSDTSELECTED%d", cfg.dvbapi_read_sdt); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + +#ifdef WITH_EXTENDED_CW + //extended_cw_api + tpl_printf(vars, TPLADD, "TMP", "EXTENDEDCWAPISELECTED%d", cfg.dvbapi_extended_cw_api); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); +#endif + + //write_sdt_prov + if(cfg.dvbapi_write_sdt_prov > 0) + { tpl_addVar(vars, TPLADD, "WRITESDTPROVCHECKED", "checked"); } + +#ifdef MODULE_STREAMRELAY + //demuxer_fix + if(cfg.dvbapi_demuxer_fix > 0) + { tpl_addVar(vars, TPLADD, "DEMUXERFIXCHECKED", "checked"); } +#endif + + //TCP listen port + if(cfg.dvbapi_listenport > 0) + { tpl_printf(vars, TPLADD, "LISTENPORT", "%d", cfg.dvbapi_listenport); } + + if(IP_ISSET(cfg.dvbapi_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.dvbapi_srvip)); } + + return tpl_getTpl(vars, "CONFIGDVBAPI"); +} +#endif + +#ifdef CS_ANTICASC +static char *send_oscam_config_anticasc(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_ANTICASC); + + webif_save_config("anticasc", vars, params); + + if(cfg.ac_enabled > 0) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + tpl_printf(vars, TPLADD, "NUMUSERS", "%d", cfg.ac_users); + tpl_printf(vars, TPLADD, "SAMPLETIME", "%d", cfg.ac_stime); + tpl_printf(vars, TPLADD, "SAMPLES", "%d", cfg.ac_samples); + + tpl_printf(vars, TPLADD, "TMP", "PENALTY%d", cfg.ac_penalty); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + if(cfg.ac_logfile) + { tpl_addVar(vars, TPLADD, "ACLOGFILE", cfg.ac_logfile); } + tpl_printf(vars, TPLADD, "FAKEDELAY", "%d", cfg.ac_fakedelay); + tpl_printf(vars, TPLADD, "DENYSAMPLES", "%d", cfg.ac_denysamples); + + if(cfg.acosc_enabled == 1) + { tpl_addVar(vars, TPLADD, "ACOSC_CHECKED", "checked"); } + tpl_printf(vars, TPLADD, "ACOSC_MAX_ECMS_PER_MINUTE", "%d", cfg.acosc_max_ecms_per_minute); + tpl_printf(vars, TPLADD, "ACOSC_MAX_ACTIVE_SIDS", "%d", cfg.acosc_max_active_sids); + tpl_printf(vars, TPLADD, "ACOSC_ZAP_LIMIT", "%d", cfg.acosc_zap_limit); + tpl_printf(vars, TPLADD, "TMP", "ACOSC_PENALTY%d", cfg.acosc_penalty); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_printf(vars, TPLADD, "ACOSC_PENALTY_DURATION", "%d", cfg.acosc_penalty_duration); + tpl_printf(vars, TPLADD, "ACOSC_DELAY", "%d", cfg.acosc_delay); + + return tpl_getTpl(vars, "CONFIGANTICASC"); +} +#endif + +static char *send_oscam_config(struct templatevars *vars, struct uriparams *params) +{ + + setActiveMenu(vars, MNU_CONFIG); + + char *part = getParam(params, "part"); + if(!strcmp(part, "webif")) { return send_oscam_config_webif(vars, params); } +#ifdef MODULE_MONITOR + else if(!strcmp(part, "monitor")) { return send_oscam_config_monitor(vars, params); } +#endif +#ifdef LCDSUPPORT + else if(!strcmp(part, "lcd")) { return send_oscam_config_lcd(vars, params); } +#endif +#ifdef MODULE_CAMD33 + else if(!strcmp(part, "camd33")) { return send_oscam_config_camd33(vars, params); } +#endif +#ifdef MODULE_CAMD35 + else if(!strcmp(part, "camd35")) { return send_oscam_config_camd35(vars, params); } +#endif +#ifdef MODULE_CAMD35_TCP + else if(!strcmp(part, "camd35tcp")) { return send_oscam_config_camd35tcp(vars, params); } +#endif + else if(!strcmp(part, "cache")) { return send_oscam_config_cache(vars, params); } +#ifdef MODULE_NEWCAMD + else if(!strcmp(part, "newcamd")) { return send_oscam_config_newcamd(vars, params); } +#endif +#ifdef MODULE_RADEGAST + else if(!strcmp(part, "radegast")) { return send_oscam_config_radegast(vars, params); } +#endif +#ifdef MODULE_SCAM + else if(!strcmp(part, "scam")) { return send_oscam_config_scam(vars, params); } +#endif +#ifdef MODULE_STREAMRELAY + else if(!strcmp(part, "streamrelay")) { return send_oscam_config_streamrelay(vars, params); } +#endif +#ifdef MODULE_CCCAM + else if(!strcmp(part, "cccam")) { return send_oscam_config_cccam(vars, params); } +#endif +#ifdef MODULE_GBOX + else if(!strcmp(part, "gbox")) { return send_oscam_config_gbox(vars, params); } +#endif +#ifdef HAVE_DVBAPI + else if(!strcmp(part, "dvbapi")) { return send_oscam_config_dvbapi(vars, params); } +#endif +#ifdef CS_ANTICASC + else if(!strcmp(part, "anticasc")) { return send_oscam_config_anticasc(vars, params); } +#endif +#ifdef MODULE_SERIAL + else if(!strcmp(part, "serial")) { return send_oscam_config_serial(vars, params); } +#endif +#ifdef WITH_LB + else if(!strcmp(part, "loadbalancer")) { return send_oscam_config_loadbalancer(vars, params); } +#endif + else { return send_oscam_config_global(vars, params); } +} + +static void inactivate_reader(struct s_reader *rdr) +{ + struct s_client *cl = rdr->client; + if(cl) + { kill_thread(cl); } +} + +static bool picon_exists(char *name) +{ + char picon_name[255], path[255]; + char *tpl_path; + tpl_path = cfg.http_piconpath ? cfg.http_piconpath : cfg.http_tpl; + if(!tpl_path) + { return false; } + snprintf(picon_name, sizeof(picon_name) - 1, "IC_%s", name); + return cs_strlen(tpl_getTplPath(picon_name, tpl_path, path, sizeof(path) - 1)) && file_exists(path); +} + +static void clear_rdr_stats(struct s_reader *rdr) +{ + int i; + for(i = 0; i < 4; i++) + { + rdr->emmerror[i] = 0; + rdr->emmwritten[i] = 0; + rdr->emmskipped[i] = 0; + rdr->emmblocked[i] = 0; + } + rdr->ecmsok = 0; +#ifdef CS_CACHEEX_AIO + rdr->ecmsoklg = 0; +#endif + rdr->ecmsnok = 0; + rdr->ecmstout = 0; + rdr->ecmshealthok = 0; +#ifdef CS_CACHEEX_AIO + rdr->ecmshealthoklg = 0; +#endif + rdr->ecmshealthnok = 0; + rdr->ecmshealthtout = 0; + rdr->ecmsfilteredhead = 0; + rdr->ecmsfilteredlen = 0; +} + +static void clear_all_rdr_stats(void) +{ + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + clear_rdr_stats(rdr); + } +} + +static char *send_oscam_reader(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + struct s_reader *rdr; + int32_t i; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + char *status; + + if(!apicall) { setActiveMenu(vars, MNU_READERS); } + if(!apicall) + { + if(strcmp(getParam(params, "action"), "resetallrdrstats") == 0) + { + clear_all_rdr_stats(); + } + } + + tpl_addVar(vars, TPLADD, "READERACTIONCOLS", config_enabled(WITH_LB) ? "6" : "5"); + + if(strcmp(getParam(params, "action"), "resetuserstats") == 0) + { + clear_info_clients_stats(); + } + if(strcmp(getParam(params, "action"), "resetreaderstats") == 0) + { + clear_info_readers_stats(); + } + if(strcmp(getParam(params, "action"), "reloadreaders") == 0) + { + if(!cfg.http_readonly) + { + refresh_oscam(REFR_READERS); + refresh_oscam(REFR_ACCOUNTS); + } + } + if((strcmp(getParam(params, "action"), "disable") == 0) || (strcmp(getParam(params, "action"), "enable") == 0)) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. Enabling or disabling readers is not possible!"); + } + else + { + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + if(strcmp(getParam(params, "action"), "enable") == 0) + { + if(!rdr->enable) + { + rdr->enable = 1; + } + } + else + { + if(rdr->enable) + { + rdr->enable = 0; + } + } + if(rdr->typ != R_GBOX) + { + restart_cardreader(rdr, 1); + } +#ifdef MODULE_GBOX + else + { + restart_gbox_peer(rdr->label, 0, 0); + cs_log("gbox -> you must restart oscam so that setting becomes effective"); + } +#endif + + cs_log("reader %s %s by WebIf", rdr->label, rdr->enable == 1 ? "enabled":"disabled"); + + if(write_server() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + +#ifdef MODULE_GBOX + if(!is_network_reader(rdr) && !rdr->enable) + { + gbx_local_card_stat(LOCALCARDDISABLED, 0); + } +#endif + } + } + } + + if(strcmp(getParam(params, "action"), "delete") == 0) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. No deletion will be made!"); + } + else + { + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + inactivate_reader(rdr); + ll_remove(configured_readers, rdr); + + free_reader(rdr); + + if(write_server() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + } + } + } + + if(strcmp(getParam(params, "action"), "reread") == 0) + { + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + struct s_client *cl = rdr->client; + //reset the counters + for(i = 0; i < 4; i++) + { + rdr->emmerror[i] = 0; + rdr->emmwritten[i] = 0; + rdr->emmskipped[i] = 0; + rdr->emmblocked[i] = 0; + } + + if(rdr->enable == 1 && cl && cl->typ == 'r') + { + add_job(cl, ACTION_READER_CARDINFO, NULL, 0); + } + } + } + + LL_ITER itr = ll_iter_create(configured_readers); + + if(!apicall) + { + for(i = 0, rdr = ll_iter_next(&itr); rdr && rdr->label[0]; rdr = ll_iter_next(&itr), i++) { ; } + tpl_printf(vars, TPLADD, "NEXTREADER", "Reader-%d", i); //Next Readername + } + + int jsondelimiter = 0; + int existing_insert = 0; + + int32_t total_readers = 0; + int32_t disabled_readers = 0; + int32_t active_readers = 0; + int32_t connected_readers = 0; + + ll_iter_reset(&itr); //going to iterate all configured readers + while((rdr = ll_iter_next(&itr))) + { +#ifdef CS_CACHEEX_AIO + const char *proto = reader_get_type_desc(rdr, 0); +#endif + struct s_client *cl = rdr->client; + if(rdr->label[0] && rdr->typ) + { +#ifdef CS_CACHEEX_AIO + char *new_proto; +#if defined(MODULE_CAMD35) || defined (MODULE_CAMD35_TCP) + if(rdr->cacheex.feature_bitfield || (cl && cl->c35_extmode > 1)) +#else + if(rdr->cacheex.feature_bitfield) +#endif + { + const char *aio_suffix = " (cx-aio)"; + + if(cs_malloc(&new_proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + if (!cs_strncat(new_proto, (char *)proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + cs_log("FIXME!"); + } + if (!cs_strncat(new_proto, (char *)aio_suffix, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + cs_log("FIXME!"); + } + } + } +#endif + total_readers += 1; + + // used for API and WebIf + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + + MD5((uint8_t *)rdr->label, cs_strlen(rdr->label), md5tmp); + int z; + tpl_addVar(vars, TPLADD, "LABELMD5","id_"); + for (z = 0; z < MD5_DIGEST_LENGTH; z++) + { + tpl_printf(vars, TPLAPPEND, "LABELMD5", "%02x", md5tmp[z]); + } +#ifdef MODULE_GBOX + if(apicall) + { + tpl_addVar(vars, TPLADD, "LASTGSMS", ""); + tpl_addVar(vars, TPLADD, "LASTGSMS", rdr->last_gsms); + } +#endif + if(apicall) + { + tpl_printf(vars, TPLADD, "PICONENABLED", "%d", cfg.http_showpicons?1:0); + } + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, rdr->label)); + if(!existing_insert) + { + tpl_printf(vars, TPLADD, "EXISTING_INS", "'%s'", urlencode(vars, rdr->label)); + existing_insert++; + }else + { + tpl_printf(vars, TPLAPPEND, "EXISTING_INS", ",'%s'", urlencode(vars, rdr->label)); + } + tpl_addVar(vars, TPLADD, "READERCLASS", rdr->enable ? "enabledreader" : "disabledreader"); + + if(rdr->enable) { active_readers += 1; } + else { disabled_readers += 1; } + + if(rdr->tcp_connected) + { + connected_readers += 1; + +#ifdef CS_CACHEEX_AIO + if(rdr->cacheex.feature_bitfield) + { + tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", (const char*)new_proto); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", (const char*)new_proto); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", new_proto); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char*)new_proto); + } + } + + if(rdr->cacheex.feature_bitfield & 32) + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", rdr->cacheex.aio_version); + else if(cl->reader->cacheex.feature_bitfield) + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", "[cx-aio < 9.2.3]"); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", proto); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", proto); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", proto); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char *)proto); + } + } + } +#else + tpl_addVar(vars, TPLADD, "CLIENTPROTO", reader_get_type_desc(rdr, 0)); + tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", reader_get_type_desc(rdr, 0)); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", reader_get_type_desc(rdr, 0)); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s", reader_get_type_desc(rdr, 0)); + } + } +#endif + switch(rdr->card_status) + { + case CARD_INSERTED: + status = "online"; + tpl_addVar(vars, TPLADD, "RSTATUS", status); + tpl_addVar(vars, TPLADD, "READERCLASS", "r_connected"); + break; + + case NO_CARD: + case UNKNOWN: + case READER_DEVICE_ERROR: + case CARD_NEED_INIT: + case CARD_FAILURE: + default: + status = "connected"; + tpl_addVar(vars, TPLADD, "RSTATUS", status); + tpl_addVar(vars, TPLADD, "READERCLASS", "r_undefined"); + break; + } + + tpl_addVar(vars, TPLADD, "READERIP", cs_inet_ntoa(rdr->client->ip)); + } + else + { + /* default initial values */ + tpl_addVar(vars, TPLADDONCE, "RSTATUS", "offline"); + tpl_addVar(vars, TPLADDONCE, "READERIP", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOSORT", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOTITLE", ""); + tpl_addVar(vars, TPLADDONCE, "PROTOICON", ""); + + if(!is_network_reader(rdr) && rdr->enable) + { + switch(rdr->card_status) + { + case CARD_INSERTED: + status = "active"; + tpl_addVar(vars, TPLADD, "RSTATUS", status); + tpl_addVar(vars, TPLADD, "READERCLASS", "r_connected"); + break; + + case NO_CARD: + case UNKNOWN: + case READER_DEVICE_ERROR: + case CARD_NEED_INIT: + case CARD_FAILURE: + default: + status = "connected"; + tpl_addVar(vars, TPLADD, "RSTATUS", status); + tpl_addVar(vars, TPLADD, "READERCLASS", "r_undefined"); + break; + } + + tpl_addVar(vars, TPLADD, "CLIENTPROTO", reader_get_type_desc(rdr, 0)); + tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", reader_get_type_desc(rdr, 0)); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", reader_get_type_desc(rdr, 0)); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s", reader_get_type_desc(rdr, 0)); + } + } + } + } + + if(rdr->description) + tpl_printf(vars, TPLADD, "DESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, rdr->description)); + else + tpl_addVar(vars, TPLADD, "DESCRIPTION", ""); + + if(cfg.http_showpicons && !apicall) + { + tpl_addVar(vars, TPLADD, "READERBIT", tpl_getTpl(vars, picon_exists(xml_encode(vars, rdr->label)) ? "READERNAMEBIT" : "READERNOICON")); +#ifdef CS_CACHEEX_AIO + if(rdr->cacheex.feature_bitfield) + { + tpl_addVar(vars, TPLADD, "CLIENTPROTO", picon_exists(xml_encode(vars, (const char*)new_proto)) ? tpl_getTpl(vars, "READERCTYPBIT") : tpl_getTpl(vars, "READERCTYPNOICON")); + } + else + { +#endif + tpl_addVar(vars, TPLADD, "CLIENTPROTO", picon_exists(xml_encode(vars, reader_get_type_desc(rdr, 0))) ? tpl_getTpl(vars, "READERCTYPBIT") : tpl_getTpl(vars, "READERCTYPNOICON")); +#ifdef CS_CACHEEX_AIO + } +#endif + } + else + tpl_addVar(vars, TPLADD, "READERBIT", tpl_getTpl(vars, "READERLABEL")); + + char *value = mk_t_group(rdr->grp); + tpl_addVar(vars, TPLADD, "GROUPS", value); + free_mk_t(value); + tpl_printf(vars, TPLADD, "EMMERRORUK", PRINTF_LOCAL_D, rdr->emmerror[UNKNOWN]); + tpl_printf(vars, TPLADD, "EMMERRORG", PRINTF_LOCAL_D, rdr->emmerror[GLOBAL]); + tpl_printf(vars, TPLADD, "EMMERRORS", PRINTF_LOCAL_D, rdr->emmerror[SHARED]); + tpl_printf(vars, TPLADD, "EMMERRORUQ", PRINTF_LOCAL_D, rdr->emmerror[UNIQUE]); + + tpl_printf(vars, TPLADD, "EMMWRITTENUK", PRINTF_LOCAL_D, rdr->emmwritten[UNKNOWN]); + tpl_printf(vars, TPLADD, "EMMWRITTENG", PRINTF_LOCAL_D, rdr->emmwritten[GLOBAL]); + tpl_printf(vars, TPLADD, "EMMWRITTENS", PRINTF_LOCAL_D, rdr->emmwritten[SHARED]); + tpl_printf(vars, TPLADD, "EMMWRITTENUQ", PRINTF_LOCAL_D, rdr->emmwritten[UNIQUE]); + + tpl_printf(vars, TPLADD, "EMMSKIPPEDUK", PRINTF_LOCAL_D, rdr->emmskipped[UNKNOWN]); + tpl_printf(vars, TPLADD, "EMMSKIPPEDG", PRINTF_LOCAL_D, rdr->emmskipped[GLOBAL]); + tpl_printf(vars, TPLADD, "EMMSKIPPEDS", PRINTF_LOCAL_D, rdr->emmskipped[SHARED]); + tpl_printf(vars, TPLADD, "EMMSKIPPEDUQ", PRINTF_LOCAL_D, rdr->emmskipped[UNIQUE]); + + tpl_printf(vars, TPLADD, "EMMBLOCKEDUK", PRINTF_LOCAL_D, rdr->emmblocked[UNKNOWN]); + tpl_printf(vars, TPLADD, "EMMBLOCKEDG", PRINTF_LOCAL_D, rdr->emmblocked[GLOBAL]); + tpl_printf(vars, TPLADD, "EMMBLOCKEDS", PRINTF_LOCAL_D, rdr->emmblocked[SHARED]); + tpl_printf(vars, TPLADD, "EMMBLOCKEDUQ", PRINTF_LOCAL_D, rdr->emmblocked[UNIQUE]); + + tpl_printf(vars, TPLADD, "ECMSOK", PRINTF_LOCAL_D, rdr->ecmsok); + tpl_printf(vars, TPLADD, "ECMSOKREL", " (%.2f %%)", rdr->ecmshealthok); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "ECMSOKLG", PRINTF_LOCAL_D, rdr->ecmsoklg); + tpl_printf(vars, TPLADD, "ECMSOKLGREL", " (%.2f %%)", rdr->ecmshealthoklg); +#endif + tpl_printf(vars, TPLADD, "ECMSNOK", PRINTF_LOCAL_D, rdr->ecmsnok); + tpl_printf(vars, TPLADD, "ECMSNOKREL", " (%.2f %%)",rdr->ecmshealthnok); + tpl_printf(vars, TPLADD, "ECMSTOUT", PRINTF_LOCAL_D, rdr->ecmstout); + tpl_printf(vars, TPLADD, "ECMSTOUTREL", " (%.2f %%)",rdr->ecmshealthtout); + tpl_printf(vars, TPLADD, "ECMSFILTEREDHEAD", PRINTF_LOCAL_D, rdr->ecmsfilteredhead); + tpl_printf(vars, TPLADD, "ECMSFILTEREDLEN", PRINTF_LOCAL_D, rdr->ecmsfilteredlen); +#ifdef WITH_LB + tpl_printf(vars, TPLADD, "LBWEIGHT", "%d", rdr->lb_weight); +#endif + if(!is_network_reader(rdr)) //reader is physical + { + tpl_addVar(vars, TPLADD, "REFRICO", "image?i=ICREF"); + tpl_addVar(vars, TPLADD, "READERREFRESH", tpl_getTpl(vars, "READERREFRESHBIT")); + tpl_addVar(vars, TPLADD, "ENTICO", "image?i=ICENT"); + tpl_addVar(vars, TPLADD, "ENTITLEMENT", tpl_getTpl(vars, "READERENTITLEBIT")); + } + else + { + tpl_addVar(vars, TPLADD, "READERREFRESH", ""); + if(rdr->typ == R_CCCAM) + { + tpl_addVar(vars, TPLADD, "ENTICO", "image?i=ICENT"); + tpl_addVar(vars, TPLADD, "ENTITLEMENT", tpl_getTpl(vars, "READERENTITLEBIT")); + } + else + { + tpl_addVar(vars, TPLADD, "ENTITLEMENT", ""); + } + } + + if(rdr->enable == 0) + { + tpl_addVar(vars, TPLADD, "SWITCHICO", "image?i=ICENA"); + tpl_addVar(vars, TPLADD, "SWITCHTITLE", "Enable"); + tpl_addVar(vars, TPLADD, "SWITCH", "enable"); + tpl_addVar(vars, TPLADD, "WRITEEMM", ""); + } + else + { + tpl_addVar(vars, TPLADD, "SWITCHICO", "image?i=ICDIS"); + tpl_addVar(vars, TPLADD, "SWITCHTITLE", "Disable"); + tpl_addVar(vars, TPLADD, "SWITCH", "disable"); + + tpl_addVar(vars, TPLADD, "EMMICO", "image?i=ICEMM"); + tpl_addVar(vars, TPLADD, "WRITEEMM", tpl_getTpl(vars, "READERWRITEEMMBIT")); + } + + if(!apicall) + { + // Add to WebIf Template +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLAPPEND, "READERLIST", tpl_getTpl(vars, "READERSBITAIO")); +#else + tpl_addVar(vars, TPLAPPEND, "READERLIST", tpl_getTpl(vars, "READERSBIT")); +#endif + } + else + { + + // used only for API + tpl_addVar(vars, TPLADD, "APIREADERENABLED", !rdr->enable ? "0" : "1"); + if(cl) + { + tpl_printf(vars, TPLADD, "APIREADERTYPE", "%c", cl->typ ? cl->typ : 'x'); + } + + if(apicall==1) + { + // Add to API Template + tpl_addVar(vars, TPLAPPEND, "APIREADERLIST", tpl_getTpl(vars, "APIREADERSBIT")); + } + if(apicall==2) + { + tpl_printf(vars, TPLAPPEND, "APIREADERLIST","%s%s",jsondelimiter?",":"", tpl_getTpl(vars, "JSONREADERBIT")); + jsondelimiter++; + } + } +#ifdef CS_CACHEEX_AIO + if(rdr->cacheex.feature_bitfield) + { + free(new_proto); + } +#endif + } + } + + tpl_printf(vars, TPLADD, "TOTAL_READERS", "%d", total_readers); + tpl_printf(vars, TPLADD, "TOTAL_DISABLED_READERS", "%d", disabled_readers); + tpl_printf(vars, TPLADD, "TOTAL_ACTIVE_READERS", "%d", active_readers); + tpl_printf(vars, TPLADD, "TOTAL_CONNECTED_READERS", "%d", connected_readers); + + //CM info + tpl_addVar(vars, TPLADD, "DISPLAYUSERINFO", "hidden"); // no userinfo in readers + set_ecm_info(vars); + + if(!apicall) + { +#ifdef MODULE_CAMD33 + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_CAMD35 + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_CAMD35_TCP + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_NEWCAMD + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_CCCAM + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_GBOX + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_RADEGAST + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_SERIAL + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_CONSTCW + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_SCAM + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif + + for(i = 0; cardreaders[i]; i++) + { + tpl_printf(vars, TPLAPPEND, "ADDPROTOCOL", "\n", xml_encode(vars, cardreaders[i]->desc)); + } +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "READERSAIO"); +#else + return tpl_getTpl(vars, "READERS"); +#endif + } + else + { + if(apicall == 1) + { + return tpl_getTpl(vars, "APIREADERS"); + } + else + { + return tpl_getTpl(vars, "JSONREADER"); + } + } +} + +static char *send_oscam_reader_config(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + int32_t apicall = 0; + char *reader_ = getParam(params, "label"); + char *value; + + struct s_reader *rdr; + + if(!apicall) { setActiveMenu(vars, MNU_READERS); } + + if(strcmp(getParam(params, "action"), "Add") == 0) + { + // Add new reader + struct s_reader *newrdr; + if(!cs_malloc(&newrdr, sizeof(struct s_reader))) { return "0"; } + for(i = 0; i < (*params).paramcount; ++i) + { + if(strcmp((*params).params[i], "action")) + { chk_reader((*params).params[i], (*params).values[i], newrdr); } + } + module_reader_set(newrdr); + reader_ = newrdr->label; + reader_set_defaults(newrdr); + newrdr->enable = 0; // do not start the reader because must configured before + ll_append(configured_readers, newrdr); + tpl_addMsg(vars, "New Reader has been added with default settings"); + } + else if(strcmp(getParam(params, "action"), "Save") == 0) + { + + rdr = get_reader_by_label(getParam(params, "label")); + if(!rdr) + { return NULL; } + //if (is_network_reader(rdr)) + // inactivate_reader(rdr); //Stop reader before reinitialization + char servicelabels[1024] = ""; + char servicelabelslb[1024] = ""; + + for(i = 0; i < (*params).paramcount; ++i) + { + if((strcmp((*params).params[i], "reader")) && (strcmp((*params).params[i], "action"))) + { + if(!strcmp((*params).params[i], "services")) + { snprintf(servicelabels + cs_strlen(servicelabels), sizeof(servicelabels) - cs_strlen(servicelabels), "%s,", (*params).values[i]); } + else if(!strcmp((*params).params[i], "lb_whitelist_services")) + { snprintf(servicelabelslb + cs_strlen(servicelabelslb), sizeof(servicelabelslb) - cs_strlen(servicelabelslb), "%s,", (*params).values[i]); } + else + /*if(cs_strlen((*params).values[i]) > 0)*/ + { chk_reader((*params).params[i], (*params).values[i], rdr); } + } + //printf("param %s value %s\n",(*params).params[i], (*params).values[i]); + } + chk_reader("services", servicelabels, rdr); + chk_reader("lb_whitelist_services", servicelabelslb, rdr); + + if(is_network_reader(rdr) || rdr->typ == R_EMU) //physical readers make trouble if re-started + { + if(rdr) + { + if(rdr->typ != R_GBOX) + { + restart_cardreader(rdr, 1); + } +#ifdef MODULE_GBOX + else + { + //cs_log("SAVE - single gbox reader %s restarted by WebIf", rdr->label); + restart_gbox_peer(rdr->label, 0, 0); + } +#endif + } + } + + if(write_server() != 0) { tpl_addMsg(vars, "Write Config failed!"); } else { tpl_addMsg(vars, "Reader config updated and saved"); } + + } + + rdr = get_reader_by_label(reader_); + if(!rdr) + { return NULL; } + + // Label, Description + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "DESCRIPTION", xml_encode(vars, rdr->description)); + + // enabled + if(!apicall) + { + tpl_addVar(vars, TPLADD, "ENABLED", (rdr->enable == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "ENABLEDVALUE", (rdr->enable == 1) ? "1" : "0"); + } + + tpl_addVar(vars, TPLADD, "PASSWORD", xml_encode(vars, rdr->r_pwd)); + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, rdr->r_usr)); + tpl_addVar(vars, TPLADD, "PASS", xml_encode(vars, rdr->r_pwd)); + + // Key Newcamd + for(i = 0; i < (int32_t)sizeof(rdr->ncd_key); i++) + { tpl_printf(vars, TPLAPPEND, "NCD_KEY", "%02X", rdr->ncd_key[i]); } + + // Pincode + tpl_addVar(vars, TPLADD, "PINCODE", rdr->pincode); + + // Emmfile Path + if(rdr->emmfile) { tpl_addVar(vars, TPLADD, "EMMFILE", (char *)rdr->emmfile); } + + // Inactivity timeout + tpl_printf(vars, TPLADD, "INACTIVITYTIMEOUT", "%d", rdr->tcp_ito); + + // Receive timeout + tpl_printf(vars, TPLADD, "RECEIVETIMEOUT", "%d", rdr->tcp_rto); + + // keepalive + tpl_addVar(vars, TPLADD, "RDRKEEPALIVE", (rdr->keepalive == 1) ? "checked" : ""); + + // Connect on init (newcamd) + if(!apicall) + { + tpl_addVar(vars, TPLADD, "CONNECTONINITCHECKED", (rdr->ncd_connect_on_init == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "CONNECTONINITCHECKED", (rdr->ncd_connect_on_init == 1) ? "1" : "0"); + } + + // Reset Cycle + tpl_printf(vars, TPLADD, "RESETCYCLE", "%d", rdr->resetcycle); + + // Disable Serverfilter + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DISABLESERVERFILTERCHECKED", (rdr->ncd_disable_server_filt == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "DISABLESERVERFILTERVALUE", (rdr->ncd_disable_server_filt == 1) ? "1" : "0"); + } + +#ifdef MODULE_GHTTP + // Use SSL + if(!apicall) + { + tpl_addVar(vars, TPLADD, "USESSLCHECKED", (rdr->ghttp_use_ssl == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "USESSLVALUE", (rdr->ghttp_use_ssl == 1) ? "1" : "0"); + } +#endif + + // IPv4 force + if (!apicall) + { + tpl_addVar(vars, TPLADD, "IPV4FORCE", (rdr->ipv4force == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "IPV4FORCE", (rdr->ipv4force == 1) ? "1" : "0"); + } + + // Fallback + if(!apicall) + { + tpl_addVar(vars, TPLADD, "FALLBACKCHECKED", (rdr->fallback == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "FALLBACKVALUE", (rdr->fallback == 1) ? "1" : "0"); + } + + // Fallback per caid + value = mk_t_ftab(&rdr->fallback_percaid); + tpl_addVar(vars, TPLADD, "FALLBACK_PERCAID", value); + free_mk_t(value); + + // disable checksum test only for selected caid/provid + value = mk_t_ftab(&rdr->disablecrccws_only_for); + tpl_addVar(vars, TPLADD, "IGN_CHKSUM_ONLYFOR", value); + free_mk_t(value); + +#ifdef WITH_LB + tpl_addVar(vars, TPLADD, "LBFORCEFALLBACK", (rdr->lb_force_fallback == 1) ? "checked" : ""); +#endif + +#ifdef CS_CACHEEX + // Cacheex + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "CACHEEXSELECTED%d", rdr->cacheex.mode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + else + { + tpl_printf(vars, TPLADD, "CACHEEX", "%d", rdr->cacheex.mode); + } + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP", "%d", rdr->cacheex.maxhop); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_LG", "%d", rdr->cacheex.maxhop_lg); +#endif + value = mk_t_cacheex_hitvaluetab(&rdr->cacheex.filter_caidtab); + //if (cs_strlen(value) > 0) + tpl_printf(vars, TPLADD, "CACHEEX_ECM_FILTER", "%s", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "DCCHECKED", (rdr->cacheex.drop_csp == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "ARCHECKED", (rdr->cacheex.allow_request == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "AFCHECKED", (rdr->cacheex.allow_filter == 1) ? "checked" : ""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "AMCHECKED", (rdr->cacheex.allow_maxhop == 1) ? "checked" : ""); +#endif + tpl_addVar(vars, TPLADD, "BLOCKFAKECWSCHECKED", (rdr->cacheex.block_fakecws == 1) ? "checked" : ""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "USECWCHECKFORPUSHCHECKED", (rdr->cacheex.cw_check_for_push == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LGONLYREMOTESETTINGSCHECKED", (rdr->cacheex.lg_only_remote_settings == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYCHECKED", (rdr->cacheex.localgenerated_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&rdr->cacheex.lg_only_tab); + tpl_addVar(vars, TPLADD, "LGONLYTAB", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYINCHECKED", (rdr->cacheex.localgenerated_only_in == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "LGONLYINAIOONLYCHECKED", (rdr->cacheex.lg_only_in_aio_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&rdr->cacheex.lg_only_in_tab); + tpl_addVar(vars, TPLADD, "LGONLYINTAB", value); + free_mk_t(value); + + value = mk_t_caidvaluetab(&rdr->cacheex.cacheex_nopushafter_tab); + tpl_addVar(vars, TPLADD, "CACHEEXNOPUSHAFTER", value); + free_mk_t(value); +#endif +#endif + + // BoxID + if(rdr->boxid) + { tpl_printf(vars, TPLADD, "BOXID", "%08X", rdr->boxid); } + +#ifdef READER_VIDEOGUARD + // Filt 07 + if(!apicall) + { + tpl_addVar(vars, TPLADD, "FIX07CHECKED", (rdr->fix_07 == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "FIX07VALUE", (rdr->fix_07 == 1) ? "1" : "0"); + } + + + // Fix 9993 + if(!apicall) + { + tpl_addVar(vars, TPLADD, "FIX9993CHECKED", (rdr->fix_9993 == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "FIX9993VALUE", (rdr->fix_9993 == 1) ? "1" : "0"); + } +#endif + + // Drop CWs with wrong checksum: + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DROPBADCWSCHECKED", (rdr->dropbadcws == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "DROPBADCWSVALUE", (rdr->dropbadcws == 1) ? "1" : "0"); + } + + // Disable CWs checksum test: + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DISABLECRCCWSCHECKED", (rdr->disablecrccws == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "DISABLECRCCWSVALUE", (rdr->disablecrccws == 1) ? "1" : "0"); + } + +#ifdef WITH_CARDREADER + // Set reader to use GPIO + if(!apicall) + { + tpl_addVar(vars, TPLADD, "USE_GPIOCHECKED", rdr->use_gpio ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "USE_GPIOVALUE", rdr->use_gpio ? "1" : "0"); + } +#endif + + // AUdisabled + if(!apicall) + { + tpl_addVar(vars, TPLADD, "AUDISABLED", (rdr->audisabled == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "AUDISABLEDVALUE", (rdr->audisabled == 1) ? "1" : "0"); + } + + // AUprovid + if(rdr->auprovid) + { tpl_printf(vars, TPLADD, "AUPROVID", "%06X", rdr->auprovid); } + + if(rdr->ecmnotfoundlimit) + { tpl_printf(vars, TPLADD, "ECMNOTFOUNDLIMIT", "%u", rdr->ecmnotfoundlimit); } + +#ifdef READER_IRDETO + // Force Irdeto + if(!apicall) + { + tpl_addVar(vars, TPLADD, "FORCEIRDETOCHECKED", (rdr->force_irdeto == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "FORCEIRDETOVALUE", (rdr->force_irdeto == 1) ? "1" : "0"); + } +#endif + +#ifdef READER_CRYPTOWORKS + // needsglobalfirst + if(!apicall) + { + tpl_addVar(vars, TPLADD, "NEEDSGLOBALFIRST", (rdr->needsglobalfirst == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "NEEDSGLOBALFIRST", (rdr->needsglobalfirst == 1) ? "1" : "0"); + } +#endif +#ifdef WITH_CARDREADER + // RSA Key + for(i = 0; i < rdr->rsa_mod_length; i++) + { tpl_printf(vars, TPLAPPEND, "RSAKEY", "%02X", rdr->rsa_mod[i]); } + + // 3DES Key + for(i = 0; i < rdr->des_key_length; i++) + { tpl_printf(vars, TPLAPPEND, "DESKEY", "%02X", rdr->des_key[i]); } + + // BoxKey + for(i = 0; i < rdr->boxkey_length ; i++) + { tpl_printf(vars, TPLAPPEND, "BOXKEY", "%02X", rdr->boxkey[i]); } +#endif +#ifdef READER_CONAX + for(i = 0; i < rdr->cwpk_mod_length; i++) + { tpl_printf(vars, TPLAPPEND, "CWPKKEY", "%02X", rdr->cwpk_mod[i]); } +#endif +#ifdef READER_NAGRA + // nuid (CAK6.3) + for(i = 0; i < rdr->cak63nuid_length; i++) + { tpl_printf(vars, TPLAPPEND, "CAK63NUID", "%02X", rdr->cak63nuid[i]); } + + // cwekey (CAK6.3) + for(i = 0; i < rdr->cak63cwekey_length; i++) + { tpl_printf(vars, TPLAPPEND, "CAK63CWEKEY", "%02X", rdr->cak63cwekey[i]); } +#endif + +#ifdef READER_NAGRA_MERLIN + int32_t j; + + // idird (CAK7) + for(i = 0; i < rdr->idird_length; i++) + { tpl_printf(vars, TPLAPPEND, "IDIRD", "%02X", rdr->idird[i]); } + + // cmd0e_provider (CAK7) + for(i = 0; i < rdr->cmd0eprov_length; i++) + { tpl_printf(vars, TPLAPPEND, "CMD0EPROV", "%02X", rdr->cmd0eprov[i]); } + + // mod1 (CAK7) + for(i = 0; i < rdr->mod1_length ; i++) + { tpl_printf(vars, TPLAPPEND, "MOD1", "%02X", rdr->mod1[i]); } + + // mod2 (CAK7) + for(i = 0; i < rdr->mod2_length ; i++) + { tpl_printf(vars, TPLAPPEND, "MOD2", "%02X", rdr->mod2[i]); } + + // key3588 (CAK7) + for(i = 0; i < rdr->key3588_length; i++) + { tpl_printf(vars, TPLAPPEND, "KEY3588", "%02X", rdr->key3588[i]); } + + // key3310 (CAK7) + for(i = 0; i < rdr->key3310_length; i++) + { tpl_printf(vars, TPLAPPEND, "KEY3310", "%02X", rdr->key3310[i]); } + + // key3460 (CAK7) + for(i = 0; i < rdr->key3460_length; i++) + { tpl_printf(vars, TPLAPPEND, "KEY3460", "%02X", rdr->key3460[i]); } + + // data50 (CAK7) + for(i = 0; i < rdr->data50_length; i++) + { tpl_printf(vars, TPLAPPEND, "DATA50", "%02X", rdr->data50[i]); } + + // mod50 (CAK7) + for(i = 0; i < rdr->mod50_length; i++) + { tpl_printf(vars, TPLAPPEND, "MOD50", "%02X", rdr->mod50[i]); } + + // nuid (CAK7) + for(i = 0; i < rdr->nuid_length; i++) + { tpl_printf(vars, TPLAPPEND, "NUID", "%02X", rdr->nuid[i]); } + + // OTP CSC (CAK7) + for(i = 0; i < rdr->otpcsc_length; i++) + { tpl_printf(vars, TPLAPPEND, "OTPCSC", "%02X", rdr->otpcsc[i]); } + + // OTA CSC (CAK7) + for(i = 0; i < rdr->otacsc_length; i++) + { tpl_printf(vars, TPLAPPEND, "OTACSC", "%02X", rdr->otacsc[i]); } + + // Force Pairing Type (CAK7) + for(i = 0; i < rdr->forcepair_length; i++) + { tpl_printf(vars, TPLAPPEND, "FORCEPAIR", "%02X", rdr->forcepair[i]); } + + // cwekeys (CAK7) + for(j = 0; j < 17; j++) + { + char key[9] = "CWEKEY"; + if(j > 9) + { + key[6] = '1'; + key[7] = '0' + (j - 10); + } + else + { + key[6] = '0' + j; + } + for(i = 0; i < rdr->cwekey_length[j]; i++) + { tpl_printf(vars, TPLAPPEND, key, "%02X", rdr->cwekey[j][i]); } + } + + // force_cw_swap + if(rdr->forcecwswap) + { tpl_addVar(vars, TPLADD, "FORCECWSWAPCHECKED", "checked"); } + + // only_even_SA + if(rdr->evensa) + { tpl_addVar(vars, TPLADD, "EVENSACHECKED", "checked"); } + + // force_EMM_82 + if(rdr->forceemmg) + { tpl_addVar(vars, TPLADD, "FORCEEMMGCHECKED", "checked"); } + + // OTA_CWPKs + if(rdr->cwpkota) + { tpl_addVar(vars, TPLADD, "CWPKOTACHECKED", "checked"); } + + tpl_printf(vars, TPLADD, "TMP", "NAGRACAK7HEADERMODE%d", rdr->headermode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + // CWPK CaID (CAK7) + for(i = 0; i < rdr->cwpkcaid_length ; i++) + { tpl_printf(vars, TPLAPPEND, "CWPKCAID", "%02X", rdr->cwpkcaid[i]); } + + // cak7_mode + if(rdr->cak7_mode) + { tpl_addVar(vars, TPLADD, "NAGRACAK7MODECHECKED", "checked"); } +#endif + +#ifdef READER_VIDEOGUARD + tpl_printf(vars, TPLADD, "TMP", "CARDSTARTDATEBASEMONTH%d", rdr->card_startdate_basemonth); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "CARDSTARTDATEBASEYEAR", "%d", rdr->card_startdate_baseyear); + + tpl_printf(vars, TPLADD, "TMP", "CARDEXPIREDATEBASEMONTH%d", rdr->card_expiredate_basemonth); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "CARDEXPIREDATEBASEYEAR", "%d", rdr->card_expiredate_baseyear); + + // ins7E + if(rdr->ins7E[0x1A]) + { + for(i = 0; i < 26 ; i++) { tpl_printf(vars, TPLAPPEND, "INS7E", "%02X", rdr->ins7E[i]); } + } + // ins42 + if(rdr->ins42[0x25]) + { + for(i = 0; i < 37 ; i++) { tpl_printf(vars, TPLAPPEND, "INS42", "%02X", rdr->ins42[i]); } + } + + // ins7E11 + if(rdr->ins7E11[0x01]) + { + tpl_printf(vars, TPLAPPEND, "INS7E11", "%02X", rdr->ins7E11[0]); + } + + // ins2e06 + if(rdr->ins2e06[0x04]) + { + for(i = 0; i < 4 ; i++) { tpl_printf(vars, TPLAPPEND, "INS2E06", "%02X", rdr->ins2e06[i]); } + } + + // k1 for generic pairing mode + if(rdr->k1_generic[0x10]) + { + for(i = 0; i < rdr->k1_generic[0x10] ; i++) { tpl_printf(vars, TPLAPPEND, "K1_GENERIC", "%02X", rdr->k1_generic[i]); } + } + + // k1 for unique pairing mode + if(rdr->k1_unique[0x10]) + { + for(i = 0; i < rdr->k1_unique[0x10] ; i++) { tpl_printf(vars, TPLAPPEND, "K1_UNIQUE", "%02X", rdr->k1_unique[i]); } + } +#endif + +#ifdef WITH_CARDREADER + // ATR + if(rdr->atr[0]) + for(i = 0; i < rdr->atrlen / 2; i++) + { tpl_printf(vars, TPLAPPEND, "ATR", "%02X", rdr->atr[i]); } +#endif + + // ECM Whitelist + value = mk_t_ecm_whitelist(&rdr->ecm_whitelist); + tpl_addVar(vars, TPLADD, "ECMWHITELIST", value); + free_mk_t(value); + + // ECM Header Whitelist + value = mk_t_ecm_hdr_whitelist(&rdr->ecm_hdr_whitelist); + tpl_addVar(vars, TPLADD, "ECMHEADERWHITELIST", value); + free_mk_t(value); + +#ifdef WITH_CARDREADER + // Deprecated + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DEPRECATEDCHECKED", (rdr->deprecated == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "DEPRECATEDVALUE", (rdr->deprecated == 1) ? "1" : "0"); + } + + // Smargopatch + if(!apicall) + { + tpl_addVar(vars, TPLADD, "SMARGOPATCHCHECKED", (rdr->smargopatch == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "SMARGOPATCHVALUE", (rdr->smargopatch == 1) ? "1" : "0"); + } + + // Autospeed + if(!apicall) + { + tpl_addVar(vars, TPLADD, "AUTOSPEEDCHECKED", (rdr->autospeed == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "AUTOSPEEDVALUE", (rdr->autospeed == 1) ? "1" : "0"); + } + // sc8in1 dtrrts patch + if(!apicall) + { + tpl_addVar(vars, TPLADD, "SC8IN1DTRRTSPATCHCHECKED", (rdr->sc8in1_dtrrts_patch == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "SC8IN1DTRRTSPATCHVALUE", (rdr->sc8in1_dtrrts_patch == 1) ? "1" : "0"); + } + +#ifdef READER_VIACCESS + if(!apicall) + { + tpl_addVar(vars, TPLADD, "READOLDCLASSES", (rdr->read_old_classes == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "READOLDCLASSES", (rdr->read_old_classes == 1) ? "1" : "0"); + } +#endif + + // Detect + if(rdr->detect & 0x80) + { tpl_printf(vars, TPLADD, "DETECT", "!%s", RDR_CD_TXT[rdr->detect & 0x7f]); } + else + { tpl_addVar(vars, TPLADD, "DETECT", RDR_CD_TXT[rdr->detect & 0x7f]); } +#endif + + // Ratelimit + if(rdr->ratelimitecm) + { + tpl_printf(vars, TPLADD, "RATELIMITECM", "%d", rdr->ratelimitecm); + // ECMUNIQUE + if(!apicall) + { + tpl_addVar(vars, TPLADD, "ECMUNIQUECHECKED", (rdr->ecmunique == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "ECMUNIQUE", (rdr->ecmunique == 1) ? "1" : "0"); + } + tpl_printf(vars, TPLADD, "RATELIMITTIME", "%d", rdr->ratelimittime); + tpl_printf(vars, TPLADD, "SRVIDHOLDTIME", "%d", rdr->srvidholdtime); + } + // Cooldown + if(rdr->cooldown[0] && rdr->cooldown[1]) + { + tpl_printf(vars, TPLADD, "COOLDOWNDELAY", "%d", rdr->cooldown[0]); + tpl_printf(vars, TPLADD, "COOLDOWNTIME", "%d", rdr->cooldown[1]); + } + // Max parallel services + tpl_printf(vars, TPLADD, "MAXPARALLEL", "%d", rdr->maxparallel); + // Parallelfactor: format as float with dot (comma would break URL parameters) + tpl_printf(vars, TPLADD, "PARALLELFACTOR", "%.1f", rdr->parallelfactor >= 0 ? rdr->parallelfactor : 2.0); + tpl_printf(vars, TPLADD, "PARALLELTIMEOUT", "%d", rdr->paralleltimeout); +#ifdef WITH_CARDREADER + // Frequencies + tpl_printf(vars, TPLADD, "MHZ", "%d", rdr->mhz); + tpl_printf(vars, TPLADD, "CARDMHZ", "%d", rdr->cardmhz); +#endif + + // Device + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DEVICE", xml_encode(vars, rdr->device)); + } + else + { + tpl_addVar(vars, TPLADD, "DEVICE", rdr->device); + } + + if(rdr->r_port) + { tpl_printf(vars, TPLAPPEND, "DEVICE", ",%d", rdr->r_port); } + if(rdr->l_port) + { + if(rdr->r_port) + { tpl_printf(vars, TPLAPPEND, "DEVICE", ",%d", rdr->l_port); } + else + { tpl_printf(vars, TPLAPPEND, "DEVICE", ",,%d", rdr->l_port); } + } + + // Group + value = mk_t_group(rdr->grp); + tpl_addVar(vars, TPLADD, "GRP", value); + free_mk_t(value); + +#ifdef WITH_LB + if(rdr->lb_weight) + { tpl_printf(vars, TPLADD, "LBWEIGHT", "%d", rdr->lb_weight); } +#endif + + //services + if(!apicall) + { + struct s_sidtab *sidtab = cfg.sidtab; + //build matrix + i = 0; + while(sidtab != NULL) + { + tpl_addVar(vars, TPLADD, "SIDLABEL", xml_encode(vars, sidtab->label)); + if(rdr->sidtabs.ok & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "READERCONFIGSIDOKBIT")); + if(rdr->sidtabs.no & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "READERCONFIGSIDNOBIT")); + if(rdr->lb_sidtabs.ok & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "READERCONFIGSIDLBOKBIT")); + sidtab = sidtab->next; + i++; + } + if(i){ + tpl_addVar(vars, TPLADD, "READERCONFIGSIDINS", tpl_getTpl(vars, "READERCONFIGSID")); + } + } + else + { + value = mk_t_service(&rdr->sidtabs); + if(cs_strlen(value) > 0) + { tpl_addVar(vars, TPLADD, "SERVICES", value); } + free_mk_t(value); + } + + // CAID + value = mk_t_caidtab(&rdr->ctab); + tpl_addVar(vars, TPLADD, "CAIDS", value); + free_mk_t(value); + +#ifdef READER_VIACCESS + // AESkeys + value = mk_t_aeskeys(rdr); + tpl_addVar(vars, TPLADD, "AESKEYS", value); + free_mk_t(value); +#endif + + //ident + value = mk_t_ftab(&rdr->ftab); + tpl_addVar(vars, TPLADD, "IDENTS", value); + free_mk_t(value); + + //CHID + value = mk_t_ftab(&rdr->fchid); + tpl_addVar(vars, TPLADD, "CHIDS", value); + free_mk_t(value); + + //Local cards + value = mk_t_ftab(&rdr->localcards); + tpl_addVar(vars, TPLADD, "LOCALCARDS", value); + free_mk_t(value); + + //class + value = mk_t_cltab(&rdr->cltab); + tpl_addVar(vars, TPLADD, "CLASS", value); + free_mk_t(value); + + if(rdr->cachemm || rdr->logemm) + { tpl_printf(vars, TPLADD, "EMMCACHE", "%d,%d,%d,%d", rdr->cachemm, rdr->rewritemm, rdr->logemm, rdr->deviceemm); } + + //savenano + value = mk_t_nano(rdr->s_nano); + tpl_addVar(vars, TPLADD, "SAVENANO", value); + free_mk_t(value); + + //blocknano + value = mk_t_nano(rdr->b_nano); + tpl_addVar(vars, TPLADD, "BLOCKNANO", value); + free_mk_t(value); + + // Blocke EMM + if(!apicall) + { + tpl_addVar(vars, TPLADD, "BLOCKEMMUNKNOWNCHK", (rdr->blockemm & EMM_UNKNOWN) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "BLOCKEMMUNIQCHK", (rdr->blockemm & EMM_UNIQUE) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "BLOCKEMMSHAREDCHK", (rdr->blockemm & EMM_SHARED) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "BLOCKEMMGLOBALCHK", (rdr->blockemm & EMM_GLOBAL) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "BLOCKEMMUNKNOWNVALUE", (rdr->blockemm & EMM_UNKNOWN) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "BLOCKEMMUNIQVALUE", (rdr->blockemm & EMM_UNIQUE) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "BLOCKEMMSHAREDVALUE", (rdr->blockemm & EMM_SHARED) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "BLOCKEMMGLOBALVALUE", (rdr->blockemm & EMM_GLOBAL) ? "1" : "0"); + } + + // Save EMM + if(!apicall) + { + tpl_addVar(vars, TPLADD, "SAVEEMMUNKNOWNCHK", (rdr->saveemm & EMM_UNKNOWN) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "SAVEEMMUNIQCHK", (rdr->saveemm & EMM_UNIQUE) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "SAVEEMMSHAREDCHK", (rdr->saveemm & EMM_SHARED) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "SAVEEMMGLOBALCHK", (rdr->saveemm & EMM_GLOBAL) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "SAVEEMMUNKNOWNVALUE", (rdr->saveemm & EMM_UNKNOWN) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "SAVEEMMUNIQVALUE", (rdr->saveemm & EMM_UNIQUE) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "SAVEEMMSHAREDVALUE", (rdr->saveemm & EMM_SHARED) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "SAVEEMMGLOBALVALUE", (rdr->saveemm & EMM_GLOBAL) ? "1" : "0"); + } + + value = mk_t_emmbylen(rdr); + if(cs_strlen(value) > 0) + { tpl_addVar(vars, TPLADD, "BLOCKEMMBYLEN", value); } + free_mk_t(value); + +#ifdef MODULE_CCCAM + if(!strcmp(rdr->cc_version, "2.0.11")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED0", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.1.1")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED1", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.1.2")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED2", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.1.3")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED3", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.1.4")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED4", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.2.0")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED5", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.2.1")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED6", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.3.0")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED7", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.3.1")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED8", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.3.2")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED9", "selected"); + } +#endif + +#ifdef READER_VIDEOGUARD + tpl_printf(vars, TPLADD, "TMP", "NDSVERSION%d", rdr->ndsversion); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "TMP", "NDSREADTIERS%d", rdr->readtiers); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); +#endif +#ifdef READER_NAGRA + tpl_printf(vars, TPLADD, "TMP", "NAGRAREAD%d", rdr->nagra_read); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + if(rdr->detect_seca_nagra_tunneled_card) + { tpl_addVar(vars, TPLADD, "NAGRADETECTSECACARDCHECKED", "checked"); } +#endif +#ifdef READER_TONGFANG + if(rdr->tongfang3_calibsn) + { tpl_printf(vars, TPLADD, "TONGFANGCALIBSN", "%08X", rdr->tongfang3_calibsn); } + + if(rdr->tongfang_boxid) + { tpl_printf(vars, TPLADD, "TONGFANGBOXID", "%08X", rdr->tongfang_boxid); } + + if(rdr->tongfang3_deskey_length > 0) + { + for(i = 0; i < rdr->tongfang3_deskey_length ; i++) + { + tpl_printf(vars, TPLAPPEND, "TONGFANGDESKEY", "%02X", rdr->tongfang3_deskey[i]); + } + } + + if(rdr->stbid_length > 0) + { + for(i = 0; i < rdr->stbid_length ; i++) + { + tpl_printf(vars, TPLAPPEND, "STBID", "%02X", rdr->stbid[i]); + } + } + +#endif + +#ifdef MODULE_CCCAM + tpl_printf(vars, TPLADD, "CCCMAXHOPS", "%d", rdr->cc_maxhops); + tpl_printf(vars, TPLADD, "CCCMINDOWN", "%d", rdr->cc_mindown); + tpl_printf(vars, TPLADD, "CCCRESHARE", "%d", rdr->cc_reshare); + tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare); + tpl_printf(vars, TPLADD, "CCCRECONNECT", "%d", rdr->cc_reconnect); + + if(rdr->cc_want_emu) + { tpl_addVar(vars, TPLADD, "CCCWANTEMUCHECKED", "checked"); } + if(rdr->cc_keepalive) + { tpl_addVar(vars, TPLADD, "KEEPALIVECHECKED", "checked"); } +#endif + +#ifdef MODULE_GBOX + tpl_printf(vars, TPLADD, "GBOXMAXDISTANCE", "%d", rdr->gbox_maxdist); + tpl_printf(vars, TPLADD, "GBOXMAXECMSEND", "%d", rdr->gbox_maxecmsend); + tpl_printf(vars, TPLADD, "GBOXRESHARE", "%d", rdr->gbox_reshare); + tpl_printf(vars, TPLADD, "PEERGBOXID", "%04X", gbox_convert_password_to_id((uint32_t)a2i(rdr->r_pwd, 4))); + tpl_addVar(vars, TPLADD, "PEERONLSTAT", (get_peer_onl_status(gbox_convert_password_to_id((uint32_t)a2i(rdr->r_pwd, 4)))) ? "checked" : ""); + tpl_printf(vars, TPLADD, "FORCEREMM", "%d", rdr->gbox_force_remm); + tpl_printf(vars, TPLADD, "CMDHERE", rdr->send_offline_cmd ? "checked" : ""); + + if(rdr->blockemm & 0x80) + { + tpl_addVar(vars, TPLADD, "REMMCNLDCHK", "checked"); + tpl_printf(vars, TPLADD, "GBOXREMMPEER", "%04X", rdr->gbox_remm_peer); + tpl_addVar(vars, TPLADD, "REMMUNIQCHK", (rdr->blockemm & EMM_UNIQUE) ? "" : "checked"); + tpl_addVar(vars, TPLADD, "REMMSHAREDCHK", (rdr->blockemm & EMM_SHARED) ? "" : "checked"); + tpl_addVar(vars, TPLADD, "REMMGLOBALCHK", (rdr->blockemm & EMM_GLOBAL) ? "" : "checked"); + tpl_addVar(vars, TPLADD, "REMMEMMUNKNOWNCHK", (rdr->blockemm & EMM_UNKNOWN) ? "" : "checked"); + } + if(rdr->gbox_gsms_peer) + { + tpl_printf(vars, TPLADD, "LASTGSMS", "%s", rdr->last_gsms); + tpl_printf(vars, TPLADD, "GBOXGSMSPEER", "%04X", rdr->gbox_gsms_peer); + } +#endif + +#ifdef READER_DRECAS + tpl_addVar(vars, TPLADD, "STMKEYS", rdr->stmkeys); +#endif + +#if defined(READER_DRE) || defined(READER_DRECAS) + tpl_addVar(vars, TPLADD, "USERSCRIPT", rdr->userscript); +#endif + +#ifdef WITH_EMU + //emu_auproviders + value = mk_t_ftab(&rdr->emu_auproviders); + tpl_addVar(vars, TPLADD, "EMUAUPROVIDERS", value); + free_mk_t(value); + + // Date-coded BISS keys + if(!apicall) + { + tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "1" : "0"); + } +#endif + + tpl_addVar(vars, TPLADD, "PROTOCOL", reader_get_type_desc(rdr, 0)); + + // Show only parameters which needed for the reader + switch(rdr->typ) + { + case R_CONSTCW: + case R_DB2COM1: + case R_DB2COM2: + case R_MOUSE : + case R_DRECAS : + case R_MP35: + case R_SC8in1 : + case R_SMART : + case R_INTERNAL: + case R_SERIAL : + case R_PCSC : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGSTDHWREADERBIT")); + break; + case R_CAMD35 : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCAMD35BIT")); + break; + case R_EMU : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGEMUBIT")); + break; + case R_CS378X : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCS378XBIT")); + break; + case R_RADEGAST: + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGRADEGASTBIT")); + break; + case R_SCAM: + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGSCAMBIT")); + break; + case R_GHTTP: + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGGHTTPBIT")); + break; + case R_GBOX: + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGGBOXBIT")); +#if defined (MODULE_CCCAM) && defined (MODULE_GBOX) + if(cfg.cc_gbx_reshare_en) + { + tpl_printf(vars, TPLADD, "GBOXCCCAMRESHARE", "%d", rdr->gbox_cccam_reshare); + value = mk_t_ftab(&rdr->ccc_gbx_reshare_ident); + tpl_addVar(vars, TPLADD, "CCCGBXRESHAREIDENT", value); + free_mk_t(value); + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "GBOXCCCAMRESHAREBIT")); + } +#endif + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERINFOGBOXREMM")); + break; + case R_NEWCAMD: + if(rdr->ncd_proto == NCD_525) + { + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGNCD525BIT")); + } + else if(rdr->ncd_proto == NCD_524) + { + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGNCD524BIT")); + } + break; +#ifdef MODULE_CCCAM + case R_CCCAM : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCCCAMBIT")); + break; +#endif + default : + tpl_addMsg(vars, "Error: protocol not resolvable"); + break; + + } + +#ifdef MODULE_CCCAM + if(rdr->typ != R_CCCAM && rdr->typ != R_GBOX) + { + tpl_printf(vars, TPLADD, "CCCHOP", "%d", rdr->cc_hop); + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGHOPBIT")); + } +#endif + +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "READERCONFIGAIO"); +#else + return tpl_getTpl(vars, "READERCONFIG"); +#endif +} + +static char *send_oscam_reader_stats(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + + if(!apicall) { setActiveMenu(vars, MNU_READERS); } + + int8_t error; + struct s_client *cl = NULL; + struct s_reader *rdr; + + rdr = get_reader_by_label(getParam(params, "label")); + error = (rdr ? 0 : 1); + + if(!error && rdr) + { + cl = rdr->client; + error = (cl ? 0 : 1); + } + + if(error) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATSROW", tpl_getTpl(vars, "READERSTATSROWBIT")); + if(!apicall) + { return tpl_getTpl(vars, "READERSTATS"); } + else + { return tpl_getTpl(vars, "APIREADERSTATS"); } + } + +#ifdef WITH_LB + char *stxt[] = {"found", "cache1", "cache2", "cache3", + "not found", "timeout", "sleeping", + "fake", "invalid", "corrupt", "no card", "expdate", + "disabled", "stopped" + }; + + if(strcmp(getParam(params, "action"), "resetstat") == 0) + { + char *rcs = getParam(params, "rc"); + int32_t retval = 0; + if(cs_strlen(rcs) > 0) + { + int8_t rc; + rc = atoi(rcs); + retval = clean_stat_by_rc(rdr, rc, 0); + cs_log("Reader %s stats %d %s entr%s deleted by WebIF from %s", + rdr->label, retval, stxt[rc], + retval == 1 ? "y" : "ies", + cs_inet_ntoa(GET_IP())); + } + else + { + clear_reader_stat(rdr); + cs_log("Reader %s stats resetted by WebIF from %s", rdr->label, cs_inet_ntoa(GET_IP())); + } + + } + + if(strcmp(getParam(params, "action"), "deleterecord") == 0) + { + char *record = getParam(params, "record"); + if(cs_strlen(record) > 0) + { + int32_t retval = 0; + uint32_t caid, provid, sid, cid, len; + sscanf(record, "%4x@%6x:%4x:%4x:%4x", &caid, &provid, &sid, &cid, &len); + retval = clean_stat_by_id(rdr, caid, provid, sid, cid, len); + cs_log("Reader %s stats %d entr%s deleted by WebIF from %s", + rdr->label, retval, + retval == 1 ? "y" : "ies", + cs_inet_ntoa(GET_IP())); + } + } + + if(strcmp(getParam(params, "action"), "updateecmlen") == 0) + { + update_ecmlen_from_stat(rdr); + write_server(); + } + +#endif + + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "LABEL", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "ENCODEDLABEL", urlencode(vars, rdr->label)); + + if(apicall) + { + int32_t i, emmcount = 0; + char *ttxt[] = {"unknown", "unique", "shared", "global"}; + + for(i = 0; i < 4; i++) + { + tpl_addVar(vars, TPLADD, "EMMRESULT", "error"); + tpl_addVar(vars, TPLADD, "EMMTYPE", ttxt[i]); + tpl_printf(vars, TPLADD, "EMMCOUNT", "%d", rdr->emmerror[i]); + tpl_addVar(vars, TPLAPPEND, "EMMSTATS", tpl_getTpl(vars, "APIREADERSTATSEMMBIT")); + emmcount += rdr->emmerror[i]; + tpl_printf(vars, TPLADD, "TOTALERROR", "%d", emmcount); + } + emmcount = 0; + for(i = 0; i < 4; i++) + { + tpl_addVar(vars, TPLADD, "EMMRESULT", "written"); + tpl_addVar(vars, TPLADD, "EMMTYPE", ttxt[i]); + tpl_printf(vars, TPLADD, "EMMCOUNT", "%d", rdr->emmwritten[i]); + tpl_addVar(vars, TPLAPPEND, "EMMSTATS", tpl_getTpl(vars, "APIREADERSTATSEMMBIT")); + emmcount += rdr->emmwritten[i]; + tpl_printf(vars, TPLADD, "TOTALWRITTEN", "%d", emmcount); + } + emmcount = 0; + for(i = 0; i < 4; i++) + { + tpl_addVar(vars, TPLADD, "EMMRESULT", "skipped"); + tpl_addVar(vars, TPLADD, "EMMTYPE", ttxt[i]); + tpl_printf(vars, TPLADD, "EMMCOUNT", "%d", rdr->emmskipped[i]); + tpl_addVar(vars, TPLAPPEND, "EMMSTATS", tpl_getTpl(vars, "APIREADERSTATSEMMBIT")); + emmcount += rdr->emmskipped[i]; + tpl_printf(vars, TPLADD, "TOTALSKIPPED", "%d", emmcount); + } + emmcount = 0; + for(i = 0; i < 4; i++) + { + tpl_addVar(vars, TPLADD, "EMMRESULT", "blocked"); + tpl_addVar(vars, TPLADD, "EMMTYPE", ttxt[i]); + tpl_printf(vars, TPLADD, "EMMCOUNT", "%d", rdr->emmblocked[i]); + tpl_addVar(vars, TPLAPPEND, "EMMSTATS", tpl_getTpl(vars, "APIREADERSTATSEMMBIT")); + emmcount += rdr->emmblocked[i]; + tpl_printf(vars, TPLADD, "TOTALBLOCKED", "%d", emmcount); + } + } + + if(apicall) + { + char *txt = "UNDEF"; + switch(rdr->card_status) + { + case NO_CARD: + if(rdr->typ == R_GBOX) + { txt = "ONL no crd"; } + else + { txt = "OFF"; } + break; + case UNKNOWN: + txt = "UNKNOWN"; + break; + case READER_DEVICE_ERROR: + txt = "READER DEVICE ERROR"; + break; + case CARD_NEED_INIT: + if(rdr->typ == R_GBOX) + { txt = "OFFLINE"; } + else + { txt = "NEEDINIT"; } + break; + case CARD_INSERTED: + if(cl->typ == 'p') + { + if(rdr->typ == R_GBOX) + { txt = "ONL w/crd"; } + else + { txt = "CONNECTED"; } + } + else + { txt = "CARDOK"; } + break; + case CARD_FAILURE: + txt = "ERROR"; + break; + default: + txt = "UNDEF"; + } + tpl_addVar(vars, TPLADD, "READERSTATUS", txt); + tpl_printf(vars, TPLADD, "READERCAID", "%04X", rdr->caid); + } + + int32_t rowcount = 0; + uint64_t ecmcount = 0; + time_t lastaccess = 0; + +#ifdef WITH_LB + int32_t rc2hide = (-1); + if(cs_strlen(getParam(params, "hide")) > 0) + { rc2hide = atoi(getParam(params, "hide")); } + + int32_t rc2show = (-1); + if(cs_strlen(getParam(params, "show")) > 0) + { rc2show = atoi(getParam(params, "show")); } + + if(rdr->lb_stat) + { + int32_t statsize; + // @todo alno: sort by click, 0=ascending, 1=descending (maybe two buttons or reverse on second click) + READER_STAT **statarray = get_sorted_stat_copy(rdr, 0, &statsize); + char channame[CS_SERVICENAME_SIZE]; + for(; rowcount < statsize; ++rowcount) + { + READER_STAT *s = statarray[rowcount]; + if(!(s->rc == rc2hide) && ((rc2show == -1) || (s->rc == rc2show))) + { + struct tm lt; + localtime_r(&s->last_received.time, <); // fixme we need walltime! + ecmcount += s->ecm_count; + if(!apicall) + { + tpl_printf(vars, TPLADD, "CHANNEL", "%04X@%06X:%04X:%04X", s->caid, s->prid, s->srvid, s->chid); + tpl_addVar(vars, TPLADD, "CHANNELNAME", xml_encode(vars, get_servicename(cur_client(), s->srvid, s->prid, s->caid, channame, sizeof(channame)))); + tpl_printf(vars, TPLADD, "ECMLEN", "%04hX", s->ecmlen); + tpl_addVar(vars, TPLADD, "RC", stxt[s->rc]); + tpl_printf(vars, TPLADD, "TIME", PRINTF_LOCAL_D " ms", s->time_avg); + if(s->time_stat[s->time_idx]) + { tpl_printf(vars, TPLADD, "TIMELAST", PRINTF_LOCAL_D " ms", s->time_stat[s->time_idx]); } + else + { tpl_addVar(vars, TPLADD, "TIMELAST", ""); } + tpl_printf(vars, TPLADD, "COUNT", PRINTF_LOCAL_D, s->ecm_count); + + if(s->last_received.time) + { + tpl_printf(vars, TPLADD, "LAST", "%02d.%02d.%02d %02d:%02d:%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100, lt.tm_hour, lt.tm_min, lt.tm_sec); + + } + else + { + tpl_addVar(vars, TPLADD, "LAST", "never"); + } + } + else + { + tpl_printf(vars, TPLADD, "ECMCAID", "%04X", s->caid); + tpl_printf(vars, TPLADD, "ECMPROVID", "%06X", s->prid); + tpl_printf(vars, TPLADD, "ECMSRVID", "%04X", s->srvid); + tpl_printf(vars, TPLADD, "ECMLEN", "%04hX", s->ecmlen); + tpl_addVar(vars, TPLADD, "ECMCHANNELNAME", xml_encode(vars, get_servicename(cur_client(), s->srvid, s->prid, s->caid, channame, sizeof(channame)))); + tpl_printf(vars, TPLADD, "ECMTIME", PRINTF_LOCAL_D, s->time_avg); + tpl_printf(vars, TPLADD, "ECMTIMELAST", PRINTF_LOCAL_D, s->time_stat[s->time_idx]); + tpl_printf(vars, TPLADD, "ECMRC", "%d", s->rc); + tpl_addVar(vars, TPLADD, "ECMRCS", stxt[s->rc]); + if(s->last_received.time) + { + char tbuffer [30]; + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", <); + tpl_addVar(vars, TPLADD, "ECMLAST", tbuffer); + } + else + { + tpl_addVar(vars, TPLADD, "ECMLAST", ""); + } + tpl_printf(vars, TPLADD, "ECMCOUNT", PRINTF_LOCAL_D, s->ecm_count); + + if(s->last_received.time > lastaccess) + { lastaccess = s->last_received.time; } + } + + if(!apicall) + { + if(s->rc == E_NOTFOUND) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATSROWNOTFOUND", tpl_getTpl(vars, "READERSTATSBIT")); + tpl_addVar(vars, TPLADD, "RESETA", urlencode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "READERSTATSNFHEADLINE", tpl_getTpl(vars, "READERSTATSROWNOTFOUNDBIT")); + } + else if(s->rc == E_TIMEOUT) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATSROWTIMEOUT", tpl_getTpl(vars, "READERSTATSBIT")); + tpl_addVar(vars, TPLADD, "RESETB", urlencode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "READERSTATSTOHEADLINE", tpl_getTpl(vars, "READERSTATSROWTIMEOUTBIT")); + } + else if(s->rc == E_INVALID) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATSROWINVALID", tpl_getTpl(vars, "READERSTATSBIT")); + tpl_addVar(vars, TPLADD, "RESETC", urlencode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "READERSTATSIVHEADLINE", tpl_getTpl(vars, "READERSTATSROWINVALIDBIT")); + } + else + { tpl_addVar(vars, TPLAPPEND, "READERSTATSROWFOUND", tpl_getTpl(vars, "READERSTATSBIT")); } + } + else + { + + tpl_addVar(vars, TPLAPPEND, "ECMSTATS", tpl_getTpl(vars, "APIREADERSTATSECMBIT")); + } + } + } + NULLFREE(statarray); + } + else +#endif + tpl_addVar(vars, TPLAPPEND, "READERSTATSROW", tpl_getTpl(vars, "READERSTATSNOSTATS")); + + tpl_printf(vars, TPLADD, "ROWCOUNT", "%d", rowcount); + + if(lastaccess > 0) + { + char tbuffer [30]; + struct tm lt; + localtime_r(&lastaccess, <); + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", <); + tpl_addVar(vars, TPLADD, "LASTACCESS", tbuffer); + } + else + { + tpl_addVar(vars, TPLADD, "LASTACCESS", ""); + } + + if(apicall) + { + if(cl) + { + char *value = get_ecm_historystring(cl); + tpl_addVar(vars, TPLADD, "ECMHISTORY", value); + free_mk_t(value); + } + } + + tpl_printf(vars, TPLADD, "TOTALECM", "%'" PRIu64, ecmcount); + + if(!apicall) + { return tpl_getTpl(vars, "READERSTATS"); } + else + { return tpl_getTpl(vars, "APIREADERSTATS"); } +} + +static char *send_oscam_user_config_edit(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + struct s_auth *account, *ptr, *chk; + char user[sizeof(first_client->account->usr)]; + + int32_t i; + int existing_insert = 0; + + if(!apicall) { setActiveMenu(vars, MNU_USERS); } + + if(strcmp(getParam(params, "action"), "Save As") == 0) { cs_strncpy(user, getParam(params, "newuser"), sizeof(user)); } + else { cs_strncpy(user, getParam(params, "user"), sizeof(user)); } + + account = NULL; + for(chk = cfg.account; chk != NULL; chk = chk->next) + { + if(strcmp(user, chk->usr) == 0) + { account = chk; } + if(!existing_insert) + { + tpl_printf(vars, TPLADD, "EXISTING_INS", "'%s'", urlencode(vars, chk->usr)); + existing_insert++; + } + else + { + tpl_printf(vars, TPLAPPEND, "EXISTING_INS", ",'%s'", urlencode(vars, chk->usr)); + } + } + + // Create a new user if it doesn't yet + if(account == NULL) + { + i = 1; + while(cs_strlen(user) < 1) + { + snprintf(user, sizeof(user) / sizeof(char) - 1, "NEWUSER%d", i); + for(account = cfg.account; account != NULL && strcmp(user, account->usr) != 0; account = account->next) { ; } + if(account != NULL) { user[0] = '\0'; } + ++i; + } + if(!cs_malloc(&account, sizeof(struct s_auth))) { return "0"; } + if(cfg.account == NULL) { cfg.account = account; } + else + { + for(ptr = cfg.account; ptr != NULL && ptr->next != NULL; ptr = ptr->next) { ; } + ptr->next = account; + } + account_set_defaults(account); + account->disabled = 1; + cs_strncpy((char *)account->usr, user, sizeof(account->usr)); + if(!account->grp) + { account->grp = 1; } + if(write_userdb() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + else if(strcmp(getParam(params, "action"), "Save As") == 0) { tpl_addMsg(vars, "New user has been added with cloned settings"); } + else { tpl_addMsg(vars, "New user has been added with default settings"); } + // no need to refresh anything here as the account is disabled by default and there's no client with this new account anyway! + } + + if((strcmp(getParam(params, "action"), "Save") == 0) || (strcmp(getParam(params, "action"), "Save As") == 0)) + { + char servicelabels[1024] = ""; + + for(i = 0; i < (*params).paramcount; i++) + { + if((strcmp((*params).params[i], "action")) && + (strcmp((*params).params[i], "user")) && + (strcmp((*params).params[i], "newuser")) && + (strcmp((*params).params[i], "part"))) + { + + if(!strcmp((*params).params[i], "services")) + { snprintf(servicelabels + cs_strlen(servicelabels), sizeof(servicelabels) - cs_strlen(servicelabels), "%s,", (*params).values[i]); } + else + { chk_account((*params).params[i], (*params).values[i], account); } + } + } + chk_account("services", servicelabels, account); + + refresh_oscam(REFR_CLIENTS); + + if(write_userdb() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + else if(strcmp(getParam(params, "action"), "Save As") != 0) { tpl_addMsg(vars, "User Account updated and saved"); } + } + + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, account->usr)); + tpl_addVar(vars, TPLADD, "PASSWORD", xml_encode(vars, account->pwd)); + tpl_addVar(vars, TPLADD, "DESCRIPTION", xml_encode(vars, account->description)); + + //Disabled + if(!apicall) + { + if(account->disabled) + { tpl_addVar(vars, TPLADD, "DISABLEDCHECKED", "checked"); } + } + else + { + tpl_printf(vars, TPLADD, "DISABLEDVALUE", "%d", account->disabled); + } + + //Expirationdate + struct tm timeinfo; + cs_gmtime_r(&account->expirationdate, &timeinfo); + char buf [80]; + strftime(buf, 80, "%Y-%m-%d", &timeinfo); + if(strcmp(buf, "1970-01-01")) { tpl_addVar(vars, TPLADD, "EXPDATE", buf); } + + //Allowed TimeFrame + char *allowedtf = mk_t_allowedtimeframe(account); + tpl_printf(vars, TPLADD, "ALLOWEDTIMEFRAME", "%s", allowedtf); + free_mk_t(allowedtf); + + //Group + char *value = mk_t_group(account->grp); + tpl_addVar(vars, TPLADD, "GROUPS", value); + free_mk_t(value); + + // allowed protocols + value = mk_t_allowedprotocols(account); + tpl_addVar(vars, TPLADD, "ALLOWEDPROTOCOLS", value); + free_mk_t(value); + + //Hostname + tpl_addVar(vars, TPLADD, "DYNDNS", xml_encode(vars, account->dyndns)); + + //Uniq + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "UNIQSELECTED%d", account->uniq); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + else + { + tpl_printf(vars, TPLADD, "UNIQVALUE", "%d", account->uniq); + } + + //Sleep + if(!account->tosleep) { tpl_addVar(vars, TPLADD, "SLEEP", "0"); } + else { tpl_printf(vars, TPLADD, "SLEEP", "%d", account->tosleep); } + + //Monlevel selector + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "MONSELECTED%d", account->monlvl); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + else + { + tpl_printf(vars, TPLADD, "MONVALUE", "%d", account->monlvl); + } + + //Au + if(account->autoau == 1) + { tpl_addVar(vars, TPLADD, "AUREADER", "1"); } + else if(account->aureader_list) + { + value = mk_t_aureader(account); + tpl_addVar(vars, TPLADD, "AUREADER", value); + free_mk_t(value); + } + + if(!apicall) + { + /* SERVICES */ + struct s_sidtab *sidtab = cfg.sidtab; + //build matrix + i = 0; + while(sidtab != NULL) + { + tpl_addVar(vars, TPLADD, "SIDLABEL", xml_encode(vars, sidtab->label)); + if(account->sidtabs.ok & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "USEREDITSIDOKBIT")); + if(account->sidtabs.no & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "USEREDITSIDNOBIT")); + sidtab = sidtab->next; + i++; + } + if(i){ + tpl_addVar(vars, TPLADD, "USEREDITSIDINS", tpl_getTpl(vars, "USEREDITSID")); + } + } + else + { + value = mk_t_service(&account->sidtabs); + if(cs_strlen(value) > 0) + { tpl_addVar(vars, TPLADD, "SERVICES", value); } + free_mk_t(value); + } + + // CAID + value = mk_t_caidtab(&account->ctab); + tpl_addVar(vars, TPLADD, "CAIDS", value); + free_mk_t(value); + + //ident + value = mk_t_ftab(&account->ftab); + tpl_addVar(vars, TPLADD, "IDENTS", value); + free_mk_t(value); + + //CHID + value = mk_t_ftab(&account->fchid); + tpl_addVar(vars, TPLADD, "CHIDS", value); + free_mk_t(value); + + //class + value = mk_t_cltab(&account->cltab); + tpl_addVar(vars, TPLADD, "CLASS", value); + free_mk_t(value); + + //Betatunnel + value = mk_t_tuntab(&account->ttab); + tpl_addVar(vars, TPLADD, "BETATUNNELS", value); + free_mk_t(value); + + //SUPPRESSCMD08 + if(!apicall) + { + if(account->c35_suppresscmd08) + { tpl_addVar(vars, TPLADD, "SUPPRESSCMD08", "selected"); } + } + else + { + tpl_printf(vars, TPLADD, "SUPPRESSCMD08VALUE", "%d", account->c35_suppresscmd08); + } + + //Sleepsend + if(account->c35_sleepsend) + { + tpl_printf(vars, TPLADD, "SLEEPSEND", "selected"); + } + + //max_connections + tpl_printf(vars, TPLADD, "MAXCONNECTIONS", "%d", account->max_connections); + + //User Max Idle + tpl_printf(vars, TPLADD, "UMAXIDLE", "%d", account->umaxidle); + + //EMM Reassembly selector + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "EMMRSELECTED%d", account->emm_reassembly); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + else + { + tpl_printf(vars, TPLADD, "EMMRVALUE", "%d", account->emm_reassembly); + } + + //Prefer local cards + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "PREFERLOCALCARDS%d", account->preferlocalcards); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + char *tmp = NULL; + switch(cfg.preferlocalcards) + { + case -1: + tmp = "-1 - Use Global prefer local cards"; + break; + case 0: + tmp = "0 - local cards like proxied"; + break; + case 1: + tmp = "1 - prefer cache-ex then local cards"; + break; + case 2: + tmp = "2 - prefer local cards above cache-ex"; + break; + } + tpl_addVar(vars, TPLADD, "CFGPREFERLOCALCARDS", tmp); + } + else + { + tpl_printf(vars, TPLADD, "PREFERLOCALCARDSVALUE", "%d", account->preferlocalcards); + } + +#ifdef CS_CACHEEX + // Cacheex + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "CACHEEXSELECTED%d", account->cacheex.mode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + } + else + { + tpl_printf(vars, TPLADD, "CACHEEX", "%d", account->cacheex.mode); + } + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP", "%d", account->cacheex.maxhop); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_LG", "%d", account->cacheex.maxhop_lg); +#endif + + value = mk_t_cacheex_hitvaluetab(&account->cacheex.filter_caidtab); + //if (cs_strlen(value) > 0) + tpl_printf(vars, TPLADD, "CACHEEX_ECM_FILTER", "%s", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "DCCHECKED", (account->cacheex.drop_csp == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "ARCHECKED", (account->cacheex.allow_request == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "AFCHECKED", (account->cacheex.allow_filter == 1) ? "checked" : ""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "AMCHECKED", (account->cacheex.allow_maxhop == 1) ? "checked" : ""); +#endif + tpl_addVar(vars, TPLADD, "BLOCKFAKECWSCHECKED", (account->cacheex.block_fakecws == 1) ? "checked" : ""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "USECWCHECKFORPUSHCHECKED", (account->cacheex.cw_check_for_push == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LGONLYREMOTESETTINGSCHECKED", (account->cacheex.lg_only_remote_settings == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYCHECKED", (account->cacheex.localgenerated_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&account->cacheex.lg_only_tab); + tpl_addVar(vars, TPLADD, "LGONLYTAB", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYINCHECKED", (account->cacheex.localgenerated_only_in == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "LGONLYINAIOONLYCHECKED", (account->cacheex.lg_only_in_aio_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&account->cacheex.lg_only_in_tab); + tpl_addVar(vars, TPLADD, "LGONLYINTAB", value); + free_mk_t(value); + + value = mk_t_caidvaluetab(&account->cacheex.cacheex_nopushafter_tab); + tpl_addVar(vars, TPLADD, "CACHEEXNOPUSHAFTER", value); + free_mk_t(value); +#endif + tpl_addVar(vars, TPLADD, "NWTCHECKED", (account->no_wait_time == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "DISABLECRCCEX4USER", (account->disablecrccacheex == 1) ? "checked" : ""); + value = mk_t_ftab(&account->disablecrccacheex_only_for); + tpl_addVar(vars, TPLADD, "IGNCRCCEX4USERONLYFOR", value); + free_mk_t(value); + +#endif + +#ifdef CW_CYCLE_CHECK + tpl_addVar(vars, TPLADD, "USERCWCYCLEDISABLE", (account->cwc_disable == 1) ? "checked" : ""); +#endif + + //Keepalive + if(!apicall) + { + if(account->ncd_keepalive) + { tpl_addVar(vars, TPLADD, "KEEPALIVE", "checked"); } + } + else + { + tpl_printf(vars, TPLADD, "KEEPALIVEVALUE", "%d", account->ncd_keepalive); + } + +#ifdef CS_ANTICASC + tpl_printf(vars, TPLADD, "AC_USERS", "%d", account->ac_users); + tpl_printf(vars, TPLADD, "CFGNUMUSERS", "%d", cfg.ac_users); + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "PENALTY%d", account->ac_penalty); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + char *tmp = NULL; + switch(cfg.ac_penalty) + { + case 0: + tmp = "(0) Only write to log"; + break; + case 1: + tmp = "(1) NULL CW"; + break; + case 2: + tmp = "(2) Ban"; + break; + case 3: + tmp = "(3) Real CW delayed"; + break; + } + tpl_addVar(vars, TPLADD, "CFGPENALTY", tmp); + } + else + { + tpl_printf(vars, TPLADD, "PENALTYVALUE", "%d", account->ac_penalty); + } + tpl_printf(vars, TPLADD, "ACOSC_MAX_ECMS_PER_MINUTE", "%d", account->acosc_max_ecms_per_minute); + tpl_printf(vars, TPLADD, "ACOSC_MAX_ACTIVE_SIDS", "%d", account->acosc_max_active_sids); + tpl_printf(vars, TPLADD, "CFG_ACOSC_MAX_ECMS_PER_MINUTE", "%d", cfg.acosc_max_ecms_per_minute); + tpl_printf(vars, TPLADD, "CFG_ACOSC_MAX_ACTIVE_SIDS", "%d", cfg.acosc_max_active_sids); + tpl_printf(vars, TPLADD, "ACOSC_ZAP_LIMIT", "%d", account->acosc_zap_limit); + tpl_printf(vars, TPLADD, "CFG_ACOSC_ZAP_LIMIT", "%d", cfg.acosc_zap_limit); + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "ACOSC_PENALTY%d", account->acosc_penalty); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + char *tmp = NULL; + switch(cfg.acosc_penalty) + { + case -1: + tmp = "(-1) Use Global Value"; + break; + case 0: + tmp = "(0) Only write to log"; + break; + case 1: + tmp = "(1) NULL CW"; + break; + case 2: + tmp = "(2) Ban"; + break; + case 3: + tmp = "(3) CW delayed"; + break; + case 4: + tmp = "(4) Hidecards"; + break; + } + tpl_addVar(vars, TPLADD, "CFG_ACOSC_PENALTY", tmp); + } else + { + tpl_printf(vars, TPLADD, "ACOSC_PENALTYVALUE", "%d", account->acosc_penalty); + } + + tpl_printf(vars, TPLADD, "ACOSC_PENALTY_DURATION", "%d", account->acosc_penalty_duration); + tpl_printf(vars, TPLADD, "CFG_ACOSC_PENALTY_DURATION", "%d", cfg.acosc_penalty_duration); + tpl_printf(vars, TPLADD, "ACOSC_DELAY", "%d", account->acosc_delay); + tpl_printf(vars, TPLADD, "CFG_ACOSC_DELAY", "%d", cfg.acosc_delay); +#endif + +#ifdef MODULE_CCCAM + tpl_printf(vars, TPLADD, "CCCMAXHOPS", "%d", account->cccmaxhops); + tpl_printf(vars, TPLADD, "CCCRESHARE", "%d", account->cccreshare); + tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare); + + //CCcam Ignore Reshare + tpl_printf(vars, TPLADD, "TMP", "CCCIGNRSHRSELECTED%d", account->cccignorereshare); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_addVar(vars, TPLADD, "CFGIGNORERESHARE", + cfg.cc_ignore_reshare == 0 ? + "0 - use reshare level of Server" : "1 - use reshare level of Reader or User"); + + //CCcam Stealth Mode + tpl_printf(vars, TPLADD, "TMP", "CCCSTEALTHSELECTED%d", account->cccstealth); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_addVar(vars, TPLADD, "STEALTH", cfg.cc_stealth ? "enable" : "disable"); +#endif + + //Failban + tpl_printf(vars, TPLADD, "FAILBAN", "%d", account->failban); + + if(!apicall) + { +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "USEREDITAIO"); +#else + return tpl_getTpl(vars, "USEREDIT"); +#endif + } + else + { + return tpl_getTpl(vars, "APIUSEREDIT"); + } + +} + +static void webif_add_client_proto(struct templatevars *vars, struct s_client *cl, const char *proto, int8_t apicall) +{ + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOSORT", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOTITLE", ""); + tpl_addVar(vars, TPLADDONCE, "PROTOICON", ""); + if(!cl) { return; } +#ifdef MODULE_NEWCAMD + if(streq(proto, "newcamd") && cl->typ == 'c') + { + tpl_printf(vars, TPLADD, "CLIENTPROTO", "%s (%s)", proto, newcamd_get_client_name(cl->ncd_client_id)); + tpl_printf(vars, TPLADD, "CLIENTPROTOSORT", "%s (%s)", proto, newcamd_get_client_name(cl->ncd_client_id)); + if(cfg.http_showpicons ) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s_%s", (char *)proto, newcamd_get_client_name(cl->ncd_client_id)); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "NCMDA", (char *)proto); + tpl_addVar(vars, TPLADD, "NCMDB", (char *)newcamd_get_client_name(cl->ncd_client_id)); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTONEWCAMDPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s_%s",(char *)proto, (char *)newcamd_get_client_name(cl->ncd_client_id)); + } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s_%s.tpl", proto, newcamd_get_client_name(cl->ncd_client_id)); + } + } + return; + } +#endif +#ifdef MODULE_CCCAM + if(strncmp(proto, "cccam", 5) == 0) + { + struct cc_data *cc = cl->cc; + if(cc && *cc->remote_version && *cc->remote_build) + { + tpl_printf(vars, TPLADD, "CLIENTPROTO", "%s (%s-%s)", proto, cc->remote_version, cc->remote_build); + tpl_printf(vars, TPLADD, "CLIENTPROTOSORT", "%s (%s-%s)", proto, cc->remote_version, cc->remote_build); + if(cccam_client_multics_mode(cl)) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "Multics, revision r%d", cc->multics_version[0] | (cc->multics_version[1] << 8)); + } + else + { +#endif +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + if(cl->reader && cl->reader->cacheex.feature_bitfield) + { + if(cl->reader->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio %s]", (cc->extended_mode ? cc->remote_oscam : ""), cl->reader->cacheex.aio_version); + } + else if(cl->reader->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio < 9.2.3]", (cc->extended_mode ? cc->remote_oscam : "")); + } + } + else if(cl->account && cl->account->cacheex.feature_bitfield) + { + if(cl->account->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio %s]", (cc->extended_mode ? cc->remote_oscam : ""), cl->account->cacheex.aio_version); + } + else if(cl->account->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio < 9.2.3]", (cc->extended_mode ? cc->remote_oscam : "")); + } + } + else + { +#endif +#endif +#ifdef MODULE_CCCAM + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", cc->extended_mode ? cc->remote_oscam : ""); +#endif +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + } +#endif +#endif +#ifdef MODULE_CCCAM + } + + if(cfg.http_showpicons) + { + char picon_name[32]; + + int8_t is_other_proto = 0; + if(cccam_client_multics_mode(cl)) { is_other_proto = 1; } + + switch(is_other_proto) + { + case 1: + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s_r_%d", proto, cc->multics_version[0] | (cc->multics_version[1] << 8)); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "CCA", (char *)proto); + tpl_addVar(vars, TPLADD, "CCB", "r"); + tpl_printf(vars, TPLADD, "CCC", "%d", cc->multics_version[0] | (cc->multics_version[1] << 8)); + tpl_addVar(vars, TPLADD, "CCD", ""); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTOCCCAMPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s_r_%d",(char *)proto, cc->multics_version[0] | (cc->multics_version[1] << 8)); + } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "Multics, revision r%d missing icon: IC_%s_r_%d.tpl", + cc->multics_version[0] | (cc->multics_version[1] << 8), proto, cc->multics_version[0] | (cc->multics_version[1] << 8)); + } + break; + + default: + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s_%s_%s", proto, cc->remote_version, cc->remote_build); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "CCA", (char *)proto); + tpl_addVar(vars, TPLADD, "CCB", cc->remote_version); + tpl_addVar(vars, TPLADD, "CCC", cc->remote_build); +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + if(cl->reader && cl->reader->cacheex.feature_bitfield) + { + if(cl->reader->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CCD", "%s [cx-aio %s]", (cc->extended_mode ? cc->remote_oscam : ""), cl->reader->cacheex.aio_version); + } + else if(cl->reader->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CCD", "%s [cx-aio < 9.2.3]", (cc->extended_mode ? cc->remote_oscam : "")); + } + } + else if(cl->account && cl->account->cacheex.feature_bitfield) + { + if(cl->account->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CCD", "%s [cx-aio %s]", (cc->extended_mode ? cc->remote_oscam : ""), cl->account->cacheex.aio_version); + } + else if(cl->account->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CCD", "%s [cx-aio < 9.2.3]", (cc->extended_mode ? cc->remote_oscam : "")); + } + } + else + { +#endif +#endif + tpl_addVar(vars, TPLADD, "CCD", cc->extended_mode ? cc->remote_oscam : ""); +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + } +#endif +#endif + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTOCCCAMPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s_%s_%s",(char *)proto, cc->remote_version, cc->remote_build); + } + } + else + { +#endif +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + if(cl->reader && cl->reader->cacheex.feature_bitfield) + { + if(cl->reader->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio %s] missing icon: IC_%s_%s_%s.tpl", (cc->extended_mode ? cc->remote_oscam : ""), cl->reader->cacheex.aio_version, proto, cc->remote_version, cc->remote_build); + } + else if(cl->reader->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio < 9.2.3] missing icon: IC_%s_%s_%s.tpl", (cc->extended_mode ? cc->remote_oscam : ""), proto, cc->remote_version, cc->remote_build); + } + } + else if(cl->account && cl->account->cacheex.feature_bitfield) + { + if(cl->account->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio %s] missing icon: IC_%s_%s_%s.tpl", (cc->extended_mode ? cc->remote_oscam : ""), cl->account->cacheex.aio_version, proto, cc->remote_version, cc->remote_build); + } + else if(cl->account->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio < 9.2.3] missing icon: IC_%s_%s_%s.tpl", (cc->extended_mode ? cc->remote_oscam : ""), proto, cc->remote_version, cc->remote_build); + } + } + else + { +#endif +#endif +#ifdef MODULE_CCCAM + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s missing icon: IC_%s_%s_%s.tpl", + cc->extended_mode ? cc->remote_oscam : "", proto, cc->remote_version, cc->remote_build); +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + } +#endif +#endif + } + break; + } + } + } + return; + } +#endif +#ifdef CS_CACHEEX_AIO +#if (defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP)) && defined(CS_CACHEEX) + if(strncmp(proto, "cs3", 3) == 0) + { + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", (char *)proto); + + char aiover[32]; + aiover[0] = '\0'; + + if(cl->account && cl->cacheex_aio_checked) + { + if(cl->account->cacheex.feature_bitfield & 32) + { + snprintf(aiover, sizeof(aiover) / sizeof(char) -1, "%s", cl->account->cacheex.aio_version); + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", aiover); + } + else if(cl->account->cacheex.feature_bitfield) + { + snprintf(aiover, sizeof(aiover) / sizeof(char) -1, "%s", "[cx-aio: < 9.2.3]"); + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", aiover); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", ""); + } + } + + if(cl->reader && cl->cacheex_aio_checked) + { + if(cl->reader->cacheex.feature_bitfield & 32) + { + snprintf(aiover, sizeof(aiover) / sizeof(char) -1, "%s", cl->reader->cacheex.aio_version); + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", aiover); + } + else if(cl->reader->cacheex.feature_bitfield) + { + snprintf(aiover, sizeof(aiover) / sizeof(char) -1, "%s", "[cx-aio: < 9.2.3]"); + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", aiover); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", ""); + } + } + + if(cfg.http_showpicons) + { + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", (char *)proto); + + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", proto); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "CAMD3A", (char *)proto); + if(aiover[0] == '\0') + tpl_addVar(vars, TPLADD, "AIOVER", ""); + else + tpl_printf(vars, TPLADD, "AIOVER", "[cx-aio %s]", aiover); + + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTOCAMD3AIOPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char *)proto); + } + } + else + { + if(cl->account && cl->cacheex_aio_checked) + { + if(cl->account->cacheex.feature_bitfield & 32) + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl [cx-aio %s]", proto, cl->account->cacheex.aio_version); + else if(cl->account->cacheex.feature_bitfield) + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl [cx-aio < 9.2.3]", proto); + else + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl", proto); + } + + if(cl->reader && cl->cacheex_aio_checked) + { + if(cl->reader->cacheex.feature_bitfield & 32) + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl [cx-aio %s]", proto, cl->reader->cacheex.aio_version); + else if(cl->reader->cacheex.feature_bitfield) + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl [cx-aio < 9.2.3]", proto); + else + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl", proto); + } + } + } + return; + } +#endif +#endif + +#ifdef HAVE_DVBAPI + if(streq(proto, "dvbapi") && cl->typ == 'c' && strcmp(dvbapi_get_client_name(), "")) + { + if (!apicall) + tpl_printf(vars, TPLADD, "CLIENTPROTO", "%sclient: %s
protocol version: %d
", proto, dvbapi_get_client_name(), dvbapi_get_client_proto_version()); + else + tpl_printf(vars, TPLADD, "CLIENTPROTO", "%s (client: %s, protocol version: %d)", proto, dvbapi_get_client_name(), dvbapi_get_client_proto_version()); + tpl_printf(vars, TPLADD, "CLIENTPROTOSORT", "%s", proto); + return; + } +#endif + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", (char *)proto); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOSORT", (char *)proto); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", proto); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "OTHER", (char *)proto); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTOOTHERPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char *)proto); + } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl", proto); + } + } +} + +static void kill_account_thread(struct s_auth *account) +{ + struct s_client *cl; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->account == account) + { + if(get_module(cl)->type & MOD_CONN_NET) + { + kill_thread(cl); + } + else + { + cl->account = first_client->account; + } + } + } +} + +static char *send_oscam_user_config(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + struct s_auth *account; + struct s_client *cl; + char *user = getParam(params, "user"); + int32_t found = 0; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + + if(!apicall) + { + setActiveMenu(vars, MNU_USERS); + } + if(strcmp(getParam(params, "action"), "reinit") == 0) + { + if(!cfg.http_readonly) + { refresh_oscam(REFR_ACCOUNTS); } + } + if(strcmp(getParam(params, "action"), "delete") == 0) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. No deletion will be made!"); + } + else + { + struct s_auth *account_prev = NULL; + + for(account = cfg.account; (account); account = account->next) + { + if(strcmp(account->usr, user) == 0) + { + if(account_prev == NULL) + { cfg.account = account->next; } + else + { account_prev->next = account->next; } + ll_clear(account->aureader_list); + kill_account_thread(account); + add_garbage(account); + found = 1; + break; + } + account_prev = account; + } + if(found > 0) + { + if(write_userdb() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + } + else { tpl_addMsg(vars, "Sorry but the specified user doesn't exist. No deletion will be made!"); } + } + } + + if((strcmp(getParam(params, "action"), "disable") == 0) || (strcmp(getParam(params, "action"), "enable") == 0)) + { + account = get_account_by_name(getParam(params, "user")); + if(account) + { + if(strcmp(getParam(params, "action"), "disable") == 0) + { + account->disabled = 1; + kill_account_thread(account); + } + else + { account->disabled = 0; } + if(write_userdb() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + } + else + { + tpl_addMsg(vars, "Sorry but the specified user doesn't exist. No deletion will be made!"); + } + } + + if(strcmp(getParam(params, "action"), "resetstats") == 0) + { + account = get_account_by_name(getParam(params, "user")); + if(account) { clear_account_stats(account); } + } + + if(strcmp(getParam(params, "action"), "resetuserstats") == 0) + { + clear_info_clients_stats(); + } + + if(strcmp(getParam(params, "action"), "resetreaderstats") == 0) + { + clear_info_readers_stats(); + } + + if(strcmp(getParam(params, "action"), "resetalluserstats") == 0) + { + clear_all_account_stats(); + } + + if((strcmp(getParam(params, "part"), "adduser") == 0) && (!cfg.http_readonly)) + { + tpl_addVar(vars, TPLAPPEND, "NEWUSERFORM", tpl_getTpl(vars, "ADDNEWUSER")); + } + + /* List accounts*/ + char *status, *expired, *classname, *lastchan; + time_t now = time((time_t *)0); + int32_t isec = 0, chsec = 0; + + char *filter = NULL; + int32_t clientcount = 0; + if(apicall) + { + filter = getParam(params, "label"); + } + int8_t grp_set = 0; + int8_t expdate_set = 0; + int32_t total_users = 0; + int32_t disabled_users = 0; + int32_t expired_users = 0; + int32_t expired_or_disabled_users = 0; + int32_t connected_users = 0; + int32_t online_users = 0; + int32_t casc_users = 0; + int32_t casc_users2 = 0; + int32_t n_request = 0; + int existing_insert = 0; + + for(account = cfg.account; (account); account = account->next) + { + if(account->expirationdate){ + expdate_set = 1; + } + + if(account->next){ + if(account->grp != account->next->grp){ + grp_set = 1; + } + } + if(expdate_set && grp_set) + break; + } + + for(account = cfg.account; (account); account = account->next) + { + //clear for next client + total_users++; + isactive = 1; + + status = "offline"; + classname = "offline"; + isec = 0; + chsec = 0; + + //reset caid/srevid template variables + tpl_addVar(vars, TPLADD, "CLIENTCAID", ""); + tpl_addVar(vars, TPLADD, "CLIENTPROVID", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVID", ""); + tpl_addVar(vars, TPLADD, "LASTCHANNEL", ""); + tpl_addVar(vars, TPLADD, "LASTCHANNELSORT", ""); + tpl_addVar(vars, TPLADD, "USERMD5", ""); + tpl_addVar(vars, TPLADD, "CWLASTRESPONSET", ""); + tpl_addVar(vars, TPLADD, "CWLASTRESPONSETMS", ""); + tpl_addVar(vars, TPLADD, "CLIENTIP", ""); + tpl_addVar(vars, TPLADD, "LASTCHANNELTITLE", ""); + tpl_addVar(vars, TPLADD, "LASTCHANNELSORT", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNELAPI", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNEL", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMETOSLEEP", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMETOSLEEPAPI", ""); + tpl_addVar(vars, TPLADD, "IDLESECS", ""); + tpl_addVar(vars, TPLADD, "UNOTIFY", ""); + + if(account->expirationdate && account->expirationdate < now) + { + expired = " (expired)"; + classname = "expired"; + expired_users++; + isactive = 0; + } + else + { + expired = ""; + } + + if(account->disabled != 0) + { + expired = " (disabled)"; + classname = "disabled"; + tpl_addVar(vars, TPLADD, "SWITCHICO", "image?i=ICENA"); + tpl_addVar(vars, TPLADD, "SWITCHTITLE", "Enable"); + tpl_addVar(vars, TPLADD, "SWITCH", "enable"); + disabled_users++; + isactive = 0; + } + else + { + tpl_addVar(vars, TPLADD, "SWITCHICO", "image?i=ICDIS"); + tpl_addVar(vars, TPLADD, "SWITCHTITLE", "Disable"); + tpl_addVar(vars, TPLADD, "SWITCH", "disable"); + } + if((account->expirationdate && account->expirationdate < now)||account->disabled != 0) + { + expired_or_disabled_users++; + } + + int32_t lastresponsetm = 0, latestactivity = 0; + const char *proto = ""; + double cwrate = 0.0, cwrate2 = 0.0; + + //search account in active clients + isactive = 0; + int16_t nrclients = 0; + struct s_client *latestclient = NULL; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->account && !strcmp(cl->account->usr, account->usr)) + { + if(cl->lastecm > latestactivity || cl->login > latestactivity) + { + if(cl->lastecm > cl->login) { latestactivity = cl->lastecm; } + else { latestactivity = cl->login; } + latestclient = cl; + } + nrclients++; + } + } + if(account->cwfound + account->cwnot + account->cwcache > 0) + { + cwrate = now - account->firstlogin; + cwrate /= (account->cwfound + account->cwnot + account->cwcache); + } + + casc_users = 0; + casc_users2 = 0; + int8_t conn = 0; + if(latestclient != NULL) + { + char channame[CS_SERVICENAME_SIZE]; + status = (!apicall) ? "connected" : "connected"; + if(account->expirationdate && account->expirationdate < now) { classname = "expired"; } + else { classname = "connected";conn = 1; } + + proto = client_get_proto(latestclient); + int clientcaid = latestclient->last_caid; + int clientsrvid = latestclient->last_srvid; + int clientprovid = latestclient->last_provid; + tpl_printf(vars, TPLADD, "CLIENTCAID", "%04X", clientcaid); + tpl_printf(vars, TPLADD, "CLIENTPROVID", "%06X", clientprovid); + tpl_printf(vars, TPLADD, "CLIENTSRVID", "%04X", clientsrvid); + + if(clientsrvid != NO_SRVID_VALUE || clientcaid != NO_CAID_VALUE) + { + lastchan = xml_encode(vars, get_servicename(latestclient, clientsrvid, clientprovid, clientcaid, channame, sizeof(channame))); + tpl_addVar(vars, TPLADD, "LASTCHANNELSORT", lastchan); + } + else + { + lastchan = ""; + tpl_addVar(vars, TPLADD, "LASTCHANNELSORT", "~~~~~"); + } + + tpl_addVar(vars, TPLADDONCE, "LASTCHANNEL", lastchan); + tpl_addVar(vars, TPLADD, "LASTCHANNELTITLE", lastchan); + + if(cfg.http_showpicons ) + { + char picon_name[128]; + char picon_channame[128]; + int8_t picon_ok = 0; + + get_picon_servicename_or_null(latestclient, clientsrvid, clientprovid, clientcaid, picon_channame, sizeof(picon_channame)); + if(picon_channame[0]) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "%s", picon_channame); + picon_ok = picon_exists(picon_name); + + if(!picon_ok && picon_servicename_remve_hd(picon_channame, sizeof(picon_channame))) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "%s", picon_channame); + picon_ok = picon_exists(picon_name); + } + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "%04X_%06X_%04X", clientcaid, clientprovid, clientsrvid); + picon_ok = picon_exists(picon_name); + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "%04X_%04X", clientcaid, clientsrvid); + picon_ok = picon_exists(picon_name); + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "0000_%04X", clientsrvid); + picon_ok = picon_exists(picon_name); + } + if(picon_ok) + { + tpl_addVar(vars, TPLADDONCE, "LCA", picon_name); + tpl_addVar(vars, TPLADDONCE, "LCB", lastchan); + if(!apicall) + { + tpl_addVar(vars, TPLADDONCE, "LASTCHANNEL", tpl_getTpl(vars, "USERCONFIGLASTCHANEL")); + } + } + else + { + tpl_printf(vars, TPLADD, "LASTCHANNELTITLE", "missing icon: IC_%s.tpl", picon_name); + } + } + + lastresponsetm = latestclient->cwlastresptime; + if(IP_ISSET(latestclient->ip)) + { tpl_addVar(vars, TPLADD, "CLIENTIP", cs_inet_ntoa(latestclient->ip)); } + else if(latestclient->login > latestclient->logout) + { tpl_addVar(vars, TPLADD, "CLIENTIP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "CLIENTIP", ""); } + connected_users++; + casc_users = ll_count(latestclient->cascadeusers); + LL_ITER it = ll_iter_create(latestclient->cascadeusers); + struct s_cascadeuser *cu; + while((cu = ll_iter_next(&it))) + { + if(cu->cwrate > 0) + { casc_users2++; } + } + if(latestactivity > 0) + { + isec = now - latestactivity; + chsec = latestclient->lastswitch ? now - latestclient->lastswitch : 0; + if(isec < cfg.hideclient_to) + { + isactive = 1; + status = (!apicall) ? "online" : "online"; + if(account->expirationdate && account->expirationdate < now) { classname = "expired"; } + else { classname = "online"; } + if(latestclient->cwfound + latestclient->cwnot + latestclient->cwcache > 0) + { + cwrate2 = now - latestclient->login; + cwrate2 /= (latestclient->cwfound + latestclient->cwnot + latestclient->cwcache); + tpl_printf(vars, TPLADDONCE, "CWRATE2", " (%.2f)", cwrate2); + online_users++; + } + } + } + } + + n_request = 0; + if(latestclient != NULL){ + n_request = latestclient->n_request[0]; + } + + tpl_addVar(vars, TPLADD, "EXPIREVIEW", expdate_set ? "" : "exp"); + tpl_addVar(vars, TPLADD, "GRPVIEW", grp_set ? "" : "grp"); +#ifdef CS_ANTICASC + tpl_addVar(vars, TPLADD, "ANTICASCVIEW", cfg.ac_enabled ? "" : "acas"); +#endif + tpl_printf(vars, TPLADD, "CWOK", PRINTF_LOCAL_D, account->cwfound); + tpl_printf(vars, TPLADD, "CWNOK", PRINTF_LOCAL_D, account->cwnot); + tpl_printf(vars, TPLADD, "CWIGN", PRINTF_LOCAL_D, account->cwignored); + tpl_printf(vars, TPLADD, "CWTOUT", PRINTF_LOCAL_D, account->cwtout); +#ifdef CW_CYCLE_CHECK + tpl_addVar(vars, TPLADD, "CWCCYCVIEW", cfg.cwcycle_check_enable ? "" : "cwc"); + tpl_printf(vars, TPLADD, "CWCYCLECHECKED", "%d", account->cwcycledchecked); + tpl_printf(vars, TPLADD, "CWCYCLEOK", "%d", account->cwcycledok); + tpl_printf(vars, TPLADD, "CWCYCLENOK", "%d", account->cwcyclednok); + tpl_printf(vars, TPLADD, "CWCYCLEIGN", "%d", account->cwcycledign); +#endif + tpl_printf(vars, TPLADD, "CWCACHE", PRINTF_LOCAL_D, account->cwcache); + tpl_printf(vars, TPLADD, "CWTUN", PRINTF_LOCAL_D, account->cwtun); + tpl_printf(vars, TPLADD, "EMMOK", PRINTF_LOCAL_D, account->emmok); + tpl_printf(vars, TPLADD, "EMMNOK", PRINTF_LOCAL_D, account->emmnok); + tpl_printf(vars, TPLADD, "CWRATE", "%.2f", cwrate); + tpl_printf(vars, TPLADD, "CASCUSERS", "%d", casc_users); + tpl_printf(vars, TPLADD, "CASCUSERS2", "%d", casc_users2); + tpl_printf(vars, TPLADD, "CASCUSERSCOMB", "%d/%d", casc_users, casc_users2); + tpl_printf(vars, TPLADD, "N_REQUEST_MIN", "%d", n_request); + + if(isactive > 0 || conn > 0) + { + if(casc_users+casc_users2>0) + { + tpl_printf(vars, TPLADD, "CWLASTRESPONSET", "%d", lastresponsetm); + tpl_printf(vars, TPLADD, "CWLASTRESPONSETMS", PRINTF_LOCAL_D " ms", lastresponsetm); + } + tpl_addVar(vars, TPLADD, "IDLESECS", sec2timeformat(vars, isec)); + } + + if(isactive > 0) + { + tpl_printf(vars, TPLADD, "CLIENTTIMEONCHANNELAPI", "%d", chsec); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNEL", sec2timeformat(vars, chsec)); + + if(account->tosleep) + { + if(account->tosleep >0){ + tpl_printf(vars, TPLADD, "CLIENTTIMETOSLEEP", "Sleeping in %d minutes", account->tosleep - (chsec / 60)); + } else { + tpl_addVar(vars, TPLADD, "CLIENTTIMETOSLEEP", "Sleeping"); + } + tpl_printf(vars, TPLADD, "CLIENTTIMETOSLEEPAPI", "%d", account->tosleep - (chsec / 60)); + } else { + tpl_addVar(vars, TPLADD, "CLIENTTIMETOSLEEPAPI", "undefined"); + } + } +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CAMD35) || defined (MODULE_CAMD35_TCP) + if(latestclient != NULL && (latestclient->account->cacheex.feature_bitfield || latestclient->c35_extmode > 1)) +#else + if(latestclient != NULL && (latestclient->account->cacheex.feature_bitfield)) +#endif + { + const char *aio_suffix = " (cx-aio)"; + char *new_proto; + if(cs_malloc(&new_proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + if (!cs_strncat(new_proto, (char *)proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + cs_log("FIXME!"); + } + if (cs_strncat(new_proto, (char *)aio_suffix, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + webif_add_client_proto(vars, latestclient, (const char *)new_proto, apicall); + } else { + cs_log("FIXME!"); + } + free(new_proto); + } + } else { +#endif + webif_add_client_proto(vars, latestclient, proto, apicall); +#ifdef CS_CACHEEX_AIO + } +#endif + + tpl_addVar(vars, TPLADD, "CLASSNAME", classname); + MD5((uint8_t *)account->usr, cs_strlen(account->usr), md5tmp); + int z; + tpl_addVar(vars, TPLADD, "USERMD5","id_"); + for (z = 0; z < MD5_DIGEST_LENGTH; z++) + { + tpl_printf(vars, TPLAPPEND, "USERMD5", "%02x", md5tmp[z]); + } + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, account->usr)); + tpl_addVar(vars, TPLADD, "USERNAMEENC", urlencode(vars, account->usr)); + if(!existing_insert) + { + tpl_printf(vars, TPLADD, "EXISTING_INS", "'%s'", urlencode(vars, account->usr)); + existing_insert++; + }else + { + tpl_printf(vars, TPLAPPEND, "EXISTING_INS", ",'%s'", urlencode(vars, account->usr)); + } + + if(account->description) + tpl_printf(vars, TPLADD, "DESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, account->description)); + else + tpl_addVar(vars, TPLADD, "DESCRIPTION", ""); + + if(cfg.http_showpicons && !apicall) + tpl_addVar(vars, TPLADD, "USERBIT", tpl_getTpl(vars, picon_exists(xml_encode(vars, account->usr)) ? "USERICON" : "USERNOICON")); + else + tpl_addVar(vars, TPLADD, "USERBIT", tpl_getTpl(vars, "USERLABEL")); + + char *value = mk_t_group(account->grp); + tpl_addVar(vars, TPLADD, "GROUPS", value); + free_mk_t(value); + tpl_addVar(vars, TPLADD, "STATUS", status); + tpl_addVar(vars, TPLAPPEND, "STATUS", expired); + + if(nrclients > 1) + { + tpl_printf(vars, TPLADD, "UNOTIFY", "%d", nrclients); + tpl_addVar(vars, TPLADDONCE, "CLIENTCOUNTNOTIFIER", tpl_getTpl(vars, "CLIENTCOUNTNOTIFIERBIT")); + } + + //Expirationdate + struct tm timeinfo; + cs_gmtime_r(&account->expirationdate, &timeinfo); + char buf [80]; + strftime(buf, 80, "%Y-%m-%d", &timeinfo); + if(strcmp(buf, "1970-01-01")) { tpl_addVar(vars, TPLADD, "EXPDATE", buf); } + else { tpl_addVar(vars, TPLADD, "EXPDATE", ""); } + + // append row to table template + if(!apicall) + { tpl_addVar(vars, TPLAPPEND, "USERCONFIGS", tpl_getTpl(vars, "USERCONFIGLISTBIT")); } + else if(!filter || strcmp(filter, account->usr) == 0 || strcmp(filter, "all") == 0 || cs_strlen(filter) == 0) + { + if(apicall == 1){ + tpl_addVar(vars, TPLAPPEND, "APIUSERCONFIGS", tpl_getTpl(vars, "APIUSERCONFIGLISTBIT")); + } else if (apicall == 2){ + tpl_printf(vars, TPLADD, "JSONDELIMITER", "%s", (total_users > 1)?",":""); + tpl_addVar(vars, TPLAPPEND, "APIUSERCONFIGS", tpl_getTpl(vars, "JSONUSERBIT")); + } + ++clientcount; + } + } + + tpl_printf(vars, TPLADD, "TOTAL_USERS", "%d", total_users); + tpl_printf(vars, TPLADD, "TOTAL_DISABLED", "%d", disabled_users); + tpl_printf(vars, TPLADD, "TOTAL_EXPIRED", "%d", expired_users); + tpl_printf(vars, TPLADD, "TOTAL_ACTIVE", "%d", total_users - expired_or_disabled_users); + tpl_printf(vars, TPLADD, "TOTAL_CONNECTED", "%d", connected_users); + tpl_printf(vars, TPLADD, "TOTAL_ONLINE", "%d", online_users); + + //CM info + tpl_addVar(vars, TPLADD, "DISPLAYREADERINFO", "hidden"); // no readerinfo in users + set_ecm_info(vars); + + if(!apicall) + { return tpl_getTpl(vars, "USERCONFIGLIST"); } + else + { + if(!filter || clientcount > 0) + { + return tpl_getTpl(vars, (apicall==1)?"APIUSERCONFIGLIST":"JSONUSER"); + } + else + { + tpl_printf(vars, TPLADD, "APIERRORMESSAGE", "Invalid client %s", xml_encode(vars, filter)); + return tpl_getTpl(vars, "APIERROR"); + } + } + +} + +#define ENTITLEMENT_PAGE_SIZE 500 + +#ifdef MODULE_CCCAM +static void print_cards(struct templatevars *vars, struct uriparams *params, struct cc_card **cardarray, int32_t cardsize, + int8_t show_global_list, struct s_reader *rdr, int32_t offset, int32_t apicall) +{ + if(cardarray) + { + uint8_t serbuf[8]; + int32_t i, count = 0; + char provname[83]; + struct cc_card *card; + int32_t cardcount = 0; + int32_t providercount = 0; + int32_t nodecount = 0; + + char *provider = ""; + + // @todo alno: sort by click, 0=ascending, 1=descending (maybe two buttons or reverse on second click) + for(i = offset; i < cardsize; ++i) + { + card = cardarray[i]; + if(count == ENTITLEMENT_PAGE_SIZE) + { break; } + count++; + + if(!apicall) + { + if(show_global_list) + { rdr = card->origin_reader; } + if(rdr) + { tpl_printf(vars, TPLADD, "HOST", "%s:%d", xml_encode(vars, rdr->device), rdr->r_port); } + tpl_printf(vars, TPLADD, "CAID", "%04X", card->caid); + tpl_printf(vars, TPLADD, "CARDTYPE", "%02X", card->card_type); + } + else + { + tpl_printf(vars, TPLADD, "APICARDNUMBER", "%d", cardcount); + tpl_printf(vars, TPLADD, "APICAID", "%04X", card->caid); + tpl_printf(vars, TPLADD, "APICARDTYPE", "%02X", card->card_type); + } + + if(cc_UA_valid(card->hexserial)) //Add UA: + { + cc_UA_cccam2oscam(card->hexserial, serbuf, card->caid); + char tmp[20]; + tpl_printf(vars, TPLAPPEND, "HOST", "
\nUA_OSCam:%s", cs_hexdump(0, serbuf, 8, tmp, 20)); + tpl_printf(vars, TPLAPPEND, "HOST", "
\nUA_CCcam:%s", cs_hexdump(0, card->hexserial, 8, tmp, 20)); + } + if(!apicall) + { + int32_t n; + char channame[CS_SERVICENAME_SIZE]; + int8_t sidname = 0; + LL_ITER its = ll_iter_create(card->goodsids); + struct cc_srvid *srv; + n = 0; + if(strcmp(getParam(params, "button"), "Show detail list") == 0) + { sidname = 1; } + + tpl_addVar(vars, TPLADD, "SERVICESGOOD", ""); + while((srv = ll_iter_next(&its))) + { + if(sidname) + { + tpl_printf(vars, TPLAPPEND, "SERVICESGOOD", "%04X - %s
", srv->sid, xml_encode(vars, get_servicename(cur_client(), srv->sid, 0, card->caid, channame, sizeof(channame)))); + } else { + tpl_printf(vars, TPLAPPEND, "SERVICESGOOD", "%04X%s", srv->sid, ++n % 10 == 0 ? "
\n" : " "); + } + } + + its = ll_iter_create(card->badsids); + n = 0; + tpl_addVar(vars, TPLADD, "SERVICESBAD", ""); + while((srv = ll_iter_next(&its))) + { + if(sidname) + { + tpl_printf(vars, TPLAPPEND, "SERVICESBAD", "%04X - %s
", srv->sid, xml_encode(vars, get_servicename(cur_client(), srv->sid, 0, card->caid, channame, sizeof(channame)))); + } else { + tpl_printf(vars, TPLAPPEND, "SERVICESBAD", "%04X%s", srv->sid, ++n % 10 == 0 ? "
\n" : " "); + } + } + } + + tpl_addVar(vars, TPLADD, "SYSTEM", get_cardsystem_desc_by_caid(card->caid)); + + tpl_printf(vars, TPLADD, "SHAREID", "%08X", card->id); + tpl_printf(vars, TPLADD, "REMOTEID", "%08X", card->remote_id); + tpl_printf(vars, TPLADD, "UPHOPS", "%d", card->hop); + tpl_printf(vars, TPLADD, "MAXDOWN", "%d", card->reshare); + + LL_ITER pit = ll_iter_create(card->providers); + struct cc_provider *prov; + + providercount = 0; + + if(!apicall) + { tpl_addVar(vars, TPLADD, "PROVIDERS", ""); } + else + { tpl_addVar(vars, TPLADD, "PROVIDERLIST", ""); } + + while((prov = ll_iter_next(&pit))) + { + provider = xml_encode(vars, get_provider(prov->prov, card->caid, provname, sizeof(provname))); + + if(!apicall) + { + if(prov->sa[0] || prov->sa[1] || prov->sa[2] || prov->sa[3]) + { + tpl_printf(vars, TPLAPPEND, "PROVIDERS", "%s SA:%02X%02X%02X%02X
\n", provider, prov->sa[0], prov->sa[1], prov->sa[2], prov->sa[3]); + } + else + { + tpl_printf(vars, TPLAPPEND, "PROVIDERS", "%s
\n", provider); + } + } + else + { + if(prov->sa[0] || prov->sa[1] || prov->sa[2] || prov->sa[3]) + { tpl_printf(vars, TPLADD, "APIPROVIDERSA", "%02X%02X%02X%02X", prov->sa[0], prov->sa[1], prov->sa[2], prov->sa[3]); } + else + { tpl_addVar(vars, TPLADD, "APIPROVIDERSA", ""); } + tpl_printf(vars, TPLADD, "APIPROVIDERCAID", "%04X", card->caid); + tpl_printf(vars, TPLADD, "APIPROVIDERPROVID", "%06X", prov->prov); + tpl_printf(vars, TPLADD, "APIPROVIDERNUMBER", "%d", providercount); + tpl_addVar(vars, TPLADD, "APIPROVIDERNAME", xml_encode(vars, provider)); + tpl_addVar(vars, TPLAPPEND, "PROVIDERLIST", tpl_getTpl(vars, "APICCCAMCARDPROVIDERBIT")); + + } + providercount++; + tpl_printf(vars, TPLADD, "APITOTALPROVIDERS", "%d", providercount); + } + + LL_ITER nit = ll_iter_create(card->remote_nodes); + uint8_t *node; + + nodecount = 0; + if(!apicall) { tpl_addVar(vars, TPLADD, "NODES", ""); } + else { tpl_addVar(vars, TPLADD, "NODELIST", ""); } + + while((node = ll_iter_next(&nit))) + { + + if(!apicall) + { + tpl_printf(vars, TPLAPPEND, "NODES", "%02X%02X%02X%02X%02X%02X%02X%02X
\n", + node[0], node[1], node[2], node[3], node[4], node[5], node[6], node[7]); + } + else + { + tpl_printf(vars, TPLADD, "APINODE", "%02X%02X%02X%02X%02X%02X%02X%02X", node[0], node[1], node[2], node[3], node[4], node[5], node[6], node[7]); + tpl_printf(vars, TPLADD, "APINODENUMBER", "%d", nodecount); + tpl_addVar(vars, TPLAPPEND, "NODELIST", tpl_getTpl(vars, "APICCCAMCARDNODEBIT")); + } + nodecount++; + tpl_printf(vars, TPLADD, "APITOTALNODES", "%d", nodecount); + } + + if(!apicall) + { tpl_addVar(vars, TPLAPPEND, "CCCAMSTATSENTRY", tpl_getTpl(vars, "ENTITLEMENTCCCAMENTRYBIT")); } + else + { tpl_addVar(vars, TPLAPPEND, "CARDLIST", tpl_getTpl(vars, "APICCCAMCARDBIT")); } + + cardcount++; + } + // set previous Link if needed + if(offset >= ENTITLEMENT_PAGE_SIZE) + { + tpl_printf(vars, TPLAPPEND, "CONTROLS", " << PREVIOUS < ", + offset - ENTITLEMENT_PAGE_SIZE, + getParam(params, "globallist"), + urlencode(vars, getParam(params, "label"))); + } + + // set next link if needed + if(cardsize > count && offset < cardsize) + { + tpl_printf(vars, TPLAPPEND, "CONTROLS", " > NEXT >> ", + offset + ENTITLEMENT_PAGE_SIZE, + getParam(params, "globallist"), + urlencode(vars, getParam(params, "label"))); + } + + if(!apicall) + { + tpl_printf(vars, TPLADD, "TOTALS", "card count=%d", cardsize); + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENT", tpl_getTpl(vars, "ENTITLEMENTCCCAMBIT")); + } + else + { + tpl_printf(vars, TPLADD, "APITOTALCARDS", "%d", cardsize); + } + + } + else + { + if(!apicall) + { + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENT", tpl_getTpl(vars, "ENTITLEMENTGENERICBIT")); + tpl_addVar(vars, TPLADD, "LOGHISTORY", "no cards found
\n"); + } + else + { + tpl_printf(vars, TPLADD, "APITOTALCARDS", "%d", 0); + } + } + +} +#endif + +static char *send_oscam_entitlement(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + if(!apicall) { setActiveMenu(vars, MNU_READERS); } + char *reader_ = getParam(params, "label"); +#ifdef MODULE_CCCAM +#ifdef MODULE_CCCSHARE + char *sharelist_ = getParam(params, "globallist"); + int32_t show_global_list = sharelist_ && sharelist_[0] == '1'; +#else + int32_t show_global_list = 0; +#endif + + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(show_global_list || cs_strlen(reader_) || (rdr && rdr->typ == R_CCCAM)) + { + + if(show_global_list || (rdr && rdr->typ == R_CCCAM && rdr->enable)) + { + + if(show_global_list) + { + tpl_addVar(vars, TPLADD, "READERNAME", "GLOBAL"); + tpl_addVar(vars, TPLADD, "APIHOST", "GLOBAL"); + tpl_addVar(vars, TPLADD, "APIHOSTPORT", "GLOBAL"); + } + else + { + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "APIHOST", xml_encode(vars, rdr->device)); + tpl_printf(vars, TPLADD, "APIHOSTPORT", "%d", rdr->r_port); + } + + int32_t offset = atoi(getParam(params, "offset")); //should be 0 if parameter is missed on very first call + int32_t cardsize; +#ifdef MODULE_CCCSHARE + if(show_global_list) + { + int32_t i; + LLIST **sharelist = get_and_lock_sharelist(); + LLIST *sharelist2 = ll_create("web-sharelist"); + for(i = 0; i < CAID_KEY; i++) + { + if(sharelist[i]) + { ll_putall(sharelist2, sharelist[i]); } + } + unlock_sharelist(); + struct cc_card **cardarray = get_sorted_card_copy(sharelist2, 0, &cardsize); + ll_destroy(&sharelist2); + print_cards(vars, params, cardarray, cardsize, 1, NULL, offset, apicall); + NULLFREE(cardarray); + } + else +#endif + { + struct s_client *rc = rdr->client; + struct cc_data *rcc = (rc) ? rc->cc : NULL; + if(rcc && rcc->cards) + { + struct cc_card **cardarray = get_sorted_card_copy(rcc->cards, 0, &cardsize); + print_cards(vars, params, cardarray, cardsize, 0, rdr, offset, apicall); + NULLFREE(cardarray); + } + } + + } + else + { +#else + if(cs_strlen(reader_)) + { + { + struct s_reader *rdr; +#endif + tpl_addVar(vars, TPLADD, "LOGHISTORY", "->"); + // normal non-cccam reader + + rdr = get_reader_by_label(reader_); + + if(rdr) + { + struct s_client *cl = rdr->client; + if(rdr->ll_entitlements) + { + + time_t now = time((time_t *)0); + + struct tm start_t, end_t; + LL_ITER itr = ll_iter_create(rdr->ll_entitlements); + S_ENTITLEMENT *item; + + tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", "

New Structure:
"); + char tbuffer[83]; +#ifdef WITH_EMU + char keyBuffer[1024]; +#endif + int jsondelimiter = 0; + while((item = ll_iter_next(&itr))) + { +#ifdef WITH_EMU + if(item->isKey) + { + tpl_addVar(vars, TPLADD, "ENTSTARTDATE", ""); + tpl_addVar(vars, TPLADD, "ENTENDDATE", ""); + cs_hexdump(0, item->key, item->keyLength, keyBuffer, sizeof(keyBuffer)); + tpl_addVar(vars, TPLADD, "ENTEXPIERED", "e_valid"); + tpl_printf(vars, TPLADD, "ENTCAID", "%04X", item->caid); + tpl_printf(vars, TPLADD, "ENTPROVID", "%08X", item->provid); + tpl_addVar(vars, TPLADD, "ENTID", item->name); + tpl_addVar(vars, TPLADD, "ENTCLASS", keyBuffer); + if(item->isData) { tpl_addVar(vars, TPLADD, "ENTTYPE", "data"); } + else { tpl_addVar(vars, TPLADD, "ENTTYPE", "key"); } + tpl_addVar(vars, TPLADD, "ENTRESNAME", ""); + + if((strcmp(getParam(params, "hideexpired"), "1") != 0) || (item->end > now)) + { tpl_addVar(vars, TPLAPPEND, "READERENTENTRY", tpl_getTpl(vars, "ENTITLEMENTITEMBIT")); } + + continue; + } +#endif + + localtime_r(&item->start, &start_t); + localtime_r(&item->end, &end_t); + + if(!apicall) + { strftime(tbuffer, 30, "%Y-%m-%d", &start_t); } + else + { strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", &start_t); } + tpl_addVar(vars, TPLADD, "ENTSTARTDATE", tbuffer); + + if(!apicall) + { strftime(tbuffer, 30, "%Y-%m-%d", &end_t); } + else + { strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", &end_t); } + tpl_addVar(vars, TPLADD, "ENTENDDATE", tbuffer); + + tpl_addVar(vars, TPLADD, "ENTEXPIERED", item->end > now ? "e_valid" : "e_expired"); + tpl_printf(vars, TPLADD, "ENTCAID", "%04X", item->caid); + tpl_printf(vars, TPLADD, "ENTPROVID", "%06X", item->provid); + tpl_printf(vars, TPLADD, "ENTID", "%08X%08X", (uint32_t)(item->id >> 32), (uint32_t)item->id); + tpl_printf(vars, TPLADD, "ENTCLASS", "%08X", item->class); + tpl_addVar(vars, TPLADD, "ENTTYPE", entitlement_type[item->type]); + + char *entresname; + entresname = xml_encode(vars, get_tiername((uint16_t)(item->id & 0xFFFF), item->caid, tbuffer)); + if(!tbuffer[0]) + { entresname = xml_encode(vars, get_provider(item->provid, item->caid, tbuffer, sizeof(tbuffer))); } + tpl_addVar(vars, TPLADD, "ENTRESNAME", entresname); + + if((strcmp(getParam(params, "hideexpired"), "1") != 0) || (item->end > now)) + { tpl_addVar(vars, TPLAPPEND, "READERENTENTRY", tpl_getTpl(vars, "ENTITLEMENTITEMBIT")); } + + if(apicall==2) + { + tpl_printf(vars, TPLAPPEND, "APIENTITLEMENTLIST","%s%s",jsondelimiter?",":"", tpl_getTpl(vars, "JSONENTITLEMENTBIT")); + jsondelimiter++; + } + } + } + + if(cl && cl->typ) + { tpl_printf(vars, TPLADD, "READERTYPE", "%c", cl->typ); } + else + { tpl_addVar(vars, TPLADD, "READERTYPE", "null"); } + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + + int8_t i, j; + for(i = 0; i < 15; i++) { tpl_printf(vars, TPLAPPEND, "READERROM", "%c", rdr->rom[i]); } + if(rdr->hexserial[0] || rdr->hexserial[1]) { i = 0; } + else { i = 2; } + if(rdr->hexserial[6] || rdr->hexserial[7]) { j = 8; } + else { j = 6; } + for(; i < j; i++) { tpl_printf(vars, TPLAPPEND, "READERSERIAL", "%02X%s", rdr->hexserial[i], i < j - 1 ? " " : ""); } + for(i = 0; i < rdr->nprov; i++) + { + for(j = 0; j < 4; j++) { tpl_printf(vars, TPLAPPEND, "READERPROVIDS", "%02X ", rdr->prid[i][j]); } + tpl_addVar(vars, TPLAPPEND, "READERPROVIDS", i == 0 ? "(sysid)
\n" : "       
\n"); + } + +#ifdef READER_VIDEOGUARD + //CountryCode Vg card + char add_nds_line = 0; + if(rdr->VgCountryC[0]) + { + for(i = 0; i < 3; i++) { tpl_printf(vars, TPLAPPEND, "READERCOUNTRYC", "%c", rdr->VgCountryC[i]); } + add_nds_line = 1; + } + else + { + tpl_addVar(vars, TPLADD, "READERCOUNTRYC", "n/a"); + } + + //regional code for Vg card + if(rdr->VgRegionC[0]) + { + for(i = 0; i < 8; i++) { tpl_printf(vars, TPLAPPEND, "READER_RCODE", "%c", rdr->VgRegionC[i]); } + add_nds_line = 1; + } + else + { + tpl_addVar(vars, TPLADD, "READER_RCODE", "n/a"); + } + + //Pin Vg card + if(rdr->VgPin) + { + tpl_printf(vars, TPLAPPEND, "READERPIN", "%04i", rdr->VgPin); + add_nds_line = 1; + } + else + { + tpl_addVar(vars, TPLADD, "READERPIN", "n/a"); + } + + //Fuse Vg card + if(rdr->VgFuse) + { + tpl_printf(vars, TPLAPPEND, "READERFUSE", "%02X", rdr->VgFuse); + add_nds_line = 1; + } + + if(caid_is_videoguard(rdr->caid)) + { + tpl_printf(vars, TPLAPPEND, "READERPAYLOAD", "%02X %02X %02X %02X %02X %02X", rdr->VgLastPayload[0], + rdr->VgLastPayload[1], rdr->VgLastPayload[2], rdr->VgLastPayload[3], rdr->VgLastPayload[4], rdr->VgLastPayload[5]); + add_nds_line = 1; + } + + //credit on Vg card + if(rdr->VgCredit) + { + tpl_printf(vars, TPLAPPEND, "READERCREDIT", "%i", rdr->VgCredit); + add_nds_line = 1; + } + else + { + tpl_addVar(vars, TPLADD, "READERCREDIT", "n/a"); + } +#endif + + if(rdr->card_valid_to) + { + struct tm vto_t; + char vtobuffer[30]; + localtime_r(&rdr->card_valid_to, &vto_t); + strftime(vtobuffer, 30, "%Y-%m-%d", &vto_t); + tpl_addVar(vars, TPLADD, "READERCARDVALIDTO", vtobuffer); + } + else + { + tpl_addVar(vars, TPLADD, "READERCARDVALIDTO", "n/a"); + } + + if(rdr->irdId[0]) + { + for(i = 0; i < 4; i++) { tpl_printf(vars, TPLAPPEND, "READERIRDID", "%02X ", rdr->irdId[i]); } + } + else + { + tpl_addVar(vars, TPLADD, "READERIRDID", "n/a"); + } + + if(rdr->card_atr_length) + for(i = 0; i < rdr->card_atr_length; i++) { tpl_printf(vars, TPLAPPEND, "READERATR", "%02X ", rdr->card_atr[i]); } + + if(caid_is_seca(rdr->caid) || caid_is_viaccess(rdr->caid)) + { + if(rdr->maturity == 0xF) + { + tpl_printf(vars, TPLAPPEND, "READERMATURITY", "%s ", "no limit"); + } + else + { + tpl_printf(vars, TPLAPPEND, "READERMATURITY", "%d+", rdr->maturity); + } + } + else + { + tpl_printf(vars, TPLAPPEND, "READERMATURITY", "%s ", "n/a"); + } + + if (rdr->csystem) + tpl_addVar(vars, TPLADD, "READERCSYSTEM", rdr->csystem->desc); + +#ifdef READER_VIDEOGUARD + if(add_nds_line) + { + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENTNDS", tpl_getTpl(vars, "ENTITLEMENTBITNDS")); + } +#endif + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENT", tpl_getTpl(vars, "ENTITLEMENTBIT")); + } + else + { + tpl_addMsg(vars, "Reader does not exist or is not started!"); + } + } + + } + else + { + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENT", tpl_getTpl(vars, "ENTITLEMENTGENERICBIT")); + } + + if(!apicall) + { return tpl_getTpl(vars, "ENTITLEMENTS"); } + else + { + if(apicall==1) + { return tpl_getTpl(vars, "APICCCAMCARDLIST"); } + else + { return tpl_getTpl(vars, "JSONENTITLEMENTS"); } + } +} + +#ifdef WEBIF_LIVELOG +static char *send_oscam_logpoll(struct templatevars * vars, struct uriparams * params) +{ + + uint64_t lastid = 0; + +#ifdef WITH_DEBUG + tpl_addVar(vars, TPLADD, "LOG_DEBUGMENU", tpl_getTpl(vars, "LOGDEBUGMENU")); +#endif + tpl_addVar(vars, TPLADD, "LOG_SIZEMENU", tpl_getTpl(vars, "LOGSIZEMENU")); + tpl_addVar(vars, TPLADD, "TITLEADD1", "Move mouse over log-window to stop scroll"); + + if(strcmp(getParam(params, "lastid"), "start") == 0){ + setActiveMenu(vars, MNU_LIVELOG); + return tpl_getTpl(vars, "LOGPAGE"); + } + else + { + lastid = strtoull(getParam(params, "lastid"), NULL, 10); + } + + char *dot = ""; //Delimiter + +#ifdef WITH_DEBUG + char *debuglvl = getParam(params, "debug"); + if(cs_strlen(debuglvl) > 0) { + int32_t dblvl = atoi(debuglvl); + if(cs_dblevel != dblvl) { + if(dblvl >= 0 && dblvl <= 65535) { cs_dblevel = dblvl; } + cs_log("%s debug_level=%d", "all", cs_dblevel); + } + } + tpl_printf(vars, TPLAPPEND, "DATA","%s\"debug\":\"%d\"", dot, cs_dblevel); + dot = ","; + tpl_printf(vars, TPLAPPEND, "DATA","%s\"maxdebug\":\"%d\"",dot, MAX_DEBUG_LEVELS); +#endif + + if(cfg.loghistorylines == 0){ + tpl_printf(vars, TPLAPPEND, "DATA","%s\"logdisabled\":\"1\"",dot); + return tpl_getTpl(vars, "POLL"); + } + + if(log_history) + { + LL_ITER it = ll_iter_create(log_history); + struct s_log_history *hist; + + tpl_printf(vars, TPLAPPEND, "DATA", "%s\"lines\":[", dot); + + dot = ""; + + while((hist = (struct s_log_history*)ll_iter_next(&it))) + { + char p_usr[32]; + size_t pos1 = strcspn(hist->txt, "\t") + 1; + cs_strncpy(p_usr, hist->txt , pos1 > sizeof(p_usr) ? sizeof(p_usr) : pos1); + + char *p_txt = hist->txt + pos1; + + pos1 = strcspn(p_txt, "\n") + 1; + char str_out[pos1]; + cs_strncpy(str_out, p_txt, pos1); + uint64_t id = hist->counter; + + size_t b64_str_in = cs_strlen(xml_encode(vars, str_out)); + size_t b64_str_out = 32 + BASE64_LENGTH(b64_str_in); + char *b64_str_out_buf; + if(!cs_malloc(&b64_str_out_buf, b64_str_out)) + { continue; } + base64_encode(xml_encode(vars, str_out), b64_str_in, b64_str_out_buf, b64_str_out); + + if(id > lastid){ + tpl_printf(vars, TPLAPPEND, "DATA","%s{\"id\":\"%" PRIu64 "\",\"usr\":\"%s\",\"line\":\"%s\"}", + dot, + id, + urlencode(vars, xml_encode(vars, p_usr)), + b64_str_out_buf); + dot = ","; // next in Array with leading delimiter + } + NULLFREE(b64_str_out_buf); + } + } + + tpl_addVar(vars, TPLAPPEND, "DATA", "]"); + return tpl_getTpl(vars, "POLL"); +} +#endif + +static char *send_oscam_status(struct templatevars * vars, struct uriparams * params, int32_t apicall) +{ + const char *usr; + int32_t lsec, isec, chsec, con, cau = 0; + time_t now = time((time_t *)0); + struct tm lt; + int delimiter=0; + + if(!apicall) + { + setActiveMenu(vars, MNU_STATUS); + if (config_enabled(WITH_LB)) + tpl_addVar(vars, TPLADD, "STATUSCOL14HEAD","LB Value/"); + } + if(strcmp(getParam(params, "action"), "kill") == 0) + { + char *cptr = getParam(params, "threadid"); + struct s_client *cl = NULL; + if(cs_strlen(cptr) > 1) + { sscanf(cptr, "%p", (void **)(void *)&cl); } + + if(cl && is_valid_client(cl)) + { + if(is_dvbapi_usr(cl->account->usr)) + { + cs_log("WebIF from %s requests to kill dvbapi client %s -> ignoring!", cs_inet_ntoa(GET_IP()), cl->account->usr); + } + else + { + kill_thread(cl); + cs_log("Client %s killed by WebIF from %s", cl->account->usr, cs_inet_ntoa(GET_IP())); + } + } + } + + if(strcmp(getParam(params, "action"), "resetuserstats") == 0) + { + clear_info_clients_stats(); + } + if(strcmp(getParam(params, "action"), "resetreaderstats") == 0) + { + clear_info_readers_stats(); + } + if(strcmp(getParam(params, "action"), "restart") == 0) + { + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + if(rdr->typ != R_GBOX) + { + add_job(rdr->client, ACTION_READER_RESTART, NULL, 0); + } +#ifdef MODULE_GBOX + else + { + restart_gbox_peer(rdr->label, 0, 0); + } +#endif + cs_log("Reader %s restarted by WebIF from %s", rdr->label, cs_inet_ntoa(GET_IP())); + } + } + + char *debuglvl = getParam(params, "debug"); + if(cs_strlen(debuglvl) > 0) + { +#ifndef WITH_DEBUG + cs_log("*** Warning: Debug Support not compiled in ***"); +#else + int32_t dblvl = atoi(debuglvl); + if(dblvl >= 0 && dblvl <= 65535) { cs_dblevel = dblvl; } + cs_log("%s debug_level=%d", "all", cs_dblevel); +#endif + } + + char *hide = getParam(params, "hide"); + if(cs_strlen(hide) > 0) + { + struct s_client *hideidx = NULL; + sscanf(hide, "%p", (void **)(void *)&hideidx); + + if(hideidx && is_valid_client(hideidx)) + { hideidx->wihidden = 1; } + } + + char *hideidle = getParam(params, "hideidle"); + if(cs_strlen(hideidle) > 0) + { + if(atoi(hideidle) == 2) + { + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 's' || cl->typ == 'h' || cl->typ == 'm'){ + cl->wihidden = 0; + } + } + } + else if(atoi(hideidle) == 3) + { + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 'r'){ + cl->wihidden = 0; + } + } + } + else if(atoi(hideidle) == 4) + { + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 'p'){ + cl->wihidden = 0; + } + } + } + else if(atoi(hideidle) == 5) + { + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 'c'){ + cl->wihidden = 0; + } + } + } + else + { + int32_t oldval = cfg.http_hide_idle_clients; + config_set("webif", "httphideidleclients", hideidle); + if(oldval != cfg.http_hide_idle_clients) + { + refresh_oscam(REFR_SERVER); + } + } + } + + if(cfg.http_hide_idle_clients > 0) { tpl_addVar(vars, TPLADD, "HIDEIDLECLIENTSSELECTED1", "selected"); } + else { tpl_addVar(vars, TPLADD, "HIDEIDLECLIENTSSELECTED0", "selected"); } + + tpl_addVar(vars, TPLADD, "SRVIDFILE", use_srvid2 ? "oscam.srvid2" : "oscam.srvid"); + + int32_t user_count_all = 0, user_count_shown = 0, user_count_active = 0; + int32_t reader_count_all = 0, reader_count_conn = 0, reader_count_off = 0; + int32_t proxy_count_all = 0, proxy_count_conn = 0, proxy_count_off = 0; + int32_t server_count_all = 0, server_count_shown = 0, server_count_hidden = 0; + int32_t monitor_count_all = 0, monitor_count_shown = 0; + int32_t shown; + + int32_t total_readers = 0; + int32_t active_readers = 0; + int32_t disabled_readers = 0; + int32_t connected_readers = 0; + + struct s_client *cl; + int8_t filtered; + + cs_readlock(__func__, &readerlist_lock); + cs_readlock(__func__, &clientlist_lock); + for(cl = first_client; cl ; cl = cl->next) + { +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + struct cc_data *cc = cl->cc; +#endif +#endif + if(cl->kill) { continue; } +#ifdef CS_CACHEEX + if(get_module(cl)->listenertype != LIS_CSPUDP) + { +#endif + + // Reset template variables + tpl_addVar(vars, TPLADD, "LBLRPSTRVALUE", ""); + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", ""); + tpl_addVar(vars, TPLADD, "LASTREADER", ""); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", ""); + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + tpl_addVar(vars, TPLADD, "CLIENTLASTRESPONSETIME", ""); + tpl_addVar(vars, TPLADD, "CLIENTLASTRESPONSETIMEHIST", ""); + tpl_addVar(vars, TPLADD, "UPICMISSING" , ""); + tpl_addVar(vars, TPLADD, "ENTITLEMENTS", ""); + + if(cl->typ == 'c') + { user_count_all++; } + else if(cl->typ == 'p') + { + proxy_count_all++; + if((cl->reader->typ == R_GBOX && cl->reader->card_status != CARD_INSERTED && cl->reader->card_status != NO_CARD) || + (cl->reader->typ != R_GBOX && cl->reader->card_status != CARD_INSERTED)) + { proxy_count_off++; } + } + else if(cl->typ == 'r') + { reader_count_all++; if(cl->reader->card_status != CARD_INSERTED) { reader_count_off++; } } + else if(cl->typ == 's' || cl->typ == 'h') + { server_count_all++; if(cl->wihidden) {server_count_hidden++;} } + else if(cl->typ == 'm') + { monitor_count_all++; } + + shown = 0; + if(cl->wihidden != 1) + { + filtered = !(cfg.http_hide_idle_clients != 1 || cl->typ != 'c' || (now - cl->lastecm) <= cfg.hideclient_to); + if(!filtered && cfg.http_hide_type) + { + char *p = cfg.http_hide_type; + while(*p && !filtered) + { + char type = *p++; +#ifdef CS_CACHEEX + filtered = (type == cl->typ) || (type == 'x' && (cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)); +#else + filtered = (type == cl->typ); +#endif +#ifdef WITH_EMU + if(type == 'e' && cl->typ == 'r' && cl->reader->typ == R_EMU) filtered = 1; +#endif + } + } + + if(!filtered) + { + if(cl->typ == 'c') + { + user_count_shown++; + if(cfg.http_hide_idle_clients != 1 && cfg.hideclient_to > 0 && (now - cl->lastecm) <= cfg.hideclient_to) + { + user_count_active++; + } + } + else if(cl->typ == 's' || cl->typ == 'h') + { + server_count_shown++; + } + else if(cl->typ == 'm') + { + monitor_count_shown++; + } + else if(cl->typ == 'r') + { + reader_count_conn++; + } + else if(cl->typ == 'p') + { + proxy_count_conn++; + } + + if(cl->typ == 'c' || cl->typ == 'r' || cl->typ == 'p') + { + if(cl->lastecm >= cl->login && cl->lastecm >= cl->logout) { isec = now - cl->lastecm; } + else if(cl->logout >= cl->login) { isec = now - cl->logout; } + else { isec = now - cl->login; } + } + else { isec = now - cl->last; } + + shown = 1; + lsec = now - cl->login; + chsec = now - cl->lastswitch; + usr = username(cl); + + if((cl->typ == 'r') || (cl->typ == 'p')) { usr = cl->reader->label; } + + if(cl->dup) { con = 2; } + else if((cl->tosleep) && (now - cl->lastswitch > cl->tosleep)) { con = 1; } + else { con = 0; } + + // no AU reader == 0 / AU ok == 1 / Last EMM > aulow == -1 + if(cl->typ == 'c' || cl->typ == 'p' || cl->typ == 'r') + { + if((cl->typ == 'c' && ll_count(cl->aureader_list) == 0) || ((cl->typ == 'p' || cl->typ == 'r') && cl->reader->audisabled)) { cau = 0; } + else if((now - cl->lastemm) / 60 > cfg.aulow) { cau = -1; } + else { cau = 1; } + + if(cau == 0) + { + tpl_addVar(vars, TPLADD, "CLIENTCAUHTTP", "OFF"); + } + else + { + if(cau == -1) + { tpl_addVar(vars, TPLADD, "CLIENTCAUHTTP", !apicall?"ON":""); } + else + { tpl_addVar(vars, TPLADD, "CLIENTCAUHTTP", !apicall?"ACTIVE":""); } + tpl_addVar(vars, TPLAPPEND, "CLIENTCAUHTTP", !apicall?"":""); + if(cl->typ == 'c') + { + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(cl->aureader_list); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->audisabled) + { tpl_printf(vars, TPLAPPEND, "CLIENTCAUHTTP", "(%s)
", xml_encode(vars, rdr->label)); } + else + { tpl_printf(vars, TPLAPPEND, "CLIENTCAUHTTP", "%s
", xml_encode(vars, rdr->label)); } + } + } + else { tpl_addVar(vars, TPLAPPEND, "CLIENTCAUHTTP", xml_encode(vars, cl->reader->label)); } + tpl_addVar(vars, TPLAPPEND, "CLIENTCAUHTTP", !apicall?"
":""); + } + } + else + { + cau = 0; + tpl_addVar(vars, TPLADD, "CLIENTCAUHTTP", ""); + } + localtime_r(&cl->login, <); + + if(cl->typ == 'c' || cl->typ == 'm') + { + if(cl->account && cl->account->description) + tpl_printf(vars, TPLADD, "CLIENTDESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, cl->account->description)); + else + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + else if(cl->typ == 'p' || cl->typ == 'r') + { + if(cl->reader && cl->reader->description) + tpl_printf(vars, TPLADD, "CLIENTDESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, cl->reader->description)); + else + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + if(!apicall) + { + tpl_addVar(vars, TPLADD, "LBL", xml_encode(vars, usr)); + tpl_printf(vars, TPLADD, "CID", "%p", cl); + if(cl->typ == 'c' || cl->typ == 'm') + { + tpl_addVar(vars, TPLADD, "TARGET", "User"); + tpl_addVar(vars, TPLADD, "CSIDX", tpl_getTpl(vars, "STATUSKBUTTON")); + } + else if(cl->typ == 'p') + { + tpl_addVar(vars, TPLADD, "TARGET", "Proxy"); + tpl_addVar(vars, TPLADD, "LBLENC", urlencode(vars, usr)); + tpl_addVar(vars, TPLADD, "CSIDX", tpl_getTpl(vars, "STATUSRBUTTON")); + } + else if(cl->typ == 'r') + { + tpl_addVar(vars, TPLADD, "TARGET", "Reader"); + tpl_addVar(vars, TPLADD, "LBLENC", urlencode(vars, usr)); + tpl_addVar(vars, TPLADD, "CSIDX", tpl_getTpl(vars, "STATUSRBUTTON")); + } + else if (cl->typ == 'h' || cl->typ == 's') + { + tpl_addVar(vars, TPLADD, "TARGET", "Server"); + tpl_addVar(vars, TPLADD, "CSIDX", ""); + } + tpl_addVar(vars, TPLADD, "HIDEIDX", tpl_getTpl(vars, "STATUSHBUTTON")); + tpl_printf(vars, TPLADD, "CSIDXPLAIN", "id_%p", cl); + } + else + { + tpl_printf(vars, TPLADD, "HIDEIDX", "%p", cl); + tpl_printf(vars, TPLADD, "CSIDX", "id_%p", cl); + } + tpl_printf(vars, TPLADD, "CLIENTTYPE", "%c", cl->typ); + tpl_printf(vars, TPLADD, "CLIENTCNR", "%d", get_threadnum(cl)); + tpl_addVar(vars, TPLADD, "CLIENTUSER", xml_encode(vars, usr)); + + tpl_addVar(vars, TPLADD, "STATUSUSERICON", xml_encode(vars, usr)); + if (cl->typ == 'c' || cl->typ == 'm') + { + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, usr)); + tpl_addVar(vars, TPLADD, "USERENC", urlencode(vars, usr)); + } + else if (cl->typ == 'p' || cl->typ == 'r') + { + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, usr)); + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, usr)); + } + + bool picon_shown = false; + const char *status_user_icon_tpl = NULL; + + char picon_name[32]; + if(cfg.http_showpicons) + { + if(picon_exists(xml_encode(vars, usr))) + { + switch (cl->typ) + { + case 'm': // Fall through + case 'c': status_user_icon_tpl = "SUSERICON"; picon_shown = true; break; + case 'p': // Fall through + case 'r': status_user_icon_tpl = "SREADERICON"; picon_shown = true; break; + } + } + else + tpl_printf(vars, TPLADD, "UPICMISSING", "%smissing icon: IC_%s.tpl",!apicall?" ":"",xml_encode(vars, usr)); + } + + if (!picon_shown) + { + switch (cl->typ) + { + case 'm': // Fall through + case 'c': status_user_icon_tpl = "SUSER"; break; + case 'p': // Fall through + case 'r': status_user_icon_tpl = "SREADER"; break; + } + } + + if (status_user_icon_tpl) + tpl_addVar(vars, TPLADD, "STATUSUSERICON", tpl_getTpl(vars, status_user_icon_tpl)); + + tpl_printf(vars, TPLADD, "CLIENTCAU", "%d", cau); + if(!apicall) + { + if(cl->typ == 'c' || cl->typ == 'p' || cl->typ == 'r') + { + if(cl->crypted) { tpl_addVar(vars, TPLADD, "CLIENTCRYPTED", "ON"); } + else { tpl_addVar(vars, TPLADD, "CLIENTCRYPTED", "OFF"); } + } + else { tpl_addVar(vars, TPLADD, "CLIENTCRYPTED", ""); } + } + else { tpl_printf(vars, TPLADD, "CLIENTCRYPTED", "%d", cl->crypted); } + + if(cl->typ == 'r' && cl->reader && !is_network_reader(cl->reader)) + { tpl_addVar(vars, TPLADD, "CLIENTIP", "local"); } + else if(IP_ISSET(cl->ip)) + { tpl_addVar(vars, TPLADD, "CLIENTIP", cs_inet_ntoa(cl->ip)); } + else if((cl->typ == 'p' || cl->typ == 'r') && cl->reader && cl->reader->tcp_connected) + { tpl_addVar(vars, TPLADD, "CLIENTIP", "camd.socket"); } + else if(cl->typ == 'c' && cl->login > cl->logout) + { tpl_addVar(vars, TPLADD, "CLIENTIP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "CLIENTIP", ""); } + + tpl_printf(vars, TPLADD, "CLIENTPORT", "%d", cl->port); + const char *proto = client_get_proto(cl); +#ifdef CS_CACHEEX_AIO + if(cl && + ( (cl->typ == 'c' && cl->account && cl->account->cacheex.feature_bitfield) +#if defined(MODULE_CAMD35) || defined (MODULE_CAMD35_TCP) + || (cl->c35_extmode > 1) +#endif +#ifdef MODULE_CCCAM + || (cc && cc->extended_lg_flagged_cws) +#endif + || (cl->typ == 'p' && cl->reader && cl->reader->cacheex.feature_bitfield)) + ) + { + const char *aio_suffix = " (cx-aio)"; + char *new_proto; + if(cs_malloc(&new_proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + new_proto[0] = '\0'; + if (!cs_strncat(new_proto, (char *)proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + cs_log("FIXME!"); + } + if (cs_strncat(new_proto, (char *)aio_suffix, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + webif_add_client_proto(vars, cl, (const char*)new_proto, apicall); + } else { + cs_log("FIXME!"); + } + free(new_proto); + } + } else { +#endif + webif_add_client_proto(vars, cl, proto, apicall); +#ifdef CS_CACHEEX_AIO + } +#endif + if(!apicall) + { + if((cl->typ != 'p' && cl->typ != 'r') || cl->reader->card_status == CARD_INSERTED) + { + tpl_printf(vars, TPLADD, "CLIENTLOGINDATE", "%02d.%02d.%02d
%02d:%02d:%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100, lt.tm_hour, lt.tm_min, lt.tm_sec); + tpl_addVar(vars, TPLADD, "CLIENTLOGINSECS", sec2timeformat(vars, lsec)); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTLOGINDATE", ""); + tpl_addVar(vars, TPLADD, "CLIENTLOGINSECS", ""); + } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTLOGINDATEFMT", "%02d.%02d.%02d %02d:%02d:%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100, lt.tm_hour, lt.tm_min, lt.tm_sec); + char tbuffer [30]; + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", <); + tpl_addVar(vars, TPLADD, "CLIENTLOGINDATE", tbuffer); + tpl_printf(vars, TPLADD, "CLIENTLOGINSECS", "%d", lsec); + } + + //load historical values from ringbuffer + char *value = get_ecm_historystring(cl); + tpl_addVar(vars, TPLADD, "CLIENTLASTRESPONSETIMEHIST", value); + free_mk_t(value); + + if((isec < cfg.hideclient_to || cfg.hideclient_to == 0 || is_dvbapi_usr(cl->account->usr)) && (cl->typ == 'c' || cl->typ == 'p' || cl->typ == 'r')) + { + if(((cl->typ == 'c')) && (cl->lastreader[0])) + { + tpl_printf(vars, TPLADD, "MSVALUE", PRINTF_LOCAL_D, cl->cwlastresptime); + if(apicall) + { + tpl_addVar(vars, TPLADD, "LASTREADER", (cl->last_caid == NO_CAID_VALUE || isec > cfg.hideclient_to ) ? "" : cl->lastreader); + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, cl->lastreader)); + } + else + { +#ifdef WITH_LB + tpl_addVar(vars, TPLADD, "LBLVALUE", xml_encode(vars, cl->lastreader)); + if(strstr(cl->lastreader, " (cache)")) + { + char lastreader_tmp[cs_strlen(cl->lastreader) - 8]; + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", tpl_getVar(vars, "LBLRPSTRVALUE")); + cs_strncpy(lastreader_tmp, cl->lastreader, sizeof(lastreader_tmp)); + tpl_addVar(vars, TPLADD, "LBLVALUEENC", urlencode(vars, lastreader_tmp)); + tpl_addVar(vars, TPLADD, "LBLVALUETITLE", xml_encode(vars, lastreader_tmp)); + } + else + { + tpl_addVar(vars, TPLADD, "LBLVALUEENC", urlencode(vars, cl->lastreader)); + tpl_addVar(vars, TPLADD, "LBLVALUETITLE", xml_encode(vars, cl->lastreader)); + } + tpl_addVar(vars, TPLAPPEND, "CLIENTLBVALUE", tpl_getTpl(vars, "CLIENTLBLVALUEBIT")); +#else + tpl_printf(vars, TPLAPPEND, "CLIENTLBVALUE", "%s (%'d ms)", xml_encode(vars, cl->lastreader), cl->cwlastresptime); +#endif + } + if(cl->last_caid == NO_CAID_VALUE || isec > cfg.hideclient_to) tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", ""); + } + if(cl->last_caid != NO_CAID_VALUE || cl->last_srvid != NO_SRVID_VALUE) + { + char channame[CS_SERVICENAME_SIZE]; + const char *lastprovidername; + + get_servicename_or_null(cl, cl->last_srvid, cl->last_provid, cl->last_caid, channame, sizeof(channame)); + if(channame[0] == '\0') + { + cs_strncpy(channame, "unknown", sizeof(channame)); + } + + lastprovidername = get_cl_lastprovidername(cl); + + tpl_printf(vars, TPLADD, "CLIENTCAID", "%04X", cl->last_caid); + tpl_printf(vars, TPLADD, "CLIENTPROVID", "%06X", cl->last_provid); + tpl_printf(vars, TPLADD, "CLIENTSRVID", "%04X", cl->last_srvid); + tpl_printf(vars, TPLADD, "CLIENTSRVPROVIDER", "%s%s%s", (lastprovidername[0] != '\0' && strcmp(lastprovidername, " ")) ? " [" : "", xml_encode(vars, lastprovidername), (lastprovidername[0] != '\0' && strcmp(lastprovidername, " ")) ? "]" : ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVNAME", xml_encode(vars, channame)); + tpl_printf(vars, TPLADD, "CLIENTLASTRESPONSETIME", "%d", cl->cwlastresptime ? cl->cwlastresptime : 1); + tpl_addVar(vars, TPLADD, "CLIENTSRVTYPE", cl->last_srvidptr && cl->last_srvidptr->type ? xml_encode(vars, cl->last_srvidptr->type) : ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVDESCRIPTION", cl->last_srvidptr && cl->last_srvidptr->desc ? xml_encode(vars, cl->last_srvidptr->desc) : ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNEL", sec2timeformat(vars, chsec)); + if(cfg.http_showpicons && cl->last_srvid) + { + char picon_channame[30]; + int8_t picon_ok = 0; + + get_picon_servicename_or_null(cl, cl->last_srvid, cl->last_provid, cl->last_caid, picon_channame, sizeof(picon_channame)); + if(picon_channame[0]) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", picon_channame); + picon_ok = picon_exists(picon_name); + if(picon_ok) tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + + if(!picon_ok && picon_servicename_remve_hd(picon_channame, sizeof(picon_channame))) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", picon_channame); + picon_ok = picon_exists(picon_name); + if(picon_ok) tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + } + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%04X_%06X_%04X", cl->last_caid, cl->last_provid, cl->last_srvid); + tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + picon_ok = picon_exists(picon_name); + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%04X_%04X", cl->last_caid, cl->last_srvid); + picon_ok = picon_exists(picon_name); + if(picon_ok) tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "0000_%04X", cl->last_srvid); + picon_ok = picon_exists(picon_name); + if(picon_ok) tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + } + if(picon_ok) + { + tpl_addVar(vars, TPLADDONCE, "CURRENTPICON", tpl_getTpl(vars, "CLIENTCURRENTCHANNELPIC")); + } + else + { + tpl_addVar(vars, TPLADDONCE, "CURRENTPICON", tpl_getTpl(vars, "CLIENTCURRENTCHANNEL")); + tpl_addVar(vars, TPLADD, "PICONNAME", ""); + } + } + else + { + tpl_addVar(vars, TPLADDONCE, "CURRENTPICON", tpl_getTpl(vars, "CLIENTCURRENTCHANNELBIT")); + tpl_addVar(vars, TPLADD, "PICONNAME", "0000_0000"); + } + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTCAID", "0000"); + tpl_addVar(vars, TPLADD, "CLIENTPROVID", "000000"); + tpl_printf(vars, TPLADD, "CLIENTSRVID", "0000"); + } + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTCAID", "0000"); + tpl_addVar(vars, TPLADD, "CLIENTPROVID", "000000"); + tpl_addVar(vars, TPLADD, "CLIENTSRVID", "0000"); + tpl_addVar(vars, TPLADD, "CURRENTPICON", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVPROVIDER", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVNAME", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVTYPE", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVDESCRIPTION", ""); + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNEL", ""); + } + + if(!apicall) + { + tpl_addVar(vars, TPLADD, "CLIENTIDLESECS", sec2timeformat(vars, isec)); + + if((cl->typ != 'p' && cl->typ != 'r') || cl->reader->card_status == CARD_INSERTED) + { tpl_addVar(vars, TPLADD, "CLIENTIDLESECSCLASS", "idlesec_normal"); } + else + { tpl_addVar(vars, TPLADD, "CLIENTIDLESECSCLASS", "idlesec_alert"); } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTIDLESECS", "%d", isec); + } + + if(con == 2) { tpl_addVar(vars, TPLADD, "CLIENTCON", "Duplicate"); } + else if(con == 1) { tpl_addVar(vars, TPLADD, "CLIENTCON", "Sleep"); } + else + { + struct s_reader *rdr = cl->reader; + char *txt = "OK"; + if(!rdr && (cl->typ == 'r' || cl->typ == 'p')) { txt = "UNKNOWN"; } + + else if(cl->typ == 'r' || cl->typ == 'p') //reader or proxy + { +#ifdef WITH_LB + if(rdr->lbvalue) + { + tpl_printf(vars, TPLADD, "LBLRPSTRVALUE", "%d", rdr->lbvalue); + } + else + { + tpl_addVar(vars, TPLADD, "LBLRPSTRVALUE", "no data"); + } + tpl_addVar(vars, TPLADD, "LBLRPVALUE", rdr->label); + tpl_addVar(vars, TPLADD, "LBLRPVALUEENC", urlencode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", tpl_getTpl(vars, "CLIENTLBLVALUERP")); +#else + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", tpl_getVar(vars, "LBLRPSTRVALUE")); +#endif + switch(rdr->card_status) + { + case NO_CARD: + if(rdr->typ == R_GBOX) + { txt = "ONL"; } + else + { txt = "OFF"; } + break; + case UNKNOWN: + txt = "UNKNOWN"; + break; + case READER_DEVICE_ERROR: + txt = "READER DEVICE ERROR"; + break; + case CARD_NEED_INIT: + if(rdr->typ == R_GBOX) + { txt = "OFFLINE"; } +#ifdef CS_CACHEEX + else if (cl->reader->cacheex.mode > 0) + { txt = "CCcam CacheEX"; } +#endif + else + { txt = "NEEDINIT"; } + break; + case CARD_INSERTED: + if(cl->typ == 'p') + { + if(rdr->typ == R_GBOX) + { txt = "ONL"; } + else + { txt = "CONNECTED"; } + } + else + { txt = "CARDOK"; } + break; + case CARD_FAILURE: + txt = "ERROR"; + break; + default: + txt = "UNDEF"; + } +#ifdef MODULE_GBOX + if(rdr->typ == R_GBOX) + { + struct gbox_peer *peer = cl->gbox; + char gbx_txt[45]; + memset(gbx_txt, 0, sizeof(gbx_txt)); + if(!strcmp(txt, "OFFLINE")) + { + snprintf(gbx_txt, sizeof(gbx_txt), "%s | ID: %04X", txt, peer->gbox.id); + } + else + { + snprintf(gbx_txt, sizeof(gbx_txt), "%s | crd: %d | ID: %04X", txt, gbox_count_peer_cards(peer->gbox.id), peer->gbox.id); + } + txt = gbx_txt; + } +#endif + } + + tpl_addVar(vars, TPLADD, "CLIENTCON", txt); + + if(rdr && (cl->typ == 'r')) //reader + { + if(rdr->ll_entitlements) + { + LL_ITER itr = ll_iter_create(rdr->ll_entitlements); + S_ENTITLEMENT *ent; + uint16_t total_ent = 0; + uint16_t active_ent = 0; + struct tm end_t; + tpl_addVar(vars, TPLADD, "TMPSPAN", ""); + while((ent = ll_iter_next(&itr))) + { + total_ent++; + if((ent->end > now) && (ent->type != 7)) + { + if(active_ent) {tpl_addVar(vars, TPLAPPEND, "TMPSPAN", "

");} + active_ent++; + localtime_r(&ent->end, &end_t); + tpl_printf(vars, TPLAPPEND, "TMPSPAN", "%04X@%06X
exp:%04d/%02d/%02d", + ent->caid, ent->provid, + end_t.tm_year + 1900, end_t.tm_mon + 1, end_t.tm_mday); + tpl_printf(vars, TPLAPPEND, "ENTITLEMENTS", "%s{\"caid\":\"%04X\",\"provid\":\"%06X\",\"exp\":\"%04d/%02d/%02d\"}", + active_ent > 1 ? ",": "", + ent->caid, ent->provid, + end_t.tm_year + 1900, end_t.tm_mon + 1, end_t.tm_mday); + } + } + tpl_printf(vars, TPLADD, "TOTENTITLEMENTS", "%d", total_ent); + if(((total_ent) && (active_ent == 0)) || (total_ent == 0)) + { + tpl_addVar(vars, TPLAPPEND, "TMPSPAN", "No active entitlements found"); + } + tpl_addVar(vars, TPLAPPEND, "TMPSPAN", "
"); + if(active_ent) + { + tpl_printf(vars, TPLADD, "TMP", "(%d entitlement%s)", active_ent, (active_ent != 1) ? "s" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "TMP", "(no entitlements)"); + } + tpl_addVar(vars, TPLADD, "ENTLABEL", urlencode(vars, cl->reader->label)); + tpl_addVar(vars, TPLADD, "ENTVALUE", active_ent > 0 ? "" : "1"); + if (!apicall) tpl_addVar(vars, TPLAPPEND, "CLIENTCON", tpl_getTpl(vars, "FOUNDENTITLEMENTS")); + } + else + { + tpl_addVar(vars, TPLADD, "ENTLABEL", urlencode(vars, cl->reader->label)); + if (!apicall) tpl_addVar(vars, TPLAPPEND, "CLIENTCON", tpl_getTpl(vars, "NOENTITLEMENTS")); + } + } +#ifdef MODULE_CCCAM + if(!apicall || apicall == 2) + { + if(rdr && (cl->typ == 'r' || cl->typ == 'p') && strncmp(proto, "cccam", 5) == 0 && rdr->tcp_connected && rdr->card_status != CARD_FAILURE) + { + struct cc_data *rcc = cl->cc; + if(rcc) + { + LLIST *cards = rcc->cards; + if(cards) + { + int32_t cnt = ll_count(cards); + int32_t locals = rcc->num_hop1; + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "(%d of %d card%s)", locals, cnt, (cnt > 1) ? "s" : ""); + tpl_printf(vars, TPLADD, "CCCOUNT", "%d", cnt); + tpl_printf(vars, TPLADD, "CCCHOP1", "%d", rcc->num_hop1); + tpl_printf(vars, TPLADD, "CCCHOP2", "%d", rcc->num_hop2); + tpl_printf(vars, TPLADD, "CCCHOPX", "%d", rcc->num_hopx); + tpl_printf(vars, TPLADD, "CCCCURR", "%d", cl->reader->currenthops); + tpl_printf(vars, TPLADD, "CCCRES0", "%d", rcc->num_reshare0); + tpl_printf(vars, TPLADD, "CCCRES1", "%d", rcc->num_reshare1); + tpl_printf(vars, TPLADD, "CCCRES2", "%d", rcc->num_reshare2); + tpl_printf(vars, TPLADD, "CCCRESX", "%d", rcc->num_resharex); + tpl_addVar(vars, TPLADD, "TMPSPAN", tpl_getTpl(vars, "CCENTITLEMENTS")); + tpl_addVar(vars, TPLADD, "CCCLABEL", urlencode(vars, cl->reader->label)); + tpl_addVar(vars, TPLADD, "CCCRESHARE", rcc->num_reshare0 > 0 ? "1" : ""); + tpl_addVar(vars, TPLADD, "CCCTMP", tpl_getVar(vars, "TMP")); + tpl_addVar(vars, TPLADD, "CCCTMPSPAN", tpl_getVar(vars, "TMPSPAN")); + tpl_addVar(vars, TPLAPPEND, "CLIENTCON", tpl_getTpl(vars, "CCENTITLETOOLTIP")); + } + if (apicall == 2) + { + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, cl->reader->label)); + tpl_printf(vars, TPLADD, "ENTITLEMENTS", "{\"locals\":\"%d\",\"cccount\":\"%d\",\"ccchop1\":\"%d\",\"ccchop2\":\"%d\",\"ccchopx\":\"%d\",\"ccccurr\":\"%d\",\"cccres0\":\"%d\",\"cccres1\":\"%d\",\"cccres2\":\"%d\",\"cccresx\":\"%d\",\"cccreshare\":\"%s\"}", + locals, cnt, rcc->num_hop1, rcc->num_hop2, rcc->num_hopx, cl->reader->currenthops, + rcc->num_reshare0, rcc->num_reshare1, rcc->num_reshare2, rcc->num_resharex, + rcc->num_reshare0 > 0 ? "1" : ""); + } + cs_log_dbg(D_TRACE, "Reader %s has total %d local%s hop1 %d hopx %d from total of %d card%s", cl->reader->label, locals, (locals > 1) ? "s" : "", rcc->num_hop2, rcc->num_hopx, cnt, (cnt > 1) ? "s" : ""); + } + } + } + } +#endif + } + } + } + if(!apicall) + { + // select right suborder + if(cl->typ == 'c') + { + if(shown) { tpl_addVar(vars, TPLAPPEND, "CLIENTSTATUS", tpl_getTpl(vars, "CLIENTSTATUSBIT")); } + if(cfg.http_hide_idle_clients != 1 && cfg.hideclient_to > 0) + { + tpl_printf(vars, TPLADD, "UCAC", "%d", user_count_active); + tpl_printf(vars, TPLADD, "CFGH", "%d", cfg.hideclient_to); + tpl_addVar(vars, TPLADD, "CHEADADD", tpl_getTpl(vars, "CLIENTHEADLINEADD")); + } + + tpl_printf(vars, TPLADD, "UCS", "%d", user_count_shown); + tpl_printf(vars, TPLADD, "UCA", "%d", user_count_all); + tpl_addVar(vars, TPLADD, "HIDEIDLE", "5"); + tpl_addVar(vars, TPLADD, "SHOWHIDDEN", "User"); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTHEADLINE")); + tpl_addVar(vars, TPLADD, "CLIENTOPTIONADD", tpl_getTpl(vars, "CLIENTHEADLINEBIT")); + tpl_addVar(vars, TPLADD, "CLIENTHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + tpl_addVar(vars, TPLADD, "CLIENTOPTIONADD", ""); + } + else if(cl->typ == 'r') + { + if(shown) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATUS", tpl_getTpl(vars, "CLIENTSTATUSBIT")); + } + + tpl_printf(vars, TPLADD, "RCC", "%d", reader_count_conn); + tpl_printf(vars, TPLADD, "RCA", "%d", reader_count_all); + + if(reader_count_off) + { + tpl_printf(vars, TPLADD, "RCO", "%d", reader_count_all-reader_count_off); + tpl_addVar(vars, TPLADD, "RHEADADD", tpl_getTpl(vars, "CLIENTRHEADLINEADD")); + } + + tpl_addVar(vars, TPLADD, "HIDEIDLE", "3"); + tpl_addVar(vars, TPLADD, "SHOWHIDDEN", "Reader"); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTRHEADLINE")); + tpl_addVar(vars, TPLADD, "READERHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + } + else if(cl->typ == 'p') + { + if(shown) + { + tpl_addVar(vars, TPLAPPEND, "PROXYSTATUS", tpl_getTpl(vars, "CLIENTSTATUSBIT")); + } + + tpl_printf(vars, TPLADD, "PCC", "%d", proxy_count_conn); + tpl_printf(vars, TPLADD, "PCA", "%d", proxy_count_all); + + if(proxy_count_off) + { + tpl_printf(vars, TPLADD, "PCO", "%d", proxy_count_all-proxy_count_off); + tpl_addVar(vars, TPLADD, "PHEADADD", tpl_getTpl(vars, "CLIENTPHEADLINEADD")); + } + + tpl_addVar(vars, TPLADD, "HIDEIDLE", "4"); + tpl_addVar(vars, TPLADD, "SHOWHIDDEN", "Proxy"); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTPHEADLINE")); + tpl_addVar(vars, TPLADD, "PROXYHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + } + else if(cl->typ == 'm' || cl->typ == 's' || cl->typ == 'h') + { + if(shown) + { + tpl_addVar(vars, TPLAPPEND, "SERVERSTATUS", tpl_getTpl(vars, "CLIENTSTATUSBIT")); + } + + tpl_addVar(vars, TPLADD, "HIDEIDLE", "2"); + tpl_addVar(vars, TPLADD, "SHOWHIDDEN", "Server"); + + if(cl->typ == 's' || cl->typ == 'h') + { + tpl_printf(vars, TPLADD, "SCS", "%d", server_count_shown); + tpl_printf(vars, TPLADD, "SCA", "%d", server_count_all); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTSHEADLINE")); + + if(shown || cl->wihidden) + { + tpl_addVar(vars, TPLADD, "SERVERHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + } + } + else + { + tpl_addVar(vars, TPLADD, "SHOWHIDDENADD", " & Monitors"); + tpl_printf(vars, TPLADD, "MCS", "%d", monitor_count_shown); + tpl_printf(vars, TPLADD, "MCA", "%d", monitor_count_all); + tpl_addVar(vars, TPLADD, "MHEADADD", tpl_getTpl(vars, "CLIENTMHEADLINE")); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTSHEADLINE")); + tpl_addVar(vars, TPLADD, "SERVERHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + tpl_addVar(vars, TPLADD, "SHOWHIDDENADD", ""); + } + } + } + else + { + if(shown) + { + if(apicall == 1) + { + tpl_addVar(vars, TPLAPPEND, "APISTATUSBITS", tpl_getTpl(vars, "APISTATUSBIT")); + } + + if(apicall == 2) + { + tpl_addVar(vars, TPLADD, "JSONARRAYDELIMITER", delimiter?",":""); + tpl_addVar(vars, TPLAPPEND, "JSONSTATUSBITS", tpl_getTpl(vars, "JSONSTATUSBIT")); + delimiter++; + } + } + } + +#ifdef CS_CACHEEX + } +#endif + + } + + LL_ITER itr = ll_iter_create(configured_readers); + struct s_reader *rdrr; + while((rdrr = ll_iter_next(&itr))) + { + if(rdrr->label[0] && rdrr->typ) + { + total_readers += 1; + + if(rdrr->enable) { active_readers += 1; } + else { disabled_readers += 1; } + + if(rdrr->tcp_connected) { connected_readers += 1; } + } + } + + cs_readunlock(__func__, &clientlist_lock); + cs_readunlock(__func__, &readerlist_lock); + + if(cfg.http_status_log || (apicall == 1 && strcmp(getParam(params, "appendlog"), "1") == 0)) + { + if(cfg.loghistorylines && log_history) + { + LL_ITER it = ll_iter_create(log_history); + struct s_log_history *hist; + + while((hist = (struct s_log_history*)ll_iter_next(&it))) + { + char p_usr[32]; + size_t pos1 = strcspn(hist->txt, "\t") + 1; + cs_strncpy(p_usr, hist->txt , pos1 > sizeof(p_usr) ? sizeof(p_usr) : pos1); + + char *p_txt = hist->txt + pos1; + + if(!apicall) + { + if(p_txt[0]) tpl_printf(vars, TPLAPPEND, "LOGHISTORY","\t\t%s\t\t
\n", xml_encode(vars, p_usr), xml_encode(vars, p_txt)); + } + else + { + tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", p_txt); + } + } + } + else + { + tpl_addVar(vars, TPLADD, "LOGHISTORY", "loghistorylines is set to 0 in your configuration"); + } + } + +#ifdef CS_CACHEEX + char *getting = "\"Getting\""; + char *pushing = "\"Pushing\""; + + float cachesum = first_client ? first_client->cwcacheexgot : 1; + if(cachesum < 1) + { + cachesum = 1; + } + tpl_printf(vars, TPLADD, "TOTAL_CACHEXPUSH", "%d", first_client ? first_client->cwcacheexpush : 0); + tpl_addVar(vars, TPLADD, "TOTAL_CACHEXPUSH_IMG", pushing); + tpl_printf(vars, TPLADD, "TOTAL_CACHEXGOT", "%d", first_client ? first_client->cwcacheexgot : 0); + tpl_addVar(vars, TPLADD, "TOTAL_CACHEXGOT_IMG", getting); + tpl_printf(vars, TPLADD, "TOTAL_CACHEXHIT", "%d", first_client ? first_client->cwcacheexhit : 0); + tpl_printf(vars, TPLADD, "TOTAL_CACHESIZE", "%d", cache_size()); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "TOTAL_CACHESIZE_LG", "%d", cache_size_lg()); +#endif + tpl_printf(vars, TPLADD, "REL_CACHEXHIT", "%.2f", (first_client ? first_client->cwcacheexhit : 0) * 100 / cachesum); + tpl_addVar(vars, TPLADD, "CACHEEXSTATS", tpl_getTpl(vars, "STATUSCACHEX")); +#endif + //User info + struct s_auth *account; + int32_t total_users = 0; + int32_t disabled_users = 0; + int32_t expired_users = 0; + int32_t expired_or_disabled_users = 0; + int32_t connected_users = 0; + int32_t online_users = 0; + + for(account = cfg.account; (account); account = account->next) + { + total_users++; + if(account->expirationdate && account->expirationdate < now) + { + expired_users++; + } + if(account->disabled != 0) + { + disabled_users++; + } + if((account->expirationdate && account->expirationdate < now)||account->disabled != 0) + { + expired_or_disabled_users++; + } + int32_t latestactivity = 0; + struct s_client *latestclient = NULL; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->account && !strcmp(cl->account->usr, account->usr)) + { + if(cl->lastecm > latestactivity || cl->login > latestactivity) + { + if(cl->lastecm > cl->login) { latestactivity = cl->lastecm; } + else { latestactivity = cl->login; } + latestclient = cl; + } + } + } + + if(latestclient != NULL) + { + connected_users++; + + if(latestactivity > 0) + { + if((now - latestactivity) < cfg.hideclient_to) + { + if(latestclient->cwfound + latestclient->cwnot + latestclient->cwcache > 0) + { + online_users++; + } + } + } + } + } + tpl_printf(vars, TPLADD, "TOTAL_USERS", "%d", total_users); + tpl_printf(vars, TPLADD, "TOTAL_ACTIVE", "%d", total_users - expired_or_disabled_users); + tpl_printf(vars, TPLADD, "TOTAL_EXPIRED", "%d", expired_users); + tpl_printf(vars, TPLADD, "TOTAL_DISABLED", "%d", disabled_users); + tpl_printf(vars, TPLADD, "TOTAL_ONLINE", "%d", online_users); + tpl_printf(vars, TPLADD, "TOTAL_CONNECTED", "%d", connected_users); + + tpl_printf(vars, TPLADD, "TOTAL_READERS", "%d", total_readers); + tpl_printf(vars, TPLADD, "TOTAL_DISABLED_READERS", "%d", disabled_readers); + tpl_printf(vars, TPLADD, "TOTAL_ACTIVE_READERS", "%d", active_readers); + tpl_printf(vars, TPLADD, "TOTAL_CONNECTED_READERS", "%d", connected_readers); + + //CM info + set_ecm_info(vars); + + //copy struct to p_stat_old for cpu_usage calculation + p_stat_old = p_stat_cur; + +/* + * check_available Bit mapping + * mem 0 total, 1 used & free, 2 buff, cached & free incl. buff, 3 share + * swap 4 total, 5 used & free, + * proc 6 count + * cpu 7 load + * oscam 8 vsize & rssize, 9 cpu user, 10 cpu sys, 11 cpu sum, 12 cpu refreshtime + * unused 13 - 15 +*/ + //Memory-CPU Info for linux based systems +#if defined(__linux__) + //get actual stats + if(!get_stats_linux(getpid(),&p_stat_cur)){ + if(p_stat_old.cpu_total_time != 0){ + calc_cpu_usage_pct(&p_stat_cur, &p_stat_old); + } + } + else{ + //something went wrong, so fill with "N/A" + p_stat_cur.check_available = 65535; + } +#else // if not linux, fill with "N/A" but probably in future gets filled also for other platforms + p_stat_cur.check_available = 65535; +#endif + set_status_info(vars, p_stat_cur); + + if(cfg.http_showmeminfo || cfg.http_showuserinfo || cfg.http_showreaderinfo || cfg.http_showloadinfo || cfg.http_showecminfo || (cfg.http_showcacheexinfo && config_enabled(CS_CACHEEX)) || (cfg.http_showcacheexinfo && config_enabled(CS_CACHEEX_AIO))){ + tpl_addVar(vars, TPLADD, "DISPLAYINFO", "visible"); + } + else{ + tpl_addVar(vars, TPLADD, "DISPLAYINFO", "hidden"); + } + + tpl_addVar(vars, TPLADD, "DISPLAYSYSINFO", cfg.http_showmeminfo ? "visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYUSERINFO", cfg.http_showuserinfo ? "visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYREADERINFO", cfg.http_showreaderinfo ? "visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYLOADINFO", cfg.http_showloadinfo ?"visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYECMINFO", cfg.http_showecminfo ? "visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYECMINFO_READERS", cfg.http_showecminfo ? "visible" : "hidden"); + + if(cfg.http_showcacheexinfo == 1 && config_enabled(CS_CACHEEX)) + { + if (config_enabled(CS_CACHEEX_AIO)) + { + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXINFO", "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXAIOINFO", "visible"); + } + else + { + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXINFO", "visible"); + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXAIOINFO", "hidden"); + } + } + else{ + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXINFO", "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXAIOINFO", "hidden"); + } + +#ifdef WITH_DEBUG + if(cfg.http_status_log) + { + // Debuglevel Selector + int32_t i, lvl; + for(i = 0; i < MAX_DEBUG_LEVELS; i++) + { + lvl = 1 << i; + tpl_printf(vars, TPLADD, "TMPC", "DCLASS%d", lvl); + tpl_printf(vars, TPLADD, "TMPV", "DEBUGVAL%d", lvl); + if(cs_dblevel & lvl) + { + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMPC"), "debugls"); + tpl_printf(vars, TPLADD, tpl_getVar(vars, "TMPV"), "%d", cs_dblevel - lvl); + } + else + { + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMPC"), "debugl"); + tpl_printf(vars, TPLADD, tpl_getVar(vars, "TMPV"), "%d", cs_dblevel + lvl); + } + } + + if(cs_dblevel == D_ALL_DUMP) + { tpl_addVar(vars, TPLADD, "DCLASS65535", "debugls"); } + else + { tpl_addVar(vars, TPLADD, "DCLASS65535", "debugl"); } + + tpl_addVar(vars, TPLADD, "NEXTPAGE", "status.html"); + tpl_addVar(vars, TPLADD, "DCLASS", "debugl"); //default + tpl_printf(vars, TPLADD, "ACTDEBUG", "%d", cs_dblevel); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "SDEBUG", tpl_getTpl(vars, "DEBUGSELECTAIO")); +#else + tpl_addVar(vars, TPLADD, "SDEBUG", tpl_getTpl(vars, "DEBUGSELECT")); +#endif + } +#endif + + if(cfg.http_status_log) + tpl_addVar(vars, TPLADDONCE, "LOG_HISTORY", tpl_getTpl(vars, "LOGHISTORYBIT")); + + if(apicall) + { + if(apicall == 1) + { return tpl_getTpl(vars, "APISTATUS"); } + if(apicall == 2) + { + tpl_printf(vars, TPLADD, "UCS", "%d", user_count_shown); + tpl_printf(vars, TPLADD, "UCA", "%d", user_count_all); + if(cfg.http_hide_idle_clients != 1 && cfg.hideclient_to > 0){ + tpl_printf(vars, TPLADD, "UCAC", "%d", user_count_active); + } + tpl_printf(vars, TPLADD, "CFGH", "%d", cfg.hideclient_to); + tpl_printf(vars, TPLADD, "MCS", "%d", monitor_count_shown); + tpl_printf(vars, TPLADD, "MCA", "%d", monitor_count_all); + tpl_printf(vars, TPLADD, "SCS", "%d", server_count_shown); + tpl_printf(vars, TPLADD, "SCH", "%d", server_count_hidden); + tpl_printf(vars, TPLADD, "SCA", "%d", server_count_all); + tpl_printf(vars, TPLADD, "RCC", "%d", reader_count_conn); + tpl_printf(vars, TPLADD, "RCO", "%d", reader_count_off); + tpl_printf(vars, TPLADD, "RCA", "%d", reader_count_all); + tpl_printf(vars, TPLADD, "PCC", "%d", proxy_count_conn); + tpl_printf(vars, TPLADD, "PCO", "%d", proxy_count_off); + tpl_printf(vars, TPLADD, "PCA", "%d", proxy_count_all); + tpl_printf(vars, TPLADD, "PICONENABLED", "%d", cfg.http_showpicons?1:0); + tpl_printf(vars, TPLADD, "SRVIDFILE", "%s", use_srvid2 ? "oscam.srvid2" : "oscam.srvid"); + return tpl_getTpl(vars, "JSONSTATUS"); + } + } + + return tpl_getTpl(vars, "STATUS"); +} + +static char *send_oscam_services_edit(struct templatevars * vars, struct uriparams * params) +{ + struct s_sidtab *sidtab, *ptr; + char label[sizeof(cfg.sidtab->label)]; + int32_t i; + + setActiveMenu(vars, MNU_SERVICES); + + cs_strncpy(label, strtolower(getParam(params, "service")), sizeof(label)); + ++cfg_sidtab_generation; + for(sidtab = cfg.sidtab; sidtab != NULL && strcmp(label, sidtab->label) != 0; sidtab = sidtab->next) { ; } + + if(sidtab == NULL) + { + i = 1; + while(cs_strlen(label) < 1) + { + snprintf(label, sizeof(label) / sizeof(char) - 1, "newservice%d", i); + for(sidtab = cfg.sidtab; sidtab != NULL && strcmp(label, sidtab->label) != 0; sidtab = sidtab->next) { ; } + if(sidtab != NULL) { label[0] = '\0'; } + ++i; + } + if(!cs_malloc(&sidtab, sizeof(struct s_sidtab))) { return "0"; } + + if(cfg.sidtab == NULL) { cfg.sidtab = sidtab; } + else + { + for(ptr = cfg.sidtab; ptr != NULL && ptr->next != NULL; ptr = ptr->next) { ; } + ptr->next = sidtab; + } + cs_strncpy((char *)sidtab->label, label, sizeof(sidtab->label)); + ++cfg_sidtab_generation; + tpl_addMsg(vars, "New service has been added"); + // Adding is uncritical as the new service is appended to sidtabs.ok/sidtabs.no and accounts/clients/readers have zeros there + if(write_services() != 0) { tpl_addMsg(vars, "Writing services to disk failed!"); } + } + + if(strcmp(getParam(params, "action"), "Save") == 0) + { + for(i = 0; i < (*params).paramcount; i++) + { + if((strcmp((*params).params[i], "action")) && (strcmp((*params).params[i], "service"))) + { + chk_sidtab((*params).params[i], (*params).values[i], sidtab); + } + } + ++cfg_sidtab_generation; + tpl_addMsg(vars, "Services updated"); + // We don't need any refresh here as accounts/clients/readers sidtabs.ok/sidtabs.no are unaffected! + if(write_services() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + + for(sidtab = cfg.sidtab; sidtab != NULL && strcmp(label, sidtab->label) != 0; sidtab = sidtab->next) { ; } + } + + if(sidtab) + { + tpl_addVar(vars, TPLADD, "LABEL", xml_encode(vars, sidtab->label)); + tpl_addVar(vars, TPLADD, "LABELENC", urlencode(vars, sidtab->label)); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "DCRCCHECKED", (sidtab->disablecrccws_only_for_exception == 1) ? "checked" : "" ); + tpl_addVar(vars, TPLADD, "NWCHECKED", (sidtab->no_wait_time == 1) ? "checked" : "" ); + tpl_addVar(vars, TPLADD, "LGOECHECKED", (sidtab->lg_only_exception == 1) ? "checked" : "" ); +#endif + for(i = 0; i < sidtab->num_caid; i++) + { + if(i == 0) { tpl_printf(vars, TPLADD, "CAIDS", "%04X", sidtab->caid[i]); } + else { tpl_printf(vars, TPLAPPEND, "CAIDS", ",%04X", sidtab->caid[i]); } + } + for(i = 0; i < sidtab->num_provid; i++) + { + if(i == 0) { tpl_printf(vars, TPLADD, "PROVIDS", "%06X", sidtab->provid[i]); } + else { tpl_printf(vars, TPLAPPEND, "PROVIDS", ",%06X", sidtab->provid[i]); } + } + for(i = 0; i < sidtab->num_srvid; i++) + { + if(i == 0) { tpl_printf(vars, TPLADD, "SRVIDS", "%04X", sidtab->srvid[i]); } + else { tpl_printf(vars, TPLAPPEND, "SRVIDS", ",%04X", sidtab->srvid[i]); } + } + } +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "SERVICEEDITAIO"); +#else + return tpl_getTpl(vars, "SERVICEEDIT"); +#endif +} + +static void delete_from_SIDTABBITS(SIDTABBITS * orgsidtab, int32_t position, int32_t sidtablength) +{ + if(*orgsidtab) + { + int32_t i; + SIDTABBITS newsidtab = 0; + for(i = 0; i < position; ++i) + { + if(*orgsidtab & ((SIDTABBITS)1 << i)) + { newsidtab |= ((SIDTABBITS)1 << i); } + } + for(; i < sidtablength; ++i) + { + if(*orgsidtab & ((SIDTABBITS)1 << (i + 1))) + { newsidtab |= ((SIDTABBITS)1 << i); } + } + *orgsidtab = newsidtab; + } +} + +static char *send_oscam_services(struct templatevars * vars, struct uriparams * params) +{ + struct s_sidtab *sidtab; + char *service = getParam(params, "service"); + char channame[CS_SERVICENAME_SIZE]; + int32_t i, counter = 0; + + setActiveMenu(vars, MNU_SERVICES); + + if(strcmp(getParam(params, "action"), "delete") == 0) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "Sorry, Webif is in readonly mode. No deletion will be made!"); + } + else + { + struct s_sidtab *sidtab_prev = NULL; + int32_t sidtablength = -1; + int32_t position = 0; + + // Calculate sidtablength before deletion so that updating sidtabs is faster + for(sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next) + { ++sidtablength; } + + for(sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next) + { + if(strcmp(sidtab->label, service) == 0) + { + struct s_auth *account; + struct s_client *cl; + struct s_reader *rdr; + + if(!sidtab_prev) + { cfg.sidtab = sidtab->next; } + else + { sidtab_prev->next = sidtab->next; } + + for(account = cfg.account; (account); account = account->next) + { + delete_from_SIDTABBITS(&account->sidtabs.ok, position, sidtablength); + delete_from_SIDTABBITS(&account->sidtabs.no, position, sidtablength); + + for(cl = first_client->next; cl ; cl = cl->next) + { + if(account == cl->account) + { + cl->sidtabs.ok = account->sidtabs.ok; + cl->sidtabs.no = account->sidtabs.no; + } + } + } + + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + delete_from_SIDTABBITS(&rdr->sidtabs.ok, position, sidtablength); + delete_from_SIDTABBITS(&rdr->sidtabs.no, position, sidtablength); + delete_from_SIDTABBITS(&rdr->lb_sidtabs.ok, position, sidtablength); + } + free_sidtab(sidtab); + ++counter; + break; + } + sidtab_prev = sidtab; + position++; + } + if(counter > 0) + { + ++cfg_sidtab_generation; + tpl_addMsg(vars, "Service has been deleted!"); + if(write_services() != 0) { tpl_addMsg(vars, "Writing services to disk failed!"); } + } + else { tpl_addMsg(vars, "Sorry but the specified service doesn't exist. No deletion will be made!"); } + } + } + + sidtab = cfg.sidtab; + // Show List + counter = 0; + while(sidtab != NULL) + { + tpl_addVar(vars, TPLADD, "SID", ""); + tpl_printf(vars, TPLADD, "SERVICENUM", "%d", counter + 1); + if((strcmp(getParam(params, "service"), sidtab->label) == 0) && (strcmp(getParam(params, "action"), "list") == 0)) + { + tpl_addVar(vars, TPLADD, "SIDCLASS", "sidlist"); + tpl_addVar(vars, TPLAPPEND, "SID", ""); + for(i = 0; i < sidtab->num_srvid; i++) + { + tpl_printf(vars, TPLAPPEND, "SID", "%04X : %s
", sidtab->srvid[i], + xml_encode(vars, get_servicename(cur_client(), sidtab->srvid[i], sidtab->num_provid ? sidtab->provid[0] : 0, + sidtab->num_caid ? sidtab->caid[0] : 0, channame, sizeof(channame)))); + } + } + else + { + tpl_addVar(vars, TPLADD, "SIDCLASS", ""); + tpl_printf(vars, TPLADD, "SID", "Show Services", urlencode(vars, sidtab->label)); + } + tpl_addVar(vars, TPLADD, "LABELENC", urlencode(vars, sidtab->label)); + tpl_addVar(vars, TPLADD, "LABEL", xml_encode(vars, sidtab->label)); + tpl_addVar(vars, TPLADD, "SIDLIST", tpl_getTpl(vars, "SERVICECONFIGSIDBIT")); + + tpl_addVar(vars, TPLAPPEND, "SERVICETABS", tpl_getTpl(vars, "SERVICECONFIGLISTBIT")); + sidtab = sidtab->next; + counter++; + } + if(counter >= MAX_SIDBITS) + { + tpl_addVar(vars, TPLADD, "BTNDISABLED", "DISABLED"); + tpl_addMsg(vars, "Maximum Number of Services is reached"); + } + return tpl_getTpl(vars, "SERVICECONFIGLIST"); +} + +static char *send_oscam_savetpls(struct templatevars * vars) +{ + if(cfg.http_tpl) + { + tpl_printf(vars, TPLADD, "CNT", "%d", tpl_saveIncludedTpls(cfg.http_tpl)); + tpl_addVar(vars, TPLADD, "PATH", cfg.http_tpl); + } + else { tpl_addVar(vars, TPLADD, "CNT", "0"); } + return tpl_getTpl(vars, "SAVETEMPLATES"); +} + +static char *send_oscam_shutdown(struct templatevars * vars, FILE * f, struct uriparams * params, int8_t apicall, int8_t *keepalive, char *extraheader) +{ + if(!apicall) { setActiveMenu(vars, MNU_SHUTDOWN); } + if(strcmp(strtolower(getParam(params, "action")), "shutdown") == 0) + { + *keepalive = 0; + if(!apicall) + { + char *CSS = tpl_getUnparsedTpl("CSS", 1, ""); + tpl_addVar(vars, TPLADD, "STYLESHEET", CSS); + NULLFREE(CSS); + tpl_printf(vars, TPLADD, "REFRESHTIME", "%d", SHUTDOWNREFRESH); + tpl_addVar(vars, TPLADD, "REFRESH", tpl_getTpl(vars, "REFRESH")); + tpl_printf(vars, TPLADD, "SECONDS", "%d", SHUTDOWNREFRESH); + char *result = tpl_getTpl(vars, "SHUTDOWN"); + send_headers(f, 200, "OK", extraheader, "text/html", 0, cs_strlen(result), NULL, 0); + webif_write(result, f); + cs_log("Shutdown requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + } + else + { + tpl_addVar(vars, TPLADD, "APICONFIRMMESSAGE", "shutdown"); + cs_log("Shutdown requested by XMLApi from %s", cs_inet_ntoa(GET_IP())); + } + cs_exit_oscam(); + + if(!apicall) + { return "1"; } + else + { return tpl_getTpl(vars, "APICONFIRMATION"); } + + } + else if(strcmp(strtolower(getParam(params, "action")), "restart") == 0) + { + *keepalive = 0; + if(!apicall) + { + char *CSS = tpl_getUnparsedTpl("CSS", 1, ""); + tpl_addVar(vars, TPLADD, "STYLESHEET", CSS); + NULLFREE(CSS); + tpl_addVar(vars, TPLADD, "REFRESHTIME", "5"); + tpl_addVar(vars, TPLADD, "REFRESH", tpl_getTpl(vars, "REFRESH")); + tpl_addVar(vars, TPLADD, "SECONDS", "5"); + char *result = tpl_getTpl(vars, "SHUTDOWN"); + send_headers(f, 200, "OK", extraheader, "text/html", 0, cs_strlen(result), NULL, 0); + webif_write(result, f); + cs_log("Restart requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + } + else + { + tpl_addVar(vars, TPLADD, "APICONFIRMMESSAGE", "restart"); + cs_log("Restart requested by XMLApi from %s", cs_inet_ntoa(GET_IP())); + } + cs_restart_oscam(); + + if(!apicall) + { return "1"; } + else + { return tpl_getTpl(vars, "APICONFIRMATION"); } + + } + else + { + return tpl_getTpl(vars, "PRESHUTDOWN"); + } +} + +static char *send_oscam_script(struct templatevars * vars, struct uriparams * params) +{ + setActiveMenu(vars, MNU_SCRIPT); + tpl_printf(vars, TPLADD, "SCRIPTOPTIONS", "\n"); + + if(!cfg.http_readonly && cfg.http_script) + { + struct dirent **namelist; + int count, i; + count = scandir(cfg.http_script, &namelist, 0, alphasort ); + if( count >= 0 ) + { + for( i = 0 ; i < count; i++ ) + { + if(is_ext(namelist[i]->d_name, ".script") || is_ext(namelist[i]->d_name, ".sh")) + { + tpl_printf(vars, TPLAPPEND, "SCRIPTOPTIONS", "\n",namelist[i]->d_name,namelist[i]->d_name); + } + free( namelist[i] ); + } + free(namelist); + } + + char *scriptname = getParam(params, "scriptname"); + char *scriptparam = getParam(params, "scriptparam"); + char system_str[256]; + struct stat s; + snprintf(system_str, sizeof(system_str), "%s/%s", cfg.http_script, scriptname); + + if(!stat(system_str,&s)) + { + if(s.st_mode & S_IFREG) + { + if(s.st_mode & S_IXUSR) + { + int32_t rc = -1; + FILE *fp; + char buf[256]; + + if((scriptparam != NULL) && (sizeof(scriptparam) > 0)) + { + cs_strncat(system_str, " ", sizeof(system_str)); + cs_strncat(system_str, scriptparam, sizeof(system_str)); + } + + fp = popen(system_str, "r"); + if (fp) + { + while (fgets(buf, sizeof(buf), fp) != NULL) + { + tpl_addVar(vars, TPLAPPEND, "SCRIPTRESULTOUT", buf); + } + rc = pclose(fp)/256; + } + + tpl_printf(vars, TPLAPPEND, "CODE", "returncode: %d", rc); + tpl_printf(vars, TPLADD, "SCRIPTNAME", "scriptname: %s", scriptname); + } + else + { + tpl_printf(vars, TPLADD, "SCRIPTRESULTOUT", "[Error]: Script \"%s\" not executable!", scriptname); + } + } + } + else + { + tpl_printf(vars, TPLADD, "SCRIPTRESULTOUT", "[Error]: Script \"%s\" not found!", scriptname); + } + + } + return tpl_getTpl(vars, "SCRIPT"); +} + +static char *send_oscam_scanusb(struct templatevars * vars) +{ + setActiveMenu(vars, MNU_READERS); +#if !defined(__CYGWIN__) + FILE *fp; + char line[100]; + uint8_t i; + char header[11], txt[13], entry[10], bit[8], scan[12], error[120]; + +// key tool package match command + char *elems[] = { "USB", "lsusb", "usbutils", "Bus ", "lsusb -v | egrep '^Bus|^ *iSerial|^ *iProduct'" + , "UDEV", "/dev/serial/by-id", "udev", "<-", "find /dev/serial/by-id -type l -exec readlink -fn {} ';' -exec echo ' <- {}' \\;" +#ifdef CARDREADER_PCSC + , "PCSC", "pcsc_scan", "pcsc-tools", ":", "pcsc_scan -r" +#endif + }; + + for (i = 0; i < (sizeof(elems) / sizeof(elems[0])); i+=5) + { + snprintf(header, sizeof(header), "%sHEADER", elems[i]); //key + snprintf(txt, sizeof(txt), "%s Devices", elems[i]); //key + snprintf(entry, sizeof(entry), "%sENTRY", elems[i]); //key + snprintf(bit, sizeof(bit), "%sBIT", elems[i]); //key + snprintf(scan, sizeof(scan), "SCAN%sBIT", elems[i]); //key + snprintf(error, sizeof(error), "%s: Failed to run or %s package not installed!", elems[i + 1], elems[i + 2]); //tool + package + + tpl_addVar(vars, TPLADD, header, txt); + fp = popen(elems[i + 4], "r"); //command + if(!fp || !fgets(line, sizeof(line) - 1, fp)) + { + tpl_addVar(vars, TPLADD, entry, error); + tpl_addVar(vars, TPLADD, bit, tpl_getTpl(vars, scan)); + } + else + { + do{ + tpl_addVar(vars, TPLADD, "USBENTRYCLASS", ""); + if(strstr(line, elems[i + 3])) //match + { + tpl_addVar(vars, TPLADD, entry, line); + tpl_addVar(vars, TPLADD, "USBENTRYCLASS", "CLASS=\"scanusbsubhead\""); + } + else + { + tpl_printf(vars, TPLADD, entry, "     %s", line); + } + tpl_addVar(vars, TPLAPPEND, bit, tpl_getTpl(vars, scan)); + } + while(fgets(line, sizeof(line) - 1, fp) != NULL); + pclose(fp); + } + } +#else + tpl_addMsg(vars, "Function not supported in CYGWIN environment"); +#endif + return tpl_getTpl(vars, "SCANUSB"); +} + +static void webif_process_logfile(struct templatevars * vars, struct uriparams * params, char *targetfile, size_t targetfile_len) +{ + snprintf(targetfile, targetfile_len, "%s", cfg.logfile); + if(strcmp(getParam(params, "clear"), "logfile") == 0) + { + if(cs_strlen(targetfile) > 0) + { + FILE *file = fopen(targetfile, "w"); + if (file != NULL) + { + fclose(file); + } + } + } +#ifdef WITH_DEBUG + // Debuglevel Selector + int32_t i, lvl; + for(i = 0; i < MAX_DEBUG_LEVELS; i++) + { + lvl = 1 << i; + tpl_printf(vars, TPLADD, "TMPC", "DCLASS%d", lvl); + tpl_printf(vars, TPLADD, "TMPV", "DEBUGVAL%d", lvl); + if(cs_dblevel & lvl) + { + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMPC"), "debugls"); + tpl_printf(vars, TPLADD, tpl_getVar(vars, "TMPV"), "%d", cs_dblevel - lvl); + } + else + { + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMPC"), "debugl"); + tpl_printf(vars, TPLADD, tpl_getVar(vars, "TMPV"), "%d", cs_dblevel + lvl); + } + } + if(cs_dblevel == D_ALL_DUMP) + { tpl_addVar(vars, TPLADD, "DCLASS65535", "debugls"); } + else + { tpl_addVar(vars, TPLADD, "DCLASS65535", "debugl"); } + tpl_addVar(vars, TPLADD, "CUSTOMPARAM", "&file=logfile"); + tpl_printf(vars, TPLADD, "ACTDEBUG", "%d", cs_dblevel); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "SDEBUG", tpl_getTpl(vars, "DEBUGSELECTAIO")); +#else + tpl_addVar(vars, TPLADD, "SDEBUG", tpl_getTpl(vars, "DEBUGSELECT")); +#endif + tpl_addVar(vars, TPLADD, "NEXTPAGE", "files.html"); +#endif + if(!cfg.disablelog) + { + tpl_printf(vars, TPLADD, "SWITCH", "%d", 1); + tpl_addVar(vars, TPLADD, "TEXT", "Stop Log"); + tpl_addVar(vars, TPLADD, "LOGMENU", tpl_getTpl(vars, "LOGMENUDISABLELOG")); + + } + else + { + tpl_printf(vars, TPLADD, "SWITCH", "%d", 0); + tpl_addVar(vars, TPLADD, "TEXT", "Start Log"); + tpl_addVar(vars, TPLADD, "LOGMENU", tpl_getTpl(vars, "LOGMENUDISABLELOG")); + } + tpl_addVar(vars, TPLAPPEND, "LOGMENU", tpl_getTpl(vars, "CLEARLOG")); + return; +} + +static void webif_process_userfile(struct templatevars * vars, struct uriparams * params, char *targetfile, size_t targetfile_len) +{ + snprintf(targetfile, targetfile_len, "%s", cfg.usrfile); + if(strcmp(getParam(params, "clear"), "usrfile") == 0) + { + if(cs_strlen(targetfile) > 0) + { + FILE *file = fopen(targetfile, "w"); + if (file) + { + fclose(file); + } + } + } + if(!cfg.disableuserfile) + { + tpl_printf(vars, TPLADD, "SWITCH", "%d", 1); + tpl_addVar(vars, TPLADD, "TEXT", "Stop Log"); + tpl_addVar(vars, TPLADD, "LOGMENU", tpl_getTpl(vars, "LOGMENUONOFF")); + } + else + { + tpl_printf(vars, TPLADD, "SWITCH", "%d", 0); + tpl_addVar(vars, TPLADD, "TEXT", "Start Log"); + tpl_addVar(vars, TPLADD, "LOGMENU", tpl_getTpl(vars, "LOGMENUONOFF")); + } + tpl_addVar(vars, TPLAPPEND, "LOGMENU", tpl_getTpl(vars, "CLEARUSERLOG")); + tpl_addVar(vars, TPLADD, "FFVAL", "all"); + tpl_addVar(vars, TPLADD, "FILTERFORMOPTIONS", tpl_getTpl(vars, "LOGMENUFILTERFORM")); + struct s_auth *account; + for(account = cfg.account; account; account = account->next) + { + tpl_addVar(vars, TPLADD, "FFVAL", xml_encode(vars, account->usr)); + tpl_addVar(vars, TPLADD, "FFSEL", strcmp(getParam(params, "filter"), account->usr) ? "" : "selected"); + tpl_addVar(vars, TPLAPPEND, "FILTERFORMOPTIONS", tpl_getTpl(vars, "LOGMENUFILTERFORM")); + } + tpl_addVar(vars, TPLADD, "FILTERFORM", tpl_getTpl(vars, "FILTERFORM")); +} + +enum file_types { FTYPE_CONFIG, FTYPE_VERSION, FTYPE_ANTICASC, FTYPE_LOGFILE, FTYPE_USERFILE , FTYPE_GBOX }; + +struct files +{ + char *file; + int menu_id; + enum file_types type; +}; + +static char *send_oscam_files(struct templatevars * vars, struct uriparams * params, int8_t apicall) +{ + bool writable = false; + const struct files *entry; + static struct files config_files[] = + { + // id are used + // new entry after last entry before first ifdef entry + // ifdef must be add to end + { "oscam.version", MNU_CFG_FVERSION, FTYPE_VERSION }, // id 0 + { "oscam.conf", MNU_CFG_FCONF, FTYPE_CONFIG }, // id 1 + { "oscam.user", MNU_CFG_FUSER, FTYPE_CONFIG }, // id 2 + { "oscam.server", MNU_CFG_FSERVER, FTYPE_CONFIG }, // id 3 + { "oscam.srvid", MNU_CFG_FSRVID, FTYPE_CONFIG }, // id 4 + { "oscam.srvid2", MNU_CFG_FSRVID2, FTYPE_CONFIG }, // id 5 + { "logfile", MNU_CFG_FLOGFILE, FTYPE_LOGFILE }, // id 6 + { "userfile", MNU_CFG_FUSERFILE, FTYPE_USERFILE }, // id 7 + { "css_file_name", MNU_CFG_FCSS, FTYPE_CONFIG }, // id 8 + { "oscam.services", MNU_CFG_FSERVICES, FTYPE_CONFIG }, // id 9 + { "oscam.provid", MNU_CFG_FPROVID, FTYPE_CONFIG }, // id 10 + { "oscam.tiers", MNU_CFG_FTIERS, FTYPE_CONFIG }, // id 11 + { "oscam.ratelimit", MNU_CFG_FRATELIMIT,FTYPE_CONFIG }, // id 12 + { "oscam.whitelist", MNU_CFG_FWHITELIST,FTYPE_CONFIG }, // id 13 +#ifdef HAVE_DVBAPI + { "oscam.dvbapi", MNU_CFG_FDVBAPI, FTYPE_CONFIG }, // id 14 +#endif +#ifdef CS_CACHEEX + { "oscam.fakecws", MNU_CFG_FFAKECWS, FTYPE_CONFIG }, // id 15 +#endif +#ifdef CS_ANTICASC + { "anticasc", MNU_CFG_FACLOG, FTYPE_ANTICASC }, // id 16 +#endif +#ifdef MODULE_SERIAL + { "oscam.twin", MNU_CFG_FTWIN, FTYPE_CONFIG }, // id 17 +#endif +#ifdef MODULE_CONSTCW + { "constant.cw", MNU_CFG_FKEYCW, FTYPE_CONFIG }, // id 18 +#endif +#ifdef MODULE_GBOX + { "sc.info", MNU_GBX_FSCINF, FTYPE_GBOX }, // id 19 + { "share.info", MNU_GBX_FSHRINF, FTYPE_GBOX }, // id 20 + { "share.onl", MNU_GBX_FSHRONL, FTYPE_GBOX }, // id 21 + { "gbox.ver", MNU_GBX_FVERS, FTYPE_GBOX }, // id 22 + { "attack.txt", MNU_GBX_FATTACK, FTYPE_GBOX }, // id 23 + { "gsms.log", MNU_GBX_FSMSLOG, FTYPE_GBOX }, // id 24 + { "gsms.ack", MNU_GBX_FSMSACK, FTYPE_GBOX }, // id 25 + { "gsms.nack", MNU_GBX_FSMSNACK, FTYPE_GBOX }, // id 26 + { "stats.info", MNU_GBX_FSTAINF, FTYPE_GBOX }, // id 27 + { "expired.info", MNU_GBX_FEXPINF, FTYPE_GBOX }, // id 28 + { "info.log", MNU_GBX_INFOLOG, FTYPE_GBOX }, // id 29 +#endif +#ifdef WITH_EMU + { "SoftCam.Key", MNU_CFG_FSOFTCAMKEY,FTYPE_CONFIG }, // id 30 +#endif + { NULL, 0, 0 }, + }; + + if(use_srvid2) + { + config_files[4].menu_id = MNU_CFG_FSRVID2; + config_files[5].menu_id = MNU_CFG_FSRVID; + } + + if(cfg.http_css) + { + if(strchr(cfg.http_css,'/')) + config_files[8].file = strrchr(cfg.http_css, '/')+1; + else if(strchr(cfg.http_css,'\\')) + config_files[8].file = strrchr(cfg.http_css, '\\')+1; + else + config_files[8].file = cfg.http_css; + tpl_addVar(vars, TPLADD, "FILE_USER_CSS", xml_encode(vars, config_files[8].file)); + } + + if(!apicall) { setActiveMenu(vars, MNU_FILES); } + + tpl_addVar(vars, TPLADD, "APIFILENAME", "null"); + tpl_addVar(vars, TPLADD, "APIWRITABLE", "0"); + + if(use_srvid2) + { + tpl_printf(vars, TPLADD, "SRVID", "%s", "oscam.srvid2"); + tpl_printf(vars, TPLADD, "SRVIDSUB", "%s", "oscam.srvid"); + } + else + { + tpl_printf(vars, TPLADD, "SRVID", "%s", "oscam.srvid"); + tpl_printf(vars, TPLADD, "SRVIDSUB", "%s", "oscam.srvid2"); + } + + char *stoplog = getParam(params, "stoplog"); + if(cs_strlen(stoplog) > 0) + { cs_disable_log(atoi(stoplog)); } + + char *stopusrlog = getParam(params, "stopusrlog"); + if(cs_strlen(stopusrlog) > 0) + { cfg.disableuserfile = atoi(stopusrlog); } + + char *debuglvl = getParam(params, "debug"); + if(cs_strlen(debuglvl) > 0) + { +#ifndef WITH_DEBUG + cs_log("*** Warning: Debug Support not compiled in ***"); +#else + int32_t dblvl = atoi(debuglvl); + if(dblvl >= 0 && dblvl <= 65535) { cs_dblevel = dblvl; } + cs_log("%s debug_level=%d", "all", cs_dblevel); +#endif + } + // Process config files + char *file = getParam(params, "file"); + char targetfile[256] = { 0 }; + int menu_id = 0; + for(entry = config_files; entry->file; entry++) + { + if(streq(file, entry->file)) + { + if(!apicall) { setActiveSubMenu(vars, entry->menu_id); } + menu_id = entry->menu_id; + tpl_addVar(vars, TPLADD, "APIWRITABLE", writable ? "1" : "0"); + switch(entry->type) + { + case FTYPE_CONFIG: + writable = 1; + get_config_filename(targetfile, sizeof(targetfile), entry->file); + break; + case FTYPE_VERSION: + get_tmp_dir_filename(targetfile, sizeof(targetfile), entry->file); + break; + case FTYPE_ANTICASC: +#ifdef CS_ANTICASC + if(!apicall) { snprintf(targetfile, sizeof(targetfile), "%s", ESTR(cfg.ac_logfile)); } +#endif + break; + case FTYPE_LOGFILE: + if(!apicall) { webif_process_logfile(vars, params, targetfile, sizeof(targetfile)); } + break; + case FTYPE_USERFILE: + if(!apicall) { webif_process_userfile(vars, params, targetfile, sizeof(targetfile)); } + break; + case FTYPE_GBOX: +#ifdef MODULE_GBOX + get_gbox_filename(targetfile, sizeof(targetfile), entry->file); +#endif + break; + } + tpl_addVar(vars, TPLADD, "APIFILENAME", entry->file); + break; + } + } + + if(cfg.http_css) + { + tpl_addVar(vars, TPLADD, "VIEW_FILEMENUCSS", tpl_getTpl(vars, "FILEMENUCSS")); + } + + if(!strstr(targetfile, "/dev/")) + { + if(strcmp(getParam(params, "action"), "Save") == 0) + { + if((cs_strlen(targetfile) > 0) /*&& (file_exists(targetfile) == 1)*/) + { + FILE *fpsave; + char *fcontent = getParam(params, "filecontent"); + if((fpsave = fopen(targetfile, "w"))) + { + int32_t i, lastpos = 0, len = cs_strlen(fcontent) + 1; + //write submitted file line by line to disk and remove windows linebreaks + for(i = 0; i < len; ++i) + { + char tmp = fcontent[i]; + if(tmp == '\r' || tmp == '\n' || tmp == 0) + { + fcontent[i] = 0; + fprintf(fpsave, "%s%s", fcontent + lastpos, tmp == 0 ? "" : "\n"); + if(tmp == '\r' && fcontent[i + 1] == '\n') { ++i; } + lastpos = i + 1; + } + } + fclose(fpsave); + tpl_addVar(vars, TPLAPPEND, "APIFILENAME", " saved!"); + // Reinit on save + switch(menu_id) + { + case MNU_CFG_FSRVID: + case MNU_CFG_FSRVID2: + init_srvid(); + break; + case MNU_CFG_FPROVID: + init_provid(); + break; + case MNU_CFG_FUSER: + cs_accounts_chk(); + break; + case MNU_CFG_FDVBAPI: + dvbapi_read_priority(); + break; + case MNU_CFG_FWHITELIST: + global_whitelist_read(); + break; + case MNU_CFG_FFAKECWS: + init_fakecws(); + break; + default: + break; + } + } + } + } + + if((cs_strlen(targetfile) > 0) && (file_exists(targetfile) == 1)) + { + FILE *fp; + char buffer[256]; + + if((fp = fopen(targetfile, "r")) == NULL) { return "0"; } + while(fgets(buffer, sizeof(buffer), fp) != NULL) + if(!strcmp(getParam(params, "filter"), "all")) + { tpl_addVar(vars, TPLAPPEND, "FILECONTENT", buffer); } + else if(strstr(buffer, getParam(params, "filter"))) + { tpl_addVar(vars, TPLAPPEND, "FILECONTENT", buffer); } + fclose(fp); + } + else + { + tpl_addVar(vars, TPLAPPEND, "FILECONTENT", "File does not exist or no file selected!"); + } + } + else + { + tpl_addVar(vars, TPLAPPEND, "FILECONTENT", "File not valid!"); + } + + tpl_addVar(vars, TPLADD, "PART", file); + + if(!writable) + { + tpl_addVar(vars, TPLADD, "WRITEPROTECTION", tpl_getTpl(vars, "WRITEPROTECTION")); + tpl_addVar(vars, TPLADD, "BTNDISABLED", "DISABLED"); + } + + if(!apicall) + { return tpl_getTpl(vars, "FILE"); } + else + { return tpl_getTpl(vars, "APIFILE"); } +} + +static char *send_oscam_failban(struct templatevars * vars, struct uriparams * params, int8_t apicall) +{ + IN_ADDR_T ip2delete; + set_null_ip(&ip2delete); + LL_ITER itr = ll_iter_create(cfg.v_list); + V_BAN *v_ban_entry; + //int8_t apicall = 0; //remove before flight + + if(!apicall) { setActiveMenu(vars, MNU_FAILBAN); } + + if(strcmp(getParam(params, "action"), "delete") == 0) + { + if(strcmp(getParam(params, "intip"), "all") == 0) + { + // clear whole list + while(ll_iter_next(&itr)) + { + ll_iter_remove_data(&itr); + } + } + else + { + //we have a single IP + cs_inet_addr(getParam(params, "intip"), &ip2delete); + while((v_ban_entry = ll_iter_next(&itr))) + { + if(IP_EQUAL(v_ban_entry->v_ip, ip2delete)) + { + ll_iter_remove_data(&itr); + break; + } + } + } + } + ll_iter_reset(&itr); + + struct timeb now; + cs_ftime(&now); + + while((v_ban_entry = ll_iter_next(&itr))) + { + tpl_printf(vars, TPLADD, "IPADDRESS", "%s@%d", cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port); + tpl_addVar(vars, TPLADD, "VIOLATIONUSER", v_ban_entry->info ? v_ban_entry->info : "unknown"); + struct tm st ; + localtime_r(&v_ban_entry->v_time.time, &st); // fix me, we need walltime! + if(!apicall) + { + tpl_printf(vars, TPLADD, "VIOLATIONDATE", "%02d.%02d.%02d %02d:%02d:%02d", + st.tm_mday, st.tm_mon + 1, + st.tm_year % 100, st.tm_hour, + st.tm_min, st.tm_sec); + } + else + { + char tbuffer [30]; + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", &st); + tpl_addVar(vars, TPLADD, "VIOLATIONDATE", tbuffer); + } + + tpl_printf(vars, TPLADD, "VIOLATIONCOUNT", "%d", v_ban_entry->v_count); + + int64_t gone = comp_timeb(&now, &v_ban_entry->v_time); + if(!apicall) + { + if(!v_ban_entry->acosc_entry) + { tpl_addVar(vars, TPLADD, "LEFTTIME", sec2timeformat(vars, (cfg.failbantime * 60) - (gone / 1000))); } + else + { tpl_addVar(vars, TPLADD, "LEFTTIME", sec2timeformat(vars, v_ban_entry->acosc_penalty_dur - (gone / 1000))); } + } + else + { + if(!v_ban_entry->acosc_entry) + { tpl_printf(vars, TPLADD, "LEFTTIME", "%"PRId64"", (cfg.failbantime * 60) - (gone / 1000)); } + else + { tpl_printf(vars, TPLADD, "LEFTTIME", "%"PRId64"", v_ban_entry->acosc_penalty_dur - (gone / 1000)); } + } + + tpl_addVar(vars, TPLADD, "INTIP", cs_inet_ntoa(v_ban_entry->v_ip)); + + if(!apicall) + { tpl_addVar(vars, TPLAPPEND, "FAILBANROW", tpl_getTpl(vars, "FAILBANBIT")); } + else + { tpl_addVar(vars, TPLAPPEND, "APIFAILBANROW", tpl_getTpl(vars, "APIFAILBANBIT")); } + } + if(!apicall) + { return tpl_getTpl(vars, "FAILBAN"); } + else + { return tpl_getTpl(vars, "APIFAILBAN"); } +} + +static bool send_EMM(struct s_reader * rdr, uint16_t caid, const struct s_cardsystem *csystem, const uint8_t *emmhex, uint32_t len) +{ + if(NULL != rdr && NULL != emmhex && 0 != len) + { + EMM_PACKET *emm_pack = NULL; + + if(cs_malloc(&emm_pack, sizeof(EMM_PACKET))) + { + struct s_client *webif_client = cur_client(); + webif_client->grp = 0xFF; /* to access to all readers */ + + memset(emm_pack, '\0', sizeof(EMM_PACKET)); + emm_pack->client = webif_client; + emm_pack->emmlen = len; + memcpy(emm_pack->emm, emmhex, len); + + emm_pack->caid[0] = (caid >> 8) & 0xFF; + emm_pack->caid[1] = caid & 0xFF; + + if(csystem && csystem->get_emm_type) + { + if(!csystem->get_emm_type(emm_pack, rdr)) + { + rdr_log_dbg(rdr, D_EMM, "get_emm_type() returns error"); + } + } + + cs_log_dbg(D_EMM, "emm is being sent to reader %s.", rdr->label); + add_job(rdr->client, ACTION_READER_EMM, emm_pack, sizeof(EMM_PACKET)); + return true; + } + } + + return false; +} + +static bool process_single_emm(struct templatevars * vars, struct s_reader * rdr, uint16_t caid, const struct s_cardsystem *csystem, const char *ep) +{ + + if(NULL != vars && NULL != rdr && NULL != ep) + { + char emmdata[1025] = {'\0'}; /*1024 + '\0'*/ + uint8_t emmhex[513] = {'\0'}; + char buff[7] = {'\0'}; + uint16_t len = 0; + cs_strncpy(emmdata, ep, sizeof(emmdata)); + remove_white_chars(emmdata); + + if('\0' != emmdata[0]) + { + len = cs_strlen(emmdata); + tpl_addVar(vars, TPLADD, "EP", strtoupper(emmdata)); + if(key_atob_l(emmdata, emmhex, len)) + { + tpl_addMsg(vars, "Single EMM has not been sent due to wrong value!"); + } + else + { + len /= 2; + snprintf(buff, sizeof(buff), "0x%02X", len); + tpl_addVar(vars, TPLADD, "EP", strtoupper(emmdata)); + tpl_addVar(vars, TPLADD, "SIZE", buff); + + if(send_EMM(rdr, caid, csystem, emmhex, len)) + { + tpl_addMsg(vars, "Single EMM has been sent."); + return true; + } + } + } + } + tpl_addVar(vars, TPLADD, "SIZE", "0x00"); + return false; +} + +static bool process_emm_file(struct templatevars * vars, struct s_reader * rdr, uint16_t caid, const struct s_cardsystem *csystem, const char *sFilePath) +{ + + bool bret = false; + uint32_t fsize = 0; + uint32_t rlines = 0; + uint32_t wemms = 0; + uint32_t errsize = 0; + char numerrl[256] = {'\0'}; + char buff[20] = {'\0'}; + + if(NULL != rdr && NULL != sFilePath && '\0' != sFilePath[0]) + { + char sMessage[128] = {0}; + if(true == file_exists(sFilePath)) + { + FILE *fp; + if((fp = fopen(sFilePath, "r"))) + { + char line[2048] = {'\0'}; + uint8_t emmhex[513] = {'\0'}; + uint32_t len = 0; + + tpl_addMsg(vars, "EMM file has been processed."); + while(fgets(line, sizeof(line), fp)) + { + ++rlines; + len = cs_strlen(remove_white_chars(line)); + + // wrong emm + if(len > (sizeof(emmhex) * 2) || + key_atob_l(line, emmhex, len)) + { + errsize += snprintf(numerrl + errsize, sizeof(numerrl) - errsize, "%d, ", rlines); + continue; + } + len /= 2; + if(send_EMM(rdr, caid, csystem, emmhex, len)) + { + ++wemms; + int32_t jcount = ll_count(rdr->client->joblist); + if (jcount > 200) + { + /* Give more time to process EMMs */ + cs_sleepms(1000); + } + rdr_log_dbg(rdr, D_READER, "pending emm jobs: %i, processed emms: %i", jcount, wemms); + } + } + fsize = ftell(fp); + fclose(fp); + } + else + { + snprintf(sMessage, sizeof(sMessage), "Cannot open file '%s' (errno=%d: %s)\n", sFilePath, errno, strerror(errno)); + tpl_addMsg(vars, sMessage); + } + } + else + { + snprintf(sMessage, sizeof(sMessage), "FILE \"%s\" not found!", sFilePath); + tpl_addMsg(vars, sMessage); + } + bret = true; + } + + snprintf(buff, sizeof(buff), "%d bytes", fsize); + tpl_addVar(vars, TPLADD, "FSIZE", buff); + snprintf(buff, sizeof(buff), "%d", rlines); + tpl_addVar(vars, TPLADD, "NUMRLINE", buff); + snprintf(buff, sizeof(buff), "%d", wemms); + tpl_addVar(vars, TPLADD, "NUMWEMM", buff); + tpl_addVar(vars, TPLADD, "ERRLINE", numerrl); + + return bret; +} + +static char *send_oscam_EMM_running(struct templatevars * vars, struct uriparams * params) +{ + + struct s_reader *rdr = NULL; + + setActiveMenu(vars, MNU_READERS); + tpl_addVar(vars, TPLADD, "READER", getParam(params, "label")); + tpl_addVar(vars, TPLADD, "FNAME", getParam(params, "emmfile")); + + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + int32_t tcaid = dyn_word_atob(getParam(params, "emmcaid")); + uint16_t caid = (-1 != tcaid) ? (uint16_t)tcaid : 0; + char buff[7] = ""; + const struct s_cardsystem *csystem = NULL; + int32_t proxy = is_cascading_reader(rdr); + + if((proxy || !rdr->csystem_active) && caid) // network reader (R_CAMD35 R_NEWCAMD R_CS378X R_CCCAM) + { + if(proxy && !rdr->ph.c_send_emm) + { + tpl_addMsg(vars, "The reader does not support EMM's!"); + return tpl_getTpl(vars, "EMM_RUNNING"); + } + + csystem = get_cardsystem_by_caid(caid); + if(!csystem) + { + rdr_log_dbg(rdr, D_EMM, "unable to find cardsystem for caid %04X", caid); + caid = 0; + } + } + else if(!proxy && rdr->csystem_active) // local active reader + { + csystem = rdr->csystem; + + if(rdr->typ != R_EMU) + { + caid = rdr->caid; + } + } + + if(csystem) + { + tpl_addVar(vars, TPLADD, "SYSTEM", csystem->desc); + } + else + { + tpl_addVar(vars, TPLADD, "SYSTEM", "unknown"); + } + if(caid) + { + snprintf(buff, sizeof(buff), "0x%04X", caid); + tpl_addVar(vars, TPLADD, "CAID", buff); + } + else + { + tpl_addVar(vars, TPLADD, "CAID", "unknown"); + } + + process_single_emm(vars, rdr, caid, csystem, getParam(params, "ep")); + process_emm_file(vars, rdr, caid, csystem, getParam(params, "emmfile")); + } + else + { + char sMessage[128] = {0}; + snprintf(sMessage, sizeof(sMessage), "READER \"%s\" not found!", getParam(params, "label")); + tpl_addMsg(vars, sMessage); + tpl_addVar(vars, TPLADD, "READER", "reader not found"); + } + + return tpl_getTpl(vars, "EMM_RUNNING"); +} + +static char *send_oscam_EMM(struct templatevars * vars, struct uriparams * params) +{ + + setActiveMenu(vars, MNU_READERS); + tpl_addVar(vars, TPLADD, "READER", getParam(params, "label")); + + struct s_reader *rdr = NULL; + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr && rdr->caid) + { + char buff[5] = ""; + snprintf(buff, sizeof(buff), "%04X", rdr->caid); + tpl_addVar(vars, TPLADD, "CAID", buff); + if(!is_cascading_reader(rdr)) + { + tpl_addVar(vars, TPLADD, "READONLY", "readonly=\"readonly\""); + } + } + + FILE *fp; + struct stat sb; + char buffer[1024]; + char emm_hex[1024]; + char filename[128]; + char targetfile[256]; + char tmpstr[20]; + char emm_txt[32]; + char emm_title[32]; + char *emm_path; + char *emm_types[] = { "unique_emm", "shared_emm", "global_emm" }; + char *emm_names[] = { "RDREMMUNIQUE", "RDREMMSHARED", "RDREMMGLOBAL" }; + char *emm_cfg_names[] = { "httpemmuclean", "httpemmsclean", "httpemmgclean" }; + int32_t emm_max_size[] = { cfg.http_emmu_clean, cfg.http_emms_clean, cfg.http_emmg_clean }; + int num_emm_types = 3; + int i; + + emm_path = cfg.emmlogdir ? cfg.emmlogdir : cs_confdir; + + for( i = 0 ; i < num_emm_types; i++ ) + { + snprintf(filename, sizeof(filename), "%s%s%s%s", getParam(params, "label"), "_", emm_types[i], ".log"); + snprintf(targetfile, sizeof(targetfile), "%s%s%s", emm_path, emm_path[cs_strlen(emm_path) - 1] == '/' ? "" : "/", filename); + snprintf(emm_txt, sizeof(emm_txt), "%s_TXT", emm_names[i]); + tpl_addVar(vars, TPLADD, emm_txt, filename); + + if((fp = fopen(targetfile, "r")) != NULL) + { + stat(targetfile, &sb); + tpl_printf(vars, TPLAPPEND, emm_txt, " (Size: %'.2f kB)", (double)sb.st_size/1024); + + if(emm_max_size[i]>=0) + { + snprintf(emm_title, sizeof(emm_title), "%s_TITLE", emm_names[i]); + tpl_addVar(vars, TPLADD, emm_title, "title=\"Click Line to Copy EMM in Single EMM Write Field\""); + } + + if(emm_max_size[i]>0) + { + uint32_t emms=0, emm_d, emmrs=0; + char *ptr, *saveptr1 = NULL; + + while(fgets(buffer, sizeof(buffer), fp) != NULL) + { + emms++; + snprintf(tmpstr, sizeof(tmpstr), "LINE_%d", emms); + tpl_addVar(vars, TPLADD, tmpstr, buffer); + } + + for(emm_d=emms;emm_d>0;--emm_d) + { + snprintf(tmpstr, sizeof(tmpstr), "LINE_%d", emm_d); + if(sscanf(tpl_getVar(vars, tmpstr), "%*s %*s %*s %s", &emm_hex[0])==1) + { + if(strstr(tpl_getVar(vars, "EMM_TMP"),emm_hex)==0) + { tpl_addVar(vars, TPLAPPEND, "EMM_TMP", tpl_getVar(vars, tmpstr)); } + tpl_addVar(vars, TPLADD, tmpstr, ""); + } + } + + for(ptr = strtok_r(tpl_getVar(vars, "EMM_TMP"),"\n", &saveptr1); ptr; ptr = strtok_r(NULL,"\n", &saveptr1)) + { + emmrs++; + snprintf(tmpstr, sizeof(tmpstr), "LINE_%d", emmrs); + tpl_addVar(vars, TPLADD, tmpstr, ptr); + } + tpl_addVar(vars, TPLADD, "EMM_TMP", ""); + + tpl_printf(vars, TPLAPPEND, emm_txt, ": %'d different EMM's from a total of %'d Entries", emmrs,emms); + for(emm_d=emmrs;emm_d>0;--emm_d) + { + snprintf(tmpstr, sizeof(tmpstr), "LINE_%d", emm_d); + tpl_printf(vars, TPLAPPEND, emm_names[i], "%s\n", tpl_getVar(vars, tmpstr)); + if(sb.st_size>emm_max_size[i]*1024) { tpl_printf(vars, TPLAPPEND, "EMM_TMP", "%s\n", tpl_getVar(vars, tmpstr)); } + tpl_addVar(vars, TPLADD, tmpstr, ""); + } + + if(sb.st_size>emm_max_size[i]*1024) + { + char orgfile[268]; + int f=0; + do { + snprintf(orgfile, sizeof(orgfile), "%s.%d", targetfile, f); + f++; + } while(access(orgfile, F_OK) != -1); + + if(rename(targetfile, orgfile) == 0) + { + FILE *fs = fopen(targetfile, "w"); + if (fs) + { + fprintf(fs, "%s", tpl_getVar(vars, "EMM_TMP")); + fclose(fs); + } + tpl_printf(vars, TPLAPPEND, emm_txt, "
New reduced File created! Size of Original File is higher as %d kB, saved to %s", emm_max_size[i], orgfile); + } + } + tpl_addVar(vars, TPLADD, "EMM_TMP", ""); + } + else if (emm_max_size[i]==0) + { + while(fgets(buffer, sizeof(buffer), fp) != NULL) + { + tpl_printf(vars, TPLAPPEND, emm_names[i], "%s", buffer); + } + } + else + { + tpl_printf(vars, TPLADD, emm_names[i],"Viewing of EMM File deactivated.
Set %s in Config Webif to 0 or higher for viewing or filtering EMM File.", emm_cfg_names[i]); + } + fclose(fp); + } + if(strcmp(tpl_getVar(vars, emm_names[i]),"")==0) { tpl_addVar(vars, TPLADD, emm_names[i],"no saved EMM's"); } + } + + return tpl_getTpl(vars, "ASKEMM"); +} + +#ifdef CS_CACHEEX +static uint64_t get_cacheex_node(struct s_client * cl) +{ + uint64_t node = 0x00; +#if defined(MODULE_CCCAM) || defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP) + struct s_module *module = (cl->reader ? &cl->reader->ph : get_module(cl)); +#endif +#ifdef MODULE_CCCAM + if(module->num == R_CCCAM && cl->cc) + { + struct cc_data *cc = cl->cc; + memcpy(&node, cc->peer_node_id, 8); + } + else +#endif +#ifdef MODULE_CAMD35 + if(module->num == R_CAMD35) + { + memcpy(&node, cl->ncd_skey, 8); + } + else +#endif +#ifdef MODULE_CAMD35_TCP + if(module->num == R_CS378X) + { + memcpy(&node, cl->ncd_skey, 8); + } + else +#endif + {} + return node; +} + + +static char *send_oscam_cacheex(struct templatevars * vars, struct uriparams * params, int8_t apicall) +{ + + if(!apicall) { setActiveMenu(vars, MNU_CACHEEX); } + + if(strcmp(getParam(params, "x"), "x") == 0) + { + // avoid compilerwarning unused vars + } + char *level[] = {"NONE", "CACHE PULL", "CACHE PUSH", "REVERSE CACHE PUSH"}; + char *getting = "\"Getting\""; + char *pushing = "\"Pushing\""; + char *rowvariable = ""; + + int16_t written = 0; +#ifdef CS_CACHEEX_AIO + int16_t i = 0; +#endif + struct s_client *cl; + time_t now = time((time_t *)0); + int delimiter=0; + + if(!apicall) + { + if(strcmp(getParam(params, "action"), "resetallcacheexstats") == 0) + { + cacheex_clear_all_stats(); + } + } + + tpl_printf(vars, TPLADD, "OWN_CACHEEX_NODEID", "%" PRIu64 "X", cacheex_node_id(cacheex_peer_id)); + + const char *cacheex_name_link_tpl = NULL; + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + + for(cl = first_client; cl ; cl = cl->next) + { +#ifdef CS_CACHEEX_AIO + i++; + char classname[9]; + snprintf(classname, 8, "class%02d", i) < 0 ? abort() : (void)0; + classname[8] = '\0'; + tpl_addVar(vars, TPLADD, "CLASSNAME", classname); +#endif + + if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) + { + cacheex_name_link_tpl = "SUSER"; + tpl_addVar(vars, TPLADD, "TYPE", "Client"); + if(!apicall) + { + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, cl->account->usr)); + tpl_addVar(vars, TPLADD, "USERENC", urlencode(vars, cl->account->usr)); + + if(cl->account->description) + { + tpl_printf(vars, TPLADD, "CLIENTDESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, cl->account->description)); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + } + else + { + tpl_addVar(vars, TPLADD, "NAME", cl->account->usr); + + if(cl->account->description) + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", cl->account->description); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + } + + { tpl_addVar(vars, TPLADD, "IP", ""); } + + +// --- NODEID changer detection (persistent, based on cxnodeid_changer_detected) --- +int idx = cl ? cl->idx : 0; +if(idx < 0 || idx >= WEBIF_MAX_NODES) idx = 0; + +uint64_t current_node = get_cacheex_node(cl); + +if(cl && cl->cxnodeid_changer_detected) +{ + tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", current_node); +} +else +{ + tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", current_node); +} + +// zapamiętaj nodeid w RAM (optional) +webif_last_nodeid[idx] = current_node; + +// --- end detection --- + +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "GOTLG", "%d", cl->account->cwcacheexgotlg); + tpl_printf(vars, TPLADD, "PUSHLG", "%d", cl->account->cwcacheexpushlg); + tpl_printf(vars, TPLADD, "REL_CACHEXHITGOT", "%.2f", (double)(cl->account->cwcacheexhit ? (double)cl->account->cwcacheexhit : 0) * 100 / (double)(cl->account->cwcacheexgot ? cl->account->cwcacheexgot : 1)); +#endif + tpl_addVar(vars, TPLADD, "DIRECTIONIMG", (cl->account->cacheex.mode == 3) ? getting : pushing); + rowvariable = "TABLECLIENTROWS"; + written = 1; + } + else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) + { + cacheex_name_link_tpl = "SREADER"; + tpl_addVar(vars, TPLADD, "TYPE", "Reader"); + + if(!apicall) + { + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, cl->reader->label)); + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, cl->reader->label)); + + if(cl->reader->description) + { + tpl_printf(vars, TPLADD, "CLIENTDESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, cl->reader->description)); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + } + else + { + tpl_addVar(vars, TPLADD, "NAME", cl->reader->label); + + if(cl->reader->description) + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", cl->reader->description); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + } + + if(IP_ISSET(cl->ip)) + { tpl_addVar(vars, TPLADD, "IP", cs_inet_ntoa(cl->ip)); } + else if(cl->reader && cl->reader->tcp_connected) + { tpl_addVar(vars, TPLADD, "IP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "IP", ""); } + + uint64_t current_node = get_cacheex_node(cl); + + if(cl && cl->cxnodeid_changer_detected) +{ + tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", current_node); +} +else +{ + tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", current_node); +} + tpl_addVar(vars, TPLADD, "LEVEL", level[cl->reader->cacheex.mode]); + tpl_printf(vars, TPLADD, "PUSH", "%d", cl->cwcacheexpush); + tpl_printf(vars, TPLADD, "CWCINFO", "%d", cl->cwc_info); + tpl_printf(vars, TPLADD, "GOT", "%d", cl->cwcacheexgot); + tpl_printf(vars, TPLADD, "CWCINFO", "%d", cl->cwc_info); + tpl_printf(vars, TPLADD, "HIT", "%d", cl->cwcacheexhit); + tpl_printf(vars, TPLADD, "ERR", "%d", cl->cwcacheexerr); + tpl_printf(vars, TPLADD, "ERRCW", "%d", cl->cwcacheexerrcw); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "GOTLG", "%d", cl->cwcacheexgotlg); + tpl_printf(vars, TPLADD, "PUSHLG", "%d", cl->cwcacheexpushlg); + tpl_printf(vars, TPLADD, "REL_CACHEXHITGOT", "%.2f", (double)(cl->cwcacheexhit ? (double)cl->cwcacheexhit : 0) * 100 / (double)(cl->cwcacheexgot ? cl->cwcacheexgot : 1)); +#endif + tpl_addVar(vars, TPLADD, "DIRECTIONIMG", (cl->reader->cacheex.mode == 3) ? pushing : getting); + + rowvariable = "TABLEREADERROWS"; + written = 1; + } + else if(get_module(cl)->listenertype == LIS_CSPUDP) + { + cacheex_name_link_tpl = "SREADER"; + tpl_addVar(vars, TPLADD, "TYPE", "csp"); + + if(!apicall) + { + tpl_addVar(vars, TPLADD, "READERNAME", "csp"); + tpl_addVar(vars, TPLADD, "READERNAMEENC", "csp"); + } + else + { + tpl_addVar(vars, TPLADD, "NAME", "csp"); + } + + if(IP_ISSET(cl->ip)) + { tpl_addVar(vars, TPLADD, "IP", cs_inet_ntoa(cl->ip)); } + else if(cl->login > cl->logout) + { tpl_addVar(vars, TPLADD, "IP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "IP", ""); } + tpl_addVar(vars, TPLADD, "NODE", "csp"); + + if(cl->cwcacheexping) + { + tpl_printf(vars, TPLADD, "LEVEL", "csp (%d ms)", cl->cwcacheexping); + } + else + { + tpl_addVar(vars, TPLADD, "LEVEL", "csp"); + } + + tpl_printf(vars, TPLADD, "PUSH", "%d", cl->cwcacheexpush); + tpl_printf(vars, TPLADD, "GOT", "%d", cl->cwcacheexgot); + tpl_printf(vars, TPLADD, "HIT", "%d", cl->cwcacheexhit); + tpl_printf(vars, TPLADD, "ERR", "%d", cl->cwcacheexerr); + tpl_printf(vars, TPLADD, "ERRCW", "%d", cl->cwcacheexerrcw); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "GOTLG", "%d", cl->cwcacheexgotlg); + tpl_printf(vars, TPLADD, "PUSHLG", "%d", cl->cwcacheexpushlg); + tpl_printf(vars, TPLADD, "REL_CACHEXHITGOT", "%.2f", (double)(cl->cwcacheexhit ? (double)cl->cwcacheexhit : 0) * 100 / (double)(cl->cwcacheexgot ? cl->cwcacheexgot : 1)); +#endif + tpl_addVar(vars, TPLADD, "DIRECTIONIMG", getting); + rowvariable = "TABLECLIENTROWS"; + written = 1; + } + + if(written) + { + if(!apicall) + { + tpl_addVar(vars, TPLADD, "NAME", tpl_getTpl(vars, cacheex_name_link_tpl)); + } +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLAPPEND, rowvariable, tpl_getTpl(vars, "CACHEEXAIOTABLEROW")); +#else + tpl_addVar(vars, TPLAPPEND, rowvariable, tpl_getTpl(vars, "CACHEEXTABLEROW")); +#endif + + if(cl->ll_cacheex_stats) + { + LL_ITER itr = ll_iter_create(cl->ll_cacheex_stats); + S_CACHEEX_STAT_ENTRY *cacheex_stats_entry; + + while((cacheex_stats_entry = ll_iter_next(&itr))) + { + tpl_addVar(vars, TPLADD, "DIRECTIONIMG", ""); + if(now - cacheex_stats_entry->cache_last < 20) + { tpl_addVar(vars, TPLADD, "TYPE", cacheex_stats_entry->cache_direction == 0 ? pushing : getting); } + else + { tpl_addVar(vars, TPLADD, "TYPE", ""); } + tpl_printf(vars, TPLADD, "NAME", "%04X@%06X:%04X", cacheex_stats_entry->cache_caid, + cacheex_stats_entry->cache_prid, + cacheex_stats_entry->cache_srvid); + if(cacheex_stats_entry->cache_direction == 0) + { + tpl_printf(vars, TPLADD, "PUSH", "%d", cacheex_stats_entry->cache_count); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "PUSHLG", "%d", cacheex_stats_entry->cache_count_lg); +#endif + tpl_addVar(vars, TPLADD, "GOT", ""); + } + else + { + tpl_printf(vars, TPLADD, "GOT", "%d", cacheex_stats_entry->cache_count); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "GOTLG", "%d", cacheex_stats_entry->cache_count_lg); +#endif + tpl_addVar(vars, TPLADD, "PUSH", ""); + } + tpl_addVar(vars, TPLADD, "HIT", ""); + char channame[CS_SERVICENAME_SIZE]; + char *lastchan = xml_encode(vars, get_servicename(cl, cacheex_stats_entry->cache_srvid, cacheex_stats_entry->cache_prid, cacheex_stats_entry->cache_caid, channame, sizeof(channame))); + tpl_addVar(vars, TPLADD, "LEVEL", lastchan); + if (apicall == 2) + { + tpl_printf(vars, TPLADD, "JSONDELIMITER", "%s", (delimiter > 1)?",":""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLAPPEND, "JSONCACHEEXBITS", tpl_getTpl(vars, "JSONCACHEEXAIOBIT")); +#else + tpl_addVar(vars, TPLAPPEND, "JSONCACHEEXBITS", tpl_getTpl(vars, "JSONCACHEEXBIT")); +#endif + delimiter++; + } + else + { +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLAPPEND, rowvariable, tpl_getTpl(vars, "CACHEEXAIOTABLEROWSTATS")); +#else + tpl_addVar(vars, TPLAPPEND, rowvariable, tpl_getTpl(vars, "CACHEEXTABLEROW")); +#endif + } + } + } + written = 0; + } + } + + float cachesum = first_client ? first_client->cwcacheexgot : 1; + if(cachesum < 1) + { + cachesum = 1; + } + tpl_printf(vars, TPLADD, "TOTAL_CACHEXPUSH", PRINTF_LOCAL_D, first_client ? first_client->cwcacheexpush : 0); + tpl_addVar(vars, TPLADD, "TOTAL_CACHEXPUSH_IMG", pushing); + tpl_printf(vars, TPLADD, "TOTAL_CACHEXGOT", PRINTF_LOCAL_D, first_client ? first_client->cwcacheexgot : 0); + tpl_addVar(vars, TPLADD, "TOTAL_CACHEXGOT_IMG", getting); + tpl_printf(vars, TPLADD, "TOTAL_CACHEXHIT", PRINTF_LOCAL_D, first_client ? first_client->cwcacheexhit : 0); + tpl_printf(vars, TPLADD, "TOTAL_CACHESIZE", "%d", cache_size()); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "TOTAL_CACHESIZE_LG", "%d", cache_size_lg()); +#endif + + tpl_printf(vars, TPLADD, "REL_CACHEXHIT", "%.2f", (first_client ? first_client->cwcacheexhit : 0) * 100 / cachesum); + + if(!apicall) + { +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "CACHEEXAIOPAGE"); +#else + return tpl_getTpl(vars, "CACHEEXPAGE"); +#endif + } + else + { + return tpl_getTpl(vars, "JSONCACHEEX"); + } +} +#endif + +#ifdef WEBIF_WIKI +static char *send_oscam_wiki(struct templatevars *vars, struct uriparams *params) +{ + const char *config = getParam(params, "config"); + const char *section = getParam(params, "section"); + const char *param = getParam(params, "param"); + + if(!config || !param || !config[0] || !param[0]) + { + return tpl_getTpl(vars, "WIKIERROR"); + } + + const char *text = wiki_get_help(config, section, param); + + if(text) + { + tpl_addVar(vars, TPLADD, "WIKIPARAM", param); + tpl_addVar(vars, TPLADD, "WIKICONFIG", config); + tpl_addVar(vars, TPLADD, "WIKITEXT", json_encode(vars, text)); + return tpl_getTpl(vars, "WIKIJSON"); + } + else + { + tpl_addVar(vars, TPLADD, "WIKIPARAM", param); + tpl_addVar(vars, TPLADD, "WIKICONFIG", config); + return tpl_getTpl(vars, "WIKINOTFOUND"); + } +} + +static char *send_oscam_wiki_status(struct templatevars *vars, struct uriparams *params) +{ + const char *config = getParam(params, "config"); + + if(!config || !config[0]) + { + return tpl_getTpl(vars, "WIKIERROR"); + } + + /* Build JSON object with status for all params in this config */ + char json_buf[8192]; + int pos = 0; + int32_t i, count = wiki_count(); + bool first = true; + + pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, "{"); + +#ifdef COMPRESSED_WIKI + /* For compressed wiki, we iterate through entries but access via decompressed data */ + const struct wiki_entry *entries = wiki_get_entries(); + char *wiki_data = wiki_get_decompressed_data(); + + if(wiki_data) + { + for(i = 0; i < count && pos < (int)sizeof(json_buf) - 100; i++) + { + const char *e_config = wiki_data + entries[i].config_ofs; + const char *e_param = wiki_data + entries[i].param_ofs; + + if(strcmp(e_config, config) != 0) + { continue; } + + /* Section filter is optional - include all params from this config */ + if(!first) + { pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, ","); } + first = false; + + const char *status_str = "ok"; + if(entries[i].status == 1) status_str = "review"; + else if(entries[i].status == 2) status_str = "missing"; + + pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, "\"%s\":\"%s\"", + e_param, status_str); + } + } +#else + const struct wiki_entry *entries = wiki_get_entries(); + for(i = 0; i < count && pos < (int)sizeof(json_buf) - 100; i++) + { + if(strcmp(entries[i].config, config) != 0) + { continue; } + + /* Section filter is optional - include all params from this config */ + if(!first) + { pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, ","); } + first = false; + + const char *status_str = "ok"; + if(entries[i].status == 1) status_str = "review"; + else if(entries[i].status == 2) status_str = "missing"; + + pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, "\"%s\":\"%s\"", + entries[i].param, status_str); + } +#endif + + pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, "}"); + + tpl_addVar(vars, TPLADD, "WIKISTATUS", json_buf); + return tpl_getTpl(vars, "WIKISTATUSJSON"); +} +#endif + +static char *send_oscam_api(struct templatevars * vars, FILE * f, struct uriparams * params, int8_t *keepalive, int8_t apicall, char *extraheader) +{ + if(strcmp(getParam(params, "part"), "status") == 0) + { + return send_oscam_status(vars, params, apicall); + } + else if(strcmp(getParam(params, "part"), "userstats") == 0) + { + return send_oscam_user_config(vars, params, apicall); + } + else if(strcmp(getParam(params, "part"), "failban") == 0) + { + return send_oscam_failban(vars, params, apicall); + } +#ifdef CS_CACHEEX + else if(strcmp(getParam(params, "part"), "cacheex") == 0) + { + return send_oscam_cacheex(vars, params, apicall); + } +#endif + else if(strcmp(getParam(params, "part"), "files") == 0) + { + return send_oscam_files(vars, params, apicall); + } + else if(strcmp(getParam(params, "part"), "readerlist") == 0) + { + return send_oscam_reader(vars, params, apicall); + } + else if(strcmp(getParam(params, "part"), "serverconfig") == 0) + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "serverconfig not yet avail"); + return tpl_getTpl(vars, "APIERROR"); + } + else if(strcmp(getParam(params, "part"), "userconfig") == 0) + { + if(((strcmp(getParam(params, "action"), "Save") == 0) || + (strcmp(getParam(params, "action"), "Save As") == 0)) && cfg.http_readonly == 1) + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "API is in readonly mode"); + return tpl_getTpl(vars, "APIERROR"); + } + else + { + struct s_auth *account = get_account_by_name(getParam(params, "user")); + if(!account && strcmp(getParam(params, "action"), "Save")) + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "user not exist"); + return tpl_getTpl(vars, "APIERROR"); + } + else + { + return send_oscam_user_config_edit(vars, params, apicall); + } + } + } + else if(strcmp(getParam(params, "part"), "entitlement") == 0) + { + + if(strcmp(getParam(params, "label"), "")) + { + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + if(rdr->enable == 1) + { + return send_oscam_entitlement(vars, params, apicall); + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no cccam reader or disabled"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "reader not exist"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no reader selected"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else if(strcmp(getParam(params, "part"), "ecmhistory") == 0) + { + int32_t isec; + int32_t shown; + time_t now = time((time_t *)0); + const char *usr; + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->wihidden != 1) + { + isec = now - cl->lastecm; + usr = username(cl); + shown = 0; + if(strcmp(getParam(params, "label"), "") == 0) + { + if(strcmp(getParam(params, "type"), "servers") == 0) + { + if(cl->typ == 'p' || cl->typ == 'r') + { shown = 1; } + } + else if(strcmp(getParam(params, "type"), "users") == 0) + { + if(cl->typ == 'c') + { shown = 1; } + } + else + { + shown = 1; + } + } + else if(strcmp(getParam(params, "label"), usr) == 0) + { + shown = 1; + } + if(shown == 1) + { + tpl_printf(vars, TPLADD, "CLIENTTYPE", "%c", cl->typ); + tpl_addVar(vars, TPLADD, "CLIENTUSER", xml_encode(vars, usr)); + if(cl->typ == 'c' || cl->typ == 'm') + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", xml_encode(vars, cl->account->description ? cl->account->description : "")); + } + else if(cl->typ == 'p' || cl->typ == 'r') + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", xml_encode(vars, cl->reader->description ? cl->reader->description : "")); + } + tpl_printf(vars, TPLADD, "CLIENTLASTRESPONSETIME", "%d", cl->cwlastresptime ? cl->cwlastresptime : -1); + tpl_printf(vars, TPLADD, "CLIENTIDLESECS", "%d", isec); + + //load historical values from ringbuffer + char *value = get_ecm_fullhistorystring(cl); + tpl_addVar(vars, TPLADD, "CLIENTLASTRESPONSETIMEHIST", value); + free_mk_t(value); + + tpl_addVar(vars, TPLAPPEND, "APISTATUSBITS", tpl_getTpl(vars, "APISTATUSBIT")); + } + } + } + return tpl_getTpl(vars, "APISTATUS"); + } + else if(strcmp(getParam(params, "part"), "readerstats") == 0) + { + if(strcmp(getParam(params, "label"), "")) + { + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + return send_oscam_reader_stats(vars, params, apicall); + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "reader not exist"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no reader selected"); + return tpl_getTpl(vars, "APIERROR"); + } + } +#ifdef READER_VIDEOGUARD + else if(strcmp(getParam(params, "part"), "sendcmd") == 0) + { + if(strcmp(getParam(params, "label"), "")) + { + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + char api_msg[150]; + CMD_PACKET *cmd_pack = NULL; + if(!cs_malloc(&cmd_pack, sizeof(CMD_PACKET))) + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "cs_malloc failed!"); + return tpl_getTpl(vars, "APIERROR"); + } + struct s_client *webif_client = cur_client(); + webif_client->grp = 0xFF; // access all readers + + memset(cmd_pack, '0', sizeof(CMD_PACKET)); + cmd_pack->client = webif_client; + cmd_pack->cmdlen = strlen(getParam(params, "cmd")) / 2; + + if(cmd_pack->cmdlen > 0 && (unsigned long)abs(cmd_pack->cmdlen) <= sizeof(cmd_pack->cmd)) + { + if(key_atob_l(getParam(params, "cmd"), cmd_pack->cmd, cmd_pack->cmdlen*2)) + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "'cmd' has not been sent due to wrong value!"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + if(cmd_pack->cmdlen) + { + snprintf(api_msg, sizeof(api_msg), "Command would exceed %lu bytes!", (long unsigned int)sizeof(cmd_pack->cmd)); + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", api_msg); + } + else + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "Missing parameter 'cmd'!"); + } + return tpl_getTpl(vars, "APIERROR"); + } + + struct s_client *cl = rdr->client; + if(rdr->enable == 1 && cl && cl->typ == 'r') + { + add_job(cl, ACTION_READER_SENDCMD, cmd_pack, sizeof(CMD_PACKET)); + tpl_addVar(vars, TPLADD, "APICONFIRMMESSAGE", "command sent"); + return tpl_getTpl(vars, "APICONFIRMATION"); + } + else + { + snprintf(api_msg, sizeof(api_msg), "Reader '%s' is not suitable!", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", api_msg); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no such reader"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no reader selected"); + return tpl_getTpl(vars, "APIERROR"); + } + } +#endif + else if(strcmp(getParam(params, "part"), "shutdown") == 0) + { + if((strcmp(strtolower(getParam(params, "action")), "restart") == 0) || + (strcmp(strtolower(getParam(params, "action")), "shutdown") == 0)) + { + if(!cfg.http_readonly) + { + return send_oscam_shutdown(vars, f, params, apicall, keepalive, extraheader); + } + else + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "webif readonly mode"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "missing parameter action"); + return tpl_getTpl(vars, "APIERROR"); + } + + } + else + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "part not found"); + return tpl_getTpl(vars, "APIERROR"); + } +} + +static char *send_oscam_image(struct templatevars * vars, FILE * f, struct uriparams * params, char *image, time_t modifiedheader, uint32_t etagheader, char *extraheader) +{ + char *wanted; + if(image == NULL) { wanted = getParam(params, "i"); } + else { wanted = image; } + if(cs_strlen(wanted) > 3 && wanted[0] == 'I' && wanted[1] == 'C') + { + if(etagheader == 0) + { + int8_t disktpl = 0; + char *tpl_path; + tpl_path = cfg.http_piconpath? cfg.http_piconpath : cfg.http_tpl; + + if(tpl_path) + { + char path[255]; + if(cs_strlen(tpl_getTplPath(wanted, tpl_path, path, 255)) > 0 && file_exists(path)) + { + struct stat st; + disktpl = 1; + stat(path, &st); + if((time_t)st.st_mtime < modifiedheader) + { + send_header304(f, extraheader); + return "1"; + } + } + } + if(disktpl == 0 && first_client->login < modifiedheader) + { + send_header304(f, extraheader); + return "1"; + } + } + char *header = strstr(tpl_getTpl(vars, wanted), "data:"); + if(header != NULL) + { + char *ptr = header + 5; + while(ptr[0] != ';' && ptr[0] != '\0') { ++ptr; } + if(ptr[0] != '\0' && ptr[1] != '\0') { ptr[0] = '\0'; } + else { return "0"; } + ptr = strstr(ptr + 1, "base64,"); + if(ptr != NULL) + { + int32_t len = b64decode((uint8_t *)ptr + 7); + if(len > 0) + { + if((uint32_t)crc32(0L, (uint8_t *)ptr + 7, len) == etagheader) + { + send_header304(f, extraheader); + } + else + { + send_headers(f, 200, "OK", extraheader, header + 5, 1, len, ptr + 7, 0); + webif_write_raw(ptr + 7, f, len); + } + return "1"; + } + } + } + } + // Return file not found + const char *not_found = "File not found.\n"; + send_headers(f, 404, "Not Found", extraheader, "text/plain", 0, cs_strlen(not_found), (char *)not_found, 0); + webif_write_raw((char *)not_found, f, cs_strlen(not_found)); + return "1"; +} + +static char *send_oscam_robots_txt(FILE * f) +{ + const char *content = "User-agent: *\nDisallow: /\n"; + send_headers(f, 200, "OK", NULL, "text/plain", 0, cs_strlen(content), (char *)content, 0); + webif_write_raw((char *)content, f, cs_strlen(content)); + return "1"; +} + +static char *send_oscam_graph(struct templatevars * vars) +{ + return tpl_getTpl(vars, "GRAPH"); +} + +#ifdef MODULE_GHTTP +static bool ghttp_autoconf(struct templatevars * vars, struct uriparams * params) +{ + int8_t i = 0; + struct s_reader *rdr; + char *name = getParam(params, "gacname"); + if(cs_strlen(name) < 3) + { + tpl_addMsg(vars, "Invalid host name!"); + return false; + } + + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + if(rdr->ph.num == R_GHTTP) { i++; } // count existing ghttp readers + + while(i < 3) // if less than 3, add more + { + char lbl[128]; + snprintf(lbl, sizeof(lbl), "%s%d", "ghttp", i + 1); + cs_log("GHttp autoconf: adding reader %s", lbl); + struct s_reader *newrdr; + if(!cs_malloc(&newrdr, sizeof(struct s_reader))) + { + tpl_addMsg(vars, "Create reader failed!"); + return false; + }; + newrdr->typ = R_GHTTP; + cs_strncpy(newrdr->label, lbl, sizeof(newrdr->label)); + module_reader_set(newrdr); + reader_set_defaults(newrdr); + newrdr->enable = 0; + newrdr->grp = 1; + ll_append(configured_readers, newrdr); + i++; + } + + uint16_t port = 0; + char *str = strstr(name, ":"); + if(str) + { + port = atoi(str + 1); + str[0] = '\0'; + } + + i = 0; + itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->ph.num == R_GHTTP) + { + if(i > 2) // remove superflous + { + cs_log("GHttp autoconf: removing reader %s", rdr->label); + inactivate_reader(rdr); + ll_iter_remove(&itr); + free_reader(rdr); + } + else // reconfigure the 3 first ghttp readers + { + cs_log("GHttp autoconf: reconfiguring reader %s", rdr->label); + snprintf(rdr->label, sizeof(rdr->label), "%s%d", "ghttp", i + 1); + rdr->r_port = port; + rdr->enable = 1; + rdr->ghttp_use_ssl = 0; +#ifdef WITH_SSL + rdr->ghttp_use_ssl = 1; +#endif + if(rdr->grp < 1) { rdr->grp = 1; } + cs_strncpy(rdr->r_usr, getParam(params, "gacuser"), sizeof(rdr->r_usr)); + cs_strncpy(rdr->r_pwd, getParam(params, "gacpasswd"), sizeof(rdr->r_pwd)); + if(i == 0) { cs_strncpy(rdr->device, name, sizeof(rdr->device)); } + else + { + if(!strstr(name, ".")) + { snprintf(rdr->device, sizeof(rdr->device), "%s%d", name, i); } // name, name1, name2 + else { cs_strncpy(rdr->device, name, sizeof(rdr->device)); } + // . in the name = assume full hostname = use same for all 3 readers + } + if(i == 2) { rdr->fallback = 1; } + else { rdr->fallback = 0; } + i++; + } + } + } + cs_log("GHttp autoconf: Saving %d readers", i); + if(write_server() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->ph.num == R_GHTTP) + { restart_cardreader(rdr, 1); } + } + return true; +} + +static char *send_oscam_ghttp(struct templatevars * vars, struct uriparams * params, int8_t apicall) +{ + if(strcmp(strtolower(getParam(params, "action")), "autoconf") == 0) + { + if(!apicall) + { + bool missing = false; + if(cs_strlen(getParam(params, "gacuser")) == 0) + { + tpl_addVar(vars, TPLADD, "USERREQ", "(Required)"); + missing = true; + } + else { tpl_addVar(vars, TPLADD, "GACUSER", getParam(params, "gacuser")); } + if(cs_strlen(getParam(params, "gacpasswd")) == 0) + { + tpl_addVar(vars, TPLADD, "PWDREQ", "(Required)"); + missing = true; + } + else { tpl_addVar(vars, TPLADD, "GACPASSWD", getParam(params, "gacpasswd")); } + if(cs_strlen(getParam(params, "gacname")) == 0) + { + tpl_addVar(vars, TPLADD, "NAMEREQ", "(Required)"); + missing = true; + } + else { tpl_addVar(vars, TPLADD, "GACNAME", getParam(params, "gacname")); } + if(missing) { return tpl_getTpl(vars, "PREAUTOCONF"); } + cs_log("GHttp autoconf requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + } + else + { + tpl_addVar(vars, TPLADD, "APICONFIRMMESSAGE", "autoconf"); + cs_log("GHttp autoconf requested by XMLApi from %s", cs_inet_ntoa(GET_IP())); + } + + if(ghttp_autoconf(vars, params)) + { + tpl_printf(vars, TPLADD, "REFRESHTIME", "%d", 3); + tpl_addVar(vars, TPLADD, "REFRESH", tpl_getTpl(vars, "REFRESH")); + tpl_printf(vars, TPLADD, "SECONDS", "%d", 3); + if(apicall) { return tpl_getTpl(vars, "APICONFIRMATION"); } + else { return tpl_getTpl(vars, "AUTOCONF"); } + } + else { return tpl_getTpl(vars, "PREAUTOCONF"); } // something failed + + } + else + { + if(cs_strlen(getParam(params, "token")) > 0) // parse autoconf token + { + char *token = getParam(params, "token"); + int32_t len = b64decode((uint8_t *)token); + if(len > 0) + { + struct uriparams tokenprms; + tokenprms.paramcount = 0; + parseParams(&tokenprms, token); + if(cs_strlen(getParam(&tokenprms, "u")) > 0) + { + tpl_addVar(vars, TPLADD, "GACUSER", getParam(&tokenprms, "u")); + tpl_addVar(vars, TPLADD, "USERRDONLY", "readonly"); + } + if(cs_strlen(getParam(&tokenprms, "p")) > 0) + { + tpl_addVar(vars, TPLADD, "GACPASSWD", getParam(&tokenprms, "p")); + tpl_addVar(vars, TPLADD, "PWDRDONLY", "readonly"); + } + if(cs_strlen(getParam(&tokenprms, "n")) > 0) + { + tpl_addVar(vars, TPLADD, "GACNAME", getParam(&tokenprms, "n")); + tpl_addVar(vars, TPLADD, "NAMERDONLY", "readonly"); + } + } + } + return tpl_getTpl(vars, "PREAUTOCONF"); + } +} +#endif + +static int8_t check_httpip(IN_ADDR_T addr) +{ + int8_t i = 0; + // check all previously dyndns resolved addresses + for(i = 0; i < MAX_HTTP_DYNDNS; i++) + { + if(IP_ISSET(cfg.http_dynip[i]) && IP_EQUAL(cfg.http_dynip[i], addr)) + { return 1; } + } + return 0; +} + +static int8_t check_httpdyndns(IN_ADDR_T addr) +{ + + // check all previously dyndns resolved addresses + if(check_httpip(addr)) + { return 1; } + + // we are not ok, so resolve all dyndns entries into IP's - maybe outdated IP's + + if(cfg.http_dyndns[0][0]) + { + int8_t i = 0; + for(i = 0; i < MAX_HTTP_DYNDNS; i++) + { + if(cfg.http_dyndns[i][0]) + { + cs_resolve((const char *)cfg.http_dyndns[i], &cfg.http_dynip[i], NULL, NULL); + cs_log_dbg(D_TRACE, "WebIf: httpdyndns [%d] resolved %s to %s ", i, (char *)cfg.http_dyndns[i], cs_inet_ntoa(cfg.http_dynip[i])); + } + } + } + else + { + cs_log_dbg(D_TRACE, "WebIf: No dyndns addresses found"); + return 0; + } + + // again check all dyndns resolved addresses + if(check_httpip(addr)) + { return 1; } + + return 0; +} + +static int8_t check_valid_origin(IN_ADDR_T addr) +{ + + // check whether requesting IP is in allowed IP ranges + if(check_ip(cfg.http_allowed, addr)) + { return 1; } + + // we havn't found the requesting IP in allowed range. So we check for allowed httpdyndns as last chance + if(cfg.http_dyndns[0][0]) + { + int8_t ok; + ok = check_httpdyndns(addr); + return ok; + } + return 0; +} + +static int8_t check_request(char *result, int32_t readen) +{ + if(readen < 50) { return 0; } + result[readen] = '\0'; + int8_t method; + if(strncmp(result, "POST", 4) == 0) { method = 1; } + else { method = 0; } + char *headerEnd = strstr(result, "\r\n\r\n"); + if(headerEnd == NULL) { return 0; } + else if(method == 0) { return 1; } + else + { + char *ptr = strstr(result, "Content-Length: "); + if(ptr != NULL) + { + ptr += 16; + if(ptr < result + readen) + { + uint32_t length = atoi(ptr); + if(cs_strlen(headerEnd + 4) >= length) { return 1; } + } + } + } + return 0; +} + +static int32_t readRequest(FILE * f, IN_ADDR_T in, char **result, int8_t forcePlain) +{ + int32_t n, bufsize = 0, errcount = 0; + char buf2[1024]; + struct pollfd pfd2[1]; +#ifdef WITH_SSL + int8_t is_ssl = 0; + if(ssl_active && !forcePlain) + { is_ssl = 1; } +#endif + + while(1) + { + errno = 0; + if(forcePlain) + { n = read(fileno(f), buf2, sizeof(buf2)); } + else + { n = webif_read(buf2, sizeof(buf2), f); } + if(n <= 0) + { + if((errno == 0 || errno == EINTR)) + { + if(errcount++ < 10) + { + cs_sleepms(5); + continue; + } + else { return -1; } + } +#ifdef WITH_SSL + if(is_ssl) + { + if(errno != ECONNRESET) + { + int32_t errcode = ERR_peek_error(); + char errstring[128]; + ERR_error_string_n(errcode, errstring, sizeof(errstring) - 1); + cs_log_dbg(D_TRACE, "WebIf: read error ret=%d (%d%s%s)", n, SSL_get_error(cur_ssl(), n), errcode ? " " : "", errcode ? errstring : ""); + } + return -1; + } +#else + if(errno != ECONNRESET) + { cs_log_dbg(D_TRACE, "WebIf: read error ret=%d (errno=%d %s)", n, errno, strerror(errno)); } +#endif + return -1; + } + if(!cs_realloc(result, bufsize + n + 1)) + { + send_error500(f); + NULLFREE(*result); + return -1; + } + + memcpy(*result + bufsize, buf2, n); + bufsize += n; + +#ifdef WITH_EMU + if(bufsize > 204800) // max request size 200kb +#else + if(bufsize > 102400) // max request size 100kb +#endif + { + cs_log("error: too much data received from %s", cs_inet_ntoa(in)); + NULLFREE(*result); + *result = NULL; + return -1; + } + +#ifdef WITH_SSL + if(ssl_active && !forcePlain) + { + int32_t len = 0; + len = SSL_pending((SSL *)f); + + if(len > 0) + { continue; } + + pfd2[0].fd = SSL_get_fd((SSL *)f); + + } + else +#endif + pfd2[0].fd = fileno(f); + + pfd2[0].events = (POLLIN | POLLPRI); + + int32_t rc = poll(pfd2, 1, 100); + if(rc > 0 || !check_request(*result, bufsize)) + { continue; } + else + { break; } + } + return bufsize; +} +static int32_t process_request(FILE * f, IN_ADDR_T in) +{ + int32_t ok = 0; + int8_t *keepalive = (int8_t *)pthread_getspecific(getkeepalive); + IN_ADDR_T addr = GET_IP(); + + do + { +#ifdef WITH_SSL + if(!ssl_active && *keepalive) { fflush(f); } +#else + if(*keepalive) { fflush(f); } +#endif + + // at this point we do all checks related origin IP, ranges and dyndns stuff + ok = check_valid_origin(addr); + cs_log_dbg(D_TRACE, "WebIf: Origin checked. Result: access from %s => %s", cs_inet_ntoa(addr), (!ok) ? "forbidden" : "allowed"); + + // based on the failed origin checks we send a 403 to calling browser + if(!ok) + { + send_error(f, 403, "Forbidden", NULL, "Access denied.", 0); + cs_log("unauthorized access from %s - invalid ip or dyndns", cs_inet_ntoa(addr)); + return 0; + } + int32_t authok = 0; + char expectednonce[(MD5_DIGEST_LENGTH * 2) + 1], opaque[(MD5_DIGEST_LENGTH * 2) + 1]; + char authheadertmp[sizeof(AUTHREALM) + sizeof(expectednonce) + sizeof(opaque) + 100]; + + char *method, *path, *protocol, *str1, *saveptr1 = NULL, *authheader = NULL, *extraheader = NULL, *filebuf = NULL; + char *pch, *tmp, *buf, *nameInUrl, subdir[32]; + /* List of possible pages */ + char *pages[] = + { + "/config.html", + "/readers.html", + "/entitlements.html", + "/status.html", + "/userconfig.html", + "/readerconfig.html", + "/services.html", + "/user_edit.html", + "/site.css", + "/services_edit.html", + "/savetemplates.html", + "/shutdown.html", + "/script.html", + "/scanusb.html", + "/files.html", + "/readerstats.html", + "/failban.html", + "/oscam.js", + "/oscamapi.html", + "/image", + "/favicon.ico", + "/graph.svg", + "/oscamapi.xml", + "/cacheex.html", + "/oscamapi.json", + "/emm.html", + "/emm_running.html", + "/robots.txt", + "/ghttp.html", + "/logpoll.html", + "/jquery.js", +#ifdef WEBIF_WIKI + "/wiki.json", + "/wiki_status.json", +#endif + }; + + int32_t pagescnt = sizeof(pages) / sizeof(char *); // Calculate the amount of items in array + int32_t i, bufsize, len, pgidx = -1; + uint32_t etagheader = 0; + struct uriparams params; + params.paramcount = 0; + time_t modifiedheader = 0; + + bufsize = readRequest(f, in, &filebuf, 0); + + if(!filebuf || bufsize < 1) + { + if(!*keepalive) { cs_log_dbg(D_CLIENT, "WebIf: No data received from client %s. Closing connection.", cs_inet_ntoa(addr)); } + return -1; + } + + buf = filebuf; + + if((method = strtok_r(buf, " ", &saveptr1)) != NULL) + { + if((path = strtok_r(NULL, " ", &saveptr1)) != NULL) + { + if((protocol = strtok_r(NULL, "\r", &saveptr1)) == NULL) + { + NULLFREE(filebuf); + return -1; + } + } + else + { + NULLFREE(filebuf); + return -1; + } + } + else + { + NULLFREE(filebuf); + return -1; + } + tmp = protocol + cs_strlen(protocol) + 2; + + pch = path; + /* advance pointer to beginning of query string */ + while(pch[0] != '?' && pch[0] != '\0') { ++pch; } + if(pch[0] == '?') + { + pch[0] = '\0'; + ++pch; + } + + nameInUrl = pch - 1; + while(nameInUrl != path && nameInUrl[0] != '/') { --nameInUrl; } + + /* allow only alphanumeric sub-folders */ + int32_t subdirLen = nameInUrl - path; + subdir[0] = '\0'; + if(subdirLen > 0 && subdirLen < 32) + { + cs_strncpy(subdir, path + 1, subdirLen); + + int32_t invalidSubdir = 0; + for(i = 0; i < subdirLen - 1; i++) + { + if(!((subdir[i] >= '0' && subdir[i] <= '9') + || (subdir[i] >= 'a' && subdir[i] <= 'z') + || (subdir[i] >= 'A' && subdir[i] <= 'Z'))) + { + + invalidSubdir = 1; + subdir[0] = '\0'; + break; + } + } + + if(!invalidSubdir) + { + subdir[subdirLen] = '\0'; +#ifdef WIN32 + subdir[subdirLen - 1] = '\\'; +#else + subdir[subdirLen - 1] = '/'; +#endif + } + } + + /* Map page to our static page definitions */ + for(i = 0; i < pagescnt; i++) + { + if(!strcmp(nameInUrl, pages[i])) { pgidx = i; } + } + + parseParams(¶ms, pch); + + if(!cfg.http_user || !cfg.http_pwd) + { authok = 1; } + + for(str1 = strtok_r(tmp, "\n", &saveptr1); str1; str1 = strtok_r(NULL, "\n", &saveptr1)) + { + len = cs_strlen(str1); + if(str1[len - 1] == '\r') + { + str1[len - 1] = '\0'; + --len; + } + if(len == 0) + { + if(strcmp(method, "POST") == 0) + { + parseParams(¶ms, str1 + 2); + } + break; + } + if(!authok && len > 50 && strncasecmp(str1, "Authorization:", 14) == 0 && strstr(str1, "Digest") != NULL) + { + if(cs_dblevel & D_CLIENT) + { + if(cs_realloc(&authheader, len + 1)) + { cs_strncpy(authheader, str1, len); } + } + authok = check_auth(str1, method, path, addr, expectednonce, opaque); + } + else if(len > 40 && strncasecmp(str1, "If-Modified-Since:", 18) == 0) + { + modifiedheader = parse_modifiedsince(str1); + } + else if(len > 20 && strncasecmp(str1, "If-None-Match:", 14) == 0) + { + for(pch = str1 + 14; pch[0] != '"' && pch[0] != '\0'; ++pch) { ; } + if(cs_strlen(pch) > 5) { etagheader = (uint32_t)strtoul(++pch, NULL, 10); } + } + else if(len > 12 && strncasecmp(str1, "Connection: Keep-Alive", 22) == 0 && strcmp(method, "POST")) + { + *keepalive = 1; + } + } + + if(cfg.http_user && cfg.http_pwd) + { + if (!authok || cs_strlen(opaque) != MD5_DIGEST_LENGTH * 2) + { + calculate_opaque(addr, opaque); + } + + if (authok != 2) + { + if(!authok) + { + if(authheader) + { + cs_log_dbg(D_CLIENT, "WebIf: Received wrong auth header from %s:", cs_inet_ntoa(addr)); + cs_log_dbg(D_CLIENT, "%s", authheader); + } + else + { cs_log_dbg(D_CLIENT, "WebIf: Received no auth header from %s.", cs_inet_ntoa(addr)); } + } + calculate_nonce(NULL, expectednonce, opaque); + } + + if (authok != 1) + { + snprintf(authheadertmp, sizeof(authheadertmp), "WWW-Authenticate: Digest algorithm=\"MD5\", realm=\"%s\", qop=\"auth\", opaque=\"%s\", nonce=\"%s\"", AUTHREALM, opaque, expectednonce); + if (authok == 2) + { + if (!cs_strncat(authheadertmp, ", stale=true", sizeof(authheadertmp))) { + cs_log("WARNING, bug here!"); + } + } + } + else + { + snprintf(authheadertmp, sizeof(authheadertmp), "Authentication-Info: nextnonce=\"%s\"", expectednonce); + } + + extraheader = authheadertmp; + + if (authok != 1) + { + char *msg = "Access denied.\n"; + send_headers(f, 401, "Unauthorized", extraheader, "text/html", 0, cs_strlen(msg), msg, 0); + webif_write(msg, f); + NULLFREE(authheader); + NULLFREE(filebuf); + if (*keepalive) { + continue; + } else { + return 0; + } + } + } + else + { + NULLFREE(authheader); + } + + /*build page*/ + if(pgidx == 8) + { + send_file(f, "CSS", subdir, modifiedheader, etagheader, extraheader); + } + else if(pgidx == 17) + { + send_file(f, "JS", subdir, modifiedheader, etagheader, extraheader); + } + else if(pgidx == 30) + { + send_file(f, "JQ", subdir, modifiedheader, etagheader, extraheader); + } + else + { + time_t t; + struct templatevars *vars = tpl_create(); + if(vars == NULL) + { + send_error500(f); + NULLFREE(filebuf); + return 0; + } + + tpl_addVar(vars, TPLADD, "SUBDIR", subdir); + + struct tm lt, st; + time(&t); + + localtime_r(&t, <); + + tpl_addVar(vars, TPLADD, "SCM_URL", SCM_URL); + tpl_addVar(vars, TPLADD, "WIKI_URL", WIKI_URL); + tpl_addVar(vars, TPLADD, "BOARD_URL", BOARD_URL); +#ifdef WEBIF_WIKI + tpl_addVar(vars, TPLADD, "WIKIINTERNALVAR", "\t\tvar wikiInternal = true;"); +#else + tpl_addVar(vars, TPLADD, "WIKIINTERNALVAR", ""); +#endif + tpl_addVar(vars, TPLADD, "CS_VERSION", CS_VERSION); + tpl_addVar(vars, TPLADD, "CS_GIT_COMMIT", CS_GIT_COMMIT); + tpl_addVar(vars, TPLADD, "CS_TARGET", CS_TARGET); + tpl_addVar(vars, TPLADD, "HTTPOSCAMLABEL", xml_encode(vars,cfg.http_oscam_label)); + if (!boxtype_is("generic")) + { + if (!boxname_is("generic")) + tpl_printf(vars, TPLADD, "DETECTED_BOXTYPE", "%s (%s)", boxtype_get(), boxname_get()); + else + tpl_addVar(vars, TPLADD, "DETECTED_BOXTYPE", boxtype_get()); + } + + if(cfg.http_locale){ + float decimal_point = 0.0; + setlocale(LC_ALL, cfg.http_locale); + tpl_printf(vars, TPLADD, "TMP_DECPOINT", "%.1f", decimal_point); + tpl_addVar(vars, TPLADD, "LOCALE_DECPOINT", strstr(tpl_getVar(vars, "TMP_DECPOINT"), ",") ? ",": "."); + } + + tpl_addVar(vars, TPLADD, "HTTP_CHARSET", "UTF-8"); + if(cfg.http_picon_size > 0) + { + tpl_printf(vars, TPLADD, "HTTPPICONSIZEINS", "img.statususericon, img.protoicon, img.usericon, img.readericon {height:%dpx !important;max-height:%dpx !important;}", cfg.http_picon_size, cfg.http_picon_size); + } + if(cfg.poll_refresh > 0) + { + tpl_printf(vars, TPLADD, "POLLREFRESHTIME", "%d", cfg.poll_refresh); + } + if ( cfg.http_refresh > 0 && + ((( pgidx == 1 || pgidx == 4 ) && !cfg.poll_refresh ) || + ( pgidx == 3 && ( cfg.http_status_log || !cfg.poll_refresh )) || + pgidx == 15 || pgidx == 23 || pgidx == -1 )) // wenn polling bei cachex.html eingeführt wird muss die 23 => 2 zeilen höher + { + tpl_printf(vars, TPLADD, "REFRESHTIME", "%d", cfg.http_refresh); + tpl_addVar(vars, TPLADD, "WITHQUERY", pgidx == 15 ? "1" : "0"); + tpl_addVar(vars, TPLADD, "REFRESH", tpl_getTpl(vars, "REFRESH")); + } +#ifdef WEBIF_JQUERY + tpl_printf(vars, TPLADD, "SRCJQUERY", "jquery.js?v=%s", CS_VERSION); +#else + tpl_addVar(vars, TPLADD, "SRCJQUERY", cfg.http_extern_jquery); +#endif + + if(picon_exists("LOGO")||cs_strlen(tpl_getTpl(vars, "IC_LOGO"))>3) + { + tpl_addVar(vars, TPLADD, "LOGO_INS", tpl_getTpl(vars, "LOGOBITIMG")); + } + else + { + tpl_addVar(vars, TPLADD, "LOGO_INS", tpl_getTpl(vars, "LOGOBITSVG")); + } + tpl_addVar(vars, TPLADD, "LOGO", tpl_getTpl(vars, "LOGOBIT")); + tpl_printf(vars, TPLADD, "CURDATE", "%02d.%02d.%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100); + tpl_printf(vars, TPLADD, "CURTIME", "%02d:%02d:%02d", lt.tm_hour, lt.tm_min, lt.tm_sec); + localtime_r(&first_client->login, &st); + tpl_printf(vars, TPLADD, "STARTDATE", "%02d.%02d.%02d", st.tm_mday, st.tm_mon + 1, st.tm_year % 100); + tpl_printf(vars, TPLADD, "STARTTIME", "%02d:%02d:%02d", st.tm_hour, st.tm_min, st.tm_sec); + tpl_printf(vars, TPLADD, "PROCESSID", "%d", getppid()); + tpl_addVar(vars, TPLADD, "RUNAS", urlencode(vars, username(first_client))); + + time_t now = time((time_t *)0); + // XMLAPI + if(pgidx == 18 || pgidx == 22 || pgidx == 24) + { + char tbuffer [30]; + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", &st); + tpl_addVar(vars, TPLADD, "APISTARTTIME", tbuffer); + tpl_printf(vars, TPLADD, "APIRUNTIME", "%" PRId64, (int64_t)now - first_client->login); + tpl_printf(vars, TPLADD, "APIREADONLY", "%d", cfg.http_readonly); + if(strcmp(getParam(¶ms, "callback"), "")) + { + tpl_printf(vars, TPLADD, "CALLBACK", "%s%s", getParam(¶ms, "callback"), "("); + tpl_addVar(vars, TPLADD, "ENDBRACKET", ")"); + } + + } + + if (config_enabled(WITH_LB)) + tpl_addVar(vars, TPLADD, "LBISDEFINED", "1"); + + // language code in helplink + tpl_addVar(vars, TPLADD, "LANGUAGE", cfg.http_help_lang); + tpl_addVar(vars, TPLADD, "RUNTIME", sec2timeformat(vars, (now - first_client->login))); + time_t uptime = oscam_get_uptime(); + if(uptime > 0){ + tpl_printf(vars, TPLADD, "UPTIMETXT", "%s Up Time: ", strcmp(tpl_getVar(vars, "DETECTED_BOXTYPE"),"")==0 ? "System" : tpl_getVar(vars, "DETECTED_BOXTYPE")); + tpl_addVar(vars, TPLADD, "UPTIME", sec2timeformat(vars, uptime)); + } + tpl_addVar(vars, TPLADD, "CURIP", cs_inet_ntoa(addr)); + if(cfg.http_readonly) + { tpl_addVar(vars, TPLAPPEND, "BTNDISABLED", "DISABLED"); } + + i = ll_count(cfg.v_list); + if(i > 0) { tpl_printf(vars, TPLADD, "FAILBANNOTIFIER", "%d", i); } + tpl_printf(vars, TPLADD, "FAILBANNOTIFIERPOLL", "%d", i); + + char *result = NULL; + + // WebIf allows modifying many things. Thus, all pages except images/css/static are expected to be non-threadsafe! + if(pgidx != 19 && pgidx != 20 && pgidx != 21 && pgidx != 27) { cs_writelock(__func__, &http_lock); } + switch(pgidx) + { + case 0: + tpl_addVar(vars, TPLADD, "CONFIG_CONTENT", send_oscam_config(vars, ¶ms)); + result = tpl_getTpl(vars, "CONFIGCONTENT"); + break; + case 1: + result = send_oscam_reader(vars, ¶ms, 0); + break; + case 2: + result = send_oscam_entitlement(vars, ¶ms, 0); + break; + case 3: + result = send_oscam_status(vars, ¶ms, 0); + break; + case 4: + result = send_oscam_user_config(vars, ¶ms, 0); + break; + case 5: + result = send_oscam_reader_config(vars, ¶ms); + break; + case 6: + result = send_oscam_services(vars, ¶ms); + break; + case 7: + result = send_oscam_user_config_edit(vars, ¶ms, 0); + break; + //case 8: css file + case 9: + result = send_oscam_services_edit(vars, ¶ms); + break; + case 10: + result = send_oscam_savetpls(vars); + break; + case 11: + result = send_oscam_shutdown(vars, f, ¶ms, 0, keepalive, extraheader); + break; + case 12: + result = send_oscam_script(vars, ¶ms); + break; + case 13: + result = send_oscam_scanusb(vars); + break; + case 14: + result = send_oscam_files(vars, ¶ms, 0); + break; + case 15: + result = send_oscam_reader_stats(vars, ¶ms, 0); + break; + case 16: + result = send_oscam_failban(vars, ¶ms, 0); + break; + //case 17: js file + case 18: + result = send_oscam_api(vars, f, ¶ms, keepalive, 1, extraheader); + break; //oscamapi.html + case 19: + result = send_oscam_image(vars, f, ¶ms, NULL, modifiedheader, etagheader, extraheader); + break; + case 20: + result = send_oscam_image(vars, f, ¶ms, "ICMAI", modifiedheader, etagheader, extraheader); + break; + case 21: + result = send_oscam_graph(vars); + break; + case 22: + result = send_oscam_api(vars, f, ¶ms, keepalive, 1, extraheader); + break; //oscamapi.xml +#ifdef CS_CACHEEX + case 23: + result = send_oscam_cacheex(vars, ¶ms, 0); + break; +#endif + case 24: + result = send_oscam_api(vars, f, ¶ms, keepalive, 2, extraheader); + break; //oscamapi.json + case 25: + result = send_oscam_EMM(vars, ¶ms); + break; //emm.html + case 26: + result = send_oscam_EMM_running(vars, ¶ms); + break; //emm_running.html + case 27: + result = send_oscam_robots_txt(f); + break; //robots.txt +#ifdef MODULE_GHTTP + case 28: + result = send_oscam_ghttp(vars, ¶ms, 0); + break; +#endif +#ifdef WEBIF_LIVELOG + case 29: + result = send_oscam_logpoll(vars, ¶ms); + break; + //case 30: jquery.js +#endif +#ifdef WEBIF_WIKI + case 31: + result = send_oscam_wiki(vars, ¶ms); + break; + case 32: + result = send_oscam_wiki_status(vars, ¶ms); + break; +#endif + default: + result = send_oscam_status(vars, ¶ms, 0); + break; + } + if(pgidx != 19 && pgidx != 20 && pgidx != 21 && pgidx != 27) { cs_writeunlock(__func__, &http_lock); } + + if(result == NULL || !strcmp(result, "0") || cs_strlen(result) == 0) { send_error500(f); } + else if(strcmp(result, "1")) + { + //it doesn't make sense to check for modified etagheader here as standard template has timestamp in output and so site changes on every request + if(pgidx == 18) + { send_headers(f, 200, "OK", extraheader, "text/xml", 0, cs_strlen(result), NULL, 0); } + else if(pgidx == 21) + { send_headers(f, 200, "OK", extraheader, "image/svg+xml", 0, cs_strlen(result), NULL, 0); } + else if(pgidx == 24) + { send_headers(f, 200, "OK", extraheader, "text/javascript", 0, cs_strlen(result), NULL, 0); } +#ifdef WEBIF_WIKI + else if(pgidx == 31) + { send_headers(f, 200, "OK", extraheader, "application/json", 0, cs_strlen(result), NULL, 0); } +#endif + else + { send_headers(f, 200, "OK", extraheader, "text/html", 0, cs_strlen(result), NULL, 0); } + webif_write(result, f); + } + tpl_clear(vars); + } + NULLFREE(filebuf); + } + while(*keepalive == 1 && !exit_oscam); + return 0; +} + +static void *serve_process(void *conn) +{ + struct s_connection *myconn = (struct s_connection *)conn; + int32_t s = myconn->socket; + struct s_client *cl = myconn->cl; + IN_ADDR_T in; + IP_ASSIGN(in, myconn->remote); + + set_thread_name(__func__); + +#ifdef WITH_SSL + SSL *ssl = myconn->ssl; + SAFE_SETSPECIFIC(getssl, ssl); +#endif + NULLFREE(myconn); + + SAFE_SETSPECIFIC(getip, &in); + SAFE_SETSPECIFIC(getclient, cl); + + int8_t keepalive = 0; + SAFE_SETSPECIFIC(getkeepalive, &keepalive); + +#ifdef WITH_SSL + if(ssl_active) + { + if(SSL_set_fd(ssl, s)) + { + int32_t ok = (SSL_accept(ssl) != -1); + if(!ok) + { + int8_t tries = 100; + while(!ok && tries--) + { + int32_t err = SSL_get_error(ssl, -1); + if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) + { break; } + else + { + struct pollfd pfd; + pfd.fd = s; + pfd.events = POLLIN | POLLPRI; + int32_t rc = poll(&pfd, 1, -1); + if(rc < 0) + { + if(errno == EINTR || errno == EAGAIN) { continue; } + break; + } + if(rc == 1) + { ok = (SSL_accept(ssl) != -1); } + } + } + } + if(ok) + { + process_request((FILE *)ssl, in); + } + else + { + FILE *f; + f = fdopen(s, "r+"); + if(f != NULL) + { + char *ptr, *filebuf = NULL, *host = NULL; + int32_t bufsize = readRequest(f, in, &filebuf, 1); + + if(filebuf) + { + filebuf[bufsize] = '\0'; + host = strstr(filebuf, "Host: "); + if(host) + { + host += 6; + ptr = strchr(host, '\r'); + if(ptr) { ptr[0] = '\0'; } + } + } + if(host) + { + char extra[cs_strlen(host) + 20]; + snprintf(extra, sizeof(extra), "Location: https://%s", host); + send_error(f, 301, "Moved Permanently", extra, "This web server is running in SSL mode.", 1); + } + else + { send_error(f, 200, "Bad Request", NULL, "This web server is running in SSL mode.", 1); } + fflush(f); + fclose(f); + NULLFREE(filebuf); + } + else + { + cs_log_dbg(D_TRACE, "WebIf: fdopen(%d) failed. (errno=%d %s)", s, errno, strerror(errno)); + } + } + } + else { cs_log("WebIf: Error calling SSL_set_fd()."); } + SSL_shutdown(ssl); + close(s); + SSL_free(ssl); + } + else +#endif + { + FILE *f; + f = fdopen(s, "r+"); + if(f != NULL) + { + process_request(f, in); + fflush(f); + fclose(f); + } + else + { + cs_log_dbg(D_TRACE, "WebIf: fdopen(%d) failed. (errno=%d %s)", s, errno, strerror(errno)); + } + shutdown(s, SHUT_WR); + close(s); + } + + return NULL; +} + +/* Creates a random string with specified length. Note that dst must be one larger than size to hold the trailing \0*/ +static void create_rand_str(char *dst, int32_t size) +{ + int32_t i; + for(i = 0; i < size; ++i) + { + dst[i] = (rand() % 94) + 32; + } + dst[i] = '\0'; +} + +static void *http_server(void *UNUSED(d)) +{ + struct s_client *cl = create_client(first_client->ip); + if(cl == NULL) { return NULL; } + SAFE_SETSPECIFIC(getclient, cl); + cl->typ = 'h'; + int32_t s, reuse = 1; + struct s_connection *conn; + + set_thread_name(__func__); + + /* Create random string for nonce value generation */ + create_rand_str(noncekey, 32); + + /* Prepare base64 decoding array */ + b64prepare(); + webif_tpls_prepare(); +#ifdef WEBIF_WIKI + webif_wiki_prepare(); +#endif + + tpl_checkDiskRevisions(); + + cs_lock_create(__func__, &http_lock, "http_lock", 10000); + init_noncelocks(); + + memset(&p_stat_cur, 0x0, sizeof(p_stat_cur)); + + if(pthread_key_create(&getip, NULL)) + { + cs_log("Could not create getip"); + return NULL; + } + if(pthread_key_create(&getkeepalive, NULL)) + { + cs_log("Could not create getkeepalive"); + return NULL; + } + + struct SOCKADDR sin; + socklen_t len = 0; + memset(&sin, 0, sizeof(sin)); + + bool do_ipv6 = config_enabled(IPV6SUPPORT); +#ifdef IPV6SUPPORT + if(do_ipv6) + { + len = sizeof(struct sockaddr_in6); + if((sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + cs_log("HTTP Server: ERROR: Creating IPv6 socket failed! (errno=%d %s)", errno, strerror(errno)); + cs_log("HTTP Server: Falling back to IPv4."); + do_ipv6 = false; + } + else if (IP_ISSET(cfg.http_srvip) && (IN6_IS_ADDR_V4MAPPED(&cfg.http_srvip) || IN6_IS_ADDR_V4COMPAT(&cfg.http_srvip))) // ipv4 set as http_srvip + { + do_ipv6 = false; + } + else + { + struct sockaddr_in6 *ia = (struct sockaddr_in6 *)&sin; + + if (IP_ISSET(cfg.http_srvip)) + { + IP_ASSIGN(SIN_GET_ADDR(sin), cfg.http_srvip); + } + else if (IP_ISSET(cfg.srvip)) + { + IP_ASSIGN(SIN_GET_ADDR(sin), cfg.srvip); + } + else + { + ia->sin6_addr = in6addr_any; + } + + ia->sin6_family = AF_INET6; + ia->sin6_port = htons(cfg.http_port); + } + } +#endif + if(!do_ipv6) + { + len = sizeof(struct sockaddr_in); + if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + cs_log("HTTP Server: ERROR: Creating socket failed! (errno=%d %s)", errno, strerror(errno)); + return NULL; + } + SIN_GET_FAMILY(sin) = AF_INET; + if(IP_ISSET(cfg.http_srvip)) + { IP_ASSIGN(SIN_GET_ADDR(sin), cfg.http_srvip); } + else if(IP_ISSET(cfg.srvip)) + { IP_ASSIGN(SIN_GET_ADDR(sin), cfg.srvip); } + // The default is INADDR_ANY (0) + SIN_GET_PORT(sin) = htons(cfg.http_port); + } + + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) + { + cs_log("HTTP Server: Setting SO_REUSEADDR via setsockopt failed! (errno=%d %s)", errno, strerror(errno)); + } + + if(bind(sock, (struct sockaddr *)&sin, len) < 0) + { + cs_log("HTTP Server couldn't bind on port %d (errno=%d %s). Not starting HTTP!", cfg.http_port, errno, strerror(errno)); + close(sock); + return NULL; + } + + if(listen(sock, SOMAXCONN) < 0) + { + cs_log("HTTP Server: Call to listen() failed! (errno=%d %s)", errno, strerror(errno)); + close(sock); + return NULL; + } + +#ifdef WITH_SSL + if(pthread_key_create(&getssl, NULL)) + { + cs_log("Could not create getssl"); + } + + SSL_CTX *ctx = NULL; + if(cfg.http_use_ssl) + { + ctx = SSL_Webif_Init(); + if(ctx == NULL) + { cs_log("SSL could not be initialized. Starting WebIf in plain mode."); } + else { ssl_active = 1; } + } + else { ssl_active = 0; } + cs_log("HTTP%s Server running. ip=%s port=%d (%s)", ssl_active ? "S" : "", cs_inet_ntoa(SIN_GET_ADDR(sin)), cfg.http_port, ssl_active ? OPENSSL_VERSION_TEXT : "no SSL"); +#else + cs_log("HTTP Server running. ip=%s port=%d", cs_inet_ntoa(SIN_GET_ADDR(sin)), cfg.http_port); +#endif + + struct SOCKADDR remote; + memset(&remote, 0, sizeof(remote)); + + while(!exit_oscam) + { + if((s = accept(sock, (struct sockaddr *) &remote, &len)) < 0) + { + if(exit_oscam) + { break; } + if(errno != EAGAIN && errno != EINTR) + { + cs_log("HTTP Server: Error calling accept() (errno=%d %s)", errno, strerror(errno)); + cs_sleepms(100); + } + else { cs_sleepms(5); } + continue; + } + else + { + getpeername(s, (struct sockaddr *) &remote, &len); + if(!cs_malloc(&conn, sizeof(struct s_connection))) + { + close(s); + continue; + } + setTCPTimeouts(s); + cur_client()->last = time((time_t *)0); //reset last busy time + conn->cl = cur_client(); +#ifdef IPV6SUPPORT + if(do_ipv6) + { + struct sockaddr_in6 *ra = (struct sockaddr_in6 *)&remote; + memcpy(&conn->remote, &ra->sin6_addr, sizeof(struct in6_addr)); + } + else + { + struct sockaddr_in *fba = (struct sockaddr_in *)&remote; + struct in6_addr taddr; + memset(&taddr, 0, sizeof(taddr)); + taddr.s6_addr32[3] = fba->sin_addr.s_addr; + memcpy(&conn->remote, &taddr, sizeof(struct in6_addr)); + } +#else + memcpy(&conn->remote, &remote.sin_addr, sizeof(struct in_addr)); +#endif + conn->socket = s; +#ifdef WITH_SSL + conn->ssl = NULL; + if(ssl_active) + { + conn->ssl = SSL_new(ctx); + if(conn->ssl == NULL) + { + close(s); + cs_log("WebIf: Error calling SSL_new()."); + continue; + } + } +#endif + + int32_t ret = start_thread("webif workthread", serve_process, (void *)conn, NULL, 1, 1); + if(ret) + { + NULLFREE(conn); + } + } + } + // Wait a bit so that we don't close ressources while http threads are active + cs_sleepms(300); +#ifdef WITH_SSL + SSL_CTX_free(ctx); + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + OPENSSL_free(lock_cs); + lock_cs = NULL; +#endif + cs_log("HTTP Server stopped"); + free_client(cl); + close(sock); + return NULL; +} + +void webif_client_reset_lastresponsetime(struct s_client * cl) +{ + int32_t i; + for(i = 0; i < CS_ECM_RINGBUFFER_MAX; i++) + { + cl->cwlastresptimes[i].duration = 0; + cl->cwlastresptimes[i].timestamp = time((time_t *)0); + cl->cwlastresptimes[i].rc = 0; + } + cl->cwlastresptimes_last = 0; +} + +void webif_client_add_lastresponsetime(struct s_client * cl, int32_t ltime, time_t timestamp, int32_t rc) +{ + int32_t last = cl->cwlastresptimes_last = (cl->cwlastresptimes_last + 1) & (CS_ECM_RINGBUFFER_MAX - 1); + cl->cwlastresptimes[last].duration = ltime > 9999 ? 9999 : ltime; + cl->cwlastresptimes[last].timestamp = timestamp; + cl->cwlastresptimes[last].rc = rc; +} + +void webif_client_init_lastreader(struct s_client * client, ECM_REQUEST * er, struct s_reader * er_reader, const char *stxt[]) +{ + if(er_reader) + { + if(er->rc == E_FOUND) + { cs_strncpy(client->lastreader, er_reader->label, sizeof(client->lastreader)); } + else if(er->rc == E_CACHEEX) + #ifdef CS_CACHEEX_AIO + { + if (cfg.cacheex_srcname_webif) + { + cs_strncpy(client->lastreader, username(er_reader->client), sizeof(client->lastreader)); + } + else + { + cs_strncpy(client->lastreader, "cache3", sizeof(client->lastreader)); + } + } + #else + { cs_strncpy(client->lastreader, "cache3", sizeof(client->lastreader)); } + #endif + else if(er->rc < E_NOTFOUND) + { snprintf(client->lastreader, sizeof(client->lastreader) - 1, "%.54s (cache)", er_reader->label); } + else + { cs_strncpy(client->lastreader, stxt[er->rc], sizeof(client->lastreader)); } + } + else + { + cs_strncpy(client->lastreader, stxt[er->rc], sizeof(client->lastreader)); + } +} + +void webif_init(void) +{ + char buf[8], fname[256]; + snprintf(buf, 8, "%'d", 7); + if(strcmp(buf, "7")) + { + useLocal = 0; + } + + if(cfg.http_port == 0) + { + cs_log("http disabled"); + return; + } + + get_config_filename(fname, sizeof(fname), "oscam.srvid2"); + use_srvid2 = file_exists(fname); + + if(start_thread("http", http_server, NULL, &httpthread, 0, 1) == 0) + { + httpthread_running = 1; + } +} + +void webif_close(void) +{ + if(!sock) + { return; } + + shutdown(sock, 2); + close(sock); + + if(httpthread_running) + { SAFE_THREAD_JOIN(httpthread, NULL); } +} + +#endif diff --git a/module-webif.c.orig b/module-webif.c.orig new file mode 100644 index 0000000..d348e00 --- /dev/null +++ b/module-webif.c.orig @@ -0,0 +1,10342 @@ +#define MODULE_LOG_PREFIX "webif" + +#include "globals.h" + +#ifdef WEBIF +// +// OSCam HTTP server module +// +#include +#include "cscrypt/md5.h" +#include "module-anticasc.h" +#include "module-cacheex.h" +#include "module-cccam.h" +#include "module-cccam-data.h" +#include "module-dvbapi.h" +#include "module-newcamd.h" +#include "module-stat.h" +#include "module-webif.h" +#include "module-webif-lib.h" +#include "module-webif-tpl.h" +#include "oscam-conf-mk.h" +#include "oscam-config.h" +#include "oscam-files.h" +#include "oscam-garbage.h" +#include "oscam-cache.h" +#include "oscam-client.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" +#include "oscam-ecm.h" // Dodano dla refresh_cw_vote_config +#ifdef MODULE_GBOX +#include "module-gbox-sms.h" +#include "module-gbox.h" +#include "module-gbox-cards.h" +#endif + +#ifdef WEBIF_WIKI +#include "webif/pages_wiki.h" +#endif + +extern const struct s_cardreader *cardreaders[]; +extern char cs_confdir[]; +extern uint32_t ecmcwcache_size; +extern uint32_t cfg_sidtab_generation; +extern int32_t exit_oscam; +extern uint8_t cacheex_peer_id[8]; + +extern char *entitlement_type[]; +extern char *RDR_CD_TXT[]; +int8_t isactive; +int32_t ssl_active = 0; +char noncekey[33]; +pthread_key_t getkeepalive; +static pthread_key_t getip; +pthread_key_t getssl; +static CS_MUTEX_LOCK http_lock; +CS_MUTEX_LOCK *lock_cs; + +static uint8_t useLocal = 1; +#define PRINTF_LOCAL_D useLocal ? "%'d" : "%d" +#define PRINTF_LOCAL_F useLocal ? "%'.0f" : "%.0f" +#define PRINTF_LOCAL_MB useLocal ? "%'.2f MB" : "%.2f MB" + +static int8_t httpthread_running = 0; +static pthread_t httpthread; +static int32_t sock; +enum refreshtypes { REFR_ACCOUNTS, REFR_READERS, REFR_CLIENTS, REFR_SERVER, REFR_ANTICASC, REFR_SERVICES }; + +//initialize structs for calculating cpu-usage depending on time between refresh of status_page +static struct pstat p_stat_cur; +static struct pstat p_stat_old; + +static bool use_srvid2 = false; + +/* constants for menuactivating */ +#define MNU_STATUS 0 +#define MNU_LIVELOG 1 +#define MNU_CONFIG 2 +#define MNU_READERS 3 +#define MNU_USERS 4 +#define MNU_SERVICES 5 +#define MNU_FILES 6 +#define MNU_FAILBAN 7 +#define MNU_CACHEEX 8 +#define MNU_SCRIPT 9 +#define MNU_SHUTDOWN 10 +#define MNU_TOTAL_ITEMS 11 // sum of items above + +/* constants for config.html submenuactivating */ +#define MNU_CFG_GLOBAL 0 +#define MNU_CFG_ANTICASC 1 +#define MNU_CFG_CACHE 2 +#define MNU_CFG_LOADBAL 3 +#define MNU_CFG_CAMD33 4 +#define MNU_CFG_CAMD35 5 +#define MNU_CFG_CAMD35TCP 6 +#define MNU_CFG_CCCAM 7 +#define MNU_CFG_NEWCAMD 8 +#define MNU_CFG_GBOX 9 +#define MNU_CFG_RADEGAST 10 +#define MNU_CFG_SCAM 11 +#define MNU_CFG_SERIAL 12 +#define MNU_CFG_DVBAPI 13 +#define MNU_CFG_LCD 14 +#define MNU_CFG_MONITOR 15 +#define MNU_CFG_WEBIF 16 +#define MNU_CFG_STREAMRELAY 17 + +/* constants for files.html submenuactivating */ +#define MNU_CFG_FVERSION 0 +#define MNU_CFG_FCONF 1 +#define MNU_CFG_FUSER 2 +#define MNU_CFG_FSERVER 3 +#define MNU_CFG_FSRVID 4 +#define MNU_CFG_FDVBAPI 5 +#define MNU_CFG_FACLOG 6 +#define MNU_CFG_FLOGFILE 7 +#define MNU_CFG_FUSERFILE 8 +#define MNU_CFG_FSERVICES 9 +#define MNU_CFG_FPROVID 10 +#define MNU_CFG_FTIERS 11 +#define MNU_CFG_FRATELIMIT 12 +#define MNU_CFG_FWHITELIST 13 +#define MNU_CFG_FSRVID2 14 +#define MNU_CFG_FFAKECWS 15 +#define MNU_CFG_FCSS 16 +#define MNU_CFG_FTWIN 17 +#define MNU_CFG_FKEYCW 18 + +/* constants for files.html for GBOX submenuactivating */ +#define MNU_GBX_FSCINF 19 +#define MNU_GBX_FSHRINF 20 +#define MNU_GBX_FSHRONL 21 +#define MNU_GBX_FVERS 22 +#define MNU_GBX_FATTACK 23 +#define MNU_GBX_FSMSLOG 24 +#define MNU_GBX_FSMSACK 25 +#define MNU_GBX_FSMSNACK 26 +#define MNU_GBX_FSTAINF 27 +#define MNU_GBX_FEXPINF 28 +#define MNU_GBX_INFOLOG 29 +#define MNU_CFG_FSOFTCAMKEY 30 + +#define MNU_CFG_TOTAL_ITEMS 31 // sum of items above. Use it for "All inactive" in function calls too. + +static void set_status_info_var(struct templatevars *vars, char *varname, int no_data, char *fmt, double value) +{ + if (no_data) + tpl_addVar(vars, TPLADD, varname, "N/A"); + else + tpl_printf(vars, TPLADD, varname, fmt, value); +} + +/* +* Creates vars Memory/CPU/OSCAM info in for status_page +* if check_available == 0 N/A will be displayed +* Bit mapping +* mem 0 total, 1 used & free, 2 buff, cached & free incl. buff, 3 share +* swap 4 total, 5 used & free, +* proc 6 count +* cpu 7 load +* oscam 8 vsize & rssize, 9 cpu user, 10 cpu sys, 11 cpu sum, 12 cpu refreshtime +* unused 13 - 15 +*/ +static void set_status_info(struct templatevars *vars, struct pstat stats){ + set_status_info_var(vars, "MEM_CUR_TOTAL", stats.check_available & (1 << 0), PRINTF_LOCAL_MB , (double)stats.mem_total/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_FREE", stats.check_available & (1 << 1), PRINTF_LOCAL_MB , (double)stats.mem_free/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_USED", stats.check_available & (1 << 1), PRINTF_LOCAL_MB , (double)stats.mem_used/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_BUFF", stats.check_available & (1 << 2), PRINTF_LOCAL_MB , (double)stats.mem_buff/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_CACHED", stats.check_available & (1 << 2), PRINTF_LOCAL_MB , (double)stats.mem_cached/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_FREEM", stats.check_available & (1 << 2), PRINTF_LOCAL_MB , (double)stats.mem_freem/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_SHARE", stats.check_available & (1 << 3), PRINTF_LOCAL_MB , (double)stats.mem_share/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_TOTSW", stats.check_available & (1 << 4), PRINTF_LOCAL_MB , (double)stats.mem_total_swap/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_FRESW", stats.check_available & (1 << 5), PRINTF_LOCAL_MB , (double)stats.mem_free_swap/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_USESW", stats.check_available & (1 << 5), PRINTF_LOCAL_MB , (double)stats.mem_used_swap/(1024.0*1024.0)); + + set_status_info_var(vars, "SERVER_PROCS", stats.check_available & (1 << 6), PRINTF_LOCAL_F , stats.info_procs); + + set_status_info_var(vars, "CPU_LOAD_0", stats.check_available & (1 << 7), "%.2f" , stats.cpu_avg[0]); + set_status_info_var(vars, "CPU_LOAD_1", stats.check_available & (1 << 7), "%.2f" , stats.cpu_avg[1]); + set_status_info_var(vars, "CPU_LOAD_2", stats.check_available & (1 << 7), "%.2f" , stats.cpu_avg[2]); + + set_status_info_var(vars, "OSCAM_VMSIZE", stats.check_available & (1 << 8), PRINTF_LOCAL_MB , (double)stats.vsize/(1024.0*1024.0)); + set_status_info_var(vars, "OSCAM_RSSSIZE", stats.check_available & (1 << 8), PRINTF_LOCAL_MB , (double)stats.rss/(1024.0*1024.0)); + set_status_info_var(vars, "OSCAM_CPU_USER", stats.check_available & (1 << 9), "%.2f %%" , stats.cpu_usage_user); + set_status_info_var(vars, "OSCAM_CPU_SYS", stats.check_available & (1 << 10), "%.2f %%" , stats.cpu_usage_sys); + double sum_cpu = stats.cpu_usage_sys + stats.cpu_usage_user; + set_status_info_var(vars, "OSCAM_CPU_SUM", stats.check_available & (1 << 11), "%.2f %%" , sum_cpu); + + if (stats.check_available & (1 << 12)) + { + tpl_addVar(vars, TPLADD, "OSCAM_REFRESH" , "N/A"); + } + else + { + tpl_printf(vars, TPLADD, "OSCAM_REFRESH" , "%02"PRId64":%02"PRId64":%02"PRId64"h", + stats.gone_refresh / 3600, + (stats.gone_refresh / 60) % 60, + stats.gone_refresh % 60); + } +} + +static void clear_account_stats(struct s_auth *account) +{ + account->cwfound = 0; + account->cwcache = 0; + account->cwnot = 0; + account->cwtun = 0; + account->cwignored = 0; + account->cwtout = 0; + account->emmok = 0; + account->emmnok = 0; +#ifdef CW_CYCLE_CHECK + account->cwcycledchecked = 0; + account->cwcycledok = 0; + account->cwcyclednok = 0; + account->cwcycledign = 0; +#endif + cacheex_clear_account_stats(account); +} + +static void clear_all_account_stats(void) +{ + struct s_auth *account = cfg.account; + while(account) + { + clear_account_stats(account); + account = account->next; + } +} + +#ifdef CS_CACHEEX +static void cacheex_clear_all_stats(void) +{ + struct s_auth *account = cfg.account; + while(account) + { + cacheex_clear_account_stats(account); + account = account->next; + } + struct s_client *cl; + for(cl = first_client->next; cl ; cl = cl->next) + { + cacheex_clear_client_stats(cl); + ll_clear_data(cl->ll_cacheex_stats); + } + cacheex_clear_client_stats(first_client); +} +#endif + +static void clear_info_clients_stats(void) +{ + first_client->cwfound = 0; + first_client->cwcache = 0; + first_client->cwnot = 0; + first_client->cwtun = 0; + first_client->cwignored = 0; + first_client->cwtout = 0; + first_client->emmok = 0; + first_client->emmnok = 0; + cacheex_clear_client_stats(first_client); +} + +static void clear_info_readers_stats(void) +{ + int8_t i; + cs_writelock(__func__, &readerlist_lock); + LL_ITER itr = ll_iter_create(configured_readers); + struct s_reader *rdr; + while((rdr = ll_iter_next(&itr))) + { + rdr->webif_ecmsok = 0; + rdr->webif_ecmsnok = 0; + rdr->webif_ecmstout = 0; + rdr->webif_ecmsfilteredhead = 0; + rdr->webif_ecmsfilteredlen = 0; + + for(i = 0; i < 4; i++) + { + rdr->webif_emmerror[i] = 0; + rdr->webif_emmwritten[i] = 0; + rdr->webif_emmskipped[i] = 0; + rdr->webif_emmblocked[i] = 0; + } + } + cs_writeunlock(__func__, &readerlist_lock); +} + +static void set_ecm_info(struct templatevars * vars) +{ + //if one of the stats overloaded, reset all stats! + if(first_client->cwfound<0 + || first_client->cwnot<0 + || first_client->cwignored<0 + || first_client->cwtout<0 + || first_client->cwcache<0 + || first_client->cwtun<0 + || first_client->emmok<0 + || first_client->emmnok<0 +#ifdef CS_CACHEEX + || first_client->cwcacheexgot<0 + || first_client->cwcacheexpush<0 + || first_client->cwcacheexhit<0 +#ifdef CS_CACHEEX_AIO + || first_client->cwcacheexgotlg<0 + || first_client->cwcacheexpushlg<0 +#endif +#endif + ){ + clear_info_clients_stats(); + } + //end reset stats + + int ecm = 0, emm = 0; + double ecmsum = first_client->cwfound + first_client->cwcache + first_client->cwnot + first_client->cwtout; //dont count TUN its included + if(ecmsum < 1) {ecmsum = 1; ecm = 1;} + double ecmpos = first_client->cwfound + first_client->cwcache; // dont count TUN its included + if(ecmpos < 1) {ecmpos = 1;} + double ecmneg = first_client->cwnot + first_client->cwtout; //dont count IGN its not part of neagtiv + if(ecmneg < 1) {ecmneg = 1;} + double emmsum = first_client->emmok + first_client->emmnok; + if(emmsum < 1) {emmsum = 1; emm = 1;} + + tpl_printf(vars, TPLADD, "TOTAL_ECM_MIN", "%d", first_client->n_request[0]); + tpl_printf(vars, TPLADD, "TOTAL_CW", PRINTF_LOCAL_F, !ecm ? ecmsum : 0); + tpl_printf(vars, TPLADD, "TOTAL_CWOK", PRINTF_LOCAL_F, (double)first_client->cwfound); + tpl_printf(vars, TPLADD, "TOTAL_CWNOK", PRINTF_LOCAL_F, (double)first_client->cwnot); + tpl_printf(vars, TPLADD, "TOTAL_CWIGN", PRINTF_LOCAL_F, (double)first_client->cwignored); + tpl_printf(vars, TPLADD, "TOTAL_CWTOUT", PRINTF_LOCAL_F, (double)first_client->cwtout); + tpl_printf(vars, TPLADD, "TOTAL_CWCACHE", PRINTF_LOCAL_F, (double)first_client->cwcache); + tpl_printf(vars, TPLADD, "TOTAL_CWTUN", PRINTF_LOCAL_F, (double)first_client->cwtun); + tpl_printf(vars, TPLADD, "TOTAL_CWPOS", PRINTF_LOCAL_F, (double)first_client->cwfound + (double)first_client->cwcache); + tpl_printf(vars, TPLADD, "TOTAL_CWNEG", PRINTF_LOCAL_F, (double)first_client->cwnot + (double)first_client->cwtout); + tpl_printf(vars, TPLADD, "TOTAL_EM", PRINTF_LOCAL_F, !emm ? emmsum : 0); + tpl_printf(vars, TPLADD, "TOTAL_EMOK", PRINTF_LOCAL_F, (double)first_client->emmok); + tpl_printf(vars, TPLADD, "TOTAL_EMNOK", PRINTF_LOCAL_F, (double)first_client->emmnok); + tpl_printf(vars, TPLADD, "REL_CWOK", "%.2f", (double)first_client->cwfound * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWNOK", "%.2f", (double)first_client->cwnot * 100 / ecmsum); + //tpl_printf(vars, TPLADD, "REL_CWIGN", "%.2f", (double)first_client->cwignored * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWTOUT", "%.2f", (double)first_client->cwtout * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWCACHE", "%.2f", (double)first_client->cwcache * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWTUN", "%.2f", (double)first_client->cwtun * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWPOS", "%.2f", (double)(first_client->cwfound + first_client->cwcache) * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWNEG", "%.2f", (double)(first_client->cwnot + first_client->cwtout) * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_EMOK", "%.2f", (double)first_client->emmok * 100 / emmsum); + tpl_printf(vars, TPLADD, "REL_EMNOK", "%.2f", (double)first_client->emmnok * 100 / emmsum); + tpl_printf(vars, TPLADD, "REL_CWPOSOK", "%.2f", (double)first_client->cwfound * 100 / ecmpos); + tpl_printf(vars, TPLADD, "REL_CWPOSCACHE", "%.2f", (double)first_client->cwcache * 100 / ecmpos); + tpl_printf(vars, TPLADD, "REL_CWNEGNOK", "%.2f", (double)first_client->cwnot * 100 / ecmneg); + //tpl_printf(vars, TPLADD, "REL_CWNEGIGN", "%.2f", (double)first_client->cwignored * 100 / ecmneg); + tpl_printf(vars, TPLADD, "REL_CWNEGTOUT", "%.2f", (double)first_client->cwtout * 100 / ecmneg); + + double totalrdrneg = 0, totalrdrpos = 0; + double totalrdrok = 0, totalrdrnok = 0, totalrdrtout = 0; + double flen = 0, fhead = 0; + double teruk = 0, terg = 0, ters = 0, teruq = 0; + double twruk = 0, twrg = 0, twrs = 0, twruq = 0; + double tskuk = 0, tskg = 0, tsks = 0, tskuq = 0; + double tbluk = 0, tblg = 0, tbls = 0, tbluq = 0; + + cs_readlock(__func__, &readerlist_lock); + LL_ITER itr = ll_iter_create(configured_readers); + struct s_reader *rdr; + while((rdr = ll_iter_next(&itr))) + { + if (rdr->webif_ecmsok) { totalrdrok += rdr->webif_ecmsok; } + if (rdr->webif_ecmsnok) { totalrdrnok += rdr->webif_ecmsnok; } + if (rdr->webif_ecmstout) { totalrdrtout += rdr->webif_ecmstout; } + + if (rdr->webif_ecmsfilteredlen) { flen += rdr->webif_ecmsfilteredlen; } + if (rdr->webif_ecmsfilteredhead) { fhead += rdr->webif_ecmsfilteredhead; } + + if (rdr->webif_emmerror[0]) { teruk += rdr->webif_emmerror[0]; } + if (rdr->webif_emmerror[1]) { teruq += rdr->webif_emmerror[1]; } + if (rdr->webif_emmerror[2]) { ters += rdr->webif_emmerror[2]; } + if (rdr->webif_emmerror[3]) { terg += rdr->webif_emmerror[3]; } + + if (rdr->webif_emmwritten[0]) { twruk += rdr->webif_emmwritten[0]; } + if (rdr->webif_emmwritten[1]) { twruq += rdr->webif_emmwritten[1]; } + if (rdr->webif_emmwritten[2]) { twrs += rdr->webif_emmwritten[2]; } + if (rdr->webif_emmwritten[3]) { twrg += rdr->webif_emmwritten[3]; } + + if (rdr->webif_emmskipped[0]) { tskuk += rdr->webif_emmskipped[0]; } + if (rdr->webif_emmskipped[1]) { tskuq += rdr->webif_emmskipped[1]; } + if (rdr->webif_emmskipped[2]) { tsks += rdr->webif_emmskipped[2]; } + if (rdr->webif_emmskipped[3]) { tskg += rdr->webif_emmskipped[3]; } + + if (rdr->webif_emmblocked[0]) { tbluk += rdr->webif_emmblocked[0]; } + if (rdr->webif_emmblocked[1]) { tbluq += rdr->webif_emmblocked[1]; } + if (rdr->webif_emmblocked[2]) { tbls += rdr->webif_emmblocked[2]; } + if (rdr->webif_emmblocked[3]) { tblg += rdr->webif_emmblocked[3]; } + } + cs_readunlock(__func__, &readerlist_lock); + + totalrdrneg = totalrdrnok + totalrdrtout; + totalrdrpos = totalrdrok; + ecmsum = totalrdrok + totalrdrnok + totalrdrtout; + + tpl_printf(vars, TPLADD, "TOTAL_CWOK_READERS", PRINTF_LOCAL_F, totalrdrok); + tpl_printf(vars, TPLADD, "TOTAL_CWNOK_READERS", PRINTF_LOCAL_F, totalrdrnok); + tpl_printf(vars, TPLADD, "TOTAL_CWTOUT_READERS", PRINTF_LOCAL_F, totalrdrtout); + tpl_printf(vars, TPLADD, "REL_CWOK_READERS", "%.2f", ecmsum ? totalrdrok * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "REL_CWNOK_READERS", "%.2f", ecmsum ? totalrdrnok * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "REL_CWTOUT_READERS", "%.2f", ecmsum ? totalrdrtout * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "TOTAL_CWPOS_READERS", PRINTF_LOCAL_F, totalrdrpos); + tpl_printf(vars, TPLADD, "TOTAL_CWNEG_READERS", PRINTF_LOCAL_F, totalrdrneg); + tpl_printf(vars, TPLADD, "REL_CWPOS_READERS", "%.2f", ecmsum ? totalrdrpos * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "REL_CWNEG_READERS", "%.2f", ecmsum ? totalrdrneg * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "TOTAL_ELENR", PRINTF_LOCAL_F, flen); + tpl_printf(vars, TPLADD, "TOTAL_EHEADR", PRINTF_LOCAL_F, fhead); + tpl_printf(vars, TPLADD, "TOTAL_SUM_ALL_READERS_ECM", PRINTF_LOCAL_F, ecmsum); + + tpl_printf(vars, TPLADD, "TOTAL_EMMERRORUK_READERS", PRINTF_LOCAL_F, teruk); + tpl_printf(vars, TPLADD, "TOTAL_EMMERRORG_READERS", PRINTF_LOCAL_F, terg); + tpl_printf(vars, TPLADD, "TOTAL_EMMERRORS_READERS", PRINTF_LOCAL_F, ters); + tpl_printf(vars, TPLADD, "TOTAL_EMMERRORUQ_READERS", PRINTF_LOCAL_F, teruq); + tpl_printf(vars, TPLADD, "TOTAL_EMMWRITTENUK_READERS", PRINTF_LOCAL_F, twruk); + tpl_printf(vars, TPLADD, "TOTAL_EMMWRITTENG_READERS", PRINTF_LOCAL_F, twrg); + tpl_printf(vars, TPLADD, "TOTAL_EMMWRITTENS_READERS", PRINTF_LOCAL_F, twrs); + tpl_printf(vars, TPLADD, "TOTAL_EMMWRITTENUQ_READERS", PRINTF_LOCAL_F, twruq); + tpl_printf(vars, TPLADD, "TOTAL_EMMSKIPPEDUK_READERS", PRINTF_LOCAL_F, tskuk); + tpl_printf(vars, TPLADD, "TOTAL_EMMSKIPPEDG_READERS", PRINTF_LOCAL_F, tskg); + tpl_printf(vars, TPLADD, "TOTAL_EMMSKIPPEDS_READERS", PRINTF_LOCAL_F, tsks); + tpl_printf(vars, TPLADD, "TOTAL_EMMSKIPPEDUQ_READERS", PRINTF_LOCAL_F, tskuq); + tpl_printf(vars, TPLADD, "TOTAL_EMMBLOCKEDUK_READERS", PRINTF_LOCAL_F, tbluk); + tpl_printf(vars, TPLADD, "TOTAL_EMMBLOCKEDG_READERS", PRINTF_LOCAL_F, tblg); + tpl_printf(vars, TPLADD, "TOTAL_EMMBLOCKEDS_READERS", PRINTF_LOCAL_F, tbls); + tpl_printf(vars, TPLADD, "TOTAL_EMMBLOCKEDUQ_READERS", PRINTF_LOCAL_F, tbluq); + + emmsum = teruk + terg + ters + teruq + twruk + twrg + twrs + twruq + tskuk + tskg + tsks + tskuq + tbluk + tblg + tbls + tbluq; + + tpl_printf(vars, TPLADD, "TOTAL_SUM_ALL_READERS_EMM", PRINTF_LOCAL_F, emmsum); +} + +static void refresh_oscam(enum refreshtypes refreshtype) +{ + + switch(refreshtype) + { + case REFR_ACCOUNTS: + cs_log("Refresh Accounts requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + cs_accounts_chk(); + break; + + case REFR_READERS: + cs_log("Refresh Readers requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + reload_readerdb(); + break; + + case REFR_CLIENTS: + cs_log("Refresh Clients requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + cs_reinit_clients(cfg.account); + break; + + case REFR_SERVER: + cs_log("Refresh Server requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + reload_global_config(); // Wczytaj ponownie globalną konfigurację + break; + + case REFR_SERVICES: + cs_log("Refresh Services requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + //init_sidtab(); + cs_accounts_chk(); + break; + +#ifdef CS_ANTICASC + case REFR_ANTICASC: + cs_log("Refresh Anticascading requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + ac_init_stat(); + struct s_client *cl; + struct s_auth *account; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->typ == 'c' && (account = cl->account)) + { + cl->ac_limit = (account->ac_users * 100 + 80) * cfg.ac_stime; + } + } + break; +#endif + default: + break; + } +} +/* + * load historical values from ringbuffer and return it in the right order + * as string. Value should be freed with free_mk_t() + */ +static char *get_ecm_historystring(struct s_client *cl) +{ + + if(cl) + { + int32_t k, i, pos = 0, needed = 1, v; + char *value, *dot = ""; + int32_t ptr = cl->cwlastresptimes_last; + + needed = CS_ECM_RINGBUFFER_MAX * 6; //5 digits + delimiter + if(!cs_malloc(&value, needed)) { return ""; } + + k = ptr + 1; + for(i = 0; i < CS_ECM_RINGBUFFER_MAX; i++) + { + if(k >= CS_ECM_RINGBUFFER_MAX) + { k = 0; } + v = cl->cwlastresptimes[k].duration; + if(v > 0 && v < (int32_t)cfg.ctimeout * 5) + { + pos += snprintf(value + pos, needed - pos, "%s%d", dot, v); + dot = ","; + } + k++; + } + if(cs_strlen(value) == 0) + { + NULLFREE(value); + return ""; + } + else { return value; } + + } + else + { + return ""; + } +} + +static char *get_ecm_fullhistorystring(struct s_client *cl) +{ + + if(cl) + { + int32_t k, i, pos = 0, needed = 1, v; + char *value, *dot = ""; + int32_t ptr = cl->cwlastresptimes_last; + + needed = CS_ECM_RINGBUFFER_MAX * 20; //5 digits + : + returncode(2) + : + time(10) + delimiter + if(!cs_malloc(&value, needed)) { return ""; } + + k = ptr + 1; + for(i = 0; i < CS_ECM_RINGBUFFER_MAX; i++) + { + if(k >= CS_ECM_RINGBUFFER_MAX) + { k = 0; } + v = cl->cwlastresptimes[k].duration; + if(v > 0 && v < (int32_t)cfg.ctimeout * 5) + { + pos += snprintf(value + pos, needed - pos, "%s%d:%d:%" PRId64, dot, cl->cwlastresptimes[k].duration, cl->cwlastresptimes[k].rc, (int64_t)cl->cwlastresptimes[k].timestamp); + dot = ","; + } + k++; + } + + return (value); + + } + else + { + return ""; + } +} + +/* + * Set the active menu to a different CSS class + */ +static void setActiveMenu(struct templatevars *vars, int8_t active) +{ + int8_t i; + for(i = 0; i < MNU_TOTAL_ITEMS; i++) + { + tpl_printf(vars, TPLADD, "TMP", "MENUACTIVE%d", i); + if(i == active) + { tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "menu_selected"); } + else + { tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "menu"); } + } + #ifdef WEBIF_LIVELOG + tpl_addVar(vars, TPLADD, "LOGPAGEMENU", tpl_getTpl(vars, "LOGMENU")); + #endif +} + +/* + * Set the active submenu to a different CSS class + */ +static void setActiveSubMenu(struct templatevars *vars, int8_t active) +{ + int8_t i; + for(i = 0; i < MNU_CFG_TOTAL_ITEMS; i++) + { + tpl_printf(vars, TPLADD, "TMP", "CMENUACTIVE%d", i); + if(i == active) + { tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "configmenu_selected"); } + else + { tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "configmenu"); } + } +} + +static void webif_save_config(char *section, struct templatevars *vars, struct uriparams *params) +{ + if(!streq(getParam(params, "action"), "execute")) + { return; } + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. No changes are possible!"); + return; + } + int i; + int cnt = (*params).paramcount; + // First pass: check for checkbox values (value=1) to identify which checkboxes are checked + // This is needed because hidden fields (value=0) should not override checkbox values + bool *checkbox_checked = NULL; + if(cnt > 0 && !cs_malloc(&checkbox_checked, cnt * sizeof(bool))) + { + return; // Allocation failed, proceed with original behavior + } + for(i = 0; i < cnt; i++) + { + char *token = (*params).params[i]; + char *value = (*params).values[i]; + if(!streq(token, "part") && !streq(token, "action")) + { + // Check if this is a checkbox with value=1 + if(strcmp(value, "1") == 0) + { + checkbox_checked[i] = true; + } + } + } + // Second pass: apply settings, skip value=0 if checkbox is checked (hidden field) + for(i = 0; i < cnt; i++) + { + char *token = (*params).params[i]; + char *value = (*params).values[i]; + if(!streq(token, "part") && !streq(token, "action")) + { + // Skip hidden field value=0 if checkbox is checked (value=1 exists) + if(strcmp(value, "0") == 0) + { + bool checkbox_will_be_checked = false; + int j; + for(j = i + 1; j < cnt; j++) + { + if(strcmp((*params).params[j], token) == 0 && strcmp((*params).values[j], "1") == 0) + { + checkbox_will_be_checked = true; + break; + } + } + if(checkbox_will_be_checked) + { + continue; // Skip this hidden field, checkbox will set it to 1 + } + } + config_set(section, token, value); + } + } + free(checkbox_checked); + if(write_config() == 0) + { + tpl_addMsg(vars, "Configuration was saved."); + enum refreshtypes ref_type = REFR_SERVER; + if(streq(getParam(params, "part"), "anticasc")) + { ref_type = REFR_ANTICASC; } + refresh_oscam(ref_type); + } + else + { + tpl_addMsg(vars, "ERROR: Failed to write config file!!!"); + } +} + +static char *send_oscam_config_global(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_GLOBAL); + + webif_save_config("global", vars, params); + + if(IP_ISSET(cfg.srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.srvip)); } + tpl_printf(vars, TPLADD, "NICE", "%d", cfg.nice); + tpl_printf(vars, TPLADD, "BINDWAIT", "%d", cfg.bindwait); + + tpl_printf(vars, TPLADD, "TMP", "NETPRIO%d", cfg.netprio); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "PIDFILE", "%s", ESTR(cfg.pidfile)); + + + if(cfg.usrfile != NULL) { tpl_addVar(vars, TPLADD, "USERFILE", cfg.usrfile); } + if(cfg.disableuserfile == 0) { tpl_addVar(vars, TPLADD, "DISABLEUSERFILECHECKED", "checked"); } + if(cfg.usrfileflag == 1) { tpl_addVar(vars, TPLADD, "USERFILEFLAGCHECKED", "selected"); } + if(cfg.mailfile != NULL) { tpl_addVar(vars, TPLADD, "MAILFILE", cfg.mailfile); } + if(cfg.disablemail == 0) { tpl_addVar(vars, TPLADD, "DISABLEMAILCHECKED", "checked"); } + + char *value = mk_t_logfile(); + tpl_addVar(vars, TPLADD, "LOGFILE", value); + free_mk_t(value); + if(cfg.disablelog == 0) { tpl_addVar(vars, TPLADD, "DISABLELOGCHECKED", "checked"); } + tpl_printf(vars, TPLADD, "MAXLOGSIZE", "%d", cfg.max_log_size); + + tpl_addVar(vars, TPLADD, "LOGDUPSCHECKED", (cfg.logduplicatelines == 1) ? "checked" : ""); + tpl_printf(vars, TPLADD, "INITIALDEBUGLEVEL", "%u", cfg.initial_debuglevel); + + if(cfg.cwlogdir != NULL) { tpl_addVar(vars, TPLADD, "CWLOGDIR", cfg.cwlogdir); } + if(cfg.emmlogdir != NULL) { tpl_addVar(vars, TPLADD, "EMMLOGDIR", cfg.emmlogdir); } + tpl_addVar(vars, TPLADD, "ECMFMT", cfg.ecmfmt); + tpl_printf(vars, TPLADD, "LOGHISTORYLINES", "%u", cfg.loghistorylines); + if(cfg.sysloghost != NULL) { tpl_addVar(vars, TPLADD, "SYSLOGHOST", cfg.sysloghost); } + tpl_printf(vars, TPLADD, "SYSLOGPORT", "%u", cfg.syslogport); + + + tpl_printf(vars, TPLADD, "CLIENTTIMEOUT", "%u", cfg.ctimeout); + value = mk_t_caidvaluetab(&cfg.ctimeouttab); + tpl_addVar(vars, TPLADD, "CLIENTTIMEOUT_PERCAID", value); + free_mk_t(value); + tpl_printf(vars, TPLADD, "FALLBACKTIMEOUT", "%u", cfg.ftimeout); + tpl_printf(vars, TPLADD, "CLIENTMAXIDLE", "%u", cfg.cmaxidle); + + + value = mk_t_caidvaluetab(&cfg.ftimeouttab); + tpl_addVar(vars, TPLADD, "FALLBACKTIMEOUT_PERCAID", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "SLEEP", "%d", cfg.tosleep); + tpl_addVar(vars, TPLADD, "UNLOCKPARENTALCHECKED", (cfg.ulparent == 1) ? "checked" : ""); + + if(cfg.reload_useraccounts) { tpl_addVar(vars, TPLADD, "RELOADUSERACCOUNTSCHECKED", "checked"); } + if(cfg.reload_readers) { tpl_addVar(vars, TPLADD, "RELOADREADERSCHECKED", "checked"); } + if(cfg.reload_provid) { tpl_addVar(vars, TPLADD, "RELOADPROVIDCHECKED", "checked"); } + if(cfg.reload_services_ids) { tpl_addVar(vars, TPLADD, "RELOADSERVICESIDSCHECKED", "checked"); } + if(cfg.reload_tier_ids) { tpl_addVar(vars, TPLADD, "RELOADTIERUDSCHECKED", "checked"); } + if(cfg.reload_fakecws) { tpl_addVar(vars, TPLADD, "RELOADFAKECWSCHECKED", "checked"); } + if(cfg.reload_ac_stat) { tpl_addVar(vars, TPLADD, "RELOADACSTATCHECKED", "checked"); } + if(cfg.reload_log) { tpl_addVar(vars, TPLADD, "RELOADLOGCHECKED", "checked"); } + + if(cfg.block_same_ip) { tpl_addVar(vars, TPLADD, "BLOCKSAMEIPCHECKED", "checked"); } + if(cfg.block_same_name) { tpl_addVar(vars, TPLADD, "BLOCKSAMENAMECHECKED", "checked"); } + + if(cfg.waitforcards == 1) { tpl_addVar(vars, TPLADD, "WAITFORCARDSCHECKED", "checked"); } + tpl_printf(vars, TPLADD, "EXTRADELAY", "%d", cfg.waitforcards_extra_delay); + if(cfg.preferlocalcards == 1) + { + tpl_addVar(vars, TPLADD, "PREFERCACHEEX", "selected"); + } + else if(cfg.preferlocalcards == 2) + { + tpl_addVar(vars, TPLADD, "PREFERLOCALCARDS", "selected"); + } + + if(cfg.c35_suppresscmd08) + { tpl_addVar(vars, TPLADD, "SUPPRESSCMD08", "checked"); } + + if(cfg.getblockemmauprovid > 0) + { + tpl_addVar(vars, TPLADD, "GETBLOCKEMMAUPROVID", "checked"); + } + + if(cfg.reader_restart_seconds) + { tpl_printf(vars, TPLADD, "READERRESTARTSECONDS", "%d", cfg.reader_restart_seconds); } + + tpl_addVar(vars, TPLADD, "DROPDUPSCHECKED", (cfg.dropdups == 1) ? "checked" : ""); + + if(cfg.resolve_gethostbyname == 1) + { tpl_addVar(vars, TPLADD, "RESOLVER1", "selected"); } + else + { tpl_addVar(vars, TPLADD, "RESOLVER0", "selected"); } + + tpl_printf(vars, TPLADD, "FAILBANTIME", "%d", cfg.failbantime); + tpl_printf(vars, TPLADD, "FAILBANCOUNT", "%d", cfg.failbancount); + + tpl_addVar(vars, TPLADD, "DCHECKCSELECTED", (cfg.double_check == 1) ? "checked" : ""); + + value = mk_t_ftab(&cfg.double_check_caid); + tpl_addVar(vars, TPLADD, "DOUBLECHECKCAID", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "DISABLECRCCWSCHECKEDGLOBAL", (cfg.disablecrccws == 1) ? "checked" : ""); + + value = mk_t_ftab(&cfg.disablecrccws_only_for); + tpl_addVar(vars, TPLADD, "IGNCHKSUMONLYFORGLOBAL", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "CWVOTEENABLEDCHECKED", (cfg.cwvote_enabled == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "CWVOTELOGENABLEDCHECKED", (cfg.cwvote_log_enabled == 1) ? "checked" : ""); + tpl_printf(vars, TPLADD, "CWVOTETIMEOUT", "%d", cfg.cwvote_timeout); + tpl_printf(vars, TPLADD, "CWVOTEMINVOTES", "%d", cfg.cwvote_min_votes); + tpl_printf(vars, TPLADD, "CWVOTELOCALWEIGHT", "%.1f", cfg.cwvote_local_weight); + tpl_printf(vars, TPLADD, "CWVOTEMAXCANDIDATES", "%d", cfg.cwvote_max_candidates); + tpl_addVar(vars, TPLADD, "CWVOTECOMPARE8", (cfg.cwvote_compare_len == 8) ? "selected" : ""); + tpl_addVar(vars, TPLADD, "CWVOTECOMPARE16", (cfg.cwvote_compare_len == 16) ? "selected" : ""); + tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK0", (cfg.cwvote_fallback == 0) ? "selected" : ""); + tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK1", (cfg.cwvote_fallback == 1) ? "selected" : ""); + tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK2", (cfg.cwvote_fallback == 2) ? "selected" : ""); + value = mk_t_cwvote_caidtab(&cfg.cwvote_caids); + tpl_addVar(vars, TPLADD, "CWVOTECAIDS", value); + free_mk_t(value); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "CACHEEXSRCNAME", (cfg.cacheex_srcname_webif == 1) ? "checked" : ""); +#endif + +#ifdef LEDSUPPORT + if(cfg.enableled == 1) + { tpl_addVar(vars, TPLADD, "ENABLELEDSELECTED1", "selected"); } + else if(cfg.enableled == 2) + { tpl_addVar(vars, TPLADD, "ENABLELEDSELECTED2", "selected"); } +#endif + + return tpl_getTpl(vars, "CONFIGGLOBAL"); +} + +#ifdef WITH_LB +static char *send_oscam_config_loadbalancer(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_LOADBAL); + + if(cs_strlen(getParam(params, "button")) > 0) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. No changes are possible!"); + } + else + { + if(strcmp(getParam(params, "button"), "Load Stats") == 0) + { + clear_all_stat(); + load_stat_from_file(); + tpl_addMsg(vars, "Stats loaded from file"); + } + + if(strcmp(getParam(params, "button"), "Save Stats") == 0) + { + save_stat_to_file(1); + tpl_addMsg(vars, "Stats saved to file"); + } + + if(strcmp(getParam(params, "button"), "Clear Stats") == 0) + { + clear_all_stat(); + tpl_addMsg(vars, "Stats cleared completly"); + } + + if(strcmp(getParam(params, "button"), "Clear Timeouts") == 0) + { + clean_all_stats_by_rc(E_TIMEOUT, 0); + tpl_addMsg(vars, "Timeout cleared from Stats"); + } + + if(strcmp(getParam(params, "button"), "Clear Not Found") == 0) + { + clean_all_stats_by_rc(E_NOTFOUND, 0); + tpl_addMsg(vars, "Not Found cleared from Stats"); + } + + if(strcmp(getParam(params, "button"), "Clear Invalid") == 0) + { + clean_all_stats_by_rc(E_INVALID, 0); + tpl_addMsg(vars, "Invalid cleared from Stats"); + } + } + } + + webif_save_config("global", vars, params); + + tpl_printf(vars, TPLADD, "TMP", "LBMODE%d", cfg.lb_mode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "LBSAVE", "%d", cfg.lb_save); + if(cfg.lb_savepath) { tpl_addVar(vars, TPLADD, "LBSAVEPATH", cfg.lb_savepath); } + + tpl_printf(vars, TPLADD, "LBNBESTREADERS", "%d", cfg.lb_nbest_readers); + char *value = mk_t_caidvaluetab(&cfg.lb_nbest_readers_tab); + tpl_addVar(vars, TPLADD, "LBNBESTPERCAID", value); + free_mk_t(value); + tpl_printf(vars, TPLADD, "LBNFBREADERS", "%d", cfg.lb_nfb_readers); + tpl_printf(vars, TPLADD, "LBMAXREADERS", "%d", cfg.lb_max_readers); + tpl_printf(vars, TPLADD, "LBMINECMCOUNT", "%d", cfg.lb_min_ecmcount); + tpl_printf(vars, TPLADD, "LBMAXECEMCOUNT", "%d", cfg.lb_max_ecmcount); + tpl_printf(vars, TPLADD, "LBRETRYLIMIT", "%d", cfg.lb_retrylimit); + + value = mk_t_caidvaluetab(&cfg.lb_retrylimittab); + tpl_addVar(vars, TPLADD, "LBRETRYLIMITS", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "LBREOPENSECONDS", "%d", cfg.lb_reopen_seconds); + tpl_printf(vars, TPLADD, "LBCLEANUP", "%d", cfg.lb_stat_cleanup); + + tpl_addVar(vars, TPLADD, "LBREOPENINVALID", (cfg.lb_reopen_invalid == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LBFORCEALWAYS", (cfg.lb_force_reopen_always == 1) ? "checked" : ""); + + value = mk_t_caidtab(&cfg.lb_noproviderforcaid); + tpl_addVar(vars, TPLADD, "LBNOPROVIDERFORCAID", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LBAUTOBETATUNNEL", (cfg.lb_auto_betatunnel == 1) ? "checked" : ""); + + if(cfg.lb_auto_betatunnel_mode) + { + tpl_printf(vars, TPLADD, "TMP", "LBAUTOBETATUNNELMODE%d", cfg.lb_auto_betatunnel_mode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + + tpl_printf(vars, TPLADD, "LBPREFERBETA", "%d", cfg.lb_auto_betatunnel_prefer_beta); + + tpl_addVar(vars, TPLADD, "LBAUTOTIMEOUT", (cfg.lb_auto_timeout == 1) ? "checked" : ""); + + tpl_printf(vars, TPLADD, "LBAUTOTIMEOUTP", "%d", cfg.lb_auto_timeout_p); + tpl_printf(vars, TPLADD, "LBAUTOTIMEOUTT", "%d", cfg.lb_auto_timeout_t); + + tpl_addVar(vars, TPLADDONCE, "CONFIG_CONTROL", tpl_getTpl(vars, "CONFIGLOADBALANCERCTRL")); + + return tpl_getTpl(vars, "CONFIGLOADBALANCER"); +} +#endif + +#ifdef MODULE_CAMD33 +static char *send_oscam_config_camd33(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + + setActiveSubMenu(vars, MNU_CFG_CAMD33); + + webif_save_config("camd33", vars, params); + + if(cfg.c33_port) + { + tpl_printf(vars, TPLADD, "PORT", "%d", cfg.c33_port); + if(IP_ISSET(cfg.c33_srvip)) { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.c33_srvip)); } + tpl_addVar(vars, TPLADD, "PASSIVECHECKED", (cfg.c33_passive == 1) ? "checked" : ""); + + for(i = 0; i < (int) sizeof(cfg.c33_key); ++i) { tpl_printf(vars, TPLAPPEND, "KEY", "%02X", cfg.c33_key[i]); } + char *value = mk_t_iprange(cfg.c33_plain); + tpl_addVar(vars, TPLADD, "NOCRYPT", value); + free_mk_t(value); + } + + return tpl_getTpl(vars, "CONFIGCAMD33"); +} +#endif + +#ifdef MODULE_CAMD35 +static char *send_oscam_config_camd35(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_CAMD35); + + webif_save_config("cs357x", vars, params); + + if(cfg.c35_port) + { + tpl_printf(vars, TPLADD, "PORT", "%d", cfg.c35_port); + if(IP_ISSET(cfg.c35_srvip)) + { tpl_addVar(vars, TPLAPPEND, "SERVERIP", cs_inet_ntoa(cfg.c35_srvip)); } + + if(cfg.c35_udp_suppresscmd08) + { tpl_addVar(vars, TPLADD, "SUPPRESSCMD08UDP", "checked"); } + + } + return tpl_getTpl(vars, "CONFIGCAMD35"); +} +#endif + +#ifdef MODULE_CAMD35_TCP +static char *send_oscam_config_camd35tcp(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_CAMD35TCP); + + webif_save_config("cs378x", vars, params); + + if((cfg.c35_tcp_ptab.nports > 0) && (cfg.c35_tcp_ptab.ports[0].s_port > 0)) + { + + char *value = mk_t_camd35tcp_port(); + tpl_addVar(vars, TPLADD, "PORT", value); + free_mk_t(value); + + if(IP_ISSET(cfg.c35_tcp_srvip)) + { tpl_addVar(vars, TPLAPPEND, "SERVERIP", cs_inet_ntoa(cfg.c35_tcp_srvip)); } + + if(cfg.c35_tcp_suppresscmd08) + { tpl_addVar(vars, TPLADD, "SUPPRESSCMD08TCP", "checked"); } + } + return tpl_getTpl(vars, "CONFIGCAMD35TCP"); +} +#endif + +static char *send_oscam_config_cache(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_CACHE); + + webif_save_config("cache", vars, params); + + tpl_printf(vars, TPLADD, "CACHEDELAY", "%u", cfg.delay); + + tpl_printf(vars, TPLADD, "MAXCACHETIME", "%d", cfg.max_cache_time); + +#ifdef CS_CACHEEX + char *value = NULL; + +#ifdef CS_CACHEEX_AIO + value = mk_t_cacheex_cwcheck_valuetab(&cfg.cw_cache_settings); + tpl_addVar(vars, TPLADD, "CWCACHESETTINGS", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "CWCACHESIZE", "%d", cfg.cw_cache_size); + + tpl_printf(vars, TPLADD, "CWCACHEMEMORY", "%d", cfg.cw_cache_memory); + + tpl_printf(vars, TPLADD, "ECMCACHESIZE", "%d", cfg.ecm_cache_size); + + tpl_printf(vars, TPLADD, "ECMCACHEMEMORY", "%d", cfg.ecm_cache_memory); + + tpl_printf(vars, TPLADD, "ECMDROPTIME", "%d", cfg.ecm_cache_droptime); +#endif + + value = mk_t_cacheex_valuetab(&cfg.cacheex_wait_timetab); + tpl_addVar(vars, TPLADD, "WAIT_TIME", value); + free_mk_t(value); + +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "WAITTIME_BLOCK_START", "%d", cfg.waittime_block_start); + + tpl_printf(vars, TPLADD, "WAITTIME_BLOCK_TIME", "%d", cfg.waittime_block_time); +#endif + + value = mk_t_caidvaluetab(&cfg.cacheex_mode1_delay_tab); + tpl_addVar(vars, TPLADD, "CACHEEXMODE1DELAY", value); + free_mk_t(value); + +#ifdef CS_CACHEEX_AIO + value = mk_t_caidvaluetab(&cfg.cacheex_nopushafter_tab); + tpl_addVar(vars, TPLADD, "CACHEEXNOPUSHAFTER", value); + free_mk_t(value); +#endif + + tpl_printf(vars, TPLADD, "MAX_HIT_TIME", "%d", cfg.max_hitcache_time); + + tpl_addVar(vars, TPLADD, "CACHEEXSTATSSELECTED", (cfg.cacheex_enable_stats == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "WTTCHECKED", (cfg.wait_until_ctimeout == 1) ? "checked" : ""); + +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "CACHEEXDROPDIFFS", (cfg.cacheex_dropdiffs == 1) ? "checked" : ""); + + value = mk_t_group(cfg.cacheex_push_lg_groups); + tpl_addVar(vars, TPLADD, "CACHEEXPUSHLGGRPS", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LGONLYREMOTESETTINGSCHECKED", (cfg.cacheex_lg_only_remote_settings == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYCHECKED", (cfg.cacheex_localgenerated_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&cfg.cacheex_lg_only_tab); + tpl_addVar(vars, TPLADD, "LGONLYTAB", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYINCHECKED", (cfg.cacheex_localgenerated_only_in == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "LGONLYINAIOONLYCHECKED", (cfg.cacheex_lg_only_in_aio_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&cfg.cacheex_lg_only_in_tab); + tpl_addVar(vars, TPLADD, "LGONLYINTAB", value); + free_mk_t(value); + + value = mk_t_cacheex_hitvaluetab(&cfg.cacheex_filter_caidtab); + tpl_addVar(vars, TPLADD, "CACHEEXECMFILTER", value); + free_mk_t(value); + + value = mk_t_cacheex_hitvaluetab(&cfg.cacheex_filter_caidtab_aio); + tpl_addVar(vars, TPLADD, "CACHEEXECMFILTERAIO", value); + free_mk_t(value); +#endif + + if(cfg.csp_port) + { tpl_printf(vars, TPLADD, "PORT", "%d", cfg.csp_port); } + + if(IP_ISSET(cfg.csp_srvip)) + { tpl_addVar(vars, TPLAPPEND, "SERVERIP", cs_inet_ntoa(cfg.csp_srvip)); } + + value = mk_t_cacheex_hitvaluetab(&cfg.csp.filter_caidtab); + tpl_addVar(vars, TPLADD, "CSP_ECM_FILTER", value); + free_mk_t(value); + + value = mk_t_cacheex_cwcheck_valuetab(&cfg.cacheex_cwcheck_tab); + tpl_addVar(vars, TPLADD, "CACHEEXCWCHECK", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "ARCHECKED", (cfg.csp.allow_request == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "ARFCHECKED", (cfg.csp.allow_reforward == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "BLOCKFAKECWSCHECKED", (cfg.csp.block_fakecws == 1) ? "checked" : ""); +#endif + +#ifdef CW_CYCLE_CHECK +#ifndef CS_CACHEEX + char *value = NULL; +#endif + + tpl_addVar(vars, TPLADD, "CWCYCLECHECK", (cfg.cwcycle_check_enable == 1) ? "checked" : ""); + + value = mk_t_caidtab(&cfg.cwcycle_check_caidtab); + tpl_addVar(vars, TPLADD, "CWCYCLECHECKCAID", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "MAXCYCLELIST", "%d", cfg.maxcyclelist); + tpl_printf(vars, TPLADD, "KEEPCYCLETIME", "%d", cfg.keepcycletime); + + if(cfg.onbadcycle) + { + tpl_printf(vars, TPLADD, "TMP", "ONBADCYCLE%d", cfg.onbadcycle); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + + tpl_addVar(vars, TPLADD, "DROPOLD", (cfg.cwcycle_dropold == 1) ? "checked" : ""); + + if(cfg.cwcycle_sensitive) + { + tpl_printf(vars, TPLADD, "TMP", "CWCSEN%d", cfg.cwcycle_sensitive); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + + tpl_addVar(vars, TPLADD, "ALLOWBADFROMFFB", (cfg.cwcycle_allowbadfromffb == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "USECWCFROMCE", (cfg.cwcycle_usecwcfromce == 1) ? "checked" : ""); + + +#endif + +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "CONFIGCACHEAIO"); +#else + return tpl_getTpl(vars, "CONFIGCACHE"); +#endif +} + +#ifdef MODULE_NEWCAMD +static char *send_oscam_config_newcamd(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + + setActiveSubMenu(vars, MNU_CFG_NEWCAMD); + + webif_save_config("newcamd", vars, params); + + if((cfg.ncd_ptab.nports > 0) && (cfg.ncd_ptab.ports[0].s_port > 0)) + { + + char *value = mk_t_newcamd_port(); + tpl_addVar(vars, TPLADD, "PORT", value); + free_mk_t(value); + + if(IP_ISSET(cfg.ncd_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.ncd_srvip)); } + + for(i = 0; i < (int32_t)sizeof(cfg.ncd_key); i++) + { tpl_printf(vars, TPLAPPEND, "KEY", "%02X", cfg.ncd_key[i]); } + + value = mk_t_iprange(cfg.ncd_allowed); + tpl_addVar(vars, TPLADD, "ALLOWED", value); + free_mk_t(value); + + if(cfg.ncd_keepalive) + { tpl_addVar(vars, TPLADD, "KEEPALIVE", "checked"); } + if(cfg.ncd_mgclient) + { tpl_addVar(vars, TPLADD, "MGCLIENTCHK", "checked"); } + } + return tpl_getTpl(vars, "CONFIGNEWCAMD"); +} +#endif + +#ifdef MODULE_GBOX +static char *send_oscam_config_gbox(struct templatevars *vars, struct uriparams *params) +{ + uint8_t i=0; + char local_gbox_save_gsms[2],local_gbox_msg_type[3], local_gbox_dest_peers[GBOX_MAX_DEST_PEERS*5], tmp_gbox_dest_peers[GBOX_MAX_DEST_PEERS*5] ; + int n=0, len_gbox_save_gsms=0, len_gbox_msg_type=0, len_gbox_dest_peers=0, len_gbox_msg_txt=0; + char *ptr1, *saveptr1, *isbroadcast = NULL; + const char *s; + uint16_t gbox_dest_peers_tmp; + + setActiveSubMenu(vars, MNU_CFG_GBOX); + webif_save_config("gbox", vars, params); + /* + * Action when GetOnlinePeers is pressed + */ + if(streq(getParam(params, "action"), "Online peers")) + { + gbox_get_online_peers(); + // init var + len_gbox_save_gsms=cs_strlen(getParam(params, "gbox_msg_type")); + len_gbox_msg_type=cs_strlen(getParam(params, "gbox_msg_type")); + len_gbox_msg_txt=cs_strlen(getParam(params, "gbox_msg_txt")); + if(len_gbox_msg_txt>GBOX_MAX_MSG_TXT) { len_gbox_msg_txt=GBOX_MAX_MSG_TXT; } + // retrieve value from Webif + cs_strncpy(local_gbox_save_gsms, getParam(params, "gbox_save_gsms"), len_gbox_save_gsms+1); + cfg.gbox_save_gsms=atoi(local_gbox_save_gsms); + cs_strncpy(local_gbox_msg_type, getParam(params, "gbox_msg_type"), len_gbox_msg_type+1); + cfg.gbox_msg_type=atoi(local_gbox_msg_type); + cs_strncpy(cfg.gbox_msg_txt,getParam(params, "gbox_msg_txt"), len_gbox_msg_txt+1); + } + /* + * Action when ResetGSMS button is pressed + */ + if(streq(getParam(params, "action"), "resetallgsms")) + { + cfg.gbox_save_gsms = 0; + cfg.gbox_msg_type = 0; + for(i = 0; i < GBOX_MAX_DEST_PEERS; i++) + { + cfg.gbox_dest_peers[i]='\0'; + } + cfg.gbox_dest_peers_num=0; + for(i = 0; i < GBOX_MAX_MSG_TXT; i++) + { + cfg.gbox_msg_txt[i]='\0'; + } + tpl_addMsg(vars, "GBOX: Reset GSMS datas done!"); + } + /* + * Action when Send GSMS is pressed + */ + if(streq(getParam(params, "action"), "Send GSMS")) + { + // init var + len_gbox_msg_type=cs_strlen(getParam(params, "gbox_msg_type")); + len_gbox_dest_peers=cs_strlen(trim(getParam(params, "gbox_dest_peers"))); + len_gbox_msg_txt=cs_strlen(getParam(params, "gbox_msg_txt")); + if(len_gbox_msg_txt>GBOX_MAX_MSG_TXT) { len_gbox_msg_txt=GBOX_MAX_MSG_TXT; } + // retrieve value from Webif + cs_strncpy(local_gbox_msg_type, getParam(params, "gbox_msg_type"), len_gbox_msg_type+1); + cfg.gbox_msg_type=atoi(local_gbox_msg_type); + cs_strncpy(local_gbox_dest_peers, strtoupper(trim(getParam(params, "gbox_dest_peers"))), len_gbox_dest_peers+1); + cs_strncpy(tmp_gbox_dest_peers, strtoupper(trim(getParam(params, "gbox_dest_peers"))), len_gbox_dest_peers+1); + cs_strncpy(cfg.gbox_msg_txt,getParam(params, "gbox_msg_txt"), len_gbox_msg_txt+1); + n=0; + for (ptr1 = strtok_r(tmp_gbox_dest_peers, ",", &saveptr1); (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s=trim(ptr1); + if ((n < GBOX_MAX_DEST_PEERS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.gbox_dest_peers[n++] = a2i(trim(ptr1), cs_strlen(trim(ptr1))); } + } + cfg.gbox_dest_peers_num = n; + /* + Start sending GBox SMS + */ + if((cs_strlen(cfg.gbox_msg_txt) > 5)) + { + isbroadcast=strstr(local_gbox_dest_peers, "FFFF"); + if(isbroadcast == NULL) + { + n =0; + for (i = 0, ptr1 = strtok_r(local_gbox_dest_peers, ",", &saveptr1); (i < 4) && (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s=ptr1; + if ((n < GBOX_MAX_DEST_PEERS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { + gbox_dest_peers_tmp = a2i(ptr1, 4); + if(gbox_direct_send_gsms(gbox_dest_peers_tmp, cfg.gbox_msg_type, cfg.gbox_msg_txt)) { cs_log("GBOX message sent to[%04X] type[%d] text[%s] ", gbox_dest_peers_tmp, cfg.gbox_msg_type, cfg.gbox_msg_txt);} + n++; + } + } + tpl_addMsg(vars, "GBOX Send SMS: individual messages started."); + } + else + { + if(gbox_direct_send_gsms(0xFFFF, cfg.gbox_msg_type, cfg.gbox_msg_txt)) { cs_log("GBOX broadcast message sent type[%d] text[%s] ", cfg.gbox_msg_type, cfg.gbox_msg_txt);} + tpl_addMsg(vars, "GBOX Send SMS: broadcast started."); + } + } + else + { + cs_log("GBox SMS: destination peers or message text not specified or too short"); + tpl_addMsg(vars, "GBOX: Send SMS failed - error in input fields: dest peers or text message."); + } + } + + tpl_addVar(vars, TPLADD, "HOSTNAME", xml_encode(vars, cfg.gbox_hostname)); + char *value0 = mk_t_gbox_port(); + tpl_addVar(vars, TPLAPPEND, "PORT", value0); + free_mk_t(value0); + tpl_printf(vars, TPLADD, "MYGBOXPASSWORD", "%08X", cfg.gbox_password); + tpl_printf(vars, TPLADD, "MYGBOXID", "%04X", gbox_get_local_gbox_id()); + tpl_printf(vars, TPLADD, "GBOXRECONNECT", "%d", cfg.gbox_reconnect); + tpl_printf(vars, TPLADD, "GBOXMYVERS", "%02X", cfg.gbox_my_vers); + tpl_printf(vars, TPLAPPEND, "GBOXMYCPUAPI", "%02X", cfg.gbox_my_cpu_api); +#ifdef MODULE_CCCAM + if(cfg.cc_gbx_reshare_en == 1) { tpl_addVar(vars, TPLADD, "GBOXCCCRESHARE", "checked"); } + tpl_addVar(vars, TPLAPPEND, "CCCDEPENDINGCONFIG", tpl_getTpl(vars, "CCCAMRESHAREBIT")); +#endif + if(cfg.log_hello == 1) { tpl_addVar(vars, TPLADD, "GBOXLOGHELLO", "checked"); } + if(cfg.gsms_dis == 1) { tpl_addVar(vars, TPLADD, "GBOXGSMSDISABLE", "checked"); } + if(cfg.dis_attack_txt == 1) { tpl_addVar(vars, TPLADD, "GBOXDISATTACKTXT", "checked"); } + if(cfg.gbox_tmp_dir != NULL) { tpl_addVar(vars, TPLADD, "GBOXTMPDIR", cfg.gbox_tmp_dir); } + char *value1 = mk_t_gbox_proxy_card(); + tpl_addVar(vars, TPLAPPEND, "GBOXPROXYCARD", value1); + free_mk_t(value1); + char *value2 = mk_t_gbox_ignored_peer(); + tpl_addVar(vars, TPLAPPEND, "GBOXIGNOREDPEER", value2); + free_mk_t(value2); + char *value3 = mk_t_gbox_block_ecm(); + tpl_addVar(vars, TPLAPPEND, "GBOXBLOCKECM", value3); + free_mk_t(value3); + char *value4 = mk_t_accept_remm_peer(); + tpl_addVar(vars, TPLAPPEND, "GBOXACCEPTREMM", value4); + free_mk_t(value4); +/* + * GBOX SMS +*/ + tpl_addVar(vars, TPLADD, "GBOXSAVEGSMS", (cfg.gbox_save_gsms == 1) ? "checked" : ""); + if(cfg.gbox_msg_type == 0) + { + tpl_addVar(vars, TPLADD, "GBOXMSGTYPENORMAL", "selected"); + } + else if(cfg.gbox_msg_type == 1) + { + tpl_addVar(vars, TPLADD, "GBOXMSGTYPEOSD", "selected"); + } + char *gmsg_dest_peers = mk_t_gbox_dest_peers(); + tpl_addVar(vars, TPLADD, "GBOXMSGDESTPEERS", gmsg_dest_peers); + free_mk_t(gmsg_dest_peers); + tpl_addVar(vars, TPLADD, "GBOXMSGTXT", cfg.gbox_msg_txt); + + return tpl_getTpl(vars, "CONFIGGBOX"); +} +#endif + +#ifdef MODULE_RADEGAST +static char *send_oscam_config_radegast(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_RADEGAST); + + webif_save_config("radegast", vars, params); + + tpl_printf(vars, TPLADD, "PORT", "%d", cfg.rad_port); + if(IP_ISSET(cfg.rad_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.rad_srvip)); } + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, cfg.rad_usr)); + + char *value = mk_t_iprange(cfg.rad_allowed); + tpl_addVar(vars, TPLADD, "ALLOWED", value); + free_mk_t(value); + + return tpl_getTpl(vars, "CONFIGRADEGAST"); +} +#endif + +#ifdef MODULE_SCAM +static char *send_oscam_config_scam(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_SCAM); + + webif_save_config("scam", vars, params); + + tpl_printf(vars, TPLADD, "PORT", "%d", cfg.scam_port); + if(IP_ISSET(cfg.scam_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.scam_srvip)); } + + char *value = mk_t_iprange(cfg.scam_allowed); + tpl_addVar(vars, TPLADD, "ALLOWED", value); + free_mk_t(value); + + return tpl_getTpl(vars, "CONFIGSCAM"); +} +#endif + +#ifdef MODULE_STREAMRELAY +static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uriparams *params) +{ + char *value; + + setActiveSubMenu(vars, MNU_CFG_STREAMRELAY); + + webif_save_config("streamrelay", vars, params); + + tpl_printf(vars, TPLADD, "TMP", "STREAMRELAYENABLEDSELECTED%d", cfg.stream_relay_enabled); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_printf(vars, TPLADD, "STREAM_RELAY_PORT", "%d", cfg.stream_relay_port); + if(cfg.stream_relay_user) + { tpl_printf(vars, TPLADD, "STREAM_RELAY_USER", "%s", cfg.stream_relay_user); } + value = mk_t_caidtab(&cfg.stream_relay_ctab); + tpl_addVar(vars, TPLADD, "STREAM_RELAY_CTAB", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "STREAM_SOURCE_HOST", "%s", cfg.stream_source_host); + tpl_addVar(vars, TPLADD, "STREAM_CLIENT_SOURCE_HOST", (cfg.stream_client_source_host == 1) ? "checked" : ""); + tpl_printf(vars, TPLADD, "STREAM_SOURCE_PORT", "%d", cfg.stream_source_port); + if(cfg.stream_source_auth_user) + { tpl_printf(vars, TPLADD, "STREAM_SOURCE_AUTH_USER", "%s", cfg.stream_source_auth_user); } + if(cfg.stream_source_auth_password) + { tpl_printf(vars, TPLADD, "STREAM_SOURCE_AUTH_PASSWORD", "%s", cfg.stream_source_auth_password); } + + tpl_printf(vars, TPLADD, "STREAM_RELAY_BUFFER_TIME", "%d", cfg.stream_relay_buffer_time); + tpl_printf(vars, TPLADD, "STREAM_RELAY_RECONNECT_COUNT", "%d", cfg.stream_relay_reconnect_count); +#ifdef WITH_EMU + tpl_printf(vars, TPLADD, "TMP", "STREAMEMMENABLEDSELECTED%d", cfg.emu_stream_emm_enabled); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_printf(vars, TPLADD, "STREAM_ECM_DELAY", "%d", cfg.emu_stream_ecm_delay); +#endif + + tpl_printf(vars, TPLADD, "TMP", "STREAMCONFIGCLIENTSELECTED%d", cfg.stream_display_client); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_addVar(vars, TPLADD, "STREAM_REUSE_CLIENT", (cfg.stream_reuse_client == 1) ? "checked" : ""); +#ifdef WEBIF + tpl_addVar(vars, TPLADD, "STREAM_HIDE_CLIENT", (cfg.stream_hide_client == 1) ? "checked" : ""); +#endif + + return tpl_getTpl(vars, "CONFIGSTREAMRELAY"); +} +#endif + +#ifdef MODULE_CCCAM +static char *send_oscam_config_cccam(struct templatevars *vars, struct uriparams *params) +{ + + setActiveSubMenu(vars, MNU_CFG_CCCAM); + + if(strcmp(getParam(params, "button"), "Refresh list") == 0) + { + cs_log_dbg(D_TRACE, "Entitlements: Refresh Shares start"); +#ifdef MODULE_CCCSHARE + refresh_shares(); +#endif + cs_log_dbg(D_TRACE, "Entitlements: Refresh Shares finished"); + tpl_addMsg(vars, "Refresh Shares started"); + } + + webif_save_config("cccam", vars, params); + + if(streq(getParam(params, "action"), "execute") && !cfg.http_readonly) + { cc_update_nodeid(); } + + char *value = mk_t_cccam_port(); + tpl_addVar(vars, TPLAPPEND, "PORT", value); + free_mk_t(value); + + if(IP_ISSET(cfg.cc_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.cc_srvip)); } + + tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare); + + if(!strcmp((char *)cfg.cc_version, "2.0.11")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED0", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.1.1")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED1", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.1.2")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED2", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.1.3")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED3", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.1.4")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED4", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.2.0")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED5", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.2.1")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED6", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.3.0")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED7", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.3.1")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED8", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.3.2")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED9", "selected"); + } + + tpl_printf(vars, TPLADD, "UPDATEINTERVAL", "%d", cfg.cc_update_interval); + tpl_printf(vars, TPLADD, "RECV_TIMEOUT", "%u", cfg.cc_recv_timeout); + + tpl_addVar(vars, TPLADD, "STEALTH", (cfg.cc_stealth == 1) ? "checked" : ""); + + tpl_printf(vars, TPLADD, "NODEID", "%02X%02X%02X%02X%02X%02X%02X%02X", + cfg.cc_fixed_nodeid[0], cfg.cc_fixed_nodeid[1], cfg.cc_fixed_nodeid[2], cfg.cc_fixed_nodeid[3], + cfg.cc_fixed_nodeid[4], cfg.cc_fixed_nodeid[5], cfg.cc_fixed_nodeid[6], cfg.cc_fixed_nodeid[7]); + + tpl_printf(vars, TPLADD, "TMP", "MINIMIZECARDSELECTED%d", cfg.cc_minimize_cards); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "TMP", "RESHAREMODE%d", cfg.cc_reshare_services); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "TMP", "IGNRSHRSELECTED%d", cfg.cc_ignore_reshare); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_addVar(vars, TPLADD, "FORWARDORIGINCARD", (cfg.cc_forward_origin_card == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "KEEPCONNECTED", (cfg.cc_keep_connected == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADDONCE, "CONFIG_CONTROL", tpl_getTpl(vars, "CONFIGCCCAMCTRL")); + + return tpl_getTpl(vars, "CONFIGCCCAM"); +} +#endif + +static bool is_ext(const char *path, const char *ext) +{ + size_t lenpath = cs_strlen(path); + size_t lenext = cs_strlen(ext); + if(lenext > lenpath) + { return 0; } + return memcmp(path + lenpath - lenext, ext, lenext) == 0; +} + +static char *send_oscam_config_webif(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + + setActiveSubMenu(vars, MNU_CFG_WEBIF); + + webif_save_config("webif", vars, params); + + tpl_printf(vars, TPLADD, "HTTPPORT", "%s%d", cfg.http_use_ssl ? "+" : "", cfg.http_port); + if(IP_ISSET(cfg.http_srvip)) + { tpl_addVar(vars, TPLAPPEND, "SERVERIP", cs_inet_ntoa(cfg.http_srvip)); } + + tpl_addVar(vars, TPLADD, "HTTPUSER", cfg.http_user); + tpl_addVar(vars, TPLADD, "HTTPPASSWORD", cfg.http_pwd); + tpl_addVar(vars, TPLADD, "HTTPOSCAMLABEL", cfg.http_oscam_label); + + // css style selector + tpl_printf(vars, TPLADD, "CSSOPTIONS", "\t\t\t\t\t\t\n", + !cfg.http_css ? " selected" : ""); + + if(cfg.http_tpl) + { + char path[255]; + tpl_getFilePathInSubdir(cfg.http_tpl, "", "style", ".css", path, 255); + if(file_exists(path)) + tpl_printf(vars, TPLAPPEND, "CSSOPTIONS", "\t\t\t\t\t\t\n", + path, + cfg.http_css && strstr(cfg.http_css, path) ? " selected" : "", + path); + } + + struct dirent **namelist; + int count = scandir(cs_confdir, &namelist, 0, NULL); + + if( count >= 0 ) + { + for( i = 0 ; i < count; i++ ) + { + if(is_ext(namelist[i]->d_name, ".css")) + { + tpl_printf(vars, TPLAPPEND, "CSSOPTIONS", "\t\t\t\t\t\t\n", + cs_confdir, + namelist[i]->d_name, + cfg.http_css && strstr(cfg.http_css, namelist[i]->d_name) ? " selected" : "", + cs_confdir, namelist[i]->d_name); + } + free( namelist[i] ); + } + free(namelist); + } + + if(cfg.http_prepend_embedded_css) + { tpl_addVar(vars, TPLADD, "HTTPPREPENDEMBEDDEDCSS", "checked"); } + + tpl_addVar(vars, TPLADD, "HTTPHELPLANG", cfg.http_help_lang); + tpl_addVar(vars, TPLADD, "HTTPLOCALE", cfg.http_locale); + tpl_printf(vars, TPLADD, "HTTPEMMUCLEAN", "%d", cfg.http_emmu_clean); + tpl_printf(vars, TPLADD, "HTTPEMMSCLEAN", "%d", cfg.http_emms_clean); + tpl_printf(vars, TPLADD, "HTTPEMMGCLEAN", "%d", cfg.http_emmg_clean); + tpl_printf(vars, TPLADD, "HTTPREFRESH", "%d", cfg.http_refresh); + tpl_printf(vars, TPLADD, "HTTPPOLLREFRESH", "%d", cfg.poll_refresh); + tpl_addVar(vars, TPLADD, "HTTPTPL", cfg.http_tpl); + tpl_addVar(vars, TPLADD, "HTTPPICONPATH", cfg.http_piconpath); + tpl_addVar(vars, TPLADD, "HTTPSCRIPT", cfg.http_script); + tpl_addVar(vars, TPLADD, "HTTPJSCRIPT", cfg.http_jscript); +#ifndef WEBIF_JQUERY + tpl_addVar(vars, TPLADD, "HTTPEXTERNJQUERY", cfg.http_extern_jquery); +#endif + tpl_printf(vars, TPLADD, "HTTPPICONSIZE", "%d", cfg.http_picon_size); + + if(cfg.http_hide_idle_clients > 0) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + tpl_addVar(vars, TPLADD, "HTTPHIDETYPE", cfg.http_hide_type); + if(cfg.http_status_log > 0) { tpl_addVar(vars, TPLADD, "SHOWLOGCHECKED", "checked"); } + if(cfg.http_showpicons > 0) { tpl_addVar(vars, TPLADD, "SHOWPICONSCHECKED", "checked"); } + if(cfg.http_showmeminfo > 0) { tpl_addVar(vars, TPLADD, "SHOWMEMINFOCHECKED", "checked"); } + if(cfg.http_showuserinfo > 0) { tpl_addVar(vars, TPLADD, "SHOWUSERINFOCHECKED", "checked"); } + if(cfg.http_showreaderinfo > 0) { tpl_addVar(vars, TPLADD, "SHOWREADERINFOCHECKED", "checked"); } + if(cfg.http_showcacheexinfo > 0) { tpl_addVar(vars, TPLADD, "SHOWCACHEEXINFOCHECKED", "checked"); } + if(cfg.http_showloadinfo > 0) { tpl_addVar(vars, TPLADD, "SHOWLOADINFOCHECKED", "checked"); } + if(cfg.http_showecminfo > 0) { tpl_addVar(vars, TPLADD, "SHOWECMINFOCHECKED", "checked"); } + + char *value = mk_t_iprange(cfg.http_allowed); + tpl_addVar(vars, TPLADD, "HTTPALLOW", value); + free_mk_t(value); + + for(i = 0; i < MAX_HTTP_DYNDNS; i++) + { + if(cfg.http_dyndns[i][0]) + { + tpl_addVar(vars, TPLAPPEND, "HTTPDYNDNS", i > 0 ? "," : ""); + tpl_addVar(vars, TPLAPPEND, "HTTPDYNDNS", (char *)cfg.http_dyndns[i]); + } + } + + tpl_addVar(vars, TPLADD, "HTTPSAVEFULLSELECT", (cfg.http_full_cfg == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "HTTPOVERWRITEBAKFILE", (cfg.http_overwrite_bak_file == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "HTTPREADONLY", (cfg.http_readonly == 1) ? "checked" : ""); + + +#ifdef WITH_SSL + if(cfg.http_cert != NULL) { tpl_addVar(vars, TPLADD, "HTTPCERT", cfg.http_cert); } + tpl_addVar(vars, TPLADD, "HTTPFORCESECUREMODESELECT", (cfg.https_force_secure_mode == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "HTTPAUTOCREATECERTSELECT", (cfg.https_auto_create_cert == 1) ? "checked" : ""); +#endif + +#ifndef WEBIF_JQUERY + tpl_addVar(vars, TPLADDONCE, "CONFIGWEBIFJQUERY", tpl_getTpl(vars, "CONFIGWEBIFJQUERYBIT")); +#endif + + tpl_printf(vars, TPLADD, "AULOW", "%d", cfg.aulow); + tpl_printf(vars, TPLADD, "HIDECLIENTTO", "%d", cfg.hideclient_to); + + return tpl_getTpl(vars, "CONFIGWEBIF"); +} + +#ifdef LCDSUPPORT +static char *send_oscam_config_lcd(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_LCD); + + webif_save_config("lcd", vars, params); + + tpl_addVar(vars, TPLADD, "ENABLELCDSELECTED", (cfg.enablelcd == 1) ? "checked" : ""); + + if(cfg.lcd_output_path != NULL) + { tpl_addVar(vars, TPLADD, "LCDOUTPUTPATH", cfg.lcd_output_path); } + + tpl_addVar(vars, TPLADD, "LCDHIDEIDLE", (cfg.lcd_hide_idle == 1) ? "checked" : ""); + + tpl_printf(vars, TPLADD, "LCDREFRESHINTERVAL", "%d", cfg.lcd_write_intervall); + + return tpl_getTpl(vars, "CONFIGLCD"); +} +#endif + +#ifdef MODULE_MONITOR +static char *send_oscam_config_monitor(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_MONITOR); + + webif_save_config("monitor", vars, params); + + tpl_printf(vars, TPLADD, "MONPORT", "%d", cfg.mon_port); + if(IP_ISSET(cfg.mon_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.mon_srvip)); } + + tpl_printf(vars, TPLADD, "AULOW", "%d", cfg.aulow); + tpl_printf(vars, TPLADD, "HIDECLIENTTO", "%d", cfg.hideclient_to); + + char *value = mk_t_iprange(cfg.mon_allowed); + tpl_addVar(vars, TPLADD, "NOCRYPT", value); + free_mk_t(value); + + //Monlevel selector + tpl_printf(vars, TPLADD, "TMP", "MONSELECTED%d", cfg.mon_level); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + return tpl_getTpl(vars, "CONFIGMONITOR"); +} +#endif + +#ifdef MODULE_SERIAL +static char *send_oscam_config_serial(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_SERIAL); + + webif_save_config("serial", vars, params); + + if(cfg.ser_device) + { + char sdevice[cs_strlen(cfg.ser_device)]; + cs_strncpy(sdevice, cfg.ser_device, sizeof(sdevice)); + char *ptr, *saveptr1 = NULL; + char delimiter[2]; + delimiter[0] = 1; + delimiter[1] = '\0'; + for(ptr = strtok_r(sdevice, delimiter, &saveptr1); ptr; ptr = strtok_r(NULL, delimiter, &saveptr1)) + { + tpl_addVar(vars, TPLADD, "SERIALDEVICE", xml_encode(vars, ptr)); + tpl_addVar(vars, TPLAPPEND, "DEVICES", tpl_getTpl(vars, "CONFIGSERIALDEVICEBIT")); + } + } + + tpl_addVar(vars, TPLADD, "SERIALDEVICE", ""); + tpl_addVar(vars, TPLAPPEND, "DEVICES", tpl_getTpl(vars, "CONFIGSERIALDEVICEBIT")); + + return tpl_getTpl(vars, "CONFIGSERIAL"); +} +#endif + +#ifdef HAVE_DVBAPI +extern const char *boxdesc[]; + +static char *send_oscam_config_dvbapi(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + + setActiveSubMenu(vars, MNU_CFG_DVBAPI); + + webif_save_config("dvbapi", vars, params); + + if(cfg.dvbapi_enabled > 0) + { tpl_addVar(vars, TPLADD, "ENABLEDCHECKED", "checked"); } + + if(cfg.dvbapi_au > 0) + { tpl_addVar(vars, TPLADD, "AUCHECKED", "checked"); } + + if(cfg.dvbapi_delayer > 0) + { tpl_printf(vars, TPLADD, "DELAYER", "%d", cfg.dvbapi_delayer); } + + tpl_printf(vars, TPLADD, "BOXTYPE", "\n", cfg.dvbapi_boxtype == 0 ? " selected" : ""); + for(i = 1; i <= BOXTYPES; i++) + { + tpl_printf(vars, TPLAPPEND, "BOXTYPE", "%s\n", cfg.dvbapi_boxtype == i ? " selected" : "", boxdesc[i]); + } + + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, cfg.dvbapi_usr)); + + //PMT Mode + tpl_printf(vars, TPLADD, "TMP", "PMTMODESELECTED%d", cfg.dvbapi_pmtmode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + //Request Mode + tpl_printf(vars, TPLADD, "TMP", "REQMODESELECTED%d", cfg.dvbapi_requestmode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + //ecminfo_file + if(cfg.dvbapi_ecminfo_file > 0) + { tpl_addVar(vars, TPLADD, "ECMINFOFILECHECKED", "checked"); } + + //ecminfo_type + tpl_printf(vars, TPLADD, "TMP", "ECMINFOTYPESELECTED%d", cfg.dvbapi_ecminfo_type); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + //read_sdt + tpl_printf(vars, TPLADD, "TMP", "READSDTSELECTED%d", cfg.dvbapi_read_sdt); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + +#ifdef WITH_EXTENDED_CW + //extended_cw_api + tpl_printf(vars, TPLADD, "TMP", "EXTENDEDCWAPISELECTED%d", cfg.dvbapi_extended_cw_api); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); +#endif + + //write_sdt_prov + if(cfg.dvbapi_write_sdt_prov > 0) + { tpl_addVar(vars, TPLADD, "WRITESDTPROVCHECKED", "checked"); } + +#ifdef MODULE_STREAMRELAY + //demuxer_fix + if(cfg.dvbapi_demuxer_fix > 0) + { tpl_addVar(vars, TPLADD, "DEMUXERFIXCHECKED", "checked"); } +#endif + + //TCP listen port + if(cfg.dvbapi_listenport > 0) + { tpl_printf(vars, TPLADD, "LISTENPORT", "%d", cfg.dvbapi_listenport); } + + if(IP_ISSET(cfg.dvbapi_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.dvbapi_srvip)); } + + return tpl_getTpl(vars, "CONFIGDVBAPI"); +} +#endif + +#ifdef CS_ANTICASC +static char *send_oscam_config_anticasc(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_ANTICASC); + + webif_save_config("anticasc", vars, params); + + if(cfg.ac_enabled > 0) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + tpl_printf(vars, TPLADD, "NUMUSERS", "%d", cfg.ac_users); + tpl_printf(vars, TPLADD, "SAMPLETIME", "%d", cfg.ac_stime); + tpl_printf(vars, TPLADD, "SAMPLES", "%d", cfg.ac_samples); + + tpl_printf(vars, TPLADD, "TMP", "PENALTY%d", cfg.ac_penalty); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + if(cfg.ac_logfile) + { tpl_addVar(vars, TPLADD, "ACLOGFILE", cfg.ac_logfile); } + tpl_printf(vars, TPLADD, "FAKEDELAY", "%d", cfg.ac_fakedelay); + tpl_printf(vars, TPLADD, "DENYSAMPLES", "%d", cfg.ac_denysamples); + + if(cfg.acosc_enabled == 1) + { tpl_addVar(vars, TPLADD, "ACOSC_CHECKED", "checked"); } + tpl_printf(vars, TPLADD, "ACOSC_MAX_ECMS_PER_MINUTE", "%d", cfg.acosc_max_ecms_per_minute); + tpl_printf(vars, TPLADD, "ACOSC_MAX_ACTIVE_SIDS", "%d", cfg.acosc_max_active_sids); + tpl_printf(vars, TPLADD, "ACOSC_ZAP_LIMIT", "%d", cfg.acosc_zap_limit); + tpl_printf(vars, TPLADD, "TMP", "ACOSC_PENALTY%d", cfg.acosc_penalty); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_printf(vars, TPLADD, "ACOSC_PENALTY_DURATION", "%d", cfg.acosc_penalty_duration); + tpl_printf(vars, TPLADD, "ACOSC_DELAY", "%d", cfg.acosc_delay); + + return tpl_getTpl(vars, "CONFIGANTICASC"); +} +#endif + +static char *send_oscam_config(struct templatevars *vars, struct uriparams *params) +{ + + setActiveMenu(vars, MNU_CONFIG); + + char *part = getParam(params, "part"); + if(!strcmp(part, "webif")) { return send_oscam_config_webif(vars, params); } +#ifdef MODULE_MONITOR + else if(!strcmp(part, "monitor")) { return send_oscam_config_monitor(vars, params); } +#endif +#ifdef LCDSUPPORT + else if(!strcmp(part, "lcd")) { return send_oscam_config_lcd(vars, params); } +#endif +#ifdef MODULE_CAMD33 + else if(!strcmp(part, "camd33")) { return send_oscam_config_camd33(vars, params); } +#endif +#ifdef MODULE_CAMD35 + else if(!strcmp(part, "camd35")) { return send_oscam_config_camd35(vars, params); } +#endif +#ifdef MODULE_CAMD35_TCP + else if(!strcmp(part, "camd35tcp")) { return send_oscam_config_camd35tcp(vars, params); } +#endif + else if(!strcmp(part, "cache")) { return send_oscam_config_cache(vars, params); } +#ifdef MODULE_NEWCAMD + else if(!strcmp(part, "newcamd")) { return send_oscam_config_newcamd(vars, params); } +#endif +#ifdef MODULE_RADEGAST + else if(!strcmp(part, "radegast")) { return send_oscam_config_radegast(vars, params); } +#endif +#ifdef MODULE_SCAM + else if(!strcmp(part, "scam")) { return send_oscam_config_scam(vars, params); } +#endif +#ifdef MODULE_STREAMRELAY + else if(!strcmp(part, "streamrelay")) { return send_oscam_config_streamrelay(vars, params); } +#endif +#ifdef MODULE_CCCAM + else if(!strcmp(part, "cccam")) { return send_oscam_config_cccam(vars, params); } +#endif +#ifdef MODULE_GBOX + else if(!strcmp(part, "gbox")) { return send_oscam_config_gbox(vars, params); } +#endif +#ifdef HAVE_DVBAPI + else if(!strcmp(part, "dvbapi")) { return send_oscam_config_dvbapi(vars, params); } +#endif +#ifdef CS_ANTICASC + else if(!strcmp(part, "anticasc")) { return send_oscam_config_anticasc(vars, params); } +#endif +#ifdef MODULE_SERIAL + else if(!strcmp(part, "serial")) { return send_oscam_config_serial(vars, params); } +#endif +#ifdef WITH_LB + else if(!strcmp(part, "loadbalancer")) { return send_oscam_config_loadbalancer(vars, params); } +#endif + else { return send_oscam_config_global(vars, params); } +} + +static void inactivate_reader(struct s_reader *rdr) +{ + struct s_client *cl = rdr->client; + if(cl) + { kill_thread(cl); } +} + +static bool picon_exists(char *name) +{ + char picon_name[255], path[255]; + char *tpl_path; + tpl_path = cfg.http_piconpath ? cfg.http_piconpath : cfg.http_tpl; + if(!tpl_path) + { return false; } + snprintf(picon_name, sizeof(picon_name) - 1, "IC_%s", name); + return cs_strlen(tpl_getTplPath(picon_name, tpl_path, path, sizeof(path) - 1)) && file_exists(path); +} + +static void clear_rdr_stats(struct s_reader *rdr) +{ + int i; + for(i = 0; i < 4; i++) + { + rdr->emmerror[i] = 0; + rdr->emmwritten[i] = 0; + rdr->emmskipped[i] = 0; + rdr->emmblocked[i] = 0; + } + rdr->ecmsok = 0; +#ifdef CS_CACHEEX_AIO + rdr->ecmsoklg = 0; +#endif + rdr->ecmsnok = 0; + rdr->ecmstout = 0; + rdr->ecmshealthok = 0; +#ifdef CS_CACHEEX_AIO + rdr->ecmshealthoklg = 0; +#endif + rdr->ecmshealthnok = 0; + rdr->ecmshealthtout = 0; + rdr->ecmsfilteredhead = 0; + rdr->ecmsfilteredlen = 0; +} + +static void clear_all_rdr_stats(void) +{ + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + clear_rdr_stats(rdr); + } +} + +static char *send_oscam_reader(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + struct s_reader *rdr; + int32_t i; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + char *status; + + if(!apicall) { setActiveMenu(vars, MNU_READERS); } + if(!apicall) + { + if(strcmp(getParam(params, "action"), "resetallrdrstats") == 0) + { + clear_all_rdr_stats(); + } + } + + tpl_addVar(vars, TPLADD, "READERACTIONCOLS", config_enabled(WITH_LB) ? "6" : "5"); + + if(strcmp(getParam(params, "action"), "resetuserstats") == 0) + { + clear_info_clients_stats(); + } + if(strcmp(getParam(params, "action"), "resetreaderstats") == 0) + { + clear_info_readers_stats(); + } + if(strcmp(getParam(params, "action"), "reloadreaders") == 0) + { + if(!cfg.http_readonly) + { + refresh_oscam(REFR_READERS); + refresh_oscam(REFR_ACCOUNTS); + } + } + if((strcmp(getParam(params, "action"), "disable") == 0) || (strcmp(getParam(params, "action"), "enable") == 0)) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. Enabling or disabling readers is not possible!"); + } + else + { + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + if(strcmp(getParam(params, "action"), "enable") == 0) + { + if(!rdr->enable) + { + rdr->enable = 1; + } + } + else + { + if(rdr->enable) + { + rdr->enable = 0; + } + } + if(rdr->typ != R_GBOX) + { + restart_cardreader(rdr, 1); + } +#ifdef MODULE_GBOX + else + { + restart_gbox_peer(rdr->label, 0, 0); + cs_log("gbox -> you must restart oscam so that setting becomes effective"); + } +#endif + + cs_log("reader %s %s by WebIf", rdr->label, rdr->enable == 1 ? "enabled":"disabled"); + + if(write_server() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + +#ifdef MODULE_GBOX + if(!is_network_reader(rdr) && !rdr->enable) + { + gbx_local_card_stat(LOCALCARDDISABLED, 0); + } +#endif + } + } + } + + if(strcmp(getParam(params, "action"), "delete") == 0) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. No deletion will be made!"); + } + else + { + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + inactivate_reader(rdr); + ll_remove(configured_readers, rdr); + + free_reader(rdr); + + if(write_server() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + } + } + } + + if(strcmp(getParam(params, "action"), "reread") == 0) + { + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + struct s_client *cl = rdr->client; + //reset the counters + for(i = 0; i < 4; i++) + { + rdr->emmerror[i] = 0; + rdr->emmwritten[i] = 0; + rdr->emmskipped[i] = 0; + rdr->emmblocked[i] = 0; + } + + if(rdr->enable == 1 && cl && cl->typ == 'r') + { + add_job(cl, ACTION_READER_CARDINFO, NULL, 0); + } + } + } + + LL_ITER itr = ll_iter_create(configured_readers); + + if(!apicall) + { + for(i = 0, rdr = ll_iter_next(&itr); rdr && rdr->label[0]; rdr = ll_iter_next(&itr), i++) { ; } + tpl_printf(vars, TPLADD, "NEXTREADER", "Reader-%d", i); //Next Readername + } + + int jsondelimiter = 0; + int existing_insert = 0; + + int32_t total_readers = 0; + int32_t disabled_readers = 0; + int32_t active_readers = 0; + int32_t connected_readers = 0; + + ll_iter_reset(&itr); //going to iterate all configured readers + while((rdr = ll_iter_next(&itr))) + { +#ifdef CS_CACHEEX_AIO + const char *proto = reader_get_type_desc(rdr, 0); +#endif + struct s_client *cl = rdr->client; + if(rdr->label[0] && rdr->typ) + { +#ifdef CS_CACHEEX_AIO + char *new_proto; +#if defined(MODULE_CAMD35) || defined (MODULE_CAMD35_TCP) + if(rdr->cacheex.feature_bitfield || (cl && cl->c35_extmode > 1)) +#else + if(rdr->cacheex.feature_bitfield) +#endif + { + const char *aio_suffix = " (cx-aio)"; + + if(cs_malloc(&new_proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + if (!cs_strncat(new_proto, (char *)proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + cs_log("FIXME!"); + } + if (!cs_strncat(new_proto, (char *)aio_suffix, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + cs_log("FIXME!"); + } + } + } +#endif + total_readers += 1; + + // used for API and WebIf + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + + MD5((uint8_t *)rdr->label, cs_strlen(rdr->label), md5tmp); + int z; + tpl_addVar(vars, TPLADD, "LABELMD5","id_"); + for (z = 0; z < MD5_DIGEST_LENGTH; z++) + { + tpl_printf(vars, TPLAPPEND, "LABELMD5", "%02x", md5tmp[z]); + } +#ifdef MODULE_GBOX + if(apicall) + { + tpl_addVar(vars, TPLADD, "LASTGSMS", ""); + tpl_addVar(vars, TPLADD, "LASTGSMS", rdr->last_gsms); + } +#endif + if(apicall) + { + tpl_printf(vars, TPLADD, "PICONENABLED", "%d", cfg.http_showpicons?1:0); + } + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, rdr->label)); + if(!existing_insert) + { + tpl_printf(vars, TPLADD, "EXISTING_INS", "'%s'", urlencode(vars, rdr->label)); + existing_insert++; + }else + { + tpl_printf(vars, TPLAPPEND, "EXISTING_INS", ",'%s'", urlencode(vars, rdr->label)); + } + tpl_addVar(vars, TPLADD, "READERCLASS", rdr->enable ? "enabledreader" : "disabledreader"); + + if(rdr->enable) { active_readers += 1; } + else { disabled_readers += 1; } + + if(rdr->tcp_connected) + { + connected_readers += 1; + +#ifdef CS_CACHEEX_AIO + if(rdr->cacheex.feature_bitfield) + { + tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", (const char*)new_proto); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", (const char*)new_proto); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", new_proto); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char*)new_proto); + } + } + + if(rdr->cacheex.feature_bitfield & 32) + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", rdr->cacheex.aio_version); + else if(cl->reader->cacheex.feature_bitfield) + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", "[cx-aio < 9.2.3]"); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", proto); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", proto); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", proto); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char *)proto); + } + } + } +#else + tpl_addVar(vars, TPLADD, "CLIENTPROTO", reader_get_type_desc(rdr, 0)); + tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", reader_get_type_desc(rdr, 0)); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", reader_get_type_desc(rdr, 0)); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s", reader_get_type_desc(rdr, 0)); + } + } +#endif + switch(rdr->card_status) + { + case CARD_INSERTED: + status = "online"; + tpl_addVar(vars, TPLADD, "RSTATUS", status); + tpl_addVar(vars, TPLADD, "READERCLASS", "r_connected"); + break; + + case NO_CARD: + case UNKNOWN: + case READER_DEVICE_ERROR: + case CARD_NEED_INIT: + case CARD_FAILURE: + default: + status = "connected"; + tpl_addVar(vars, TPLADD, "RSTATUS", status); + tpl_addVar(vars, TPLADD, "READERCLASS", "r_undefined"); + break; + } + + tpl_addVar(vars, TPLADD, "READERIP", cs_inet_ntoa(rdr->client->ip)); + } + else + { + /* default initial values */ + tpl_addVar(vars, TPLADDONCE, "RSTATUS", "offline"); + tpl_addVar(vars, TPLADDONCE, "READERIP", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOSORT", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOTITLE", ""); + tpl_addVar(vars, TPLADDONCE, "PROTOICON", ""); + + if(!is_network_reader(rdr) && rdr->enable) + { + switch(rdr->card_status) + { + case CARD_INSERTED: + status = "active"; + tpl_addVar(vars, TPLADD, "RSTATUS", status); + tpl_addVar(vars, TPLADD, "READERCLASS", "r_connected"); + break; + + case NO_CARD: + case UNKNOWN: + case READER_DEVICE_ERROR: + case CARD_NEED_INIT: + case CARD_FAILURE: + default: + status = "connected"; + tpl_addVar(vars, TPLADD, "RSTATUS", status); + tpl_addVar(vars, TPLADD, "READERCLASS", "r_undefined"); + break; + } + + tpl_addVar(vars, TPLADD, "CLIENTPROTO", reader_get_type_desc(rdr, 0)); + tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", reader_get_type_desc(rdr, 0)); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", reader_get_type_desc(rdr, 0)); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s", reader_get_type_desc(rdr, 0)); + } + } + } + } + + if(rdr->description) + tpl_printf(vars, TPLADD, "DESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, rdr->description)); + else + tpl_addVar(vars, TPLADD, "DESCRIPTION", ""); + + if(cfg.http_showpicons && !apicall) + { + tpl_addVar(vars, TPLADD, "READERBIT", tpl_getTpl(vars, picon_exists(xml_encode(vars, rdr->label)) ? "READERNAMEBIT" : "READERNOICON")); +#ifdef CS_CACHEEX_AIO + if(rdr->cacheex.feature_bitfield) + { + tpl_addVar(vars, TPLADD, "CLIENTPROTO", picon_exists(xml_encode(vars, (const char*)new_proto)) ? tpl_getTpl(vars, "READERCTYPBIT") : tpl_getTpl(vars, "READERCTYPNOICON")); + } + else + { +#endif + tpl_addVar(vars, TPLADD, "CLIENTPROTO", picon_exists(xml_encode(vars, reader_get_type_desc(rdr, 0))) ? tpl_getTpl(vars, "READERCTYPBIT") : tpl_getTpl(vars, "READERCTYPNOICON")); +#ifdef CS_CACHEEX_AIO + } +#endif + } + else + tpl_addVar(vars, TPLADD, "READERBIT", tpl_getTpl(vars, "READERLABEL")); + + char *value = mk_t_group(rdr->grp); + tpl_addVar(vars, TPLADD, "GROUPS", value); + free_mk_t(value); + tpl_printf(vars, TPLADD, "EMMERRORUK", PRINTF_LOCAL_D, rdr->emmerror[UNKNOWN]); + tpl_printf(vars, TPLADD, "EMMERRORG", PRINTF_LOCAL_D, rdr->emmerror[GLOBAL]); + tpl_printf(vars, TPLADD, "EMMERRORS", PRINTF_LOCAL_D, rdr->emmerror[SHARED]); + tpl_printf(vars, TPLADD, "EMMERRORUQ", PRINTF_LOCAL_D, rdr->emmerror[UNIQUE]); + + tpl_printf(vars, TPLADD, "EMMWRITTENUK", PRINTF_LOCAL_D, rdr->emmwritten[UNKNOWN]); + tpl_printf(vars, TPLADD, "EMMWRITTENG", PRINTF_LOCAL_D, rdr->emmwritten[GLOBAL]); + tpl_printf(vars, TPLADD, "EMMWRITTENS", PRINTF_LOCAL_D, rdr->emmwritten[SHARED]); + tpl_printf(vars, TPLADD, "EMMWRITTENUQ", PRINTF_LOCAL_D, rdr->emmwritten[UNIQUE]); + + tpl_printf(vars, TPLADD, "EMMSKIPPEDUK", PRINTF_LOCAL_D, rdr->emmskipped[UNKNOWN]); + tpl_printf(vars, TPLADD, "EMMSKIPPEDG", PRINTF_LOCAL_D, rdr->emmskipped[GLOBAL]); + tpl_printf(vars, TPLADD, "EMMSKIPPEDS", PRINTF_LOCAL_D, rdr->emmskipped[SHARED]); + tpl_printf(vars, TPLADD, "EMMSKIPPEDUQ", PRINTF_LOCAL_D, rdr->emmskipped[UNIQUE]); + + tpl_printf(vars, TPLADD, "EMMBLOCKEDUK", PRINTF_LOCAL_D, rdr->emmblocked[UNKNOWN]); + tpl_printf(vars, TPLADD, "EMMBLOCKEDG", PRINTF_LOCAL_D, rdr->emmblocked[GLOBAL]); + tpl_printf(vars, TPLADD, "EMMBLOCKEDS", PRINTF_LOCAL_D, rdr->emmblocked[SHARED]); + tpl_printf(vars, TPLADD, "EMMBLOCKEDUQ", PRINTF_LOCAL_D, rdr->emmblocked[UNIQUE]); + + tpl_printf(vars, TPLADD, "ECMSOK", PRINTF_LOCAL_D, rdr->ecmsok); + tpl_printf(vars, TPLADD, "ECMSOKREL", " (%.2f %%)", rdr->ecmshealthok); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "ECMSOKLG", PRINTF_LOCAL_D, rdr->ecmsoklg); + tpl_printf(vars, TPLADD, "ECMSOKLGREL", " (%.2f %%)", rdr->ecmshealthoklg); +#endif + tpl_printf(vars, TPLADD, "ECMSNOK", PRINTF_LOCAL_D, rdr->ecmsnok); + tpl_printf(vars, TPLADD, "ECMSNOKREL", " (%.2f %%)",rdr->ecmshealthnok); + tpl_printf(vars, TPLADD, "ECMSTOUT", PRINTF_LOCAL_D, rdr->ecmstout); + tpl_printf(vars, TPLADD, "ECMSTOUTREL", " (%.2f %%)",rdr->ecmshealthtout); + tpl_printf(vars, TPLADD, "ECMSFILTEREDHEAD", PRINTF_LOCAL_D, rdr->ecmsfilteredhead); + tpl_printf(vars, TPLADD, "ECMSFILTEREDLEN", PRINTF_LOCAL_D, rdr->ecmsfilteredlen); +#ifdef WITH_LB + tpl_printf(vars, TPLADD, "LBWEIGHT", "%d", rdr->lb_weight); +#endif + if(!is_network_reader(rdr)) //reader is physical + { + tpl_addVar(vars, TPLADD, "REFRICO", "image?i=ICREF"); + tpl_addVar(vars, TPLADD, "READERREFRESH", tpl_getTpl(vars, "READERREFRESHBIT")); + tpl_addVar(vars, TPLADD, "ENTICO", "image?i=ICENT"); + tpl_addVar(vars, TPLADD, "ENTITLEMENT", tpl_getTpl(vars, "READERENTITLEBIT")); + } + else + { + tpl_addVar(vars, TPLADD, "READERREFRESH", ""); + if(rdr->typ == R_CCCAM) + { + tpl_addVar(vars, TPLADD, "ENTICO", "image?i=ICENT"); + tpl_addVar(vars, TPLADD, "ENTITLEMENT", tpl_getTpl(vars, "READERENTITLEBIT")); + } + else + { + tpl_addVar(vars, TPLADD, "ENTITLEMENT", ""); + } + } + + if(rdr->enable == 0) + { + tpl_addVar(vars, TPLADD, "SWITCHICO", "image?i=ICENA"); + tpl_addVar(vars, TPLADD, "SWITCHTITLE", "Enable"); + tpl_addVar(vars, TPLADD, "SWITCH", "enable"); + tpl_addVar(vars, TPLADD, "WRITEEMM", ""); + } + else + { + tpl_addVar(vars, TPLADD, "SWITCHICO", "image?i=ICDIS"); + tpl_addVar(vars, TPLADD, "SWITCHTITLE", "Disable"); + tpl_addVar(vars, TPLADD, "SWITCH", "disable"); + + tpl_addVar(vars, TPLADD, "EMMICO", "image?i=ICEMM"); + tpl_addVar(vars, TPLADD, "WRITEEMM", tpl_getTpl(vars, "READERWRITEEMMBIT")); + } + + if(!apicall) + { + // Add to WebIf Template +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLAPPEND, "READERLIST", tpl_getTpl(vars, "READERSBITAIO")); +#else + tpl_addVar(vars, TPLAPPEND, "READERLIST", tpl_getTpl(vars, "READERSBIT")); +#endif + } + else + { + + // used only for API + tpl_addVar(vars, TPLADD, "APIREADERENABLED", !rdr->enable ? "0" : "1"); + if(cl) + { + tpl_printf(vars, TPLADD, "APIREADERTYPE", "%c", cl->typ ? cl->typ : 'x'); + } + + if(apicall==1) + { + // Add to API Template + tpl_addVar(vars, TPLAPPEND, "APIREADERLIST", tpl_getTpl(vars, "APIREADERSBIT")); + } + if(apicall==2) + { + tpl_printf(vars, TPLAPPEND, "APIREADERLIST","%s%s",jsondelimiter?",":"", tpl_getTpl(vars, "JSONREADERBIT")); + jsondelimiter++; + } + } +#ifdef CS_CACHEEX_AIO + if(rdr->cacheex.feature_bitfield) + { + free(new_proto); + } +#endif + } + } + + tpl_printf(vars, TPLADD, "TOTAL_READERS", "%d", total_readers); + tpl_printf(vars, TPLADD, "TOTAL_DISABLED_READERS", "%d", disabled_readers); + tpl_printf(vars, TPLADD, "TOTAL_ACTIVE_READERS", "%d", active_readers); + tpl_printf(vars, TPLADD, "TOTAL_CONNECTED_READERS", "%d", connected_readers); + + //CM info + tpl_addVar(vars, TPLADD, "DISPLAYUSERINFO", "hidden"); // no userinfo in readers + set_ecm_info(vars); + + if(!apicall) + { +#ifdef MODULE_CAMD33 + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_CAMD35 + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_CAMD35_TCP + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_NEWCAMD + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_CCCAM + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_GBOX + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_RADEGAST + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_SERIAL + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_CONSTCW + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_SCAM + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif + + for(i = 0; cardreaders[i]; i++) + { + tpl_printf(vars, TPLAPPEND, "ADDPROTOCOL", "\n", xml_encode(vars, cardreaders[i]->desc)); + } +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "READERSAIO"); +#else + return tpl_getTpl(vars, "READERS"); +#endif + } + else + { + if(apicall == 1) + { + return tpl_getTpl(vars, "APIREADERS"); + } + else + { + return tpl_getTpl(vars, "JSONREADER"); + } + } +} + +static char *send_oscam_reader_config(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + int32_t apicall = 0; + char *reader_ = getParam(params, "label"); + char *value; + + struct s_reader *rdr; + + if(!apicall) { setActiveMenu(vars, MNU_READERS); } + + if(strcmp(getParam(params, "action"), "Add") == 0) + { + // Add new reader + struct s_reader *newrdr; + if(!cs_malloc(&newrdr, sizeof(struct s_reader))) { return "0"; } + for(i = 0; i < (*params).paramcount; ++i) + { + if(strcmp((*params).params[i], "action")) + { chk_reader((*params).params[i], (*params).values[i], newrdr); } + } + module_reader_set(newrdr); + reader_ = newrdr->label; + reader_set_defaults(newrdr); + newrdr->enable = 0; // do not start the reader because must configured before + ll_append(configured_readers, newrdr); + tpl_addMsg(vars, "New Reader has been added with default settings"); + } + else if(strcmp(getParam(params, "action"), "Save") == 0) + { + + rdr = get_reader_by_label(getParam(params, "label")); + if(!rdr) + { return NULL; } + //if (is_network_reader(rdr)) + // inactivate_reader(rdr); //Stop reader before reinitialization + char servicelabels[1024] = ""; + char servicelabelslb[1024] = ""; + + for(i = 0; i < (*params).paramcount; ++i) + { + if((strcmp((*params).params[i], "reader")) && (strcmp((*params).params[i], "action"))) + { + if(!strcmp((*params).params[i], "services")) + { snprintf(servicelabels + cs_strlen(servicelabels), sizeof(servicelabels) - cs_strlen(servicelabels), "%s,", (*params).values[i]); } + else if(!strcmp((*params).params[i], "lb_whitelist_services")) + { snprintf(servicelabelslb + cs_strlen(servicelabelslb), sizeof(servicelabelslb) - cs_strlen(servicelabelslb), "%s,", (*params).values[i]); } + else + /*if(cs_strlen((*params).values[i]) > 0)*/ + { chk_reader((*params).params[i], (*params).values[i], rdr); } + } + //printf("param %s value %s\n",(*params).params[i], (*params).values[i]); + } + chk_reader("services", servicelabels, rdr); + chk_reader("lb_whitelist_services", servicelabelslb, rdr); + + if(is_network_reader(rdr) || rdr->typ == R_EMU) //physical readers make trouble if re-started + { + if(rdr) + { + if(rdr->typ != R_GBOX) + { + restart_cardreader(rdr, 1); + } +#ifdef MODULE_GBOX + else + { + //cs_log("SAVE - single gbox reader %s restarted by WebIf", rdr->label); + restart_gbox_peer(rdr->label, 0, 0); + } +#endif + } + } + + if(write_server() != 0) { tpl_addMsg(vars, "Write Config failed!"); } else { tpl_addMsg(vars, "Reader config updated and saved"); } + + } + + rdr = get_reader_by_label(reader_); + if(!rdr) + { return NULL; } + + // Label, Description + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "DESCRIPTION", xml_encode(vars, rdr->description)); + + // enabled + if(!apicall) + { + tpl_addVar(vars, TPLADD, "ENABLED", (rdr->enable == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "ENABLEDVALUE", (rdr->enable == 1) ? "1" : "0"); + } + + tpl_addVar(vars, TPLADD, "PASSWORD", xml_encode(vars, rdr->r_pwd)); + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, rdr->r_usr)); + tpl_addVar(vars, TPLADD, "PASS", xml_encode(vars, rdr->r_pwd)); + + // Key Newcamd + for(i = 0; i < (int32_t)sizeof(rdr->ncd_key); i++) + { tpl_printf(vars, TPLAPPEND, "NCD_KEY", "%02X", rdr->ncd_key[i]); } + + // Pincode + tpl_addVar(vars, TPLADD, "PINCODE", rdr->pincode); + + // Emmfile Path + if(rdr->emmfile) { tpl_addVar(vars, TPLADD, "EMMFILE", (char *)rdr->emmfile); } + + // Inactivity timeout + tpl_printf(vars, TPLADD, "INACTIVITYTIMEOUT", "%d", rdr->tcp_ito); + + // Receive timeout + tpl_printf(vars, TPLADD, "RECEIVETIMEOUT", "%d", rdr->tcp_rto); + + // keepalive + tpl_addVar(vars, TPLADD, "RDRKEEPALIVE", (rdr->keepalive == 1) ? "checked" : ""); + + // Connect on init (newcamd) + if(!apicall) + { + tpl_addVar(vars, TPLADD, "CONNECTONINITCHECKED", (rdr->ncd_connect_on_init == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "CONNECTONINITCHECKED", (rdr->ncd_connect_on_init == 1) ? "1" : "0"); + } + + // Reset Cycle + tpl_printf(vars, TPLADD, "RESETCYCLE", "%d", rdr->resetcycle); + + // Disable Serverfilter + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DISABLESERVERFILTERCHECKED", (rdr->ncd_disable_server_filt == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "DISABLESERVERFILTERVALUE", (rdr->ncd_disable_server_filt == 1) ? "1" : "0"); + } + +#ifdef MODULE_GHTTP + // Use SSL + if(!apicall) + { + tpl_addVar(vars, TPLADD, "USESSLCHECKED", (rdr->ghttp_use_ssl == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "USESSLVALUE", (rdr->ghttp_use_ssl == 1) ? "1" : "0"); + } +#endif + + // IPv4 force + if (!apicall) + { + tpl_addVar(vars, TPLADD, "IPV4FORCE", (rdr->ipv4force == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "IPV4FORCE", (rdr->ipv4force == 1) ? "1" : "0"); + } + + // Fallback + if(!apicall) + { + tpl_addVar(vars, TPLADD, "FALLBACKCHECKED", (rdr->fallback == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "FALLBACKVALUE", (rdr->fallback == 1) ? "1" : "0"); + } + + // Fallback per caid + value = mk_t_ftab(&rdr->fallback_percaid); + tpl_addVar(vars, TPLADD, "FALLBACK_PERCAID", value); + free_mk_t(value); + + // disable checksum test only for selected caid/provid + value = mk_t_ftab(&rdr->disablecrccws_only_for); + tpl_addVar(vars, TPLADD, "IGN_CHKSUM_ONLYFOR", value); + free_mk_t(value); + +#ifdef WITH_LB + tpl_addVar(vars, TPLADD, "LBFORCEFALLBACK", (rdr->lb_force_fallback == 1) ? "checked" : ""); +#endif + +#ifdef CS_CACHEEX + // Cacheex + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "CACHEEXSELECTED%d", rdr->cacheex.mode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + else + { + tpl_printf(vars, TPLADD, "CACHEEX", "%d", rdr->cacheex.mode); + } + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP", "%d", rdr->cacheex.maxhop); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_LG", "%d", rdr->cacheex.maxhop_lg); +#endif + value = mk_t_cacheex_hitvaluetab(&rdr->cacheex.filter_caidtab); + //if (cs_strlen(value) > 0) + tpl_printf(vars, TPLADD, "CACHEEX_ECM_FILTER", "%s", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "DCCHECKED", (rdr->cacheex.drop_csp == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "ARCHECKED", (rdr->cacheex.allow_request == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "AFCHECKED", (rdr->cacheex.allow_filter == 1) ? "checked" : ""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "AMCHECKED", (rdr->cacheex.allow_maxhop == 1) ? "checked" : ""); +#endif + tpl_addVar(vars, TPLADD, "BLOCKFAKECWSCHECKED", (rdr->cacheex.block_fakecws == 1) ? "checked" : ""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "USECWCHECKFORPUSHCHECKED", (rdr->cacheex.cw_check_for_push == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LGONLYREMOTESETTINGSCHECKED", (rdr->cacheex.lg_only_remote_settings == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYCHECKED", (rdr->cacheex.localgenerated_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&rdr->cacheex.lg_only_tab); + tpl_addVar(vars, TPLADD, "LGONLYTAB", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYINCHECKED", (rdr->cacheex.localgenerated_only_in == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "LGONLYINAIOONLYCHECKED", (rdr->cacheex.lg_only_in_aio_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&rdr->cacheex.lg_only_in_tab); + tpl_addVar(vars, TPLADD, "LGONLYINTAB", value); + free_mk_t(value); + + value = mk_t_caidvaluetab(&rdr->cacheex.cacheex_nopushafter_tab); + tpl_addVar(vars, TPLADD, "CACHEEXNOPUSHAFTER", value); + free_mk_t(value); +#endif +#endif + + // BoxID + if(rdr->boxid) + { tpl_printf(vars, TPLADD, "BOXID", "%08X", rdr->boxid); } + +#ifdef READER_VIDEOGUARD + // Filt 07 + if(!apicall) + { + tpl_addVar(vars, TPLADD, "FIX07CHECKED", (rdr->fix_07 == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "FIX07VALUE", (rdr->fix_07 == 1) ? "1" : "0"); + } + + + // Fix 9993 + if(!apicall) + { + tpl_addVar(vars, TPLADD, "FIX9993CHECKED", (rdr->fix_9993 == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "FIX9993VALUE", (rdr->fix_9993 == 1) ? "1" : "0"); + } +#endif + + // Drop CWs with wrong checksum: + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DROPBADCWSCHECKED", (rdr->dropbadcws == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "DROPBADCWSVALUE", (rdr->dropbadcws == 1) ? "1" : "0"); + } + + // Disable CWs checksum test: + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DISABLECRCCWSCHECKED", (rdr->disablecrccws == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "DISABLECRCCWSVALUE", (rdr->disablecrccws == 1) ? "1" : "0"); + } + +#ifdef WITH_CARDREADER + // Set reader to use GPIO + if(!apicall) + { + tpl_addVar(vars, TPLADD, "USE_GPIOCHECKED", rdr->use_gpio ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "USE_GPIOVALUE", rdr->use_gpio ? "1" : "0"); + } +#endif + + // AUdisabled + if(!apicall) + { + tpl_addVar(vars, TPLADD, "AUDISABLED", (rdr->audisabled == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "AUDISABLEDVALUE", (rdr->audisabled == 1) ? "1" : "0"); + } + + // AUprovid + if(rdr->auprovid) + { tpl_printf(vars, TPLADD, "AUPROVID", "%06X", rdr->auprovid); } + + if(rdr->ecmnotfoundlimit) + { tpl_printf(vars, TPLADD, "ECMNOTFOUNDLIMIT", "%u", rdr->ecmnotfoundlimit); } + +#ifdef READER_IRDETO + // Force Irdeto + if(!apicall) + { + tpl_addVar(vars, TPLADD, "FORCEIRDETOCHECKED", (rdr->force_irdeto == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "FORCEIRDETOVALUE", (rdr->force_irdeto == 1) ? "1" : "0"); + } +#endif + +#ifdef READER_CRYPTOWORKS + // needsglobalfirst + if(!apicall) + { + tpl_addVar(vars, TPLADD, "NEEDSGLOBALFIRST", (rdr->needsglobalfirst == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "NEEDSGLOBALFIRST", (rdr->needsglobalfirst == 1) ? "1" : "0"); + } +#endif +#ifdef WITH_CARDREADER + // RSA Key + for(i = 0; i < rdr->rsa_mod_length; i++) + { tpl_printf(vars, TPLAPPEND, "RSAKEY", "%02X", rdr->rsa_mod[i]); } + + // 3DES Key + for(i = 0; i < rdr->des_key_length; i++) + { tpl_printf(vars, TPLAPPEND, "DESKEY", "%02X", rdr->des_key[i]); } + + // BoxKey + for(i = 0; i < rdr->boxkey_length ; i++) + { tpl_printf(vars, TPLAPPEND, "BOXKEY", "%02X", rdr->boxkey[i]); } +#endif +#ifdef READER_CONAX + for(i = 0; i < rdr->cwpk_mod_length; i++) + { tpl_printf(vars, TPLAPPEND, "CWPKKEY", "%02X", rdr->cwpk_mod[i]); } +#endif +#ifdef READER_NAGRA + // nuid (CAK6.3) + for(i = 0; i < rdr->cak63nuid_length; i++) + { tpl_printf(vars, TPLAPPEND, "CAK63NUID", "%02X", rdr->cak63nuid[i]); } + + // cwekey (CAK6.3) + for(i = 0; i < rdr->cak63cwekey_length; i++) + { tpl_printf(vars, TPLAPPEND, "CAK63CWEKEY", "%02X", rdr->cak63cwekey[i]); } +#endif + +#ifdef READER_NAGRA_MERLIN + int32_t j; + + // idird (CAK7) + for(i = 0; i < rdr->idird_length; i++) + { tpl_printf(vars, TPLAPPEND, "IDIRD", "%02X", rdr->idird[i]); } + + // cmd0e_provider (CAK7) + for(i = 0; i < rdr->cmd0eprov_length; i++) + { tpl_printf(vars, TPLAPPEND, "CMD0EPROV", "%02X", rdr->cmd0eprov[i]); } + + // mod1 (CAK7) + for(i = 0; i < rdr->mod1_length ; i++) + { tpl_printf(vars, TPLAPPEND, "MOD1", "%02X", rdr->mod1[i]); } + + // mod2 (CAK7) + for(i = 0; i < rdr->mod2_length ; i++) + { tpl_printf(vars, TPLAPPEND, "MOD2", "%02X", rdr->mod2[i]); } + + // key3588 (CAK7) + for(i = 0; i < rdr->key3588_length; i++) + { tpl_printf(vars, TPLAPPEND, "KEY3588", "%02X", rdr->key3588[i]); } + + // key3310 (CAK7) + for(i = 0; i < rdr->key3310_length; i++) + { tpl_printf(vars, TPLAPPEND, "KEY3310", "%02X", rdr->key3310[i]); } + + // key3460 (CAK7) + for(i = 0; i < rdr->key3460_length; i++) + { tpl_printf(vars, TPLAPPEND, "KEY3460", "%02X", rdr->key3460[i]); } + + // data50 (CAK7) + for(i = 0; i < rdr->data50_length; i++) + { tpl_printf(vars, TPLAPPEND, "DATA50", "%02X", rdr->data50[i]); } + + // mod50 (CAK7) + for(i = 0; i < rdr->mod50_length; i++) + { tpl_printf(vars, TPLAPPEND, "MOD50", "%02X", rdr->mod50[i]); } + + // nuid (CAK7) + for(i = 0; i < rdr->nuid_length; i++) + { tpl_printf(vars, TPLAPPEND, "NUID", "%02X", rdr->nuid[i]); } + + // OTP CSC (CAK7) + for(i = 0; i < rdr->otpcsc_length; i++) + { tpl_printf(vars, TPLAPPEND, "OTPCSC", "%02X", rdr->otpcsc[i]); } + + // OTA CSC (CAK7) + for(i = 0; i < rdr->otacsc_length; i++) + { tpl_printf(vars, TPLAPPEND, "OTACSC", "%02X", rdr->otacsc[i]); } + + // Force Pairing Type (CAK7) + for(i = 0; i < rdr->forcepair_length; i++) + { tpl_printf(vars, TPLAPPEND, "FORCEPAIR", "%02X", rdr->forcepair[i]); } + + // cwekeys (CAK7) + for(j = 0; j < 17; j++) + { + char key[9] = "CWEKEY"; + if(j > 9) + { + key[6] = '1'; + key[7] = '0' + (j - 10); + } + else + { + key[6] = '0' + j; + } + for(i = 0; i < rdr->cwekey_length[j]; i++) + { tpl_printf(vars, TPLAPPEND, key, "%02X", rdr->cwekey[j][i]); } + } + + // force_cw_swap + if(rdr->forcecwswap) + { tpl_addVar(vars, TPLADD, "FORCECWSWAPCHECKED", "checked"); } + + // only_even_SA + if(rdr->evensa) + { tpl_addVar(vars, TPLADD, "EVENSACHECKED", "checked"); } + + // force_EMM_82 + if(rdr->forceemmg) + { tpl_addVar(vars, TPLADD, "FORCEEMMGCHECKED", "checked"); } + + // OTA_CWPKs + if(rdr->cwpkota) + { tpl_addVar(vars, TPLADD, "CWPKOTACHECKED", "checked"); } + + tpl_printf(vars, TPLADD, "TMP", "NAGRACAK7HEADERMODE%d", rdr->headermode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + // CWPK CaID (CAK7) + for(i = 0; i < rdr->cwpkcaid_length ; i++) + { tpl_printf(vars, TPLAPPEND, "CWPKCAID", "%02X", rdr->cwpkcaid[i]); } + + // cak7_mode + if(rdr->cak7_mode) + { tpl_addVar(vars, TPLADD, "NAGRACAK7MODECHECKED", "checked"); } +#endif + +#ifdef READER_VIDEOGUARD + tpl_printf(vars, TPLADD, "TMP", "CARDSTARTDATEBASEMONTH%d", rdr->card_startdate_basemonth); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "CARDSTARTDATEBASEYEAR", "%d", rdr->card_startdate_baseyear); + + tpl_printf(vars, TPLADD, "TMP", "CARDEXPIREDATEBASEMONTH%d", rdr->card_expiredate_basemonth); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "CARDEXPIREDATEBASEYEAR", "%d", rdr->card_expiredate_baseyear); + + // ins7E + if(rdr->ins7E[0x1A]) + { + for(i = 0; i < 26 ; i++) { tpl_printf(vars, TPLAPPEND, "INS7E", "%02X", rdr->ins7E[i]); } + } + // ins42 + if(rdr->ins42[0x25]) + { + for(i = 0; i < 37 ; i++) { tpl_printf(vars, TPLAPPEND, "INS42", "%02X", rdr->ins42[i]); } + } + + // ins7E11 + if(rdr->ins7E11[0x01]) + { + tpl_printf(vars, TPLAPPEND, "INS7E11", "%02X", rdr->ins7E11[0]); + } + + // ins2e06 + if(rdr->ins2e06[0x04]) + { + for(i = 0; i < 4 ; i++) { tpl_printf(vars, TPLAPPEND, "INS2E06", "%02X", rdr->ins2e06[i]); } + } + + // k1 for generic pairing mode + if(rdr->k1_generic[0x10]) + { + for(i = 0; i < rdr->k1_generic[0x10] ; i++) { tpl_printf(vars, TPLAPPEND, "K1_GENERIC", "%02X", rdr->k1_generic[i]); } + } + + // k1 for unique pairing mode + if(rdr->k1_unique[0x10]) + { + for(i = 0; i < rdr->k1_unique[0x10] ; i++) { tpl_printf(vars, TPLAPPEND, "K1_UNIQUE", "%02X", rdr->k1_unique[i]); } + } +#endif + +#ifdef WITH_CARDREADER + // ATR + if(rdr->atr[0]) + for(i = 0; i < rdr->atrlen / 2; i++) + { tpl_printf(vars, TPLAPPEND, "ATR", "%02X", rdr->atr[i]); } +#endif + + // ECM Whitelist + value = mk_t_ecm_whitelist(&rdr->ecm_whitelist); + tpl_addVar(vars, TPLADD, "ECMWHITELIST", value); + free_mk_t(value); + + // ECM Header Whitelist + value = mk_t_ecm_hdr_whitelist(&rdr->ecm_hdr_whitelist); + tpl_addVar(vars, TPLADD, "ECMHEADERWHITELIST", value); + free_mk_t(value); + +#ifdef WITH_CARDREADER + // Deprecated + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DEPRECATEDCHECKED", (rdr->deprecated == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "DEPRECATEDVALUE", (rdr->deprecated == 1) ? "1" : "0"); + } + + // Smargopatch + if(!apicall) + { + tpl_addVar(vars, TPLADD, "SMARGOPATCHCHECKED", (rdr->smargopatch == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "SMARGOPATCHVALUE", (rdr->smargopatch == 1) ? "1" : "0"); + } + + // Autospeed + if(!apicall) + { + tpl_addVar(vars, TPLADD, "AUTOSPEEDCHECKED", (rdr->autospeed == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "AUTOSPEEDVALUE", (rdr->autospeed == 1) ? "1" : "0"); + } + // sc8in1 dtrrts patch + if(!apicall) + { + tpl_addVar(vars, TPLADD, "SC8IN1DTRRTSPATCHCHECKED", (rdr->sc8in1_dtrrts_patch == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "SC8IN1DTRRTSPATCHVALUE", (rdr->sc8in1_dtrrts_patch == 1) ? "1" : "0"); + } + +#ifdef READER_VIACCESS + if(!apicall) + { + tpl_addVar(vars, TPLADD, "READOLDCLASSES", (rdr->read_old_classes == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "READOLDCLASSES", (rdr->read_old_classes == 1) ? "1" : "0"); + } +#endif + + // Detect + if(rdr->detect & 0x80) + { tpl_printf(vars, TPLADD, "DETECT", "!%s", RDR_CD_TXT[rdr->detect & 0x7f]); } + else + { tpl_addVar(vars, TPLADD, "DETECT", RDR_CD_TXT[rdr->detect & 0x7f]); } +#endif + + // Ratelimit + if(rdr->ratelimitecm) + { + tpl_printf(vars, TPLADD, "RATELIMITECM", "%d", rdr->ratelimitecm); + // ECMUNIQUE + if(!apicall) + { + tpl_addVar(vars, TPLADD, "ECMUNIQUECHECKED", (rdr->ecmunique == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "ECMUNIQUE", (rdr->ecmunique == 1) ? "1" : "0"); + } + tpl_printf(vars, TPLADD, "RATELIMITTIME", "%d", rdr->ratelimittime); + tpl_printf(vars, TPLADD, "SRVIDHOLDTIME", "%d", rdr->srvidholdtime); + } + // Cooldown + if(rdr->cooldown[0] && rdr->cooldown[1]) + { + tpl_printf(vars, TPLADD, "COOLDOWNDELAY", "%d", rdr->cooldown[0]); + tpl_printf(vars, TPLADD, "COOLDOWNTIME", "%d", rdr->cooldown[1]); + } + // Max parallel services + tpl_printf(vars, TPLADD, "MAXPARALLEL", "%d", rdr->maxparallel); + // Parallelfactor: format as float with dot (comma would break URL parameters) + tpl_printf(vars, TPLADD, "PARALLELFACTOR", "%.1f", rdr->parallelfactor >= 0 ? rdr->parallelfactor : 2.0); + tpl_printf(vars, TPLADD, "PARALLELTIMEOUT", "%d", rdr->paralleltimeout); +#ifdef WITH_CARDREADER + // Frequencies + tpl_printf(vars, TPLADD, "MHZ", "%d", rdr->mhz); + tpl_printf(vars, TPLADD, "CARDMHZ", "%d", rdr->cardmhz); +#endif + + // Device + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DEVICE", xml_encode(vars, rdr->device)); + } + else + { + tpl_addVar(vars, TPLADD, "DEVICE", rdr->device); + } + + if(rdr->r_port) + { tpl_printf(vars, TPLAPPEND, "DEVICE", ",%d", rdr->r_port); } + if(rdr->l_port) + { + if(rdr->r_port) + { tpl_printf(vars, TPLAPPEND, "DEVICE", ",%d", rdr->l_port); } + else + { tpl_printf(vars, TPLAPPEND, "DEVICE", ",,%d", rdr->l_port); } + } + + // Group + value = mk_t_group(rdr->grp); + tpl_addVar(vars, TPLADD, "GRP", value); + free_mk_t(value); + +#ifdef WITH_LB + if(rdr->lb_weight) + { tpl_printf(vars, TPLADD, "LBWEIGHT", "%d", rdr->lb_weight); } +#endif + + //services + if(!apicall) + { + struct s_sidtab *sidtab = cfg.sidtab; + //build matrix + i = 0; + while(sidtab != NULL) + { + tpl_addVar(vars, TPLADD, "SIDLABEL", xml_encode(vars, sidtab->label)); + if(rdr->sidtabs.ok & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "READERCONFIGSIDOKBIT")); + if(rdr->sidtabs.no & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "READERCONFIGSIDNOBIT")); + if(rdr->lb_sidtabs.ok & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "READERCONFIGSIDLBOKBIT")); + sidtab = sidtab->next; + i++; + } + if(i){ + tpl_addVar(vars, TPLADD, "READERCONFIGSIDINS", tpl_getTpl(vars, "READERCONFIGSID")); + } + } + else + { + value = mk_t_service(&rdr->sidtabs); + if(cs_strlen(value) > 0) + { tpl_addVar(vars, TPLADD, "SERVICES", value); } + free_mk_t(value); + } + + // CAID + value = mk_t_caidtab(&rdr->ctab); + tpl_addVar(vars, TPLADD, "CAIDS", value); + free_mk_t(value); + +#ifdef READER_VIACCESS + // AESkeys + value = mk_t_aeskeys(rdr); + tpl_addVar(vars, TPLADD, "AESKEYS", value); + free_mk_t(value); +#endif + + //ident + value = mk_t_ftab(&rdr->ftab); + tpl_addVar(vars, TPLADD, "IDENTS", value); + free_mk_t(value); + + //CHID + value = mk_t_ftab(&rdr->fchid); + tpl_addVar(vars, TPLADD, "CHIDS", value); + free_mk_t(value); + + //Local cards + value = mk_t_ftab(&rdr->localcards); + tpl_addVar(vars, TPLADD, "LOCALCARDS", value); + free_mk_t(value); + + //class + value = mk_t_cltab(&rdr->cltab); + tpl_addVar(vars, TPLADD, "CLASS", value); + free_mk_t(value); + + if(rdr->cachemm || rdr->logemm) + { tpl_printf(vars, TPLADD, "EMMCACHE", "%d,%d,%d,%d", rdr->cachemm, rdr->rewritemm, rdr->logemm, rdr->deviceemm); } + + //savenano + value = mk_t_nano(rdr->s_nano); + tpl_addVar(vars, TPLADD, "SAVENANO", value); + free_mk_t(value); + + //blocknano + value = mk_t_nano(rdr->b_nano); + tpl_addVar(vars, TPLADD, "BLOCKNANO", value); + free_mk_t(value); + + // Blocke EMM + if(!apicall) + { + tpl_addVar(vars, TPLADD, "BLOCKEMMUNKNOWNCHK", (rdr->blockemm & EMM_UNKNOWN) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "BLOCKEMMUNIQCHK", (rdr->blockemm & EMM_UNIQUE) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "BLOCKEMMSHAREDCHK", (rdr->blockemm & EMM_SHARED) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "BLOCKEMMGLOBALCHK", (rdr->blockemm & EMM_GLOBAL) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "BLOCKEMMUNKNOWNVALUE", (rdr->blockemm & EMM_UNKNOWN) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "BLOCKEMMUNIQVALUE", (rdr->blockemm & EMM_UNIQUE) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "BLOCKEMMSHAREDVALUE", (rdr->blockemm & EMM_SHARED) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "BLOCKEMMGLOBALVALUE", (rdr->blockemm & EMM_GLOBAL) ? "1" : "0"); + } + + // Save EMM + if(!apicall) + { + tpl_addVar(vars, TPLADD, "SAVEEMMUNKNOWNCHK", (rdr->saveemm & EMM_UNKNOWN) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "SAVEEMMUNIQCHK", (rdr->saveemm & EMM_UNIQUE) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "SAVEEMMSHAREDCHK", (rdr->saveemm & EMM_SHARED) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "SAVEEMMGLOBALCHK", (rdr->saveemm & EMM_GLOBAL) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "SAVEEMMUNKNOWNVALUE", (rdr->saveemm & EMM_UNKNOWN) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "SAVEEMMUNIQVALUE", (rdr->saveemm & EMM_UNIQUE) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "SAVEEMMSHAREDVALUE", (rdr->saveemm & EMM_SHARED) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "SAVEEMMGLOBALVALUE", (rdr->saveemm & EMM_GLOBAL) ? "1" : "0"); + } + + value = mk_t_emmbylen(rdr); + if(cs_strlen(value) > 0) + { tpl_addVar(vars, TPLADD, "BLOCKEMMBYLEN", value); } + free_mk_t(value); + +#ifdef MODULE_CCCAM + if(!strcmp(rdr->cc_version, "2.0.11")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED0", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.1.1")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED1", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.1.2")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED2", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.1.3")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED3", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.1.4")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED4", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.2.0")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED5", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.2.1")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED6", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.3.0")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED7", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.3.1")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED8", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.3.2")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED9", "selected"); + } +#endif + +#ifdef READER_VIDEOGUARD + tpl_printf(vars, TPLADD, "TMP", "NDSVERSION%d", rdr->ndsversion); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "TMP", "NDSREADTIERS%d", rdr->readtiers); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); +#endif +#ifdef READER_NAGRA + tpl_printf(vars, TPLADD, "TMP", "NAGRAREAD%d", rdr->nagra_read); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + if(rdr->detect_seca_nagra_tunneled_card) + { tpl_addVar(vars, TPLADD, "NAGRADETECTSECACARDCHECKED", "checked"); } +#endif +#ifdef READER_TONGFANG + if(rdr->tongfang3_calibsn) + { tpl_printf(vars, TPLADD, "TONGFANGCALIBSN", "%08X", rdr->tongfang3_calibsn); } + + if(rdr->tongfang_boxid) + { tpl_printf(vars, TPLADD, "TONGFANGBOXID", "%08X", rdr->tongfang_boxid); } + + if(rdr->tongfang3_deskey_length > 0) + { + for(i = 0; i < rdr->tongfang3_deskey_length ; i++) + { + tpl_printf(vars, TPLAPPEND, "TONGFANGDESKEY", "%02X", rdr->tongfang3_deskey[i]); + } + } + + if(rdr->stbid_length > 0) + { + for(i = 0; i < rdr->stbid_length ; i++) + { + tpl_printf(vars, TPLAPPEND, "STBID", "%02X", rdr->stbid[i]); + } + } + +#endif + +#ifdef MODULE_CCCAM + tpl_printf(vars, TPLADD, "CCCMAXHOPS", "%d", rdr->cc_maxhops); + tpl_printf(vars, TPLADD, "CCCMINDOWN", "%d", rdr->cc_mindown); + tpl_printf(vars, TPLADD, "CCCRESHARE", "%d", rdr->cc_reshare); + tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare); + tpl_printf(vars, TPLADD, "CCCRECONNECT", "%d", rdr->cc_reconnect); + + if(rdr->cc_want_emu) + { tpl_addVar(vars, TPLADD, "CCCWANTEMUCHECKED", "checked"); } + if(rdr->cc_keepalive) + { tpl_addVar(vars, TPLADD, "KEEPALIVECHECKED", "checked"); } +#endif + +#ifdef MODULE_GBOX + tpl_printf(vars, TPLADD, "GBOXMAXDISTANCE", "%d", rdr->gbox_maxdist); + tpl_printf(vars, TPLADD, "GBOXMAXECMSEND", "%d", rdr->gbox_maxecmsend); + tpl_printf(vars, TPLADD, "GBOXRESHARE", "%d", rdr->gbox_reshare); + tpl_printf(vars, TPLADD, "PEERGBOXID", "%04X", gbox_convert_password_to_id((uint32_t)a2i(rdr->r_pwd, 4))); + tpl_addVar(vars, TPLADD, "PEERONLSTAT", (get_peer_onl_status(gbox_convert_password_to_id((uint32_t)a2i(rdr->r_pwd, 4)))) ? "checked" : ""); + tpl_printf(vars, TPLADD, "FORCEREMM", "%d", rdr->gbox_force_remm); + tpl_printf(vars, TPLADD, "CMDHERE", rdr->send_offline_cmd ? "checked" : ""); + + if(rdr->blockemm & 0x80) + { + tpl_addVar(vars, TPLADD, "REMMCNLDCHK", "checked"); + tpl_printf(vars, TPLADD, "GBOXREMMPEER", "%04X", rdr->gbox_remm_peer); + tpl_addVar(vars, TPLADD, "REMMUNIQCHK", (rdr->blockemm & EMM_UNIQUE) ? "" : "checked"); + tpl_addVar(vars, TPLADD, "REMMSHAREDCHK", (rdr->blockemm & EMM_SHARED) ? "" : "checked"); + tpl_addVar(vars, TPLADD, "REMMGLOBALCHK", (rdr->blockemm & EMM_GLOBAL) ? "" : "checked"); + tpl_addVar(vars, TPLADD, "REMMEMMUNKNOWNCHK", (rdr->blockemm & EMM_UNKNOWN) ? "" : "checked"); + } + if(rdr->gbox_gsms_peer) + { + tpl_printf(vars, TPLADD, "LASTGSMS", "%s", rdr->last_gsms); + tpl_printf(vars, TPLADD, "GBOXGSMSPEER", "%04X", rdr->gbox_gsms_peer); + } +#endif + +#ifdef READER_DRECAS + tpl_addVar(vars, TPLADD, "STMKEYS", rdr->stmkeys); +#endif + +#if defined(READER_DRE) || defined(READER_DRECAS) + tpl_addVar(vars, TPLADD, "USERSCRIPT", rdr->userscript); +#endif + +#ifdef WITH_EMU + //emu_auproviders + value = mk_t_ftab(&rdr->emu_auproviders); + tpl_addVar(vars, TPLADD, "EMUAUPROVIDERS", value); + free_mk_t(value); + + // Date-coded BISS keys + if(!apicall) + { + tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "1" : "0"); + } +#endif + + tpl_addVar(vars, TPLADD, "PROTOCOL", reader_get_type_desc(rdr, 0)); + + // Show only parameters which needed for the reader + switch(rdr->typ) + { + case R_CONSTCW: + case R_DB2COM1: + case R_DB2COM2: + case R_MOUSE : + case R_DRECAS : + case R_MP35: + case R_SC8in1 : + case R_SMART : + case R_INTERNAL: + case R_SERIAL : + case R_PCSC : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGSTDHWREADERBIT")); + break; + case R_CAMD35 : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCAMD35BIT")); + break; + case R_EMU : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGEMUBIT")); + break; + case R_CS378X : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCS378XBIT")); + break; + case R_RADEGAST: + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGRADEGASTBIT")); + break; + case R_SCAM: + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGSCAMBIT")); + break; + case R_GHTTP: + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGGHTTPBIT")); + break; + case R_GBOX: + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGGBOXBIT")); +#if defined (MODULE_CCCAM) && defined (MODULE_GBOX) + if(cfg.cc_gbx_reshare_en) + { + tpl_printf(vars, TPLADD, "GBOXCCCAMRESHARE", "%d", rdr->gbox_cccam_reshare); + value = mk_t_ftab(&rdr->ccc_gbx_reshare_ident); + tpl_addVar(vars, TPLADD, "CCCGBXRESHAREIDENT", value); + free_mk_t(value); + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "GBOXCCCAMRESHAREBIT")); + } +#endif + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERINFOGBOXREMM")); + break; + case R_NEWCAMD: + if(rdr->ncd_proto == NCD_525) + { + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGNCD525BIT")); + } + else if(rdr->ncd_proto == NCD_524) + { + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGNCD524BIT")); + } + break; +#ifdef MODULE_CCCAM + case R_CCCAM : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCCCAMBIT")); + break; +#endif + default : + tpl_addMsg(vars, "Error: protocol not resolvable"); + break; + + } + +#ifdef MODULE_CCCAM + if(rdr->typ != R_CCCAM && rdr->typ != R_GBOX) + { + tpl_printf(vars, TPLADD, "CCCHOP", "%d", rdr->cc_hop); + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGHOPBIT")); + } +#endif + +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "READERCONFIGAIO"); +#else + return tpl_getTpl(vars, "READERCONFIG"); +#endif +} + +static char *send_oscam_reader_stats(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + + if(!apicall) { setActiveMenu(vars, MNU_READERS); } + + int8_t error; + struct s_client *cl = NULL; + struct s_reader *rdr; + + rdr = get_reader_by_label(getParam(params, "label")); + error = (rdr ? 0 : 1); + + if(!error && rdr) + { + cl = rdr->client; + error = (cl ? 0 : 1); + } + + if(error) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATSROW", tpl_getTpl(vars, "READERSTATSROWBIT")); + if(!apicall) + { return tpl_getTpl(vars, "READERSTATS"); } + else + { return tpl_getTpl(vars, "APIREADERSTATS"); } + } + +#ifdef WITH_LB + char *stxt[] = {"found", "cache1", "cache2", "cache3", + "not found", "timeout", "sleeping", + "fake", "invalid", "corrupt", "no card", "expdate", + "disabled", "stopped" + }; + + if(strcmp(getParam(params, "action"), "resetstat") == 0) + { + char *rcs = getParam(params, "rc"); + int32_t retval = 0; + if(cs_strlen(rcs) > 0) + { + int8_t rc; + rc = atoi(rcs); + retval = clean_stat_by_rc(rdr, rc, 0); + cs_log("Reader %s stats %d %s entr%s deleted by WebIF from %s", + rdr->label, retval, stxt[rc], + retval == 1 ? "y" : "ies", + cs_inet_ntoa(GET_IP())); + } + else + { + clear_reader_stat(rdr); + cs_log("Reader %s stats resetted by WebIF from %s", rdr->label, cs_inet_ntoa(GET_IP())); + } + + } + + if(strcmp(getParam(params, "action"), "deleterecord") == 0) + { + char *record = getParam(params, "record"); + if(cs_strlen(record) > 0) + { + int32_t retval = 0; + uint32_t caid, provid, sid, cid, len; + sscanf(record, "%4x@%6x:%4x:%4x:%4x", &caid, &provid, &sid, &cid, &len); + retval = clean_stat_by_id(rdr, caid, provid, sid, cid, len); + cs_log("Reader %s stats %d entr%s deleted by WebIF from %s", + rdr->label, retval, + retval == 1 ? "y" : "ies", + cs_inet_ntoa(GET_IP())); + } + } + + if(strcmp(getParam(params, "action"), "updateecmlen") == 0) + { + update_ecmlen_from_stat(rdr); + write_server(); + } + +#endif + + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "LABEL", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "ENCODEDLABEL", urlencode(vars, rdr->label)); + + if(apicall) + { + int32_t i, emmcount = 0; + char *ttxt[] = {"unknown", "unique", "shared", "global"}; + + for(i = 0; i < 4; i++) + { + tpl_addVar(vars, TPLADD, "EMMRESULT", "error"); + tpl_addVar(vars, TPLADD, "EMMTYPE", ttxt[i]); + tpl_printf(vars, TPLADD, "EMMCOUNT", "%d", rdr->emmerror[i]); + tpl_addVar(vars, TPLAPPEND, "EMMSTATS", tpl_getTpl(vars, "APIREADERSTATSEMMBIT")); + emmcount += rdr->emmerror[i]; + tpl_printf(vars, TPLADD, "TOTALERROR", "%d", emmcount); + } + emmcount = 0; + for(i = 0; i < 4; i++) + { + tpl_addVar(vars, TPLADD, "EMMRESULT", "written"); + tpl_addVar(vars, TPLADD, "EMMTYPE", ttxt[i]); + tpl_printf(vars, TPLADD, "EMMCOUNT", "%d", rdr->emmwritten[i]); + tpl_addVar(vars, TPLAPPEND, "EMMSTATS", tpl_getTpl(vars, "APIREADERSTATSEMMBIT")); + emmcount += rdr->emmwritten[i]; + tpl_printf(vars, TPLADD, "TOTALWRITTEN", "%d", emmcount); + } + emmcount = 0; + for(i = 0; i < 4; i++) + { + tpl_addVar(vars, TPLADD, "EMMRESULT", "skipped"); + tpl_addVar(vars, TPLADD, "EMMTYPE", ttxt[i]); + tpl_printf(vars, TPLADD, "EMMCOUNT", "%d", rdr->emmskipped[i]); + tpl_addVar(vars, TPLAPPEND, "EMMSTATS", tpl_getTpl(vars, "APIREADERSTATSEMMBIT")); + emmcount += rdr->emmskipped[i]; + tpl_printf(vars, TPLADD, "TOTALSKIPPED", "%d", emmcount); + } + emmcount = 0; + for(i = 0; i < 4; i++) + { + tpl_addVar(vars, TPLADD, "EMMRESULT", "blocked"); + tpl_addVar(vars, TPLADD, "EMMTYPE", ttxt[i]); + tpl_printf(vars, TPLADD, "EMMCOUNT", "%d", rdr->emmblocked[i]); + tpl_addVar(vars, TPLAPPEND, "EMMSTATS", tpl_getTpl(vars, "APIREADERSTATSEMMBIT")); + emmcount += rdr->emmblocked[i]; + tpl_printf(vars, TPLADD, "TOTALBLOCKED", "%d", emmcount); + } + } + + if(apicall) + { + char *txt = "UNDEF"; + switch(rdr->card_status) + { + case NO_CARD: + if(rdr->typ == R_GBOX) + { txt = "ONL no crd"; } + else + { txt = "OFF"; } + break; + case UNKNOWN: + txt = "UNKNOWN"; + break; + case READER_DEVICE_ERROR: + txt = "READER DEVICE ERROR"; + break; + case CARD_NEED_INIT: + if(rdr->typ == R_GBOX) + { txt = "OFFLINE"; } + else + { txt = "NEEDINIT"; } + break; + case CARD_INSERTED: + if(cl->typ == 'p') + { + if(rdr->typ == R_GBOX) + { txt = "ONL w/crd"; } + else + { txt = "CONNECTED"; } + } + else + { txt = "CARDOK"; } + break; + case CARD_FAILURE: + txt = "ERROR"; + break; + default: + txt = "UNDEF"; + } + tpl_addVar(vars, TPLADD, "READERSTATUS", txt); + tpl_printf(vars, TPLADD, "READERCAID", "%04X", rdr->caid); + } + + int32_t rowcount = 0; + uint64_t ecmcount = 0; + time_t lastaccess = 0; + +#ifdef WITH_LB + int32_t rc2hide = (-1); + if(cs_strlen(getParam(params, "hide")) > 0) + { rc2hide = atoi(getParam(params, "hide")); } + + int32_t rc2show = (-1); + if(cs_strlen(getParam(params, "show")) > 0) + { rc2show = atoi(getParam(params, "show")); } + + if(rdr->lb_stat) + { + int32_t statsize; + // @todo alno: sort by click, 0=ascending, 1=descending (maybe two buttons or reverse on second click) + READER_STAT **statarray = get_sorted_stat_copy(rdr, 0, &statsize); + char channame[CS_SERVICENAME_SIZE]; + for(; rowcount < statsize; ++rowcount) + { + READER_STAT *s = statarray[rowcount]; + if(!(s->rc == rc2hide) && ((rc2show == -1) || (s->rc == rc2show))) + { + struct tm lt; + localtime_r(&s->last_received.time, <); // fixme we need walltime! + ecmcount += s->ecm_count; + if(!apicall) + { + tpl_printf(vars, TPLADD, "CHANNEL", "%04X@%06X:%04X:%04X", s->caid, s->prid, s->srvid, s->chid); + tpl_addVar(vars, TPLADD, "CHANNELNAME", xml_encode(vars, get_servicename(cur_client(), s->srvid, s->prid, s->caid, channame, sizeof(channame)))); + tpl_printf(vars, TPLADD, "ECMLEN", "%04hX", s->ecmlen); + tpl_addVar(vars, TPLADD, "RC", stxt[s->rc]); + tpl_printf(vars, TPLADD, "TIME", PRINTF_LOCAL_D " ms", s->time_avg); + if(s->time_stat[s->time_idx]) + { tpl_printf(vars, TPLADD, "TIMELAST", PRINTF_LOCAL_D " ms", s->time_stat[s->time_idx]); } + else + { tpl_addVar(vars, TPLADD, "TIMELAST", ""); } + tpl_printf(vars, TPLADD, "COUNT", PRINTF_LOCAL_D, s->ecm_count); + + if(s->last_received.time) + { + tpl_printf(vars, TPLADD, "LAST", "%02d.%02d.%02d %02d:%02d:%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100, lt.tm_hour, lt.tm_min, lt.tm_sec); + + } + else + { + tpl_addVar(vars, TPLADD, "LAST", "never"); + } + } + else + { + tpl_printf(vars, TPLADD, "ECMCAID", "%04X", s->caid); + tpl_printf(vars, TPLADD, "ECMPROVID", "%06X", s->prid); + tpl_printf(vars, TPLADD, "ECMSRVID", "%04X", s->srvid); + tpl_printf(vars, TPLADD, "ECMLEN", "%04hX", s->ecmlen); + tpl_addVar(vars, TPLADD, "ECMCHANNELNAME", xml_encode(vars, get_servicename(cur_client(), s->srvid, s->prid, s->caid, channame, sizeof(channame)))); + tpl_printf(vars, TPLADD, "ECMTIME", PRINTF_LOCAL_D, s->time_avg); + tpl_printf(vars, TPLADD, "ECMTIMELAST", PRINTF_LOCAL_D, s->time_stat[s->time_idx]); + tpl_printf(vars, TPLADD, "ECMRC", "%d", s->rc); + tpl_addVar(vars, TPLADD, "ECMRCS", stxt[s->rc]); + if(s->last_received.time) + { + char tbuffer [30]; + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", <); + tpl_addVar(vars, TPLADD, "ECMLAST", tbuffer); + } + else + { + tpl_addVar(vars, TPLADD, "ECMLAST", ""); + } + tpl_printf(vars, TPLADD, "ECMCOUNT", PRINTF_LOCAL_D, s->ecm_count); + + if(s->last_received.time > lastaccess) + { lastaccess = s->last_received.time; } + } + + if(!apicall) + { + if(s->rc == E_NOTFOUND) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATSROWNOTFOUND", tpl_getTpl(vars, "READERSTATSBIT")); + tpl_addVar(vars, TPLADD, "RESETA", urlencode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "READERSTATSNFHEADLINE", tpl_getTpl(vars, "READERSTATSROWNOTFOUNDBIT")); + } + else if(s->rc == E_TIMEOUT) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATSROWTIMEOUT", tpl_getTpl(vars, "READERSTATSBIT")); + tpl_addVar(vars, TPLADD, "RESETB", urlencode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "READERSTATSTOHEADLINE", tpl_getTpl(vars, "READERSTATSROWTIMEOUTBIT")); + } + else if(s->rc == E_INVALID) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATSROWINVALID", tpl_getTpl(vars, "READERSTATSBIT")); + tpl_addVar(vars, TPLADD, "RESETC", urlencode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "READERSTATSIVHEADLINE", tpl_getTpl(vars, "READERSTATSROWINVALIDBIT")); + } + else + { tpl_addVar(vars, TPLAPPEND, "READERSTATSROWFOUND", tpl_getTpl(vars, "READERSTATSBIT")); } + } + else + { + + tpl_addVar(vars, TPLAPPEND, "ECMSTATS", tpl_getTpl(vars, "APIREADERSTATSECMBIT")); + } + } + } + NULLFREE(statarray); + } + else +#endif + tpl_addVar(vars, TPLAPPEND, "READERSTATSROW", tpl_getTpl(vars, "READERSTATSNOSTATS")); + + tpl_printf(vars, TPLADD, "ROWCOUNT", "%d", rowcount); + + if(lastaccess > 0) + { + char tbuffer [30]; + struct tm lt; + localtime_r(&lastaccess, <); + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", <); + tpl_addVar(vars, TPLADD, "LASTACCESS", tbuffer); + } + else + { + tpl_addVar(vars, TPLADD, "LASTACCESS", ""); + } + + if(apicall) + { + if(cl) + { + char *value = get_ecm_historystring(cl); + tpl_addVar(vars, TPLADD, "ECMHISTORY", value); + free_mk_t(value); + } + } + + tpl_printf(vars, TPLADD, "TOTALECM", "%'" PRIu64, ecmcount); + + if(!apicall) + { return tpl_getTpl(vars, "READERSTATS"); } + else + { return tpl_getTpl(vars, "APIREADERSTATS"); } +} + +static char *send_oscam_user_config_edit(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + struct s_auth *account, *ptr, *chk; + char user[sizeof(first_client->account->usr)]; + + int32_t i; + int existing_insert = 0; + + if(!apicall) { setActiveMenu(vars, MNU_USERS); } + + if(strcmp(getParam(params, "action"), "Save As") == 0) { cs_strncpy(user, getParam(params, "newuser"), sizeof(user)); } + else { cs_strncpy(user, getParam(params, "user"), sizeof(user)); } + + account = NULL; + for(chk = cfg.account; chk != NULL; chk = chk->next) + { + if(strcmp(user, chk->usr) == 0) + { account = chk; } + if(!existing_insert) + { + tpl_printf(vars, TPLADD, "EXISTING_INS", "'%s'", urlencode(vars, chk->usr)); + existing_insert++; + } + else + { + tpl_printf(vars, TPLAPPEND, "EXISTING_INS", ",'%s'", urlencode(vars, chk->usr)); + } + } + + // Create a new user if it doesn't yet + if(account == NULL) + { + i = 1; + while(cs_strlen(user) < 1) + { + snprintf(user, sizeof(user) / sizeof(char) - 1, "NEWUSER%d", i); + for(account = cfg.account; account != NULL && strcmp(user, account->usr) != 0; account = account->next) { ; } + if(account != NULL) { user[0] = '\0'; } + ++i; + } + if(!cs_malloc(&account, sizeof(struct s_auth))) { return "0"; } + if(cfg.account == NULL) { cfg.account = account; } + else + { + for(ptr = cfg.account; ptr != NULL && ptr->next != NULL; ptr = ptr->next) { ; } + ptr->next = account; + } + account_set_defaults(account); + account->disabled = 1; + cs_strncpy((char *)account->usr, user, sizeof(account->usr)); + if(!account->grp) + { account->grp = 1; } + if(write_userdb() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + else if(strcmp(getParam(params, "action"), "Save As") == 0) { tpl_addMsg(vars, "New user has been added with cloned settings"); } + else { tpl_addMsg(vars, "New user has been added with default settings"); } + // no need to refresh anything here as the account is disabled by default and there's no client with this new account anyway! + } + + if((strcmp(getParam(params, "action"), "Save") == 0) || (strcmp(getParam(params, "action"), "Save As") == 0)) + { + char servicelabels[1024] = ""; + + for(i = 0; i < (*params).paramcount; i++) + { + if((strcmp((*params).params[i], "action")) && + (strcmp((*params).params[i], "user")) && + (strcmp((*params).params[i], "newuser")) && + (strcmp((*params).params[i], "part"))) + { + + if(!strcmp((*params).params[i], "services")) + { snprintf(servicelabels + cs_strlen(servicelabels), sizeof(servicelabels) - cs_strlen(servicelabels), "%s,", (*params).values[i]); } + else + { chk_account((*params).params[i], (*params).values[i], account); } + } + } + chk_account("services", servicelabels, account); + + refresh_oscam(REFR_CLIENTS); + + if(write_userdb() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + else if(strcmp(getParam(params, "action"), "Save As") != 0) { tpl_addMsg(vars, "User Account updated and saved"); } + } + + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, account->usr)); + tpl_addVar(vars, TPLADD, "PASSWORD", xml_encode(vars, account->pwd)); + tpl_addVar(vars, TPLADD, "DESCRIPTION", xml_encode(vars, account->description)); + + //Disabled + if(!apicall) + { + if(account->disabled) + { tpl_addVar(vars, TPLADD, "DISABLEDCHECKED", "checked"); } + } + else + { + tpl_printf(vars, TPLADD, "DISABLEDVALUE", "%d", account->disabled); + } + + //Expirationdate + struct tm timeinfo; + cs_gmtime_r(&account->expirationdate, &timeinfo); + char buf [80]; + strftime(buf, 80, "%Y-%m-%d", &timeinfo); + if(strcmp(buf, "1970-01-01")) { tpl_addVar(vars, TPLADD, "EXPDATE", buf); } + + //Allowed TimeFrame + char *allowedtf = mk_t_allowedtimeframe(account); + tpl_printf(vars, TPLADD, "ALLOWEDTIMEFRAME", "%s", allowedtf); + free_mk_t(allowedtf); + + //Group + char *value = mk_t_group(account->grp); + tpl_addVar(vars, TPLADD, "GROUPS", value); + free_mk_t(value); + + // allowed protocols + value = mk_t_allowedprotocols(account); + tpl_addVar(vars, TPLADD, "ALLOWEDPROTOCOLS", value); + free_mk_t(value); + + //Hostname + tpl_addVar(vars, TPLADD, "DYNDNS", xml_encode(vars, account->dyndns)); + + //Uniq + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "UNIQSELECTED%d", account->uniq); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + else + { + tpl_printf(vars, TPLADD, "UNIQVALUE", "%d", account->uniq); + } + + //Sleep + if(!account->tosleep) { tpl_addVar(vars, TPLADD, "SLEEP", "0"); } + else { tpl_printf(vars, TPLADD, "SLEEP", "%d", account->tosleep); } + + //Monlevel selector + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "MONSELECTED%d", account->monlvl); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + else + { + tpl_printf(vars, TPLADD, "MONVALUE", "%d", account->monlvl); + } + + //Au + if(account->autoau == 1) + { tpl_addVar(vars, TPLADD, "AUREADER", "1"); } + else if(account->aureader_list) + { + value = mk_t_aureader(account); + tpl_addVar(vars, TPLADD, "AUREADER", value); + free_mk_t(value); + } + + if(!apicall) + { + /* SERVICES */ + struct s_sidtab *sidtab = cfg.sidtab; + //build matrix + i = 0; + while(sidtab != NULL) + { + tpl_addVar(vars, TPLADD, "SIDLABEL", xml_encode(vars, sidtab->label)); + if(account->sidtabs.ok & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "USEREDITSIDOKBIT")); + if(account->sidtabs.no & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "USEREDITSIDNOBIT")); + sidtab = sidtab->next; + i++; + } + if(i){ + tpl_addVar(vars, TPLADD, "USEREDITSIDINS", tpl_getTpl(vars, "USEREDITSID")); + } + } + else + { + value = mk_t_service(&account->sidtabs); + if(cs_strlen(value) > 0) + { tpl_addVar(vars, TPLADD, "SERVICES", value); } + free_mk_t(value); + } + + // CAID + value = mk_t_caidtab(&account->ctab); + tpl_addVar(vars, TPLADD, "CAIDS", value); + free_mk_t(value); + + //ident + value = mk_t_ftab(&account->ftab); + tpl_addVar(vars, TPLADD, "IDENTS", value); + free_mk_t(value); + + //CHID + value = mk_t_ftab(&account->fchid); + tpl_addVar(vars, TPLADD, "CHIDS", value); + free_mk_t(value); + + //class + value = mk_t_cltab(&account->cltab); + tpl_addVar(vars, TPLADD, "CLASS", value); + free_mk_t(value); + + //Betatunnel + value = mk_t_tuntab(&account->ttab); + tpl_addVar(vars, TPLADD, "BETATUNNELS", value); + free_mk_t(value); + + //SUPPRESSCMD08 + if(!apicall) + { + if(account->c35_suppresscmd08) + { tpl_addVar(vars, TPLADD, "SUPPRESSCMD08", "selected"); } + } + else + { + tpl_printf(vars, TPLADD, "SUPPRESSCMD08VALUE", "%d", account->c35_suppresscmd08); + } + + //Sleepsend + if(account->c35_sleepsend) + { + tpl_printf(vars, TPLADD, "SLEEPSEND", "selected"); + } + + //max_connections + tpl_printf(vars, TPLADD, "MAXCONNECTIONS", "%d", account->max_connections); + + //User Max Idle + tpl_printf(vars, TPLADD, "UMAXIDLE", "%d", account->umaxidle); + + //EMM Reassembly selector + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "EMMRSELECTED%d", account->emm_reassembly); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + else + { + tpl_printf(vars, TPLADD, "EMMRVALUE", "%d", account->emm_reassembly); + } + + //Prefer local cards + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "PREFERLOCALCARDS%d", account->preferlocalcards); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + char *tmp = NULL; + switch(cfg.preferlocalcards) + { + case -1: + tmp = "-1 - Use Global prefer local cards"; + break; + case 0: + tmp = "0 - local cards like proxied"; + break; + case 1: + tmp = "1 - prefer cache-ex then local cards"; + break; + case 2: + tmp = "2 - prefer local cards above cache-ex"; + break; + } + tpl_addVar(vars, TPLADD, "CFGPREFERLOCALCARDS", tmp); + } + else + { + tpl_printf(vars, TPLADD, "PREFERLOCALCARDSVALUE", "%d", account->preferlocalcards); + } + +#ifdef CS_CACHEEX + // Cacheex + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "CACHEEXSELECTED%d", account->cacheex.mode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + } + else + { + tpl_printf(vars, TPLADD, "CACHEEX", "%d", account->cacheex.mode); + } + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP", "%d", account->cacheex.maxhop); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_LG", "%d", account->cacheex.maxhop_lg); +#endif + + value = mk_t_cacheex_hitvaluetab(&account->cacheex.filter_caidtab); + //if (cs_strlen(value) > 0) + tpl_printf(vars, TPLADD, "CACHEEX_ECM_FILTER", "%s", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "DCCHECKED", (account->cacheex.drop_csp == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "ARCHECKED", (account->cacheex.allow_request == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "AFCHECKED", (account->cacheex.allow_filter == 1) ? "checked" : ""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "AMCHECKED", (account->cacheex.allow_maxhop == 1) ? "checked" : ""); +#endif + tpl_addVar(vars, TPLADD, "BLOCKFAKECWSCHECKED", (account->cacheex.block_fakecws == 1) ? "checked" : ""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "USECWCHECKFORPUSHCHECKED", (account->cacheex.cw_check_for_push == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LGONLYREMOTESETTINGSCHECKED", (account->cacheex.lg_only_remote_settings == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYCHECKED", (account->cacheex.localgenerated_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&account->cacheex.lg_only_tab); + tpl_addVar(vars, TPLADD, "LGONLYTAB", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYINCHECKED", (account->cacheex.localgenerated_only_in == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "LGONLYINAIOONLYCHECKED", (account->cacheex.lg_only_in_aio_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&account->cacheex.lg_only_in_tab); + tpl_addVar(vars, TPLADD, "LGONLYINTAB", value); + free_mk_t(value); + + value = mk_t_caidvaluetab(&account->cacheex.cacheex_nopushafter_tab); + tpl_addVar(vars, TPLADD, "CACHEEXNOPUSHAFTER", value); + free_mk_t(value); +#endif + tpl_addVar(vars, TPLADD, "NWTCHECKED", (account->no_wait_time == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "DISABLECRCCEX4USER", (account->disablecrccacheex == 1) ? "checked" : ""); + value = mk_t_ftab(&account->disablecrccacheex_only_for); + tpl_addVar(vars, TPLADD, "IGNCRCCEX4USERONLYFOR", value); + free_mk_t(value); + +#endif + +#ifdef CW_CYCLE_CHECK + tpl_addVar(vars, TPLADD, "USERCWCYCLEDISABLE", (account->cwc_disable == 1) ? "checked" : ""); +#endif + + //Keepalive + if(!apicall) + { + if(account->ncd_keepalive) + { tpl_addVar(vars, TPLADD, "KEEPALIVE", "checked"); } + } + else + { + tpl_printf(vars, TPLADD, "KEEPALIVEVALUE", "%d", account->ncd_keepalive); + } + +#ifdef CS_ANTICASC + tpl_printf(vars, TPLADD, "AC_USERS", "%d", account->ac_users); + tpl_printf(vars, TPLADD, "CFGNUMUSERS", "%d", cfg.ac_users); + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "PENALTY%d", account->ac_penalty); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + char *tmp = NULL; + switch(cfg.ac_penalty) + { + case 0: + tmp = "(0) Only write to log"; + break; + case 1: + tmp = "(1) NULL CW"; + break; + case 2: + tmp = "(2) Ban"; + break; + case 3: + tmp = "(3) Real CW delayed"; + break; + } + tpl_addVar(vars, TPLADD, "CFGPENALTY", tmp); + } + else + { + tpl_printf(vars, TPLADD, "PENALTYVALUE", "%d", account->ac_penalty); + } + tpl_printf(vars, TPLADD, "ACOSC_MAX_ECMS_PER_MINUTE", "%d", account->acosc_max_ecms_per_minute); + tpl_printf(vars, TPLADD, "ACOSC_MAX_ACTIVE_SIDS", "%d", account->acosc_max_active_sids); + tpl_printf(vars, TPLADD, "CFG_ACOSC_MAX_ECMS_PER_MINUTE", "%d", cfg.acosc_max_ecms_per_minute); + tpl_printf(vars, TPLADD, "CFG_ACOSC_MAX_ACTIVE_SIDS", "%d", cfg.acosc_max_active_sids); + tpl_printf(vars, TPLADD, "ACOSC_ZAP_LIMIT", "%d", account->acosc_zap_limit); + tpl_printf(vars, TPLADD, "CFG_ACOSC_ZAP_LIMIT", "%d", cfg.acosc_zap_limit); + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "ACOSC_PENALTY%d", account->acosc_penalty); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + char *tmp = NULL; + switch(cfg.acosc_penalty) + { + case -1: + tmp = "(-1) Use Global Value"; + break; + case 0: + tmp = "(0) Only write to log"; + break; + case 1: + tmp = "(1) NULL CW"; + break; + case 2: + tmp = "(2) Ban"; + break; + case 3: + tmp = "(3) CW delayed"; + break; + case 4: + tmp = "(4) Hidecards"; + break; + } + tpl_addVar(vars, TPLADD, "CFG_ACOSC_PENALTY", tmp); + } else + { + tpl_printf(vars, TPLADD, "ACOSC_PENALTYVALUE", "%d", account->acosc_penalty); + } + + tpl_printf(vars, TPLADD, "ACOSC_PENALTY_DURATION", "%d", account->acosc_penalty_duration); + tpl_printf(vars, TPLADD, "CFG_ACOSC_PENALTY_DURATION", "%d", cfg.acosc_penalty_duration); + tpl_printf(vars, TPLADD, "ACOSC_DELAY", "%d", account->acosc_delay); + tpl_printf(vars, TPLADD, "CFG_ACOSC_DELAY", "%d", cfg.acosc_delay); +#endif + +#ifdef MODULE_CCCAM + tpl_printf(vars, TPLADD, "CCCMAXHOPS", "%d", account->cccmaxhops); + tpl_printf(vars, TPLADD, "CCCRESHARE", "%d", account->cccreshare); + tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare); + + //CCcam Ignore Reshare + tpl_printf(vars, TPLADD, "TMP", "CCCIGNRSHRSELECTED%d", account->cccignorereshare); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_addVar(vars, TPLADD, "CFGIGNORERESHARE", + cfg.cc_ignore_reshare == 0 ? + "0 - use reshare level of Server" : "1 - use reshare level of Reader or User"); + + //CCcam Stealth Mode + tpl_printf(vars, TPLADD, "TMP", "CCCSTEALTHSELECTED%d", account->cccstealth); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_addVar(vars, TPLADD, "STEALTH", cfg.cc_stealth ? "enable" : "disable"); +#endif + + //Failban + tpl_printf(vars, TPLADD, "FAILBAN", "%d", account->failban); + + if(!apicall) + { +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "USEREDITAIO"); +#else + return tpl_getTpl(vars, "USEREDIT"); +#endif + } + else + { + return tpl_getTpl(vars, "APIUSEREDIT"); + } + +} + +static void webif_add_client_proto(struct templatevars *vars, struct s_client *cl, const char *proto, int8_t apicall) +{ + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOSORT", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOTITLE", ""); + tpl_addVar(vars, TPLADDONCE, "PROTOICON", ""); + if(!cl) { return; } +#ifdef MODULE_NEWCAMD + if(streq(proto, "newcamd") && cl->typ == 'c') + { + tpl_printf(vars, TPLADD, "CLIENTPROTO", "%s (%s)", proto, newcamd_get_client_name(cl->ncd_client_id)); + tpl_printf(vars, TPLADD, "CLIENTPROTOSORT", "%s (%s)", proto, newcamd_get_client_name(cl->ncd_client_id)); + if(cfg.http_showpicons ) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s_%s", (char *)proto, newcamd_get_client_name(cl->ncd_client_id)); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "NCMDA", (char *)proto); + tpl_addVar(vars, TPLADD, "NCMDB", (char *)newcamd_get_client_name(cl->ncd_client_id)); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTONEWCAMDPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s_%s",(char *)proto, (char *)newcamd_get_client_name(cl->ncd_client_id)); + } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s_%s.tpl", proto, newcamd_get_client_name(cl->ncd_client_id)); + } + } + return; + } +#endif +#ifdef MODULE_CCCAM + if(strncmp(proto, "cccam", 5) == 0) + { + struct cc_data *cc = cl->cc; + if(cc && *cc->remote_version && *cc->remote_build) + { + tpl_printf(vars, TPLADD, "CLIENTPROTO", "%s (%s-%s)", proto, cc->remote_version, cc->remote_build); + tpl_printf(vars, TPLADD, "CLIENTPROTOSORT", "%s (%s-%s)", proto, cc->remote_version, cc->remote_build); + if(cccam_client_multics_mode(cl)) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "Multics, revision r%d", cc->multics_version[0] | (cc->multics_version[1] << 8)); + } + else + { +#endif +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + if(cl->reader && cl->reader->cacheex.feature_bitfield) + { + if(cl->reader->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio %s]", (cc->extended_mode ? cc->remote_oscam : ""), cl->reader->cacheex.aio_version); + } + else if(cl->reader->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio < 9.2.3]", (cc->extended_mode ? cc->remote_oscam : "")); + } + } + else if(cl->account && cl->account->cacheex.feature_bitfield) + { + if(cl->account->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio %s]", (cc->extended_mode ? cc->remote_oscam : ""), cl->account->cacheex.aio_version); + } + else if(cl->account->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio < 9.2.3]", (cc->extended_mode ? cc->remote_oscam : "")); + } + } + else + { +#endif +#endif +#ifdef MODULE_CCCAM + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", cc->extended_mode ? cc->remote_oscam : ""); +#endif +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + } +#endif +#endif +#ifdef MODULE_CCCAM + } + + if(cfg.http_showpicons) + { + char picon_name[32]; + + int8_t is_other_proto = 0; + if(cccam_client_multics_mode(cl)) { is_other_proto = 1; } + + switch(is_other_proto) + { + case 1: + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s_r_%d", proto, cc->multics_version[0] | (cc->multics_version[1] << 8)); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "CCA", (char *)proto); + tpl_addVar(vars, TPLADD, "CCB", "r"); + tpl_printf(vars, TPLADD, "CCC", "%d", cc->multics_version[0] | (cc->multics_version[1] << 8)); + tpl_addVar(vars, TPLADD, "CCD", ""); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTOCCCAMPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s_r_%d",(char *)proto, cc->multics_version[0] | (cc->multics_version[1] << 8)); + } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "Multics, revision r%d missing icon: IC_%s_r_%d.tpl", + cc->multics_version[0] | (cc->multics_version[1] << 8), proto, cc->multics_version[0] | (cc->multics_version[1] << 8)); + } + break; + + default: + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s_%s_%s", proto, cc->remote_version, cc->remote_build); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "CCA", (char *)proto); + tpl_addVar(vars, TPLADD, "CCB", cc->remote_version); + tpl_addVar(vars, TPLADD, "CCC", cc->remote_build); +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + if(cl->reader && cl->reader->cacheex.feature_bitfield) + { + if(cl->reader->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CCD", "%s [cx-aio %s]", (cc->extended_mode ? cc->remote_oscam : ""), cl->reader->cacheex.aio_version); + } + else if(cl->reader->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CCD", "%s [cx-aio < 9.2.3]", (cc->extended_mode ? cc->remote_oscam : "")); + } + } + else if(cl->account && cl->account->cacheex.feature_bitfield) + { + if(cl->account->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CCD", "%s [cx-aio %s]", (cc->extended_mode ? cc->remote_oscam : ""), cl->account->cacheex.aio_version); + } + else if(cl->account->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CCD", "%s [cx-aio < 9.2.3]", (cc->extended_mode ? cc->remote_oscam : "")); + } + } + else + { +#endif +#endif + tpl_addVar(vars, TPLADD, "CCD", cc->extended_mode ? cc->remote_oscam : ""); +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + } +#endif +#endif + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTOCCCAMPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s_%s_%s",(char *)proto, cc->remote_version, cc->remote_build); + } + } + else + { +#endif +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + if(cl->reader && cl->reader->cacheex.feature_bitfield) + { + if(cl->reader->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio %s] missing icon: IC_%s_%s_%s.tpl", (cc->extended_mode ? cc->remote_oscam : ""), cl->reader->cacheex.aio_version, proto, cc->remote_version, cc->remote_build); + } + else if(cl->reader->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio < 9.2.3] missing icon: IC_%s_%s_%s.tpl", (cc->extended_mode ? cc->remote_oscam : ""), proto, cc->remote_version, cc->remote_build); + } + } + else if(cl->account && cl->account->cacheex.feature_bitfield) + { + if(cl->account->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio %s] missing icon: IC_%s_%s_%s.tpl", (cc->extended_mode ? cc->remote_oscam : ""), cl->account->cacheex.aio_version, proto, cc->remote_version, cc->remote_build); + } + else if(cl->account->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio < 9.2.3] missing icon: IC_%s_%s_%s.tpl", (cc->extended_mode ? cc->remote_oscam : ""), proto, cc->remote_version, cc->remote_build); + } + } + else + { +#endif +#endif +#ifdef MODULE_CCCAM + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s missing icon: IC_%s_%s_%s.tpl", + cc->extended_mode ? cc->remote_oscam : "", proto, cc->remote_version, cc->remote_build); +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + } +#endif +#endif + } + break; + } + } + } + return; + } +#endif +#ifdef CS_CACHEEX_AIO +#if (defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP)) && defined(CS_CACHEEX) + if(strncmp(proto, "cs3", 3) == 0) + { + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", (char *)proto); + + char aiover[32]; + aiover[0] = '\0'; + + if(cl->account && cl->cacheex_aio_checked) + { + if(cl->account->cacheex.feature_bitfield & 32) + { + snprintf(aiover, sizeof(aiover) / sizeof(char) -1, "%s", cl->account->cacheex.aio_version); + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", aiover); + } + else if(cl->account->cacheex.feature_bitfield) + { + snprintf(aiover, sizeof(aiover) / sizeof(char) -1, "%s", "[cx-aio: < 9.2.3]"); + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", aiover); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", ""); + } + } + + if(cl->reader && cl->cacheex_aio_checked) + { + if(cl->reader->cacheex.feature_bitfield & 32) + { + snprintf(aiover, sizeof(aiover) / sizeof(char) -1, "%s", cl->reader->cacheex.aio_version); + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", aiover); + } + else if(cl->reader->cacheex.feature_bitfield) + { + snprintf(aiover, sizeof(aiover) / sizeof(char) -1, "%s", "[cx-aio: < 9.2.3]"); + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", aiover); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", ""); + } + } + + if(cfg.http_showpicons) + { + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", (char *)proto); + + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", proto); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "CAMD3A", (char *)proto); + if(aiover[0] == '\0') + tpl_addVar(vars, TPLADD, "AIOVER", ""); + else + tpl_printf(vars, TPLADD, "AIOVER", "[cx-aio %s]", aiover); + + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTOCAMD3AIOPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char *)proto); + } + } + else + { + if(cl->account && cl->cacheex_aio_checked) + { + if(cl->account->cacheex.feature_bitfield & 32) + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl [cx-aio %s]", proto, cl->account->cacheex.aio_version); + else if(cl->account->cacheex.feature_bitfield) + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl [cx-aio < 9.2.3]", proto); + else + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl", proto); + } + + if(cl->reader && cl->cacheex_aio_checked) + { + if(cl->reader->cacheex.feature_bitfield & 32) + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl [cx-aio %s]", proto, cl->reader->cacheex.aio_version); + else if(cl->reader->cacheex.feature_bitfield) + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl [cx-aio < 9.2.3]", proto); + else + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl", proto); + } + } + } + return; + } +#endif +#endif + +#ifdef HAVE_DVBAPI + if(streq(proto, "dvbapi") && cl->typ == 'c' && strcmp(dvbapi_get_client_name(), "")) + { + if (!apicall) + tpl_printf(vars, TPLADD, "CLIENTPROTO", "%sclient: %s
protocol version: %d
", proto, dvbapi_get_client_name(), dvbapi_get_client_proto_version()); + else + tpl_printf(vars, TPLADD, "CLIENTPROTO", "%s (client: %s, protocol version: %d)", proto, dvbapi_get_client_name(), dvbapi_get_client_proto_version()); + tpl_printf(vars, TPLADD, "CLIENTPROTOSORT", "%s", proto); + return; + } +#endif + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", (char *)proto); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOSORT", (char *)proto); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", proto); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "OTHER", (char *)proto); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTOOTHERPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char *)proto); + } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl", proto); + } + } +} + +static void kill_account_thread(struct s_auth *account) +{ + struct s_client *cl; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->account == account) + { + if(get_module(cl)->type & MOD_CONN_NET) + { + kill_thread(cl); + } + else + { + cl->account = first_client->account; + } + } + } +} + +static char *send_oscam_user_config(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + struct s_auth *account; + struct s_client *cl; + char *user = getParam(params, "user"); + int32_t found = 0; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + + if(!apicall) + { + setActiveMenu(vars, MNU_USERS); + } + if(strcmp(getParam(params, "action"), "reinit") == 0) + { + if(!cfg.http_readonly) + { refresh_oscam(REFR_ACCOUNTS); } + } + if(strcmp(getParam(params, "action"), "delete") == 0) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. No deletion will be made!"); + } + else + { + struct s_auth *account_prev = NULL; + + for(account = cfg.account; (account); account = account->next) + { + if(strcmp(account->usr, user) == 0) + { + if(account_prev == NULL) + { cfg.account = account->next; } + else + { account_prev->next = account->next; } + ll_clear(account->aureader_list); + kill_account_thread(account); + add_garbage(account); + found = 1; + break; + } + account_prev = account; + } + if(found > 0) + { + if(write_userdb() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + } + else { tpl_addMsg(vars, "Sorry but the specified user doesn't exist. No deletion will be made!"); } + } + } + + if((strcmp(getParam(params, "action"), "disable") == 0) || (strcmp(getParam(params, "action"), "enable") == 0)) + { + account = get_account_by_name(getParam(params, "user")); + if(account) + { + if(strcmp(getParam(params, "action"), "disable") == 0) + { + account->disabled = 1; + kill_account_thread(account); + } + else + { account->disabled = 0; } + if(write_userdb() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + } + else + { + tpl_addMsg(vars, "Sorry but the specified user doesn't exist. No deletion will be made!"); + } + } + + if(strcmp(getParam(params, "action"), "resetstats") == 0) + { + account = get_account_by_name(getParam(params, "user")); + if(account) { clear_account_stats(account); } + } + + if(strcmp(getParam(params, "action"), "resetuserstats") == 0) + { + clear_info_clients_stats(); + } + + if(strcmp(getParam(params, "action"), "resetreaderstats") == 0) + { + clear_info_readers_stats(); + } + + if(strcmp(getParam(params, "action"), "resetalluserstats") == 0) + { + clear_all_account_stats(); + } + + if((strcmp(getParam(params, "part"), "adduser") == 0) && (!cfg.http_readonly)) + { + tpl_addVar(vars, TPLAPPEND, "NEWUSERFORM", tpl_getTpl(vars, "ADDNEWUSER")); + } + + /* List accounts*/ + char *status, *expired, *classname, *lastchan; + time_t now = time((time_t *)0); + int32_t isec = 0, chsec = 0; + + char *filter = NULL; + int32_t clientcount = 0; + if(apicall) + { + filter = getParam(params, "label"); + } + int8_t grp_set = 0; + int8_t expdate_set = 0; + int32_t total_users = 0; + int32_t disabled_users = 0; + int32_t expired_users = 0; + int32_t expired_or_disabled_users = 0; + int32_t connected_users = 0; + int32_t online_users = 0; + int32_t casc_users = 0; + int32_t casc_users2 = 0; + int32_t n_request = 0; + int existing_insert = 0; + + for(account = cfg.account; (account); account = account->next) + { + if(account->expirationdate){ + expdate_set = 1; + } + + if(account->next){ + if(account->grp != account->next->grp){ + grp_set = 1; + } + } + if(expdate_set && grp_set) + break; + } + + for(account = cfg.account; (account); account = account->next) + { + //clear for next client + total_users++; + isactive = 1; + + status = "offline"; + classname = "offline"; + isec = 0; + chsec = 0; + + //reset caid/srevid template variables + tpl_addVar(vars, TPLADD, "CLIENTCAID", ""); + tpl_addVar(vars, TPLADD, "CLIENTPROVID", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVID", ""); + tpl_addVar(vars, TPLADD, "LASTCHANNEL", ""); + tpl_addVar(vars, TPLADD, "LASTCHANNELSORT", ""); + tpl_addVar(vars, TPLADD, "USERMD5", ""); + tpl_addVar(vars, TPLADD, "CWLASTRESPONSET", ""); + tpl_addVar(vars, TPLADD, "CWLASTRESPONSETMS", ""); + tpl_addVar(vars, TPLADD, "CLIENTIP", ""); + tpl_addVar(vars, TPLADD, "LASTCHANNELTITLE", ""); + tpl_addVar(vars, TPLADD, "LASTCHANNELSORT", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNELAPI", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNEL", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMETOSLEEP", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMETOSLEEPAPI", ""); + tpl_addVar(vars, TPLADD, "IDLESECS", ""); + tpl_addVar(vars, TPLADD, "UNOTIFY", ""); + + if(account->expirationdate && account->expirationdate < now) + { + expired = " (expired)"; + classname = "expired"; + expired_users++; + isactive = 0; + } + else + { + expired = ""; + } + + if(account->disabled != 0) + { + expired = " (disabled)"; + classname = "disabled"; + tpl_addVar(vars, TPLADD, "SWITCHICO", "image?i=ICENA"); + tpl_addVar(vars, TPLADD, "SWITCHTITLE", "Enable"); + tpl_addVar(vars, TPLADD, "SWITCH", "enable"); + disabled_users++; + isactive = 0; + } + else + { + tpl_addVar(vars, TPLADD, "SWITCHICO", "image?i=ICDIS"); + tpl_addVar(vars, TPLADD, "SWITCHTITLE", "Disable"); + tpl_addVar(vars, TPLADD, "SWITCH", "disable"); + } + if((account->expirationdate && account->expirationdate < now)||account->disabled != 0) + { + expired_or_disabled_users++; + } + + int32_t lastresponsetm = 0, latestactivity = 0; + const char *proto = ""; + double cwrate = 0.0, cwrate2 = 0.0; + + //search account in active clients + isactive = 0; + int16_t nrclients = 0; + struct s_client *latestclient = NULL; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->account && !strcmp(cl->account->usr, account->usr)) + { + if(cl->lastecm > latestactivity || cl->login > latestactivity) + { + if(cl->lastecm > cl->login) { latestactivity = cl->lastecm; } + else { latestactivity = cl->login; } + latestclient = cl; + } + nrclients++; + } + } + if(account->cwfound + account->cwnot + account->cwcache > 0) + { + cwrate = now - account->firstlogin; + cwrate /= (account->cwfound + account->cwnot + account->cwcache); + } + + casc_users = 0; + casc_users2 = 0; + int8_t conn = 0; + if(latestclient != NULL) + { + char channame[CS_SERVICENAME_SIZE]; + status = (!apicall) ? "connected" : "connected"; + if(account->expirationdate && account->expirationdate < now) { classname = "expired"; } + else { classname = "connected";conn = 1; } + + proto = client_get_proto(latestclient); + int clientcaid = latestclient->last_caid; + int clientsrvid = latestclient->last_srvid; + int clientprovid = latestclient->last_provid; + tpl_printf(vars, TPLADD, "CLIENTCAID", "%04X", clientcaid); + tpl_printf(vars, TPLADD, "CLIENTPROVID", "%06X", clientprovid); + tpl_printf(vars, TPLADD, "CLIENTSRVID", "%04X", clientsrvid); + + if(clientsrvid != NO_SRVID_VALUE || clientcaid != NO_CAID_VALUE) + { + lastchan = xml_encode(vars, get_servicename(latestclient, clientsrvid, clientprovid, clientcaid, channame, sizeof(channame))); + tpl_addVar(vars, TPLADD, "LASTCHANNELSORT", lastchan); + } + else + { + lastchan = ""; + tpl_addVar(vars, TPLADD, "LASTCHANNELSORT", "~~~~~"); + } + + tpl_addVar(vars, TPLADDONCE, "LASTCHANNEL", lastchan); + tpl_addVar(vars, TPLADD, "LASTCHANNELTITLE", lastchan); + + if(cfg.http_showpicons ) + { + char picon_name[128]; + char picon_channame[128]; + int8_t picon_ok = 0; + + get_picon_servicename_or_null(latestclient, clientsrvid, clientprovid, clientcaid, picon_channame, sizeof(picon_channame)); + if(picon_channame[0]) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "%s", picon_channame); + picon_ok = picon_exists(picon_name); + + if(!picon_ok && picon_servicename_remve_hd(picon_channame, sizeof(picon_channame))) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "%s", picon_channame); + picon_ok = picon_exists(picon_name); + } + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "%04X_%06X_%04X", clientcaid, clientprovid, clientsrvid); + picon_ok = picon_exists(picon_name); + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "%04X_%04X", clientcaid, clientsrvid); + picon_ok = picon_exists(picon_name); + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "0000_%04X", clientsrvid); + picon_ok = picon_exists(picon_name); + } + if(picon_ok) + { + tpl_addVar(vars, TPLADDONCE, "LCA", picon_name); + tpl_addVar(vars, TPLADDONCE, "LCB", lastchan); + if(!apicall) + { + tpl_addVar(vars, TPLADDONCE, "LASTCHANNEL", tpl_getTpl(vars, "USERCONFIGLASTCHANEL")); + } + } + else + { + tpl_printf(vars, TPLADD, "LASTCHANNELTITLE", "missing icon: IC_%s.tpl", picon_name); + } + } + + lastresponsetm = latestclient->cwlastresptime; + if(IP_ISSET(latestclient->ip)) + { tpl_addVar(vars, TPLADD, "CLIENTIP", cs_inet_ntoa(latestclient->ip)); } + else if(latestclient->login > latestclient->logout) + { tpl_addVar(vars, TPLADD, "CLIENTIP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "CLIENTIP", ""); } + connected_users++; + casc_users = ll_count(latestclient->cascadeusers); + LL_ITER it = ll_iter_create(latestclient->cascadeusers); + struct s_cascadeuser *cu; + while((cu = ll_iter_next(&it))) + { + if(cu->cwrate > 0) + { casc_users2++; } + } + if(latestactivity > 0) + { + isec = now - latestactivity; + chsec = latestclient->lastswitch ? now - latestclient->lastswitch : 0; + if(isec < cfg.hideclient_to) + { + isactive = 1; + status = (!apicall) ? "online" : "online"; + if(account->expirationdate && account->expirationdate < now) { classname = "expired"; } + else { classname = "online"; } + if(latestclient->cwfound + latestclient->cwnot + latestclient->cwcache > 0) + { + cwrate2 = now - latestclient->login; + cwrate2 /= (latestclient->cwfound + latestclient->cwnot + latestclient->cwcache); + tpl_printf(vars, TPLADDONCE, "CWRATE2", " (%.2f)", cwrate2); + online_users++; + } + } + } + } + + n_request = 0; + if(latestclient != NULL){ + n_request = latestclient->n_request[0]; + } + + tpl_addVar(vars, TPLADD, "EXPIREVIEW", expdate_set ? "" : "exp"); + tpl_addVar(vars, TPLADD, "GRPVIEW", grp_set ? "" : "grp"); +#ifdef CS_ANTICASC + tpl_addVar(vars, TPLADD, "ANTICASCVIEW", cfg.ac_enabled ? "" : "acas"); +#endif + tpl_printf(vars, TPLADD, "CWOK", PRINTF_LOCAL_D, account->cwfound); + tpl_printf(vars, TPLADD, "CWNOK", PRINTF_LOCAL_D, account->cwnot); + tpl_printf(vars, TPLADD, "CWIGN", PRINTF_LOCAL_D, account->cwignored); + tpl_printf(vars, TPLADD, "CWTOUT", PRINTF_LOCAL_D, account->cwtout); +#ifdef CW_CYCLE_CHECK + tpl_addVar(vars, TPLADD, "CWCCYCVIEW", cfg.cwcycle_check_enable ? "" : "cwc"); + tpl_printf(vars, TPLADD, "CWCYCLECHECKED", "%d", account->cwcycledchecked); + tpl_printf(vars, TPLADD, "CWCYCLEOK", "%d", account->cwcycledok); + tpl_printf(vars, TPLADD, "CWCYCLENOK", "%d", account->cwcyclednok); + tpl_printf(vars, TPLADD, "CWCYCLEIGN", "%d", account->cwcycledign); +#endif + tpl_printf(vars, TPLADD, "CWCACHE", PRINTF_LOCAL_D, account->cwcache); + tpl_printf(vars, TPLADD, "CWTUN", PRINTF_LOCAL_D, account->cwtun); + tpl_printf(vars, TPLADD, "EMMOK", PRINTF_LOCAL_D, account->emmok); + tpl_printf(vars, TPLADD, "EMMNOK", PRINTF_LOCAL_D, account->emmnok); + tpl_printf(vars, TPLADD, "CWRATE", "%.2f", cwrate); + tpl_printf(vars, TPLADD, "CASCUSERS", "%d", casc_users); + tpl_printf(vars, TPLADD, "CASCUSERS2", "%d", casc_users2); + tpl_printf(vars, TPLADD, "CASCUSERSCOMB", "%d/%d", casc_users, casc_users2); + tpl_printf(vars, TPLADD, "N_REQUEST_MIN", "%d", n_request); + + if(isactive > 0 || conn > 0) + { + if(casc_users+casc_users2>0) + { + tpl_printf(vars, TPLADD, "CWLASTRESPONSET", "%d", lastresponsetm); + tpl_printf(vars, TPLADD, "CWLASTRESPONSETMS", PRINTF_LOCAL_D " ms", lastresponsetm); + } + tpl_addVar(vars, TPLADD, "IDLESECS", sec2timeformat(vars, isec)); + } + + if(isactive > 0) + { + tpl_printf(vars, TPLADD, "CLIENTTIMEONCHANNELAPI", "%d", chsec); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNEL", sec2timeformat(vars, chsec)); + + if(account->tosleep) + { + if(account->tosleep >0){ + tpl_printf(vars, TPLADD, "CLIENTTIMETOSLEEP", "Sleeping in %d minutes", account->tosleep - (chsec / 60)); + } else { + tpl_addVar(vars, TPLADD, "CLIENTTIMETOSLEEP", "Sleeping"); + } + tpl_printf(vars, TPLADD, "CLIENTTIMETOSLEEPAPI", "%d", account->tosleep - (chsec / 60)); + } else { + tpl_addVar(vars, TPLADD, "CLIENTTIMETOSLEEPAPI", "undefined"); + } + } +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CAMD35) || defined (MODULE_CAMD35_TCP) + if(latestclient != NULL && (latestclient->account->cacheex.feature_bitfield || latestclient->c35_extmode > 1)) +#else + if(latestclient != NULL && (latestclient->account->cacheex.feature_bitfield)) +#endif + { + const char *aio_suffix = " (cx-aio)"; + char *new_proto; + if(cs_malloc(&new_proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + if (!cs_strncat(new_proto, (char *)proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + cs_log("FIXME!"); + } + if (cs_strncat(new_proto, (char *)aio_suffix, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + webif_add_client_proto(vars, latestclient, (const char *)new_proto, apicall); + } else { + cs_log("FIXME!"); + } + free(new_proto); + } + } else { +#endif + webif_add_client_proto(vars, latestclient, proto, apicall); +#ifdef CS_CACHEEX_AIO + } +#endif + + tpl_addVar(vars, TPLADD, "CLASSNAME", classname); + MD5((uint8_t *)account->usr, cs_strlen(account->usr), md5tmp); + int z; + tpl_addVar(vars, TPLADD, "USERMD5","id_"); + for (z = 0; z < MD5_DIGEST_LENGTH; z++) + { + tpl_printf(vars, TPLAPPEND, "USERMD5", "%02x", md5tmp[z]); + } + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, account->usr)); + tpl_addVar(vars, TPLADD, "USERNAMEENC", urlencode(vars, account->usr)); + if(!existing_insert) + { + tpl_printf(vars, TPLADD, "EXISTING_INS", "'%s'", urlencode(vars, account->usr)); + existing_insert++; + }else + { + tpl_printf(vars, TPLAPPEND, "EXISTING_INS", ",'%s'", urlencode(vars, account->usr)); + } + + if(account->description) + tpl_printf(vars, TPLADD, "DESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, account->description)); + else + tpl_addVar(vars, TPLADD, "DESCRIPTION", ""); + + if(cfg.http_showpicons && !apicall) + tpl_addVar(vars, TPLADD, "USERBIT", tpl_getTpl(vars, picon_exists(xml_encode(vars, account->usr)) ? "USERICON" : "USERNOICON")); + else + tpl_addVar(vars, TPLADD, "USERBIT", tpl_getTpl(vars, "USERLABEL")); + + char *value = mk_t_group(account->grp); + tpl_addVar(vars, TPLADD, "GROUPS", value); + free_mk_t(value); + tpl_addVar(vars, TPLADD, "STATUS", status); + tpl_addVar(vars, TPLAPPEND, "STATUS", expired); + + if(nrclients > 1) + { + tpl_printf(vars, TPLADD, "UNOTIFY", "%d", nrclients); + tpl_addVar(vars, TPLADDONCE, "CLIENTCOUNTNOTIFIER", tpl_getTpl(vars, "CLIENTCOUNTNOTIFIERBIT")); + } + + //Expirationdate + struct tm timeinfo; + cs_gmtime_r(&account->expirationdate, &timeinfo); + char buf [80]; + strftime(buf, 80, "%Y-%m-%d", &timeinfo); + if(strcmp(buf, "1970-01-01")) { tpl_addVar(vars, TPLADD, "EXPDATE", buf); } + else { tpl_addVar(vars, TPLADD, "EXPDATE", ""); } + + // append row to table template + if(!apicall) + { tpl_addVar(vars, TPLAPPEND, "USERCONFIGS", tpl_getTpl(vars, "USERCONFIGLISTBIT")); } + else if(!filter || strcmp(filter, account->usr) == 0 || strcmp(filter, "all") == 0 || cs_strlen(filter) == 0) + { + if(apicall == 1){ + tpl_addVar(vars, TPLAPPEND, "APIUSERCONFIGS", tpl_getTpl(vars, "APIUSERCONFIGLISTBIT")); + } else if (apicall == 2){ + tpl_printf(vars, TPLADD, "JSONDELIMITER", "%s", (total_users > 1)?",":""); + tpl_addVar(vars, TPLAPPEND, "APIUSERCONFIGS", tpl_getTpl(vars, "JSONUSERBIT")); + } + ++clientcount; + } + } + + tpl_printf(vars, TPLADD, "TOTAL_USERS", "%d", total_users); + tpl_printf(vars, TPLADD, "TOTAL_DISABLED", "%d", disabled_users); + tpl_printf(vars, TPLADD, "TOTAL_EXPIRED", "%d", expired_users); + tpl_printf(vars, TPLADD, "TOTAL_ACTIVE", "%d", total_users - expired_or_disabled_users); + tpl_printf(vars, TPLADD, "TOTAL_CONNECTED", "%d", connected_users); + tpl_printf(vars, TPLADD, "TOTAL_ONLINE", "%d", online_users); + + //CM info + tpl_addVar(vars, TPLADD, "DISPLAYREADERINFO", "hidden"); // no readerinfo in users + set_ecm_info(vars); + + if(!apicall) + { return tpl_getTpl(vars, "USERCONFIGLIST"); } + else + { + if(!filter || clientcount > 0) + { + return tpl_getTpl(vars, (apicall==1)?"APIUSERCONFIGLIST":"JSONUSER"); + } + else + { + tpl_printf(vars, TPLADD, "APIERRORMESSAGE", "Invalid client %s", xml_encode(vars, filter)); + return tpl_getTpl(vars, "APIERROR"); + } + } + +} + +#define ENTITLEMENT_PAGE_SIZE 500 + +#ifdef MODULE_CCCAM +static void print_cards(struct templatevars *vars, struct uriparams *params, struct cc_card **cardarray, int32_t cardsize, + int8_t show_global_list, struct s_reader *rdr, int32_t offset, int32_t apicall) +{ + if(cardarray) + { + uint8_t serbuf[8]; + int32_t i, count = 0; + char provname[83]; + struct cc_card *card; + int32_t cardcount = 0; + int32_t providercount = 0; + int32_t nodecount = 0; + + char *provider = ""; + + // @todo alno: sort by click, 0=ascending, 1=descending (maybe two buttons or reverse on second click) + for(i = offset; i < cardsize; ++i) + { + card = cardarray[i]; + if(count == ENTITLEMENT_PAGE_SIZE) + { break; } + count++; + + if(!apicall) + { + if(show_global_list) + { rdr = card->origin_reader; } + if(rdr) + { tpl_printf(vars, TPLADD, "HOST", "%s:%d", xml_encode(vars, rdr->device), rdr->r_port); } + tpl_printf(vars, TPLADD, "CAID", "%04X", card->caid); + tpl_printf(vars, TPLADD, "CARDTYPE", "%02X", card->card_type); + } + else + { + tpl_printf(vars, TPLADD, "APICARDNUMBER", "%d", cardcount); + tpl_printf(vars, TPLADD, "APICAID", "%04X", card->caid); + tpl_printf(vars, TPLADD, "APICARDTYPE", "%02X", card->card_type); + } + + if(cc_UA_valid(card->hexserial)) //Add UA: + { + cc_UA_cccam2oscam(card->hexserial, serbuf, card->caid); + char tmp[20]; + tpl_printf(vars, TPLAPPEND, "HOST", "
\nUA_OSCam:%s", cs_hexdump(0, serbuf, 8, tmp, 20)); + tpl_printf(vars, TPLAPPEND, "HOST", "
\nUA_CCcam:%s", cs_hexdump(0, card->hexserial, 8, tmp, 20)); + } + if(!apicall) + { + int32_t n; + char channame[CS_SERVICENAME_SIZE]; + int8_t sidname = 0; + LL_ITER its = ll_iter_create(card->goodsids); + struct cc_srvid *srv; + n = 0; + if(strcmp(getParam(params, "button"), "Show detail list") == 0) + { sidname = 1; } + + tpl_addVar(vars, TPLADD, "SERVICESGOOD", ""); + while((srv = ll_iter_next(&its))) + { + if(sidname) + { + tpl_printf(vars, TPLAPPEND, "SERVICESGOOD", "%04X - %s
", srv->sid, xml_encode(vars, get_servicename(cur_client(), srv->sid, 0, card->caid, channame, sizeof(channame)))); + } else { + tpl_printf(vars, TPLAPPEND, "SERVICESGOOD", "%04X%s", srv->sid, ++n % 10 == 0 ? "
\n" : " "); + } + } + + its = ll_iter_create(card->badsids); + n = 0; + tpl_addVar(vars, TPLADD, "SERVICESBAD", ""); + while((srv = ll_iter_next(&its))) + { + if(sidname) + { + tpl_printf(vars, TPLAPPEND, "SERVICESBAD", "%04X - %s
", srv->sid, xml_encode(vars, get_servicename(cur_client(), srv->sid, 0, card->caid, channame, sizeof(channame)))); + } else { + tpl_printf(vars, TPLAPPEND, "SERVICESBAD", "%04X%s", srv->sid, ++n % 10 == 0 ? "
\n" : " "); + } + } + } + + tpl_addVar(vars, TPLADD, "SYSTEM", get_cardsystem_desc_by_caid(card->caid)); + + tpl_printf(vars, TPLADD, "SHAREID", "%08X", card->id); + tpl_printf(vars, TPLADD, "REMOTEID", "%08X", card->remote_id); + tpl_printf(vars, TPLADD, "UPHOPS", "%d", card->hop); + tpl_printf(vars, TPLADD, "MAXDOWN", "%d", card->reshare); + + LL_ITER pit = ll_iter_create(card->providers); + struct cc_provider *prov; + + providercount = 0; + + if(!apicall) + { tpl_addVar(vars, TPLADD, "PROVIDERS", ""); } + else + { tpl_addVar(vars, TPLADD, "PROVIDERLIST", ""); } + + while((prov = ll_iter_next(&pit))) + { + provider = xml_encode(vars, get_provider(prov->prov, card->caid, provname, sizeof(provname))); + + if(!apicall) + { + if(prov->sa[0] || prov->sa[1] || prov->sa[2] || prov->sa[3]) + { + tpl_printf(vars, TPLAPPEND, "PROVIDERS", "%s SA:%02X%02X%02X%02X
\n", provider, prov->sa[0], prov->sa[1], prov->sa[2], prov->sa[3]); + } + else + { + tpl_printf(vars, TPLAPPEND, "PROVIDERS", "%s
\n", provider); + } + } + else + { + if(prov->sa[0] || prov->sa[1] || prov->sa[2] || prov->sa[3]) + { tpl_printf(vars, TPLADD, "APIPROVIDERSA", "%02X%02X%02X%02X", prov->sa[0], prov->sa[1], prov->sa[2], prov->sa[3]); } + else + { tpl_addVar(vars, TPLADD, "APIPROVIDERSA", ""); } + tpl_printf(vars, TPLADD, "APIPROVIDERCAID", "%04X", card->caid); + tpl_printf(vars, TPLADD, "APIPROVIDERPROVID", "%06X", prov->prov); + tpl_printf(vars, TPLADD, "APIPROVIDERNUMBER", "%d", providercount); + tpl_addVar(vars, TPLADD, "APIPROVIDERNAME", xml_encode(vars, provider)); + tpl_addVar(vars, TPLAPPEND, "PROVIDERLIST", tpl_getTpl(vars, "APICCCAMCARDPROVIDERBIT")); + + } + providercount++; + tpl_printf(vars, TPLADD, "APITOTALPROVIDERS", "%d", providercount); + } + + LL_ITER nit = ll_iter_create(card->remote_nodes); + uint8_t *node; + + nodecount = 0; + if(!apicall) { tpl_addVar(vars, TPLADD, "NODES", ""); } + else { tpl_addVar(vars, TPLADD, "NODELIST", ""); } + + while((node = ll_iter_next(&nit))) + { + + if(!apicall) + { + tpl_printf(vars, TPLAPPEND, "NODES", "%02X%02X%02X%02X%02X%02X%02X%02X
\n", + node[0], node[1], node[2], node[3], node[4], node[5], node[6], node[7]); + } + else + { + tpl_printf(vars, TPLADD, "APINODE", "%02X%02X%02X%02X%02X%02X%02X%02X", node[0], node[1], node[2], node[3], node[4], node[5], node[6], node[7]); + tpl_printf(vars, TPLADD, "APINODENUMBER", "%d", nodecount); + tpl_addVar(vars, TPLAPPEND, "NODELIST", tpl_getTpl(vars, "APICCCAMCARDNODEBIT")); + } + nodecount++; + tpl_printf(vars, TPLADD, "APITOTALNODES", "%d", nodecount); + } + + if(!apicall) + { tpl_addVar(vars, TPLAPPEND, "CCCAMSTATSENTRY", tpl_getTpl(vars, "ENTITLEMENTCCCAMENTRYBIT")); } + else + { tpl_addVar(vars, TPLAPPEND, "CARDLIST", tpl_getTpl(vars, "APICCCAMCARDBIT")); } + + cardcount++; + } + // set previous Link if needed + if(offset >= ENTITLEMENT_PAGE_SIZE) + { + tpl_printf(vars, TPLAPPEND, "CONTROLS", " << PREVIOUS < ", + offset - ENTITLEMENT_PAGE_SIZE, + getParam(params, "globallist"), + urlencode(vars, getParam(params, "label"))); + } + + // set next link if needed + if(cardsize > count && offset < cardsize) + { + tpl_printf(vars, TPLAPPEND, "CONTROLS", " > NEXT >> ", + offset + ENTITLEMENT_PAGE_SIZE, + getParam(params, "globallist"), + urlencode(vars, getParam(params, "label"))); + } + + if(!apicall) + { + tpl_printf(vars, TPLADD, "TOTALS", "card count=%d", cardsize); + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENT", tpl_getTpl(vars, "ENTITLEMENTCCCAMBIT")); + } + else + { + tpl_printf(vars, TPLADD, "APITOTALCARDS", "%d", cardsize); + } + + } + else + { + if(!apicall) + { + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENT", tpl_getTpl(vars, "ENTITLEMENTGENERICBIT")); + tpl_addVar(vars, TPLADD, "LOGHISTORY", "no cards found
\n"); + } + else + { + tpl_printf(vars, TPLADD, "APITOTALCARDS", "%d", 0); + } + } + +} +#endif + +static char *send_oscam_entitlement(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + if(!apicall) { setActiveMenu(vars, MNU_READERS); } + char *reader_ = getParam(params, "label"); +#ifdef MODULE_CCCAM +#ifdef MODULE_CCCSHARE + char *sharelist_ = getParam(params, "globallist"); + int32_t show_global_list = sharelist_ && sharelist_[0] == '1'; +#else + int32_t show_global_list = 0; +#endif + + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(show_global_list || cs_strlen(reader_) || (rdr && rdr->typ == R_CCCAM)) + { + + if(show_global_list || (rdr && rdr->typ == R_CCCAM && rdr->enable)) + { + + if(show_global_list) + { + tpl_addVar(vars, TPLADD, "READERNAME", "GLOBAL"); + tpl_addVar(vars, TPLADD, "APIHOST", "GLOBAL"); + tpl_addVar(vars, TPLADD, "APIHOSTPORT", "GLOBAL"); + } + else + { + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "APIHOST", xml_encode(vars, rdr->device)); + tpl_printf(vars, TPLADD, "APIHOSTPORT", "%d", rdr->r_port); + } + + int32_t offset = atoi(getParam(params, "offset")); //should be 0 if parameter is missed on very first call + int32_t cardsize; +#ifdef MODULE_CCCSHARE + if(show_global_list) + { + int32_t i; + LLIST **sharelist = get_and_lock_sharelist(); + LLIST *sharelist2 = ll_create("web-sharelist"); + for(i = 0; i < CAID_KEY; i++) + { + if(sharelist[i]) + { ll_putall(sharelist2, sharelist[i]); } + } + unlock_sharelist(); + struct cc_card **cardarray = get_sorted_card_copy(sharelist2, 0, &cardsize); + ll_destroy(&sharelist2); + print_cards(vars, params, cardarray, cardsize, 1, NULL, offset, apicall); + NULLFREE(cardarray); + } + else +#endif + { + struct s_client *rc = rdr->client; + struct cc_data *rcc = (rc) ? rc->cc : NULL; + if(rcc && rcc->cards) + { + struct cc_card **cardarray = get_sorted_card_copy(rcc->cards, 0, &cardsize); + print_cards(vars, params, cardarray, cardsize, 0, rdr, offset, apicall); + NULLFREE(cardarray); + } + } + + } + else + { +#else + if(cs_strlen(reader_)) + { + { + struct s_reader *rdr; +#endif + tpl_addVar(vars, TPLADD, "LOGHISTORY", "->"); + // normal non-cccam reader + + rdr = get_reader_by_label(reader_); + + if(rdr) + { + struct s_client *cl = rdr->client; + if(rdr->ll_entitlements) + { + + time_t now = time((time_t *)0); + + struct tm start_t, end_t; + LL_ITER itr = ll_iter_create(rdr->ll_entitlements); + S_ENTITLEMENT *item; + + tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", "

New Structure:
"); + char tbuffer[83]; +#ifdef WITH_EMU + char keyBuffer[1024]; +#endif + int jsondelimiter = 0; + while((item = ll_iter_next(&itr))) + { +#ifdef WITH_EMU + if(item->isKey) + { + tpl_addVar(vars, TPLADD, "ENTSTARTDATE", ""); + tpl_addVar(vars, TPLADD, "ENTENDDATE", ""); + cs_hexdump(0, item->key, item->keyLength, keyBuffer, sizeof(keyBuffer)); + tpl_addVar(vars, TPLADD, "ENTEXPIERED", "e_valid"); + tpl_printf(vars, TPLADD, "ENTCAID", "%04X", item->caid); + tpl_printf(vars, TPLADD, "ENTPROVID", "%08X", item->provid); + tpl_addVar(vars, TPLADD, "ENTID", item->name); + tpl_addVar(vars, TPLADD, "ENTCLASS", keyBuffer); + if(item->isData) { tpl_addVar(vars, TPLADD, "ENTTYPE", "data"); } + else { tpl_addVar(vars, TPLADD, "ENTTYPE", "key"); } + tpl_addVar(vars, TPLADD, "ENTRESNAME", ""); + + if((strcmp(getParam(params, "hideexpired"), "1") != 0) || (item->end > now)) + { tpl_addVar(vars, TPLAPPEND, "READERENTENTRY", tpl_getTpl(vars, "ENTITLEMENTITEMBIT")); } + + continue; + } +#endif + + localtime_r(&item->start, &start_t); + localtime_r(&item->end, &end_t); + + if(!apicall) + { strftime(tbuffer, 30, "%Y-%m-%d", &start_t); } + else + { strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", &start_t); } + tpl_addVar(vars, TPLADD, "ENTSTARTDATE", tbuffer); + + if(!apicall) + { strftime(tbuffer, 30, "%Y-%m-%d", &end_t); } + else + { strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", &end_t); } + tpl_addVar(vars, TPLADD, "ENTENDDATE", tbuffer); + + tpl_addVar(vars, TPLADD, "ENTEXPIERED", item->end > now ? "e_valid" : "e_expired"); + tpl_printf(vars, TPLADD, "ENTCAID", "%04X", item->caid); + tpl_printf(vars, TPLADD, "ENTPROVID", "%06X", item->provid); + tpl_printf(vars, TPLADD, "ENTID", "%08X%08X", (uint32_t)(item->id >> 32), (uint32_t)item->id); + tpl_printf(vars, TPLADD, "ENTCLASS", "%08X", item->class); + tpl_addVar(vars, TPLADD, "ENTTYPE", entitlement_type[item->type]); + + char *entresname; + entresname = xml_encode(vars, get_tiername((uint16_t)(item->id & 0xFFFF), item->caid, tbuffer)); + if(!tbuffer[0]) + { entresname = xml_encode(vars, get_provider(item->provid, item->caid, tbuffer, sizeof(tbuffer))); } + tpl_addVar(vars, TPLADD, "ENTRESNAME", entresname); + + if((strcmp(getParam(params, "hideexpired"), "1") != 0) || (item->end > now)) + { tpl_addVar(vars, TPLAPPEND, "READERENTENTRY", tpl_getTpl(vars, "ENTITLEMENTITEMBIT")); } + + if(apicall==2) + { + tpl_printf(vars, TPLAPPEND, "APIENTITLEMENTLIST","%s%s",jsondelimiter?",":"", tpl_getTpl(vars, "JSONENTITLEMENTBIT")); + jsondelimiter++; + } + } + } + + if(cl && cl->typ) + { tpl_printf(vars, TPLADD, "READERTYPE", "%c", cl->typ); } + else + { tpl_addVar(vars, TPLADD, "READERTYPE", "null"); } + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + + int8_t i, j; + for(i = 0; i < 15; i++) { tpl_printf(vars, TPLAPPEND, "READERROM", "%c", rdr->rom[i]); } + if(rdr->hexserial[0] || rdr->hexserial[1]) { i = 0; } + else { i = 2; } + if(rdr->hexserial[6] || rdr->hexserial[7]) { j = 8; } + else { j = 6; } + for(; i < j; i++) { tpl_printf(vars, TPLAPPEND, "READERSERIAL", "%02X%s", rdr->hexserial[i], i < j - 1 ? " " : ""); } + for(i = 0; i < rdr->nprov; i++) + { + for(j = 0; j < 4; j++) { tpl_printf(vars, TPLAPPEND, "READERPROVIDS", "%02X ", rdr->prid[i][j]); } + tpl_addVar(vars, TPLAPPEND, "READERPROVIDS", i == 0 ? "(sysid)
\n" : "       
\n"); + } + +#ifdef READER_VIDEOGUARD + //CountryCode Vg card + char add_nds_line = 0; + if(rdr->VgCountryC[0]) + { + for(i = 0; i < 3; i++) { tpl_printf(vars, TPLAPPEND, "READERCOUNTRYC", "%c", rdr->VgCountryC[i]); } + add_nds_line = 1; + } + else + { + tpl_addVar(vars, TPLADD, "READERCOUNTRYC", "n/a"); + } + + //regional code for Vg card + if(rdr->VgRegionC[0]) + { + for(i = 0; i < 8; i++) { tpl_printf(vars, TPLAPPEND, "READER_RCODE", "%c", rdr->VgRegionC[i]); } + add_nds_line = 1; + } + else + { + tpl_addVar(vars, TPLADD, "READER_RCODE", "n/a"); + } + + //Pin Vg card + if(rdr->VgPin) + { + tpl_printf(vars, TPLAPPEND, "READERPIN", "%04i", rdr->VgPin); + add_nds_line = 1; + } + else + { + tpl_addVar(vars, TPLADD, "READERPIN", "n/a"); + } + + //Fuse Vg card + if(rdr->VgFuse) + { + tpl_printf(vars, TPLAPPEND, "READERFUSE", "%02X", rdr->VgFuse); + add_nds_line = 1; + } + + if(caid_is_videoguard(rdr->caid)) + { + tpl_printf(vars, TPLAPPEND, "READERPAYLOAD", "%02X %02X %02X %02X %02X %02X", rdr->VgLastPayload[0], + rdr->VgLastPayload[1], rdr->VgLastPayload[2], rdr->VgLastPayload[3], rdr->VgLastPayload[4], rdr->VgLastPayload[5]); + add_nds_line = 1; + } + + //credit on Vg card + if(rdr->VgCredit) + { + tpl_printf(vars, TPLAPPEND, "READERCREDIT", "%i", rdr->VgCredit); + add_nds_line = 1; + } + else + { + tpl_addVar(vars, TPLADD, "READERCREDIT", "n/a"); + } +#endif + + if(rdr->card_valid_to) + { + struct tm vto_t; + char vtobuffer[30]; + localtime_r(&rdr->card_valid_to, &vto_t); + strftime(vtobuffer, 30, "%Y-%m-%d", &vto_t); + tpl_addVar(vars, TPLADD, "READERCARDVALIDTO", vtobuffer); + } + else + { + tpl_addVar(vars, TPLADD, "READERCARDVALIDTO", "n/a"); + } + + if(rdr->irdId[0]) + { + for(i = 0; i < 4; i++) { tpl_printf(vars, TPLAPPEND, "READERIRDID", "%02X ", rdr->irdId[i]); } + } + else + { + tpl_addVar(vars, TPLADD, "READERIRDID", "n/a"); + } + + if(rdr->card_atr_length) + for(i = 0; i < rdr->card_atr_length; i++) { tpl_printf(vars, TPLAPPEND, "READERATR", "%02X ", rdr->card_atr[i]); } + + if(caid_is_seca(rdr->caid) || caid_is_viaccess(rdr->caid)) + { + if(rdr->maturity == 0xF) + { + tpl_printf(vars, TPLAPPEND, "READERMATURITY", "%s ", "no limit"); + } + else + { + tpl_printf(vars, TPLAPPEND, "READERMATURITY", "%d+", rdr->maturity); + } + } + else + { + tpl_printf(vars, TPLAPPEND, "READERMATURITY", "%s ", "n/a"); + } + + if (rdr->csystem) + tpl_addVar(vars, TPLADD, "READERCSYSTEM", rdr->csystem->desc); + +#ifdef READER_VIDEOGUARD + if(add_nds_line) + { + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENTNDS", tpl_getTpl(vars, "ENTITLEMENTBITNDS")); + } +#endif + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENT", tpl_getTpl(vars, "ENTITLEMENTBIT")); + } + else + { + tpl_addMsg(vars, "Reader does not exist or is not started!"); + } + } + + } + else + { + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENT", tpl_getTpl(vars, "ENTITLEMENTGENERICBIT")); + } + + if(!apicall) + { return tpl_getTpl(vars, "ENTITLEMENTS"); } + else + { + if(apicall==1) + { return tpl_getTpl(vars, "APICCCAMCARDLIST"); } + else + { return tpl_getTpl(vars, "JSONENTITLEMENTS"); } + } +} + +#ifdef WEBIF_LIVELOG +static char *send_oscam_logpoll(struct templatevars * vars, struct uriparams * params) +{ + + uint64_t lastid = 0; + +#ifdef WITH_DEBUG + tpl_addVar(vars, TPLADD, "LOG_DEBUGMENU", tpl_getTpl(vars, "LOGDEBUGMENU")); +#endif + tpl_addVar(vars, TPLADD, "LOG_SIZEMENU", tpl_getTpl(vars, "LOGSIZEMENU")); + tpl_addVar(vars, TPLADD, "TITLEADD1", "Move mouse over log-window to stop scroll"); + + if(strcmp(getParam(params, "lastid"), "start") == 0){ + setActiveMenu(vars, MNU_LIVELOG); + return tpl_getTpl(vars, "LOGPAGE"); + } + else + { + lastid = strtoull(getParam(params, "lastid"), NULL, 10); + } + + char *dot = ""; //Delimiter + +#ifdef WITH_DEBUG + char *debuglvl = getParam(params, "debug"); + if(cs_strlen(debuglvl) > 0) { + int32_t dblvl = atoi(debuglvl); + if(cs_dblevel != dblvl) { + if(dblvl >= 0 && dblvl <= 65535) { cs_dblevel = dblvl; } + cs_log("%s debug_level=%d", "all", cs_dblevel); + } + } + tpl_printf(vars, TPLAPPEND, "DATA","%s\"debug\":\"%d\"", dot, cs_dblevel); + dot = ","; + tpl_printf(vars, TPLAPPEND, "DATA","%s\"maxdebug\":\"%d\"",dot, MAX_DEBUG_LEVELS); +#endif + + if(cfg.loghistorylines == 0){ + tpl_printf(vars, TPLAPPEND, "DATA","%s\"logdisabled\":\"1\"",dot); + return tpl_getTpl(vars, "POLL"); + } + + if(log_history) + { + LL_ITER it = ll_iter_create(log_history); + struct s_log_history *hist; + + tpl_printf(vars, TPLAPPEND, "DATA", "%s\"lines\":[", dot); + + dot = ""; + + while((hist = (struct s_log_history*)ll_iter_next(&it))) + { + char p_usr[32]; + size_t pos1 = strcspn(hist->txt, "\t") + 1; + cs_strncpy(p_usr, hist->txt , pos1 > sizeof(p_usr) ? sizeof(p_usr) : pos1); + + char *p_txt = hist->txt + pos1; + + pos1 = strcspn(p_txt, "\n") + 1; + char str_out[pos1]; + cs_strncpy(str_out, p_txt, pos1); + uint64_t id = hist->counter; + + size_t b64_str_in = cs_strlen(xml_encode(vars, str_out)); + size_t b64_str_out = 32 + BASE64_LENGTH(b64_str_in); + char *b64_str_out_buf; + if(!cs_malloc(&b64_str_out_buf, b64_str_out)) + { continue; } + base64_encode(xml_encode(vars, str_out), b64_str_in, b64_str_out_buf, b64_str_out); + + if(id > lastid){ + tpl_printf(vars, TPLAPPEND, "DATA","%s{\"id\":\"%" PRIu64 "\",\"usr\":\"%s\",\"line\":\"%s\"}", + dot, + id, + urlencode(vars, xml_encode(vars, p_usr)), + b64_str_out_buf); + dot = ","; // next in Array with leading delimiter + } + NULLFREE(b64_str_out_buf); + } + } + + tpl_addVar(vars, TPLAPPEND, "DATA", "]"); + return tpl_getTpl(vars, "POLL"); +} +#endif + +static char *send_oscam_status(struct templatevars * vars, struct uriparams * params, int32_t apicall) +{ + const char *usr; + int32_t lsec, isec, chsec, con, cau = 0; + time_t now = time((time_t *)0); + struct tm lt; + int delimiter=0; + + if(!apicall) + { + setActiveMenu(vars, MNU_STATUS); + if (config_enabled(WITH_LB)) + tpl_addVar(vars, TPLADD, "STATUSCOL14HEAD","LB Value/"); + } + if(strcmp(getParam(params, "action"), "kill") == 0) + { + char *cptr = getParam(params, "threadid"); + struct s_client *cl = NULL; + if(cs_strlen(cptr) > 1) + { sscanf(cptr, "%p", (void **)(void *)&cl); } + + if(cl && is_valid_client(cl)) + { + if(is_dvbapi_usr(cl->account->usr)) + { + cs_log("WebIF from %s requests to kill dvbapi client %s -> ignoring!", cs_inet_ntoa(GET_IP()), cl->account->usr); + } + else + { + kill_thread(cl); + cs_log("Client %s killed by WebIF from %s", cl->account->usr, cs_inet_ntoa(GET_IP())); + } + } + } + + if(strcmp(getParam(params, "action"), "resetuserstats") == 0) + { + clear_info_clients_stats(); + } + if(strcmp(getParam(params, "action"), "resetreaderstats") == 0) + { + clear_info_readers_stats(); + } + if(strcmp(getParam(params, "action"), "restart") == 0) + { + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + if(rdr->typ != R_GBOX) + { + add_job(rdr->client, ACTION_READER_RESTART, NULL, 0); + } +#ifdef MODULE_GBOX + else + { + restart_gbox_peer(rdr->label, 0, 0); + } +#endif + cs_log("Reader %s restarted by WebIF from %s", rdr->label, cs_inet_ntoa(GET_IP())); + } + } + + char *debuglvl = getParam(params, "debug"); + if(cs_strlen(debuglvl) > 0) + { +#ifndef WITH_DEBUG + cs_log("*** Warning: Debug Support not compiled in ***"); +#else + int32_t dblvl = atoi(debuglvl); + if(dblvl >= 0 && dblvl <= 65535) { cs_dblevel = dblvl; } + cs_log("%s debug_level=%d", "all", cs_dblevel); +#endif + } + + char *hide = getParam(params, "hide"); + if(cs_strlen(hide) > 0) + { + struct s_client *hideidx = NULL; + sscanf(hide, "%p", (void **)(void *)&hideidx); + + if(hideidx && is_valid_client(hideidx)) + { hideidx->wihidden = 1; } + } + + char *hideidle = getParam(params, "hideidle"); + if(cs_strlen(hideidle) > 0) + { + if(atoi(hideidle) == 2) + { + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 's' || cl->typ == 'h' || cl->typ == 'm'){ + cl->wihidden = 0; + } + } + } + else if(atoi(hideidle) == 3) + { + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 'r'){ + cl->wihidden = 0; + } + } + } + else if(atoi(hideidle) == 4) + { + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 'p'){ + cl->wihidden = 0; + } + } + } + else if(atoi(hideidle) == 5) + { + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 'c'){ + cl->wihidden = 0; + } + } + } + else + { + int32_t oldval = cfg.http_hide_idle_clients; + config_set("webif", "httphideidleclients", hideidle); + if(oldval != cfg.http_hide_idle_clients) + { + refresh_oscam(REFR_SERVER); + } + } + } + + if(cfg.http_hide_idle_clients > 0) { tpl_addVar(vars, TPLADD, "HIDEIDLECLIENTSSELECTED1", "selected"); } + else { tpl_addVar(vars, TPLADD, "HIDEIDLECLIENTSSELECTED0", "selected"); } + + tpl_addVar(vars, TPLADD, "SRVIDFILE", use_srvid2 ? "oscam.srvid2" : "oscam.srvid"); + + int32_t user_count_all = 0, user_count_shown = 0, user_count_active = 0; + int32_t reader_count_all = 0, reader_count_conn = 0, reader_count_off = 0; + int32_t proxy_count_all = 0, proxy_count_conn = 0, proxy_count_off = 0; + int32_t server_count_all = 0, server_count_shown = 0, server_count_hidden = 0; + int32_t monitor_count_all = 0, monitor_count_shown = 0; + int32_t shown; + + int32_t total_readers = 0; + int32_t active_readers = 0; + int32_t disabled_readers = 0; + int32_t connected_readers = 0; + + struct s_client *cl; + int8_t filtered; + + cs_readlock(__func__, &readerlist_lock); + cs_readlock(__func__, &clientlist_lock); + for(cl = first_client; cl ; cl = cl->next) + { +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + struct cc_data *cc = cl->cc; +#endif +#endif + if(cl->kill) { continue; } +#ifdef CS_CACHEEX + if(get_module(cl)->listenertype != LIS_CSPUDP) + { +#endif + + // Reset template variables + tpl_addVar(vars, TPLADD, "LBLRPSTRVALUE", ""); + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", ""); + tpl_addVar(vars, TPLADD, "LASTREADER", ""); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", ""); + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + tpl_addVar(vars, TPLADD, "CLIENTLASTRESPONSETIME", ""); + tpl_addVar(vars, TPLADD, "CLIENTLASTRESPONSETIMEHIST", ""); + tpl_addVar(vars, TPLADD, "UPICMISSING" , ""); + tpl_addVar(vars, TPLADD, "ENTITLEMENTS", ""); + + if(cl->typ == 'c') + { user_count_all++; } + else if(cl->typ == 'p') + { + proxy_count_all++; + if((cl->reader->typ == R_GBOX && cl->reader->card_status != CARD_INSERTED && cl->reader->card_status != NO_CARD) || + (cl->reader->typ != R_GBOX && cl->reader->card_status != CARD_INSERTED)) + { proxy_count_off++; } + } + else if(cl->typ == 'r') + { reader_count_all++; if(cl->reader->card_status != CARD_INSERTED) { reader_count_off++; } } + else if(cl->typ == 's' || cl->typ == 'h') + { server_count_all++; if(cl->wihidden) {server_count_hidden++;} } + else if(cl->typ == 'm') + { monitor_count_all++; } + + shown = 0; + if(cl->wihidden != 1) + { + filtered = !(cfg.http_hide_idle_clients != 1 || cl->typ != 'c' || (now - cl->lastecm) <= cfg.hideclient_to); + if(!filtered && cfg.http_hide_type) + { + char *p = cfg.http_hide_type; + while(*p && !filtered) + { + char type = *p++; +#ifdef CS_CACHEEX + filtered = (type == cl->typ) || (type == 'x' && (cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)); +#else + filtered = (type == cl->typ); +#endif +#ifdef WITH_EMU + if(type == 'e' && cl->typ == 'r' && cl->reader->typ == R_EMU) filtered = 1; +#endif + } + } + + if(!filtered) + { + if(cl->typ == 'c') + { + user_count_shown++; + if(cfg.http_hide_idle_clients != 1 && cfg.hideclient_to > 0 && (now - cl->lastecm) <= cfg.hideclient_to) + { + user_count_active++; + } + } + else if(cl->typ == 's' || cl->typ == 'h') + { + server_count_shown++; + } + else if(cl->typ == 'm') + { + monitor_count_shown++; + } + else if(cl->typ == 'r') + { + reader_count_conn++; + } + else if(cl->typ == 'p') + { + proxy_count_conn++; + } + + if(cl->typ == 'c' || cl->typ == 'r' || cl->typ == 'p') + { + if(cl->lastecm >= cl->login && cl->lastecm >= cl->logout) { isec = now - cl->lastecm; } + else if(cl->logout >= cl->login) { isec = now - cl->logout; } + else { isec = now - cl->login; } + } + else { isec = now - cl->last; } + + shown = 1; + lsec = now - cl->login; + chsec = now - cl->lastswitch; + usr = username(cl); + + if((cl->typ == 'r') || (cl->typ == 'p')) { usr = cl->reader->label; } + + if(cl->dup) { con = 2; } + else if((cl->tosleep) && (now - cl->lastswitch > cl->tosleep)) { con = 1; } + else { con = 0; } + + // no AU reader == 0 / AU ok == 1 / Last EMM > aulow == -1 + if(cl->typ == 'c' || cl->typ == 'p' || cl->typ == 'r') + { + if((cl->typ == 'c' && ll_count(cl->aureader_list) == 0) || ((cl->typ == 'p' || cl->typ == 'r') && cl->reader->audisabled)) { cau = 0; } + else if((now - cl->lastemm) / 60 > cfg.aulow) { cau = -1; } + else { cau = 1; } + + if(cau == 0) + { + tpl_addVar(vars, TPLADD, "CLIENTCAUHTTP", "OFF"); + } + else + { + if(cau == -1) + { tpl_addVar(vars, TPLADD, "CLIENTCAUHTTP", !apicall?"ON":""); } + else + { tpl_addVar(vars, TPLADD, "CLIENTCAUHTTP", !apicall?"ACTIVE":""); } + tpl_addVar(vars, TPLAPPEND, "CLIENTCAUHTTP", !apicall?"":""); + if(cl->typ == 'c') + { + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(cl->aureader_list); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->audisabled) + { tpl_printf(vars, TPLAPPEND, "CLIENTCAUHTTP", "(%s)
", xml_encode(vars, rdr->label)); } + else + { tpl_printf(vars, TPLAPPEND, "CLIENTCAUHTTP", "%s
", xml_encode(vars, rdr->label)); } + } + } + else { tpl_addVar(vars, TPLAPPEND, "CLIENTCAUHTTP", xml_encode(vars, cl->reader->label)); } + tpl_addVar(vars, TPLAPPEND, "CLIENTCAUHTTP", !apicall?"
":""); + } + } + else + { + cau = 0; + tpl_addVar(vars, TPLADD, "CLIENTCAUHTTP", ""); + } + localtime_r(&cl->login, <); + + if(cl->typ == 'c' || cl->typ == 'm') + { + if(cl->account && cl->account->description) + tpl_printf(vars, TPLADD, "CLIENTDESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, cl->account->description)); + else + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + else if(cl->typ == 'p' || cl->typ == 'r') + { + if(cl->reader && cl->reader->description) + tpl_printf(vars, TPLADD, "CLIENTDESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, cl->reader->description)); + else + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + if(!apicall) + { + tpl_addVar(vars, TPLADD, "LBL", xml_encode(vars, usr)); + tpl_printf(vars, TPLADD, "CID", "%p", cl); + if(cl->typ == 'c' || cl->typ == 'm') + { + tpl_addVar(vars, TPLADD, "TARGET", "User"); + tpl_addVar(vars, TPLADD, "CSIDX", tpl_getTpl(vars, "STATUSKBUTTON")); + } + else if(cl->typ == 'p') + { + tpl_addVar(vars, TPLADD, "TARGET", "Proxy"); + tpl_addVar(vars, TPLADD, "LBLENC", urlencode(vars, usr)); + tpl_addVar(vars, TPLADD, "CSIDX", tpl_getTpl(vars, "STATUSRBUTTON")); + } + else if(cl->typ == 'r') + { + tpl_addVar(vars, TPLADD, "TARGET", "Reader"); + tpl_addVar(vars, TPLADD, "LBLENC", urlencode(vars, usr)); + tpl_addVar(vars, TPLADD, "CSIDX", tpl_getTpl(vars, "STATUSRBUTTON")); + } + else if (cl->typ == 'h' || cl->typ == 's') + { + tpl_addVar(vars, TPLADD, "TARGET", "Server"); + tpl_addVar(vars, TPLADD, "CSIDX", ""); + } + tpl_addVar(vars, TPLADD, "HIDEIDX", tpl_getTpl(vars, "STATUSHBUTTON")); + tpl_printf(vars, TPLADD, "CSIDXPLAIN", "id_%p", cl); + } + else + { + tpl_printf(vars, TPLADD, "HIDEIDX", "%p", cl); + tpl_printf(vars, TPLADD, "CSIDX", "id_%p", cl); + } + tpl_printf(vars, TPLADD, "CLIENTTYPE", "%c", cl->typ); + tpl_printf(vars, TPLADD, "CLIENTCNR", "%d", get_threadnum(cl)); + tpl_addVar(vars, TPLADD, "CLIENTUSER", xml_encode(vars, usr)); + + tpl_addVar(vars, TPLADD, "STATUSUSERICON", xml_encode(vars, usr)); + if (cl->typ == 'c' || cl->typ == 'm') + { + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, usr)); + tpl_addVar(vars, TPLADD, "USERENC", urlencode(vars, usr)); + } + else if (cl->typ == 'p' || cl->typ == 'r') + { + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, usr)); + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, usr)); + } + + bool picon_shown = false; + const char *status_user_icon_tpl = NULL; + + char picon_name[32]; + if(cfg.http_showpicons) + { + if(picon_exists(xml_encode(vars, usr))) + { + switch (cl->typ) + { + case 'm': // Fall through + case 'c': status_user_icon_tpl = "SUSERICON"; picon_shown = true; break; + case 'p': // Fall through + case 'r': status_user_icon_tpl = "SREADERICON"; picon_shown = true; break; + } + } + else + tpl_printf(vars, TPLADD, "UPICMISSING", "%smissing icon: IC_%s.tpl",!apicall?" ":"",xml_encode(vars, usr)); + } + + if (!picon_shown) + { + switch (cl->typ) + { + case 'm': // Fall through + case 'c': status_user_icon_tpl = "SUSER"; break; + case 'p': // Fall through + case 'r': status_user_icon_tpl = "SREADER"; break; + } + } + + if (status_user_icon_tpl) + tpl_addVar(vars, TPLADD, "STATUSUSERICON", tpl_getTpl(vars, status_user_icon_tpl)); + + tpl_printf(vars, TPLADD, "CLIENTCAU", "%d", cau); + if(!apicall) + { + if(cl->typ == 'c' || cl->typ == 'p' || cl->typ == 'r') + { + if(cl->crypted) { tpl_addVar(vars, TPLADD, "CLIENTCRYPTED", "ON"); } + else { tpl_addVar(vars, TPLADD, "CLIENTCRYPTED", "OFF"); } + } + else { tpl_addVar(vars, TPLADD, "CLIENTCRYPTED", ""); } + } + else { tpl_printf(vars, TPLADD, "CLIENTCRYPTED", "%d", cl->crypted); } + + if(cl->typ == 'r' && cl->reader && !is_network_reader(cl->reader)) + { tpl_addVar(vars, TPLADD, "CLIENTIP", "local"); } + else if(IP_ISSET(cl->ip)) + { tpl_addVar(vars, TPLADD, "CLIENTIP", cs_inet_ntoa(cl->ip)); } + else if((cl->typ == 'p' || cl->typ == 'r') && cl->reader && cl->reader->tcp_connected) + { tpl_addVar(vars, TPLADD, "CLIENTIP", "camd.socket"); } + else if(cl->typ == 'c' && cl->login > cl->logout) + { tpl_addVar(vars, TPLADD, "CLIENTIP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "CLIENTIP", ""); } + + tpl_printf(vars, TPLADD, "CLIENTPORT", "%d", cl->port); + const char *proto = client_get_proto(cl); +#ifdef CS_CACHEEX_AIO + if(cl && + ( (cl->typ == 'c' && cl->account && cl->account->cacheex.feature_bitfield) +#if defined(MODULE_CAMD35) || defined (MODULE_CAMD35_TCP) + || (cl->c35_extmode > 1) +#endif +#ifdef MODULE_CCCAM + || (cc && cc->extended_lg_flagged_cws) +#endif + || (cl->typ == 'p' && cl->reader && cl->reader->cacheex.feature_bitfield)) + ) + { + const char *aio_suffix = " (cx-aio)"; + char *new_proto; + if(cs_malloc(&new_proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + new_proto[0] = '\0'; + if (!cs_strncat(new_proto, (char *)proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + cs_log("FIXME!"); + } + if (cs_strncat(new_proto, (char *)aio_suffix, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + webif_add_client_proto(vars, cl, (const char*)new_proto, apicall); + } else { + cs_log("FIXME!"); + } + free(new_proto); + } + } else { +#endif + webif_add_client_proto(vars, cl, proto, apicall); +#ifdef CS_CACHEEX_AIO + } +#endif + if(!apicall) + { + if((cl->typ != 'p' && cl->typ != 'r') || cl->reader->card_status == CARD_INSERTED) + { + tpl_printf(vars, TPLADD, "CLIENTLOGINDATE", "%02d.%02d.%02d
%02d:%02d:%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100, lt.tm_hour, lt.tm_min, lt.tm_sec); + tpl_addVar(vars, TPLADD, "CLIENTLOGINSECS", sec2timeformat(vars, lsec)); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTLOGINDATE", ""); + tpl_addVar(vars, TPLADD, "CLIENTLOGINSECS", ""); + } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTLOGINDATEFMT", "%02d.%02d.%02d %02d:%02d:%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100, lt.tm_hour, lt.tm_min, lt.tm_sec); + char tbuffer [30]; + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", <); + tpl_addVar(vars, TPLADD, "CLIENTLOGINDATE", tbuffer); + tpl_printf(vars, TPLADD, "CLIENTLOGINSECS", "%d", lsec); + } + + //load historical values from ringbuffer + char *value = get_ecm_historystring(cl); + tpl_addVar(vars, TPLADD, "CLIENTLASTRESPONSETIMEHIST", value); + free_mk_t(value); + + if((isec < cfg.hideclient_to || cfg.hideclient_to == 0 || is_dvbapi_usr(cl->account->usr)) && (cl->typ == 'c' || cl->typ == 'p' || cl->typ == 'r')) + { + if(((cl->typ == 'c')) && (cl->lastreader[0])) + { + tpl_printf(vars, TPLADD, "MSVALUE", PRINTF_LOCAL_D, cl->cwlastresptime); + if(apicall) + { + tpl_addVar(vars, TPLADD, "LASTREADER", (cl->last_caid == NO_CAID_VALUE || isec > cfg.hideclient_to ) ? "" : cl->lastreader); + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, cl->lastreader)); + } + else + { +#ifdef WITH_LB + tpl_addVar(vars, TPLADD, "LBLVALUE", xml_encode(vars, cl->lastreader)); + if(strstr(cl->lastreader, " (cache)")) + { + char lastreader_tmp[cs_strlen(cl->lastreader) - 8]; + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", tpl_getVar(vars, "LBLRPSTRVALUE")); + cs_strncpy(lastreader_tmp, cl->lastreader, sizeof(lastreader_tmp)); + tpl_addVar(vars, TPLADD, "LBLVALUEENC", urlencode(vars, lastreader_tmp)); + tpl_addVar(vars, TPLADD, "LBLVALUETITLE", xml_encode(vars, lastreader_tmp)); + } + else + { + tpl_addVar(vars, TPLADD, "LBLVALUEENC", urlencode(vars, cl->lastreader)); + tpl_addVar(vars, TPLADD, "LBLVALUETITLE", xml_encode(vars, cl->lastreader)); + } + tpl_addVar(vars, TPLAPPEND, "CLIENTLBVALUE", tpl_getTpl(vars, "CLIENTLBLVALUEBIT")); +#else + tpl_printf(vars, TPLAPPEND, "CLIENTLBVALUE", "%s (%'d ms)", xml_encode(vars, cl->lastreader), cl->cwlastresptime); +#endif + } + if(cl->last_caid == NO_CAID_VALUE || isec > cfg.hideclient_to) tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", ""); + } + if(cl->last_caid != NO_CAID_VALUE || cl->last_srvid != NO_SRVID_VALUE) + { + char channame[CS_SERVICENAME_SIZE]; + const char *lastprovidername; + + get_servicename_or_null(cl, cl->last_srvid, cl->last_provid, cl->last_caid, channame, sizeof(channame)); + if(channame[0] == '\0') + { + cs_strncpy(channame, "unknown", sizeof(channame)); + } + + lastprovidername = get_cl_lastprovidername(cl); + + tpl_printf(vars, TPLADD, "CLIENTCAID", "%04X", cl->last_caid); + tpl_printf(vars, TPLADD, "CLIENTPROVID", "%06X", cl->last_provid); + tpl_printf(vars, TPLADD, "CLIENTSRVID", "%04X", cl->last_srvid); + tpl_printf(vars, TPLADD, "CLIENTSRVPROVIDER", "%s%s%s", (lastprovidername[0] != '\0' && strcmp(lastprovidername, " ")) ? " [" : "", xml_encode(vars, lastprovidername), (lastprovidername[0] != '\0' && strcmp(lastprovidername, " ")) ? "]" : ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVNAME", xml_encode(vars, channame)); + tpl_printf(vars, TPLADD, "CLIENTLASTRESPONSETIME", "%d", cl->cwlastresptime ? cl->cwlastresptime : 1); + tpl_addVar(vars, TPLADD, "CLIENTSRVTYPE", cl->last_srvidptr && cl->last_srvidptr->type ? xml_encode(vars, cl->last_srvidptr->type) : ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVDESCRIPTION", cl->last_srvidptr && cl->last_srvidptr->desc ? xml_encode(vars, cl->last_srvidptr->desc) : ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNEL", sec2timeformat(vars, chsec)); + if(cfg.http_showpicons && cl->last_srvid) + { + char picon_channame[30]; + int8_t picon_ok = 0; + + get_picon_servicename_or_null(cl, cl->last_srvid, cl->last_provid, cl->last_caid, picon_channame, sizeof(picon_channame)); + if(picon_channame[0]) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", picon_channame); + picon_ok = picon_exists(picon_name); + if(picon_ok) tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + + if(!picon_ok && picon_servicename_remve_hd(picon_channame, sizeof(picon_channame))) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", picon_channame); + picon_ok = picon_exists(picon_name); + if(picon_ok) tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + } + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%04X_%06X_%04X", cl->last_caid, cl->last_provid, cl->last_srvid); + tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + picon_ok = picon_exists(picon_name); + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%04X_%04X", cl->last_caid, cl->last_srvid); + picon_ok = picon_exists(picon_name); + if(picon_ok) tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "0000_%04X", cl->last_srvid); + picon_ok = picon_exists(picon_name); + if(picon_ok) tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + } + if(picon_ok) + { + tpl_addVar(vars, TPLADDONCE, "CURRENTPICON", tpl_getTpl(vars, "CLIENTCURRENTCHANNELPIC")); + } + else + { + tpl_addVar(vars, TPLADDONCE, "CURRENTPICON", tpl_getTpl(vars, "CLIENTCURRENTCHANNEL")); + tpl_addVar(vars, TPLADD, "PICONNAME", ""); + } + } + else + { + tpl_addVar(vars, TPLADDONCE, "CURRENTPICON", tpl_getTpl(vars, "CLIENTCURRENTCHANNELBIT")); + tpl_addVar(vars, TPLADD, "PICONNAME", "0000_0000"); + } + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTCAID", "0000"); + tpl_addVar(vars, TPLADD, "CLIENTPROVID", "000000"); + tpl_printf(vars, TPLADD, "CLIENTSRVID", "0000"); + } + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTCAID", "0000"); + tpl_addVar(vars, TPLADD, "CLIENTPROVID", "000000"); + tpl_addVar(vars, TPLADD, "CLIENTSRVID", "0000"); + tpl_addVar(vars, TPLADD, "CURRENTPICON", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVPROVIDER", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVNAME", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVTYPE", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVDESCRIPTION", ""); + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNEL", ""); + } + + if(!apicall) + { + tpl_addVar(vars, TPLADD, "CLIENTIDLESECS", sec2timeformat(vars, isec)); + + if((cl->typ != 'p' && cl->typ != 'r') || cl->reader->card_status == CARD_INSERTED) + { tpl_addVar(vars, TPLADD, "CLIENTIDLESECSCLASS", "idlesec_normal"); } + else + { tpl_addVar(vars, TPLADD, "CLIENTIDLESECSCLASS", "idlesec_alert"); } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTIDLESECS", "%d", isec); + } + + if(con == 2) { tpl_addVar(vars, TPLADD, "CLIENTCON", "Duplicate"); } + else if(con == 1) { tpl_addVar(vars, TPLADD, "CLIENTCON", "Sleep"); } + else + { + struct s_reader *rdr = cl->reader; + char *txt = "OK"; + if(!rdr && (cl->typ == 'r' || cl->typ == 'p')) { txt = "UNKNOWN"; } + + else if(cl->typ == 'r' || cl->typ == 'p') //reader or proxy + { +#ifdef WITH_LB + if(rdr->lbvalue) + { + tpl_printf(vars, TPLADD, "LBLRPSTRVALUE", "%d", rdr->lbvalue); + } + else + { + tpl_addVar(vars, TPLADD, "LBLRPSTRVALUE", "no data"); + } + tpl_addVar(vars, TPLADD, "LBLRPVALUE", rdr->label); + tpl_addVar(vars, TPLADD, "LBLRPVALUEENC", urlencode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", tpl_getTpl(vars, "CLIENTLBLVALUERP")); +#else + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", tpl_getVar(vars, "LBLRPSTRVALUE")); +#endif + switch(rdr->card_status) + { + case NO_CARD: + if(rdr->typ == R_GBOX) + { txt = "ONL"; } + else + { txt = "OFF"; } + break; + case UNKNOWN: + txt = "UNKNOWN"; + break; + case READER_DEVICE_ERROR: + txt = "READER DEVICE ERROR"; + break; + case CARD_NEED_INIT: + if(rdr->typ == R_GBOX) + { txt = "OFFLINE"; } +#ifdef CS_CACHEEX + else if (cl->reader->cacheex.mode > 0) + { txt = "CCcam CacheEX"; } +#endif + else + { txt = "NEEDINIT"; } + break; + case CARD_INSERTED: + if(cl->typ == 'p') + { + if(rdr->typ == R_GBOX) + { txt = "ONL"; } + else + { txt = "CONNECTED"; } + } + else + { txt = "CARDOK"; } + break; + case CARD_FAILURE: + txt = "ERROR"; + break; + default: + txt = "UNDEF"; + } +#ifdef MODULE_GBOX + if(rdr->typ == R_GBOX) + { + struct gbox_peer *peer = cl->gbox; + char gbx_txt[45]; + memset(gbx_txt, 0, sizeof(gbx_txt)); + if(!strcmp(txt, "OFFLINE")) + { + snprintf(gbx_txt, sizeof(gbx_txt), "%s | ID: %04X", txt, peer->gbox.id); + } + else + { + snprintf(gbx_txt, sizeof(gbx_txt), "%s | crd: %d | ID: %04X", txt, gbox_count_peer_cards(peer->gbox.id), peer->gbox.id); + } + txt = gbx_txt; + } +#endif + } + + tpl_addVar(vars, TPLADD, "CLIENTCON", txt); + + if(rdr && (cl->typ == 'r')) //reader + { + if(rdr->ll_entitlements) + { + LL_ITER itr = ll_iter_create(rdr->ll_entitlements); + S_ENTITLEMENT *ent; + uint16_t total_ent = 0; + uint16_t active_ent = 0; + struct tm end_t; + tpl_addVar(vars, TPLADD, "TMPSPAN", ""); + while((ent = ll_iter_next(&itr))) + { + total_ent++; + if((ent->end > now) && (ent->type != 7)) + { + if(active_ent) {tpl_addVar(vars, TPLAPPEND, "TMPSPAN", "

");} + active_ent++; + localtime_r(&ent->end, &end_t); + tpl_printf(vars, TPLAPPEND, "TMPSPAN", "%04X@%06X
exp:%04d/%02d/%02d", + ent->caid, ent->provid, + end_t.tm_year + 1900, end_t.tm_mon + 1, end_t.tm_mday); + tpl_printf(vars, TPLAPPEND, "ENTITLEMENTS", "%s{\"caid\":\"%04X\",\"provid\":\"%06X\",\"exp\":\"%04d/%02d/%02d\"}", + active_ent > 1 ? ",": "", + ent->caid, ent->provid, + end_t.tm_year + 1900, end_t.tm_mon + 1, end_t.tm_mday); + } + } + tpl_printf(vars, TPLADD, "TOTENTITLEMENTS", "%d", total_ent); + if(((total_ent) && (active_ent == 0)) || (total_ent == 0)) + { + tpl_addVar(vars, TPLAPPEND, "TMPSPAN", "No active entitlements found"); + } + tpl_addVar(vars, TPLAPPEND, "TMPSPAN", "
"); + if(active_ent) + { + tpl_printf(vars, TPLADD, "TMP", "(%d entitlement%s)", active_ent, (active_ent != 1) ? "s" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "TMP", "(no entitlements)"); + } + tpl_addVar(vars, TPLADD, "ENTLABEL", urlencode(vars, cl->reader->label)); + tpl_addVar(vars, TPLADD, "ENTVALUE", active_ent > 0 ? "" : "1"); + if (!apicall) tpl_addVar(vars, TPLAPPEND, "CLIENTCON", tpl_getTpl(vars, "FOUNDENTITLEMENTS")); + } + else + { + tpl_addVar(vars, TPLADD, "ENTLABEL", urlencode(vars, cl->reader->label)); + if (!apicall) tpl_addVar(vars, TPLAPPEND, "CLIENTCON", tpl_getTpl(vars, "NOENTITLEMENTS")); + } + } +#ifdef MODULE_CCCAM + if(!apicall || apicall == 2) + { + if(rdr && (cl->typ == 'r' || cl->typ == 'p') && strncmp(proto, "cccam", 5) == 0 && rdr->tcp_connected && rdr->card_status != CARD_FAILURE) + { + struct cc_data *rcc = cl->cc; + if(rcc) + { + LLIST *cards = rcc->cards; + if(cards) + { + int32_t cnt = ll_count(cards); + int32_t locals = rcc->num_hop1; + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "(%d of %d card%s)", locals, cnt, (cnt > 1) ? "s" : ""); + tpl_printf(vars, TPLADD, "CCCOUNT", "%d", cnt); + tpl_printf(vars, TPLADD, "CCCHOP1", "%d", rcc->num_hop1); + tpl_printf(vars, TPLADD, "CCCHOP2", "%d", rcc->num_hop2); + tpl_printf(vars, TPLADD, "CCCHOPX", "%d", rcc->num_hopx); + tpl_printf(vars, TPLADD, "CCCCURR", "%d", cl->reader->currenthops); + tpl_printf(vars, TPLADD, "CCCRES0", "%d", rcc->num_reshare0); + tpl_printf(vars, TPLADD, "CCCRES1", "%d", rcc->num_reshare1); + tpl_printf(vars, TPLADD, "CCCRES2", "%d", rcc->num_reshare2); + tpl_printf(vars, TPLADD, "CCCRESX", "%d", rcc->num_resharex); + tpl_addVar(vars, TPLADD, "TMPSPAN", tpl_getTpl(vars, "CCENTITLEMENTS")); + tpl_addVar(vars, TPLADD, "CCCLABEL", urlencode(vars, cl->reader->label)); + tpl_addVar(vars, TPLADD, "CCCRESHARE", rcc->num_reshare0 > 0 ? "1" : ""); + tpl_addVar(vars, TPLADD, "CCCTMP", tpl_getVar(vars, "TMP")); + tpl_addVar(vars, TPLADD, "CCCTMPSPAN", tpl_getVar(vars, "TMPSPAN")); + tpl_addVar(vars, TPLAPPEND, "CLIENTCON", tpl_getTpl(vars, "CCENTITLETOOLTIP")); + } + if (apicall == 2) + { + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, cl->reader->label)); + tpl_printf(vars, TPLADD, "ENTITLEMENTS", "{\"locals\":\"%d\",\"cccount\":\"%d\",\"ccchop1\":\"%d\",\"ccchop2\":\"%d\",\"ccchopx\":\"%d\",\"ccccurr\":\"%d\",\"cccres0\":\"%d\",\"cccres1\":\"%d\",\"cccres2\":\"%d\",\"cccresx\":\"%d\",\"cccreshare\":\"%s\"}", + locals, cnt, rcc->num_hop1, rcc->num_hop2, rcc->num_hopx, cl->reader->currenthops, + rcc->num_reshare0, rcc->num_reshare1, rcc->num_reshare2, rcc->num_resharex, + rcc->num_reshare0 > 0 ? "1" : ""); + } + cs_log_dbg(D_TRACE, "Reader %s has total %d local%s hop1 %d hopx %d from total of %d card%s", cl->reader->label, locals, (locals > 1) ? "s" : "", rcc->num_hop2, rcc->num_hopx, cnt, (cnt > 1) ? "s" : ""); + } + } + } + } +#endif + } + } + } + if(!apicall) + { + // select right suborder + if(cl->typ == 'c') + { + if(shown) { tpl_addVar(vars, TPLAPPEND, "CLIENTSTATUS", tpl_getTpl(vars, "CLIENTSTATUSBIT")); } + if(cfg.http_hide_idle_clients != 1 && cfg.hideclient_to > 0) + { + tpl_printf(vars, TPLADD, "UCAC", "%d", user_count_active); + tpl_printf(vars, TPLADD, "CFGH", "%d", cfg.hideclient_to); + tpl_addVar(vars, TPLADD, "CHEADADD", tpl_getTpl(vars, "CLIENTHEADLINEADD")); + } + + tpl_printf(vars, TPLADD, "UCS", "%d", user_count_shown); + tpl_printf(vars, TPLADD, "UCA", "%d", user_count_all); + tpl_addVar(vars, TPLADD, "HIDEIDLE", "5"); + tpl_addVar(vars, TPLADD, "SHOWHIDDEN", "User"); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTHEADLINE")); + tpl_addVar(vars, TPLADD, "CLIENTOPTIONADD", tpl_getTpl(vars, "CLIENTHEADLINEBIT")); + tpl_addVar(vars, TPLADD, "CLIENTHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + tpl_addVar(vars, TPLADD, "CLIENTOPTIONADD", ""); + } + else if(cl->typ == 'r') + { + if(shown) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATUS", tpl_getTpl(vars, "CLIENTSTATUSBIT")); + } + + tpl_printf(vars, TPLADD, "RCC", "%d", reader_count_conn); + tpl_printf(vars, TPLADD, "RCA", "%d", reader_count_all); + + if(reader_count_off) + { + tpl_printf(vars, TPLADD, "RCO", "%d", reader_count_all-reader_count_off); + tpl_addVar(vars, TPLADD, "RHEADADD", tpl_getTpl(vars, "CLIENTRHEADLINEADD")); + } + + tpl_addVar(vars, TPLADD, "HIDEIDLE", "3"); + tpl_addVar(vars, TPLADD, "SHOWHIDDEN", "Reader"); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTRHEADLINE")); + tpl_addVar(vars, TPLADD, "READERHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + } + else if(cl->typ == 'p') + { + if(shown) + { + tpl_addVar(vars, TPLAPPEND, "PROXYSTATUS", tpl_getTpl(vars, "CLIENTSTATUSBIT")); + } + + tpl_printf(vars, TPLADD, "PCC", "%d", proxy_count_conn); + tpl_printf(vars, TPLADD, "PCA", "%d", proxy_count_all); + + if(proxy_count_off) + { + tpl_printf(vars, TPLADD, "PCO", "%d", proxy_count_all-proxy_count_off); + tpl_addVar(vars, TPLADD, "PHEADADD", tpl_getTpl(vars, "CLIENTPHEADLINEADD")); + } + + tpl_addVar(vars, TPLADD, "HIDEIDLE", "4"); + tpl_addVar(vars, TPLADD, "SHOWHIDDEN", "Proxy"); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTPHEADLINE")); + tpl_addVar(vars, TPLADD, "PROXYHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + } + else if(cl->typ == 'm' || cl->typ == 's' || cl->typ == 'h') + { + if(shown) + { + tpl_addVar(vars, TPLAPPEND, "SERVERSTATUS", tpl_getTpl(vars, "CLIENTSTATUSBIT")); + } + + tpl_addVar(vars, TPLADD, "HIDEIDLE", "2"); + tpl_addVar(vars, TPLADD, "SHOWHIDDEN", "Server"); + + if(cl->typ == 's' || cl->typ == 'h') + { + tpl_printf(vars, TPLADD, "SCS", "%d", server_count_shown); + tpl_printf(vars, TPLADD, "SCA", "%d", server_count_all); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTSHEADLINE")); + + if(shown || cl->wihidden) + { + tpl_addVar(vars, TPLADD, "SERVERHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + } + } + else + { + tpl_addVar(vars, TPLADD, "SHOWHIDDENADD", " & Monitors"); + tpl_printf(vars, TPLADD, "MCS", "%d", monitor_count_shown); + tpl_printf(vars, TPLADD, "MCA", "%d", monitor_count_all); + tpl_addVar(vars, TPLADD, "MHEADADD", tpl_getTpl(vars, "CLIENTMHEADLINE")); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTSHEADLINE")); + tpl_addVar(vars, TPLADD, "SERVERHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + tpl_addVar(vars, TPLADD, "SHOWHIDDENADD", ""); + } + } + } + else + { + if(shown) + { + if(apicall == 1) + { + tpl_addVar(vars, TPLAPPEND, "APISTATUSBITS", tpl_getTpl(vars, "APISTATUSBIT")); + } + + if(apicall == 2) + { + tpl_addVar(vars, TPLADD, "JSONARRAYDELIMITER", delimiter?",":""); + tpl_addVar(vars, TPLAPPEND, "JSONSTATUSBITS", tpl_getTpl(vars, "JSONSTATUSBIT")); + delimiter++; + } + } + } + +#ifdef CS_CACHEEX + } +#endif + + } + + LL_ITER itr = ll_iter_create(configured_readers); + struct s_reader *rdrr; + while((rdrr = ll_iter_next(&itr))) + { + if(rdrr->label[0] && rdrr->typ) + { + total_readers += 1; + + if(rdrr->enable) { active_readers += 1; } + else { disabled_readers += 1; } + + if(rdrr->tcp_connected) { connected_readers += 1; } + } + } + + cs_readunlock(__func__, &clientlist_lock); + cs_readunlock(__func__, &readerlist_lock); + + if(cfg.http_status_log || (apicall == 1 && strcmp(getParam(params, "appendlog"), "1") == 0)) + { + if(cfg.loghistorylines && log_history) + { + LL_ITER it = ll_iter_create(log_history); + struct s_log_history *hist; + + while((hist = (struct s_log_history*)ll_iter_next(&it))) + { + char p_usr[32]; + size_t pos1 = strcspn(hist->txt, "\t") + 1; + cs_strncpy(p_usr, hist->txt , pos1 > sizeof(p_usr) ? sizeof(p_usr) : pos1); + + char *p_txt = hist->txt + pos1; + + if(!apicall) + { + if(p_txt[0]) tpl_printf(vars, TPLAPPEND, "LOGHISTORY","\t\t%s\t\t
\n", xml_encode(vars, p_usr), xml_encode(vars, p_txt)); + } + else + { + tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", p_txt); + } + } + } + else + { + tpl_addVar(vars, TPLADD, "LOGHISTORY", "loghistorylines is set to 0 in your configuration"); + } + } + +#ifdef CS_CACHEEX + char *getting = "\"Getting\""; + char *pushing = "\"Pushing\""; + + float cachesum = first_client ? first_client->cwcacheexgot : 1; + if(cachesum < 1) + { + cachesum = 1; + } + tpl_printf(vars, TPLADD, "TOTAL_CACHEXPUSH", "%d", first_client ? first_client->cwcacheexpush : 0); + tpl_addVar(vars, TPLADD, "TOTAL_CACHEXPUSH_IMG", pushing); + tpl_printf(vars, TPLADD, "TOTAL_CACHEXGOT", "%d", first_client ? first_client->cwcacheexgot : 0); + tpl_addVar(vars, TPLADD, "TOTAL_CACHEXGOT_IMG", getting); + tpl_printf(vars, TPLADD, "TOTAL_CACHEXHIT", "%d", first_client ? first_client->cwcacheexhit : 0); + tpl_printf(vars, TPLADD, "TOTAL_CACHESIZE", "%d", cache_size()); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "TOTAL_CACHESIZE_LG", "%d", cache_size_lg()); +#endif + tpl_printf(vars, TPLADD, "REL_CACHEXHIT", "%.2f", (first_client ? first_client->cwcacheexhit : 0) * 100 / cachesum); + tpl_addVar(vars, TPLADD, "CACHEEXSTATS", tpl_getTpl(vars, "STATUSCACHEX")); +#endif + //User info + struct s_auth *account; + int32_t total_users = 0; + int32_t disabled_users = 0; + int32_t expired_users = 0; + int32_t expired_or_disabled_users = 0; + int32_t connected_users = 0; + int32_t online_users = 0; + + for(account = cfg.account; (account); account = account->next) + { + total_users++; + if(account->expirationdate && account->expirationdate < now) + { + expired_users++; + } + if(account->disabled != 0) + { + disabled_users++; + } + if((account->expirationdate && account->expirationdate < now)||account->disabled != 0) + { + expired_or_disabled_users++; + } + int32_t latestactivity = 0; + struct s_client *latestclient = NULL; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->account && !strcmp(cl->account->usr, account->usr)) + { + if(cl->lastecm > latestactivity || cl->login > latestactivity) + { + if(cl->lastecm > cl->login) { latestactivity = cl->lastecm; } + else { latestactivity = cl->login; } + latestclient = cl; + } + } + } + + if(latestclient != NULL) + { + connected_users++; + + if(latestactivity > 0) + { + if((now - latestactivity) < cfg.hideclient_to) + { + if(latestclient->cwfound + latestclient->cwnot + latestclient->cwcache > 0) + { + online_users++; + } + } + } + } + } + tpl_printf(vars, TPLADD, "TOTAL_USERS", "%d", total_users); + tpl_printf(vars, TPLADD, "TOTAL_ACTIVE", "%d", total_users - expired_or_disabled_users); + tpl_printf(vars, TPLADD, "TOTAL_EXPIRED", "%d", expired_users); + tpl_printf(vars, TPLADD, "TOTAL_DISABLED", "%d", disabled_users); + tpl_printf(vars, TPLADD, "TOTAL_ONLINE", "%d", online_users); + tpl_printf(vars, TPLADD, "TOTAL_CONNECTED", "%d", connected_users); + + tpl_printf(vars, TPLADD, "TOTAL_READERS", "%d", total_readers); + tpl_printf(vars, TPLADD, "TOTAL_DISABLED_READERS", "%d", disabled_readers); + tpl_printf(vars, TPLADD, "TOTAL_ACTIVE_READERS", "%d", active_readers); + tpl_printf(vars, TPLADD, "TOTAL_CONNECTED_READERS", "%d", connected_readers); + + //CM info + set_ecm_info(vars); + + //copy struct to p_stat_old for cpu_usage calculation + p_stat_old = p_stat_cur; + +/* + * check_available Bit mapping + * mem 0 total, 1 used & free, 2 buff, cached & free incl. buff, 3 share + * swap 4 total, 5 used & free, + * proc 6 count + * cpu 7 load + * oscam 8 vsize & rssize, 9 cpu user, 10 cpu sys, 11 cpu sum, 12 cpu refreshtime + * unused 13 - 15 +*/ + //Memory-CPU Info for linux based systems +#if defined(__linux__) + //get actual stats + if(!get_stats_linux(getpid(),&p_stat_cur)){ + if(p_stat_old.cpu_total_time != 0){ + calc_cpu_usage_pct(&p_stat_cur, &p_stat_old); + } + } + else{ + //something went wrong, so fill with "N/A" + p_stat_cur.check_available = 65535; + } +#else // if not linux, fill with "N/A" but probably in future gets filled also for other platforms + p_stat_cur.check_available = 65535; +#endif + set_status_info(vars, p_stat_cur); + + if(cfg.http_showmeminfo || cfg.http_showuserinfo || cfg.http_showreaderinfo || cfg.http_showloadinfo || cfg.http_showecminfo || (cfg.http_showcacheexinfo && config_enabled(CS_CACHEEX)) || (cfg.http_showcacheexinfo && config_enabled(CS_CACHEEX_AIO))){ + tpl_addVar(vars, TPLADD, "DISPLAYINFO", "visible"); + } + else{ + tpl_addVar(vars, TPLADD, "DISPLAYINFO", "hidden"); + } + + tpl_addVar(vars, TPLADD, "DISPLAYSYSINFO", cfg.http_showmeminfo ? "visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYUSERINFO", cfg.http_showuserinfo ? "visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYREADERINFO", cfg.http_showreaderinfo ? "visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYLOADINFO", cfg.http_showloadinfo ?"visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYECMINFO", cfg.http_showecminfo ? "visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYECMINFO_READERS", cfg.http_showecminfo ? "visible" : "hidden"); + + if(cfg.http_showcacheexinfo == 1 && config_enabled(CS_CACHEEX)) + { + if (config_enabled(CS_CACHEEX_AIO)) + { + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXINFO", "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXAIOINFO", "visible"); + } + else + { + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXINFO", "visible"); + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXAIOINFO", "hidden"); + } + } + else{ + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXINFO", "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXAIOINFO", "hidden"); + } + +#ifdef WITH_DEBUG + if(cfg.http_status_log) + { + // Debuglevel Selector + int32_t i, lvl; + for(i = 0; i < MAX_DEBUG_LEVELS; i++) + { + lvl = 1 << i; + tpl_printf(vars, TPLADD, "TMPC", "DCLASS%d", lvl); + tpl_printf(vars, TPLADD, "TMPV", "DEBUGVAL%d", lvl); + if(cs_dblevel & lvl) + { + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMPC"), "debugls"); + tpl_printf(vars, TPLADD, tpl_getVar(vars, "TMPV"), "%d", cs_dblevel - lvl); + } + else + { + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMPC"), "debugl"); + tpl_printf(vars, TPLADD, tpl_getVar(vars, "TMPV"), "%d", cs_dblevel + lvl); + } + } + + if(cs_dblevel == D_ALL_DUMP) + { tpl_addVar(vars, TPLADD, "DCLASS65535", "debugls"); } + else + { tpl_addVar(vars, TPLADD, "DCLASS65535", "debugl"); } + + tpl_addVar(vars, TPLADD, "NEXTPAGE", "status.html"); + tpl_addVar(vars, TPLADD, "DCLASS", "debugl"); //default + tpl_printf(vars, TPLADD, "ACTDEBUG", "%d", cs_dblevel); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "SDEBUG", tpl_getTpl(vars, "DEBUGSELECTAIO")); +#else + tpl_addVar(vars, TPLADD, "SDEBUG", tpl_getTpl(vars, "DEBUGSELECT")); +#endif + } +#endif + + if(cfg.http_status_log) + tpl_addVar(vars, TPLADDONCE, "LOG_HISTORY", tpl_getTpl(vars, "LOGHISTORYBIT")); + + if(apicall) + { + if(apicall == 1) + { return tpl_getTpl(vars, "APISTATUS"); } + if(apicall == 2) + { + tpl_printf(vars, TPLADD, "UCS", "%d", user_count_shown); + tpl_printf(vars, TPLADD, "UCA", "%d", user_count_all); + if(cfg.http_hide_idle_clients != 1 && cfg.hideclient_to > 0){ + tpl_printf(vars, TPLADD, "UCAC", "%d", user_count_active); + } + tpl_printf(vars, TPLADD, "CFGH", "%d", cfg.hideclient_to); + tpl_printf(vars, TPLADD, "MCS", "%d", monitor_count_shown); + tpl_printf(vars, TPLADD, "MCA", "%d", monitor_count_all); + tpl_printf(vars, TPLADD, "SCS", "%d", server_count_shown); + tpl_printf(vars, TPLADD, "SCH", "%d", server_count_hidden); + tpl_printf(vars, TPLADD, "SCA", "%d", server_count_all); + tpl_printf(vars, TPLADD, "RCC", "%d", reader_count_conn); + tpl_printf(vars, TPLADD, "RCO", "%d", reader_count_off); + tpl_printf(vars, TPLADD, "RCA", "%d", reader_count_all); + tpl_printf(vars, TPLADD, "PCC", "%d", proxy_count_conn); + tpl_printf(vars, TPLADD, "PCO", "%d", proxy_count_off); + tpl_printf(vars, TPLADD, "PCA", "%d", proxy_count_all); + tpl_printf(vars, TPLADD, "PICONENABLED", "%d", cfg.http_showpicons?1:0); + tpl_printf(vars, TPLADD, "SRVIDFILE", "%s", use_srvid2 ? "oscam.srvid2" : "oscam.srvid"); + return tpl_getTpl(vars, "JSONSTATUS"); + } + } + + return tpl_getTpl(vars, "STATUS"); +} + +static char *send_oscam_services_edit(struct templatevars * vars, struct uriparams * params) +{ + struct s_sidtab *sidtab, *ptr; + char label[sizeof(cfg.sidtab->label)]; + int32_t i; + + setActiveMenu(vars, MNU_SERVICES); + + cs_strncpy(label, strtolower(getParam(params, "service")), sizeof(label)); + ++cfg_sidtab_generation; + for(sidtab = cfg.sidtab; sidtab != NULL && strcmp(label, sidtab->label) != 0; sidtab = sidtab->next) { ; } + + if(sidtab == NULL) + { + i = 1; + while(cs_strlen(label) < 1) + { + snprintf(label, sizeof(label) / sizeof(char) - 1, "newservice%d", i); + for(sidtab = cfg.sidtab; sidtab != NULL && strcmp(label, sidtab->label) != 0; sidtab = sidtab->next) { ; } + if(sidtab != NULL) { label[0] = '\0'; } + ++i; + } + if(!cs_malloc(&sidtab, sizeof(struct s_sidtab))) { return "0"; } + + if(cfg.sidtab == NULL) { cfg.sidtab = sidtab; } + else + { + for(ptr = cfg.sidtab; ptr != NULL && ptr->next != NULL; ptr = ptr->next) { ; } + ptr->next = sidtab; + } + cs_strncpy((char *)sidtab->label, label, sizeof(sidtab->label)); + ++cfg_sidtab_generation; + tpl_addMsg(vars, "New service has been added"); + // Adding is uncritical as the new service is appended to sidtabs.ok/sidtabs.no and accounts/clients/readers have zeros there + if(write_services() != 0) { tpl_addMsg(vars, "Writing services to disk failed!"); } + } + + if(strcmp(getParam(params, "action"), "Save") == 0) + { + for(i = 0; i < (*params).paramcount; i++) + { + if((strcmp((*params).params[i], "action")) && (strcmp((*params).params[i], "service"))) + { + chk_sidtab((*params).params[i], (*params).values[i], sidtab); + } + } + ++cfg_sidtab_generation; + tpl_addMsg(vars, "Services updated"); + // We don't need any refresh here as accounts/clients/readers sidtabs.ok/sidtabs.no are unaffected! + if(write_services() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + + for(sidtab = cfg.sidtab; sidtab != NULL && strcmp(label, sidtab->label) != 0; sidtab = sidtab->next) { ; } + } + + if(sidtab) + { + tpl_addVar(vars, TPLADD, "LABEL", xml_encode(vars, sidtab->label)); + tpl_addVar(vars, TPLADD, "LABELENC", urlencode(vars, sidtab->label)); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "DCRCCHECKED", (sidtab->disablecrccws_only_for_exception == 1) ? "checked" : "" ); + tpl_addVar(vars, TPLADD, "NWCHECKED", (sidtab->no_wait_time == 1) ? "checked" : "" ); + tpl_addVar(vars, TPLADD, "LGOECHECKED", (sidtab->lg_only_exception == 1) ? "checked" : "" ); +#endif + for(i = 0; i < sidtab->num_caid; i++) + { + if(i == 0) { tpl_printf(vars, TPLADD, "CAIDS", "%04X", sidtab->caid[i]); } + else { tpl_printf(vars, TPLAPPEND, "CAIDS", ",%04X", sidtab->caid[i]); } + } + for(i = 0; i < sidtab->num_provid; i++) + { + if(i == 0) { tpl_printf(vars, TPLADD, "PROVIDS", "%06X", sidtab->provid[i]); } + else { tpl_printf(vars, TPLAPPEND, "PROVIDS", ",%06X", sidtab->provid[i]); } + } + for(i = 0; i < sidtab->num_srvid; i++) + { + if(i == 0) { tpl_printf(vars, TPLADD, "SRVIDS", "%04X", sidtab->srvid[i]); } + else { tpl_printf(vars, TPLAPPEND, "SRVIDS", ",%04X", sidtab->srvid[i]); } + } + } +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "SERVICEEDITAIO"); +#else + return tpl_getTpl(vars, "SERVICEEDIT"); +#endif +} + +static void delete_from_SIDTABBITS(SIDTABBITS * orgsidtab, int32_t position, int32_t sidtablength) +{ + if(*orgsidtab) + { + int32_t i; + SIDTABBITS newsidtab = 0; + for(i = 0; i < position; ++i) + { + if(*orgsidtab & ((SIDTABBITS)1 << i)) + { newsidtab |= ((SIDTABBITS)1 << i); } + } + for(; i < sidtablength; ++i) + { + if(*orgsidtab & ((SIDTABBITS)1 << (i + 1))) + { newsidtab |= ((SIDTABBITS)1 << i); } + } + *orgsidtab = newsidtab; + } +} + +static char *send_oscam_services(struct templatevars * vars, struct uriparams * params) +{ + struct s_sidtab *sidtab; + char *service = getParam(params, "service"); + char channame[CS_SERVICENAME_SIZE]; + int32_t i, counter = 0; + + setActiveMenu(vars, MNU_SERVICES); + + if(strcmp(getParam(params, "action"), "delete") == 0) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "Sorry, Webif is in readonly mode. No deletion will be made!"); + } + else + { + struct s_sidtab *sidtab_prev = NULL; + int32_t sidtablength = -1; + int32_t position = 0; + + // Calculate sidtablength before deletion so that updating sidtabs is faster + for(sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next) + { ++sidtablength; } + + for(sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next) + { + if(strcmp(sidtab->label, service) == 0) + { + struct s_auth *account; + struct s_client *cl; + struct s_reader *rdr; + + if(!sidtab_prev) + { cfg.sidtab = sidtab->next; } + else + { sidtab_prev->next = sidtab->next; } + + for(account = cfg.account; (account); account = account->next) + { + delete_from_SIDTABBITS(&account->sidtabs.ok, position, sidtablength); + delete_from_SIDTABBITS(&account->sidtabs.no, position, sidtablength); + + for(cl = first_client->next; cl ; cl = cl->next) + { + if(account == cl->account) + { + cl->sidtabs.ok = account->sidtabs.ok; + cl->sidtabs.no = account->sidtabs.no; + } + } + } + + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + delete_from_SIDTABBITS(&rdr->sidtabs.ok, position, sidtablength); + delete_from_SIDTABBITS(&rdr->sidtabs.no, position, sidtablength); + delete_from_SIDTABBITS(&rdr->lb_sidtabs.ok, position, sidtablength); + } + free_sidtab(sidtab); + ++counter; + break; + } + sidtab_prev = sidtab; + position++; + } + if(counter > 0) + { + ++cfg_sidtab_generation; + tpl_addMsg(vars, "Service has been deleted!"); + if(write_services() != 0) { tpl_addMsg(vars, "Writing services to disk failed!"); } + } + else { tpl_addMsg(vars, "Sorry but the specified service doesn't exist. No deletion will be made!"); } + } + } + + sidtab = cfg.sidtab; + // Show List + counter = 0; + while(sidtab != NULL) + { + tpl_addVar(vars, TPLADD, "SID", ""); + tpl_printf(vars, TPLADD, "SERVICENUM", "%d", counter + 1); + if((strcmp(getParam(params, "service"), sidtab->label) == 0) && (strcmp(getParam(params, "action"), "list") == 0)) + { + tpl_addVar(vars, TPLADD, "SIDCLASS", "sidlist"); + tpl_addVar(vars, TPLAPPEND, "SID", ""); + for(i = 0; i < sidtab->num_srvid; i++) + { + tpl_printf(vars, TPLAPPEND, "SID", "%04X : %s
", sidtab->srvid[i], + xml_encode(vars, get_servicename(cur_client(), sidtab->srvid[i], sidtab->num_provid ? sidtab->provid[0] : 0, + sidtab->num_caid ? sidtab->caid[0] : 0, channame, sizeof(channame)))); + } + } + else + { + tpl_addVar(vars, TPLADD, "SIDCLASS", ""); + tpl_printf(vars, TPLADD, "SID", "Show Services", urlencode(vars, sidtab->label)); + } + tpl_addVar(vars, TPLADD, "LABELENC", urlencode(vars, sidtab->label)); + tpl_addVar(vars, TPLADD, "LABEL", xml_encode(vars, sidtab->label)); + tpl_addVar(vars, TPLADD, "SIDLIST", tpl_getTpl(vars, "SERVICECONFIGSIDBIT")); + + tpl_addVar(vars, TPLAPPEND, "SERVICETABS", tpl_getTpl(vars, "SERVICECONFIGLISTBIT")); + sidtab = sidtab->next; + counter++; + } + if(counter >= MAX_SIDBITS) + { + tpl_addVar(vars, TPLADD, "BTNDISABLED", "DISABLED"); + tpl_addMsg(vars, "Maximum Number of Services is reached"); + } + return tpl_getTpl(vars, "SERVICECONFIGLIST"); +} + +static char *send_oscam_savetpls(struct templatevars * vars) +{ + if(cfg.http_tpl) + { + tpl_printf(vars, TPLADD, "CNT", "%d", tpl_saveIncludedTpls(cfg.http_tpl)); + tpl_addVar(vars, TPLADD, "PATH", cfg.http_tpl); + } + else { tpl_addVar(vars, TPLADD, "CNT", "0"); } + return tpl_getTpl(vars, "SAVETEMPLATES"); +} + +static char *send_oscam_shutdown(struct templatevars * vars, FILE * f, struct uriparams * params, int8_t apicall, int8_t *keepalive, char *extraheader) +{ + if(!apicall) { setActiveMenu(vars, MNU_SHUTDOWN); } + if(strcmp(strtolower(getParam(params, "action")), "shutdown") == 0) + { + *keepalive = 0; + if(!apicall) + { + char *CSS = tpl_getUnparsedTpl("CSS", 1, ""); + tpl_addVar(vars, TPLADD, "STYLESHEET", CSS); + NULLFREE(CSS); + tpl_printf(vars, TPLADD, "REFRESHTIME", "%d", SHUTDOWNREFRESH); + tpl_addVar(vars, TPLADD, "REFRESH", tpl_getTpl(vars, "REFRESH")); + tpl_printf(vars, TPLADD, "SECONDS", "%d", SHUTDOWNREFRESH); + char *result = tpl_getTpl(vars, "SHUTDOWN"); + send_headers(f, 200, "OK", extraheader, "text/html", 0, cs_strlen(result), NULL, 0); + webif_write(result, f); + cs_log("Shutdown requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + } + else + { + tpl_addVar(vars, TPLADD, "APICONFIRMMESSAGE", "shutdown"); + cs_log("Shutdown requested by XMLApi from %s", cs_inet_ntoa(GET_IP())); + } + cs_exit_oscam(); + + if(!apicall) + { return "1"; } + else + { return tpl_getTpl(vars, "APICONFIRMATION"); } + + } + else if(strcmp(strtolower(getParam(params, "action")), "restart") == 0) + { + *keepalive = 0; + if(!apicall) + { + char *CSS = tpl_getUnparsedTpl("CSS", 1, ""); + tpl_addVar(vars, TPLADD, "STYLESHEET", CSS); + NULLFREE(CSS); + tpl_addVar(vars, TPLADD, "REFRESHTIME", "5"); + tpl_addVar(vars, TPLADD, "REFRESH", tpl_getTpl(vars, "REFRESH")); + tpl_addVar(vars, TPLADD, "SECONDS", "5"); + char *result = tpl_getTpl(vars, "SHUTDOWN"); + send_headers(f, 200, "OK", extraheader, "text/html", 0, cs_strlen(result), NULL, 0); + webif_write(result, f); + cs_log("Restart requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + } + else + { + tpl_addVar(vars, TPLADD, "APICONFIRMMESSAGE", "restart"); + cs_log("Restart requested by XMLApi from %s", cs_inet_ntoa(GET_IP())); + } + cs_restart_oscam(); + + if(!apicall) + { return "1"; } + else + { return tpl_getTpl(vars, "APICONFIRMATION"); } + + } + else + { + return tpl_getTpl(vars, "PRESHUTDOWN"); + } +} + +static char *send_oscam_script(struct templatevars * vars, struct uriparams * params) +{ + setActiveMenu(vars, MNU_SCRIPT); + tpl_printf(vars, TPLADD, "SCRIPTOPTIONS", "\n"); + + if(!cfg.http_readonly && cfg.http_script) + { + struct dirent **namelist; + int count, i; + count = scandir(cfg.http_script, &namelist, 0, alphasort ); + if( count >= 0 ) + { + for( i = 0 ; i < count; i++ ) + { + if(is_ext(namelist[i]->d_name, ".script") || is_ext(namelist[i]->d_name, ".sh")) + { + tpl_printf(vars, TPLAPPEND, "SCRIPTOPTIONS", "\n",namelist[i]->d_name,namelist[i]->d_name); + } + free( namelist[i] ); + } + free(namelist); + } + + char *scriptname = getParam(params, "scriptname"); + char *scriptparam = getParam(params, "scriptparam"); + char system_str[256]; + struct stat s; + snprintf(system_str, sizeof(system_str), "%s/%s", cfg.http_script, scriptname); + + if(!stat(system_str,&s)) + { + if(s.st_mode & S_IFREG) + { + if(s.st_mode & S_IXUSR) + { + int32_t rc = -1; + FILE *fp; + char buf[256]; + + if((scriptparam != NULL) && (sizeof(scriptparam) > 0)) + { + cs_strncat(system_str, " ", sizeof(system_str)); + cs_strncat(system_str, scriptparam, sizeof(system_str)); + } + + fp = popen(system_str, "r"); + if (fp) + { + while (fgets(buf, sizeof(buf), fp) != NULL) + { + tpl_addVar(vars, TPLAPPEND, "SCRIPTRESULTOUT", buf); + } + rc = pclose(fp)/256; + } + + tpl_printf(vars, TPLAPPEND, "CODE", "returncode: %d", rc); + tpl_printf(vars, TPLADD, "SCRIPTNAME", "scriptname: %s", scriptname); + } + else + { + tpl_printf(vars, TPLADD, "SCRIPTRESULTOUT", "[Error]: Script \"%s\" not executable!", scriptname); + } + } + } + else + { + tpl_printf(vars, TPLADD, "SCRIPTRESULTOUT", "[Error]: Script \"%s\" not found!", scriptname); + } + + } + return tpl_getTpl(vars, "SCRIPT"); +} + +static char *send_oscam_scanusb(struct templatevars * vars) +{ + setActiveMenu(vars, MNU_READERS); +#if !defined(__CYGWIN__) + FILE *fp; + char line[100]; + uint8_t i; + char header[11], txt[13], entry[10], bit[8], scan[12], error[120]; + +// key tool package match command + char *elems[] = { "USB", "lsusb", "usbutils", "Bus ", "lsusb -v | egrep '^Bus|^ *iSerial|^ *iProduct'" + , "UDEV", "/dev/serial/by-id", "udev", "<-", "find /dev/serial/by-id -type l -exec readlink -fn {} ';' -exec echo ' <- {}' \\;" +#ifdef CARDREADER_PCSC + , "PCSC", "pcsc_scan", "pcsc-tools", ":", "pcsc_scan -r" +#endif + }; + + for (i = 0; i < (sizeof(elems) / sizeof(elems[0])); i+=5) + { + snprintf(header, sizeof(header), "%sHEADER", elems[i]); //key + snprintf(txt, sizeof(txt), "%s Devices", elems[i]); //key + snprintf(entry, sizeof(entry), "%sENTRY", elems[i]); //key + snprintf(bit, sizeof(bit), "%sBIT", elems[i]); //key + snprintf(scan, sizeof(scan), "SCAN%sBIT", elems[i]); //key + snprintf(error, sizeof(error), "%s: Failed to run or %s package not installed!", elems[i + 1], elems[i + 2]); //tool + package + + tpl_addVar(vars, TPLADD, header, txt); + fp = popen(elems[i + 4], "r"); //command + if(!fp || !fgets(line, sizeof(line) - 1, fp)) + { + tpl_addVar(vars, TPLADD, entry, error); + tpl_addVar(vars, TPLADD, bit, tpl_getTpl(vars, scan)); + } + else + { + do{ + tpl_addVar(vars, TPLADD, "USBENTRYCLASS", ""); + if(strstr(line, elems[i + 3])) //match + { + tpl_addVar(vars, TPLADD, entry, line); + tpl_addVar(vars, TPLADD, "USBENTRYCLASS", "CLASS=\"scanusbsubhead\""); + } + else + { + tpl_printf(vars, TPLADD, entry, "     %s", line); + } + tpl_addVar(vars, TPLAPPEND, bit, tpl_getTpl(vars, scan)); + } + while(fgets(line, sizeof(line) - 1, fp) != NULL); + pclose(fp); + } + } +#else + tpl_addMsg(vars, "Function not supported in CYGWIN environment"); +#endif + return tpl_getTpl(vars, "SCANUSB"); +} + +static void webif_process_logfile(struct templatevars * vars, struct uriparams * params, char *targetfile, size_t targetfile_len) +{ + snprintf(targetfile, targetfile_len, "%s", cfg.logfile); + if(strcmp(getParam(params, "clear"), "logfile") == 0) + { + if(cs_strlen(targetfile) > 0) + { + FILE *file = fopen(targetfile, "w"); + if (file != NULL) + { + fclose(file); + } + } + } +#ifdef WITH_DEBUG + // Debuglevel Selector + int32_t i, lvl; + for(i = 0; i < MAX_DEBUG_LEVELS; i++) + { + lvl = 1 << i; + tpl_printf(vars, TPLADD, "TMPC", "DCLASS%d", lvl); + tpl_printf(vars, TPLADD, "TMPV", "DEBUGVAL%d", lvl); + if(cs_dblevel & lvl) + { + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMPC"), "debugls"); + tpl_printf(vars, TPLADD, tpl_getVar(vars, "TMPV"), "%d", cs_dblevel - lvl); + } + else + { + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMPC"), "debugl"); + tpl_printf(vars, TPLADD, tpl_getVar(vars, "TMPV"), "%d", cs_dblevel + lvl); + } + } + if(cs_dblevel == D_ALL_DUMP) + { tpl_addVar(vars, TPLADD, "DCLASS65535", "debugls"); } + else + { tpl_addVar(vars, TPLADD, "DCLASS65535", "debugl"); } + tpl_addVar(vars, TPLADD, "CUSTOMPARAM", "&file=logfile"); + tpl_printf(vars, TPLADD, "ACTDEBUG", "%d", cs_dblevel); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "SDEBUG", tpl_getTpl(vars, "DEBUGSELECTAIO")); +#else + tpl_addVar(vars, TPLADD, "SDEBUG", tpl_getTpl(vars, "DEBUGSELECT")); +#endif + tpl_addVar(vars, TPLADD, "NEXTPAGE", "files.html"); +#endif + if(!cfg.disablelog) + { + tpl_printf(vars, TPLADD, "SWITCH", "%d", 1); + tpl_addVar(vars, TPLADD, "TEXT", "Stop Log"); + tpl_addVar(vars, TPLADD, "LOGMENU", tpl_getTpl(vars, "LOGMENUDISABLELOG")); + + } + else + { + tpl_printf(vars, TPLADD, "SWITCH", "%d", 0); + tpl_addVar(vars, TPLADD, "TEXT", "Start Log"); + tpl_addVar(vars, TPLADD, "LOGMENU", tpl_getTpl(vars, "LOGMENUDISABLELOG")); + } + tpl_addVar(vars, TPLAPPEND, "LOGMENU", tpl_getTpl(vars, "CLEARLOG")); + return; +} + +static void webif_process_userfile(struct templatevars * vars, struct uriparams * params, char *targetfile, size_t targetfile_len) +{ + snprintf(targetfile, targetfile_len, "%s", cfg.usrfile); + if(strcmp(getParam(params, "clear"), "usrfile") == 0) + { + if(cs_strlen(targetfile) > 0) + { + FILE *file = fopen(targetfile, "w"); + if (file) + { + fclose(file); + } + } + } + if(!cfg.disableuserfile) + { + tpl_printf(vars, TPLADD, "SWITCH", "%d", 1); + tpl_addVar(vars, TPLADD, "TEXT", "Stop Log"); + tpl_addVar(vars, TPLADD, "LOGMENU", tpl_getTpl(vars, "LOGMENUONOFF")); + } + else + { + tpl_printf(vars, TPLADD, "SWITCH", "%d", 0); + tpl_addVar(vars, TPLADD, "TEXT", "Start Log"); + tpl_addVar(vars, TPLADD, "LOGMENU", tpl_getTpl(vars, "LOGMENUONOFF")); + } + tpl_addVar(vars, TPLAPPEND, "LOGMENU", tpl_getTpl(vars, "CLEARUSERLOG")); + tpl_addVar(vars, TPLADD, "FFVAL", "all"); + tpl_addVar(vars, TPLADD, "FILTERFORMOPTIONS", tpl_getTpl(vars, "LOGMENUFILTERFORM")); + struct s_auth *account; + for(account = cfg.account; account; account = account->next) + { + tpl_addVar(vars, TPLADD, "FFVAL", xml_encode(vars, account->usr)); + tpl_addVar(vars, TPLADD, "FFSEL", strcmp(getParam(params, "filter"), account->usr) ? "" : "selected"); + tpl_addVar(vars, TPLAPPEND, "FILTERFORMOPTIONS", tpl_getTpl(vars, "LOGMENUFILTERFORM")); + } + tpl_addVar(vars, TPLADD, "FILTERFORM", tpl_getTpl(vars, "FILTERFORM")); +} + +enum file_types { FTYPE_CONFIG, FTYPE_VERSION, FTYPE_ANTICASC, FTYPE_LOGFILE, FTYPE_USERFILE , FTYPE_GBOX }; + +struct files +{ + char *file; + int menu_id; + enum file_types type; +}; + +static char *send_oscam_files(struct templatevars * vars, struct uriparams * params, int8_t apicall) +{ + bool writable = false; + const struct files *entry; + static struct files config_files[] = + { + // id are used + // new entry after last entry before first ifdef entry + // ifdef must be add to end + { "oscam.version", MNU_CFG_FVERSION, FTYPE_VERSION }, // id 0 + { "oscam.conf", MNU_CFG_FCONF, FTYPE_CONFIG }, // id 1 + { "oscam.user", MNU_CFG_FUSER, FTYPE_CONFIG }, // id 2 + { "oscam.server", MNU_CFG_FSERVER, FTYPE_CONFIG }, // id 3 + { "oscam.srvid", MNU_CFG_FSRVID, FTYPE_CONFIG }, // id 4 + { "oscam.srvid2", MNU_CFG_FSRVID2, FTYPE_CONFIG }, // id 5 + { "logfile", MNU_CFG_FLOGFILE, FTYPE_LOGFILE }, // id 6 + { "userfile", MNU_CFG_FUSERFILE, FTYPE_USERFILE }, // id 7 + { "css_file_name", MNU_CFG_FCSS, FTYPE_CONFIG }, // id 8 + { "oscam.services", MNU_CFG_FSERVICES, FTYPE_CONFIG }, // id 9 + { "oscam.provid", MNU_CFG_FPROVID, FTYPE_CONFIG }, // id 10 + { "oscam.tiers", MNU_CFG_FTIERS, FTYPE_CONFIG }, // id 11 + { "oscam.ratelimit", MNU_CFG_FRATELIMIT,FTYPE_CONFIG }, // id 12 + { "oscam.whitelist", MNU_CFG_FWHITELIST,FTYPE_CONFIG }, // id 13 +#ifdef HAVE_DVBAPI + { "oscam.dvbapi", MNU_CFG_FDVBAPI, FTYPE_CONFIG }, // id 14 +#endif +#ifdef CS_CACHEEX + { "oscam.fakecws", MNU_CFG_FFAKECWS, FTYPE_CONFIG }, // id 15 +#endif +#ifdef CS_ANTICASC + { "anticasc", MNU_CFG_FACLOG, FTYPE_ANTICASC }, // id 16 +#endif +#ifdef MODULE_SERIAL + { "oscam.twin", MNU_CFG_FTWIN, FTYPE_CONFIG }, // id 17 +#endif +#ifdef MODULE_CONSTCW + { "constant.cw", MNU_CFG_FKEYCW, FTYPE_CONFIG }, // id 18 +#endif +#ifdef MODULE_GBOX + { "sc.info", MNU_GBX_FSCINF, FTYPE_GBOX }, // id 19 + { "share.info", MNU_GBX_FSHRINF, FTYPE_GBOX }, // id 20 + { "share.onl", MNU_GBX_FSHRONL, FTYPE_GBOX }, // id 21 + { "gbox.ver", MNU_GBX_FVERS, FTYPE_GBOX }, // id 22 + { "attack.txt", MNU_GBX_FATTACK, FTYPE_GBOX }, // id 23 + { "gsms.log", MNU_GBX_FSMSLOG, FTYPE_GBOX }, // id 24 + { "gsms.ack", MNU_GBX_FSMSACK, FTYPE_GBOX }, // id 25 + { "gsms.nack", MNU_GBX_FSMSNACK, FTYPE_GBOX }, // id 26 + { "stats.info", MNU_GBX_FSTAINF, FTYPE_GBOX }, // id 27 + { "expired.info", MNU_GBX_FEXPINF, FTYPE_GBOX }, // id 28 + { "info.log", MNU_GBX_INFOLOG, FTYPE_GBOX }, // id 29 +#endif +#ifdef WITH_EMU + { "SoftCam.Key", MNU_CFG_FSOFTCAMKEY,FTYPE_CONFIG }, // id 30 +#endif + { NULL, 0, 0 }, + }; + + if(use_srvid2) + { + config_files[4].menu_id = MNU_CFG_FSRVID2; + config_files[5].menu_id = MNU_CFG_FSRVID; + } + + if(cfg.http_css) + { + if(strchr(cfg.http_css,'/')) + config_files[8].file = strrchr(cfg.http_css, '/')+1; + else if(strchr(cfg.http_css,'\\')) + config_files[8].file = strrchr(cfg.http_css, '\\')+1; + else + config_files[8].file = cfg.http_css; + tpl_addVar(vars, TPLADD, "FILE_USER_CSS", xml_encode(vars, config_files[8].file)); + } + + if(!apicall) { setActiveMenu(vars, MNU_FILES); } + + tpl_addVar(vars, TPLADD, "APIFILENAME", "null"); + tpl_addVar(vars, TPLADD, "APIWRITABLE", "0"); + + if(use_srvid2) + { + tpl_printf(vars, TPLADD, "SRVID", "%s", "oscam.srvid2"); + tpl_printf(vars, TPLADD, "SRVIDSUB", "%s", "oscam.srvid"); + } + else + { + tpl_printf(vars, TPLADD, "SRVID", "%s", "oscam.srvid"); + tpl_printf(vars, TPLADD, "SRVIDSUB", "%s", "oscam.srvid2"); + } + + char *stoplog = getParam(params, "stoplog"); + if(cs_strlen(stoplog) > 0) + { cs_disable_log(atoi(stoplog)); } + + char *stopusrlog = getParam(params, "stopusrlog"); + if(cs_strlen(stopusrlog) > 0) + { cfg.disableuserfile = atoi(stopusrlog); } + + char *debuglvl = getParam(params, "debug"); + if(cs_strlen(debuglvl) > 0) + { +#ifndef WITH_DEBUG + cs_log("*** Warning: Debug Support not compiled in ***"); +#else + int32_t dblvl = atoi(debuglvl); + if(dblvl >= 0 && dblvl <= 65535) { cs_dblevel = dblvl; } + cs_log("%s debug_level=%d", "all", cs_dblevel); +#endif + } + // Process config files + char *file = getParam(params, "file"); + char targetfile[256] = { 0 }; + int menu_id = 0; + for(entry = config_files; entry->file; entry++) + { + if(streq(file, entry->file)) + { + if(!apicall) { setActiveSubMenu(vars, entry->menu_id); } + menu_id = entry->menu_id; + tpl_addVar(vars, TPLADD, "APIWRITABLE", writable ? "1" : "0"); + switch(entry->type) + { + case FTYPE_CONFIG: + writable = 1; + get_config_filename(targetfile, sizeof(targetfile), entry->file); + break; + case FTYPE_VERSION: + get_tmp_dir_filename(targetfile, sizeof(targetfile), entry->file); + break; + case FTYPE_ANTICASC: +#ifdef CS_ANTICASC + if(!apicall) { snprintf(targetfile, sizeof(targetfile), "%s", ESTR(cfg.ac_logfile)); } +#endif + break; + case FTYPE_LOGFILE: + if(!apicall) { webif_process_logfile(vars, params, targetfile, sizeof(targetfile)); } + break; + case FTYPE_USERFILE: + if(!apicall) { webif_process_userfile(vars, params, targetfile, sizeof(targetfile)); } + break; + case FTYPE_GBOX: +#ifdef MODULE_GBOX + get_gbox_filename(targetfile, sizeof(targetfile), entry->file); +#endif + break; + } + tpl_addVar(vars, TPLADD, "APIFILENAME", entry->file); + break; + } + } + + if(cfg.http_css) + { + tpl_addVar(vars, TPLADD, "VIEW_FILEMENUCSS", tpl_getTpl(vars, "FILEMENUCSS")); + } + + if(!strstr(targetfile, "/dev/")) + { + if(strcmp(getParam(params, "action"), "Save") == 0) + { + if((cs_strlen(targetfile) > 0) /*&& (file_exists(targetfile) == 1)*/) + { + FILE *fpsave; + char *fcontent = getParam(params, "filecontent"); + if((fpsave = fopen(targetfile, "w"))) + { + int32_t i, lastpos = 0, len = cs_strlen(fcontent) + 1; + //write submitted file line by line to disk and remove windows linebreaks + for(i = 0; i < len; ++i) + { + char tmp = fcontent[i]; + if(tmp == '\r' || tmp == '\n' || tmp == 0) + { + fcontent[i] = 0; + fprintf(fpsave, "%s%s", fcontent + lastpos, tmp == 0 ? "" : "\n"); + if(tmp == '\r' && fcontent[i + 1] == '\n') { ++i; } + lastpos = i + 1; + } + } + fclose(fpsave); + tpl_addVar(vars, TPLAPPEND, "APIFILENAME", " saved!"); + // Reinit on save + switch(menu_id) + { + case MNU_CFG_FSRVID: + case MNU_CFG_FSRVID2: + init_srvid(); + break; + case MNU_CFG_FPROVID: + init_provid(); + break; + case MNU_CFG_FUSER: + cs_accounts_chk(); + break; + case MNU_CFG_FDVBAPI: + dvbapi_read_priority(); + break; + case MNU_CFG_FWHITELIST: + global_whitelist_read(); + break; + case MNU_CFG_FFAKECWS: + init_fakecws(); + break; + default: + break; + } + } + } + } + + if((cs_strlen(targetfile) > 0) && (file_exists(targetfile) == 1)) + { + FILE *fp; + char buffer[256]; + + if((fp = fopen(targetfile, "r")) == NULL) { return "0"; } + while(fgets(buffer, sizeof(buffer), fp) != NULL) + if(!strcmp(getParam(params, "filter"), "all")) + { tpl_addVar(vars, TPLAPPEND, "FILECONTENT", buffer); } + else if(strstr(buffer, getParam(params, "filter"))) + { tpl_addVar(vars, TPLAPPEND, "FILECONTENT", buffer); } + fclose(fp); + } + else + { + tpl_addVar(vars, TPLAPPEND, "FILECONTENT", "File does not exist or no file selected!"); + } + } + else + { + tpl_addVar(vars, TPLAPPEND, "FILECONTENT", "File not valid!"); + } + + tpl_addVar(vars, TPLADD, "PART", file); + + if(!writable) + { + tpl_addVar(vars, TPLADD, "WRITEPROTECTION", tpl_getTpl(vars, "WRITEPROTECTION")); + tpl_addVar(vars, TPLADD, "BTNDISABLED", "DISABLED"); + } + + if(!apicall) + { return tpl_getTpl(vars, "FILE"); } + else + { return tpl_getTpl(vars, "APIFILE"); } +} + +static char *send_oscam_failban(struct templatevars * vars, struct uriparams * params, int8_t apicall) +{ + IN_ADDR_T ip2delete; + set_null_ip(&ip2delete); + LL_ITER itr = ll_iter_create(cfg.v_list); + V_BAN *v_ban_entry; + //int8_t apicall = 0; //remove before flight + + if(!apicall) { setActiveMenu(vars, MNU_FAILBAN); } + + if(strcmp(getParam(params, "action"), "delete") == 0) + { + if(strcmp(getParam(params, "intip"), "all") == 0) + { + // clear whole list + while(ll_iter_next(&itr)) + { + ll_iter_remove_data(&itr); + } + } + else + { + //we have a single IP + cs_inet_addr(getParam(params, "intip"), &ip2delete); + while((v_ban_entry = ll_iter_next(&itr))) + { + if(IP_EQUAL(v_ban_entry->v_ip, ip2delete)) + { + ll_iter_remove_data(&itr); + break; + } + } + } + } + ll_iter_reset(&itr); + + struct timeb now; + cs_ftime(&now); + + while((v_ban_entry = ll_iter_next(&itr))) + { + tpl_printf(vars, TPLADD, "IPADDRESS", "%s@%d", cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port); + tpl_addVar(vars, TPLADD, "VIOLATIONUSER", v_ban_entry->info ? v_ban_entry->info : "unknown"); + struct tm st ; + localtime_r(&v_ban_entry->v_time.time, &st); // fix me, we need walltime! + if(!apicall) + { + tpl_printf(vars, TPLADD, "VIOLATIONDATE", "%02d.%02d.%02d %02d:%02d:%02d", + st.tm_mday, st.tm_mon + 1, + st.tm_year % 100, st.tm_hour, + st.tm_min, st.tm_sec); + } + else + { + char tbuffer [30]; + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", &st); + tpl_addVar(vars, TPLADD, "VIOLATIONDATE", tbuffer); + } + + tpl_printf(vars, TPLADD, "VIOLATIONCOUNT", "%d", v_ban_entry->v_count); + + int64_t gone = comp_timeb(&now, &v_ban_entry->v_time); + if(!apicall) + { + if(!v_ban_entry->acosc_entry) + { tpl_addVar(vars, TPLADD, "LEFTTIME", sec2timeformat(vars, (cfg.failbantime * 60) - (gone / 1000))); } + else + { tpl_addVar(vars, TPLADD, "LEFTTIME", sec2timeformat(vars, v_ban_entry->acosc_penalty_dur - (gone / 1000))); } + } + else + { + if(!v_ban_entry->acosc_entry) + { tpl_printf(vars, TPLADD, "LEFTTIME", "%"PRId64"", (cfg.failbantime * 60) - (gone / 1000)); } + else + { tpl_printf(vars, TPLADD, "LEFTTIME", "%"PRId64"", v_ban_entry->acosc_penalty_dur - (gone / 1000)); } + } + + tpl_addVar(vars, TPLADD, "INTIP", cs_inet_ntoa(v_ban_entry->v_ip)); + + if(!apicall) + { tpl_addVar(vars, TPLAPPEND, "FAILBANROW", tpl_getTpl(vars, "FAILBANBIT")); } + else + { tpl_addVar(vars, TPLAPPEND, "APIFAILBANROW", tpl_getTpl(vars, "APIFAILBANBIT")); } + } + if(!apicall) + { return tpl_getTpl(vars, "FAILBAN"); } + else + { return tpl_getTpl(vars, "APIFAILBAN"); } +} + +static bool send_EMM(struct s_reader * rdr, uint16_t caid, const struct s_cardsystem *csystem, const uint8_t *emmhex, uint32_t len) +{ + if(NULL != rdr && NULL != emmhex && 0 != len) + { + EMM_PACKET *emm_pack = NULL; + + if(cs_malloc(&emm_pack, sizeof(EMM_PACKET))) + { + struct s_client *webif_client = cur_client(); + webif_client->grp = 0xFF; /* to access to all readers */ + + memset(emm_pack, '\0', sizeof(EMM_PACKET)); + emm_pack->client = webif_client; + emm_pack->emmlen = len; + memcpy(emm_pack->emm, emmhex, len); + + emm_pack->caid[0] = (caid >> 8) & 0xFF; + emm_pack->caid[1] = caid & 0xFF; + + if(csystem && csystem->get_emm_type) + { + if(!csystem->get_emm_type(emm_pack, rdr)) + { + rdr_log_dbg(rdr, D_EMM, "get_emm_type() returns error"); + } + } + + cs_log_dbg(D_EMM, "emm is being sent to reader %s.", rdr->label); + add_job(rdr->client, ACTION_READER_EMM, emm_pack, sizeof(EMM_PACKET)); + return true; + } + } + + return false; +} + +static bool process_single_emm(struct templatevars * vars, struct s_reader * rdr, uint16_t caid, const struct s_cardsystem *csystem, const char *ep) +{ + + if(NULL != vars && NULL != rdr && NULL != ep) + { + char emmdata[1025] = {'\0'}; /*1024 + '\0'*/ + uint8_t emmhex[513] = {'\0'}; + char buff[7] = {'\0'}; + uint16_t len = 0; + cs_strncpy(emmdata, ep, sizeof(emmdata)); + remove_white_chars(emmdata); + + if('\0' != emmdata[0]) + { + len = cs_strlen(emmdata); + tpl_addVar(vars, TPLADD, "EP", strtoupper(emmdata)); + if(key_atob_l(emmdata, emmhex, len)) + { + tpl_addMsg(vars, "Single EMM has not been sent due to wrong value!"); + } + else + { + len /= 2; + snprintf(buff, sizeof(buff), "0x%02X", len); + tpl_addVar(vars, TPLADD, "EP", strtoupper(emmdata)); + tpl_addVar(vars, TPLADD, "SIZE", buff); + + if(send_EMM(rdr, caid, csystem, emmhex, len)) + { + tpl_addMsg(vars, "Single EMM has been sent."); + return true; + } + } + } + } + tpl_addVar(vars, TPLADD, "SIZE", "0x00"); + return false; +} + +static bool process_emm_file(struct templatevars * vars, struct s_reader * rdr, uint16_t caid, const struct s_cardsystem *csystem, const char *sFilePath) +{ + + bool bret = false; + uint32_t fsize = 0; + uint32_t rlines = 0; + uint32_t wemms = 0; + uint32_t errsize = 0; + char numerrl[256] = {'\0'}; + char buff[20] = {'\0'}; + + if(NULL != rdr && NULL != sFilePath && '\0' != sFilePath[0]) + { + char sMessage[128] = {0}; + if(true == file_exists(sFilePath)) + { + FILE *fp; + if((fp = fopen(sFilePath, "r"))) + { + char line[2048] = {'\0'}; + uint8_t emmhex[513] = {'\0'}; + uint32_t len = 0; + + tpl_addMsg(vars, "EMM file has been processed."); + while(fgets(line, sizeof(line), fp)) + { + ++rlines; + len = cs_strlen(remove_white_chars(line)); + + // wrong emm + if(len > (sizeof(emmhex) * 2) || + key_atob_l(line, emmhex, len)) + { + errsize += snprintf(numerrl + errsize, sizeof(numerrl) - errsize, "%d, ", rlines); + continue; + } + len /= 2; + if(send_EMM(rdr, caid, csystem, emmhex, len)) + { + ++wemms; + int32_t jcount = ll_count(rdr->client->joblist); + if (jcount > 200) + { + /* Give more time to process EMMs */ + cs_sleepms(1000); + } + rdr_log_dbg(rdr, D_READER, "pending emm jobs: %i, processed emms: %i", jcount, wemms); + } + } + fsize = ftell(fp); + fclose(fp); + } + else + { + snprintf(sMessage, sizeof(sMessage), "Cannot open file '%s' (errno=%d: %s)\n", sFilePath, errno, strerror(errno)); + tpl_addMsg(vars, sMessage); + } + } + else + { + snprintf(sMessage, sizeof(sMessage), "FILE \"%s\" not found!", sFilePath); + tpl_addMsg(vars, sMessage); + } + bret = true; + } + + snprintf(buff, sizeof(buff), "%d bytes", fsize); + tpl_addVar(vars, TPLADD, "FSIZE", buff); + snprintf(buff, sizeof(buff), "%d", rlines); + tpl_addVar(vars, TPLADD, "NUMRLINE", buff); + snprintf(buff, sizeof(buff), "%d", wemms); + tpl_addVar(vars, TPLADD, "NUMWEMM", buff); + tpl_addVar(vars, TPLADD, "ERRLINE", numerrl); + + return bret; +} + +static char *send_oscam_EMM_running(struct templatevars * vars, struct uriparams * params) +{ + + struct s_reader *rdr = NULL; + + setActiveMenu(vars, MNU_READERS); + tpl_addVar(vars, TPLADD, "READER", getParam(params, "label")); + tpl_addVar(vars, TPLADD, "FNAME", getParam(params, "emmfile")); + + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + int32_t tcaid = dyn_word_atob(getParam(params, "emmcaid")); + uint16_t caid = (-1 != tcaid) ? (uint16_t)tcaid : 0; + char buff[7] = ""; + const struct s_cardsystem *csystem = NULL; + int32_t proxy = is_cascading_reader(rdr); + + if((proxy || !rdr->csystem_active) && caid) // network reader (R_CAMD35 R_NEWCAMD R_CS378X R_CCCAM) + { + if(proxy && !rdr->ph.c_send_emm) + { + tpl_addMsg(vars, "The reader does not support EMM's!"); + return tpl_getTpl(vars, "EMM_RUNNING"); + } + + csystem = get_cardsystem_by_caid(caid); + if(!csystem) + { + rdr_log_dbg(rdr, D_EMM, "unable to find cardsystem for caid %04X", caid); + caid = 0; + } + } + else if(!proxy && rdr->csystem_active) // local active reader + { + csystem = rdr->csystem; + + if(rdr->typ != R_EMU) + { + caid = rdr->caid; + } + } + + if(csystem) + { + tpl_addVar(vars, TPLADD, "SYSTEM", csystem->desc); + } + else + { + tpl_addVar(vars, TPLADD, "SYSTEM", "unknown"); + } + if(caid) + { + snprintf(buff, sizeof(buff), "0x%04X", caid); + tpl_addVar(vars, TPLADD, "CAID", buff); + } + else + { + tpl_addVar(vars, TPLADD, "CAID", "unknown"); + } + + process_single_emm(vars, rdr, caid, csystem, getParam(params, "ep")); + process_emm_file(vars, rdr, caid, csystem, getParam(params, "emmfile")); + } + else + { + char sMessage[128] = {0}; + snprintf(sMessage, sizeof(sMessage), "READER \"%s\" not found!", getParam(params, "label")); + tpl_addMsg(vars, sMessage); + tpl_addVar(vars, TPLADD, "READER", "reader not found"); + } + + return tpl_getTpl(vars, "EMM_RUNNING"); +} + +static char *send_oscam_EMM(struct templatevars * vars, struct uriparams * params) +{ + + setActiveMenu(vars, MNU_READERS); + tpl_addVar(vars, TPLADD, "READER", getParam(params, "label")); + + struct s_reader *rdr = NULL; + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr && rdr->caid) + { + char buff[5] = ""; + snprintf(buff, sizeof(buff), "%04X", rdr->caid); + tpl_addVar(vars, TPLADD, "CAID", buff); + if(!is_cascading_reader(rdr)) + { + tpl_addVar(vars, TPLADD, "READONLY", "readonly=\"readonly\""); + } + } + + FILE *fp; + struct stat sb; + char buffer[1024]; + char emm_hex[1024]; + char filename[128]; + char targetfile[256]; + char tmpstr[20]; + char emm_txt[32]; + char emm_title[32]; + char *emm_path; + char *emm_types[] = { "unique_emm", "shared_emm", "global_emm" }; + char *emm_names[] = { "RDREMMUNIQUE", "RDREMMSHARED", "RDREMMGLOBAL" }; + char *emm_cfg_names[] = { "httpemmuclean", "httpemmsclean", "httpemmgclean" }; + int32_t emm_max_size[] = { cfg.http_emmu_clean, cfg.http_emms_clean, cfg.http_emmg_clean }; + int num_emm_types = 3; + int i; + + emm_path = cfg.emmlogdir ? cfg.emmlogdir : cs_confdir; + + for( i = 0 ; i < num_emm_types; i++ ) + { + snprintf(filename, sizeof(filename), "%s%s%s%s", getParam(params, "label"), "_", emm_types[i], ".log"); + snprintf(targetfile, sizeof(targetfile), "%s%s%s", emm_path, emm_path[cs_strlen(emm_path) - 1] == '/' ? "" : "/", filename); + snprintf(emm_txt, sizeof(emm_txt), "%s_TXT", emm_names[i]); + tpl_addVar(vars, TPLADD, emm_txt, filename); + + if((fp = fopen(targetfile, "r")) != NULL) + { + stat(targetfile, &sb); + tpl_printf(vars, TPLAPPEND, emm_txt, " (Size: %'.2f kB)", (double)sb.st_size/1024); + + if(emm_max_size[i]>=0) + { + snprintf(emm_title, sizeof(emm_title), "%s_TITLE", emm_names[i]); + tpl_addVar(vars, TPLADD, emm_title, "title=\"Click Line to Copy EMM in Single EMM Write Field\""); + } + + if(emm_max_size[i]>0) + { + uint32_t emms=0, emm_d, emmrs=0; + char *ptr, *saveptr1 = NULL; + + while(fgets(buffer, sizeof(buffer), fp) != NULL) + { + emms++; + snprintf(tmpstr, sizeof(tmpstr), "LINE_%d", emms); + tpl_addVar(vars, TPLADD, tmpstr, buffer); + } + + for(emm_d=emms;emm_d>0;--emm_d) + { + snprintf(tmpstr, sizeof(tmpstr), "LINE_%d", emm_d); + if(sscanf(tpl_getVar(vars, tmpstr), "%*s %*s %*s %s", &emm_hex[0])==1) + { + if(strstr(tpl_getVar(vars, "EMM_TMP"),emm_hex)==0) + { tpl_addVar(vars, TPLAPPEND, "EMM_TMP", tpl_getVar(vars, tmpstr)); } + tpl_addVar(vars, TPLADD, tmpstr, ""); + } + } + + for(ptr = strtok_r(tpl_getVar(vars, "EMM_TMP"),"\n", &saveptr1); ptr; ptr = strtok_r(NULL,"\n", &saveptr1)) + { + emmrs++; + snprintf(tmpstr, sizeof(tmpstr), "LINE_%d", emmrs); + tpl_addVar(vars, TPLADD, tmpstr, ptr); + } + tpl_addVar(vars, TPLADD, "EMM_TMP", ""); + + tpl_printf(vars, TPLAPPEND, emm_txt, ": %'d different EMM's from a total of %'d Entries", emmrs,emms); + for(emm_d=emmrs;emm_d>0;--emm_d) + { + snprintf(tmpstr, sizeof(tmpstr), "LINE_%d", emm_d); + tpl_printf(vars, TPLAPPEND, emm_names[i], "%s\n", tpl_getVar(vars, tmpstr)); + if(sb.st_size>emm_max_size[i]*1024) { tpl_printf(vars, TPLAPPEND, "EMM_TMP", "%s\n", tpl_getVar(vars, tmpstr)); } + tpl_addVar(vars, TPLADD, tmpstr, ""); + } + + if(sb.st_size>emm_max_size[i]*1024) + { + char orgfile[268]; + int f=0; + do { + snprintf(orgfile, sizeof(orgfile), "%s.%d", targetfile, f); + f++; + } while(access(orgfile, F_OK) != -1); + + if(rename(targetfile, orgfile) == 0) + { + FILE *fs = fopen(targetfile, "w"); + if (fs) + { + fprintf(fs, "%s", tpl_getVar(vars, "EMM_TMP")); + fclose(fs); + } + tpl_printf(vars, TPLAPPEND, emm_txt, "
New reduced File created! Size of Original File is higher as %d kB, saved to %s", emm_max_size[i], orgfile); + } + } + tpl_addVar(vars, TPLADD, "EMM_TMP", ""); + } + else if (emm_max_size[i]==0) + { + while(fgets(buffer, sizeof(buffer), fp) != NULL) + { + tpl_printf(vars, TPLAPPEND, emm_names[i], "%s", buffer); + } + } + else + { + tpl_printf(vars, TPLADD, emm_names[i],"Viewing of EMM File deactivated.
Set %s in Config Webif to 0 or higher for viewing or filtering EMM File.", emm_cfg_names[i]); + } + fclose(fp); + } + if(strcmp(tpl_getVar(vars, emm_names[i]),"")==0) { tpl_addVar(vars, TPLADD, emm_names[i],"no saved EMM's"); } + } + + return tpl_getTpl(vars, "ASKEMM"); +} + +#ifdef CS_CACHEEX +static uint64_t get_cacheex_node(struct s_client * cl) +{ + uint64_t node = 0x00; +#if defined(MODULE_CCCAM) || defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP) + struct s_module *module = (cl->reader ? &cl->reader->ph : get_module(cl)); +#endif +#ifdef MODULE_CCCAM + if(module->num == R_CCCAM && cl->cc) + { + struct cc_data *cc = cl->cc; + memcpy(&node, cc->peer_node_id, 8); + } + else +#endif +#ifdef MODULE_CAMD35 + if(module->num == R_CAMD35) + { + memcpy(&node, cl->ncd_skey, 8); + } + else +#endif +#ifdef MODULE_CAMD35_TCP + if(module->num == R_CS378X) + { + memcpy(&node, cl->ncd_skey, 8); + } + else +#endif + {} + return node; +} + + +static char *send_oscam_cacheex(struct templatevars * vars, struct uriparams * params, int8_t apicall) +{ + + if(!apicall) { setActiveMenu(vars, MNU_CACHEEX); } + + if(strcmp(getParam(params, "x"), "x") == 0) + { + // avoid compilerwarning unused vars + } + char *level[] = {"NONE", "CACHE PULL", "CACHE PUSH", "REVERSE CACHE PUSH"}; + char *getting = "\"Getting\""; + char *pushing = "\"Pushing\""; + char *rowvariable = ""; + + int16_t written = 0; +#ifdef CS_CACHEEX_AIO + int16_t i = 0; +#endif + struct s_client *cl; + time_t now = time((time_t *)0); + int delimiter=0; + + if(!apicall) + { + if(strcmp(getParam(params, "action"), "resetallcacheexstats") == 0) + { + cacheex_clear_all_stats(); + } + } + + tpl_printf(vars, TPLADD, "OWN_CACHEEX_NODEID", "%" PRIu64 "X", cacheex_node_id(cacheex_peer_id)); + + const char *cacheex_name_link_tpl = NULL; + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + + for(cl = first_client; cl ; cl = cl->next) + { +#ifdef CS_CACHEEX_AIO + i++; + char classname[9]; + snprintf(classname, 8, "class%02d", i) < 0 ? abort() : (void)0; + classname[8] = '\0'; + tpl_addVar(vars, TPLADD, "CLASSNAME", classname); +#endif + + if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) + { + cacheex_name_link_tpl = "SUSER"; + tpl_addVar(vars, TPLADD, "TYPE", "Client"); + if(!apicall) + { + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, cl->account->usr)); + tpl_addVar(vars, TPLADD, "USERENC", urlencode(vars, cl->account->usr)); + + if(cl->account->description) + { + tpl_printf(vars, TPLADD, "CLIENTDESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, cl->account->description)); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + } + else + { + tpl_addVar(vars, TPLADD, "NAME", cl->account->usr); + + if(cl->account->description) + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", cl->account->description); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + } + + if(IP_ISSET(cl->ip)) + { tpl_addVar(vars, TPLADD, "IP", cs_inet_ntoa(cl->ip)); } + else if(cl->login > cl->logout) + { tpl_addVar(vars, TPLADD, "IP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "IP", ""); } + tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", get_cacheex_node(cl)); + tpl_addVar(vars, TPLADD, "LEVEL", level[cl->account->cacheex.mode]); + tpl_printf(vars, TPLADD, "PUSH", "%d", cl->account->cwcacheexpush); + tpl_printf(vars, TPLADD, "GOT", "%d", cl->account->cwcacheexgot); + tpl_printf(vars, TPLADD, "CWCINFO", "%d", cl->account->cwc_info); + tpl_printf(vars, TPLADD, "HIT", "%d", cl->account->cwcacheexhit); + tpl_printf(vars, TPLADD, "ERR", "%d", cl->account->cwcacheexerr); + tpl_printf(vars, TPLADD, "ERRCW", "%d", cl->account->cwcacheexerrcw); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "GOTLG", "%d", cl->account->cwcacheexgotlg); + tpl_printf(vars, TPLADD, "PUSHLG", "%d", cl->account->cwcacheexpushlg); + tpl_printf(vars, TPLADD, "REL_CACHEXHITGOT", "%.2f", (double)(cl->account->cwcacheexhit ? (double)cl->account->cwcacheexhit : 0) * 100 / (double)(cl->account->cwcacheexgot ? cl->account->cwcacheexgot : 1)); +#endif + tpl_addVar(vars, TPLADD, "DIRECTIONIMG", (cl->account->cacheex.mode == 3) ? getting : pushing); + rowvariable = "TABLECLIENTROWS"; + written = 1; + } + else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) + { + cacheex_name_link_tpl = "SREADER"; + tpl_addVar(vars, TPLADD, "TYPE", "Reader"); + + if(!apicall) + { + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, cl->reader->label)); + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, cl->reader->label)); + + if(cl->reader->description) + { + tpl_printf(vars, TPLADD, "CLIENTDESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, cl->reader->description)); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + } + else + { + tpl_addVar(vars, TPLADD, "NAME", cl->reader->label); + + if(cl->reader->description) + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", cl->reader->description); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + } + + if(IP_ISSET(cl->ip)) + { tpl_addVar(vars, TPLADD, "IP", cs_inet_ntoa(cl->ip)); } + else if(cl->reader && cl->reader->tcp_connected) + { tpl_addVar(vars, TPLADD, "IP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "IP", ""); } + tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", get_cacheex_node(cl)); + tpl_addVar(vars, TPLADD, "LEVEL", level[cl->reader->cacheex.mode]); + tpl_printf(vars, TPLADD, "PUSH", "%d", cl->cwcacheexpush); + tpl_printf(vars, TPLADD, "CWCINFO", "%d", cl->cwc_info); + tpl_printf(vars, TPLADD, "GOT", "%d", cl->cwcacheexgot); + tpl_printf(vars, TPLADD, "CWCINFO", "%d", cl->cwc_info); + tpl_printf(vars, TPLADD, "HIT", "%d", cl->cwcacheexhit); + tpl_printf(vars, TPLADD, "ERR", "%d", cl->cwcacheexerr); + tpl_printf(vars, TPLADD, "ERRCW", "%d", cl->cwcacheexerrcw); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "GOTLG", "%d", cl->cwcacheexgotlg); + tpl_printf(vars, TPLADD, "PUSHLG", "%d", cl->cwcacheexpushlg); + tpl_printf(vars, TPLADD, "REL_CACHEXHITGOT", "%.2f", (double)(cl->cwcacheexhit ? (double)cl->cwcacheexhit : 0) * 100 / (double)(cl->cwcacheexgot ? cl->cwcacheexgot : 1)); +#endif + tpl_addVar(vars, TPLADD, "DIRECTIONIMG", (cl->reader->cacheex.mode == 3) ? pushing : getting); + + rowvariable = "TABLEREADERROWS"; + written = 1; + } + else if(get_module(cl)->listenertype == LIS_CSPUDP) + { + cacheex_name_link_tpl = "SREADER"; + tpl_addVar(vars, TPLADD, "TYPE", "csp"); + + if(!apicall) + { + tpl_addVar(vars, TPLADD, "READERNAME", "csp"); + tpl_addVar(vars, TPLADD, "READERNAMEENC", "csp"); + } + else + { + tpl_addVar(vars, TPLADD, "NAME", "csp"); + } + + if(IP_ISSET(cl->ip)) + { tpl_addVar(vars, TPLADD, "IP", cs_inet_ntoa(cl->ip)); } + else if(cl->login > cl->logout) + { tpl_addVar(vars, TPLADD, "IP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "IP", ""); } + tpl_addVar(vars, TPLADD, "NODE", "csp"); + + if(cl->cwcacheexping) + { + tpl_printf(vars, TPLADD, "LEVEL", "csp (%d ms)", cl->cwcacheexping); + } + else + { + tpl_addVar(vars, TPLADD, "LEVEL", "csp"); + } + + tpl_printf(vars, TPLADD, "PUSH", "%d", cl->cwcacheexpush); + tpl_printf(vars, TPLADD, "GOT", "%d", cl->cwcacheexgot); + tpl_printf(vars, TPLADD, "HIT", "%d", cl->cwcacheexhit); + tpl_printf(vars, TPLADD, "ERR", "%d", cl->cwcacheexerr); + tpl_printf(vars, TPLADD, "ERRCW", "%d", cl->cwcacheexerrcw); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "GOTLG", "%d", cl->cwcacheexgotlg); + tpl_printf(vars, TPLADD, "PUSHLG", "%d", cl->cwcacheexpushlg); + tpl_printf(vars, TPLADD, "REL_CACHEXHITGOT", "%.2f", (double)(cl->cwcacheexhit ? (double)cl->cwcacheexhit : 0) * 100 / (double)(cl->cwcacheexgot ? cl->cwcacheexgot : 1)); +#endif + tpl_addVar(vars, TPLADD, "DIRECTIONIMG", getting); + rowvariable = "TABLECLIENTROWS"; + written = 1; + } + + if(written) + { + if(!apicall) + { + tpl_addVar(vars, TPLADD, "NAME", tpl_getTpl(vars, cacheex_name_link_tpl)); + } +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLAPPEND, rowvariable, tpl_getTpl(vars, "CACHEEXAIOTABLEROW")); +#else + tpl_addVar(vars, TPLAPPEND, rowvariable, tpl_getTpl(vars, "CACHEEXTABLEROW")); +#endif + + if(cl->ll_cacheex_stats) + { + LL_ITER itr = ll_iter_create(cl->ll_cacheex_stats); + S_CACHEEX_STAT_ENTRY *cacheex_stats_entry; + + while((cacheex_stats_entry = ll_iter_next(&itr))) + { + tpl_addVar(vars, TPLADD, "DIRECTIONIMG", ""); + if(now - cacheex_stats_entry->cache_last < 20) + { tpl_addVar(vars, TPLADD, "TYPE", cacheex_stats_entry->cache_direction == 0 ? pushing : getting); } + else + { tpl_addVar(vars, TPLADD, "TYPE", ""); } + tpl_printf(vars, TPLADD, "NAME", "%04X@%06X:%04X", cacheex_stats_entry->cache_caid, + cacheex_stats_entry->cache_prid, + cacheex_stats_entry->cache_srvid); + if(cacheex_stats_entry->cache_direction == 0) + { + tpl_printf(vars, TPLADD, "PUSH", "%d", cacheex_stats_entry->cache_count); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "PUSHLG", "%d", cacheex_stats_entry->cache_count_lg); +#endif + tpl_addVar(vars, TPLADD, "GOT", ""); + } + else + { + tpl_printf(vars, TPLADD, "GOT", "%d", cacheex_stats_entry->cache_count); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "GOTLG", "%d", cacheex_stats_entry->cache_count_lg); +#endif + tpl_addVar(vars, TPLADD, "PUSH", ""); + } + tpl_addVar(vars, TPLADD, "HIT", ""); + char channame[CS_SERVICENAME_SIZE]; + char *lastchan = xml_encode(vars, get_servicename(cl, cacheex_stats_entry->cache_srvid, cacheex_stats_entry->cache_prid, cacheex_stats_entry->cache_caid, channame, sizeof(channame))); + tpl_addVar(vars, TPLADD, "LEVEL", lastchan); + if (apicall == 2) + { + tpl_printf(vars, TPLADD, "JSONDELIMITER", "%s", (delimiter > 1)?",":""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLAPPEND, "JSONCACHEEXBITS", tpl_getTpl(vars, "JSONCACHEEXAIOBIT")); +#else + tpl_addVar(vars, TPLAPPEND, "JSONCACHEEXBITS", tpl_getTpl(vars, "JSONCACHEEXBIT")); +#endif + delimiter++; + } + else + { +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLAPPEND, rowvariable, tpl_getTpl(vars, "CACHEEXAIOTABLEROWSTATS")); +#else + tpl_addVar(vars, TPLAPPEND, rowvariable, tpl_getTpl(vars, "CACHEEXTABLEROW")); +#endif + } + } + } + written = 0; + } + } + + float cachesum = first_client ? first_client->cwcacheexgot : 1; + if(cachesum < 1) + { + cachesum = 1; + } + tpl_printf(vars, TPLADD, "TOTAL_CACHEXPUSH", PRINTF_LOCAL_D, first_client ? first_client->cwcacheexpush : 0); + tpl_addVar(vars, TPLADD, "TOTAL_CACHEXPUSH_IMG", pushing); + tpl_printf(vars, TPLADD, "TOTAL_CACHEXGOT", PRINTF_LOCAL_D, first_client ? first_client->cwcacheexgot : 0); + tpl_addVar(vars, TPLADD, "TOTAL_CACHEXGOT_IMG", getting); + tpl_printf(vars, TPLADD, "TOTAL_CACHEXHIT", PRINTF_LOCAL_D, first_client ? first_client->cwcacheexhit : 0); + tpl_printf(vars, TPLADD, "TOTAL_CACHESIZE", "%d", cache_size()); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "TOTAL_CACHESIZE_LG", "%d", cache_size_lg()); +#endif + + tpl_printf(vars, TPLADD, "REL_CACHEXHIT", "%.2f", (first_client ? first_client->cwcacheexhit : 0) * 100 / cachesum); + + if(!apicall) + { +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "CACHEEXAIOPAGE"); +#else + return tpl_getTpl(vars, "CACHEEXPAGE"); +#endif + } + else + { + return tpl_getTpl(vars, "JSONCACHEEX"); + } +} +#endif + +#ifdef WEBIF_WIKI +static char *send_oscam_wiki(struct templatevars *vars, struct uriparams *params) +{ + const char *config = getParam(params, "config"); + const char *section = getParam(params, "section"); + const char *param = getParam(params, "param"); + + if(!config || !param || !config[0] || !param[0]) + { + return tpl_getTpl(vars, "WIKIERROR"); + } + + const char *text = wiki_get_help(config, section, param); + + if(text) + { + tpl_addVar(vars, TPLADD, "WIKIPARAM", param); + tpl_addVar(vars, TPLADD, "WIKICONFIG", config); + tpl_addVar(vars, TPLADD, "WIKITEXT", json_encode(vars, text)); + return tpl_getTpl(vars, "WIKIJSON"); + } + else + { + tpl_addVar(vars, TPLADD, "WIKIPARAM", param); + tpl_addVar(vars, TPLADD, "WIKICONFIG", config); + return tpl_getTpl(vars, "WIKINOTFOUND"); + } +} + +static char *send_oscam_wiki_status(struct templatevars *vars, struct uriparams *params) +{ + const char *config = getParam(params, "config"); + + if(!config || !config[0]) + { + return tpl_getTpl(vars, "WIKIERROR"); + } + + /* Build JSON object with status for all params in this config */ + char json_buf[8192]; + int pos = 0; + int32_t i, count = wiki_count(); + bool first = true; + + pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, "{"); + +#ifdef COMPRESSED_WIKI + /* For compressed wiki, we iterate through entries but access via decompressed data */ + const struct wiki_entry *entries = wiki_get_entries(); + char *wiki_data = wiki_get_decompressed_data(); + + if(wiki_data) + { + for(i = 0; i < count && pos < (int)sizeof(json_buf) - 100; i++) + { + const char *e_config = wiki_data + entries[i].config_ofs; + const char *e_param = wiki_data + entries[i].param_ofs; + + if(strcmp(e_config, config) != 0) + { continue; } + + /* Section filter is optional - include all params from this config */ + if(!first) + { pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, ","); } + first = false; + + const char *status_str = "ok"; + if(entries[i].status == 1) status_str = "review"; + else if(entries[i].status == 2) status_str = "missing"; + + pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, "\"%s\":\"%s\"", + e_param, status_str); + } + } +#else + const struct wiki_entry *entries = wiki_get_entries(); + for(i = 0; i < count && pos < (int)sizeof(json_buf) - 100; i++) + { + if(strcmp(entries[i].config, config) != 0) + { continue; } + + /* Section filter is optional - include all params from this config */ + if(!first) + { pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, ","); } + first = false; + + const char *status_str = "ok"; + if(entries[i].status == 1) status_str = "review"; + else if(entries[i].status == 2) status_str = "missing"; + + pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, "\"%s\":\"%s\"", + entries[i].param, status_str); + } +#endif + + pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, "}"); + + tpl_addVar(vars, TPLADD, "WIKISTATUS", json_buf); + return tpl_getTpl(vars, "WIKISTATUSJSON"); +} +#endif + +static char *send_oscam_api(struct templatevars * vars, FILE * f, struct uriparams * params, int8_t *keepalive, int8_t apicall, char *extraheader) +{ + if(strcmp(getParam(params, "part"), "status") == 0) + { + return send_oscam_status(vars, params, apicall); + } + else if(strcmp(getParam(params, "part"), "userstats") == 0) + { + return send_oscam_user_config(vars, params, apicall); + } + else if(strcmp(getParam(params, "part"), "failban") == 0) + { + return send_oscam_failban(vars, params, apicall); + } +#ifdef CS_CACHEEX + else if(strcmp(getParam(params, "part"), "cacheex") == 0) + { + return send_oscam_cacheex(vars, params, apicall); + } +#endif + else if(strcmp(getParam(params, "part"), "files") == 0) + { + return send_oscam_files(vars, params, apicall); + } + else if(strcmp(getParam(params, "part"), "readerlist") == 0) + { + return send_oscam_reader(vars, params, apicall); + } + else if(strcmp(getParam(params, "part"), "serverconfig") == 0) + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "serverconfig not yet avail"); + return tpl_getTpl(vars, "APIERROR"); + } + else if(strcmp(getParam(params, "part"), "userconfig") == 0) + { + if(((strcmp(getParam(params, "action"), "Save") == 0) || + (strcmp(getParam(params, "action"), "Save As") == 0)) && cfg.http_readonly == 1) + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "API is in readonly mode"); + return tpl_getTpl(vars, "APIERROR"); + } + else + { + struct s_auth *account = get_account_by_name(getParam(params, "user")); + if(!account && strcmp(getParam(params, "action"), "Save")) + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "user not exist"); + return tpl_getTpl(vars, "APIERROR"); + } + else + { + return send_oscam_user_config_edit(vars, params, apicall); + } + } + } + else if(strcmp(getParam(params, "part"), "entitlement") == 0) + { + + if(strcmp(getParam(params, "label"), "")) + { + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + if(rdr->enable == 1) + { + return send_oscam_entitlement(vars, params, apicall); + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no cccam reader or disabled"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "reader not exist"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no reader selected"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else if(strcmp(getParam(params, "part"), "ecmhistory") == 0) + { + int32_t isec; + int32_t shown; + time_t now = time((time_t *)0); + const char *usr; + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->wihidden != 1) + { + isec = now - cl->lastecm; + usr = username(cl); + shown = 0; + if(strcmp(getParam(params, "label"), "") == 0) + { + if(strcmp(getParam(params, "type"), "servers") == 0) + { + if(cl->typ == 'p' || cl->typ == 'r') + { shown = 1; } + } + else if(strcmp(getParam(params, "type"), "users") == 0) + { + if(cl->typ == 'c') + { shown = 1; } + } + else + { + shown = 1; + } + } + else if(strcmp(getParam(params, "label"), usr) == 0) + { + shown = 1; + } + if(shown == 1) + { + tpl_printf(vars, TPLADD, "CLIENTTYPE", "%c", cl->typ); + tpl_addVar(vars, TPLADD, "CLIENTUSER", xml_encode(vars, usr)); + if(cl->typ == 'c' || cl->typ == 'm') + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", xml_encode(vars, cl->account->description ? cl->account->description : "")); + } + else if(cl->typ == 'p' || cl->typ == 'r') + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", xml_encode(vars, cl->reader->description ? cl->reader->description : "")); + } + tpl_printf(vars, TPLADD, "CLIENTLASTRESPONSETIME", "%d", cl->cwlastresptime ? cl->cwlastresptime : -1); + tpl_printf(vars, TPLADD, "CLIENTIDLESECS", "%d", isec); + + //load historical values from ringbuffer + char *value = get_ecm_fullhistorystring(cl); + tpl_addVar(vars, TPLADD, "CLIENTLASTRESPONSETIMEHIST", value); + free_mk_t(value); + + tpl_addVar(vars, TPLAPPEND, "APISTATUSBITS", tpl_getTpl(vars, "APISTATUSBIT")); + } + } + } + return tpl_getTpl(vars, "APISTATUS"); + } + else if(strcmp(getParam(params, "part"), "readerstats") == 0) + { + if(strcmp(getParam(params, "label"), "")) + { + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + return send_oscam_reader_stats(vars, params, apicall); + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "reader not exist"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no reader selected"); + return tpl_getTpl(vars, "APIERROR"); + } + } +#ifdef READER_VIDEOGUARD + else if(strcmp(getParam(params, "part"), "sendcmd") == 0) + { + if(strcmp(getParam(params, "label"), "")) + { + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + char api_msg[150]; + CMD_PACKET *cmd_pack = NULL; + if(!cs_malloc(&cmd_pack, sizeof(CMD_PACKET))) + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "cs_malloc failed!"); + return tpl_getTpl(vars, "APIERROR"); + } + struct s_client *webif_client = cur_client(); + webif_client->grp = 0xFF; // access all readers + + memset(cmd_pack, '0', sizeof(CMD_PACKET)); + cmd_pack->client = webif_client; + cmd_pack->cmdlen = strlen(getParam(params, "cmd")) / 2; + + if(cmd_pack->cmdlen > 0 && (unsigned long)abs(cmd_pack->cmdlen) <= sizeof(cmd_pack->cmd)) + { + if(key_atob_l(getParam(params, "cmd"), cmd_pack->cmd, cmd_pack->cmdlen*2)) + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "'cmd' has not been sent due to wrong value!"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + if(cmd_pack->cmdlen) + { + snprintf(api_msg, sizeof(api_msg), "Command would exceed %lu bytes!", (long unsigned int)sizeof(cmd_pack->cmd)); + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", api_msg); + } + else + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "Missing parameter 'cmd'!"); + } + return tpl_getTpl(vars, "APIERROR"); + } + + struct s_client *cl = rdr->client; + if(rdr->enable == 1 && cl && cl->typ == 'r') + { + add_job(cl, ACTION_READER_SENDCMD, cmd_pack, sizeof(CMD_PACKET)); + tpl_addVar(vars, TPLADD, "APICONFIRMMESSAGE", "command sent"); + return tpl_getTpl(vars, "APICONFIRMATION"); + } + else + { + snprintf(api_msg, sizeof(api_msg), "Reader '%s' is not suitable!", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", api_msg); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no such reader"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no reader selected"); + return tpl_getTpl(vars, "APIERROR"); + } + } +#endif + else if(strcmp(getParam(params, "part"), "shutdown") == 0) + { + if((strcmp(strtolower(getParam(params, "action")), "restart") == 0) || + (strcmp(strtolower(getParam(params, "action")), "shutdown") == 0)) + { + if(!cfg.http_readonly) + { + return send_oscam_shutdown(vars, f, params, apicall, keepalive, extraheader); + } + else + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "webif readonly mode"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "missing parameter action"); + return tpl_getTpl(vars, "APIERROR"); + } + + } + else + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "part not found"); + return tpl_getTpl(vars, "APIERROR"); + } +} + +static char *send_oscam_image(struct templatevars * vars, FILE * f, struct uriparams * params, char *image, time_t modifiedheader, uint32_t etagheader, char *extraheader) +{ + char *wanted; + if(image == NULL) { wanted = getParam(params, "i"); } + else { wanted = image; } + if(cs_strlen(wanted) > 3 && wanted[0] == 'I' && wanted[1] == 'C') + { + if(etagheader == 0) + { + int8_t disktpl = 0; + char *tpl_path; + tpl_path = cfg.http_piconpath? cfg.http_piconpath : cfg.http_tpl; + + if(tpl_path) + { + char path[255]; + if(cs_strlen(tpl_getTplPath(wanted, tpl_path, path, 255)) > 0 && file_exists(path)) + { + struct stat st; + disktpl = 1; + stat(path, &st); + if((time_t)st.st_mtime < modifiedheader) + { + send_header304(f, extraheader); + return "1"; + } + } + } + if(disktpl == 0 && first_client->login < modifiedheader) + { + send_header304(f, extraheader); + return "1"; + } + } + char *header = strstr(tpl_getTpl(vars, wanted), "data:"); + if(header != NULL) + { + char *ptr = header + 5; + while(ptr[0] != ';' && ptr[0] != '\0') { ++ptr; } + if(ptr[0] != '\0' && ptr[1] != '\0') { ptr[0] = '\0'; } + else { return "0"; } + ptr = strstr(ptr + 1, "base64,"); + if(ptr != NULL) + { + int32_t len = b64decode((uint8_t *)ptr + 7); + if(len > 0) + { + if((uint32_t)crc32(0L, (uint8_t *)ptr + 7, len) == etagheader) + { + send_header304(f, extraheader); + } + else + { + send_headers(f, 200, "OK", extraheader, header + 5, 1, len, ptr + 7, 0); + webif_write_raw(ptr + 7, f, len); + } + return "1"; + } + } + } + } + // Return file not found + const char *not_found = "File not found.\n"; + send_headers(f, 404, "Not Found", extraheader, "text/plain", 0, cs_strlen(not_found), (char *)not_found, 0); + webif_write_raw((char *)not_found, f, cs_strlen(not_found)); + return "1"; +} + +static char *send_oscam_robots_txt(FILE * f) +{ + const char *content = "User-agent: *\nDisallow: /\n"; + send_headers(f, 200, "OK", NULL, "text/plain", 0, cs_strlen(content), (char *)content, 0); + webif_write_raw((char *)content, f, cs_strlen(content)); + return "1"; +} + +static char *send_oscam_graph(struct templatevars * vars) +{ + return tpl_getTpl(vars, "GRAPH"); +} + +#ifdef MODULE_GHTTP +static bool ghttp_autoconf(struct templatevars * vars, struct uriparams * params) +{ + int8_t i = 0; + struct s_reader *rdr; + char *name = getParam(params, "gacname"); + if(cs_strlen(name) < 3) + { + tpl_addMsg(vars, "Invalid host name!"); + return false; + } + + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + if(rdr->ph.num == R_GHTTP) { i++; } // count existing ghttp readers + + while(i < 3) // if less than 3, add more + { + char lbl[128]; + snprintf(lbl, sizeof(lbl), "%s%d", "ghttp", i + 1); + cs_log("GHttp autoconf: adding reader %s", lbl); + struct s_reader *newrdr; + if(!cs_malloc(&newrdr, sizeof(struct s_reader))) + { + tpl_addMsg(vars, "Create reader failed!"); + return false; + }; + newrdr->typ = R_GHTTP; + cs_strncpy(newrdr->label, lbl, sizeof(newrdr->label)); + module_reader_set(newrdr); + reader_set_defaults(newrdr); + newrdr->enable = 0; + newrdr->grp = 1; + ll_append(configured_readers, newrdr); + i++; + } + + uint16_t port = 0; + char *str = strstr(name, ":"); + if(str) + { + port = atoi(str + 1); + str[0] = '\0'; + } + + i = 0; + itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->ph.num == R_GHTTP) + { + if(i > 2) // remove superflous + { + cs_log("GHttp autoconf: removing reader %s", rdr->label); + inactivate_reader(rdr); + ll_iter_remove(&itr); + free_reader(rdr); + } + else // reconfigure the 3 first ghttp readers + { + cs_log("GHttp autoconf: reconfiguring reader %s", rdr->label); + snprintf(rdr->label, sizeof(rdr->label), "%s%d", "ghttp", i + 1); + rdr->r_port = port; + rdr->enable = 1; + rdr->ghttp_use_ssl = 0; +#ifdef WITH_SSL + rdr->ghttp_use_ssl = 1; +#endif + if(rdr->grp < 1) { rdr->grp = 1; } + cs_strncpy(rdr->r_usr, getParam(params, "gacuser"), sizeof(rdr->r_usr)); + cs_strncpy(rdr->r_pwd, getParam(params, "gacpasswd"), sizeof(rdr->r_pwd)); + if(i == 0) { cs_strncpy(rdr->device, name, sizeof(rdr->device)); } + else + { + if(!strstr(name, ".")) + { snprintf(rdr->device, sizeof(rdr->device), "%s%d", name, i); } // name, name1, name2 + else { cs_strncpy(rdr->device, name, sizeof(rdr->device)); } + // . in the name = assume full hostname = use same for all 3 readers + } + if(i == 2) { rdr->fallback = 1; } + else { rdr->fallback = 0; } + i++; + } + } + } + cs_log("GHttp autoconf: Saving %d readers", i); + if(write_server() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->ph.num == R_GHTTP) + { restart_cardreader(rdr, 1); } + } + return true; +} + +static char *send_oscam_ghttp(struct templatevars * vars, struct uriparams * params, int8_t apicall) +{ + if(strcmp(strtolower(getParam(params, "action")), "autoconf") == 0) + { + if(!apicall) + { + bool missing = false; + if(cs_strlen(getParam(params, "gacuser")) == 0) + { + tpl_addVar(vars, TPLADD, "USERREQ", "(Required)"); + missing = true; + } + else { tpl_addVar(vars, TPLADD, "GACUSER", getParam(params, "gacuser")); } + if(cs_strlen(getParam(params, "gacpasswd")) == 0) + { + tpl_addVar(vars, TPLADD, "PWDREQ", "(Required)"); + missing = true; + } + else { tpl_addVar(vars, TPLADD, "GACPASSWD", getParam(params, "gacpasswd")); } + if(cs_strlen(getParam(params, "gacname")) == 0) + { + tpl_addVar(vars, TPLADD, "NAMEREQ", "(Required)"); + missing = true; + } + else { tpl_addVar(vars, TPLADD, "GACNAME", getParam(params, "gacname")); } + if(missing) { return tpl_getTpl(vars, "PREAUTOCONF"); } + cs_log("GHttp autoconf requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + } + else + { + tpl_addVar(vars, TPLADD, "APICONFIRMMESSAGE", "autoconf"); + cs_log("GHttp autoconf requested by XMLApi from %s", cs_inet_ntoa(GET_IP())); + } + + if(ghttp_autoconf(vars, params)) + { + tpl_printf(vars, TPLADD, "REFRESHTIME", "%d", 3); + tpl_addVar(vars, TPLADD, "REFRESH", tpl_getTpl(vars, "REFRESH")); + tpl_printf(vars, TPLADD, "SECONDS", "%d", 3); + if(apicall) { return tpl_getTpl(vars, "APICONFIRMATION"); } + else { return tpl_getTpl(vars, "AUTOCONF"); } + } + else { return tpl_getTpl(vars, "PREAUTOCONF"); } // something failed + + } + else + { + if(cs_strlen(getParam(params, "token")) > 0) // parse autoconf token + { + char *token = getParam(params, "token"); + int32_t len = b64decode((uint8_t *)token); + if(len > 0) + { + struct uriparams tokenprms; + tokenprms.paramcount = 0; + parseParams(&tokenprms, token); + if(cs_strlen(getParam(&tokenprms, "u")) > 0) + { + tpl_addVar(vars, TPLADD, "GACUSER", getParam(&tokenprms, "u")); + tpl_addVar(vars, TPLADD, "USERRDONLY", "readonly"); + } + if(cs_strlen(getParam(&tokenprms, "p")) > 0) + { + tpl_addVar(vars, TPLADD, "GACPASSWD", getParam(&tokenprms, "p")); + tpl_addVar(vars, TPLADD, "PWDRDONLY", "readonly"); + } + if(cs_strlen(getParam(&tokenprms, "n")) > 0) + { + tpl_addVar(vars, TPLADD, "GACNAME", getParam(&tokenprms, "n")); + tpl_addVar(vars, TPLADD, "NAMERDONLY", "readonly"); + } + } + } + return tpl_getTpl(vars, "PREAUTOCONF"); + } +} +#endif + +static int8_t check_httpip(IN_ADDR_T addr) +{ + int8_t i = 0; + // check all previously dyndns resolved addresses + for(i = 0; i < MAX_HTTP_DYNDNS; i++) + { + if(IP_ISSET(cfg.http_dynip[i]) && IP_EQUAL(cfg.http_dynip[i], addr)) + { return 1; } + } + return 0; +} + +static int8_t check_httpdyndns(IN_ADDR_T addr) +{ + + // check all previously dyndns resolved addresses + if(check_httpip(addr)) + { return 1; } + + // we are not ok, so resolve all dyndns entries into IP's - maybe outdated IP's + + if(cfg.http_dyndns[0][0]) + { + int8_t i = 0; + for(i = 0; i < MAX_HTTP_DYNDNS; i++) + { + if(cfg.http_dyndns[i][0]) + { + cs_resolve((const char *)cfg.http_dyndns[i], &cfg.http_dynip[i], NULL, NULL); + cs_log_dbg(D_TRACE, "WebIf: httpdyndns [%d] resolved %s to %s ", i, (char *)cfg.http_dyndns[i], cs_inet_ntoa(cfg.http_dynip[i])); + } + } + } + else + { + cs_log_dbg(D_TRACE, "WebIf: No dyndns addresses found"); + return 0; + } + + // again check all dyndns resolved addresses + if(check_httpip(addr)) + { return 1; } + + return 0; +} + +static int8_t check_valid_origin(IN_ADDR_T addr) +{ + + // check whether requesting IP is in allowed IP ranges + if(check_ip(cfg.http_allowed, addr)) + { return 1; } + + // we havn't found the requesting IP in allowed range. So we check for allowed httpdyndns as last chance + if(cfg.http_dyndns[0][0]) + { + int8_t ok; + ok = check_httpdyndns(addr); + return ok; + } + return 0; +} + +static int8_t check_request(char *result, int32_t readen) +{ + if(readen < 50) { return 0; } + result[readen] = '\0'; + int8_t method; + if(strncmp(result, "POST", 4) == 0) { method = 1; } + else { method = 0; } + char *headerEnd = strstr(result, "\r\n\r\n"); + if(headerEnd == NULL) { return 0; } + else if(method == 0) { return 1; } + else + { + char *ptr = strstr(result, "Content-Length: "); + if(ptr != NULL) + { + ptr += 16; + if(ptr < result + readen) + { + uint32_t length = atoi(ptr); + if(cs_strlen(headerEnd + 4) >= length) { return 1; } + } + } + } + return 0; +} + +static int32_t readRequest(FILE * f, IN_ADDR_T in, char **result, int8_t forcePlain) +{ + int32_t n, bufsize = 0, errcount = 0; + char buf2[1024]; + struct pollfd pfd2[1]; +#ifdef WITH_SSL + int8_t is_ssl = 0; + if(ssl_active && !forcePlain) + { is_ssl = 1; } +#endif + + while(1) + { + errno = 0; + if(forcePlain) + { n = read(fileno(f), buf2, sizeof(buf2)); } + else + { n = webif_read(buf2, sizeof(buf2), f); } + if(n <= 0) + { + if((errno == 0 || errno == EINTR)) + { + if(errcount++ < 10) + { + cs_sleepms(5); + continue; + } + else { return -1; } + } +#ifdef WITH_SSL + if(is_ssl) + { + if(errno != ECONNRESET) + { + int32_t errcode = ERR_peek_error(); + char errstring[128]; + ERR_error_string_n(errcode, errstring, sizeof(errstring) - 1); + cs_log_dbg(D_TRACE, "WebIf: read error ret=%d (%d%s%s)", n, SSL_get_error(cur_ssl(), n), errcode ? " " : "", errcode ? errstring : ""); + } + return -1; + } +#else + if(errno != ECONNRESET) + { cs_log_dbg(D_TRACE, "WebIf: read error ret=%d (errno=%d %s)", n, errno, strerror(errno)); } +#endif + return -1; + } + if(!cs_realloc(result, bufsize + n + 1)) + { + send_error500(f); + NULLFREE(*result); + return -1; + } + + memcpy(*result + bufsize, buf2, n); + bufsize += n; + +#ifdef WITH_EMU + if(bufsize > 204800) // max request size 200kb +#else + if(bufsize > 102400) // max request size 100kb +#endif + { + cs_log("error: too much data received from %s", cs_inet_ntoa(in)); + NULLFREE(*result); + *result = NULL; + return -1; + } + +#ifdef WITH_SSL + if(ssl_active && !forcePlain) + { + int32_t len = 0; + len = SSL_pending((SSL *)f); + + if(len > 0) + { continue; } + + pfd2[0].fd = SSL_get_fd((SSL *)f); + + } + else +#endif + pfd2[0].fd = fileno(f); + + pfd2[0].events = (POLLIN | POLLPRI); + + int32_t rc = poll(pfd2, 1, 100); + if(rc > 0 || !check_request(*result, bufsize)) + { continue; } + else + { break; } + } + return bufsize; +} +static int32_t process_request(FILE * f, IN_ADDR_T in) +{ + int32_t ok = 0; + int8_t *keepalive = (int8_t *)pthread_getspecific(getkeepalive); + IN_ADDR_T addr = GET_IP(); + + do + { +#ifdef WITH_SSL + if(!ssl_active && *keepalive) { fflush(f); } +#else + if(*keepalive) { fflush(f); } +#endif + + // at this point we do all checks related origin IP, ranges and dyndns stuff + ok = check_valid_origin(addr); + cs_log_dbg(D_TRACE, "WebIf: Origin checked. Result: access from %s => %s", cs_inet_ntoa(addr), (!ok) ? "forbidden" : "allowed"); + + // based on the failed origin checks we send a 403 to calling browser + if(!ok) + { + send_error(f, 403, "Forbidden", NULL, "Access denied.", 0); + cs_log("unauthorized access from %s - invalid ip or dyndns", cs_inet_ntoa(addr)); + return 0; + } + int32_t authok = 0; + char expectednonce[(MD5_DIGEST_LENGTH * 2) + 1], opaque[(MD5_DIGEST_LENGTH * 2) + 1]; + char authheadertmp[sizeof(AUTHREALM) + sizeof(expectednonce) + sizeof(opaque) + 100]; + + char *method, *path, *protocol, *str1, *saveptr1 = NULL, *authheader = NULL, *extraheader = NULL, *filebuf = NULL; + char *pch, *tmp, *buf, *nameInUrl, subdir[32]; + /* List of possible pages */ + char *pages[] = + { + "/config.html", + "/readers.html", + "/entitlements.html", + "/status.html", + "/userconfig.html", + "/readerconfig.html", + "/services.html", + "/user_edit.html", + "/site.css", + "/services_edit.html", + "/savetemplates.html", + "/shutdown.html", + "/script.html", + "/scanusb.html", + "/files.html", + "/readerstats.html", + "/failban.html", + "/oscam.js", + "/oscamapi.html", + "/image", + "/favicon.ico", + "/graph.svg", + "/oscamapi.xml", + "/cacheex.html", + "/oscamapi.json", + "/emm.html", + "/emm_running.html", + "/robots.txt", + "/ghttp.html", + "/logpoll.html", + "/jquery.js", +#ifdef WEBIF_WIKI + "/wiki.json", + "/wiki_status.json", +#endif + }; + + int32_t pagescnt = sizeof(pages) / sizeof(char *); // Calculate the amount of items in array + int32_t i, bufsize, len, pgidx = -1; + uint32_t etagheader = 0; + struct uriparams params; + params.paramcount = 0; + time_t modifiedheader = 0; + + bufsize = readRequest(f, in, &filebuf, 0); + + if(!filebuf || bufsize < 1) + { + if(!*keepalive) { cs_log_dbg(D_CLIENT, "WebIf: No data received from client %s. Closing connection.", cs_inet_ntoa(addr)); } + return -1; + } + + buf = filebuf; + + if((method = strtok_r(buf, " ", &saveptr1)) != NULL) + { + if((path = strtok_r(NULL, " ", &saveptr1)) != NULL) + { + if((protocol = strtok_r(NULL, "\r", &saveptr1)) == NULL) + { + NULLFREE(filebuf); + return -1; + } + } + else + { + NULLFREE(filebuf); + return -1; + } + } + else + { + NULLFREE(filebuf); + return -1; + } + tmp = protocol + cs_strlen(protocol) + 2; + + pch = path; + /* advance pointer to beginning of query string */ + while(pch[0] != '?' && pch[0] != '\0') { ++pch; } + if(pch[0] == '?') + { + pch[0] = '\0'; + ++pch; + } + + nameInUrl = pch - 1; + while(nameInUrl != path && nameInUrl[0] != '/') { --nameInUrl; } + + /* allow only alphanumeric sub-folders */ + int32_t subdirLen = nameInUrl - path; + subdir[0] = '\0'; + if(subdirLen > 0 && subdirLen < 32) + { + cs_strncpy(subdir, path + 1, subdirLen); + + int32_t invalidSubdir = 0; + for(i = 0; i < subdirLen - 1; i++) + { + if(!((subdir[i] >= '0' && subdir[i] <= '9') + || (subdir[i] >= 'a' && subdir[i] <= 'z') + || (subdir[i] >= 'A' && subdir[i] <= 'Z'))) + { + + invalidSubdir = 1; + subdir[0] = '\0'; + break; + } + } + + if(!invalidSubdir) + { + subdir[subdirLen] = '\0'; +#ifdef WIN32 + subdir[subdirLen - 1] = '\\'; +#else + subdir[subdirLen - 1] = '/'; +#endif + } + } + + /* Map page to our static page definitions */ + for(i = 0; i < pagescnt; i++) + { + if(!strcmp(nameInUrl, pages[i])) { pgidx = i; } + } + + parseParams(¶ms, pch); + + if(!cfg.http_user || !cfg.http_pwd) + { authok = 1; } + + for(str1 = strtok_r(tmp, "\n", &saveptr1); str1; str1 = strtok_r(NULL, "\n", &saveptr1)) + { + len = cs_strlen(str1); + if(str1[len - 1] == '\r') + { + str1[len - 1] = '\0'; + --len; + } + if(len == 0) + { + if(strcmp(method, "POST") == 0) + { + parseParams(¶ms, str1 + 2); + } + break; + } + if(!authok && len > 50 && strncasecmp(str1, "Authorization:", 14) == 0 && strstr(str1, "Digest") != NULL) + { + if(cs_dblevel & D_CLIENT) + { + if(cs_realloc(&authheader, len + 1)) + { cs_strncpy(authheader, str1, len); } + } + authok = check_auth(str1, method, path, addr, expectednonce, opaque); + } + else if(len > 40 && strncasecmp(str1, "If-Modified-Since:", 18) == 0) + { + modifiedheader = parse_modifiedsince(str1); + } + else if(len > 20 && strncasecmp(str1, "If-None-Match:", 14) == 0) + { + for(pch = str1 + 14; pch[0] != '"' && pch[0] != '\0'; ++pch) { ; } + if(cs_strlen(pch) > 5) { etagheader = (uint32_t)strtoul(++pch, NULL, 10); } + } + else if(len > 12 && strncasecmp(str1, "Connection: Keep-Alive", 22) == 0 && strcmp(method, "POST")) + { + *keepalive = 1; + } + } + + if(cfg.http_user && cfg.http_pwd) + { + if (!authok || cs_strlen(opaque) != MD5_DIGEST_LENGTH * 2) + { + calculate_opaque(addr, opaque); + } + + if (authok != 2) + { + if(!authok) + { + if(authheader) + { + cs_log_dbg(D_CLIENT, "WebIf: Received wrong auth header from %s:", cs_inet_ntoa(addr)); + cs_log_dbg(D_CLIENT, "%s", authheader); + } + else + { cs_log_dbg(D_CLIENT, "WebIf: Received no auth header from %s.", cs_inet_ntoa(addr)); } + } + calculate_nonce(NULL, expectednonce, opaque); + } + + if (authok != 1) + { + snprintf(authheadertmp, sizeof(authheadertmp), "WWW-Authenticate: Digest algorithm=\"MD5\", realm=\"%s\", qop=\"auth\", opaque=\"%s\", nonce=\"%s\"", AUTHREALM, opaque, expectednonce); + if (authok == 2) + { + if (!cs_strncat(authheadertmp, ", stale=true", sizeof(authheadertmp))) { + cs_log("WARNING, bug here!"); + } + } + } + else + { + snprintf(authheadertmp, sizeof(authheadertmp), "Authentication-Info: nextnonce=\"%s\"", expectednonce); + } + + extraheader = authheadertmp; + + if (authok != 1) + { + char *msg = "Access denied.\n"; + send_headers(f, 401, "Unauthorized", extraheader, "text/html", 0, cs_strlen(msg), msg, 0); + webif_write(msg, f); + NULLFREE(authheader); + NULLFREE(filebuf); + if (*keepalive) { + continue; + } else { + return 0; + } + } + } + else + { + NULLFREE(authheader); + } + + /*build page*/ + if(pgidx == 8) + { + send_file(f, "CSS", subdir, modifiedheader, etagheader, extraheader); + } + else if(pgidx == 17) + { + send_file(f, "JS", subdir, modifiedheader, etagheader, extraheader); + } + else if(pgidx == 30) + { + send_file(f, "JQ", subdir, modifiedheader, etagheader, extraheader); + } + else + { + time_t t; + struct templatevars *vars = tpl_create(); + if(vars == NULL) + { + send_error500(f); + NULLFREE(filebuf); + return 0; + } + + tpl_addVar(vars, TPLADD, "SUBDIR", subdir); + + struct tm lt, st; + time(&t); + + localtime_r(&t, <); + + tpl_addVar(vars, TPLADD, "SCM_URL", SCM_URL); + tpl_addVar(vars, TPLADD, "WIKI_URL", WIKI_URL); + tpl_addVar(vars, TPLADD, "BOARD_URL", BOARD_URL); +#ifdef WEBIF_WIKI + tpl_addVar(vars, TPLADD, "WIKIINTERNALVAR", "\t\tvar wikiInternal = true;"); +#else + tpl_addVar(vars, TPLADD, "WIKIINTERNALVAR", ""); +#endif + tpl_addVar(vars, TPLADD, "CS_VERSION", CS_VERSION); + tpl_addVar(vars, TPLADD, "CS_GIT_COMMIT", CS_GIT_COMMIT); + tpl_addVar(vars, TPLADD, "CS_TARGET", CS_TARGET); + tpl_addVar(vars, TPLADD, "HTTPOSCAMLABEL", xml_encode(vars,cfg.http_oscam_label)); + if (!boxtype_is("generic")) + { + if (!boxname_is("generic")) + tpl_printf(vars, TPLADD, "DETECTED_BOXTYPE", "%s (%s)", boxtype_get(), boxname_get()); + else + tpl_addVar(vars, TPLADD, "DETECTED_BOXTYPE", boxtype_get()); + } + + if(cfg.http_locale){ + float decimal_point = 0.0; + setlocale(LC_ALL, cfg.http_locale); + tpl_printf(vars, TPLADD, "TMP_DECPOINT", "%.1f", decimal_point); + tpl_addVar(vars, TPLADD, "LOCALE_DECPOINT", strstr(tpl_getVar(vars, "TMP_DECPOINT"), ",") ? ",": "."); + } + + tpl_addVar(vars, TPLADD, "HTTP_CHARSET", "UTF-8"); + if(cfg.http_picon_size > 0) + { + tpl_printf(vars, TPLADD, "HTTPPICONSIZEINS", "img.statususericon, img.protoicon, img.usericon, img.readericon {height:%dpx !important;max-height:%dpx !important;}", cfg.http_picon_size, cfg.http_picon_size); + } + if(cfg.poll_refresh > 0) + { + tpl_printf(vars, TPLADD, "POLLREFRESHTIME", "%d", cfg.poll_refresh); + } + if ( cfg.http_refresh > 0 && + ((( pgidx == 1 || pgidx == 4 ) && !cfg.poll_refresh ) || + ( pgidx == 3 && ( cfg.http_status_log || !cfg.poll_refresh )) || + pgidx == 15 || pgidx == 23 || pgidx == -1 )) // wenn polling bei cachex.html eingeführt wird muss die 23 => 2 zeilen höher + { + tpl_printf(vars, TPLADD, "REFRESHTIME", "%d", cfg.http_refresh); + tpl_addVar(vars, TPLADD, "WITHQUERY", pgidx == 15 ? "1" : "0"); + tpl_addVar(vars, TPLADD, "REFRESH", tpl_getTpl(vars, "REFRESH")); + } +#ifdef WEBIF_JQUERY + tpl_printf(vars, TPLADD, "SRCJQUERY", "jquery.js?v=%s", CS_VERSION); +#else + tpl_addVar(vars, TPLADD, "SRCJQUERY", cfg.http_extern_jquery); +#endif + + if(picon_exists("LOGO")||cs_strlen(tpl_getTpl(vars, "IC_LOGO"))>3) + { + tpl_addVar(vars, TPLADD, "LOGO_INS", tpl_getTpl(vars, "LOGOBITIMG")); + } + else + { + tpl_addVar(vars, TPLADD, "LOGO_INS", tpl_getTpl(vars, "LOGOBITSVG")); + } + tpl_addVar(vars, TPLADD, "LOGO", tpl_getTpl(vars, "LOGOBIT")); + tpl_printf(vars, TPLADD, "CURDATE", "%02d.%02d.%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100); + tpl_printf(vars, TPLADD, "CURTIME", "%02d:%02d:%02d", lt.tm_hour, lt.tm_min, lt.tm_sec); + localtime_r(&first_client->login, &st); + tpl_printf(vars, TPLADD, "STARTDATE", "%02d.%02d.%02d", st.tm_mday, st.tm_mon + 1, st.tm_year % 100); + tpl_printf(vars, TPLADD, "STARTTIME", "%02d:%02d:%02d", st.tm_hour, st.tm_min, st.tm_sec); + tpl_printf(vars, TPLADD, "PROCESSID", "%d", getppid()); + tpl_addVar(vars, TPLADD, "RUNAS", urlencode(vars, username(first_client))); + + time_t now = time((time_t *)0); + // XMLAPI + if(pgidx == 18 || pgidx == 22 || pgidx == 24) + { + char tbuffer [30]; + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", &st); + tpl_addVar(vars, TPLADD, "APISTARTTIME", tbuffer); + tpl_printf(vars, TPLADD, "APIRUNTIME", "%" PRId64, (int64_t)now - first_client->login); + tpl_printf(vars, TPLADD, "APIREADONLY", "%d", cfg.http_readonly); + if(strcmp(getParam(¶ms, "callback"), "")) + { + tpl_printf(vars, TPLADD, "CALLBACK", "%s%s", getParam(¶ms, "callback"), "("); + tpl_addVar(vars, TPLADD, "ENDBRACKET", ")"); + } + + } + + if (config_enabled(WITH_LB)) + tpl_addVar(vars, TPLADD, "LBISDEFINED", "1"); + + // language code in helplink + tpl_addVar(vars, TPLADD, "LANGUAGE", cfg.http_help_lang); + tpl_addVar(vars, TPLADD, "RUNTIME", sec2timeformat(vars, (now - first_client->login))); + time_t uptime = oscam_get_uptime(); + if(uptime > 0){ + tpl_printf(vars, TPLADD, "UPTIMETXT", "%s Up Time: ", strcmp(tpl_getVar(vars, "DETECTED_BOXTYPE"),"")==0 ? "System" : tpl_getVar(vars, "DETECTED_BOXTYPE")); + tpl_addVar(vars, TPLADD, "UPTIME", sec2timeformat(vars, uptime)); + } + tpl_addVar(vars, TPLADD, "CURIP", cs_inet_ntoa(addr)); + if(cfg.http_readonly) + { tpl_addVar(vars, TPLAPPEND, "BTNDISABLED", "DISABLED"); } + + i = ll_count(cfg.v_list); + if(i > 0) { tpl_printf(vars, TPLADD, "FAILBANNOTIFIER", "%d", i); } + tpl_printf(vars, TPLADD, "FAILBANNOTIFIERPOLL", "%d", i); + + char *result = NULL; + + // WebIf allows modifying many things. Thus, all pages except images/css/static are expected to be non-threadsafe! + if(pgidx != 19 && pgidx != 20 && pgidx != 21 && pgidx != 27) { cs_writelock(__func__, &http_lock); } + switch(pgidx) + { + case 0: + tpl_addVar(vars, TPLADD, "CONFIG_CONTENT", send_oscam_config(vars, ¶ms)); + result = tpl_getTpl(vars, "CONFIGCONTENT"); + break; + case 1: + result = send_oscam_reader(vars, ¶ms, 0); + break; + case 2: + result = send_oscam_entitlement(vars, ¶ms, 0); + break; + case 3: + result = send_oscam_status(vars, ¶ms, 0); + break; + case 4: + result = send_oscam_user_config(vars, ¶ms, 0); + break; + case 5: + result = send_oscam_reader_config(vars, ¶ms); + break; + case 6: + result = send_oscam_services(vars, ¶ms); + break; + case 7: + result = send_oscam_user_config_edit(vars, ¶ms, 0); + break; + //case 8: css file + case 9: + result = send_oscam_services_edit(vars, ¶ms); + break; + case 10: + result = send_oscam_savetpls(vars); + break; + case 11: + result = send_oscam_shutdown(vars, f, ¶ms, 0, keepalive, extraheader); + break; + case 12: + result = send_oscam_script(vars, ¶ms); + break; + case 13: + result = send_oscam_scanusb(vars); + break; + case 14: + result = send_oscam_files(vars, ¶ms, 0); + break; + case 15: + result = send_oscam_reader_stats(vars, ¶ms, 0); + break; + case 16: + result = send_oscam_failban(vars, ¶ms, 0); + break; + //case 17: js file + case 18: + result = send_oscam_api(vars, f, ¶ms, keepalive, 1, extraheader); + break; //oscamapi.html + case 19: + result = send_oscam_image(vars, f, ¶ms, NULL, modifiedheader, etagheader, extraheader); + break; + case 20: + result = send_oscam_image(vars, f, ¶ms, "ICMAI", modifiedheader, etagheader, extraheader); + break; + case 21: + result = send_oscam_graph(vars); + break; + case 22: + result = send_oscam_api(vars, f, ¶ms, keepalive, 1, extraheader); + break; //oscamapi.xml +#ifdef CS_CACHEEX + case 23: + result = send_oscam_cacheex(vars, ¶ms, 0); + break; +#endif + case 24: + result = send_oscam_api(vars, f, ¶ms, keepalive, 2, extraheader); + break; //oscamapi.json + case 25: + result = send_oscam_EMM(vars, ¶ms); + break; //emm.html + case 26: + result = send_oscam_EMM_running(vars, ¶ms); + break; //emm_running.html + case 27: + result = send_oscam_robots_txt(f); + break; //robots.txt +#ifdef MODULE_GHTTP + case 28: + result = send_oscam_ghttp(vars, ¶ms, 0); + break; +#endif +#ifdef WEBIF_LIVELOG + case 29: + result = send_oscam_logpoll(vars, ¶ms); + break; + //case 30: jquery.js +#endif +#ifdef WEBIF_WIKI + case 31: + result = send_oscam_wiki(vars, ¶ms); + break; + case 32: + result = send_oscam_wiki_status(vars, ¶ms); + break; +#endif + default: + result = send_oscam_status(vars, ¶ms, 0); + break; + } + if(pgidx != 19 && pgidx != 20 && pgidx != 21 && pgidx != 27) { cs_writeunlock(__func__, &http_lock); } + + if(result == NULL || !strcmp(result, "0") || cs_strlen(result) == 0) { send_error500(f); } + else if(strcmp(result, "1")) + { + //it doesn't make sense to check for modified etagheader here as standard template has timestamp in output and so site changes on every request + if(pgidx == 18) + { send_headers(f, 200, "OK", extraheader, "text/xml", 0, cs_strlen(result), NULL, 0); } + else if(pgidx == 21) + { send_headers(f, 200, "OK", extraheader, "image/svg+xml", 0, cs_strlen(result), NULL, 0); } + else if(pgidx == 24) + { send_headers(f, 200, "OK", extraheader, "text/javascript", 0, cs_strlen(result), NULL, 0); } +#ifdef WEBIF_WIKI + else if(pgidx == 31) + { send_headers(f, 200, "OK", extraheader, "application/json", 0, cs_strlen(result), NULL, 0); } +#endif + else + { send_headers(f, 200, "OK", extraheader, "text/html", 0, cs_strlen(result), NULL, 0); } + webif_write(result, f); + } + tpl_clear(vars); + } + NULLFREE(filebuf); + } + while(*keepalive == 1 && !exit_oscam); + return 0; +} + +static void *serve_process(void *conn) +{ + struct s_connection *myconn = (struct s_connection *)conn; + int32_t s = myconn->socket; + struct s_client *cl = myconn->cl; + IN_ADDR_T in; + IP_ASSIGN(in, myconn->remote); + + set_thread_name(__func__); + +#ifdef WITH_SSL + SSL *ssl = myconn->ssl; + SAFE_SETSPECIFIC(getssl, ssl); +#endif + NULLFREE(myconn); + + SAFE_SETSPECIFIC(getip, &in); + SAFE_SETSPECIFIC(getclient, cl); + + int8_t keepalive = 0; + SAFE_SETSPECIFIC(getkeepalive, &keepalive); + +#ifdef WITH_SSL + if(ssl_active) + { + if(SSL_set_fd(ssl, s)) + { + int32_t ok = (SSL_accept(ssl) != -1); + if(!ok) + { + int8_t tries = 100; + while(!ok && tries--) + { + int32_t err = SSL_get_error(ssl, -1); + if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) + { break; } + else + { + struct pollfd pfd; + pfd.fd = s; + pfd.events = POLLIN | POLLPRI; + int32_t rc = poll(&pfd, 1, -1); + if(rc < 0) + { + if(errno == EINTR || errno == EAGAIN) { continue; } + break; + } + if(rc == 1) + { ok = (SSL_accept(ssl) != -1); } + } + } + } + if(ok) + { + process_request((FILE *)ssl, in); + } + else + { + FILE *f; + f = fdopen(s, "r+"); + if(f != NULL) + { + char *ptr, *filebuf = NULL, *host = NULL; + int32_t bufsize = readRequest(f, in, &filebuf, 1); + + if(filebuf) + { + filebuf[bufsize] = '\0'; + host = strstr(filebuf, "Host: "); + if(host) + { + host += 6; + ptr = strchr(host, '\r'); + if(ptr) { ptr[0] = '\0'; } + } + } + if(host) + { + char extra[cs_strlen(host) + 20]; + snprintf(extra, sizeof(extra), "Location: https://%s", host); + send_error(f, 301, "Moved Permanently", extra, "This web server is running in SSL mode.", 1); + } + else + { send_error(f, 200, "Bad Request", NULL, "This web server is running in SSL mode.", 1); } + fflush(f); + fclose(f); + NULLFREE(filebuf); + } + else + { + cs_log_dbg(D_TRACE, "WebIf: fdopen(%d) failed. (errno=%d %s)", s, errno, strerror(errno)); + } + } + } + else { cs_log("WebIf: Error calling SSL_set_fd()."); } + SSL_shutdown(ssl); + close(s); + SSL_free(ssl); + } + else +#endif + { + FILE *f; + f = fdopen(s, "r+"); + if(f != NULL) + { + process_request(f, in); + fflush(f); + fclose(f); + } + else + { + cs_log_dbg(D_TRACE, "WebIf: fdopen(%d) failed. (errno=%d %s)", s, errno, strerror(errno)); + } + shutdown(s, SHUT_WR); + close(s); + } + + return NULL; +} + +/* Creates a random string with specified length. Note that dst must be one larger than size to hold the trailing \0*/ +static void create_rand_str(char *dst, int32_t size) +{ + int32_t i; + for(i = 0; i < size; ++i) + { + dst[i] = (rand() % 94) + 32; + } + dst[i] = '\0'; +} + +static void *http_server(void *UNUSED(d)) +{ + struct s_client *cl = create_client(first_client->ip); + if(cl == NULL) { return NULL; } + SAFE_SETSPECIFIC(getclient, cl); + cl->typ = 'h'; + int32_t s, reuse = 1; + struct s_connection *conn; + + set_thread_name(__func__); + + /* Create random string for nonce value generation */ + create_rand_str(noncekey, 32); + + /* Prepare base64 decoding array */ + b64prepare(); + webif_tpls_prepare(); +#ifdef WEBIF_WIKI + webif_wiki_prepare(); +#endif + + tpl_checkDiskRevisions(); + + cs_lock_create(__func__, &http_lock, "http_lock", 10000); + init_noncelocks(); + + memset(&p_stat_cur, 0x0, sizeof(p_stat_cur)); + + if(pthread_key_create(&getip, NULL)) + { + cs_log("Could not create getip"); + return NULL; + } + if(pthread_key_create(&getkeepalive, NULL)) + { + cs_log("Could not create getkeepalive"); + return NULL; + } + + struct SOCKADDR sin; + socklen_t len = 0; + memset(&sin, 0, sizeof(sin)); + + bool do_ipv6 = config_enabled(IPV6SUPPORT); +#ifdef IPV6SUPPORT + if(do_ipv6) + { + len = sizeof(struct sockaddr_in6); + if((sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + cs_log("HTTP Server: ERROR: Creating IPv6 socket failed! (errno=%d %s)", errno, strerror(errno)); + cs_log("HTTP Server: Falling back to IPv4."); + do_ipv6 = false; + } + else if (IP_ISSET(cfg.http_srvip) && (IN6_IS_ADDR_V4MAPPED(&cfg.http_srvip) || IN6_IS_ADDR_V4COMPAT(&cfg.http_srvip))) // ipv4 set as http_srvip + { + do_ipv6 = false; + } + else + { + struct sockaddr_in6 *ia = (struct sockaddr_in6 *)&sin; + + if (IP_ISSET(cfg.http_srvip)) + { + IP_ASSIGN(SIN_GET_ADDR(sin), cfg.http_srvip); + } + else if (IP_ISSET(cfg.srvip)) + { + IP_ASSIGN(SIN_GET_ADDR(sin), cfg.srvip); + } + else + { + ia->sin6_addr = in6addr_any; + } + + ia->sin6_family = AF_INET6; + ia->sin6_port = htons(cfg.http_port); + } + } +#endif + if(!do_ipv6) + { + len = sizeof(struct sockaddr_in); + if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + cs_log("HTTP Server: ERROR: Creating socket failed! (errno=%d %s)", errno, strerror(errno)); + return NULL; + } + SIN_GET_FAMILY(sin) = AF_INET; + if(IP_ISSET(cfg.http_srvip)) + { IP_ASSIGN(SIN_GET_ADDR(sin), cfg.http_srvip); } + else if(IP_ISSET(cfg.srvip)) + { IP_ASSIGN(SIN_GET_ADDR(sin), cfg.srvip); } + // The default is INADDR_ANY (0) + SIN_GET_PORT(sin) = htons(cfg.http_port); + } + + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) + { + cs_log("HTTP Server: Setting SO_REUSEADDR via setsockopt failed! (errno=%d %s)", errno, strerror(errno)); + } + + if(bind(sock, (struct sockaddr *)&sin, len) < 0) + { + cs_log("HTTP Server couldn't bind on port %d (errno=%d %s). Not starting HTTP!", cfg.http_port, errno, strerror(errno)); + close(sock); + return NULL; + } + + if(listen(sock, SOMAXCONN) < 0) + { + cs_log("HTTP Server: Call to listen() failed! (errno=%d %s)", errno, strerror(errno)); + close(sock); + return NULL; + } + +#ifdef WITH_SSL + if(pthread_key_create(&getssl, NULL)) + { + cs_log("Could not create getssl"); + } + + SSL_CTX *ctx = NULL; + if(cfg.http_use_ssl) + { + ctx = SSL_Webif_Init(); + if(ctx == NULL) + { cs_log("SSL could not be initialized. Starting WebIf in plain mode."); } + else { ssl_active = 1; } + } + else { ssl_active = 0; } + cs_log("HTTP%s Server running. ip=%s port=%d (%s)", ssl_active ? "S" : "", cs_inet_ntoa(SIN_GET_ADDR(sin)), cfg.http_port, ssl_active ? OPENSSL_VERSION_TEXT : "no SSL"); +#else + cs_log("HTTP Server running. ip=%s port=%d", cs_inet_ntoa(SIN_GET_ADDR(sin)), cfg.http_port); +#endif + + struct SOCKADDR remote; + memset(&remote, 0, sizeof(remote)); + + while(!exit_oscam) + { + if((s = accept(sock, (struct sockaddr *) &remote, &len)) < 0) + { + if(exit_oscam) + { break; } + if(errno != EAGAIN && errno != EINTR) + { + cs_log("HTTP Server: Error calling accept() (errno=%d %s)", errno, strerror(errno)); + cs_sleepms(100); + } + else { cs_sleepms(5); } + continue; + } + else + { + getpeername(s, (struct sockaddr *) &remote, &len); + if(!cs_malloc(&conn, sizeof(struct s_connection))) + { + close(s); + continue; + } + setTCPTimeouts(s); + cur_client()->last = time((time_t *)0); //reset last busy time + conn->cl = cur_client(); +#ifdef IPV6SUPPORT + if(do_ipv6) + { + struct sockaddr_in6 *ra = (struct sockaddr_in6 *)&remote; + memcpy(&conn->remote, &ra->sin6_addr, sizeof(struct in6_addr)); + } + else + { + struct sockaddr_in *fba = (struct sockaddr_in *)&remote; + struct in6_addr taddr; + memset(&taddr, 0, sizeof(taddr)); + taddr.s6_addr32[3] = fba->sin_addr.s_addr; + memcpy(&conn->remote, &taddr, sizeof(struct in6_addr)); + } +#else + memcpy(&conn->remote, &remote.sin_addr, sizeof(struct in_addr)); +#endif + conn->socket = s; +#ifdef WITH_SSL + conn->ssl = NULL; + if(ssl_active) + { + conn->ssl = SSL_new(ctx); + if(conn->ssl == NULL) + { + close(s); + cs_log("WebIf: Error calling SSL_new()."); + continue; + } + } +#endif + + int32_t ret = start_thread("webif workthread", serve_process, (void *)conn, NULL, 1, 1); + if(ret) + { + NULLFREE(conn); + } + } + } + // Wait a bit so that we don't close ressources while http threads are active + cs_sleepms(300); +#ifdef WITH_SSL + SSL_CTX_free(ctx); + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + OPENSSL_free(lock_cs); + lock_cs = NULL; +#endif + cs_log("HTTP Server stopped"); + free_client(cl); + close(sock); + return NULL; +} + +void webif_client_reset_lastresponsetime(struct s_client * cl) +{ + int32_t i; + for(i = 0; i < CS_ECM_RINGBUFFER_MAX; i++) + { + cl->cwlastresptimes[i].duration = 0; + cl->cwlastresptimes[i].timestamp = time((time_t *)0); + cl->cwlastresptimes[i].rc = 0; + } + cl->cwlastresptimes_last = 0; +} + +void webif_client_add_lastresponsetime(struct s_client * cl, int32_t ltime, time_t timestamp, int32_t rc) +{ + int32_t last = cl->cwlastresptimes_last = (cl->cwlastresptimes_last + 1) & (CS_ECM_RINGBUFFER_MAX - 1); + cl->cwlastresptimes[last].duration = ltime > 9999 ? 9999 : ltime; + cl->cwlastresptimes[last].timestamp = timestamp; + cl->cwlastresptimes[last].rc = rc; +} + +void webif_client_init_lastreader(struct s_client * client, ECM_REQUEST * er, struct s_reader * er_reader, const char *stxt[]) +{ + if(er_reader) + { + if(er->rc == E_FOUND) + { cs_strncpy(client->lastreader, er_reader->label, sizeof(client->lastreader)); } + else if(er->rc == E_CACHEEX) + #ifdef CS_CACHEEX_AIO + { + if (cfg.cacheex_srcname_webif) + { + cs_strncpy(client->lastreader, username(er_reader->client), sizeof(client->lastreader)); + } + else + { + cs_strncpy(client->lastreader, "cache3", sizeof(client->lastreader)); + } + } + #else + { cs_strncpy(client->lastreader, "cache3", sizeof(client->lastreader)); } + #endif + else if(er->rc < E_NOTFOUND) + { snprintf(client->lastreader, sizeof(client->lastreader) - 1, "%.54s (cache)", er_reader->label); } + else + { cs_strncpy(client->lastreader, stxt[er->rc], sizeof(client->lastreader)); } + } + else + { + cs_strncpy(client->lastreader, stxt[er->rc], sizeof(client->lastreader)); + } +} + +void webif_init(void) +{ + char buf[8], fname[256]; + snprintf(buf, 8, "%'d", 7); + if(strcmp(buf, "7")) + { + useLocal = 0; + } + + if(cfg.http_port == 0) + { + cs_log("http disabled"); + return; + } + + get_config_filename(fname, sizeof(fname), "oscam.srvid2"); + use_srvid2 = file_exists(fname); + + if(start_thread("http", http_server, NULL, &httpthread, 0, 1) == 0) + { + httpthread_running = 1; + } +} + +void webif_close(void) +{ + if(!sock) + { return; } + + shutdown(sock, 2); + close(sock); + + if(httpthread_running) + { SAFE_THREAD_JOIN(httpthread, NULL); } +} + +#endif diff --git a/module-webif.h b/module-webif.h new file mode 100644 index 0000000..8e1bb90 --- /dev/null +++ b/module-webif.h @@ -0,0 +1,18 @@ +#ifndef MODULE_WEBIF_H_ +#define MODULE_WEBIF_H_ + +#ifdef WEBIF +void webif_init(void); +void webif_close(void); +void webif_client_reset_lastresponsetime(struct s_client *cl); +void webif_client_add_lastresponsetime(struct s_client *cl, int32_t ltime, time_t timestamp, int32_t rc); +void webif_client_init_lastreader(struct s_client *cl, ECM_REQUEST *er, struct s_reader *er_reader, const char *stxt[]); +#else +static inline void webif_init(void) { } +static inline void webif_close(void) { } +static inline void webif_client_reset_lastresponsetime(struct s_client *UNUSED(cl)) { } +static inline void webif_client_add_lastresponsetime(struct s_client *UNUSED(cl), int32_t UNUSED(ltime), time_t UNUSED(timestamp), int32_t UNUSED(rc)) { } +static inline void webif_client_init_lastreader(struct s_client *UNUSED(cl), ECM_REQUEST *UNUSED(er), struct s_reader *UNUSED(er_reader), const char *UNUSED(stxt[])) { } +#endif + +#endif diff --git a/modules.h b/modules.h new file mode 100644 index 0000000..47173fa --- /dev/null +++ b/modules.h @@ -0,0 +1,21 @@ +#ifndef MODULES_H_ +#define MODULES_H_ + +void module_monitor(struct s_module *); +void module_camd35(struct s_module *); +void module_camd35_tcp(struct s_module *); +void module_camd33(struct s_module *); +void module_newcamd(struct s_module *); +void module_radegast(struct s_module *); +void module_serial(struct s_module *); +void module_cccam(struct s_module *); +void module_pandora(struct s_module *); +void module_scam(struct s_module *); +void module_ghttp(struct s_module *); +void module_gbox(struct s_module *); +void module_constcw(struct s_module *); +void module_csp(struct s_module *); +void module_dvbapi(struct s_module *); +void module_streamrelay(struct s_module *); + +#endif diff --git a/oscam-aes.c b/oscam-aes.c new file mode 100644 index 0000000..2ad70e0 --- /dev/null +++ b/oscam-aes.c @@ -0,0 +1,236 @@ +#define MODULE_LOG_PREFIX "aes" + +#include "globals.h" +#include "oscam-aes.h" +#include "oscam-garbage.h" +#include "oscam-string.h" + +void aes_set_key(struct aes_keys *aes, char *key) +{ + AES_set_decrypt_key((const uint8_t *)key, 128, &aes->aeskey_decrypt); + AES_set_encrypt_key((const uint8_t *)key, 128, &aes->aeskey_encrypt); +} + +bool aes_set_key_alloc(struct aes_keys **aes, char *key) +{ + if (!cs_malloc(aes, sizeof(struct aes_keys))) + { + return false; + } + aes_set_key(*aes, key); + return true; +} + +void aes_decrypt(struct aes_keys *aes, uint8_t *buf, int32_t n) +{ + int32_t i; + for(i = 0; i < n; i += 16) + { + AES_decrypt(buf + i, buf + i, &aes->aeskey_decrypt); + } +} + +void aes_encrypt_idx(struct aes_keys *aes, uint8_t *buf, int32_t n) +{ + int32_t i; + for(i = 0; i < n; i += 16) + { + AES_encrypt(buf + i, buf + i, &aes->aeskey_encrypt); + } +} + +void aes_cbc_encrypt(struct aes_keys *aes, uint8_t *buf, int32_t n, uint8_t *iv) +{ + AES_cbc_encrypt(buf, buf, n, &aes->aeskey_encrypt, iv, AES_ENCRYPT); +} + +void aes_cbc_decrypt(struct aes_keys *aes, uint8_t *buf, int32_t n, uint8_t *iv) +{ + AES_cbc_encrypt(buf, buf, n, &aes->aeskey_decrypt, iv, AES_DECRYPT); +} + +#ifdef READER_VIACCESS +/* Creates an AES_ENTRY and adds it to the given linked list. */ +void add_aes_entry(AES_ENTRY **list, uint16_t caid, uint32_t ident, int32_t keyid, uint8_t *aesKey) +{ + AES_ENTRY *new_entry, *next, *current; + + // create the AES key entry for the linked list + if(!cs_malloc(&new_entry, sizeof(AES_ENTRY))) + { return; } + + memcpy(new_entry->plainkey, aesKey, 16); + new_entry->caid = caid; + new_entry->ident = ident; + new_entry->keyid = keyid; + if(memcmp(aesKey, "\xFF\xFF", 2)) + { + AES_set_decrypt_key((const uint8_t *)aesKey, 128, &(new_entry->key)); + // cs_log("adding key : %s",cs_hexdump(1,aesKey,16, tmp, sizeof(tmp))); + } + else + { + memset(&new_entry->key, 0, sizeof(AES_KEY)); + // cs_log("adding fake key"); + } + new_entry->next = NULL; + + //if list is empty, new_entry is the new head + if(!*list) + { + *list = new_entry; + return; + } + + //append it to the list + current = *list; + next = current->next; + while(next) + { + current = next; + next = current->next; + } + current->next = new_entry; +} + +/* Parses a single AES_KEYS entry and assigns it to the given list. + The expected format for value is caid1@ident1:key0,key1 */ +void parse_aes_entry(AES_ENTRY **list, char *label, char *value) +{ + uint16_t caid, dummy; + uint32_t ident; + int32_t len; + char *tmp; + int32_t nb_keys, key_id; + uint8_t aes_key[16]; + char *save = NULL; + + tmp = strtok_r(value, "@", &save); + + //if we got error caid + len = cs_strlen(tmp); + if(len == 0 || len > 4) { return; } + + //if there is not value after @ + len = cs_strlen(save); + if(len == 0) { return; } + + caid = a2i(tmp, 2); + tmp = strtok_r(NULL, ":", &save); + + //if we got error ident + len = cs_strlen(tmp); + if(len == 0 || len > 6) { return; } + + ident = a2i(tmp, 3); + + // now we need to split the key and add the entry to the reader. + nb_keys = 0; + key_id = 0; + while((tmp = strtok_r(NULL, ",", &save))) + { + dummy = 0; + len = cs_strlen(tmp); + if(len != 32) + { + dummy = a2i(tmp, 1); + // FF means the card will do the AES decrypt + // 00 means we don't have the aes. + if((dummy != 0xFF && dummy != 0x00) || len > 2) + { + key_id++; + cs_log("AES key length error .. not adding"); + continue; + } + if(dummy == 0x00) + { + key_id++; + continue; + } + } + nb_keys++; + if(dummy) + { memset(aes_key, 0xFF, 16); } + else + { key_atob_l(tmp, aes_key, 32); } + // now add the key to the reader... TBD + add_aes_entry(list, caid, ident, key_id, aes_key); + key_id++; + } + + cs_log("%d AES key(s) added on reader %s for %04x@%06x", nb_keys, label, caid, ident); +} + +/* Clears all entries from an AES list*/ +void aes_clear_entries(AES_ENTRY **list) +{ + AES_ENTRY *current, *next; + current = NULL; + next = *list; + while(next) + { + current = next; + next = current->next; + add_garbage(current); + } + *list = NULL; +} + +/* Parses multiple AES_KEYS entrys in a reader section and assigns them to the reader. + The expected format for value is caid1@ident1:key0,key1;caid2@ident2:key0,key1 */ +void parse_aes_keys(struct s_reader *rdr, char *value) +{ + char *entry; + char *save = NULL; + AES_ENTRY *newlist = NULL, *savelist = rdr->aes_list; + + for(entry = strtok_r(value, ";", &save); entry; entry = strtok_r(NULL, ";", &save)) + { + parse_aes_entry(&newlist, rdr->label, entry); + } + rdr->aes_list = newlist; + aes_clear_entries(&savelist); +} + +static AES_ENTRY *aes_list_find(AES_ENTRY *list, uint16_t caid, uint32_t provid, int32_t keyid) +{ + AES_ENTRY *current = list; + while(current) + { + if(current->caid == caid && current->ident == provid && current->keyid == keyid) + { break; } + current = current->next; + } + if(!current) + { + cs_log("AES Decrypt key %d not found for %04X@%06X (aka V %06X E%X ...) ", keyid, caid, provid, provid, keyid); + return NULL; + } + return current; +} + + +int32_t aes_decrypt_from_list(AES_ENTRY *list, uint16_t caid, uint32_t provid, int32_t keyid, uint8_t *buf, int32_t n) +{ + AES_ENTRY *current = aes_list_find(list, caid, provid, keyid); + if(!current) + { return 0; } + AES_KEY dummy; + int32_t i; + // hack for card that do the AES decrypt themsleves + memset(&dummy, 0, sizeof(AES_KEY)); + if(!memcmp(¤t->key, &dummy, sizeof(AES_KEY))) + { + return 1; + } + // decode the key + for(i = 0; i < n; i += 16) + { AES_decrypt(buf + i, buf + i, &(current->key)); } + return 1; // all ok, key decoded. +} + +int32_t aes_present(AES_ENTRY *list, uint16_t caid, uint32_t provid, int32_t keyid) +{ + return aes_list_find(list, caid, provid, keyid) != NULL; +} +#endif diff --git a/oscam-aes.h b/oscam-aes.h new file mode 100644 index 0000000..5c6c99c --- /dev/null +++ b/oscam-aes.h @@ -0,0 +1,18 @@ +#ifndef OSCAM_AES_H_ +#define OSCAM_AES_H_ + +void aes_set_key(struct aes_keys *aes, char *key); +bool aes_set_key_alloc(struct aes_keys **aes, char *key); +void aes_decrypt(struct aes_keys *aes, uint8_t *buf, int32_t n); +void aes_encrypt_idx(struct aes_keys *aes, uint8_t *buf, int32_t n); +void aes_cbc_encrypt(struct aes_keys *aes, uint8_t *buf, int32_t n, uint8_t *iv); +void aes_cbc_decrypt(struct aes_keys *aes, uint8_t *buf, int32_t n, uint8_t *iv); + +void add_aes_entry(AES_ENTRY **list, uint16_t caid, uint32_t ident, int32_t keyid, uint8_t *aesKey); +void parse_aes_entry(AES_ENTRY **list, char *label, char *value); +void aes_clear_entries(AES_ENTRY **list); +void parse_aes_keys(struct s_reader *rdr, char *value); +int32_t aes_decrypt_from_list(AES_ENTRY *list, uint16_t caid, uint32_t provid, int32_t keyid, uint8_t *buf, int32_t n); +int32_t aes_present(AES_ENTRY *list, uint16_t caid, uint32_t provid, int32_t keyid); + +#endif diff --git a/oscam-array.c b/oscam-array.c new file mode 100644 index 0000000..33ae15c --- /dev/null +++ b/oscam-array.c @@ -0,0 +1,74 @@ +#define MODULE_LOG_PREFIX "array" + +#include "globals.h" +#include "oscam-string.h" + +void array_clear(void **arr_data, int32_t *arr_num_entries) +{ + *arr_num_entries = 0; + if (arr_data) + { + free(*arr_data); + *arr_data = NULL; + } +} + +bool array_clone(void **src_arr_data, int32_t *src_arr_num_entries, uint32_t entry_size, void **dst_arr_data, int32_t *dst_arr_num_entries) +{ + array_clear(dst_arr_data, dst_arr_num_entries); + if (!src_arr_data || !dst_arr_data || !*src_arr_data) + return false; + if (!cs_malloc(dst_arr_data, *src_arr_num_entries * entry_size)) + return false; + memcpy(*dst_arr_data, *src_arr_data, *src_arr_num_entries * entry_size); + *dst_arr_num_entries = *src_arr_num_entries; + return true; +} + +bool array_add(void **arr_data, int32_t *arr_num_entries, uint32_t entry_size, void *new_entry) +{ + if (!cs_realloc(arr_data, (*arr_num_entries + 1) * entry_size)) + return false; + memcpy(*arr_data + (*arr_num_entries * entry_size), new_entry, entry_size); + *arr_num_entries += 1; + return true; +} + +/* Array functions for different types */ +#define DECLARE_ARRAY_FUNCS(NAME, BASE_TYPE, DATA_TYPE, DATA_FIELD, NUM_FIELD) \ + void NAME##_clear(BASE_TYPE *in) \ + { \ + if (!in) return; \ + void *pin = in->DATA_FIELD; /* Prevent warnings about strict-aliasing rules */ \ + array_clear(&pin, &in->NUM_FIELD); \ + in->DATA_FIELD = pin; \ + } \ + \ + bool NAME##_clone(BASE_TYPE *src, BASE_TYPE *dst) \ + { \ + if (!src || !dst) return false; \ + void *psrc = src->DATA_FIELD, *pdst = dst->DATA_FIELD; /* Prevent warnings about strict-aliasing rules */ \ + bool ret = array_clone(&psrc, &src->NUM_FIELD, sizeof(*src->DATA_FIELD), &pdst, &dst->NUM_FIELD); \ + dst->DATA_FIELD = pdst; \ + return ret; \ + } \ + \ + bool NAME##_add(BASE_TYPE *in, DATA_TYPE *td) \ + { \ + if (!in) return false; \ + void *pin = in->DATA_FIELD; /* Prevent warnings about strict-aliasing rules */ \ + bool ret = array_add(&pin, &in->NUM_FIELD, sizeof(*in->DATA_FIELD), td); \ + in->DATA_FIELD = pin; \ + return ret; \ + } + +DECLARE_ARRAY_FUNCS(ftab, FTAB, FILTER, filts, nfilts); // Declare ftab_clear(), ftab_clone(), ftab_add() +DECLARE_ARRAY_FUNCS(tuntab, TUNTAB, TUNTAB_DATA, ttdata, ttnum); // Declare tuntab_clear(), tuntab_clone(), tuntab_add() +DECLARE_ARRAY_FUNCS(ecm_whitelist, ECM_WHITELIST, ECM_WHITELIST_DATA, ewdata, ewnum); // Declare ecm_whitelist_clear(), ecm_whitelist_clone(), ecm_whitelist_add() +DECLARE_ARRAY_FUNCS(ecm_hdr_whitelist, ECM_HDR_WHITELIST, ECM_HDR_WHITELIST_DATA, ehdata, ehnum); // Declare ecm_hdr_whitelist_clear(), ecm_hdr_whitelist_clone(), ecm_hdr_whitelist_add() +DECLARE_ARRAY_FUNCS(caidvaluetab, CAIDVALUETAB, CAIDVALUETAB_DATA, cvdata, cvnum); // Declare caidvaluetab_clear(), caidvaluetab_clone(), caidvaluetab_add() +DECLARE_ARRAY_FUNCS(caidtab, CAIDTAB, CAIDTAB_DATA, ctdata, ctnum); // Declare caidtab_clear(), caidtab_clone(), caidtab_add() +DECLARE_ARRAY_FUNCS(cecspvaluetab, CECSPVALUETAB, CECSPVALUETAB_DATA, cevdata, cevnum); // Declare cecspvaluetab_clear(), cecspvaluetab_clone(), cecspvaluetab_add() +DECLARE_ARRAY_FUNCS(cwcheckvaluetab, CWCHECKTAB, CWCHECKTAB_DATA, cwcheckdata, cwchecknum); // Declare cwcheckvaluetab_clear(), cwcheckvaluetab_clone(), cwcheckvaluetab_add() + +#undef DECLARE_ARRAY_FUNCS diff --git a/oscam-array.h b/oscam-array.h new file mode 100644 index 0000000..6706c2a --- /dev/null +++ b/oscam-array.h @@ -0,0 +1,32 @@ +#ifndef OSCAM_ARRAY_H +#define OSCAM_ARRAY_H + +/* Functions for manipulating dynamic arrays */ + +/* Frees array data and reset array num_entries */ +void array_clear(void **arr_data, int32_t *arr_num_entries); + +/* Initializes dst array with src array data. dst array is cleared first */ +bool array_clone(void **src_arr_data, int32_t *src_arr_num_entries, uint32_t entry_size, void **dst_arr_data, int32_t *dst_arr_num_entries); + +/* Add element at the end of array */ +bool array_add(void **arr_data, int32_t *arr_num_entries, uint32_t entry_size, void *new_entry); + +/* Array functions for different types */ +#define DECLARE_ARRAY_FUNCS(NAME, BASE_TYPE, DATA_TYPE, DATA_FIELD, NUM_FIELD) \ + void NAME##_clear(BASE_TYPE *in); \ + bool NAME##_clone(BASE_TYPE *src, BASE_TYPE *dst); \ + bool NAME##_add(BASE_TYPE *in, DATA_TYPE *td); \ + +DECLARE_ARRAY_FUNCS(ftab, FTAB, FILTER, filts, nfilts); // Declare ftab_clear(), ftab_clone(), ftab_add() +DECLARE_ARRAY_FUNCS(tuntab, TUNTAB, TUNTAB_DATA, ttdata, ttnum); // Declare tuntab_clear(), tuntab_clone(), tuntab_add() +DECLARE_ARRAY_FUNCS(ecm_whitelist, ECM_WHITELIST, ECM_WHITELIST_DATA, ewdata, ewnum); // Declare ecm_whitelist_clear(), ecm_whitelist_clone(), ecm_whitelist_add() +DECLARE_ARRAY_FUNCS(ecm_hdr_whitelist, ECM_HDR_WHITELIST, ECM_HDR_WHITELIST_DATA, ehdata, ehnum); // Declare ecm_hdr_whitelist_clear(), ecm_hdr_whitelist_clone(), ecm_hdr_whitelist_add() +DECLARE_ARRAY_FUNCS(caidvaluetab, CAIDVALUETAB, CAIDVALUETAB_DATA, cvdata, cvnum); // Declare caidvaluetab_clear(), caidvaluetab_clone(), caidvaluetab_add() +DECLARE_ARRAY_FUNCS(caidtab, CAIDTAB, CAIDTAB_DATA, ctdata, ctnum); // Declare caidtab_clear(), caidtab_clone(), caidtab_add() +DECLARE_ARRAY_FUNCS(cecspvaluetab, CECSPVALUETAB, CECSPVALUETAB_DATA, cevdata, cevnum); // Declare cecspvaluetab_clear(), cecspvaluetab_clone(), cecspvaluetab_add() +DECLARE_ARRAY_FUNCS(cwcheckvaluetab, CWCHECKTAB, CWCHECKTAB_DATA, cwcheckdata, cwchecknum); // Declare cwcheckvaluetab_clear(), cwcheckvaluetab_clone(), cwcheckvaluetab_add() + +#undef DECLARE_ARRAY_FUNCS + +#endif diff --git a/oscam-cache.c b/oscam-cache.c new file mode 100644 index 0000000..d4832a8 --- /dev/null +++ b/oscam-cache.c @@ -0,0 +1,987 @@ +#define MODULE_LOG_PREFIX "cache" + +#include "globals.h" +#include "module-cacheex.h" +#include "module-cw-cycle-check.h" +#include "oscam-cache.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-hashtable.h" +#ifdef CS_CACHEEX_AIO +#include "oscam-log.h" +#endif + + +// CACHE functions **************************************************************+ +struct s_pushclient +{ + struct s_client *cl; + struct s_pushclient *next_push; +}; + +typedef struct cw_t +{ + uint8_t cw[16]; + uint8_t odd_even; // odd/even byte (0x80 0x81) + uint8_t cwc_cycletime; + uint8_t cwc_next_cw_cycle; + uint8_t got_bad_cwc; // used by cycle check + uint16_t caid; // first caid received + uint32_t prid; // first prid received + uint16_t srvid; // first srvid received + struct s_reader *selected_reader; // first answering: reader + struct s_client *cacheex_src; // first answering: cacheex client + uint64_t grp; // updated grp + uint8_t csp; // updated if answer from csp + uint8_t cacheex; // updated if answer from cacheex + uint8_t localcards; // updated if answer from local cards (or proxy using localcards option) + uint8_t proxy; // updated if answer from local reader + uint32_t count; // count of same cws receved +#ifdef CS_CACHEEX_AIO + uint8_t localgenerated; // flag for local generated CWs +#endif + // for push out + pthread_rwlock_t pushout_client_lock; + struct s_pushclient *pushout_client; // list of clients that pushing cw + // end push out + node ht_node; // node for hash table + node ll_node; // node for linked list +} CW; + +typedef struct cache_t +{ + hash_table ht_cw; + list ll_cw; + struct timeb upd_time; // updated time. Update time at each cw got + struct timeb first_recv_time; // time of first cw received + uint32_t csp_hash; + node ht_node; // node for hash table + node ll_node; // node for linked list +} ECMHASH; + +#ifdef CS_CACHEEX_AIO +typedef struct cw_cache_t +{ + uint8_t cw[16]; + uint16_t caid; + uint32_t prid; + uint16_t srvid; + struct timeb first_recv_time; // time of first cw received + struct timeb upd_time; // updated time. Update time at each cw got + node ht_node; + node ll_node; +} CW_CACHE; + +typedef struct cw_cache_setting_t +{ + int8_t mode; + uint16_t timediff_old_cw; +} CW_CACHE_SETTING; +#endif + +static pthread_rwlock_t cache_lock; +#ifdef CS_CACHEEX_AIO +static pthread_rwlock_t cw_cache_lock; +#endif +static hash_table ht_cache; +#ifdef CS_CACHEEX_AIO +static hash_table ht_cw_cache; +#endif +static list ll_cache; +#ifdef CS_CACHEEX_AIO +static list ll_cw_cache; +#endif +static int8_t cache_init_done = 0; + +#ifdef CS_CACHEEX_AIO +static int8_t cw_cache_init_done = 0; +static uint32_t lg_cache_size = 0; + +void init_cw_cache(void) +{ +#ifdef CS_CACHEEX + if(cfg.cw_cache_size > 0 || cfg.cw_cache_memory > 0) + { + init_hash_table(&ht_cw_cache, &ll_cw_cache); + if (pthread_rwlock_init(&cw_cache_lock,NULL) != 0) + { cs_log("Error creating lock cw_cache_lock!"); } + else + { cw_cache_init_done = 1; } + } +#endif +} +#endif + +void init_cache(void) +{ + init_hash_table(&ht_cache, &ll_cache); + if (pthread_rwlock_init(&cache_lock,NULL) != 0) + { cs_log("Error creating lock cache_lock!"); } + else + { cache_init_done = 1; } +} + +void free_cache(void) +{ + cleanup_cache(true); +#ifdef CS_CACHEEX_AIO + cw_cache_cleanup(true); + ecm_cache_cleanup(true); + cw_cache_init_done = 0; + deinitialize_hash_table(&ht_cw_cache); + pthread_rwlock_destroy(&cw_cache_lock); +#endif + cache_init_done = 0; + deinitialize_hash_table(&ht_cache); + pthread_rwlock_destroy(&cache_lock); +} + +#ifdef CS_CACHEEX_AIO +uint32_t cache_size_lg(void) +{ + if(!cache_init_done) + { return 0; } + + return lg_cache_size; +} +#endif + +uint32_t cache_size(void) +{ + if(!cache_init_done) + { return 0; } + + return count_hash_table(&ht_cache); +} + +static uint8_t count_sort(CW *a, CW *b) +{ + if (a->count == b->count) return 0; + return (a->count > b->count) ? -1 : 1; // DESC order by count +} + +#ifdef CS_CACHEEX_AIO +static uint8_t time_sort(CW_CACHE *a, CW_CACHE *b) +{ + if (((int64_t)(a->upd_time.time) * 1000ull + (int64_t) a->upd_time.millitm) == ((int64_t)(b->upd_time.time) * 1000ull + (int64_t) b->upd_time.millitm)) return 0; + return (((int64_t)(a->upd_time.time) * 1000ull + (int64_t) a->upd_time.millitm) > ((int64_t)(b->upd_time.time) * 1000ull + (int64_t) b->upd_time.millitm)) ? -1 : 1; +} +#endif + +uint8_t check_is_pushed(void *cwp, struct s_client *cl) +{ + struct s_pushclient *cl_tmp; + CW* cw = (CW*)cwp; + bool pushed=false; + + SAFE_RWLOCK_RDLOCK(&cw->pushout_client_lock); + for (cl_tmp = cw->pushout_client; cl_tmp; cl_tmp = cl_tmp->next_push) + { + if(cl_tmp->cl==cl) + { + pushed=true; + break; + } + } + + if(!pushed) + { + SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock); + SAFE_RWLOCK_WRLOCK(&cw->pushout_client_lock); + + struct s_pushclient *new_push_client; + if(cs_malloc(&new_push_client, sizeof(struct s_pushclient))) + { + new_push_client->cl=cl; + + new_push_client->next_push=cw->pushout_client; + cw->pushout_client=new_push_client; + } + + SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock); + return 0; + } + else + { + SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock); + return 1; + } +} + +uint8_t get_odd_even(ECM_REQUEST *er) +{ + return (er->ecm[0] != 0x80 && er->ecm[0] != 0x81 ? 0 : er->ecm[0]); +} + + +CW *get_first_cw(ECMHASH *ecmhash, ECM_REQUEST *er) +{ + if(!ecmhash) return NULL; + + node *j; + CW *cw; + + j = get_first_node_list(&ecmhash->ll_cw); + while (j) { + cw = get_data_from_node(j); + + if(cw && cw->odd_even == get_odd_even(er) && !cw->got_bad_cwc) + return cw; + + j = j->next; + } + + return NULL; +} + +int compare_csp_hash(const void *arg, const void *obj) +{ + uint32_t h = ((const ECMHASH*)obj)->csp_hash; + return memcmp(arg, &h, 4); +} + +static int compare_cw(const void *arg, const void *obj) +{ + return memcmp(arg, ((const CW*)obj)->cw, 16); +} + +#ifdef CS_CACHEEX_AIO +static int compare_cw_cache(const void *arg, const void *obj) +{ + return memcmp(arg, ((const CW_CACHE*)obj)->cw, 16); +} +#endif + +static bool cwcycle_check_cache(struct s_client *cl, ECM_REQUEST *er, CW *cw) +{ + (void)cl; (void)er; (void)cw; + +#ifdef CW_CYCLE_CHECK + if(cw->got_bad_cwc) + return 0; + + uint8_t cwc_ct = cw->cwc_cycletime > 0 ? cw->cwc_cycletime : 0; + uint8_t cwc_ncwc = cw->cwc_next_cw_cycle < 2 ? cw->cwc_next_cw_cycle : 2; + if(checkcwcycle(cl, er, NULL, cw->cw, 0, cwc_ct, cwc_ncwc) != 0) + { + cs_log_dbg(D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [check_cache] cyclecheck passed ecm in INT. cache.", (cl ? cl->account->usr : "-"), er->caid, er->srvid); + } + else + { +#ifdef CS_CACHEEX_AIO + if(!er->localgenerated) + { +#endif + cs_log_dbg(D_CWC, "cyclecheck [BAD CW Cycle] from Int. Cache detected.. {client %s, caid %04X, srvid %04X} [check_cache] -> skip cache answer", (cl ? cl->account->usr : "-"), er->caid, er->srvid); + cw->got_bad_cwc = 1; // no need to check it again + return 0; +#ifdef CS_CACHEEX_AIO + } + else + { + cs_log_dbg(D_CWC, "cyclecheck [BAD CW Cycle] from Int. Cache detected.. {client %s, caid %04X, srvid %04X} [check_cache] -> lg-flagged CW -> do nothing", (cl ? cl->account->usr : "-"), er->caid, er->srvid); + } +#endif + } +#endif + return 1; +} + +/* + * This function returns cw (mostly received) in cache for er, or NULL if not found. + * IMPORTANT: + * - If found, DON'T forget to free returned ecm, because it is a copy useful to get data + * - If found, and cacheex_src client of returned ecm is not NULL, and we want to access it, + * remember to check for its validity (client structure is still existent) + * E.g.: if(ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill) + * We don't want make this stuff here to avoid useless cpu time if outside function we would not access to it. + */ +struct ecm_request_t *check_cache(ECM_REQUEST *er, struct s_client *cl) +{ + if(!cache_init_done || !er->csp_hash) return NULL; + + ECM_REQUEST *ecm = NULL; + ECMHASH *result; + CW *cw; + uint64_t grp = cl?cl->grp:0; + + SAFE_RWLOCK_RDLOCK(&cache_lock); + + result = find_hash_table(&ht_cache, &er->csp_hash, sizeof(uint32_t),&compare_csp_hash); + cw = get_first_cw(result, er); + if (!cw) + goto out_err; + + if(cw->csp // csp have no grp! + || !grp // csp client(no grp) searching for cache + || (grp && cw->grp // ecm group --> only when readers/ex-clients answer (e_found) it + && (grp & cw->grp))) + { +#ifdef CS_CACHEEX + //if preferlocalcards=2 for this ecm request, we can server ONLY cw from localcards readers until stage<3 + if(er->preferlocalcards==2 && !cw->localcards && er->stage<3){ + goto out_err; + } + + CWCHECK check_cw = get_cwcheck(er); + + if((!cw->proxy && !cw->localcards) // cw received from ONLY cacheex/csp peers + && check_cw.counter>1 + && cw->count < check_cw.counter + && (check_cw.mode == 1 || !er->cacheex_wait_time_expired)) + { + goto out_err; + } + +#ifdef CS_CACHEEX_AIO + // client + if( cl && !cw->localgenerated + && !(chk_srvid_localgenerated_only_exception(er)) // service-based exception + && (cl->account->cacheex.localgenerated_only + || (chk_lg_only(er, &cl->account->cacheex.lg_only_tab)) + ) // only lg-flagged CWs + ) + { + goto out_err; + } +#endif +#endif + + if (!cwcycle_check_cache(cl, er, cw)) + goto out_err; + + if (cs_malloc(&ecm, sizeof(ECM_REQUEST))) + { + ecm->rc = E_FOUND; + ecm->rcEx = 0; + memcpy(ecm->cw, cw->cw, 16); + ecm->grp = cw->grp; + ecm->selected_reader = cw->selected_reader; + ecm->cwc_cycletime = cw->cwc_cycletime; + ecm->cwc_next_cw_cycle = cw->cwc_next_cw_cycle; + ecm->cacheex_src = cw->cacheex_src; +#ifdef CS_CACHEEX_AIO + ecm->localgenerated = (cw->localgenerated) ? 1:0; +#endif + ecm->cw_count = cw->count; + ecm->from_cacheex = cw->cacheex; // Kopiowanie flagi cacheex + ecm->from_csp = cw->csp; // Kopiowanie flagi csp + } + } + +out_err: + SAFE_RWLOCK_UNLOCK(&cache_lock); + return ecm; +} + +#ifdef CS_CACHEEX_AIO +uint16_t get_cacheex_nopushafter(ECM_REQUEST *er) +{ + return caidvaluetab_get_value(&cfg.cacheex_nopushafter_tab, er->caid, 0); +} +#endif + +static void cacheex_cache_add(ECM_REQUEST *er, ECMHASH *result, CW *cw, bool add_new_cw) +{ + (void)er; (void)result; (void)cw; (void)add_new_cw; +#ifdef CS_CACHEEX + er->cw_cache = cw; + cacheex_cache_push(er); + + // cacheex debug log lines and cw diff stuff + if(!check_client(er->cacheex_src)) + return; + +#ifdef CS_CACHEEX_AIO + if (D_CACHEEX & cs_dblevel) + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + + if(!add_new_cw) + { + debug_ecm(D_CACHEEX| D_CSP, "got duplicate pushed ECM %s from %s - hop %i %s, src-nodeid %" PRIu64 "X", buf, er->from_csp ? "csp" : username(er->cacheex_src), ll_count(er->csp_lastnodes), er->localgenerated ? "(lg)" : "", cacheex_node_id(remotenodeid)); + return; + } + + debug_ecm(D_CACHEEX|D_CSP, "got pushed ECM %s from %s - hop %i %s, src-nodeid %" PRIu64 "X", buf, er->from_csp ? "csp" : username(er->cacheex_src), ll_count(er->csp_lastnodes), er->localgenerated ? "(lg)" : "", cacheex_node_id(remotenodeid)); + } + else + { +#endif + if(!add_new_cw) + { +#ifdef CS_CACHEEX_AIO + debug_ecm(D_CACHEEX| D_CSP, "got duplicate pushed ECM %s from %s - hop %i %s", buf, er->from_csp ? "csp" : username(er->cacheex_src), ll_count(er->csp_lastnodes), er->localgenerated ? "(lg)" : ""); +#else + debug_ecm(D_CACHEEX| D_CSP, "got duplicate pushed ECM %s from %s", buf, er->from_csp ? "csp" : username(er->cacheex_src)); +#endif + return; + } + +#ifdef CS_CACHEEX_AIO + debug_ecm(D_CACHEEX|D_CSP, "got pushed ECM %s from %s - hop %i %s", buf, er->from_csp ? "csp" : username(er->cacheex_src), ll_count(er->csp_lastnodes), er->localgenerated ? "(lg)" : ""); +#else + debug_ecm(D_CACHEEX|D_CSP, "got pushed ECM %s from %s", buf, er->from_csp ? "csp" : username(er->cacheex_src)); +#endif + +#ifdef CS_CACHEEX_AIO + } +#endif + + CW *cw_first = get_first_cw(result, er); + if(!cw_first) + return; + + // compare er cw with mostly counted cached cw + if(memcmp(er->cw, cw_first->cw, sizeof(er->cw)) != 0) + { + er->cacheex_src->cwcacheexerrcw++; + if (er->cacheex_src->account) + er->cacheex_src->account->cwcacheexerrcw++; + + if (((0x0200| 0x0800) & cs_dblevel)) // avoid useless operations if debug is not enabled + { + char cw1[16*3+2], cw2[16*3+2]; + cs_hexdump(0, er->cw, 16, cw1, sizeof(cw1)); + cs_hexdump(0, cw_first->cw, 16, cw2, sizeof(cw2)); + + char ip1[20]="", ip2[20]=""; + if (check_client(er->cacheex_src)) + cs_strncpy(ip1, cs_inet_ntoa(er->cacheex_src->ip), sizeof(ip1)); + if (check_client(cw_first->cacheex_src)) + cs_strncpy(ip2, cs_inet_ntoa(cw_first->cacheex_src->ip), sizeof(ip2)); + else if (cw_first->selected_reader && check_client(cw_first->selected_reader->client)) + cs_strncpy(ip2, cs_inet_ntoa(cw_first->selected_reader->client->ip), sizeof(ip2)); + +#ifdef CS_CACHEEX_AIO + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + + uint8_t fakeF0 = 0, offset = 0; + + if(get_odd_even(er) == 0x81) + offset = 8; + + if( + (cw_first->cw[7+offset] != 0x00 && er->cw[7+offset] != 0x00) + && (cw_first->cw[7+offset] ^ 0xF0) == er->cw[7+offset] + ) + { + fakeF0 = 1; + } + + debug_ecm(D_CACHEEX| D_CSP, "WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s lg: %i<>%i, hop:%02i, src-nodeid: %" PRIu64 "X%s", buf, +#else + debug_ecm(D_CACHEEX| D_CSP, "WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s ", buf, +#endif + er->from_csp ? "csp" : username(er->cacheex_src), ip1, + check_client(cw_first->cacheex_src)?username(cw_first->cacheex_src):(cw_first->selected_reader?cw_first->selected_reader->label:"unknown/csp"), ip2, +#ifdef CS_CACHEEX_AIO + cw1, cw2, er->localgenerated, cw_first->localgenerated, er->csp_lastnodes ? ll_count(er->csp_lastnodes) : 0, er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0, fakeF0 ? " [last byte xor 0xF0]" : ""); +#else + cw1, cw2); +#endif +#ifdef WITH_DEBUG + if(cs_dblevel & D_CACHEEX) + { + LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0); + uint8_t *nodeid; + uint8_t hops = 0; + while((nodeid = ll_li_next(li))) + { + cs_log_dbg(D_CACHEEX, "Different CW-nodelist hop%02u: %" PRIu64 "X", ++hops, cacheex_node_id(nodeid)); + } + ll_li_destroy(li); + } +#endif + } + } +#endif +} + +#ifdef CS_CACHEEX_AIO +CW_CACHE_SETTING get_cw_cache(ECM_REQUEST *er) +{ + int32_t i, timediff_old_cw = 0; + int8_t mode = 0; + + for(i = 0; i < cfg.cw_cache_settings.cwchecknum; i++) + { + CWCHECKTAB_DATA *d = &cfg.cw_cache_settings.cwcheckdata[i]; + + if(i == 0 && d->caid <= 0) + { + mode = d->mode; + timediff_old_cw = d->counter; + continue; //check other, only valid for unset + } + + if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1)) + { + if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1) + { + if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1) + { + mode = d->mode; + timediff_old_cw = d->counter; + break; + } + } + } + } + + //check for correct values + if(mode>3 || mode<0) mode=0; + if(timediff_old_cw<1) timediff_old_cw=0; + + CW_CACHE_SETTING cw_cache_setting; + memset(&cw_cache_setting, 0, sizeof(CW_CACHE_SETTING)); + cw_cache_setting.mode = mode; + cw_cache_setting.timediff_old_cw = timediff_old_cw; + + return cw_cache_setting; +} + +static bool cw_cache_check(ECM_REQUEST *er) +{ + if(cw_cache_init_done) + { + CW_CACHE_SETTING cw_cache_setting = get_cw_cache(er); + if(cw_cache_setting.mode > 0) + { + CW_CACHE *cw_cache = NULL; + SAFE_RWLOCK_WRLOCK(&cw_cache_lock); + cw_cache = find_hash_table(&ht_cw_cache, &er->cw, sizeof(er->cw), &compare_cw_cache); + // add cw to ht_cw_cache if < cw_cache_size + if(!cw_cache) + { + // cw_cache-size(count/memory) pre-check + if( + (cfg.cw_cache_size && (cfg.cw_cache_size > tommy_hashlin_count(&ht_cw_cache))) + || (cfg.cw_cache_memory && (cfg.cw_cache_memory*1024*1024 > (2 * tommy_hashlin_memory_usage(&ht_cw_cache)))) + ) + { + if(cs_malloc(&cw_cache, sizeof(CW_CACHE))) + { + memcpy(cw_cache->cw, er->cw, sizeof(er->cw)); + cw_cache->caid = er->caid; + cw_cache->prid = er->prid; + cw_cache->srvid = er->srvid; + cs_ftime(&cw_cache->first_recv_time); + cs_ftime(&cw_cache->upd_time); + + tommy_hashlin_insert(&ht_cw_cache, &cw_cache->ht_node, cw_cache, tommy_hash_u32(0, &er->cw, sizeof(er->cw))); + tommy_list_insert_tail(&ll_cw_cache, &cw_cache->ll_node, cw_cache); + + SAFE_RWLOCK_UNLOCK(&cw_cache_lock); + return true; + } + else + { + SAFE_RWLOCK_UNLOCK(&cw_cache_lock); + cs_log("[cw_cache] ERROR: NO added HASH to cw_cache!!"); + return false; + } + } + else + { + // clean cache call; + SAFE_RWLOCK_UNLOCK(&cw_cache_lock); + cw_cache_cleanup(false); + return false; + } + } + // cw found + else + { + char cw1[16*3+2]; + char cw2[16*3+2]; + int8_t drop_cw = 0; + int64_t gone_diff = 0; + + gone_diff = comp_timeb(&er->tps, &cw_cache->first_recv_time); + + if(D_CW_CACHE & cs_dblevel) + { + cs_hexdump(0, cw_cache->cw, 16, cw1, sizeof(cw1)); + cs_hexdump(0, er->cw, 16, cw2, sizeof(cw2)); + } + + if(cw_cache_setting.timediff_old_cw > 0 && gone_diff > cw_cache_setting.timediff_old_cw) // late (>cw_cache_setting.timediff_old_cw) cw incoming + { + // log every dupe cw + if(cs_dblevel & D_CW_CACHE) + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + cs_log_dbg(D_CW_CACHE,"[dupe CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s | diff(now): %"PRIi64" ms > %"PRIu16" - %s - hop %i%s, src-nodeid %" PRIu64 "X", cw_cache->caid, cw_cache->prid, cw_cache->srvid, cw1, er->caid, er->prid, er->srvid, cw2, gone_diff, cw_cache_setting.timediff_old_cw, (er->selected_reader && cs_strlen(er->selected_reader->label)) ? er->selected_reader->label : username(er->cacheex_src), ll_count(er->csp_lastnodes), (er->localgenerated) ? " (lg)" : "", er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0); + } + + if(cw_cache->srvid == er->srvid && cw_cache->caid == er->caid) // same cw for same caid&srvid + { + cs_ftime(&cw_cache->upd_time); + cs_log_dbg(D_CW_CACHE,"[late CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s | diff(now): %"PRIi64" ms > %"PRIu16" - %s - hop %i%s", cw_cache->caid, cw_cache->prid, cw_cache->srvid, cw1, er->caid, er->prid, er->srvid, cw2, gone_diff, cw_cache_setting.timediff_old_cw, (er->selected_reader && cs_strlen(er->selected_reader->label)) ? er->selected_reader->label : username(er->cacheex_src), ll_count(er->csp_lastnodes), (er->localgenerated) ? " (lg)" : ""); + drop_cw=1; + + } + else if(cw_cache->srvid != er->srvid) // same cw for different srvid & late + { + cs_ftime(&cw_cache->upd_time); + cs_log_dbg(D_CW_CACHE,"[dupe&late CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s| diff(now): %"PRIi64" ms - %s - hop %i%s", cw_cache->caid, cw_cache->prid, cw_cache->srvid, cw1, er->caid, er->prid, er->srvid, cw2, gone_diff, (er->selected_reader && cs_strlen(er->selected_reader->label)) ? er->selected_reader->label : username(er->cacheex_src), ll_count(er->csp_lastnodes), (er->localgenerated) ? " (lg)" : ""); + drop_cw = 1; + } + else if(gone_diff > 15000) // same cw later as 15 secs + { +#ifdef WITH_DEBUG + if(cs_dblevel & D_CW_CACHE) + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + cs_log_dbg(D_CW_CACHE,"[late-15sec+ CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s | diff(now): %"PRIi64" ms > %"PRIu16" - %s - hop %i%s, src-nodeid %" PRIu64 "X", cw_cache->caid, cw_cache->prid, cw_cache->srvid, cw1, er->caid, er->prid, er->srvid, cw2, gone_diff, cw_cache_setting.timediff_old_cw, (er->selected_reader && cs_strlen(er->selected_reader->label)) ? er->selected_reader->label : username(er->cacheex_src), ll_count(er->csp_lastnodes), (er->localgenerated) ? " (lg)" : "", er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0); + } +#endif + drop_cw = 1; + } + + if(cw_cache_setting.mode > 1 && drop_cw) + { + // cw_cache->drop_count++; + cs_log_dbg(D_CW_CACHE,"incoming CW dropped - current cw_cache_size: %i - cw_cache-mem-size: %iMiB", count_hash_table(&ht_cw_cache), 2*(int)tommy_hashlin_memory_usage(&ht_cw_cache)/1024/1024); + SAFE_RWLOCK_UNLOCK(&cw_cache_lock); + return false; + } + } + } + + SAFE_RWLOCK_UNLOCK(&cw_cache_lock); + return true; + } + } + else + { + cs_log_dbg(D_CW_CACHE,"[cw_cache] cw_cache_init_done %i cfg.cw_cache_size: %u cfg.cw_cache_memory %u", cw_cache_init_done, cfg.cw_cache_size, cfg.cw_cache_memory); + return true; + } + return true; +} +#endif + +void add_cache(ECM_REQUEST *er) +{ + if(!cache_init_done || !er->csp_hash) return; +#ifdef CS_CACHEEX_AIO + // cw_cache_check + if(!cw_cache_check(er)) + { + return; + } +#endif + ECMHASH *result = NULL; + CW *cw = NULL; + bool add_new_cw=false; + + SAFE_RWLOCK_WRLOCK(&cache_lock); + + // add csp_hash to cache + result = find_hash_table(&ht_cache, &er->csp_hash, sizeof(uint32_t), &compare_csp_hash); + if(!result) + { + if(cs_malloc(&result, sizeof(ECMHASH))) + { + result->csp_hash = er->csp_hash; + init_hash_table(&result->ht_cw, &result->ll_cw); + cs_ftime(&result->first_recv_time); + add_hash_table(&ht_cache, &result->ht_node, &ll_cache, &result->ll_node, result, &result->csp_hash, sizeof(uint32_t)); + } + else + { + SAFE_RWLOCK_UNLOCK(&cache_lock); + cs_log("ERROR: NO added HASH to cache!!"); + return; + } + } + + cs_ftime(&result->upd_time); // need to be updated at each cw! We use it for deleting this hash when no more cws arrive inside max_cache_time! + + //add cw to this csp hash + cw = find_hash_table(&result->ht_cw, er->cw, sizeof(er->cw), &compare_cw); + + if(!cw) + { + if(count_hash_table(&result->ht_cw) >= 10) // max 10 different cws stored + { + SAFE_RWLOCK_UNLOCK(&cache_lock); + return; + } + + while(1) + { + if(cs_malloc(&cw, sizeof(CW))) + { + memcpy(cw->cw, er->cw, sizeof(er->cw)); + cw->odd_even = get_odd_even(er); + cw->cwc_cycletime = er->cwc_cycletime; + cw->cwc_next_cw_cycle = er->cwc_next_cw_cycle; + cw->count= 0; + cw->csp = 0; + cw->cacheex = 0; + cw->localcards=0; + cw->proxy=0; + cw->grp = 0; + cw->caid = er->caid; + cw->prid = er->prid; + cw->srvid = er->srvid; + cw->selected_reader=er->selected_reader; + cw->cacheex_src=er->cacheex_src; + cw->pushout_client = NULL; + + while(1) + { + if (pthread_rwlock_init(&cw->pushout_client_lock, NULL) == 0) + break; + + cs_log("Error creating lock pushout_client_lock!"); + cs_sleepms(1); + } + + add_hash_table(&result->ht_cw, &cw->ht_node, &result->ll_cw, &cw->ll_node, cw, cw->cw, sizeof(er->cw)); + add_new_cw=true; + break; + } + + cs_log("ERROR: NO added CW to cache!! Re-trying..."); + cs_sleepms(1); + } + } + + // update if answered from csp/cacheex/local_proxy + if(er->from_cacheex) cw->cacheex = 1; + if(er->from_csp) cw->csp = 1; + if(!er->cacheex_src) + { + if(is_localreader(er->selected_reader, er)) cw->localcards=1; + else cw->proxy = 1; + } + +#ifdef CS_CACHEEX_AIO + // copy flag for local generated CW + if(er->localgenerated || (er->selected_reader && !is_network_reader(er->selected_reader))) + { + cw->localgenerated = 1; + er->localgenerated = 1; + // to favorite CWs with this flag while sorting + if(cw->count < 0x0F000000) + { + cw->count |= 0x0F000000; + lg_cache_size++; + } + } + else + { + cw->localgenerated = 0; + } +#endif + + // always update group and counter + cw->grp |= er->grp; + cw->count++; + +#ifdef CS_CACHEEX_AIO + // add count to er for checking @ cacheex_push + er->cw_count += cw->count; +#endif + // sort cw_list by counter (DESC order) + if(cw->count>1) + sort_list(&result->ll_cw, count_sort); + +#ifdef CS_CACHEEX_AIO + // dont push not flagged CWs - global + if(!er->localgenerated && + ( + !chk_srvid_localgenerated_only_exception(er) + && (cfg.cacheex_localgenerated_only || chk_lg_only(er, &cfg.cacheex_lg_only_tab)) + ) ) + { + cs_log_dbg(D_CACHEEX, "cacheex: push denied, cacheex_localgenerated_only->global"); + SAFE_RWLOCK_UNLOCK(&cache_lock); + return; + } + + // dont push CW if time for caid > x && from local reader | proxy + if(er->rc < 3 && er->ecm_time && get_cacheex_nopushafter(er) != 0 &&(get_cacheex_nopushafter(er) < er->ecm_time )) + { + cs_log_dbg(D_CACHEEX, "cacheex: push denied, cacheex_nopushafter %04X:%u < %i, reader: %s", er->caid, get_cacheex_nopushafter(er), er->ecm_time, er->selected_reader->label); + SAFE_RWLOCK_UNLOCK(&cache_lock); + return; + } + + // no cacheex-push on diff-cw's if no localgenerated flag exist + if(cfg.cacheex_dropdiffs && (count_hash_table(&result->ht_cw) > 1) && !er->localgenerated) + { + cs_log_dbg(D_CACHEEX,"cacheex: diff CW - cacheex push denied src: %s", er->selected_reader->label); + SAFE_RWLOCK_UNLOCK(&cache_lock); + return; + } +#endif + + SAFE_RWLOCK_UNLOCK(&cache_lock); + + cacheex_cache_add(er, result, cw, add_new_cw); +} + +#ifdef CS_CACHEEX_AIO +void cw_cache_cleanup(bool force) +{ + if(!cw_cache_init_done) + { return; } + + SAFE_RWLOCK_WRLOCK(&cw_cache_lock); + + CW_CACHE *cw_cache; + node *i, *i_next; + + uint32_t ll_c = 0; + uint32_t ll_ten_percent = (uint)tommy_list_count(&ll_cw_cache)*0.1; // 10 percent of cache + + if(!force) + sort_list(&ll_cw_cache, time_sort); + + i = get_first_node_list(&ll_cw_cache); + while(i) + { + i_next = i->next; + + cw_cache = get_data_from_node(i); + + if(!cw_cache) + { + i = i_next; + continue; + } + if(!force) + { + ++ll_c; + + if(ll_c < ll_ten_percent) + { + remove_elem_list(&ll_cw_cache, &cw_cache->ll_node); + remove_elem_hash_table(&ht_cw_cache, &cw_cache->ht_node); + NULLFREE(cw_cache); + } + else{ + break; + } + } + else + { + remove_elem_list(&ll_cw_cache, &cw_cache->ll_node); + remove_elem_hash_table(&ht_cw_cache, &cw_cache->ht_node); + NULLFREE(cw_cache); + } + + i = i_next; + } + + SAFE_RWLOCK_UNLOCK(&cw_cache_lock); +} +#endif + +void cleanup_cache(bool force) +{ + ECMHASH *ecmhash; + CW *cw; + struct s_pushclient *pc, *nxt; + node *i,*i_next,*j,*j_next; + + struct timeb now; + int64_t gone_first, gone_upd; + + if(!cache_init_done) + { return; } + + SAFE_RWLOCK_WRLOCK(&cache_lock); + + i = get_first_node_list(&ll_cache); + while(i) + { + i_next = i->next; + ecmhash = get_data_from_node(i); + + if(!ecmhash) + { + i = i_next; + continue; + } + + cs_ftime(&now); + gone_first = comp_timeb(&now, &ecmhash->first_recv_time); + gone_upd = comp_timeb(&now, &ecmhash->upd_time); + + if(!force && gone_first<=(cfg.max_cache_time*1000)) // not continue, useless check for nexts one! + { + break; + } + + if(force || gone_upd>(cfg.max_cache_time*1000)) + { + j = get_first_node_list(&ecmhash->ll_cw); + while(j) + { + j_next = j->next; + cw = get_data_from_node(j); + if(cw) + { + pthread_rwlock_destroy(&cw->pushout_client_lock); + pc = cw->pushout_client; + cw->pushout_client=NULL; + while(pc) + { + nxt = pc->next_push; + NULLFREE(pc); + pc = nxt; + } +#ifdef CS_CACHEEX_AIO + if(cw->count >= 0x0F000000) + { + lg_cache_size--; + } +#endif + remove_elem_list(&ecmhash->ll_cw, &cw->ll_node); + remove_elem_hash_table(&ecmhash->ht_cw, &cw->ht_node); + NULLFREE(cw); + } + j = j_next; + } + + deinitialize_hash_table(&ecmhash->ht_cw); + remove_elem_list(&ll_cache, &ecmhash->ll_node); + remove_elem_hash_table(&ht_cache, &ecmhash->ht_node); + NULLFREE(ecmhash); + } + i = i_next; + } + SAFE_RWLOCK_UNLOCK(&cache_lock); +} + +#ifdef CS_CACHEEX_AIO +void cacheex_get_srcnodeid(ECM_REQUEST *er, uint8_t *remotenodeid) +{ + uint8_t *data; + data = ll_last_element(er->csp_lastnodes); + if(data) + { + memcpy(remotenodeid, data, 8); + } + else + { + memset(remotenodeid, 0 , 8); + } +} +#endif diff --git a/oscam-cache.h b/oscam-cache.h new file mode 100644 index 0000000..d28aabb --- /dev/null +++ b/oscam-cache.h @@ -0,0 +1,24 @@ +#ifndef OSCAM_CACHE_H_ +#define OSCAM_CACHE_H_ + +void init_cache(void); +#ifdef CS_CACHEEX_AIO +void init_cw_cache(void); +#endif +void free_cache(void); +void add_cache(ECM_REQUEST *er); +struct ecm_request_t *check_cache(ECM_REQUEST *er, struct s_client *cl); +void cleanup_cache(bool force); +void remove_client_from_cache(struct s_client *cl); +uint32_t cache_size(void); +#ifdef CS_CACHEEX_AIO +uint32_t cache_size_lg(void); +#endif +uint8_t get_odd_even(ECM_REQUEST *er); +uint8_t check_is_pushed(void *cw, struct s_client *cl); +#ifdef CS_CACHEEX_AIO +void cw_cache_cleanup(bool force); +int compare_csp_hash(const void *arg, const void *obj); +void cacheex_get_srcnodeid(ECM_REQUEST *er, uint8_t *remotenodeid); +#endif +#endif diff --git a/oscam-chk.c b/oscam-chk.c new file mode 100644 index 0000000..d2c104f --- /dev/null +++ b/oscam-chk.c @@ -0,0 +1,1353 @@ +#define MODULE_LOG_PREFIX "chk" + +#include "globals.h" +#include "oscam-cache.h" +#include "oscam-chk.h" +#include "oscam-ecm.h" +#include "oscam-client.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "module-stat.h" +#include "oscam-reader.h" + +#define CS_NANO_CLASS 0xE2 +#define OK 1 +#define ERROR 0 + +uint32_t get_fallbacktimeout(uint16_t caid) +{ + uint32_t ftimeout = caidvaluetab_get_value(&cfg.ftimeouttab, caid, 0); + + if(ftimeout == 0) { ftimeout = cfg.ftimeout; } + + if(ftimeout < 100) { ftimeout = CS_CLIENT_TIMEOUT / 2; } + if(ftimeout >= cfg.ctimeout) { ftimeout = cfg.ctimeout - 100; } + + return ftimeout; +} + +static int32_t find_nano(uint8_t *ecm, int32_t l, uint8_t nano, int32_t s) +{ + uint8_t *snano; + + if(s >= l) { return 0; } + if(!s) { s = (ecm[4] == 0xD2) ? 12 : 9; } // tpsflag -> offset+3 + snano = ecm + s; + + while((*snano != nano) && (s < l)) + { + if(*snano == 0xEA) { return 0; } + snano++; + s++; + } + + return (s < l) ? ++s : 0; +} + +static int32_t chk_class(ECM_REQUEST *er, CLASSTAB *clstab, const char *type, const char *name) +{ + int32_t i, j, an, cl_n, l; + uint8_t ecm_class; + + if(er->caid != 0x0500 && er->caid != 0x4AE1) { return 1; } + if(!clstab->bn && !clstab->an) { return 1; } + + j = an = cl_n = 0; + + if(er->caid == 0x0500) + { + while((j = find_nano(er->ecm, er->ecmlen, CS_NANO_CLASS, j)) > 0) + { + l = er->ecm[j]; + if(l + j > er->ecmlen) { continue; } // skip, this is not a valid class identifier! + + ecm_class = er->ecm[j + l]; + cs_log_dbg(D_CLIENT, "ecm class=%02X", ecm_class); + + for(i = 0; i < clstab->bn; i++) // search in blocked + { + if(ecm_class == clstab->bclass[i]) + { + cs_log_dbg(D_CLIENT, "class %02X rejected by %s '%s' !%02X filter", + ecm_class, type, name, ecm_class); + return 0; + } + } + cl_n++; + + for(i = 0; i < clstab->an; i++) // search in allowed + { + if(ecm_class == clstab->aclass[i]) + { + an++; + break; + } + } + j += l; + } + } + else + { + if(er->prid != 0x11 || er->ecm[0] == 0) { return 1; } + + cl_n++; + ecm_class = er->ecm[5]; + cs_log_dbg(D_CLIENT, "ecm class=%02X", ecm_class); + + for(i = 0; i < clstab->bn; i++) // search in blocked + { + if(ecm_class == clstab->bclass[i]) + { + cs_log_dbg(D_CLIENT, "class %02X rejected by %s '%s' !%02X filter", + ecm_class, type, name, ecm_class); + return 0; + } + } + + for(i = 0; i < clstab->an; i++) // search in allowed + { + if(ecm_class == clstab->aclass[i]) + { + an++; + break; + } + } + } + + if(cl_n && clstab->an) + { + if(an) + { cs_log_dbg(D_CLIENT, "ECM classes allowed by %s '%s' filter", type, name); } + else + { + cs_log_dbg(D_CLIENT, "ECM classes don't match %s '%s' filter, rejecting", type, name); + return 0; + } + } + + return 1; +} + +int32_t chk_srvid_match(ECM_REQUEST *er, SIDTAB *sidtab) +{ + int32_t i, rc = 0; + + if(!sidtab->num_caid) + { rc |= 1; } + else + for(i = 0; (i < sidtab->num_caid) && (!(rc & 1)); i++) + if(er->caid == sidtab->caid[i]) { rc |= 1; } + + if(!er->prid || !sidtab->num_provid) + { rc |= 2; } + else + for(i = 0; (i < sidtab->num_provid) && (!(rc & 2)); i++) + if(er->prid == sidtab->provid[i]) { rc |= 2; } + + if(!sidtab->num_srvid) + { rc |= 4; } + else + for(i = 0; (i < sidtab->num_srvid) && (!(rc & 4)); i++) + if(er->srvid == sidtab->srvid[i]) { rc |= 4; } + + return (rc == 7); +} + +#ifdef CS_CACHEEX_AIO +int32_t chk_srvid_disablecrccws_only_for_exception(ECM_REQUEST *er) +{ + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->disablecrccws_only_for_exception && (sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) && chk_srvid_match(er, sidtab)) + { + return(1); + } + } + return(0); +} + +int32_t chk_srvid_no_wait_time(ECM_REQUEST *er) +{ + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->no_wait_time && (sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) && chk_srvid_match(er, sidtab)) + { + return(1); + } + } + return(0); +} + +int32_t chk_srvid_localgenerated_only_exception(ECM_REQUEST *er) +{ + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->lg_only_exception && (sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) && chk_srvid_match(er, sidtab)) + { + return(1); + } + } + return(0); +} +#endif + +int32_t chk_srvid(struct s_client *cl, ECM_REQUEST *er) +{ + int32_t nr, rc = 0; + SIDTAB *sidtab; + + if(!cl->sidtabs.ok) + { + if(!cl->sidtabs.no) { return (1); } + rc = 1; + } + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) + { + if((cl->sidtabs.no & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { return (0); } + + if((cl->sidtabs.ok & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { rc = 1; } + } + } + return (rc); +} + +int32_t has_srvid(struct s_client *cl, ECM_REQUEST *er) +{ + if(!cl->sidtabs.ok) + { return 0; } + + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_srvid) + { + if((cl->sidtabs.ok & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { return 1; } + } + } + return 0; +} + +int32_t has_lb_srvid(struct s_client *cl, ECM_REQUEST *er) +{ + if(!cl->lb_sidtabs.ok) + { return 0; } + + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if((cl->lb_sidtabs.ok & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { return 1; } + } + return 0; +} + +int32_t chk_srvid_match_by_caid_prov(uint16_t caid, uint32_t provid, SIDTAB *sidtab) +{ + int32_t i, rc = 0; + + if(!sidtab->num_caid) + { rc |= 1; } + else + for(i = 0; (i < sidtab->num_caid) && (!(rc & 1)); i++) + if(caid == sidtab->caid[i]) { rc |= 1; } + + if(!sidtab->num_provid) + { rc |= 2; } + else + for(i = 0; (i < sidtab->num_provid) && (!(rc & 2)); i++) + if(provid == sidtab->provid[i]) { rc |= 2; } + + return (rc == 3); +} + +int32_t chk_srvid_by_caid_prov(struct s_client *cl, uint16_t caid, uint32_t provid) +{ + int32_t nr, rc = 0; + SIDTAB *sidtab; + + if(!cl->sidtabs.ok) + { + if(!cl->sidtabs.no) { return (1); } + rc = 1; + } + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_caid | sidtab->num_provid) + { + if((cl->sidtabs.no & ((SIDTABBITS)1 << nr)) && !sidtab->num_srvid && + (chk_srvid_match_by_caid_prov(caid, provid, sidtab))) + { return (0); } + + if((cl->sidtabs.ok & ((SIDTABBITS)1 << nr)) && + (chk_srvid_match_by_caid_prov(caid, provid, sidtab))) + { rc = 1; } + } + } + return (rc); +} + +int32_t chk_srvid_by_caid_prov_rdr(struct s_reader *rdr, uint16_t caid, uint32_t provid) +{ + int32_t nr, rc = 0; + SIDTAB *sidtab; + + if(!rdr->sidtabs.ok) + { + if(!rdr->sidtabs.no) { return (1); } + rc = 1; + } + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_caid | sidtab->num_provid) + { + if((rdr->sidtabs.no & ((SIDTABBITS)1 << nr)) && !sidtab->num_srvid && + (chk_srvid_match_by_caid_prov(caid, provid, sidtab))) + { return (0); } + + if((rdr->sidtabs.ok & ((SIDTABBITS)1 << nr)) && + (chk_srvid_match_by_caid_prov(caid, provid, sidtab))) + { rc = 1; } + } + } + return (rc); +} + +int32_t chk_is_betatunnel_caid(uint16_t caid) +{ + if(caid == 0x1702 || caid == 0x1722) { return 1; } + if(caid == 0x1801 || caid == 0x1833 || caid == 0x1834 || caid == 0x1835) { return 2; } + return 0; +} + +uint16_t chk_on_btun(uint8_t chk_sx, struct s_client *cl, ECM_REQUEST *er) +{ + if(chk_is_betatunnel_caid(er->caid)) + { + int32_t i; + TUNTAB *ttab; + ttab = &cl->ttab; + + if(ttab->ttdata) + { + for(i = 0; i < ttab->ttnum; i++) + { + if(er->caid == ttab->ttdata[i].bt_caidfrom) + { + if(er->srvid == ttab->ttdata[i].bt_srvid) { return ttab->ttdata[i].bt_caidto; } + if(chk_sx && ttab->ttdata[i].bt_srvid == 0xFFFF) { return ttab->ttdata[i].bt_caidto; } + if(!chk_sx && !ttab->ttdata[i].bt_srvid) { return ttab->ttdata[i].bt_caidto; } + } + } + } + + if(chk_sx) + return lb_get_betatunnel_caid_to(er); + } + return 0; +} + +// server filter for newcamd +int32_t chk_sfilter(ECM_REQUEST *er, PTAB *ptab) +{ +#ifdef MODULE_NEWCAMD + int32_t i, j, pi, rc = 1; + uint16_t caid, scaid; + uint32_t prid, sprid; + + if(!ptab) { return (1); } + struct s_client *cur_cl = cur_client(); + + caid = er->caid; + prid = er->prid; + pi = cur_cl->port_idx; + + if(cfg.ncd_mgclient) + { return 1; } + + if(ptab->nports && ptab->ports[pi].ncd && ptab->ports[pi].ncd->ncd_ftab.nfilts) + { + for(rc = j = 0; (!rc) && (j < ptab->ports[pi].ncd->ncd_ftab.nfilts); j++) + { + scaid = ptab->ports[pi].ncd->ncd_ftab.filts[j].caid; + if(caid == 0 || (caid != 0 && caid == scaid)) + { + for(i = 0; (!rc) && i < ptab->ports[pi].ncd->ncd_ftab.filts[j].nprids; i++) + { + sprid = ptab->ports[pi].ncd->ncd_ftab.filts[j].prids[i]; + cs_log_dbg(D_CLIENT, "trying server filter %04X@%06X", scaid, sprid); + if(prid == sprid) + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by server filter %04X@%06X", + caid, prid, scaid, sprid); + } + } + } + } + + if(!rc) + { + cs_log_dbg(D_CLIENT, "no match, %04X@%06X rejected by server filters", caid, prid); + snprintf(er->msglog, MSGLOGSIZE, "no server match %04X@%06X", caid, (uint32_t) prid); + + if(!er->rcEx) { er->rcEx = (E1_LSERVER << 4) | E2_IDENT; } + return (rc); + } + } + return (rc); +#else + (void)er; + (void)ptab; + return 1; +#endif +} + +static int32_t chk_chid(ECM_REQUEST *er, FTAB *fchid, char *type, char *name) +{ + int32_t rc = 1, i, j, found_caid = 0; + if(!fchid->nfilts) { return 1; } + if(er->chid == 0 && er->ecm[0] == 0) { return 1; } // skip empty ecm, chid 00 to avoid no matching readers in dvbapi + + for(i = rc = 0; (!rc) && i < fchid->nfilts; i++) + { + if(er->caid == fchid->filts[i].caid) + { + found_caid = 1; + for(j = 0; (!rc) && j < fchid->filts[i].nprids; j++) + { + cs_log_dbg(D_CLIENT, "trying %s '%s' CHID filter %04X:%04X", + type, name, fchid->filts[i].caid, fchid->filts[i].prids[j]); + + if(er->chid == fchid->filts[i].prids[j]) + { + cs_log_dbg(D_CLIENT, "%04X:%04X allowed by %s '%s' CHID filter %04X:%04X", + er->caid, er->chid, type, name, fchid->filts[i].caid, fchid->filts[i].prids[j]); + rc = 1; + } + } + } + } + + if(!rc) + { + if(found_caid) + cs_log_dbg(D_CLIENT, "no match, %04X:%04X rejected by %s '%s' CHID filter(s)", + er->caid, er->chid, type, name); + else + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X:%04X allowed by %s '%s' CHID filter, CAID not spezified", + er->caid, er->chid, type, name); + } + } + return (rc); +} + +int32_t chk_ident_filter(uint16_t rcaid, uint32_t rprid, FTAB *ftab) +{ + int32_t i, j, rc = 1; + uint16_t caid = 0; + uint32_t prid = 0; + + if(ftab->nfilts) + { + for(rc = i = 0; (!rc) && (i < ftab->nfilts); i++) + { + caid = ftab->filts[i].caid; + if((caid != 0 && caid == rcaid) || caid == 0) + { + for(j = 0; (!rc) && (j < ftab->filts[i].nprids); j++) + { + prid = ftab->filts[i].prids[j]; + if(prid == rprid) + { + rc=1; + } + } + } + } + + if(!rc) + { return 0; } + } + + return(rc); +} + +int32_t chk_ufilters(ECM_REQUEST *er) +{ + int32_t i, j, rc = 1; + uint16_t ucaid; + uint32_t uprid; + struct s_client *cur_cl = cur_client(); + + if(cur_cl->ftab.nfilts) + { + FTAB *f = &cur_cl->ftab; + for(i = rc = 0; (!rc) && (i < f->nfilts); i++) + { + ucaid = f->filts[i].caid; + if(er->caid == 0 || ucaid == 0 || (er->caid != 0 && er->caid == ucaid)) + { + if (er->prid == 0) + { + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by user '%s' filter caid %04X prid %06X", + er->caid, er->prid, cur_cl->account->usr, ucaid, 0); + rc = 1; + break; + } + + for(j = rc = 0; (!rc) && (j < f->filts[i].nprids); j++) + { + uprid = f->filts[i].prids[j]; + cs_log_dbg(D_CLIENT, "trying user '%s' filter %04X@%06X", cur_cl->account->usr, ucaid, uprid); + + if(er->prid == uprid) + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by user '%s' filter %04X@%06X", + er->caid, er->prid, cur_cl->account->usr, ucaid, uprid); + } + } + } + } + + if(!rc) + { + cs_log_dbg(D_CLIENT, "no match, %04X@%06X rejected by user '%s' filters", + er->caid, er->prid, cur_cl->account->usr); + + snprintf(er->msglog, MSGLOGSIZE, "no card support %04X@%06X", er->caid, (uint32_t) er->prid); + + if(!er->rcEx) { er->rcEx = (E1_USER << 4) | E2_IDENT; } + return (rc); + } + } + + if(!(rc = chk_class(er, &cur_cl->cltab, "user", cur_cl->account->usr))) + { + if(!er->rcEx) { er->rcEx = (E1_USER << 4) | E2_CLASS; } + } + else if(!(rc = chk_chid(er, &cur_cl->fchid, "user", cur_cl->account->usr))) + { + if(!er->rcEx) { er->rcEx = (E1_USER << 4) | E2_CHID; } + } + + if(rc) { er->rcEx = 0; } + + return (rc); +} + +int32_t chk_rsfilter(struct s_reader *reader, ECM_REQUEST *er) +{ + int32_t i, rc = 1; + uint16_t caid; + uint32_t prid; + + if(reader->ncd_disable_server_filt) + { + cs_log_dbg(D_CLIENT, "%04X@%06X allowed - server filters disabled", er->caid, er->prid); + return 1; + } + + rc = 0; + caid = reader->caid; + if(caid == er->caid) + { + for(i = 0; (!rc) && (i < reader->nprov); i++) + { + prid = (uint32_t)((reader->prid[i][1] << 16) | (reader->prid[i][2] << 8) | (reader->prid[i][3])); + cs_log_dbg(D_CLIENT, "trying server '%s' filter %04X@%06X", reader->device, caid, prid); + + if(prid == er->prid || !er->prid) + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by server '%s' filter %04X@%06X", + er->caid, er->prid, reader->device, caid, prid); + } + } + } + + if(!rc) + { + cs_log_dbg(D_CLIENT, "no match, %04X@%06X rejected by server '%s' filters", + er->caid, er->prid, reader->device); + + if(!er->rcEx) { er->rcEx = (E1_SERVER << 4) | E2_IDENT; } + return 0; + } + + return (rc); +} + +int32_t chk_rfilter2(uint16_t rcaid, uint32_t rprid, struct s_reader *rdr) +{ + int32_t i, j, rc = 1; + uint16_t caid = 0; + uint32_t prid = 0; + + if(rdr->ftab.nfilts) + { + for(rc = i = 0; (!rc) && (i < rdr->ftab.nfilts); i++) + { + caid = rdr->ftab.filts[i].caid; + if((caid != 0 && caid == rcaid) || caid == 0) + { + for(j = 0; (!rc) && (j < rdr->ftab.filts[i].nprids); j++) + { + prid = rdr->ftab.filts[i].prids[j]; + cs_log_dbg(D_CLIENT, "trying reader '%s' filter %04X@%06X", rdr->label, caid, prid); + + if(prid == rprid) + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by reader '%s' filter %04X@%06X", + rcaid, rprid, rdr->label, caid, prid); + } + } + } + } + + if(!rc) + { + cs_log_dbg(D_CLIENT, "no match, %04X@%06X rejected by reader '%s' filters", + rcaid, rprid, rdr->label); + return 0; + } + } + + return (rc); +} + +static int32_t chk_rfilter(ECM_REQUEST *er, struct s_reader *rdr) +{ + return chk_rfilter2(er->caid, er->prid, rdr); +} + +int32_t chk_ctab(uint16_t caid, CAIDTAB *ctab) +{ + if(!caid || !ctab->ctnum) + { return 1; } + + int32_t i; + for(i = 0; i < ctab->ctnum; i++) + { + CAIDTAB_DATA *d = &ctab->ctdata[i]; + if(!d->caid) + { + return 0; + } + if((caid & d->mask) == d->caid) + { return 1; } + } + return 0; +} + +int32_t chk_ctab_ex(uint16_t caid, CAIDTAB *ctab) +{ + if(!caid || !ctab->ctnum) + { return 0; } + + int32_t i; + for(i = 0; i < ctab->ctnum; i++) + { + CAIDTAB_DATA *d = &ctab->ctdata[i]; + if(!d->caid) + { + return 0; + } + + if((caid & d->mask) == d->caid) + { + return 1; + } + } + return 0; +} + +uint8_t is_localreader(struct s_reader *rdr, ECM_REQUEST *er) // to be used for LB/reader selections checks only +{ + if(!rdr) return 0; + + if(!is_network_reader(rdr)) + { + return 1; + } + + if(!rdr->localcards.nfilts) { return 0; } + + int32_t i, k; + for(i = 0; i < rdr->localcards.nfilts; i++) + { + uint16_t tcaid = rdr->localcards.filts[i].caid; + if(tcaid && tcaid == er->caid) // caid match + { + int32_t nprids = rdr->localcards.filts[i].nprids; + if(!nprids) // No Provider -> Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid = rdr->localcards.filts[i].prids[k]; + if(prid == er->prid) // Provider matches + { + return 1; + } + } + } + } + + return 0; +} + +uint8_t chk_is_fixed_fallback(struct s_reader *rdr, ECM_REQUEST *er) +{ + if(!rdr->fallback && !rdr->fallback_percaid.nfilts) { return 0; } + + if(!rdr->fallback_percaid.nfilts) + { + if(rdr->fallback) + { return 1; } + } + + int32_t i, k; + for(i = 0; i < rdr->fallback_percaid.nfilts; i++) + { + uint16_t tcaid = rdr->fallback_percaid.filts[i].caid; + if(tcaid && (tcaid == er->caid || (tcaid < 0x0100 && (er->caid >> 8) == tcaid))) // caid match + { + int32_t nprids = rdr->fallback_percaid.filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid = rdr->fallback_percaid.filts[i].prids[k]; + if(prid == er->prid) // Provider matches + { + return 1; + } + } + } + } + + return 0; +} + +#ifdef CS_CACHEEX_AIO +uint8_t chk_lg_only(ECM_REQUEST *er, FTAB *lg_only_ftab) +{ + int32_t i, k; + + if(!lg_only_ftab->nfilts) + return 0; + + for(i = 0; i < lg_only_ftab->nfilts; i++) + { + uint16_t tcaid = lg_only_ftab->filts[i].caid; + if(tcaid && (tcaid == er->caid || (tcaid < 0x0100 && (er->caid >> 8) == tcaid))) // caid match + { + int32_t nprids = lg_only_ftab->filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid = lg_only_ftab->filts[i].prids[k]; + if(prid == NO_PROVID_VALUE || prid == er->prid) // Provider matches + { + return 1; + } + } + } + } + + return 0; +} + +uint8_t chk_lg_only_cp(uint16_t caid, uint32_t prid, FTAB *lg_only_ftab) +{ + int32_t i, k; + + if(!lg_only_ftab->nfilts) + return 0; + + for(i = 0; i < lg_only_ftab->nfilts; i++) + { + uint16_t tcaid = lg_only_ftab->filts[i].caid; + if(tcaid && (tcaid == caid || (tcaid < 0x0100 && (caid >> 8) == tcaid))) // caid match + { + int32_t nprids = lg_only_ftab->filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t fprid = lg_only_ftab->filts[i].prids[k]; + if(fprid == NO_PROVID_VALUE || fprid == prid) // Provider matches + { + return 1; + } + } + } + } + + return 0; +} +#endif + +uint8_t chk_has_fixed_fallback(ECM_REQUEST *er) +{ + struct s_ecm_answer *ea; + struct s_reader *rdr; + int32_t n_falb = 0; + for(ea = er->matching_rdr; ea; ea = ea->next) + { + rdr = ea->reader; + if(chk_is_fixed_fallback(rdr, er)) + { n_falb++; } + } + return n_falb; +} + +uint8_t chk_if_ignore_checksum(ECM_REQUEST *er, FTAB *disablecrc_only_for) +{ + if(!disablecrc_only_for->nfilts) { return 0; } + + int32_t i, k; + for(i = 0; i < disablecrc_only_for->nfilts; i++) + { + uint16_t tcaid = disablecrc_only_for->filts[i].caid; + if(tcaid && (tcaid == er->caid || (tcaid < 0x0100 && (er->caid >> 8) == tcaid))) // caid match + { + int32_t nprids = disablecrc_only_for->filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid =disablecrc_only_for->filts[i].prids[k]; + if(prid == er->prid) // Provider matches + { return 1; } + } + } + } + + return 0; +} + +int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr) +{ + // simple checks first: + if(!er || !rdr) + { return (0); } + + // reader active? + struct s_client *cl = rdr->client; + if(!cl || !rdr->enable) + { return (0); } + + // if physical reader a card needs to be inserted + if(!is_network_reader(rdr) && rdr->card_status != CARD_INSERTED) + { return (0); } + + // Checking connected & group valid: + struct s_client *cur_cl = er->client; //cur_client(); + +#ifdef CS_CACHEEX + // Cacheex=3 defines a Cacheex-only reader. never match them. + if(rdr->cacheex.mode == 3) + { return (0); } + if(rdr->cacheex.mode == 2 && !rdr->cacheex.allow_request) + { return (0); } +#endif + + if(!(rdr->grp & cur_cl->grp)) + { return (0); } + + // Checking caids: + if((!er->ocaid || !chk_ctab(er->ocaid, &rdr->ctab)) && !chk_ctab(er->caid, &rdr->ctab)) + { + cs_log_dbg(D_TRACE, "caid %04X not found in caidlist reader %s", er->caid, rdr->label); + return 0; + } + + if(!(rdr->typ == R_EMU) && !is_network_reader(rdr) && ((rdr->caid >> 8) != ((er->caid >> 8) & 0xFF) && (rdr->caid >> 8) != ((er->ocaid >> 8) & 0xFF))) + { + if (!rdr->csystem) + { return 0; } + + int i, caid_found = 0; + for(i = 0; rdr->csystem->caids[i]; i++) + { + uint16_t cs_caid = rdr->csystem->caids[i]; + if(!cs_caid) + { continue; } + + if(cs_caid == er->caid || cs_caid == er->ocaid) + { + caid_found = 1; + break; + } + } + + if(!caid_found) + { return 0; } + } + + // Supports long ecms? + if(er->ecmlen > 255 && is_network_reader(rdr) && !rdr->ph.large_ecm_support) + { + cs_log_dbg(D_TRACE, "no large ecm support (l=%d) for reader %s", er->ecmlen, rdr->label); + return 0; + } + + // Checking services: + if(!chk_srvid(rdr->client, er)) + { + cs_log_dbg(D_TRACE, "service %04X not matching reader %s", er->srvid, rdr->label); + return (0); + } + + // Checking ident: + if(!(rdr->typ == R_EMU && caid_is_biss(er->caid)) && !chk_rfilter(er, rdr)) + { + cs_log_dbg(D_TRACE, "r-filter reader %s", rdr->label); + return (0); + } + + // Check ECM nanos: + if(!chk_class(er, &rdr->cltab, "reader", rdr->label)) + { + cs_log_dbg(D_TRACE, "class filter reader %s", rdr->label); + return (0); + } + + // CDS NL: check for right seca type + if(!is_network_reader(rdr) && er->caid == 0x100 && er->prid == 0x00006a + && !(er->ecm[8] == 0x00 && er->ecm[9] == 0x00)) // no empty ecm + { + if(er->ecm[8] == 0x00 && rdr->secatype == 2) + { + cs_log_dbg(D_TRACE, "Error: this is a nagra/mediaguard3 ECM and readertype is seca2!"); + return 0; // we dont send a nagra/mediaguard3 ecm to a seca2 reader! + } + + if((er->ecm[8] == 0x10) && (er->ecm[9] == 0x01) && rdr->secatype == 3) + { + cs_log_dbg(D_TRACE, "Error: this is a seca2 ECM and readertype is nagra/mediaguard3!"); + return 0; // we dont send a seca2 ecm to a nagra/mediaguard3 reader! + } + } + + // CDS NL: check for right seca type by ECMPID + if(!is_network_reader(rdr) && er->caid == 0x100 && er->prid == 0x00006a) + { + if(rdr->secatype == 2 && er->pid >> 8 == 7) + { + cs_log_dbg(D_TRACE, "Error: this is a nagra/mediaguard3 ECM and readertype is seca2!"); + return 0; // we dont send a nagra/mediaguard3 ecm to a seca2 reader! + } + + if(rdr->secatype == 3 && er->pid >> 8 == 6) + { + cs_log_dbg(D_TRACE, "Error: this is a seca2 ECM and readertype is nagra/mediaguard3!"); + return 0; // we dont send a seca2 ecm to a nagra/mediaguard3 reader! + } + } + + // Checking chid: + if(!chk_chid(er, &rdr->fchid, "reader", rdr->label)) + { + cs_log_dbg(D_TRACE, "chid filter reader %s", rdr->label); + return (0); + } + + // Schlocke reader-defined function, reader-self-check + if(rdr->ph.c_available && !rdr->ph.c_available(rdr, AVAIL_CHECK_CONNECTED, er)) + { + cs_log_dbg(D_TRACE, "reader unavailable %s", rdr->label); + return 0; + } + + // Checking entitlements: + if(ll_count(rdr->ll_entitlements) > 0 && !(rdr->typ == R_EMU)) + { + LL_ITER itr = ll_iter_create(rdr->ll_entitlements); + S_ENTITLEMENT *item; + int8_t found = 0; + + while((item = ll_iter_next(&itr))) + { + if(item->caid != er->caid) continue; // skip wrong caid! + if(item->type == 7) continue; // skip seca-admin type (provid 000000) since its not used for decoding! + if(er->prid && item->provid && er->prid != item->provid) continue; // skip non matching provid! + if(!er->prid && caid_is_seca(er->caid)) continue; // dont accept requests without provid for seca cas. + if(!er->prid && caid_is_viaccess(er->caid)) continue; // dont accept requests without provid for viaccess cas + if(!er->prid && caid_is_cryptoworks(er->caid)) continue; // dont accept requests without provid for cryptoworks cas + found =1; + break; + } + + if(!found && er->ecm[0]) // ecmrequest can get corrected provid parsed from payload in ecm + { + cs_log_dbg(D_TRACE, "entitlements check failed on reader %s", rdr->label); + return 0; + } + } + + // Checking ecmlength: + if(rdr->ecm_whitelist.ewnum && er->ecmlen) + { + int32_t i; + int8_t ok = 0, foundident = 0; + + for (i = 0; i < rdr->ecm_whitelist.ewnum; i++) + { + ECM_WHITELIST_DATA *d = &rdr->ecm_whitelist.ewdata[i]; + if ((d->caid == 0 || d->caid == er->caid) && (d->ident == 0 || d->ident == er->prid)) + { + foundident = 1; + if (d->len == er->ecmlen) + { + ok = 1; + break; + } + } + } + + if(foundident == 1 && ok == 0) + { + cs_log_dbg(D_TRACE, "ECM is not in ecmwhitelist of reader %s.", rdr->label); + rdr->ecmsfilteredlen += 1; + rdr->webif_ecmsfilteredlen += 1; + return (0); + } + } + + // ECM Header Check + if(rdr->ecm_hdr_whitelist.ehdata && er->ecmlen) + { + int8_t byteok = 0; + int8_t entryok = 0; + int8_t foundcaid = 0; + int8_t foundprovid = 0; + int16_t len = 0; + int32_t i = 0; + int8_t skip = 0; + int32_t r; + + for(r = 0; r < rdr->ecm_hdr_whitelist.ehnum; r++) + { + ECM_HDR_WHITELIST_DATA *tmp = &rdr->ecm_hdr_whitelist.ehdata[r]; + skip = 0; + byteok = 0; + entryok = 0; + + if(tmp->caid == 0 || tmp->caid == er->caid) + { + foundcaid = 1; //-> caid was in list + //rdr_log_dbg(rdr, D_READER, "Headerwhitelist: found matching CAID: %04X in list", tmp->caid); + + if(tmp->provid == 0 || tmp->provid == er->prid) + { + foundprovid = 1; //-> provid was in list + //rdr_log_dbg(rdr, D_READER, "Headerwhitelist: found matching Provid: %06X in list", tmp->provid); + + len = tmp->len; + for(i = 0; i < len / 2; i++) + { + if(tmp->header[i] == er->ecm[i]) + { + byteok = 1; + //rdr_log_dbg(rdr, D_READER, "ECM Byte: %i of ECMHeaderwhitelist is correct. (%02X = %02X Headerlen: %i)", i, er->ecm[i], tmp->header[i], len/2); + } + else + { + byteok = 0; + //rdr_log_dbg(rdr, D_READER, "ECM Byte: %i of ECMHeaderwhitelist is not valid. (%02X != %02X Headerlen: %i)", i, er->ecm[i], tmp->header[i], len/2); + entryok = 0; + break; + } + + if(i == len / 2 - 1 && byteok == 1) + { + entryok = 1; + } + } + } + else + { + //rdr_log_dbg(rdr, D_READER, "ECMHeaderwhitelist: Provid: %06X not found in List-Entry -> skipping check", er->prid); + skip = 1; + continue; + } + } + else + { + //rdr_log_dbg(rdr, D_READER, "ECMHeaderwhitelist: CAID: %04X not found in List-Entry -> skipping check", er->caid); + skip = 1; + continue; + } + + if(entryok == 1) + { + break; + } + } + + if(foundcaid == 1 && foundprovid == 1 && byteok == 1 && entryok == 1) + { + //cs_log("ECM for %04X@%06X:%04X is valid for ECMHeaderwhitelist of reader %s.", er->caid, er->prid, er->srvid, rdr->label); + } + else + { + if(skip == 0 || (foundcaid == 1 && foundprovid == 1 && entryok == 0 && skip == 1)) + { + cs_log_dump_dbg(D_TRACE, er->ecm, er->ecmlen, "following ECM %04X@%06X:%04X was filtered by ECMHeaderwhitelist of Reader %s from User %s because of not matching Header:", er->caid, er->prid, er->srvid, rdr->label, username(er->client)); + rdr->ecmsfilteredhead += 1; + rdr->webif_ecmsfilteredhead += 1; + return (0); + } + } + } + + // Simple ring connection check: + + // Check ip source+dest: + if(cfg.block_same_ip && IP_EQUAL(cur_cl->ip, rdr->client->ip) && get_module(cur_cl)->listenertype != LIS_DVBAPI && is_network_reader(rdr)) + { + rdr_log_dbg(rdr, D_TRACE, "User (%s) has the same ip (%s) as the reader, blocked because block_same_ip=1!", username(cur_cl), cs_inet_ntoa(rdr->client->ip)); + return 0; + } + + if(cfg.block_same_name && strcmp(username(cur_cl), rdr->label) == 0) + { + rdr_log_dbg(rdr, D_TRACE, "User (%s) has the same name as the reader, blocked because block_same_name=1!", username(cur_cl)); + return 0; + } + + if(!reader_slots_available(rdr, er)&& er->ecmlen > 0) // check free slots, er->ecmlen>0 trick to skip this test for matching readers in dvbapi module + { + return 0; + } + + // All checks done, reader is matching! + return (1); +} + +int32_t chk_caid(uint16_t caid, CAIDTAB *ctab) +{ + int32_t i; + + if (!ctab->ctnum) { return caid; } + + for(i = 0; i < ctab->ctnum; i++) + { + CAIDTAB_DATA *d = &ctab->ctdata[i]; + if((caid & d->mask) == d->caid) { return d->cmap ? d->cmap : caid; } + } + return -1; +} + +int32_t chk_caid_rdr(struct s_reader *rdr, uint16_t caid) +{ + if(is_network_reader(rdr) || rdr->typ == R_EMU) + { + return 1; // reader caid is not real caid + } + else if(rdr->caid == caid) + { + return 1; + } + return 0; +} + +int32_t chk_bcaid(ECM_REQUEST *er, CAIDTAB *ctab) +{ + int32_t caid; + caid = chk_caid(er->caid, ctab); + if(caid < 0) { return 0; } + er->caid = caid; + return 1; +} + +/** + * Check for NULL CWs + **/ +int32_t chk_is_null_CW(uint8_t cw[]) +{ + int8_t i; + for(i = 0; i < 16; i++) + { + if(cw[i]) + { return 0; } + } + return 1; +} + +/** + * Check for ecm request that expects half cw format + **/ +int8_t is_halfCW_er(ECM_REQUEST *er) +{ + if(caid_is_videoguard(er->caid) && (er->caid != 0x09C7 && er->caid != 0x09EF)) + { return 1; } + return 0; +} + +/** + * Check for wrong half CWs + **/ +int8_t chk_halfCW(ECM_REQUEST *er, uint8_t *cw) +{ + if(is_halfCW_er(er) && cw) + { + uint8_t cw15 = cw[15]; + if(get_odd_even(er) == 0x80 && cw[15] == 0xF0) { cw[15] = 0; } + + int8_t part1 = checkCWpart(cw, 0); + int8_t part2 = checkCWpart(cw, 1); + + // check for correct half cw format + if(part1 && part2){ cw[15] = cw15; return 0; } + + // check for correct cw position + if((get_odd_even(er) == 0x80 && part1 && !part2) // xxxxxxxx00000000 + ||(get_odd_even(er) == 0x81 && !part1 && part2)) // 00000000xxxxxxxx + { + return 1; + } + + cw[15] = cw15; + return 0; // not correct swapped cw + } + else + { + return 1; + } +} + + /** + * Check for ecm request that expects full cw format + **/ +int8_t is_fullCW_er(ECM_REQUEST *er) +{ + return !is_halfCW_er(er); +} + +/** + * Check for wrong full CWs + **/ +int8_t chk_fullCW(ECM_REQUEST *er, uint8_t *cw) +{ + if(!cw) + { + return -1; + } + + // jeśli to halfCW system — pomijamy + if(is_halfCW_er(er)) + { + return -1; + } + + int8_t part1 = checkCWpart(cw, 0); + int8_t part2 = checkCWpart(cw, 1); + + // full CW musi mieć obie części poprawne + if(part1 && part2) + { + return 1; // OK + } + + return 0; // ZŁY FULL CW +} + + +/** + * Check for NULL nodeid + **/ +int32_t chk_is_null_nodeid(uint8_t node_id[]) +{ + int8_t i; + for(i = 0; i < 8; i++) + { + if(node_id[i]) { return 0; } + } + return 1; +} + +// check if client structure is accessible +bool check_client(struct s_client *cl) +{ + if(cl && !cl->kill) { return true; } + return false; +} + +uint16_t caidvaluetab_get_value(CAIDVALUETAB *cv, uint16_t caid, uint16_t default_value) +{ + int32_t i; + for(i = 0; i < cv->cvnum; i++) + { + CAIDVALUETAB_DATA *cvdata = &cv->cvdata[i]; + if(cvdata->caid == caid || cvdata->caid == caid >> 8) { return cvdata->value; } + } + return default_value; +} + +int32_t chk_is_fakecw(uint8_t *cw) +{ + uint32_t i, is_fakecw = 0; + uint32_t idx = ((cw[0] & 0xF) << 4) | (cw[8] & 0xF); + + cs_readlock(__func__, &config_lock); + + for(i = 0; i < cfg.fakecws[idx].count; i++) + { + if(memcmp(cw, cfg.fakecws[idx].data[i].cw, 16) == 0) + { + is_fakecw = 1; + break; + } + } + cs_readunlock(__func__, &config_lock); + + return is_fakecw; +} + +#ifdef CS_CACHEEX_AIO +bool chk_nopushafter(uint16_t caid, CAIDVALUETAB *cv, int32_t ecm_time) +{ + uint16_t npa_time = caidvaluetab_get_value(cv, caid, 0); + if(npa_time && (ecm_time > npa_time)) + { + cs_log_dbg(D_CACHEEX, "REJECTED push: nopushafter %u < ecm_time %i", npa_time, ecm_time); + return 0; + } + else + return 1; +} +#endif diff --git a/oscam-chk.c.orig b/oscam-chk.c.orig new file mode 100644 index 0000000..3dd9536 --- /dev/null +++ b/oscam-chk.c.orig @@ -0,0 +1,1316 @@ +#define MODULE_LOG_PREFIX "chk" + +#include "globals.h" +#include "oscam-cache.h" +#include "oscam-chk.h" +#include "oscam-ecm.h" +#include "oscam-client.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "module-stat.h" +#include "oscam-reader.h" + +#define CS_NANO_CLASS 0xE2 +#define OK 1 +#define ERROR 0 + +uint32_t get_fallbacktimeout(uint16_t caid) +{ + uint32_t ftimeout = caidvaluetab_get_value(&cfg.ftimeouttab, caid, 0); + + if(ftimeout == 0) { ftimeout = cfg.ftimeout; } + + if(ftimeout < 100) { ftimeout = CS_CLIENT_TIMEOUT / 2; } + if(ftimeout >= cfg.ctimeout) { ftimeout = cfg.ctimeout - 100; } + + return ftimeout; +} + +static int32_t find_nano(uint8_t *ecm, int32_t l, uint8_t nano, int32_t s) +{ + uint8_t *snano; + + if(s >= l) { return 0; } + if(!s) { s = (ecm[4] == 0xD2) ? 12 : 9; } // tpsflag -> offset+3 + snano = ecm + s; + + while((*snano != nano) && (s < l)) + { + if(*snano == 0xEA) { return 0; } + snano++; + s++; + } + + return (s < l) ? ++s : 0; +} + +static int32_t chk_class(ECM_REQUEST *er, CLASSTAB *clstab, const char *type, const char *name) +{ + int32_t i, j, an, cl_n, l; + uint8_t ecm_class; + + if(er->caid != 0x0500 && er->caid != 0x4AE1) { return 1; } + if(!clstab->bn && !clstab->an) { return 1; } + + j = an = cl_n = 0; + + if(er->caid == 0x0500) + { + while((j = find_nano(er->ecm, er->ecmlen, CS_NANO_CLASS, j)) > 0) + { + l = er->ecm[j]; + if(l + j > er->ecmlen) { continue; } // skip, this is not a valid class identifier! + + ecm_class = er->ecm[j + l]; + cs_log_dbg(D_CLIENT, "ecm class=%02X", ecm_class); + + for(i = 0; i < clstab->bn; i++) // search in blocked + { + if(ecm_class == clstab->bclass[i]) + { + cs_log_dbg(D_CLIENT, "class %02X rejected by %s '%s' !%02X filter", + ecm_class, type, name, ecm_class); + return 0; + } + } + cl_n++; + + for(i = 0; i < clstab->an; i++) // search in allowed + { + if(ecm_class == clstab->aclass[i]) + { + an++; + break; + } + } + j += l; + } + } + else + { + if(er->prid != 0x11 || er->ecm[0] == 0) { return 1; } + + cl_n++; + ecm_class = er->ecm[5]; + cs_log_dbg(D_CLIENT, "ecm class=%02X", ecm_class); + + for(i = 0; i < clstab->bn; i++) // search in blocked + { + if(ecm_class == clstab->bclass[i]) + { + cs_log_dbg(D_CLIENT, "class %02X rejected by %s '%s' !%02X filter", + ecm_class, type, name, ecm_class); + return 0; + } + } + + for(i = 0; i < clstab->an; i++) // search in allowed + { + if(ecm_class == clstab->aclass[i]) + { + an++; + break; + } + } + } + + if(cl_n && clstab->an) + { + if(an) + { cs_log_dbg(D_CLIENT, "ECM classes allowed by %s '%s' filter", type, name); } + else + { + cs_log_dbg(D_CLIENT, "ECM classes don't match %s '%s' filter, rejecting", type, name); + return 0; + } + } + + return 1; +} + +int32_t chk_srvid_match(ECM_REQUEST *er, SIDTAB *sidtab) +{ + int32_t i, rc = 0; + + if(!sidtab->num_caid) + { rc |= 1; } + else + for(i = 0; (i < sidtab->num_caid) && (!(rc & 1)); i++) + if(er->caid == sidtab->caid[i]) { rc |= 1; } + + if(!er->prid || !sidtab->num_provid) + { rc |= 2; } + else + for(i = 0; (i < sidtab->num_provid) && (!(rc & 2)); i++) + if(er->prid == sidtab->provid[i]) { rc |= 2; } + + if(!sidtab->num_srvid) + { rc |= 4; } + else + for(i = 0; (i < sidtab->num_srvid) && (!(rc & 4)); i++) + if(er->srvid == sidtab->srvid[i]) { rc |= 4; } + + return (rc == 7); +} + +#ifdef CS_CACHEEX_AIO +int32_t chk_srvid_disablecrccws_only_for_exception(ECM_REQUEST *er) +{ + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->disablecrccws_only_for_exception && (sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) && chk_srvid_match(er, sidtab)) + { + return(1); + } + } + return(0); +} + +int32_t chk_srvid_no_wait_time(ECM_REQUEST *er) +{ + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->no_wait_time && (sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) && chk_srvid_match(er, sidtab)) + { + return(1); + } + } + return(0); +} + +int32_t chk_srvid_localgenerated_only_exception(ECM_REQUEST *er) +{ + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->lg_only_exception && (sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) && chk_srvid_match(er, sidtab)) + { + return(1); + } + } + return(0); +} +#endif + +int32_t chk_srvid(struct s_client *cl, ECM_REQUEST *er) +{ + int32_t nr, rc = 0; + SIDTAB *sidtab; + + if(!cl->sidtabs.ok) + { + if(!cl->sidtabs.no) { return (1); } + rc = 1; + } + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) + { + if((cl->sidtabs.no & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { return (0); } + + if((cl->sidtabs.ok & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { rc = 1; } + } + } + return (rc); +} + +int32_t has_srvid(struct s_client *cl, ECM_REQUEST *er) +{ + if(!cl->sidtabs.ok) + { return 0; } + + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_srvid) + { + if((cl->sidtabs.ok & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { return 1; } + } + } + return 0; +} + +int32_t has_lb_srvid(struct s_client *cl, ECM_REQUEST *er) +{ + if(!cl->lb_sidtabs.ok) + { return 0; } + + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if((cl->lb_sidtabs.ok & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { return 1; } + } + return 0; +} + +int32_t chk_srvid_match_by_caid_prov(uint16_t caid, uint32_t provid, SIDTAB *sidtab) +{ + int32_t i, rc = 0; + + if(!sidtab->num_caid) + { rc |= 1; } + else + for(i = 0; (i < sidtab->num_caid) && (!(rc & 1)); i++) + if(caid == sidtab->caid[i]) { rc |= 1; } + + if(!sidtab->num_provid) + { rc |= 2; } + else + for(i = 0; (i < sidtab->num_provid) && (!(rc & 2)); i++) + if(provid == sidtab->provid[i]) { rc |= 2; } + + return (rc == 3); +} + +int32_t chk_srvid_by_caid_prov(struct s_client *cl, uint16_t caid, uint32_t provid) +{ + int32_t nr, rc = 0; + SIDTAB *sidtab; + + if(!cl->sidtabs.ok) + { + if(!cl->sidtabs.no) { return (1); } + rc = 1; + } + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_caid | sidtab->num_provid) + { + if((cl->sidtabs.no & ((SIDTABBITS)1 << nr)) && !sidtab->num_srvid && + (chk_srvid_match_by_caid_prov(caid, provid, sidtab))) + { return (0); } + + if((cl->sidtabs.ok & ((SIDTABBITS)1 << nr)) && + (chk_srvid_match_by_caid_prov(caid, provid, sidtab))) + { rc = 1; } + } + } + return (rc); +} + +int32_t chk_srvid_by_caid_prov_rdr(struct s_reader *rdr, uint16_t caid, uint32_t provid) +{ + int32_t nr, rc = 0; + SIDTAB *sidtab; + + if(!rdr->sidtabs.ok) + { + if(!rdr->sidtabs.no) { return (1); } + rc = 1; + } + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_caid | sidtab->num_provid) + { + if((rdr->sidtabs.no & ((SIDTABBITS)1 << nr)) && !sidtab->num_srvid && + (chk_srvid_match_by_caid_prov(caid, provid, sidtab))) + { return (0); } + + if((rdr->sidtabs.ok & ((SIDTABBITS)1 << nr)) && + (chk_srvid_match_by_caid_prov(caid, provid, sidtab))) + { rc = 1; } + } + } + return (rc); +} + +int32_t chk_is_betatunnel_caid(uint16_t caid) +{ + if(caid == 0x1702 || caid == 0x1722) { return 1; } + if(caid == 0x1801 || caid == 0x1833 || caid == 0x1834 || caid == 0x1835) { return 2; } + return 0; +} + +uint16_t chk_on_btun(uint8_t chk_sx, struct s_client *cl, ECM_REQUEST *er) +{ + if(chk_is_betatunnel_caid(er->caid)) + { + int32_t i; + TUNTAB *ttab; + ttab = &cl->ttab; + + if(ttab->ttdata) + { + for(i = 0; i < ttab->ttnum; i++) + { + if(er->caid == ttab->ttdata[i].bt_caidfrom) + { + if(er->srvid == ttab->ttdata[i].bt_srvid) { return ttab->ttdata[i].bt_caidto; } + if(chk_sx && ttab->ttdata[i].bt_srvid == 0xFFFF) { return ttab->ttdata[i].bt_caidto; } + if(!chk_sx && !ttab->ttdata[i].bt_srvid) { return ttab->ttdata[i].bt_caidto; } + } + } + } + + if(chk_sx) + return lb_get_betatunnel_caid_to(er); + } + return 0; +} + +// server filter for newcamd +int32_t chk_sfilter(ECM_REQUEST *er, PTAB *ptab) +{ +#ifdef MODULE_NEWCAMD + int32_t i, j, pi, rc = 1; + uint16_t caid, scaid; + uint32_t prid, sprid; + + if(!ptab) { return (1); } + struct s_client *cur_cl = cur_client(); + + caid = er->caid; + prid = er->prid; + pi = cur_cl->port_idx; + + if(cfg.ncd_mgclient) + { return 1; } + + if(ptab->nports && ptab->ports[pi].ncd && ptab->ports[pi].ncd->ncd_ftab.nfilts) + { + for(rc = j = 0; (!rc) && (j < ptab->ports[pi].ncd->ncd_ftab.nfilts); j++) + { + scaid = ptab->ports[pi].ncd->ncd_ftab.filts[j].caid; + if(caid == 0 || (caid != 0 && caid == scaid)) + { + for(i = 0; (!rc) && i < ptab->ports[pi].ncd->ncd_ftab.filts[j].nprids; i++) + { + sprid = ptab->ports[pi].ncd->ncd_ftab.filts[j].prids[i]; + cs_log_dbg(D_CLIENT, "trying server filter %04X@%06X", scaid, sprid); + if(prid == sprid) + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by server filter %04X@%06X", + caid, prid, scaid, sprid); + } + } + } + } + + if(!rc) + { + cs_log_dbg(D_CLIENT, "no match, %04X@%06X rejected by server filters", caid, prid); + snprintf(er->msglog, MSGLOGSIZE, "no server match %04X@%06X", caid, (uint32_t) prid); + + if(!er->rcEx) { er->rcEx = (E1_LSERVER << 4) | E2_IDENT; } + return (rc); + } + } + return (rc); +#else + (void)er; + (void)ptab; + return 1; +#endif +} + +static int32_t chk_chid(ECM_REQUEST *er, FTAB *fchid, char *type, char *name) +{ + int32_t rc = 1, i, j, found_caid = 0; + if(!fchid->nfilts) { return 1; } + if(er->chid == 0 && er->ecm[0] == 0) { return 1; } // skip empty ecm, chid 00 to avoid no matching readers in dvbapi + + for(i = rc = 0; (!rc) && i < fchid->nfilts; i++) + { + if(er->caid == fchid->filts[i].caid) + { + found_caid = 1; + for(j = 0; (!rc) && j < fchid->filts[i].nprids; j++) + { + cs_log_dbg(D_CLIENT, "trying %s '%s' CHID filter %04X:%04X", + type, name, fchid->filts[i].caid, fchid->filts[i].prids[j]); + + if(er->chid == fchid->filts[i].prids[j]) + { + cs_log_dbg(D_CLIENT, "%04X:%04X allowed by %s '%s' CHID filter %04X:%04X", + er->caid, er->chid, type, name, fchid->filts[i].caid, fchid->filts[i].prids[j]); + rc = 1; + } + } + } + } + + if(!rc) + { + if(found_caid) + cs_log_dbg(D_CLIENT, "no match, %04X:%04X rejected by %s '%s' CHID filter(s)", + er->caid, er->chid, type, name); + else + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X:%04X allowed by %s '%s' CHID filter, CAID not spezified", + er->caid, er->chid, type, name); + } + } + return (rc); +} + +int32_t chk_ident_filter(uint16_t rcaid, uint32_t rprid, FTAB *ftab) +{ + int32_t i, j, rc = 1; + uint16_t caid = 0; + uint32_t prid = 0; + + if(ftab->nfilts) + { + for(rc = i = 0; (!rc) && (i < ftab->nfilts); i++) + { + caid = ftab->filts[i].caid; + if((caid != 0 && caid == rcaid) || caid == 0) + { + for(j = 0; (!rc) && (j < ftab->filts[i].nprids); j++) + { + prid = ftab->filts[i].prids[j]; + if(prid == rprid) + { + rc=1; + } + } + } + } + + if(!rc) + { return 0; } + } + + return(rc); +} + +int32_t chk_ufilters(ECM_REQUEST *er) +{ + int32_t i, j, rc = 1; + uint16_t ucaid; + uint32_t uprid; + struct s_client *cur_cl = cur_client(); + + if(cur_cl->ftab.nfilts) + { + FTAB *f = &cur_cl->ftab; + for(i = rc = 0; (!rc) && (i < f->nfilts); i++) + { + ucaid = f->filts[i].caid; + if(er->caid == 0 || ucaid == 0 || (er->caid != 0 && er->caid == ucaid)) + { + if (er->prid == 0) + { + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by user '%s' filter caid %04X prid %06X", + er->caid, er->prid, cur_cl->account->usr, ucaid, 0); + rc = 1; + break; + } + + for(j = rc = 0; (!rc) && (j < f->filts[i].nprids); j++) + { + uprid = f->filts[i].prids[j]; + cs_log_dbg(D_CLIENT, "trying user '%s' filter %04X@%06X", cur_cl->account->usr, ucaid, uprid); + + if(er->prid == uprid) + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by user '%s' filter %04X@%06X", + er->caid, er->prid, cur_cl->account->usr, ucaid, uprid); + } + } + } + } + + if(!rc) + { + cs_log_dbg(D_CLIENT, "no match, %04X@%06X rejected by user '%s' filters", + er->caid, er->prid, cur_cl->account->usr); + + snprintf(er->msglog, MSGLOGSIZE, "no card support %04X@%06X", er->caid, (uint32_t) er->prid); + + if(!er->rcEx) { er->rcEx = (E1_USER << 4) | E2_IDENT; } + return (rc); + } + } + + if(!(rc = chk_class(er, &cur_cl->cltab, "user", cur_cl->account->usr))) + { + if(!er->rcEx) { er->rcEx = (E1_USER << 4) | E2_CLASS; } + } + else if(!(rc = chk_chid(er, &cur_cl->fchid, "user", cur_cl->account->usr))) + { + if(!er->rcEx) { er->rcEx = (E1_USER << 4) | E2_CHID; } + } + + if(rc) { er->rcEx = 0; } + + return (rc); +} + +int32_t chk_rsfilter(struct s_reader *reader, ECM_REQUEST *er) +{ + int32_t i, rc = 1; + uint16_t caid; + uint32_t prid; + + if(reader->ncd_disable_server_filt) + { + cs_log_dbg(D_CLIENT, "%04X@%06X allowed - server filters disabled", er->caid, er->prid); + return 1; + } + + rc = 0; + caid = reader->caid; + if(caid == er->caid) + { + for(i = 0; (!rc) && (i < reader->nprov); i++) + { + prid = (uint32_t)((reader->prid[i][1] << 16) | (reader->prid[i][2] << 8) | (reader->prid[i][3])); + cs_log_dbg(D_CLIENT, "trying server '%s' filter %04X@%06X", reader->device, caid, prid); + + if(prid == er->prid || !er->prid) + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by server '%s' filter %04X@%06X", + er->caid, er->prid, reader->device, caid, prid); + } + } + } + + if(!rc) + { + cs_log_dbg(D_CLIENT, "no match, %04X@%06X rejected by server '%s' filters", + er->caid, er->prid, reader->device); + + if(!er->rcEx) { er->rcEx = (E1_SERVER << 4) | E2_IDENT; } + return 0; + } + + return (rc); +} + +int32_t chk_rfilter2(uint16_t rcaid, uint32_t rprid, struct s_reader *rdr) +{ + int32_t i, j, rc = 1; + uint16_t caid = 0; + uint32_t prid = 0; + + if(rdr->ftab.nfilts) + { + for(rc = i = 0; (!rc) && (i < rdr->ftab.nfilts); i++) + { + caid = rdr->ftab.filts[i].caid; + if((caid != 0 && caid == rcaid) || caid == 0) + { + for(j = 0; (!rc) && (j < rdr->ftab.filts[i].nprids); j++) + { + prid = rdr->ftab.filts[i].prids[j]; + cs_log_dbg(D_CLIENT, "trying reader '%s' filter %04X@%06X", rdr->label, caid, prid); + + if(prid == rprid) + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by reader '%s' filter %04X@%06X", + rcaid, rprid, rdr->label, caid, prid); + } + } + } + } + + if(!rc) + { + cs_log_dbg(D_CLIENT, "no match, %04X@%06X rejected by reader '%s' filters", + rcaid, rprid, rdr->label); + return 0; + } + } + + return (rc); +} + +static int32_t chk_rfilter(ECM_REQUEST *er, struct s_reader *rdr) +{ + return chk_rfilter2(er->caid, er->prid, rdr); +} + +int32_t chk_ctab(uint16_t caid, CAIDTAB *ctab) +{ + if(!caid || !ctab->ctnum) + { return 1; } + + int32_t i; + for(i = 0; i < ctab->ctnum; i++) + { + CAIDTAB_DATA *d = &ctab->ctdata[i]; + if(!d->caid) + { + return 0; + } + if((caid & d->mask) == d->caid) + { return 1; } + } + return 0; +} + +int32_t chk_ctab_ex(uint16_t caid, CAIDTAB *ctab) +{ + if(!caid || !ctab->ctnum) + { return 0; } + + int32_t i; + for(i = 0; i < ctab->ctnum; i++) + { + CAIDTAB_DATA *d = &ctab->ctdata[i]; + if(!d->caid) + { + return 0; + } + + if((caid & d->mask) == d->caid) + { + return 1; + } + } + return 0; +} + +uint8_t is_localreader(struct s_reader *rdr, ECM_REQUEST *er) // to be used for LB/reader selections checks only +{ + if(!rdr) return 0; + + if(!is_network_reader(rdr)) + { + return 1; + } + + if(!rdr->localcards.nfilts) { return 0; } + + int32_t i, k; + for(i = 0; i < rdr->localcards.nfilts; i++) + { + uint16_t tcaid = rdr->localcards.filts[i].caid; + if(tcaid && tcaid == er->caid) // caid match + { + int32_t nprids = rdr->localcards.filts[i].nprids; + if(!nprids) // No Provider -> Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid = rdr->localcards.filts[i].prids[k]; + if(prid == er->prid) // Provider matches + { + return 1; + } + } + } + } + + return 0; +} + +uint8_t chk_is_fixed_fallback(struct s_reader *rdr, ECM_REQUEST *er) +{ + if(!rdr->fallback && !rdr->fallback_percaid.nfilts) { return 0; } + + if(!rdr->fallback_percaid.nfilts) + { + if(rdr->fallback) + { return 1; } + } + + int32_t i, k; + for(i = 0; i < rdr->fallback_percaid.nfilts; i++) + { + uint16_t tcaid = rdr->fallback_percaid.filts[i].caid; + if(tcaid && (tcaid == er->caid || (tcaid < 0x0100 && (er->caid >> 8) == tcaid))) // caid match + { + int32_t nprids = rdr->fallback_percaid.filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid = rdr->fallback_percaid.filts[i].prids[k]; + if(prid == er->prid) // Provider matches + { + return 1; + } + } + } + } + + return 0; +} + +#ifdef CS_CACHEEX_AIO +uint8_t chk_lg_only(ECM_REQUEST *er, FTAB *lg_only_ftab) +{ + int32_t i, k; + + if(!lg_only_ftab->nfilts) + return 0; + + for(i = 0; i < lg_only_ftab->nfilts; i++) + { + uint16_t tcaid = lg_only_ftab->filts[i].caid; + if(tcaid && (tcaid == er->caid || (tcaid < 0x0100 && (er->caid >> 8) == tcaid))) // caid match + { + int32_t nprids = lg_only_ftab->filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid = lg_only_ftab->filts[i].prids[k]; + if(prid == NO_PROVID_VALUE || prid == er->prid) // Provider matches + { + return 1; + } + } + } + } + + return 0; +} + +uint8_t chk_lg_only_cp(uint16_t caid, uint32_t prid, FTAB *lg_only_ftab) +{ + int32_t i, k; + + if(!lg_only_ftab->nfilts) + return 0; + + for(i = 0; i < lg_only_ftab->nfilts; i++) + { + uint16_t tcaid = lg_only_ftab->filts[i].caid; + if(tcaid && (tcaid == caid || (tcaid < 0x0100 && (caid >> 8) == tcaid))) // caid match + { + int32_t nprids = lg_only_ftab->filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t fprid = lg_only_ftab->filts[i].prids[k]; + if(fprid == NO_PROVID_VALUE || fprid == prid) // Provider matches + { + return 1; + } + } + } + } + + return 0; +} +#endif + +uint8_t chk_has_fixed_fallback(ECM_REQUEST *er) +{ + struct s_ecm_answer *ea; + struct s_reader *rdr; + int32_t n_falb = 0; + for(ea = er->matching_rdr; ea; ea = ea->next) + { + rdr = ea->reader; + if(chk_is_fixed_fallback(rdr, er)) + { n_falb++; } + } + return n_falb; +} + +uint8_t chk_if_ignore_checksum(ECM_REQUEST *er, FTAB *disablecrc_only_for) +{ + if(!disablecrc_only_for->nfilts) { return 0; } + + int32_t i, k; + for(i = 0; i < disablecrc_only_for->nfilts; i++) + { + uint16_t tcaid = disablecrc_only_for->filts[i].caid; + if(tcaid && (tcaid == er->caid || (tcaid < 0x0100 && (er->caid >> 8) == tcaid))) // caid match + { + int32_t nprids = disablecrc_only_for->filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid =disablecrc_only_for->filts[i].prids[k]; + if(prid == er->prid) // Provider matches + { return 1; } + } + } + } + + return 0; +} + +int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr) +{ + // simple checks first: + if(!er || !rdr) + { return (0); } + + // reader active? + struct s_client *cl = rdr->client; + if(!cl || !rdr->enable) + { return (0); } + + // if physical reader a card needs to be inserted + if(!is_network_reader(rdr) && rdr->card_status != CARD_INSERTED) + { return (0); } + + // Checking connected & group valid: + struct s_client *cur_cl = er->client; //cur_client(); + +#ifdef CS_CACHEEX + // Cacheex=3 defines a Cacheex-only reader. never match them. + if(rdr->cacheex.mode == 3) + { return (0); } + if(rdr->cacheex.mode == 2 && !rdr->cacheex.allow_request) + { return (0); } +#endif + + if(!(rdr->grp & cur_cl->grp)) + { return (0); } + + // Checking caids: + if((!er->ocaid || !chk_ctab(er->ocaid, &rdr->ctab)) && !chk_ctab(er->caid, &rdr->ctab)) + { + cs_log_dbg(D_TRACE, "caid %04X not found in caidlist reader %s", er->caid, rdr->label); + return 0; + } + + if(!(rdr->typ == R_EMU) && !is_network_reader(rdr) && ((rdr->caid >> 8) != ((er->caid >> 8) & 0xFF) && (rdr->caid >> 8) != ((er->ocaid >> 8) & 0xFF))) + { + if (!rdr->csystem) + { return 0; } + + int i, caid_found = 0; + for(i = 0; rdr->csystem->caids[i]; i++) + { + uint16_t cs_caid = rdr->csystem->caids[i]; + if(!cs_caid) + { continue; } + + if(cs_caid == er->caid || cs_caid == er->ocaid) + { + caid_found = 1; + break; + } + } + + if(!caid_found) + { return 0; } + } + + // Supports long ecms? + if(er->ecmlen > 255 && is_network_reader(rdr) && !rdr->ph.large_ecm_support) + { + cs_log_dbg(D_TRACE, "no large ecm support (l=%d) for reader %s", er->ecmlen, rdr->label); + return 0; + } + + // Checking services: + if(!chk_srvid(rdr->client, er)) + { + cs_log_dbg(D_TRACE, "service %04X not matching reader %s", er->srvid, rdr->label); + return (0); + } + + // Checking ident: + if(!(rdr->typ == R_EMU && caid_is_biss(er->caid)) && !chk_rfilter(er, rdr)) + { + cs_log_dbg(D_TRACE, "r-filter reader %s", rdr->label); + return (0); + } + + // Check ECM nanos: + if(!chk_class(er, &rdr->cltab, "reader", rdr->label)) + { + cs_log_dbg(D_TRACE, "class filter reader %s", rdr->label); + return (0); + } + + // CDS NL: check for right seca type + if(!is_network_reader(rdr) && er->caid == 0x100 && er->prid == 0x00006a + && !(er->ecm[8] == 0x00 && er->ecm[9] == 0x00)) // no empty ecm + { + if(er->ecm[8] == 0x00 && rdr->secatype == 2) + { + cs_log_dbg(D_TRACE, "Error: this is a nagra/mediaguard3 ECM and readertype is seca2!"); + return 0; // we dont send a nagra/mediaguard3 ecm to a seca2 reader! + } + + if((er->ecm[8] == 0x10) && (er->ecm[9] == 0x01) && rdr->secatype == 3) + { + cs_log_dbg(D_TRACE, "Error: this is a seca2 ECM and readertype is nagra/mediaguard3!"); + return 0; // we dont send a seca2 ecm to a nagra/mediaguard3 reader! + } + } + + // CDS NL: check for right seca type by ECMPID + if(!is_network_reader(rdr) && er->caid == 0x100 && er->prid == 0x00006a) + { + if(rdr->secatype == 2 && er->pid >> 8 == 7) + { + cs_log_dbg(D_TRACE, "Error: this is a nagra/mediaguard3 ECM and readertype is seca2!"); + return 0; // we dont send a nagra/mediaguard3 ecm to a seca2 reader! + } + + if(rdr->secatype == 3 && er->pid >> 8 == 6) + { + cs_log_dbg(D_TRACE, "Error: this is a seca2 ECM and readertype is nagra/mediaguard3!"); + return 0; // we dont send a seca2 ecm to a nagra/mediaguard3 reader! + } + } + + // Checking chid: + if(!chk_chid(er, &rdr->fchid, "reader", rdr->label)) + { + cs_log_dbg(D_TRACE, "chid filter reader %s", rdr->label); + return (0); + } + + // Schlocke reader-defined function, reader-self-check + if(rdr->ph.c_available && !rdr->ph.c_available(rdr, AVAIL_CHECK_CONNECTED, er)) + { + cs_log_dbg(D_TRACE, "reader unavailable %s", rdr->label); + return 0; + } + + // Checking entitlements: + if(ll_count(rdr->ll_entitlements) > 0 && !(rdr->typ == R_EMU)) + { + LL_ITER itr = ll_iter_create(rdr->ll_entitlements); + S_ENTITLEMENT *item; + int8_t found = 0; + + while((item = ll_iter_next(&itr))) + { + if(item->caid != er->caid) continue; // skip wrong caid! + if(item->type == 7) continue; // skip seca-admin type (provid 000000) since its not used for decoding! + if(er->prid && item->provid && er->prid != item->provid) continue; // skip non matching provid! + if(!er->prid && caid_is_seca(er->caid)) continue; // dont accept requests without provid for seca cas. + if(!er->prid && caid_is_viaccess(er->caid)) continue; // dont accept requests without provid for viaccess cas + if(!er->prid && caid_is_cryptoworks(er->caid)) continue; // dont accept requests without provid for cryptoworks cas + found =1; + break; + } + + if(!found && er->ecm[0]) // ecmrequest can get corrected provid parsed from payload in ecm + { + cs_log_dbg(D_TRACE, "entitlements check failed on reader %s", rdr->label); + return 0; + } + } + + // Checking ecmlength: + if(rdr->ecm_whitelist.ewnum && er->ecmlen) + { + int32_t i; + int8_t ok = 0, foundident = 0; + + for (i = 0; i < rdr->ecm_whitelist.ewnum; i++) + { + ECM_WHITELIST_DATA *d = &rdr->ecm_whitelist.ewdata[i]; + if ((d->caid == 0 || d->caid == er->caid) && (d->ident == 0 || d->ident == er->prid)) + { + foundident = 1; + if (d->len == er->ecmlen) + { + ok = 1; + break; + } + } + } + + if(foundident == 1 && ok == 0) + { + cs_log_dbg(D_TRACE, "ECM is not in ecmwhitelist of reader %s.", rdr->label); + rdr->ecmsfilteredlen += 1; + rdr->webif_ecmsfilteredlen += 1; + return (0); + } + } + + // ECM Header Check + if(rdr->ecm_hdr_whitelist.ehdata && er->ecmlen) + { + int8_t byteok = 0; + int8_t entryok = 0; + int8_t foundcaid = 0; + int8_t foundprovid = 0; + int16_t len = 0; + int32_t i = 0; + int8_t skip = 0; + int32_t r; + + for(r = 0; r < rdr->ecm_hdr_whitelist.ehnum; r++) + { + ECM_HDR_WHITELIST_DATA *tmp = &rdr->ecm_hdr_whitelist.ehdata[r]; + skip = 0; + byteok = 0; + entryok = 0; + + if(tmp->caid == 0 || tmp->caid == er->caid) + { + foundcaid = 1; //-> caid was in list + //rdr_log_dbg(rdr, D_READER, "Headerwhitelist: found matching CAID: %04X in list", tmp->caid); + + if(tmp->provid == 0 || tmp->provid == er->prid) + { + foundprovid = 1; //-> provid was in list + //rdr_log_dbg(rdr, D_READER, "Headerwhitelist: found matching Provid: %06X in list", tmp->provid); + + len = tmp->len; + for(i = 0; i < len / 2; i++) + { + if(tmp->header[i] == er->ecm[i]) + { + byteok = 1; + //rdr_log_dbg(rdr, D_READER, "ECM Byte: %i of ECMHeaderwhitelist is correct. (%02X = %02X Headerlen: %i)", i, er->ecm[i], tmp->header[i], len/2); + } + else + { + byteok = 0; + //rdr_log_dbg(rdr, D_READER, "ECM Byte: %i of ECMHeaderwhitelist is not valid. (%02X != %02X Headerlen: %i)", i, er->ecm[i], tmp->header[i], len/2); + entryok = 0; + break; + } + + if(i == len / 2 - 1 && byteok == 1) + { + entryok = 1; + } + } + } + else + { + //rdr_log_dbg(rdr, D_READER, "ECMHeaderwhitelist: Provid: %06X not found in List-Entry -> skipping check", er->prid); + skip = 1; + continue; + } + } + else + { + //rdr_log_dbg(rdr, D_READER, "ECMHeaderwhitelist: CAID: %04X not found in List-Entry -> skipping check", er->caid); + skip = 1; + continue; + } + + if(entryok == 1) + { + break; + } + } + + if(foundcaid == 1 && foundprovid == 1 && byteok == 1 && entryok == 1) + { + //cs_log("ECM for %04X@%06X:%04X is valid for ECMHeaderwhitelist of reader %s.", er->caid, er->prid, er->srvid, rdr->label); + } + else + { + if(skip == 0 || (foundcaid == 1 && foundprovid == 1 && entryok == 0 && skip == 1)) + { + cs_log_dump_dbg(D_TRACE, er->ecm, er->ecmlen, "following ECM %04X@%06X:%04X was filtered by ECMHeaderwhitelist of Reader %s from User %s because of not matching Header:", er->caid, er->prid, er->srvid, rdr->label, username(er->client)); + rdr->ecmsfilteredhead += 1; + rdr->webif_ecmsfilteredhead += 1; + return (0); + } + } + } + + // Simple ring connection check: + + // Check ip source+dest: + if(cfg.block_same_ip && IP_EQUAL(cur_cl->ip, rdr->client->ip) && get_module(cur_cl)->listenertype != LIS_DVBAPI && is_network_reader(rdr)) + { + rdr_log_dbg(rdr, D_TRACE, "User (%s) has the same ip (%s) as the reader, blocked because block_same_ip=1!", username(cur_cl), cs_inet_ntoa(rdr->client->ip)); + return 0; + } + + if(cfg.block_same_name && strcmp(username(cur_cl), rdr->label) == 0) + { + rdr_log_dbg(rdr, D_TRACE, "User (%s) has the same name as the reader, blocked because block_same_name=1!", username(cur_cl)); + return 0; + } + + if(!reader_slots_available(rdr, er)&& er->ecmlen > 0) // check free slots, er->ecmlen>0 trick to skip this test for matching readers in dvbapi module + { + return 0; + } + + // All checks done, reader is matching! + return (1); +} + +int32_t chk_caid(uint16_t caid, CAIDTAB *ctab) +{ + int32_t i; + + if (!ctab->ctnum) { return caid; } + + for(i = 0; i < ctab->ctnum; i++) + { + CAIDTAB_DATA *d = &ctab->ctdata[i]; + if((caid & d->mask) == d->caid) { return d->cmap ? d->cmap : caid; } + } + return -1; +} + +int32_t chk_caid_rdr(struct s_reader *rdr, uint16_t caid) +{ + if(is_network_reader(rdr) || rdr->typ == R_EMU) + { + return 1; // reader caid is not real caid + } + else if(rdr->caid == caid) + { + return 1; + } + return 0; +} + +int32_t chk_bcaid(ECM_REQUEST *er, CAIDTAB *ctab) +{ + int32_t caid; + caid = chk_caid(er->caid, ctab); + if(caid < 0) { return 0; } + er->caid = caid; + return 1; +} + +/** + * Check for NULL CWs + **/ +int32_t chk_is_null_CW(uint8_t cw[]) +{ + int8_t i; + for(i = 0; i < 16; i++) + { + if(cw[i]) + { return 0; } + } + return 1; +} + +/** + * Check for ecm request that expects half cw format + **/ +int8_t is_halfCW_er(ECM_REQUEST *er) +{ + if(caid_is_videoguard(er->caid) && (er->caid != 0x09C7 && er->caid != 0x09EF)) + { return 1; } + return 0; +} + +/** + * Check for wrong half CWs + **/ +int8_t chk_halfCW(ECM_REQUEST *er, uint8_t *cw) +{ + if(is_halfCW_er(er) && cw) + { + uint8_t cw15 = cw[15]; + if(get_odd_even(er) == 0x80 && cw[15] == 0xF0) { cw[15] = 0; } + + int8_t part1 = checkCWpart(cw, 0); + int8_t part2 = checkCWpart(cw, 1); + + // check for correct half cw format + if(part1 && part2){ cw[15] = cw15; return 0; } + + // check for correct cw position + if((get_odd_even(er) == 0x80 && part1 && !part2) // xxxxxxxx00000000 + ||(get_odd_even(er) == 0x81 && !part1 && part2)) // 00000000xxxxxxxx + { + return 1; + } + + cw[15] = cw15; + return 0; // not correct swapped cw + } + else + { + return 1; + } +} + +/** + * Check for NULL nodeid + **/ +int32_t chk_is_null_nodeid(uint8_t node_id[]) +{ + int8_t i; + for(i = 0; i < 8; i++) + { + if(node_id[i]) { return 0; } + } + return 1; +} + +// check if client structure is accessible +bool check_client(struct s_client *cl) +{ + if(cl && !cl->kill) { return true; } + return false; +} + +uint16_t caidvaluetab_get_value(CAIDVALUETAB *cv, uint16_t caid, uint16_t default_value) +{ + int32_t i; + for(i = 0; i < cv->cvnum; i++) + { + CAIDVALUETAB_DATA *cvdata = &cv->cvdata[i]; + if(cvdata->caid == caid || cvdata->caid == caid >> 8) { return cvdata->value; } + } + return default_value; +} + +int32_t chk_is_fakecw(uint8_t *cw) +{ + uint32_t i, is_fakecw = 0; + uint32_t idx = ((cw[0] & 0xF) << 4) | (cw[8] & 0xF); + + cs_readlock(__func__, &config_lock); + + for(i = 0; i < cfg.fakecws[idx].count; i++) + { + if(memcmp(cw, cfg.fakecws[idx].data[i].cw, 16) == 0) + { + is_fakecw = 1; + break; + } + } + cs_readunlock(__func__, &config_lock); + + return is_fakecw; +} + +#ifdef CS_CACHEEX_AIO +bool chk_nopushafter(uint16_t caid, CAIDVALUETAB *cv, int32_t ecm_time) +{ + uint16_t npa_time = caidvaluetab_get_value(cv, caid, 0); + if(npa_time && (ecm_time > npa_time)) + { + cs_log_dbg(D_CACHEEX, "REJECTED push: nopushafter %u < ecm_time %i", npa_time, ecm_time); + return 0; + } + else + return 1; +} +#endif diff --git a/oscam-chk.c.rej b/oscam-chk.c.rej new file mode 100644 index 0000000..9a39ab3 --- /dev/null +++ b/oscam-chk.c.rej @@ -0,0 +1,11 @@ +--- oscam-chk.c (revision 11871) ++++ oscam-chk.c (working copy) +@@ -1213,7 +1213,7 @@ + **/ + int8_t is_halfCW_er(ECM_REQUEST *er) + { +- if( caid_is_videoguard(er->caid) && (er->caid == 0x09C4 || er->caid == 0x098C || er->caid == 0x098D || er->caid == 0x0963 || er->caid == 0x09CD || er->caid == 0x0919 || er->caid == 0x093B || er->caid == 0x098E)) ++ if( caid_is_videoguard(er->caid) && (er->caid == 0x098C || er->caid == 0x098D || er->caid == 0x098E || er->caid == 0x09BD || er->caid == 0x0963 || er->caid == 0x09CD || er->caid == 0x0919 || er->caid == 0x093B)) + { return 1; } + return 0; + } diff --git a/oscam-chk.h b/oscam-chk.h new file mode 100644 index 0000000..38b3f3a --- /dev/null +++ b/oscam-chk.h @@ -0,0 +1,51 @@ +#ifndef OSCAM_CHK_H_ +#define OSCAM_CHK_H_ + +// betatunnel check (chk_on_btun) +#define SRVID_ZERO 0 // srvid + 0000 (used for service-filter bypass) +#define SRVID_MASK 1 // srvid + FFFF + +uint32_t get_fallbacktimeout(uint16_t caid); +int32_t ecm_ratelimit_check(struct s_reader *reader, ECM_REQUEST *er, int32_t reader_mode); +int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr); +uint8_t chk_if_ignore_checksum(ECM_REQUEST *er, FTAB *disablecrc_only_for); + +uint8_t is_localreader(struct s_reader *rdr, ECM_REQUEST *er); +uint8_t chk_is_fixed_fallback(struct s_reader *rdr, ECM_REQUEST *er); +uint8_t chk_has_fixed_fallback(ECM_REQUEST *er); +int32_t chk_srvid_match(ECM_REQUEST *er, SIDTAB *sidtab); +int32_t chk_srvid(struct s_client *cl, ECM_REQUEST *er); +int32_t has_srvid(struct s_client *cl, ECM_REQUEST *er); +int32_t has_lb_srvid(struct s_client *cl, ECM_REQUEST *er); +int32_t chk_srvid_match_by_caid_prov(uint16_t caid, uint32_t provid, SIDTAB *sidtab); +int32_t chk_srvid_by_caid_prov(struct s_client *cl, uint16_t caid, uint32_t provid); +int32_t chk_srvid_by_caid_prov_rdr(struct s_reader *rdr, uint16_t caid, uint32_t provid); +int32_t chk_is_betatunnel_caid(uint16_t caid); +uint16_t chk_on_btun(uint8_t chk_sx, struct s_client *cl, ECM_REQUEST *er); +int32_t chk_ident_filter(uint16_t rcaid, uint32_t rprid, FTAB *ftab); +int32_t chk_sfilter(ECM_REQUEST *er, PTAB *ptab); +int32_t chk_ufilters(ECM_REQUEST *er); +int32_t chk_rsfilter(struct s_reader *reader, ECM_REQUEST *er); +int32_t chk_rfilter2(uint16_t rcaid, uint32_t rprid, struct s_reader *rdr); +int32_t chk_ctab(uint16_t caid, CAIDTAB *ctab); +int32_t chk_ctab_ex(uint16_t caid, CAIDTAB *ctab); +int32_t chk_caid(uint16_t caid, CAIDTAB *ctab); +int32_t chk_caid_rdr(struct s_reader *rdr, uint16_t caid); +int32_t chk_bcaid(ECM_REQUEST *er, CAIDTAB *ctab); +int32_t chk_is_null_CW(uint8_t cw[]); +int8_t is_halfCW_er(ECM_REQUEST *er); +int8_t chk_halfCW(ECM_REQUEST *er, uint8_t *cw); +int32_t chk_is_null_nodeid(uint8_t node_id[]); +bool check_client(struct s_client *cl); +uint16_t caidvaluetab_get_value(CAIDVALUETAB *cv, uint16_t caid, uint16_t default_value); +int32_t chk_is_fakecw(uint8_t *cw); +#ifdef CS_CACHEEX_AIO +int32_t chk_srvid_disablecrccws_only_for_exception(ECM_REQUEST *er); +int32_t chk_srvid_no_wait_time(ECM_REQUEST *er); +int32_t chk_srvid_localgenerated_only_exception(ECM_REQUEST *er); +bool chk_nopushafter(uint16_t caid, CAIDVALUETAB *cv, int32_t ecm_time); +uint8_t chk_lg_only(ECM_REQUEST *er, FTAB *ftab); +uint8_t chk_lg_only_cp(uint16_t caid, uint32_t prid, FTAB *lg_only_ftab); +#endif +int32_t is_double_check_caid(ECM_REQUEST *er, FTAB *double_check_caid); +#endif diff --git a/oscam-client.c b/oscam-client.c new file mode 100644 index 0000000..5363c79 --- /dev/null +++ b/oscam-client.c @@ -0,0 +1,919 @@ +#define MODULE_LOG_PREFIX "client" + +#include "globals.h" + +#include "cscrypt/md5.h" +#include "module-anticasc.h" +#include "module-cccam.h" +#include "module-webif.h" +#include "oscam-array.h" +#include "oscam-conf-chk.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-failban.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" +#include "reader-common.h" +#include "oscam-chk.h" + +extern CS_MUTEX_LOCK fakeuser_lock; + +static char *processUsername; +static struct s_client *first_client_hashed[CS_CLIENT_HASHBUCKETS]; // Alternative hashed client list + +/* Gets the unique thread number from the client. Used in monitor and newcamd. */ +int32_t get_threadnum(struct s_client *client) +{ + struct s_client *cl; + int32_t count = 0; + + for(cl = first_client->next; cl; cl = cl->next) + { + if(cl->typ == client->typ) + { + count++; + } + + if(cl == client) + { + return count; + } + } + return 0; +} + +struct s_auth *get_account_by_name(char *name) +{ + struct s_auth *account; + for(account = cfg.account; (account); account = account->next) + { + if(streq(name, account->usr)) + { + return account; + } + } + return NULL; +} + +int8_t is_valid_client(struct s_client *client) +{ + struct s_client *cl; + int32_t bucket = (uintptr_t)client / 16 % CS_CLIENT_HASHBUCKETS; + + for(cl = first_client_hashed[bucket]; cl; cl = cl->nexthashed) + { + if(cl == client) + { + return 1; + } + } + return 0; +} + +const char *remote_txt(void) +{ + return cur_client()->typ == 'c' ? "client" : "remote server"; +} + +const char *client_get_proto(struct s_client *cl) +{ + const char *ctyp = "unknown"; + switch(cl->typ) + { + case 's': + ctyp = "server"; + break; + + case 'h': + ctyp = "http"; + break; + + case 'p': + case 'r': + ctyp = reader_get_type_desc(cl->reader, 1); + break; + +#ifdef CS_ANTICASC + case 'a': + ctyp = "anticascader"; + break; + +#endif + case 'c': + if(cccam_client_extended_mode(cl)) + { + ctyp = "cccam_ext"; + break; + } /* fallthrough */ + + default: + ctyp = get_module(cl)->desc; + break; + } + return ctyp; +} + +static void cs_fake_client(struct s_client *client, char *usr, int32_t uniq, IN_ADDR_T ip) +{ + /* Uniq = 1: only one connection per user + * + * Uniq = 2: set (new connected) user only to fake if source + * ip is different (e.g. for newcamd clients with + * different CAID's -> Ports) + * + * Uniq = 3: only one connection per user, but only the last + * login will survive (old mpcs behavior) + * + * Uniq = 4: set user only to fake if source ip is + * different, but only the last login will survive + */ + + struct s_client *cl; + struct s_auth *account; + uint32_t con_count = 1; + cs_writelock(__func__, &fakeuser_lock); + + for(cl = first_client->next; cl; cl = cl->next) + { + account = cl->account; + if(cl != client && cl->typ == 'c' && !cl->dup && account && streq(account->usr, usr) + && uniq < 5 && ((uniq % 2) || !IP_EQUAL(cl->ip, ip))) + { + char buf[20]; + + con_count++; + if(con_count <= account->max_connections) + { + continue; + } + + if(uniq == 3 || uniq == 4) + { + cl->dup = 1; + cl->aureader_list = NULL; + cs_strncpy(buf, cs_inet_ntoa(cl->ip), sizeof(buf)); + cs_log("client(%8lX) duplicate user '%s' from %s (prev %s) set to fake (uniq=%d)", + (unsigned long)cl->thread, usr, cs_inet_ntoa(ip), buf, uniq); + + if(cl->failban & BAN_DUPLICATE) + { + cs_add_violation(cl, usr); + } + + if(cfg.dropdups) + { + cs_writeunlock(__func__, &fakeuser_lock); + cs_sleepms(120); // sleep a bit to prevent against saturation from fast reconnecting clients + kill_thread(cl); + cs_writelock(__func__, &fakeuser_lock); + } + } + else + { + client->dup = 1; + client->aureader_list = NULL; + cs_strncpy(buf, cs_inet_ntoa(ip), sizeof(buf)); + cs_log("client(%8lX) duplicate user '%s' from %s (current %s) set to fake (uniq=%d)", + (unsigned long)pthread_self(), usr, cs_inet_ntoa(cl->ip), buf, uniq); + + if(client->failban & BAN_DUPLICATE) + { + cs_add_violation_by_ip(ip, get_module(client)->ptab.ports[client->port_idx].s_port, usr); + } + + if(cfg.dropdups) + { + cs_writeunlock(__func__, &fakeuser_lock); // we need to unlock here as cs_disconnect_client kills the current thread! + cs_sleepms(120); // sleep a bit to prevent against saturation from fast reconnecting clients + cs_disconnect_client(client); + cs_writelock(__func__, &fakeuser_lock); + } + break; + } + } + } + cs_writeunlock(__func__, &fakeuser_lock); +} + +/* Resolves the ip of the hostname of the specified account and saves it in account->dynip. + If the hostname is not configured, the ip is set to 0. */ +static void cs_user_resolve(struct s_auth *account +#ifdef IPV6SUPPORT +, uint8_t ipv4force +#endif +) +{ + if(account->dyndns) + { + IN_ADDR_T lastip; + IP_ASSIGN(lastip, account->dynip); +#ifdef IPV6SUPPORT + if (ipv4force) + { + cs_resolve_v4(account->dyndns, &account->dynip, NULL, NULL); + } + else + { +#endif + cs_resolve(account->dyndns, &account->dynip, NULL, NULL); +#ifdef IPV6SUPPORT + } +#endif + if(!IP_EQUAL(lastip, account->dynip)) + { + cs_log("%s: resolved ip=%s", account->dyndns, cs_inet_ntoa(account->dynip)); + } + } + else + { + set_null_ip(&account->dynip); + } +} + +/* Returns the username from the client. You will always get a char reference back (no NULLs but it may be string containting "NULL") + which you should never modify and not free()! */ +const char *username(struct s_client *client) +{ + if(!check_client(client)) + { + return "NULL"; + } + + if(client->typ == 's' || client->typ == 'h' || client->typ == 'a') + { + return processUsername ? processUsername : "NULL"; + } + + if(client->typ == 'c' || client->typ == 'm') + { + struct s_auth *acc = client->account; + if(acc) + { + if(acc->usr[0]) + { + return acc->usr; + } + else + { + return "anonymous"; + } + } + else + { + return "NULL"; + } + } + else if(client->typ == 'r' || client->typ == 'p') + { + struct s_reader *rdr = client->reader; + if(rdr) + { + return rdr->label; + } + } + return "NULL"; +} + + +struct s_client *create_client(IN_ADDR_T ip) +{ + struct s_client *cl; + if(!cs_malloc(&cl, sizeof(struct s_client))) + { + cs_log("max connections reached (out of memory) -> reject client %s", + IP_ISSET(ip) ? cs_inet_ntoa(ip) : "with null address"); + + return NULL; + } + + //client part + IP_ASSIGN(cl->ip, ip); + cl->account = first_client->account; + + //master part + SAFE_MUTEX_INIT(&cl->thread_lock, NULL); + cl->login = cl->last = time(NULL); + cl->tid = (uint32_t)rand(); + + //Now add new client to the list: + struct s_client *last; + cs_writelock(__func__, &clientlist_lock); + + for(last = first_client; last && last->next; last = last->next) + { ; } //ends with cl on last client + + if (last) + { + last->next = cl; + } + + int32_t bucket = (uintptr_t)cl / 16 % CS_CLIENT_HASHBUCKETS; + cl->nexthashed = first_client_hashed[bucket]; + first_client_hashed[bucket] = cl; + + cs_writeunlock(__func__, &clientlist_lock); + + return cl; +} + +/* Creates the master client of OSCam and inits some global variables/mutexes. */ +void init_first_client(void) +{ + // get username OScam is running under + struct passwd pwd; + struct passwd *pwdbuf; + +#ifdef __ANDROID__ + pwdbuf = getpwuid(getuid()); // This is safe + if(pwdbuf) + { + memcpy(&pwd, pwdbuf, sizeof(pwd)); + processUsername = cs_strdup(pwd.pw_name); + } +#else + char buf[256]; + if(getpwuid_r(getuid(), &pwd, buf, sizeof(buf), &pwdbuf) == 0) + { + processUsername = cs_strdup(pwd.pw_name); + } +#endif + + if(!cs_malloc(&first_client, sizeof(struct s_client))) + { + fprintf(stderr, "Could not allocate memory for master client, exiting..."); + exit(1); + } + + memset(first_client_hashed, 0, sizeof(first_client_hashed)); + int32_t bucket = (uintptr_t)first_client / 16 % CS_CLIENT_HASHBUCKETS; + first_client_hashed[bucket] = first_client; + + first_client->next = NULL; // terminate clients list with NULL + first_client->login = time(NULL); + first_client->typ = 's'; + first_client->thread = pthread_self(); + set_localhost_ip(&first_client->ip); + + struct s_auth *null_account; + if(!cs_malloc(&null_account, sizeof(struct s_auth))) + { + fprintf(stderr, "Could not allocate memory for master account, exiting..."); + exit(1); + } + + first_client->account = null_account; + if(pthread_setspecific(getclient, first_client)) + { + fprintf(stderr, "Could not setspecific getclient in master process, exiting..."); + exit(1); + } +} + +int32_t cs_auth_client(struct s_client *client, struct s_auth *account, const char *e_txt) +{ + int32_t rc = 0; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + uint8_t i; + uint8_t j; + char buf[32]; + char *t_crypt = "encrypted"; + char *t_plain = "plain"; + char *t_grant = " granted"; + char *t_reject = " rejected"; + char *t_msg[] = { buf, "invalid access", "invalid ip", "unknown reason", "protocol not allowed" }; + struct s_module *module = get_module(client); + + memset(&client->grp, 0xff, sizeof(uint64_t)); + //client->grp=0xffffffffffffff; + if((intptr_t)account != 0 && (intptr_t)account != -1 && account->disabled) + { + cs_add_violation(client, account->usr); + cs_log("%s %s-client %s%s (%s%sdisabled account)", + client->crypted ? t_crypt : t_plain, + module->desc, + IP_ISSET(client->ip) ? cs_inet_ntoa(client->ip) : "", + IP_ISSET(client->ip) ? t_reject : t_reject + 1, + e_txt ? e_txt : "", + e_txt ? " " : ""); + return 1; + } + + // check whether client comes in over allowed protocol + if((intptr_t)account != 0 && (intptr_t)account != -1 && (intptr_t)account->allowedprotocols && + (((intptr_t)account->allowedprotocols & module->listenertype) != module->listenertype)) + { + cs_add_violation(client, account->usr); + cs_log("%s %s-client %s%s (%s%sprotocol not allowed)", + client->crypted ? t_crypt : t_plain, + module->desc, + IP_ISSET(client->ip) ? cs_inet_ntoa(client->ip) : "", + IP_ISSET(client->ip) ? t_reject : t_reject + 1, + e_txt ? e_txt : "", + e_txt ? " " : ""); + return 1; + } + + client->account = first_client->account; + switch((intptr_t)account) + { + case 0: // reject access + { + rc = 1; + cs_add_violation(client, NULL); + cs_log("%s %s-client %s%s (%s)", + client->crypted ? t_crypt : t_plain, + module->desc, + IP_ISSET(client->ip) ? cs_inet_ntoa(client->ip) : "", + IP_ISSET(client->ip) ? t_reject : t_reject + 1, + e_txt ? e_txt : t_msg[rc]); + break; + } + + default: // grant/check access + { + if(IP_ISSET(client->ip) && account->dyndns) + { + if(!IP_EQUAL(client->ip, account->dynip)) +#ifndef IPV6SUPPORT + { cs_user_resolve(account); } +#else + { cs_user_resolve(account, 0); } + // fallback to ipv4 if ipv6 not matching + if (!IP_EQUAL(client->ip, account->dynip)) + { cs_user_resolve(account, 1); } +#endif + if(!IP_EQUAL(client->ip, account->dynip)) + { + cs_add_violation(client, account->usr); + rc = 2; + } + } + + client->monlvl = account->monlvl; + client->account = account; + + if(!rc) + { + client->dup = 0; + if(client->typ == 'c' || client->typ == 'm') + { + client->pcrc = crc32(0L, MD5((uint8_t *)(ESTR(account->pwd)), cs_strlen(ESTR(account->pwd)), md5tmp), MD5_DIGEST_LENGTH); + } + + if(client->typ == 'c') + { + client->last_caid = NO_CAID_VALUE; + client->last_provid = NO_PROVID_VALUE; + client->last_srvid = NO_SRVID_VALUE; + client->expirationdate = account->expirationdate; + client->disabled = account->disabled; + client->allowedtimeframe_set=account->allowedtimeframe_set; + + for(i = 0; i < SIZE_SHORTDAY; i++) + { + for(j = 0; j < 24; j++) + { + client->allowedtimeframe[i][j][0] = account->allowedtimeframe[i][j][0]; + client->allowedtimeframe[i][j][1] = account->allowedtimeframe[i][j][1]; + } + } + + if(account->firstlogin == 0) + { + account->firstlogin = time((time_t *)0); + } + + client->failban = account->failban; + client->c35_suppresscmd08 = account->c35_suppresscmd08; + client->ncd_keepalive = account->ncd_keepalive; + client->grp = account->grp; + client->aureader_list = account->aureader_list; + client->autoau = account->autoau; + client->tosleep = (60 * account->tosleep); + client->c35_sleepsend = account->c35_sleepsend; + caidtab_clone(&account->ctab, &client->ctab); + + if(account->uniq) + { + cs_fake_client(client, account->usr, account->uniq, client->ip); + } + + client->cltab = account->cltab; // CLASS filter + ftab_clone(&account->ftab, &client->ftab); // IDENT filter + ftab_clone(&account->fchid, &client->fchid); // CHID filter + client->sidtabs.ok = account->sidtabs.ok; // services + client->sidtabs.no = account->sidtabs.no; // services + tuntab_clone(&account->ttab, &client->ttab); + ac_init_client(client, account); + } + } + } /* fallthrough */ + + case -1: // anonymous grant access + { + if(rc) + { + t_grant = t_reject; + } + else + { + if(client->typ == 'm') + { + snprintf(t_msg[0], sizeof(buf), "lvl=%d", client->monlvl); + } + else + { + int32_t rcount = ll_count(client->aureader_list); + snprintf(buf, sizeof(buf), "au="); + + if(!rcount) + { + snprintf(buf + 3, sizeof(buf) - 3, "off"); + } + else + { + if(client->autoau) + { + snprintf(buf + 3, sizeof(buf) - 3, "auto (%d reader)", rcount); + } + else + { + snprintf(buf + 3, sizeof(buf) - 3, "on (%d reader)", rcount); + } + } + } + } + + cs_log("%s %s-client %s%s (%s, %s)", + client->crypted ? t_crypt : t_plain, + e_txt ? e_txt : module->desc, + IP_ISSET(client->ip) ? cs_inet_ntoa(client->ip) : "", + IP_ISSET(client->ip) ? t_grant : t_grant + 1, + username(client), t_msg[rc]); + + break; + } + } + return rc; +} + +void cs_disconnect_client(struct s_client *client) +{ + char buf[32] = { 0 }; + + if(IP_ISSET(client->ip)) + { + snprintf(buf, sizeof(buf), " from %s", cs_inet_ntoa(client->ip)); + } + + cs_log("%s disconnected%s", username(client), buf); + + if(client == cur_client()) + { + cs_exit(0); + } + else + { + kill_thread(client); + } +} + +void kill_all_clients(void) +{ + struct s_client *cl; + for(cl = first_client->next; cl; cl = cl->next) + { + if(cl->typ == 'c' || cl->typ == 'm') + { + if(cl->account) + { + cs_log("killing client %s", cl->account->usr); + } + kill_thread(cl); +#ifdef CS_CACHEEX_AIO + ll_destroy_data(&cl->ll_cacheex_stats); +#endif + } + } + NULLFREE(processUsername); +} + +void cs_reinit_clients(struct s_auth *new_accounts) +{ + struct s_auth *account; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + uint8_t i; + uint8_t j; + + struct s_client *cl; + for(cl = first_client->next; cl; cl = cl->next) + { + if((cl->typ == 'c' || cl->typ == 'm') && cl->account) + { + for(account = new_accounts; (account) ; account = account->next) + { + if(!strcmp(cl->account->usr, account->usr)) + { + break; + } + } + + if(account && !account->disabled && cl->pcrc == crc32(0L, MD5((uint8_t *)ESTR(account->pwd), cs_strlen(ESTR(account->pwd)), md5tmp), MD5_DIGEST_LENGTH)) + { + cl->account = account; +#ifdef CS_CACHEEX_AIO + cl->cacheex_aio_checked = 0; +#endif + if(cl->typ == 'c') + { + cl->grp = account->grp; + cl->aureader_list = account->aureader_list; + cl->autoau = account->autoau; + cl->expirationdate = account->expirationdate; + cl->allowedtimeframe_set = account->allowedtimeframe_set; + + for(i = 0; i < SIZE_SHORTDAY; i++) + { + for(j = 0; j < 24; j++) + { + cl->allowedtimeframe[i][j][0] = account->allowedtimeframe[i][j][0]; + cl->allowedtimeframe[i][j][1] = account->allowedtimeframe[i][j][1]; + } + } + + cl->ncd_keepalive = account->ncd_keepalive; + cl->c35_suppresscmd08 = account->c35_suppresscmd08; + cl->tosleep = (60 * account->tosleep); + cl->c35_sleepsend = account->c35_sleepsend; + cl->monlvl = account->monlvl; + cl->disabled = account->disabled; + cl->cltab = account->cltab; // Class + + // newcamd module doesn't like ident reloading + if(!cl->ncd_server) + { + ftab_clone(&account->ftab, &cl->ftab); // IDENT filter + ftab_clone(&account->fchid, &cl->fchid); // CHID filter + } + + cl->sidtabs.ok = account->sidtabs.ok; // services + cl->sidtabs.no = account->sidtabs.no; // services + cl->failban = account->failban; + + caidtab_clone(&account->ctab, &cl->ctab); + tuntab_clone(&account->ttab, &cl->ttab); + + webif_client_reset_lastresponsetime(cl); + + if(account->uniq) + { + cs_fake_client(cl, account->usr, (account->uniq == 1 || account->uniq == 2) ? account->uniq + 2 : account->uniq, cl->ip); + } + + ac_init_client(cl, account); + } + } + else + { + if(get_module(cl)->type & MOD_CONN_NET) + { + cs_log_dbg(D_TRACE, "client '%s', thread=%8lX not found in db (or password changed)", cl->account->usr, (unsigned long)cl->thread); + kill_thread(cl); + } + else + { + cl->account = first_client->account; + } + } + } + else + { + cl->account = NULL; + } + } +} + +void client_check_status(struct s_client *cl) +{ + if(!cl || cl->kill || !cl->init_done) + { + return; + } + + switch(cl->typ) + { + case 'm': + case 'c': + if((get_module(cl)->listenertype & LIS_CCCAM) && cl->last && (time(NULL) - cl->last) > (time_t)12) + { + add_job(cl, ACTION_CLIENT_IDLE, NULL, 0); + } + + // Check umaxidle to avoid client is killed for inactivity, it has priority than cmaxidle + if(!cl->account->umaxidle) + { + break; + } + + // Check user for exceeding umaxidle by checking cl->last, except Newcamd & Gbox + if(!(cl->ncd_keepalive && (get_module(cl)->listenertype & LIS_NEWCAMD)) && !(get_module(cl)->listenertype & LIS_GBOX) && cl->account->umaxidle>0 && cl->last && (time(NULL) - cl->last) > (time_t)cl->account->umaxidle) + { + add_job(cl, ACTION_CLIENT_IDLE, NULL, 0); + } + + // Check clients for exceeding cmaxidle by checking cl->last, except Newcamd & Gbox + if(!(cl->ncd_keepalive && (get_module(cl)->listenertype & LIS_NEWCAMD)) && !(get_module(cl)->listenertype & LIS_GBOX) && cl->last && cl->account->umaxidle==-1 && cfg.cmaxidle && (time(NULL) - cl->last) > (time_t)cfg.cmaxidle) + { + add_job(cl, ACTION_CLIENT_IDLE, NULL, 0); + } + +#ifdef MODULE_GBOX + if((get_module(cl)->listenertype & LIS_GBOX) && cl->last && (time(NULL) - cl->last) > (time_t)cfg.gbox_reconnect) + { + add_job(cl, ACTION_PEER_IDLE, NULL, 0); + } +#endif + break; + + case 'r': + cardreader_checkhealth(cl, cl->reader); + break; + + case 'p': + { + struct s_reader *rdr = cl->reader; + if(!rdr || !rdr->enable || !rdr->active) // reader is disabled or restarting at this moment + { + break; + } + + // execute reader do idle on proxy reader after a certain time (rdr->tcp_ito = inactivitytimeout) + // disconnect when no keepalive available + if((rdr->tcp_ito && is_cascading_reader(rdr)) || (rdr->typ == R_CCCAM) || (rdr->typ == R_CAMD35) || (rdr->typ == R_CS378X) || (rdr->typ == R_SCAM) || (rdr->tcp_ito != 0 && rdr->typ == R_RADEGAST)) + { + time_t now = time(NULL); + int32_t time_diff = llabs(now - rdr->last_check); + + if(time_diff > 60 || (time_diff > 12 && (rdr->typ == R_CCCAM || rdr->typ == R_CAMD35 || rdr->typ == R_CS378X)) || ((time_diff > (rdr->tcp_rto?rdr->tcp_rto:60)) && rdr->typ == R_RADEGAST)) //check 1x per minute or every 10s for cccam/camd35 or reconnecttimeout radegast if 0 defaut 60s + { + add_job(rdr->client, ACTION_READER_IDLE, NULL, 0); + rdr->last_check = now; + } + } + break; + } + } +} + +void free_client(struct s_client *cl) +{ + if(!cl) + { + return; + } + + struct s_reader *rdr = cl->reader; + + // Remove client from client list. kill_thread also removes this client, so here just if client exits itself... + struct s_client *prev, *cl2; + cs_writelock(__func__, &clientlist_lock); + + if(!cl->kill_started) + { + cl->kill_started = 1; + } + else + { + cs_writeunlock(__func__, &clientlist_lock); + cs_log("[free_client] ERROR: free already started!"); + return; + } + cl->kill = 1; + + for(prev = first_client, cl2 = first_client->next; prev->next != NULL; prev = prev->next, cl2 = cl2->next) + { + if(cl == cl2) + { + break; + } + } + + if(cl == cl2) // Remove client from list + { + prev->next = cl2->next; + } + + int32_t bucket = (uintptr_t)cl / 16 % CS_CLIENT_HASHBUCKETS; + + // Remove client from hashed list + if(first_client_hashed[bucket] == cl) + { + first_client_hashed[bucket] = cl->nexthashed; + } + else + { + for(prev = first_client_hashed[bucket], cl2 = first_client_hashed[bucket]->nexthashed; + prev->nexthashed != NULL; prev = prev->nexthashed, cl2 = cl2->nexthashed) + { + if(cl == cl2) + { + break; + } + } + + if(cl == cl2) + { + prev->nexthashed = cl2->nexthashed; + } + } + + cs_writeunlock(__func__, &clientlist_lock); + cleanup_ecmtasks(cl); + + // Clean reader. The cleaned structures should be only used by the reader thread, so we should be save without waiting + if(rdr) + { + ll_destroy_data(&rdr->emmstat); + remove_reader_from_active(rdr); + + cs_sleepms(1000); // just wait a bit that really really nobody is accessing client data + + if(rdr->ph.cleanup) + { + rdr->ph.cleanup(cl); + } + + if(cl->typ == 'r') + { + cardreader_close(rdr); + } + + if(cl->typ == 'p') + { + network_tcp_connection_close(rdr, "cleanup"); + } + + cl->reader = NULL; + } + + // Clean client specific data + if(cl->typ == 'c') + { + cs_statistics(cl); + cl->last_caid = NO_CAID_VALUE; + cl->last_provid = NO_PROVID_VALUE; + cl->last_srvid = NO_SRVID_VALUE; + cs_statistics(cl); + + cs_sleepms(1000); // just wait a bit that really really nobody is accessing client data + } + + struct s_module *module = get_module(cl); + if(module->cleanup) + { + module->cleanup(cl); + } + + // Close network socket if not already cleaned by previous cleanup functions + if(cl->pfd) + { + close(cl->pfd); + } + + // Clean all remaining structures + free_joblist(cl); + NULLFREE(cl->work_mbuf); + + if(cl->ecmtask) + { + add_garbage(cl->ecmtask); + cl->ecmtask = NULL; + } + + ll_destroy_data(&cl->cascadeusers); + + ftab_clear(&cl->ftab); + ftab_clear(&cl->fchid); + tuntab_clear(&cl->ttab); + caidtab_clear(&cl->ctab); + + NULLFREE(cl->cltab.aclass); + NULLFREE(cl->cltab.bclass); + + NULLFREE(cl->cw_rass); + ll_destroy_data(&cl->ra_buf); + NULLFREE(cl->aes_keys); + +#ifdef MODULE_CCCAM + add_garbage(cl->cc); +#endif +#ifdef MODULE_SERIAL + add_garbage(cl->serialdata); +#endif + add_garbage(cl); +} diff --git a/oscam-client.h b/oscam-client.h new file mode 100644 index 0000000..8dac661 --- /dev/null +++ b/oscam-client.h @@ -0,0 +1,24 @@ +#ifndef OSCAM_CLIENT_H_ +#define OSCAM_CLIENT_H_ + +/* Gets the client associated to the calling thread. */ +static inline struct s_client *cur_client(void) +{ + return (struct s_client *)pthread_getspecific(getclient); +} +int32_t get_threadnum(struct s_client *client); +struct s_auth *get_account_by_name(char *name); +int8_t is_valid_client(struct s_client *client); +const char *remote_txt(void); +const char *client_get_proto(struct s_client *cl); +const char *username(struct s_client *client); +void init_first_client(void); +struct s_client *create_client(IN_ADDR_T ip); +int32_t cs_auth_client(struct s_client *client, struct s_auth *account, const char *e_txt); +void cs_disconnect_client(struct s_client *client); +void cs_reinit_clients(struct s_auth *new_accounts); +void kill_all_clients(void); +void client_check_status(struct s_client *cl); +void free_client(struct s_client *cl); + +#endif diff --git a/oscam-conf-chk.c b/oscam-conf-chk.c new file mode 100644 index 0000000..16636b5 --- /dev/null +++ b/oscam-conf-chk.c @@ -0,0 +1,668 @@ +#define MODULE_LOG_PREFIX "config" + +#include "globals.h" +#include "oscam-array.h" +#include "oscam-conf-chk.h" +#include "oscam-garbage.h" +#include "oscam-net.h" +#include "oscam-string.h" + +void chk_iprange(char *value, struct s_ip **base) +{ + int32_t i = 0; + char *ptr1, *ptr2, *saveptr1 = NULL; + struct s_ip *fip, *lip, *cip; + + if(!cs_malloc(&cip, sizeof(struct s_ip))) + { return; } + fip = cip; + + for(ptr1 = strtok_r(value, ",", &saveptr1); ptr1; ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + if(i == 0) + { ++i; } + else + { + if(!cs_malloc(&cip, sizeof(struct s_ip))) + { break; } + lip->next = cip; + } + + if((ptr2 = strchr(trim(ptr1), '-'))) + { + *ptr2++ = '\0'; + cs_inet_addr(trim(ptr1), &cip->ip[0]); + cs_inet_addr(trim(ptr2), &cip->ip[1]); + } + else + { + cs_inet_addr(ptr1, &cip->ip[0]); + IP_ASSIGN(cip->ip[1], cip->ip[0]); + } + lip = cip; + } + lip = *base; + *base = fip; + clear_sip(&lip); +} + +void chk_caidtab(char *value, CAIDTAB *caidtab) +{ + caidtab_clear(caidtab); + char *ptr, *saveptr1 = NULL; + for(ptr = strtok_r(value, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1)) + { + CAIDTAB_DATA d; + memset(&d, 0, sizeof(d)); + d.mask = 0xffff; + char *caid_end_ptr = strchr(ptr, ':'); // caid_end_ptr + 1 -> cmap + if(caid_end_ptr) { + *caid_end_ptr++ = '\0'; + d.cmap = a2i(caid_end_ptr, 2); + if (errno == EINVAL) continue; + } + char *mask_start_ptr = strchr(ptr, '&'); // mask_start_ptr + 1 -> mask + errno = 0; + if(mask_start_ptr) { // Mask is optional + *mask_start_ptr++ = '\0'; + d.mask = a2i(mask_start_ptr, 2); + if (errno == EINVAL) continue; + } + d.caid = a2i(ptr, 2); + if (errno == EINVAL) continue; + if (d.caid || d.cmap) + caidtab_add(caidtab, &d); + } +} + +void chk_caidvaluetab(char *value, CAIDVALUETAB *caidvaluetab) +{ + caidvaluetab_clear(caidvaluetab); + char *ptr, *saveptr1 = NULL; + for(ptr = strtok_r(value, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1)) + { + CAIDVALUETAB_DATA d; + memset(&d, 0, sizeof(d)); + char *caid_end_ptr = strchr(ptr, ':'); // caid_end_ptr + 1 -> value + if(!caid_end_ptr) + continue; + *caid_end_ptr++ = '\0'; + errno = 0; + d.caid = a2i(ptr, 2); + if (errno == EINVAL) + continue; + d.value = atoi(caid_end_ptr); + if (d.caid && d.value < 10000) + caidvaluetab_add(caidvaluetab, &d); + } +} + +void chk_cacheex_valuetab(char *lbrlt, CECSPVALUETAB *tab) +{ + //[caid][&mask][@provid][$servid][:awtime][:]dwtime + char *ptr = NULL, *saveptr1 = NULL; + cecspvaluetab_clear(tab); + + int32_t i; + for(i = 0, ptr = strtok_r(lbrlt, ",", &saveptr1); (ptr); ptr = strtok_r(NULL, ",", &saveptr1), i++) + { + CECSPVALUETAB_DATA d; + memset(&d, 0, sizeof(d)); + + int32_t caid = -1, cmask = -1, srvid = -1; + int32_t j, provid = -1; + int16_t awtime = -1, dwtime = -1; + char *ptr1 = NULL, *ptr2 = NULL, *ptr3 = NULL, *ptr4 = NULL, *ptr5 = NULL, *saveptr2 = NULL; + + if((ptr4 = strchr(trim(ptr), ':'))) + { + //awtime & dwtime + *ptr4++ = '\0'; + for(j = 0, ptr5 = strtok_r(ptr4, ":", &saveptr2); (j < 2) && ptr5; ptr5 = strtok_r(NULL, ":", &saveptr2), j++) + { + if(!j) + { + dwtime = atoi(ptr5); + } + if(j) + { + awtime = dwtime; + dwtime = atoi(ptr5); + } + } + } + if((ptr3 = strchr(trim(ptr), '$'))) + { + *ptr3++ = '\0'; + srvid = a2i(ptr3, 4); + } + if((ptr2 = strchr(trim(ptr), '@'))) + { + *ptr2++ = '\0'; + provid = a2i(ptr2, 6); + } + if((ptr1 = strchr(ptr, '&'))) + { + *ptr1++ = '\0'; + cmask = a2i(ptr1, -2); + } + if(!ptr1 && !ptr2 && !ptr3 && !ptr4) //only dwtime + { dwtime = atoi(ptr); } + else + { caid = a2i(ptr, 2); } + if((i == 0 && (caid <= 0)) || (caid > 0)) + { + d.caid = caid; + d.cmask = cmask; + d.prid = provid; + d.srvid = srvid; + d.awtime = awtime; + d.dwtime = dwtime; + cecspvaluetab_add(tab, &d); + } + } +} + + +void chk_cacheex_cwcheck_valuetab(char *lbrlt, CWCHECKTAB *tab) +{ + //caid[&mask][@provid][$servid]:mode:counter + int32_t i; + char *ptr = NULL, *saveptr1 = NULL; + cwcheckvaluetab_clear(tab); + + for(i = 0, ptr = strtok_r(lbrlt, ",", &saveptr1); (ptr); ptr = strtok_r(NULL, ",", &saveptr1), i++) + { + CWCHECKTAB_DATA d; + memset(&d, 0, sizeof(d)); + + int32_t caid = -1, cmask = -1, provid = -1, srvid = -1; + int16_t mode = -1, counter = -1; + + char *ptr1 = NULL, *ptr2 = NULL, *ptr3 = NULL, *ptr4 = NULL, *ptr5 = NULL, *saveptr2 = NULL; + + if((ptr4 = strchr(trim(ptr), ':'))) + { + *ptr4++ = '\0'; + ptr5 = strtok_r(ptr4, ":", &saveptr2); + if(ptr5) mode = atoi(ptr5); + ptr5 = strtok_r(NULL, ":", &saveptr2); + if(ptr5) counter = atoi(ptr5); + } + if((ptr3 = strchr(trim(ptr), '$'))) + { + *ptr3++ = '\0'; + srvid = a2i(ptr3, 4); + } + if((ptr2 = strchr(trim(ptr), '@'))) + { + *ptr2++ = '\0'; + provid = a2i(ptr2, 6); + } + if((ptr1 = strchr(ptr, '&'))) + { + *ptr1++ = '\0'; + cmask = a2i(ptr1, -2); + } + + caid = a2i(ptr, 2); + + if((i == 0 && (caid <= 0)) || (caid > 0)) + { + d.caid = caid; + d.cmask = cmask; + d.prid = provid; + d.srvid = srvid; + d.mode = mode; + d.counter = counter; + cwcheckvaluetab_add(tab, &d); + } + + } +} + + +void chk_cacheex_hitvaluetab(char *lbrlt, CECSPVALUETAB *tab) +{ + //[caid][&mask][@provid][$servid] + char *ptr = NULL, *saveptr1 = NULL; + cecspvaluetab_clear(tab); + + for(ptr = strtok_r(lbrlt, ",", &saveptr1); (ptr); ptr = strtok_r(NULL, ",", &saveptr1)) + { + CECSPVALUETAB_DATA d; + memset(&d, 0, sizeof(d)); + + int32_t caid = -1, cmask = -1, srvid = -1; + int32_t provid = -1; + char *ptr1 = NULL, *ptr2 = NULL, *ptr3 = NULL; + + if((ptr3 = strchr(trim(ptr), '$'))) + { + *ptr3++ = '\0'; + srvid = a2i(ptr3, 4); + } + if((ptr2 = strchr(trim(ptr), '@'))) + { + *ptr2++ = '\0'; + provid = a2i(ptr2, 6); + } + if((ptr1 = strchr(ptr, '&'))) + { + *ptr1++ = '\0'; + cmask = a2i(ptr1, -2); + } + caid = a2i(ptr, 2); + if(caid > 0) + { + d.caid = caid; + d.cmask = cmask; + d.prid = provid; + d.srvid = srvid; + cecspvaluetab_add(tab, &d); + } + } + +} + +void chk_tuntab(char *tunasc, TUNTAB *ttab) +{ + tuntab_clear(ttab); + errno = 0; + char *caid_ptr, *savecaid_ptr = NULL; + for(caid_ptr = strtok_r(tunasc, ",", &savecaid_ptr); (caid_ptr); caid_ptr = strtok_r(NULL, ",", &savecaid_ptr)) + { + TUNTAB_DATA d; + char *srvid_ptr = strchr(trim(caid_ptr), '.'); + char *caidto_ptr = strchr(trim(caid_ptr), ':'); + if (!srvid_ptr) + continue; + *srvid_ptr++ = '\0'; + if (caidto_ptr) + *caidto_ptr++ = '\0'; + d.bt_caidfrom = a2i(caid_ptr, 2); + d.bt_srvid = a2i(srvid_ptr, 2); + d.bt_caidto = 0; + if (caidto_ptr) + d.bt_caidto = a2i(caidto_ptr, 2); + if (errno == EINVAL) + continue; + if (d.bt_caidfrom | d.bt_srvid | d.bt_caidto) + tuntab_add(ttab, &d); + } +} + +void chk_services(char *labels, SIDTABS *sidtabs) +{ + int32_t i; + char *ptr, *saveptr1 = NULL; + SIDTAB *sidtab; + SIDTABBITS newsidok, newsidno; + newsidok = newsidno = 0; + for(ptr = strtok_r(labels, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1)) + { + for(trim(ptr), i = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, i++) + { + if(!strcmp(sidtab->label, ptr)) { newsidok |= ((SIDTABBITS)1 << i); } + if((ptr[0] == '!') && (!strcmp(sidtab->label, ptr + 1))) { newsidno |= ((SIDTABBITS)1 << i); } + } + } + sidtabs->ok = newsidok; + sidtabs->no = newsidno; +} + +void chk_ftab(char *value, FTAB *ftab) +{ + ftab_clear(ftab); + char *ptr1, *saveptr1 = NULL; + errno = 0; + for(ptr1 = strtok_r(value, ";", &saveptr1); (ptr1); ptr1 = strtok_r(NULL, ";", &saveptr1)) + { + FILTER d; + memset(&d, 0, sizeof(d)); + char *caid_end_ptr = strchr(ptr1, ':'); // caid_end_ptr + 1 -> headers + if(!caid_end_ptr) + continue; + caid_end_ptr[0] = '\0'; + d.caid = a2i(ptr1, 4); + if (!d.caid || errno == EINVAL) + { + errno = 0; + continue; + } + ptr1 = caid_end_ptr + 1; // -> headers + char *ident_ptr, *saveident_ptr = NULL; + for(ident_ptr = strtok_r(ptr1, ",", &saveident_ptr); ident_ptr && d.nprids < ARRAY_SIZE(d.prids); ident_ptr = strtok_r(NULL, ",", &saveident_ptr)) + { + uint32_t ident = a2i(ident_ptr, 4); + if (errno == EINVAL) + { + errno = 0; + continue; + } + d.prids[d.nprids++] = ident; + } + if (d.nprids) + ftab_add(ftab, &d); + } +} + +void chk_cltab(char *classasc, CLASSTAB *clstab) +{ + int32_t max_an = 0, max_bn = 0; + char *ptr1, *saveptr1 = NULL, *classasc_org; + CLASSTAB newclstab, oldclstab; + memset(&newclstab, 0, sizeof(newclstab)); + newclstab.an = newclstab.bn = 0; + + if(!cs_malloc(&classasc_org, sizeof(char)*cs_strlen(classasc)+1)) + { return; } + + cs_strncpy(classasc_org, classasc, sizeof(char)*cs_strlen(classasc)+1); + + for(ptr1 = strtok_r(classasc, ",", &saveptr1); ptr1; ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + ptr1 = trim(ptr1); + if(ptr1[0] == '!') + { max_bn++; } + else + { max_an++; } + } + + if(max_an && !cs_malloc(&newclstab.aclass, sizeof(uint8_t) * max_an)) + { NULLFREE(classasc_org); return; } + + if(max_bn && !cs_malloc(&newclstab.bclass, sizeof(uint8_t) * max_bn)) + { NULLFREE(newclstab.aclass); NULLFREE(classasc_org); return; } + + classasc = classasc_org; + + for(ptr1 = strtok_r(classasc, ",", &saveptr1); ptr1; ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + ptr1 = trim(ptr1); + if(ptr1[0] == '!' && newclstab.bclass != NULL) + { newclstab.bclass[newclstab.bn++] = (uint8_t)a2i(ptr1 + 1, 2); } + else if(newclstab.aclass != NULL) + { newclstab.aclass[newclstab.an++] = (uint8_t)a2i(ptr1, 2); } + } + + NULLFREE(classasc_org); + + memcpy(&oldclstab, clstab, sizeof(CLASSTAB)); + memcpy(clstab, &newclstab, sizeof(CLASSTAB)); + + NULLFREE(oldclstab.aclass); + NULLFREE(oldclstab.bclass); +} + +void chk_port_tab(char *portasc, PTAB *ptab) +{ + int32_t i, j, nfilts, ifilt, iport; + PTAB *newptab; + char *ptr1, *ptr2, *ptr3, *saveptr1 = NULL; + char *ptr[CS_MAXPORTS] = {0}; + int32_t port[CS_MAXPORTS] = {0}; + if(!cs_malloc(&newptab, sizeof(PTAB))) + { return; } + + for(nfilts = i = 0, ptr1 = strtok_r(portasc, ";", &saveptr1); (i < CS_MAXPORTS) && (ptr1); ptr1 = strtok_r(NULL, ";", &saveptr1), i++) + { + ptr[i] = ptr1; + + if(!newptab->ports[i].ncd && !cs_malloc(&newptab->ports[i].ncd, sizeof(struct ncd_port))) + { break; } + + if((ptr2 = strchr(trim(ptr1), '@'))) + { + *ptr2++ = '\0'; + newptab->ports[i].s_port = atoi(ptr1); + + //checking for des key for port + newptab->ports[i].ncd->ncd_key_is_set = false; + if((ptr3 = strchr(trim(ptr1), '{'))) + { + *ptr3++ = '\0'; + if(key_atob_l(ptr3, newptab->ports[i].ncd->ncd_key, sizeof(newptab->ports[i].ncd->ncd_key) * 2)) + { fprintf(stderr, "newcamd: error in DES Key for port %s -> ignored\n", ptr1); } + else + { newptab->ports[i].ncd->ncd_key_is_set = true; } + } + + ptr[i] = ptr2; + port[i] = newptab->ports[i].s_port; + newptab->nports++; + } + nfilts++; + } + + if(nfilts == 1 && cs_strlen(portasc) < 6 && newptab->ports[0].s_port == 0) + { + newptab->ports[0].s_port = atoi(portasc); + newptab->nports = 1; + } + + iport = ifilt = 0; + for(i = 0; i < nfilts; i++) + { + if(port[i] != 0) + { iport = i; } + for(j = 0, ptr3 = strtok_r(ptr[i], ",", &saveptr1); (j < CS_MAXPROV) && (ptr3); ptr3 = strtok_r(NULL, ",", &saveptr1), j++) + { + if((ptr2 = strchr(trim(ptr3), ':'))) + { + *ptr2++ = '\0'; + ifilt = newptab->ports[iport].ncd->ncd_ftab.nfilts++; + j = 0; + newptab->ports[iport].ncd->ncd_ftab.filts[ifilt].caid = (uint16_t)a2i(ptr3, 4); + newptab->ports[iport].ncd->ncd_ftab.filts[ifilt].prids[j] = a2i(ptr2, 6); + } + else + { + newptab->ports[iport].ncd->ncd_ftab.filts[ifilt].prids[j] = a2i(ptr3, 6); + } + newptab->ports[iport].ncd->ncd_ftab.filts[ifilt].nprids++; + } + } + memcpy(ptab, newptab, sizeof(PTAB)); + NULLFREE(newptab); +} + +void chk_port_camd35_tab(char *portasc, PTAB *ptab) +{ + int32_t i, j; + PTAB *newptab; + char *ptr1, *ptr2, *ptr4, *provids, *saveptr1 = NULL, *saveptr2 = NULL; + if(!cs_malloc(&newptab, sizeof(PTAB))) + { return; } + + for(i = 0, ptr1 = strtok_r(portasc, ";", &saveptr1); (i < CS_MAXPORTS) && (ptr1); ptr1 = strtok_r(NULL, ";", &saveptr1), i++) + { + if(!newptab->ports[newptab->nports].ncd && !cs_malloc(&newptab->ports[i].ncd, sizeof(struct ncd_port))) + { break; } + newptab->ports[newptab->nports].s_port = atoi(ptr1); + if((ptr2 = strchr(trim(ptr1), '@'))) + { + *ptr2++ = '\0'; // clean up @ symbol + newptab->ports[newptab->nports].s_port = atoi(ptr1); + if((provids = strchr(trim(ptr2), ':'))) + { + *provids++ = '\0'; // clean up : symbol + for(j = 0, ptr4 = strtok_r(provids, ",", &saveptr2); (i < CS_MAXPORTS) && (ptr4); ptr4 = strtok_r(NULL, ",", &saveptr2), j++) + { + newptab->ports[newptab->nports].ncd->ncd_ftab.filts[0].prids[j] = a2i(ptr4, 6); + } + newptab->ports[newptab->nports].ncd->ncd_ftab.filts[0].nprids=j; + } + newptab->ports[newptab->nports].ncd->ncd_ftab.filts[0].caid = (uint16_t)a2i(ptr2, sizeof(ptr2)); + newptab->nports++; + } + else + { + newptab->ports[newptab->nports].s_port = atoi(ptr1); + if (newptab->ports[newptab->nports].s_port) { newptab->nports++; }; + } + } + memcpy(ptab, newptab, sizeof(PTAB)); + NULLFREE(newptab); +} + +void chk_ecm_whitelist(char *value, ECM_WHITELIST *ecm_whitelist) +{ + ecm_whitelist_clear(ecm_whitelist); + char *ptr, *saveptr1 = NULL; + for(ptr = strtok_r(value, ";", &saveptr1); ptr; ptr = strtok_r(NULL, ";", &saveptr1)) + { + ECM_WHITELIST_DATA d; + memset(&d, 0, sizeof(d)); + char *caid_end_ptr = strchr(ptr, ':'); // caid_end_ptr + 1 -> headers + char *provid_ptr = strchr(ptr, '@'); // provid_ptr + 1 -> provid + char *headers = ptr; + if(caid_end_ptr) + { + caid_end_ptr[0] = '\0'; + if (provid_ptr) + { + provid_ptr[0] = '\0'; + provid_ptr++; + d.ident = a2i(provid_ptr, 6); + } + d.caid = dyn_word_atob(ptr); + headers = caid_end_ptr + 1; // -> headers + } else if(provid_ptr) { + provid_ptr[0] = '\0'; + d.ident = a2i(provid_ptr, 6); + } + if (d.caid == 0xffff) d.caid = 0; + if (d.ident == 0xffff) d.ident = 0; + char *len_ptr, *savelen_ptr = NULL; + for(len_ptr = strtok_r(headers, ",", &savelen_ptr); len_ptr; len_ptr = strtok_r(NULL, ",", &savelen_ptr)) + { + d.len = dyn_word_atob(len_ptr); + if (d.len == 0xffff) + continue; + ecm_whitelist_add(ecm_whitelist, &d); + } + } +} + +void chk_ecm_hdr_whitelist(char *value, ECM_HDR_WHITELIST *ecm_hdr_whitelist) +{ + ecm_hdr_whitelist_clear(ecm_hdr_whitelist); + char *ptr, *saveptr = NULL; + for(ptr = strtok_r(value, ";", &saveptr); ptr; ptr = strtok_r(NULL, ";", &saveptr)) + { + ECM_HDR_WHITELIST_DATA d; + memset(&d, 0, sizeof(d)); + char *caid_end_ptr = strchr(ptr, ':'); // caid_end_ptr + 1 -> headers + char *provid_ptr = strchr(ptr, '@'); // provid_ptr + 1 -> provid + char *headers = ptr; + if(caid_end_ptr) + { + caid_end_ptr[0] = '\0'; + if (provid_ptr) + { + provid_ptr[0] = '\0'; + provid_ptr++; + d.provid = a2i(provid_ptr, 6); + } + d.caid = dyn_word_atob(ptr); + headers = caid_end_ptr + 1; // -> headers + } else if(provid_ptr) { + provid_ptr[0] = '\0'; + d.provid = a2i(provid_ptr, 6); + } + if (d.caid == 0xffff) d.caid = 0; + if (d.provid == 0xffff) d.provid = 0; + char *hdr_ptr, *savehdr_ptr = NULL; + for(hdr_ptr = strtok_r(headers, ",", &savehdr_ptr); hdr_ptr; hdr_ptr = strtok_r(NULL, ",", &savehdr_ptr)) + { + hdr_ptr = trim(hdr_ptr); + d.len = cs_strlen(hdr_ptr); + if (d.len / 2 > sizeof(d.header)) + d.len = sizeof(d.header) * 2; + if (d.len > 1) + { + key_atob_l(hdr_ptr, d.header, d.len); + ecm_hdr_whitelist_add(ecm_hdr_whitelist, &d); + } + } + } +} + +/* Clears the s_ip structure provided. The pointer will be set to NULL so everything is cleared.*/ +void clear_sip(struct s_ip **sip) +{ + struct s_ip *cip = *sip; + for(*sip = NULL; cip != NULL; cip = cip->next) + { + add_garbage(cip); + } +} + +/* Clears the s_ptab struct provided by setting nfilts and nprids to zero. */ +void clear_ptab(struct s_ptab *ptab) +{ + int32_t i; + ptab->nports = 0; + for(i = 0; i < CS_MAXPORTS; i++) + { + if(ptab->ports[i].ncd) + { + ptab->ports[i].ncd->ncd_ftab.nfilts = 0; + ptab->ports[i].ncd->ncd_ftab.filts[0].nprids = 0; + NULLFREE(ptab->ports[i].ncd); + ptab->ports[i].ncd = NULL; + } + } +} + +/* Clears given csptab */ +void clear_cacheextab(CECSPVALUETAB *ctab) +{ + ctab->cevnum = 0; + NULLFREE(ctab->cevdata); +} + +void cwvote_caidtab_clear(CW_VOTE_CAID_TAB *cwvote_caid_table) +{ + if (cwvote_caid_table->cvcdata) + { + NULLFREE(cwvote_caid_table->cvcdata); + } + cwvote_caid_table->cvcnum = 0; +} + +void chk_cwvote_caidtab(char *value, CW_VOTE_CAID_TAB *cwvote_caid_table) +{ + char *ptr1, *saveptr1 = NULL; + int32_t i; + uint16_t caid; + + cwvote_caidtab_clear(cwvote_caid_table); + + for (i = 0, ptr1 = strtok_r(value, ",", &saveptr1); ptr1; ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + if (i >= CS_MAXPROV) + { + cs_log("WARNING: Too many CW Vote CAIDs defined, max %d allowed!", CS_MAXPROV); + break; + } + errno = 0; // Reset errno before calling strtoul + caid = (uint16_t)strtoul(ptr1, NULL, 16); + if (errno != 0) { + cs_log("WARNING: Invalid CAID format '%s' in cwvote_caids, ignoring.", ptr1); + continue; + } + if (caid > 0) + { + if (!cs_realloc(&cwvote_caid_table->cvcdata, sizeof(CW_VOTE_CAID_DATA) * (i + 1))) + { + cwvote_caid_table->cvcnum = 0; + return; + } + cwvote_caid_table->cvcdata[i].caid = caid; + i++; + } + } + cwvote_caid_table->cvcnum = i; +} diff --git a/oscam-conf-chk.h b/oscam-conf-chk.h new file mode 100644 index 0000000..c1b34e5 --- /dev/null +++ b/oscam-conf-chk.h @@ -0,0 +1,25 @@ +#ifndef OSCAM_CONF_CHK_H +#define OSCAM_CONF_CHK_H + +void chk_iprange(char *value, struct s_ip **base); +void chk_caidtab(char *value, CAIDTAB *caidtab); +void chk_caidvaluetab(char *value, CAIDVALUETAB *tab); +void chk_cacheex_valuetab(char *lbrlt, CECSPVALUETAB *tab); +void chk_cacheex_cwcheck_valuetab(char *lbrlt, CWCHECKTAB *tab); +void chk_cacheex_hitvaluetab(char *lbrlt, CECSPVALUETAB *tab); +void chk_tuntab(char *tunasc, TUNTAB *ttab); +void chk_services(char *labels, SIDTABS *sidtabs); +void chk_ftab(char *value, FTAB *ftab); +void chk_cltab(char *classasc, CLASSTAB *clstab); +void chk_port_tab(char *portasc, PTAB *ptab); +void chk_port_camd35_tab(char *portasc, PTAB *ptab); +void chk_ecm_whitelist(char *value, ECM_WHITELIST *ecm_whitelist); +void chk_ecm_hdr_whitelist(char *value, ECM_HDR_WHITELIST *ecm_hdr_whitelist); +void chk_cwvote_caidtab(char *value, CW_VOTE_CAID_TAB *cwvote_caid_table); + +void clear_sip(struct s_ip **base); +void clear_ptab(struct s_ptab *ptab); +void clear_cacheextab(CECSPVALUETAB *ctab); +void cwvote_caidtab_clear(CW_VOTE_CAID_TAB *cwvote_caid_table); + +#endif diff --git a/oscam-conf-mk.c b/oscam-conf-mk.c new file mode 100644 index 0000000..639301d --- /dev/null +++ b/oscam-conf-mk.c @@ -0,0 +1,1135 @@ +#define MODULE_LOG_PREFIX "config" + +#include "globals.h" +#include "oscam-conf-mk.h" +#include "oscam-net.h" +#include "oscam-string.h" + +const char *shortDay[8] = {"SUN","MON","TUE","WED","THU","FRI","SAT","ALL"}; +const char *weekdstr = "SUNMONTUEWEDTHUFRISATALL"; + +/* + * Creates a string ready to write as a token into config or WebIf for CAIDs. You must free the returned value through free_mk_t(). + */ +char *mk_t_caidtab(CAIDTAB *caidtab) +{ + if (!caidtab || !caidtab->ctnum) return ""; + + // Max entry length is cs_strlen("1234&ffff:1234,") == 15 + int32_t i, maxlen = 16 * caidtab->ctnum, pos = 0; + + char *ret; + if (!cs_malloc(&ret, maxlen)) + return ""; + + const char *comma = ""; + + for(i = 0; i < caidtab->ctnum; i++) + { + CAIDTAB_DATA *d = &caidtab->ctdata[i]; + if (d->caid < 0x0100) + pos += snprintf(ret + pos, maxlen - pos, "%s%02X", comma, d->caid); + else + pos += snprintf(ret + pos, maxlen - pos, "%s%04X", comma, d->caid); + if (d->mask && d->mask != 0xffff) + pos += snprintf(ret + pos, maxlen - pos, "&%04X", d->mask); + if (d->cmap) + pos += snprintf(ret + pos, maxlen - pos, ":%04X", d->cmap); + comma = ","; + } + return ret; +} + +/* + * Creates a string ready to write as a token into config or WebIf for TunTabs. You must free the returned value through free_mk_t(). + */ +char *mk_t_tuntab(TUNTAB *ttab) +{ + if (!ttab || !ttab->ttnum) return ""; + + // Each entry max length is cs_strlen("aaaa.bbbb:cccc,") == 15 + int32_t i, maxlen = 16 * ttab->ttnum, pos = 0; + + char *ret; + if (!cs_malloc(&ret, maxlen)) + return ""; + + const char *comma = ""; + + for(i = 0; i < ttab->ttnum; i++) + { + TUNTAB_DATA *d = &ttab->ttdata[i]; + pos += snprintf(ret + pos, maxlen - pos, "%s%04X", comma, d->bt_caidfrom); + pos += snprintf(ret + pos, maxlen - pos, ".%04X", d->bt_srvid); + if (d->bt_caidto) + pos += snprintf(ret + pos, maxlen - pos, ":%04X", d->bt_caidto); + comma = ","; + } + return ret; +} + +/* + * Creates a string ready to write as a token into config or WebIf for groups. You must free the returned value through free_mk_t(). + */ +char *mk_t_group(uint64_t grp) +{ + int32_t i = 0, needed = 1, pos = 0, dot = 0; + + for(i = 0; i < 64; i++) + { + if(grp & ((uint64_t)1 << i)) + { + needed += 2; + if(i > 9) { needed += 1; } + } + } + + char *value; + if(needed == 1 || !cs_malloc(&value, needed)) { return ""; } + char *saveptr = value; + + for(i = 0; i < 64; i++) + { + if(grp & ((uint64_t)1 << i)) + { + if(dot == 0) + { + snprintf(value + pos, needed - (value - saveptr), "%d", i + 1); + if(i > 8) { pos += 2; } + else { pos += 1; } + dot = 1; + } + else + { + snprintf(value + pos, needed - (value - saveptr), ",%d", i + 1); + if(i > 8) { pos += 3; } + else { pos += 2; } + } + } + } + value[pos] = '\0'; + return value; +} + +/* + * Creates a string ready to write as a token into config or WebIf for FTabs (CHID, Ident). You must free the returned value through free_mk_t(). + */ +char *mk_t_ftab(FTAB *ftab) +{ + if (!ftab || !ftab->nfilts) return ""; + + // Worst case scenario where each entry have different + // caid, ident and only one length in it is cs_strlen("1234:123456,") == 12 + int32_t i, j, maxlen = 13 * ftab->nfilts, pos = 0; + + for(i = 0; i < ftab->nfilts; i++) + { + maxlen += ftab->filts[i].nprids * 7; /* cs_strlen("123456,") == 7 */ + } + + char *ret; + if (!cs_malloc(&ret, maxlen)) + return ""; + + const char *semicolon = "", *comma = ""; + + for(i = 0; i < ftab->nfilts; i++) + { + FILTER *cur = &ftab->filts[i]; + pos += snprintf(ret + pos, maxlen - pos, "%s%04X:", semicolon, cur->caid); + semicolon = ";"; + comma = ""; + + for (j = 0; j < cur->nprids; j++) + { + pos += snprintf(ret + pos, maxlen - pos, "%s%06X", comma, cur->prids[j]); + comma = ","; + } + } + return ret; +} + +/* + * Creates a string ready to write as a token into config or WebIf for the camd35 tcp ports. You must free the returned value through free_mk_t(). + */ +char *mk_t_camd35tcp_port(void) +{ +#if defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP) + int32_t i, j, pos = 0, needed = 1; + + /* Precheck to determine how long the resulting string will maximally be (might be a little bit smaller but that shouldn't hurt) */ + for(i = 0; i < cfg.c35_tcp_ptab.nports; ++i) + { + /* Port is maximally 5 chars long, plus comma, plus the @caid, plus the :provid plus the ";" between ports */ + needed += 18; + if(cfg.c35_tcp_ptab.ports[i].ncd && cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids > 0) + { + needed += cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids * 7; + } + } + + char *value; + if(needed == 1 || !cs_malloc(&value, needed)) { return ""; } + char *saveptr = value; + char *dot1 = "", *dot2; + + for(i = 0; i < cfg.c35_tcp_ptab.nports; ++i) + { + if(cfg.c35_tcp_ptab.ports[i].ncd && cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].caid) + { + pos += snprintf(value + pos, needed - (value - saveptr), "%s%d@%04X", dot1, + cfg.c35_tcp_ptab.ports[i].s_port, + cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].caid); + + if(cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids > 0) + { + dot2 = ":"; + for(j = 0; j < cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids; ++j) + { + pos += snprintf(value + pos, needed - (value - saveptr), "%s%06X", + dot2, cfg.c35_tcp_ptab.ports[i].ncd->ncd_ftab.filts[0].prids[j]); + dot2 = ","; + } + } + dot1 = ";"; + } + else + { + pos += snprintf(value + pos, needed - (value - saveptr), "%s%d", + dot1, cfg.c35_tcp_ptab.ports[i].s_port); + dot1 = ";"; + } + } + return value; +#else + return NULL; +#endif +} + +#ifdef MODULE_CCCAM +/* + * Creates a string ready to write as a token into config or WebIf for the cccam tcp ports. You must free the returned value through free_mk_t(). + */ +char *mk_t_cccam_port(void) +{ + int32_t i, pos = 0, needed = CS_MAXPORTS * 6 + 8; + + char *value; + if(!cs_malloc(&value, needed)) { return ""; } + char *dot = ""; + + for(i = 0; i < CS_MAXPORTS; i++) + { + if(!cfg.cc_port[i]) { break; } + + pos += snprintf(value + pos, needed - pos, "%s%d", dot, cfg.cc_port[i]); + dot = ","; + } + return value; +} +#endif + +#ifdef MODULE_GBOX +/* + * Creates a string ready to write as a token into config or WebIf for the gbox udp ports. You must free the returned value through free_mk_t(). + */ +char *mk_t_gbox_port(void) +{ + int32_t i, pos = 0, needed = CS_MAXPORTS * 6 + 8; + + char *value; + if(!cs_malloc(&value, needed)) { return ""; } + char *dot = ""; + + for(i = 0; i < CS_MAXPORTS; i++) + { + if(!cfg.gbox_port[i]) { break; } + + pos += snprintf(value + pos, needed - pos, "%s%d", dot, cfg.gbox_port[i]); + dot = ","; + } + return value; +} + +/* + * Creates a string ready to write as a token into config or WebIf for the gbox proxy card. You must free the returned value through free_mk_t(). + */ +char *mk_t_gbox_proxy_card(void) +{ + int32_t i, pos = 0, needed = GBOX_MAX_PROXY_CARDS * 9 + 8; + + char *value; + if(!cs_malloc(&value, needed)) { return ""; } + char *dot = ""; + + for(i = 0; i < GBOX_MAX_PROXY_CARDS; i++) + { + if(!cfg.gbox_proxy_card[i]) { break; } + + pos += snprintf(value + pos, needed - pos, "%s%08lX", dot, cfg.gbox_proxy_card[i]); + dot = ","; + } + return value; +} + +/* + * Creates a string ready to write as a token into config or WebIf for the gbox ignore peer. You must free the returned value through free_mk_t(). + */ +char *mk_t_gbox_ignored_peer(void) +{ + int32_t i, pos = 0, needed = GBOX_MAX_IGNORED_PEERS * 5 + 8; + + char *value; + if(!cs_malloc(&value, needed)) { return ""; } + char *dot = ""; + + for(i = 0; i < GBOX_MAX_IGNORED_PEERS; i++) + { + if(!cfg.gbox_ignored_peer[i]) { break; } + + pos += snprintf(value + pos, needed - pos, "%s%04hX", dot, cfg.gbox_ignored_peer[i]); + dot = ","; + } + return value; +} + +/* + * Creates a string ready to write as a token into config or WebIf for the gbox accept_remm_peer. You must free the returned value through free_mk_t(). + */ +char *mk_t_accept_remm_peer(void) +{ + int32_t i, pos = 0, needed = GBOX_MAX_REMM_PEERS * 5 + 8; + + char *value; + if(!cs_malloc(&value, needed)) { return ""; } + char *dot = ""; + + for(i = 0; i < GBOX_MAX_REMM_PEERS; i++) + { + if(!cfg.accept_remm_peer[i]) { break; } + + pos += snprintf(value + pos, needed - pos, "%s%04hX", dot, cfg.accept_remm_peer[i]); + dot = ","; + } + return value; +} + +/* + * Creates a string ready to write as a token into config or WebIf for the gbox block ecm. You must free the returned value through free_mk_t(). + */ +char *mk_t_gbox_block_ecm(void) +{ + int32_t i, pos = 0, needed = GBOX_MAX_BLOCKED_ECM * 5 + 8; + + char *value; + if(!cs_malloc(&value, needed)) { return ""; } + char *dot = ""; + + for(i = 0; i < GBOX_MAX_BLOCKED_ECM; i++) + { + if(!cfg.gbox_block_ecm[i]) { break; } + + pos += snprintf(value + pos, needed - pos, "%s%04hX", dot, cfg.gbox_block_ecm[i]); + dot = ","; + } + return value; +} +/* + * Creates a string ready to write as a token into config or WebIf for the gbox SMS dest peers. You must free the returned value through free_mk_t(). + */ +char *mk_t_gbox_dest_peers(void) +{ + int32_t i, pos = 0, needed = GBOX_MAX_DEST_PEERS * 5 + 8; + + char *value; + if(!cs_malloc(&value, needed)) { return ""; } + char *dot = ""; + + for(i = 0; i < GBOX_MAX_DEST_PEERS; i++) + { + if(!cfg.gbox_dest_peers[i]) { break; } + pos += snprintf(value + pos, needed - pos, "%s%04hX", dot, cfg.gbox_dest_peers[i]); + dot = ","; + } + return value; +} +#endif + +#ifdef READER_VIACCESS +/* + * Creates a string ready to write as a token into config or WebIf for AESKeys. You must free the returned value through free_mk_t(). + */ +char *mk_t_aeskeys(struct s_reader *rdr) +{ + AES_ENTRY *current = rdr->aes_list; + int32_t i, pos = 0, needed = 1, prevKeyid = 0, prevCaid = 0; + uint32_t prevIdent = 0; + + /* Precheck for the approximate size that we will need; it's a bit overestimated but we correct that at the end of the function */ + while(current) + { + /* The caid, ident, "@" and the trailing ";" need to be output when they are changing */ + if(prevCaid != current->caid || prevIdent != current->ident) { needed += 12 + (current->keyid * 2); } + /* "0" keys are not saved so we need to check for gaps */ + else if(prevKeyid != current->keyid + 1) { needed += (current->keyid - prevKeyid - 1) * 2; } + /* The 32 byte key plus either the (heading) ":" or "," */ + needed += 33; + prevCaid = current->caid; + prevIdent = current->ident; + prevKeyid = current->keyid; + current = current->next; + } + + /* Set everything back and now create the string */ + current = rdr->aes_list; + prevCaid = 0; + prevIdent = 0; + prevKeyid = 0; + char tmp[needed]; + char dot; + if(needed == 1) { tmp[0] = '\0'; } + char tmpkey[33]; + + while(current) + { + /* A change in the ident or caid means that we need to output caid and ident */ + if(prevCaid != current->caid || prevIdent != current->ident) + { + if(pos > 0) + { + tmp[pos] = ';'; + ++pos; + } + pos += snprintf(tmp + pos, sizeof(tmp) - pos, "%04X@%06X", current->caid, current->ident); + prevKeyid = -1; + dot = ':'; + } + else { dot = ','; } + + /* "0" keys are not saved so we need to check for gaps and output them! */ + for(i = prevKeyid + 1; i < current->keyid; ++i) + { + pos += snprintf(tmp + pos, sizeof(tmp) - pos, "%c0", dot); + dot = ','; + } + + tmp[pos] = dot; + ++pos; + + for(i = 0; i < 16; ++i) + { + snprintf(tmpkey + (i * 2), sizeof(tmpkey) - (i * 2), "%02X", current->plainkey[i]); + } + + /* A key consisting of only FFs has a special meaning (just return what the card outputted) and can be specified more compact */ + if(strcmp(tmpkey, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") == 0) + { + pos += snprintf(tmp + pos, sizeof(tmp) - pos, "FF"); + } + else + { + pos += snprintf(tmp + pos, sizeof(tmp) - pos, "%s", tmpkey); + } + + prevCaid = current->caid; + prevIdent = current->ident; + prevKeyid = current->keyid; + current = current->next; + } + + /* copy to result array of correct size */ + char *value; + if(pos == 0 || !cs_malloc(&value, pos + 1)) { return ""; } + memcpy(value, tmp, pos + 1); + return (value); +} +#endif + +/* + * Creates a string ready to write as a token into config or WebIf for the Newcamd Port. You must free the returned value through free_mk_t(). + */ +char *mk_t_newcamd_port(void) +{ +#ifdef MODULE_NEWCAMD + int32_t i, j, k, pos = 0, needed = 1; + + /* Precheck to determine how long the resulting string will maximally be (might be a little bit smaller but that shouldn't hurt) */ + for(i = 0; i < cfg.ncd_ptab.nports; ++i) + { + /* Port is maximally 5 chars long, plus the @caid, plus the ";" between ports */ + needed += 11; + if(cfg.ncd_ptab.ports[i].ncd) + { + if(cfg.ncd_ptab.ports[i].ncd->ncd_key_is_set) { needed += 30; } + + if(cfg.ncd_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids > 0) + { + needed += cfg.ncd_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids * 7; + } + } + } + char *value; + if(needed == 1 || !cs_malloc(&value, needed)) { return ""; } + char *dot1 = "", *dot2; + + for(i = 0; i < cfg.ncd_ptab.nports; ++i) + { + pos += snprintf(value + pos, needed - pos, "%s%d", dot1, cfg.ncd_ptab.ports[i].s_port); + + // separate DES Key for this port + if(cfg.ncd_ptab.ports[i].ncd) + { + if(cfg.ncd_ptab.ports[i].ncd->ncd_key_is_set) + { + pos += snprintf(value + pos, needed - pos, "{"); + for(k = 0; k < (int32_t)sizeof(cfg.ncd_ptab.ports[i].ncd->ncd_key); k++) + { + pos += snprintf(value + pos, needed - pos, "%02X", cfg.ncd_ptab.ports[i].ncd->ncd_key[k]); + } + pos += snprintf(value + pos, needed - pos, "}"); + } + + pos += snprintf(value + pos, needed - pos, "@%04X", cfg.ncd_ptab.ports[i].ncd->ncd_ftab.filts[0].caid); + + if(cfg.ncd_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids > 0) + { + dot2 = ":"; + for(j = 0; j < cfg.ncd_ptab.ports[i].ncd->ncd_ftab.filts[0].nprids; ++j) + { + pos += snprintf(value + pos, needed - pos, "%s%06X", dot2, (int)cfg.ncd_ptab.ports[i].ncd->ncd_ftab.filts[0].prids[j]); + dot2 = ","; + } + } + } + dot1 = ";"; + } + return value; +#else + return NULL; +#endif +} + +/* + * Creates a string ready to write as a token into config or WebIf for au readers. You must free the returned value through free_mk_t(). + */ +char *mk_t_aureader(struct s_auth *account) +{ + int32_t pos = 0; + char *dot = ""; + + char *value; + if(ll_count(account->aureader_list) == 0 || !cs_malloc(&value, 256)) { return ""; } + value[0] = '\0'; + + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(account->aureader_list); + while((rdr = ll_iter_next(&itr))) + { + pos += snprintf(value + pos, 256 - pos, "%s%s", dot, rdr->label); + dot = ","; + } + + return value; +} + +/* + * Creates a string ready to write as a token into config or WebIf for blocknano and savenano. You must free the returned value through free_mk_t(). + */ +char *mk_t_nano(uint16_t nano) +{ + int32_t i, pos = 0, needed = 0; + + for(i = 0; i < 16; i++) + { + if((1 << i) & nano) + { needed++; } + } + + char *value; + if(nano == 0xFFFF) + { + if(!cs_malloc(&value, 4)) { return ""; } + snprintf(value, 4, "all"); + } + else + { + if(needed == 0 || !cs_malloc(&value, needed * 3 + 1)) { return ""; } + + value[0] = '\0'; + for(i = 0; i < 16; i++) + { + if((1 << i) & nano) + { pos += snprintf(value + pos, (needed * 3) + 1 - pos, "%s%02x", pos ? "," : "", (i + 0x80)); } + } + } + return value; +} + +/* + * Creates a string ready to write as a token into config or WebIf for the sidtab. You must free the returned value through free_mk_t(). + */ +char *mk_t_service(SIDTABS *sidtabs) +{ + int32_t i, pos; + char *dot; + char *value; + struct s_sidtab *sidtab = cfg.sidtab; + if(!sidtab || (!sidtabs->ok && !sidtabs->no) || !cs_malloc(&value, 1024)) { return ""; } + + value[0] = '\0'; + + for(i = pos = 0, dot = ""; sidtab; sidtab = sidtab->next, i++) + { + if(sidtabs->ok & ((SIDTABBITS)1 << i)) + { + pos += snprintf(value + pos, 1024 - pos, "%s%s", dot, sidtab->label); + dot = ","; + } + if(sidtabs->no & ((SIDTABBITS)1 << i)) + { + pos += snprintf(value + pos, 1024 - pos, "%s!%s", dot, sidtab->label); + dot = ","; + } + } + return value; +} + +/* + * Creates a string ready to write as a token into config or WebIf for the logfile parameter. You must free the returned value through free_mk_t(). + */ +char *mk_t_logfile(void) +{ + int32_t pos = 0, needed = 1; + char *value, *dot = ""; + + if(cfg.logtostdout == 1) { needed += 7; } + if(cfg.logtosyslog == 1) { needed += 7; } + if(cfg.logfile) { needed += cs_strlen(cfg.logfile); } + if(needed == 1 || !cs_malloc(&value, needed)) { return ""; } + + if(cfg.logtostdout == 1) + { + pos += snprintf(value + pos, needed - pos, "stdout"); + dot = ";"; + } + + if(cfg.logtosyslog == 1) + { + pos += snprintf(value + pos, needed - pos, "%ssyslog", dot); + dot = ";"; + } + + if(cfg.logfile) + { + snprintf(value + pos, needed - pos, "%s%s", dot, cfg.logfile); + } + return value; +} + +/* + * Creates a string ready to write as a token into config or WebIf for the ecm whitelist. You must free the returned value through free_mk_t(). + */ +char *mk_t_ecm_whitelist(struct s_ecm_whitelist *ecm_whitelist) +{ + if (!ecm_whitelist || !ecm_whitelist->ewnum) return ""; + + // Worst case scenario where each entry have different + // caid, ident and only one length in it is cs_strlen("1234@123456:01;") == 15 + int32_t i, maxlen = 16 * ecm_whitelist->ewnum, pos = 0; + char *ret; + if (!cs_malloc(&ret, maxlen)) + return ""; + + const char *semicolon = "", *comma = ""; + ECM_WHITELIST_DATA *last = NULL; + + for(i = 0; i < ecm_whitelist->ewnum; i++) + { + ECM_WHITELIST_DATA *cur = &ecm_whitelist->ewdata[i]; + bool change = !last || last->caid != cur->caid || last->ident != cur->ident; + + if(change) + { + if(cur->caid && cur->ident) + pos += snprintf(ret + pos, maxlen - pos, "%s%04X@%06X:", semicolon, cur->caid, cur->ident); + else if(cur->caid) + pos += snprintf(ret + pos, maxlen - pos, "%s%04X:", semicolon, cur->caid); + else if(cur->ident) + pos += snprintf(ret + pos, maxlen - pos, "%s@%06X:", semicolon, cur->ident); + else + pos += snprintf(ret + pos, maxlen - pos, "%s", semicolon); + + semicolon = ";"; + comma = ""; + } + pos += snprintf(ret + pos, maxlen - pos, "%s%02X", comma, cur->len); + comma = ","; + last = &ecm_whitelist->ewdata[i]; + } + return ret; +} + +/* + * Creates a string ready to write as a token into config or WebIf for the ECM Headerwhitelist. You must free the returned value through free_mk_t(). + */ +char *mk_t_ecm_hdr_whitelist(struct s_ecm_hdr_whitelist *ecm_hdr_whitelist) +{ + if(!ecm_hdr_whitelist || !ecm_hdr_whitelist->ehnum) return ""; + + // Worst case scenario where each entry have different + // caid, provid and only one header in it is cs_strlen("1234@123456:0102030405060708091011121314151617181920;") == 52 ((sizeof(header) / 2) + 12) + int32_t i, r, maxlen = 53 * ecm_hdr_whitelist->ehnum, pos = 0; + char *ret; + if (!cs_malloc(&ret, maxlen)) + return ""; + + const char *semicolon = "", *comma = ""; + ECM_HDR_WHITELIST_DATA *last = NULL; + + for(i = 0; i < ecm_hdr_whitelist->ehnum; i++) + { + ECM_HDR_WHITELIST_DATA *cur = &ecm_hdr_whitelist->ehdata[i]; + bool change = !last || last->caid != cur->caid || last->provid != cur->provid; + if (change) + { + if (cur->caid && cur->provid) + pos += snprintf(ret + pos, maxlen - pos, "%s%04X@%06X:", semicolon, cur->caid, cur->provid); + else if (cur->caid) + pos += snprintf(ret + pos, maxlen - pos, "%s%04X:", semicolon, cur->caid); + else if (cur->provid) + pos += snprintf(ret + pos, maxlen - pos, "%s@%06X:", semicolon, cur->provid); + else + pos += snprintf(ret + pos, maxlen - pos, "%s", semicolon); + + semicolon = ";"; + comma = ""; + } + pos += snprintf(ret + pos, maxlen - pos, "%s", comma); + for(r = 0; r < cur->len / 2; r++) + pos += snprintf(ret + pos, maxlen - pos, "%02X", cur->header[r]); + comma = ","; + last = &ecm_hdr_whitelist->ehdata[i]; + } + return ret; +} + +/* + * Creates a string ready to write as a token into config or WebIf for an iprange. You must free the returned value through free_mk_t(). + */ +char *mk_t_iprange(struct s_ip *range) +{ + struct s_ip *cip; + char *value, *dot = ""; + int32_t needed = 1, pos = 0; + for(cip = range; cip; cip = cip->next) { needed += 64; } + + char tmp[needed]; + + for(cip = range; cip; cip = cip->next) + { + pos += snprintf(tmp + pos, needed - pos, "%s%s", dot, cs_inet_ntoa(cip->ip[0])); + if(!IP_EQUAL(cip->ip[0], cip->ip[1])) { pos += snprintf(tmp + pos, needed - pos, "-%s", cs_inet_ntoa(cip->ip[1])); } + dot = ","; + } + + if(pos == 0 || !cs_malloc(&value, pos + 1)) { return ""; } + + memcpy(value, tmp, pos + 1); + return value; +} + +/* + * Creates a string ready to write as a token into config or WebIf for the class attribute. You must free the returned value through free_mk_t(). + */ +char *mk_t_cltab(CLASSTAB *clstab) +{ + char *value, *dot = ""; + int32_t i, needed = 1, pos = 0; + for(i = 0; i < clstab->an; ++i) { needed += 3; } + for(i = 0; i < clstab->bn; ++i) { needed += 4; } + + char tmp[needed]; + + for(i = 0; i < clstab->an; ++i) + { + pos += snprintf(tmp + pos, needed - pos, "%s%02x", dot, (int32_t)clstab->aclass[i]); + dot = ","; + } + + for(i = 0; i < clstab->bn; ++i) + { + pos += snprintf(tmp + pos, needed - pos, "%s!%02x", dot, (int32_t)clstab->bclass[i]); + dot = ","; + } + + if(pos == 0 || !cs_malloc(&value, pos + 1)) { return ""; } + + memcpy(value, tmp, pos + 1); + return value; +} + +/* + * Creates a string ready to write as a token into config or WebIf. You must free the returned value through free_mk_t(). + */ +char *mk_t_caidvaluetab(CAIDVALUETAB *caidvaluetab) +{ + if (!caidvaluetab || !caidvaluetab->cvnum) return ""; + + // Max entry length is cs_strlen("1234@65535,") == 11 + int32_t i, maxlen = 12 * caidvaluetab->cvnum, pos = 0; + char *ret; + if (!cs_malloc(&ret, maxlen)) + return ""; + const char *comma = ""; + + for(i = 0; i < caidvaluetab->cvnum; i++) + { + CAIDVALUETAB_DATA *d = &caidvaluetab->cvdata[i]; + if (d->caid < 0x0100) + pos += snprintf(ret + pos, maxlen - pos, "%s%02X:%d", comma, d->caid, d->value); + else + pos += snprintf(ret + pos, maxlen - pos, "%s%04X:%d", comma, d->caid, d->value); + + comma = ","; + } + return ret; +} + +char *mk_t_cacheex_valuetab(CECSPVALUETAB *tab) +{ + if (!tab || !tab->cevnum) return ""; + + int32_t i, size = 2 + tab->cevnum * (4 + 1 + 4 + 1 + 6 + 1 + 4 + 1 + 5 + 1 + 5 + 1); // caid&mask@provid$servid:awtime:dwtime"," + + char *buf; + if(!cs_malloc(&buf, size)) + { return ""; } + + char *ptr = buf; + + for(i = 0; i < tab->cevnum; i++) + { + CECSPVALUETAB_DATA *d = &tab->cevdata[i]; + + if(i) { ptr += snprintf(ptr, size - (ptr - buf), ","); } + + if(d->caid >= 0) + { + if(d->caid == 0) + { + if(d->awtime > 0) + { ptr += snprintf(ptr, size - (ptr - buf), "%d", d->caid); } + } + else if(d->caid < 256) // Do not format 0D as 000D, its a shortcut for 0Dxx: + { + ptr += snprintf(ptr, size - (ptr - buf), "%02X", d->caid); + } + else + { + ptr += snprintf(ptr, size - (ptr - buf), "%04X", d->caid); + } + } + + if(d->cmask >= 0) + { ptr += snprintf(ptr, size - (ptr - buf), "&%04X", d->cmask); } + + if(d->prid >= 0) + { ptr += snprintf(ptr, size - (ptr - buf), "@%06X", d->prid); } + + if(d->srvid >= 0) + { ptr += snprintf(ptr, size - (ptr - buf), "$%04X", d->srvid); } + + if(d->awtime > 0) + { ptr += snprintf(ptr, size - (ptr - buf), ":%d", d->awtime); } + + if(!(d->dwtime > 0)) + { ptr += snprintf(ptr, size - (ptr - buf), ":0"); } + + if(d->dwtime > 0) + { + if((d->caid <= 0) && (d->prid == -1) && (d->srvid == -1) && (d->srvid == -1) && (d->awtime <= 0)) + { + ptr += snprintf(ptr, size - (ptr - buf), "%d", d->dwtime); + } + else + { + ptr += snprintf(ptr, size - (ptr - buf), ":%d", d->dwtime); + } + } + } + *ptr = 0; + return buf; +} + + +char *mk_t_cacheex_cwcheck_valuetab(CWCHECKTAB *tab) +{ + if(!tab || !tab->cwchecknum) { return ""; } + + int32_t i, size = 2 + tab->cwchecknum * (4 + 1 + 4 + 1 + 6 + 1 + 4 + 1 + 5 + 1 + 5 + 1); // caid[&mask][@provid][$servid]:mode:counter"," + char *buf; + if(!cs_malloc(&buf, size)) + { return ""; } + char *ptr = buf; + + for(i = 0; i < tab->cwchecknum; i++) + { + CWCHECKTAB_DATA *d = &tab->cwcheckdata[i]; + + if(i) { ptr += snprintf(ptr, size - (ptr - buf), ","); } + + if(d->caid >= 0) + { + if(d->caid == 0) + { + ptr += snprintf(ptr, size - (ptr - buf), "%d", d->caid); + } + else if(d->caid < 256) // Do not format 0D as 000D, its a shortcut for 0Dxx: + { + ptr += snprintf(ptr, size - (ptr - buf), "%02X", d->caid); + } + else + { + ptr += snprintf(ptr, size - (ptr - buf), "%04X", d->caid); + } + } + + if(d->cmask >= 0) + { ptr += snprintf(ptr, size - (ptr - buf), "&%04X", d->cmask); } + + if(d->prid >= 0) + { ptr += snprintf(ptr, size - (ptr - buf), "@%06X", d->prid); } + + if(d->srvid >= 0) + { ptr += snprintf(ptr, size - (ptr - buf), "$%04X", d->srvid); } + + if(d->mode >= 0) + { ptr += snprintf(ptr, size - (ptr - buf), ":%d", d->mode); } + + if(d->counter > 0) + { ptr += snprintf(ptr, size - (ptr - buf), ":%d", d->counter); } + } + *ptr = 0; + return buf; +} + +char *mk_t_cacheex_hitvaluetab(CECSPVALUETAB *tab) +{ + if (!tab || !tab->cevnum) return ""; + + int32_t i, size = 2 + tab->cevnum * (4 + 1 + 4 + 1 + 6 + 1 + 4 + 1); // caid&mask@provid$servid"," + char *buf; + if(!cs_malloc(&buf, size)) + { return ""; } + char *ptr = buf; + + for(i = 0; i < tab->cevnum; i++) + { + CECSPVALUETAB_DATA *d = &tab->cevdata[i]; + + if(i) { ptr += snprintf(ptr, size - (ptr - buf), ","); } + + if(d->caid > 0) + { + if(d->caid < 256) // Do not format 0D as 000D, its a shortcut for 0Dxx: + { + ptr += snprintf(ptr, size - (ptr - buf), "%02X", d->caid); + } + else + { + ptr += snprintf(ptr, size - (ptr - buf), "%04X", d->caid); + } + + if(d->cmask >= 0) + { ptr += snprintf(ptr, size - (ptr - buf), "&%04X", d->cmask); } + + if(d->prid >= 0) + { ptr += snprintf(ptr, size - (ptr - buf), "@%06X", d->prid); } + + if(d->srvid >= 0) + { ptr += snprintf(ptr, size - (ptr - buf), "$%04X", d->srvid); } + } + } + *ptr = 0; + return buf; +} + +/* + * returns string of comma separated values + */ +char *mk_t_emmbylen(struct s_reader *rdr) +{ + char *value, *pos, *dot = ""; + int32_t num, needed = 0; + struct s_emmlen_range *blocklen; + + if(!rdr->blockemmbylen) + { return ""; } + + LL_ITER it = ll_iter_create(rdr->blockemmbylen); + while((blocklen = ll_iter_next(&it))) + { + needed += 5 + 1; // max digits of int16 + "," + if(blocklen->max == 0) + { needed += 1 + 1; } // "-" + "," + else if(blocklen->min != blocklen->max) + { needed += 1 + 5 + 1; } // "-" + max digits of int16 + "," + } + + // the trailing zero is already included: it's the first "," + if(!cs_malloc(&value, needed)) + { return ""; } + + pos = value; + ll_iter_reset(&it); + while((blocklen = ll_iter_next(&it))) + { + if(blocklen->min == blocklen->max) + { num = snprintf(pos, needed, "%s%d", dot, blocklen->min); } + else if(blocklen->max == 0) + { num = snprintf(pos, needed, "%s%d-", dot, blocklen->min); } + else + { num = snprintf(pos, needed, "%s%d-%d", dot, blocklen->min, blocklen->max); } + + pos += num; + needed -= num; + dot = ","; + } + return value; +} + +/* + * makes string from binary structure + */ +char *mk_t_allowedprotocols(struct s_auth *account) +{ + if(!account->allowedprotocols) + { return ""; } + + int16_t i, tmp = 1, pos = 0, needed = 255, tagcnt; + char *tag[] = {"camd33", "cs357x", "cs378x", "newcamd", "cccam", "gbox", "radegast", "dvbapi", "constcw", "serial"}; + char *value, *dot = ""; + + if(!cs_malloc(&value, needed)) + { return ""; } + + tagcnt = sizeof(tag) / sizeof(char *); + for(i = 0; i < tagcnt; i++) + { + if((account->allowedprotocols & tmp) == tmp) + { + pos += snprintf(value + pos, needed, "%s%s", dot, tag[i]); + dot = ","; + } + tmp = tmp << 1; + } + return value; +} + +/* + * return allowed time frame string from internal array content + * + */ + +char *mk_t_allowedtimeframe(struct s_auth *account) +{ + char *result; + if(!cs_malloc(&result, MAXALLOWEDTF)) + { return ""; } + + if(account->allowedtimeframe_set) + { + char mytime[8]; + uint8_t day; + uint8_t value_in_day = 0; + uint8_t intime = 0; + uint8_t minutes =0; + uint8_t hours; + char septime[2] = {'\0'}; + char sepday[2] = {'\0'}; + + for(day = 0; day < SIZE_SHORTDAY; day++) + { + for(hours = 0; hours < 24; hours++) + { + for(minutes = 0; minutes < 60; minutes++) + { + if(CHECK_BIT(account->allowedtimeframe[day][hours][minutes / 30], (minutes % 30))) + { + if(value_in_day == 0) + { + cs_strncat(result, &sepday[0], MAXALLOWEDTF); + cs_strncat(result, (char *)shortDay[day], MAXALLOWEDTF); + cs_strncat(result, "@", MAXALLOWEDTF); + value_in_day = 1; + intime = 0; + sepday[0] = ';'; + septime[0] = '\0'; + } + + if(!intime) + { + cs_strncat(result, &septime[0], MAXALLOWEDTF); + snprintf(mytime, sizeof(mytime), "%02d:%02d", hours, minutes); + cs_strncat(result, mytime, MAXALLOWEDTF); + cs_strncat(result, "-", MAXALLOWEDTF); + septime[0] = ','; + intime = 1; + } + + // Special case 23H59 is enabled we close the day at 24H00 + if(((hours * 60) + minutes) == 1439) + { + cs_strncat(result, "24:00", MAXALLOWEDTF); + intime = 0; + septime[0] = '\0'; + value_in_day = 0; + } + } + else if(intime) + { + snprintf(mytime, sizeof(mytime), "%02d:%02d", hours, minutes); + cs_strncat(result, mytime, MAXALLOWEDTF); + septime[0] = ','; + intime = 0; + } + } + } + value_in_day = 0; + } + } + else + { + result = ""; + } + return result; +} + +/* + * mk_t-functions give back a constant empty string when allocation fails or when the result is an empty string. + * This function thus checks the stringlength and only frees if necessary. + */ +char *mk_t_cwvote_caidtab(CW_VOTE_CAID_TAB *cwvote_caid_table) +{ + if (!cwvote_caid_table || !cwvote_caid_table->cvcnum) { return ""; } + + // Max entry length is cs_strlen("0000,") == 5 + int32_t i, maxlen = 6 * cwvote_caid_table->cvcnum, pos = 0; + char *ret; + if (!cs_malloc(&ret, maxlen)) + { return ""; } + + const char *comma = ""; + + for (i = 0; i < cwvote_caid_table->cvcnum; i++) + { + pos += snprintf(ret + pos, maxlen - pos, "%s%04X", comma, cwvote_caid_table->cvcdata[i].caid); + comma = ","; + } + return ret; +} + +void free_mk_t(char *value) +{ + if(cs_strlen(value) > 0) { NULLFREE(value); } +} diff --git a/oscam-conf-mk.h b/oscam-conf-mk.h new file mode 100644 index 0000000..925c49c --- /dev/null +++ b/oscam-conf-mk.h @@ -0,0 +1,36 @@ +#ifndef OSCAM_CONF_MK_H_ +#define OSCAM_CONF_MK_H_ + +extern char *mk_t_caidtab(CAIDTAB *ctab); +extern char *mk_t_caidvaluetab(CAIDVALUETAB *tab); +extern char *mk_t_cacheex_valuetab(CECSPVALUETAB *tab); +extern char *mk_t_cacheex_cwcheck_valuetab(CWCHECKTAB *tab); +extern char *mk_t_cacheex_hitvaluetab(CECSPVALUETAB *tab); +extern char *mk_t_tuntab(TUNTAB *ttab); +extern char *mk_t_group(uint64_t grp); +extern char *mk_t_ftab(FTAB *ftab); +extern char *mk_t_camd35tcp_port(void); +extern char *mk_t_cccam_port(void); +extern char *mk_t_gbox_port(void); +extern char *mk_t_gbox_proxy_card(void); +extern char *mk_t_gbox_ignored_peer(void); +extern char *mk_t_accept_remm_peer(void); +extern char *mk_t_gbox_block_ecm(void); +extern char *mk_t_gbox_dest_peers(void); +extern char *mk_t_aeskeys(struct s_reader *rdr); +extern char *mk_t_newcamd_port(void); +extern char *mk_t_aureader(struct s_auth *account); +extern char *mk_t_nano(uint16_t nano); +extern char *mk_t_service(SIDTABS *sidtabs); +extern char *mk_t_logfile(void); +extern char *mk_t_iprange(struct s_ip *range); +extern char *mk_t_ecm_whitelist(struct s_ecm_whitelist *ecm_whitelist); +extern char *mk_t_ecm_hdr_whitelist(struct s_ecm_hdr_whitelist *ecm_hdr_whitelist); +extern char *mk_t_cltab(CLASSTAB *clstab); +extern char *mk_t_emmbylen(struct s_reader *rdr); +extern char *mk_t_allowedprotocols(struct s_auth *account); +extern char *mk_t_allowedtimeframe(struct s_auth *account); +extern char *mk_t_cwvote_caidtab(CW_VOTE_CAID_TAB *cwvote_caid_table); +extern void free_mk_t(char *value); + +#endif diff --git a/oscam-conf.c b/oscam-conf.c new file mode 100644 index 0000000..3cf55e3 --- /dev/null +++ b/oscam-conf.c @@ -0,0 +1,581 @@ +#define MODULE_LOG_PREFIX "config" + +#include "globals.h" +#include "oscam-conf.h" +#include "oscam-config.h" +#include "oscam-files.h" +#include "oscam-garbage.h" +#include "oscam-string.h" + +#define CONFVARWIDTH 30 + +/* Returns the default value if string length is zero, otherwise atoi is called*/ +int32_t strToIntVal(char *value, int32_t defaultvalue) +{ + if(cs_strlen(value) == 0) { return defaultvalue; } + errno = 0; // errno should be set to 0 before calling strtol + int32_t i = strtol(value, NULL, 10); + return (errno == 0) ? i : defaultvalue; +} + +/* Returns the default value if string length is zero, otherwise strtoul is called*/ +uint32_t strToUIntVal(char *value, uint32_t defaultvalue) +{ + if(cs_strlen(value) == 0) { return defaultvalue; } + errno = 0; // errno should be set to 0 before calling strtoul + uint32_t i = strtoul(value, NULL, 10); + return (errno == 0) ? i : defaultvalue; +} + +/* Replacement of fprintf which adds necessary whitespace to fill up the varname to a fixed width. + If varname is longer than CONFVARWIDTH, no whitespace is added*/ +void fprintf_conf(FILE *f, const char *varname, const char *fmtstring, ...) +{ + int32_t varlen = cs_strlen(varname); + int32_t max = (varlen > CONFVARWIDTH) ? varlen : CONFVARWIDTH; + char varnamebuf[max + 3]; + char *ptr = varnamebuf + varlen; + va_list argptr; + + cs_strncpy(varnamebuf, varname, sizeof(varnamebuf)); + while(varlen < CONFVARWIDTH) + { + ptr[0] = ' '; + ++ptr; + ++varlen; + } + cs_strncpy(ptr, "= ", sizeof(varnamebuf) - (ptr - varnamebuf)); + if(fwrite(varnamebuf, sizeof(char), cs_strlen(varnamebuf), f)) + { + if(cs_strlen(fmtstring) > 0) + { + va_start(argptr, fmtstring); + vfprintf(f, fmtstring, argptr); + va_end(argptr); + } + } +} + +int config_list_parse(const struct config_list *clist, const char *token, char *value, void *config_data) +{ + const struct config_list *c; + for(c = clist; c->opt_type != OPT_UNKNOWN; c++) + { + if(c->opt_type == OPT_SAVE_FUNC || c->opt_type == OPT_FIXUP_FUNC) + { continue; } + if(strcasecmp(token, c->config_name) != 0) + { continue; } + void *var = config_data + c->var_offset; + switch(c->opt_type) + { + case OPT_INT8: + { + int8_t tmp = (int8_t)strToIntVal(value, c->def.d_int8); + *(int8_t *)var = tmp; + return 1; + } + case OPT_UINT8: + { + uint32_t tmp = strToUIntVal(value, c->def.d_uint8); + *(uint8_t *)var = (uint8_t)(tmp <= 0xff ? tmp : 0xff); + return 1; + } + case OPT_INT32: + { + int32_t tmp = strToIntVal(value, c->def.d_int32); + memcpy(var, &tmp, sizeof(int32_t)); + return 1; + } + case OPT_UINT32: + { + uint32_t tmp = strToUIntVal(value, c->def.d_uint32); + memcpy(var, &tmp, sizeof(uint32_t)); + return 1; + } + case OPT_FLOAT: + { + float tmp = (float)atof(value); // Użyj atof do konwersji na float + memcpy(var, &tmp, sizeof(float)); + return 1; + } + case OPT_STRING: + { + char **scfg = var; + if(c->def.d_char && cs_strlen(value) == 0) // Set default + { value = c->def.d_char; } + NULLFREE(*scfg); + if(cs_strlen(value)) + { *scfg = cs_strdup(value); } + return 1; + } + case OPT_SSTRING: + { + char *scfg = var; + if(c->def.d_char && cs_strlen(value) == 0) // Set default + { value = c->def.d_char; } + scfg[0] = '\0'; + unsigned int len = cs_strlen(value); + if(len) + { + cs_strncpy(scfg, value, c->str_size); + if(len > c->str_size) + { + fprintf(stderr, "WARNING: Config value for '%s' (%s, len=%u) exceeds max length: %d (%s)\n", + token, value, len, c->str_size - 1, scfg); + } + } + return 1; + } + case OPT_HEX_ARRAY: + { + uint8_t *hex_array = var; + if(!cs_strlen(value)) + { memset(hex_array, 0, c->def.array_size); } + else if(key_atob_l(value, hex_array, c->def.array_size * 2)) + { + memset(hex_array, 0, c->def.array_size); + fprintf(stderr, "WARNING: Config value for '%s' (%s, len=%zu) requires %d chars.\n", + token, value, cs_strlen(value), c->def.array_size * 2); + } + return 1; + } + case OPT_FUNC: + { + c->ops.process_fn(token, value, var, NULL); + return 1; + } + case OPT_FUNC_EXTRA: + { + c->ops.process_fn_extra(token, value, var, c->def.d_extra, NULL); + return 1; + } + case OPT_FIXUP_FUNC: + case OPT_SAVE_FUNC: + return 1; + case OPT_UNKNOWN: + { + fprintf(stderr, "Unknown config type (%s = %s).", token, value); + break; + } + } + } + return 0; +} + +void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_data, int save_all, + bool (*check_func)(const struct config_list *clist, void *config_data, const char *setting)) +{ + const struct config_list *c; + for(c = clist; c->opt_type != OPT_UNKNOWN; c++) + { + void *var = config_data + c->var_offset; + if(check_func && c->opt_type != OPT_UNKNOWN) + { + if(!check_func(clist, config_data, c->config_name)) + { continue; } + } + switch(c->opt_type) + { + case OPT_INT8: + { + int8_t val = *(int8_t *)var; + + // always save pmt_mode, and all cwvote settings, because external tools parse it + if(save_all || val != c->def.d_int8 || !strcmp(c->config_name, "pmt_mode") || + !strcmp(c->config_name, "cwvote_enabled") || !strcmp(c->config_name, "cwvote_log_enabled")) + { + fprintf_conf(f, c->config_name, "%d\n", val); + } + continue; + } + case OPT_UINT8: + { + uint8_t val = *(uint8_t *)var; + if(save_all || val != c->def.d_uint8) + { + fprintf_conf(f, c->config_name, "%u\n", val); + } + continue; + } + case OPT_INT32: + { + int32_t val; + memcpy(&val, var, sizeof(int32_t)); + // always save all cwvote settings + if(save_all || val != c->def.d_int32 || + !strcmp(c->config_name, "cwvote_timeout") || !strcmp(c->config_name, "cwvote_min_votes") || + !strcmp(c->config_name, "cwvote_max_candidates") || !strcmp(c->config_name, "cwvote_compare_len") || + !strcmp(c->config_name, "cwvote_fallback")) + { + fprintf_conf(f, c->config_name, "%d\n", val); + } + continue; + } + case OPT_UINT32: + { + uint32_t val; + memcpy(&val, var, sizeof(uint32_t)); + if(save_all || val != c->def.d_uint32) + { + fprintf_conf(f, c->config_name, "%u\n", val); + } + continue; + } + case OPT_FLOAT: + { + float val; + memcpy(&val, var, sizeof(float)); + // always save cwvote_local_weight + if(save_all || val != c->def.d_float || !strcmp(c->config_name, "cwvote_local_weight")) + { + fprintf_conf(f, c->config_name, "%g\n", val); + } + continue; + } + case OPT_STRING: + { + char **val = var; + if(save_all || !streq(*val, c->def.d_char)) + { + fprintf_conf(f, c->config_name, "%s\n", *val ? *val : ""); + } + continue; + } + case OPT_SSTRING: + { + char *val = var; + if(save_all || !streq(val, c->def.d_char)) + { + fprintf_conf(f, c->config_name, "%s\n", val[0] ? val : ""); + } + continue; + } + case OPT_HEX_ARRAY: + { + uint8_t *hex_array = var; + uint32_t ok = array_has_nonzero_byte(hex_array, c->def.array_size); + if(save_all || ok) + { + fprintf_conf(f, c->config_name, "%s", ""); // it should not have \n at the end + if(ok) + { + for(ok = 0; ok < c->def.array_size; ok++) + { + fprintf(f, "%02X", hex_array[ok]); + } + } + fprintf(f, "\n"); + } + continue; + } + case OPT_FUNC: + { + // always save cwvote_caids + if(save_all || !strcmp(c->config_name, "cwvote_caids")) + { + c->ops.process_fn((const char *)c->config_name, NULL, var, f); + } + else + { + // Call the function only if it's not a cwvote_caids and not save_all + // or if it's a cwvote_caids but save_all is false (handled above) + c->ops.process_fn((const char *)c->config_name, NULL, var, f); + } + continue; + } + case OPT_FUNC_EXTRA: + { + c->ops.process_fn_extra((const char *)c->config_name, NULL, var, c->def.d_extra, f); + continue; + } + case OPT_FIXUP_FUNC: + case OPT_SAVE_FUNC: + continue; + case OPT_UNKNOWN: + break; + } + } +} + +bool config_list_should_be_saved(const struct config_list *clist, void *var) +{ + const struct config_list *c; + for(c = clist; c->opt_type != OPT_UNKNOWN; c++) + { + if(c->opt_type == OPT_SAVE_FUNC) + { + return c->ops.should_save_fn(var); + } + } + return true; +} + +void config_list_apply_fixups(const struct config_list *clist, void *var) +{ + const struct config_list *c; + for(c = clist; c->opt_type != OPT_UNKNOWN; c++) + { + if(c->opt_type == OPT_FIXUP_FUNC) + { + c->ops.fixup_fn(var); + break; + } + } +} + +void config_list_set_defaults(const struct config_list *clist, void *config_data) +{ + const struct config_list *c; + for(c = clist; c->opt_type != OPT_UNKNOWN; c++) + { + void *var = config_data + c->var_offset; + switch(c->opt_type) + { + case OPT_INT8: + { + *(int8_t *)var = c->def.d_int8; + break; + } + case OPT_UINT8: + { + *(uint8_t *)var = c->def.d_uint8; + break; + } + case OPT_INT32: + { + memcpy(var, &c->def.d_int32, sizeof(int32_t)); + break; + } + case OPT_UINT32: + { + memcpy(var, &c->def.d_uint32, sizeof(uint32_t)); + break; + } + case OPT_FLOAT: + { + memcpy(var, &c->def.d_float, sizeof(float)); + break; + } + case OPT_STRING: + { + char **scfg = var; + NULLFREE(*scfg); + if(c->def.d_char) + { *scfg = cs_strdup(c->def.d_char); } + break; + } + case OPT_SSTRING: + { + char *scfg = var; + scfg[0] = '\0'; + if(c->def.d_char && cs_strlen(c->def.d_char)) + { cs_strncpy(scfg, c->def.d_char, c->str_size); } + break; + } + case OPT_HEX_ARRAY: + { + uint8_t *hex_array = var; + memset(hex_array, 0, c->def.array_size); + break; + } + case OPT_FUNC: + { + c->ops.process_fn((const char *)c->config_name, "", var, NULL); + break; + } + case OPT_FUNC_EXTRA: + { + c->ops.process_fn_extra((const char *)c->config_name, "", var, c->def.d_extra, NULL); + break; + } + case OPT_SAVE_FUNC: + case OPT_FIXUP_FUNC: + case OPT_UNKNOWN: + continue; + } + } + return; +} + +void config_list_free_values(const struct config_list *clist, void *config_data) +{ + const struct config_list *c; + for(c = clist; c->opt_type != OPT_UNKNOWN; c++) + { + void *var = config_data + c->var_offset; + if(c->opt_type == OPT_STRING) + { + char **scfg = var; + NULLFREE(*scfg); + } + if(c->free_value && (c->opt_type == OPT_FUNC || c->opt_type == OPT_FUNC_EXTRA)) + { + c->free_value(var); + } + } + return; +} + +void config_list_gc_values(const struct config_list *clist, void *config_data) +{ + const struct config_list *c; + for(c = clist; c->opt_type != OPT_UNKNOWN; c++) + { + void *var = config_data + c->var_offset; + if(c->opt_type == OPT_STRING) + { + char **scfg = var; + add_garbage(*scfg); + } + } + return; +} + +int config_section_is_active(const struct config_sections *sec) +{ + if(!sec) + { return 0; } + if(sec->config[0].opt_type == OPT_UNKNOWN) + { return 0; } + return 1; +} + +const struct config_sections *config_find_section(const struct config_sections *conf, char *section_name) +{ + const struct config_sections *sec; + for(sec = conf; sec && sec->section; sec++) + { + if(streq(section_name, sec->section)) + { + return sec; + } + } + return NULL; +} + +void config_sections_save(const struct config_sections *conf, FILE *f, void *var) +{ + const struct config_sections *sec; + for(sec = conf; sec && sec->section; sec++) + { + if(config_section_is_active(sec) && config_list_should_be_saved(sec->config, var)) + { + fprintf(f, "[%s]\n", sec->section); + config_list_apply_fixups(sec->config, var); + config_list_save(f, sec->config, var, cfg.http_full_cfg); + fprintf(f, "\n"); + } + } +} + +void config_sections_set_defaults(const struct config_sections *conf, void *var) +{ + const struct config_sections *sec; + for(sec = conf; sec && sec->section; sec++) + { + if(config_section_is_active(sec)) + { config_list_set_defaults(sec->config, var); } + } +} + +void config_sections_free(const struct config_sections *conf, void *var) +{ + const struct config_sections *sec; + for(sec = conf; sec && sec->section; sec++) + { + if(config_section_is_active(sec)) + { + config_list_free_values(sec->config, var); + } + } +} + +void config_set_value(const struct config_sections *conf, char *section, const char *token, char *value, void *var) +{ + const struct config_sections *sec = config_find_section(conf, section); + if(!sec) + { + fprintf(stderr, "WARNING: Unknown section '%s'.\n", section); + return; + } + if(config_section_is_active(sec)) + { + if(!config_list_parse(sec->config, token, value, var)) + { + fprintf(stderr, "WARNING: In section [%s] unknown setting '%s=%s' tried.\n", + section, token, value); + } + } + else + { + fprintf(stderr, "WARNING: Section is not active '%s'.\n", section); + } +} + +static FILE *__open_config_file(const char *conf_filename, bool die_on_err) +{ + char filename[256]; + FILE *f = fopen(get_config_filename(filename, sizeof(filename), conf_filename), "r"); + if(!f) + { + if(die_on_err) + { + fprintf(stderr, "ERROR: Cannot open file \"%s\" (errno=%d %s)", filename, errno, strerror(errno)); + fprintf(stderr, "\n"); + exit(1); + } + else + { + cs_log_dbg(D_TRACE, "INFO: Cannot open file \"%s\" (errno=%d %s)", filename, errno, strerror(errno)); + } + return NULL; + } + return f; +} + +FILE *open_config_file(const char *conf_filename) +{ + return __open_config_file(conf_filename, false); +} + +FILE *open_config_file_or_die(const char *conf_filename) +{ + return __open_config_file(conf_filename, true); +} + + +FILE *create_config_file(const char *conf_filename) +{ + char temp_file[256]; + get_config_filename(temp_file, sizeof(temp_file), conf_filename); + if (!cs_strncat(temp_file, ".tmp", sizeof(temp_file))) { + return NULL; + } + FILE *f = fopen(temp_file, "w"); + if(!f) + { + cs_log("ERROR: Cannot create file \"%s\" (errno=%d %s)", temp_file, errno, strerror(errno)); + return NULL; + } + fprintf(f, "# %s generated automatically by Streamboard OSCam %s\n", + conf_filename, CS_VERSION); + fprintf(f, "# Read more: %s/blob/master/Distribution/doc/txt/%s.txt\n\n", + SCM_URL, conf_filename); + return f; +} + +bool flush_config_file(FILE *f, const char *conf_filename) +{ + char dst_file[220], tmp_file[220], bak_file[220]; // Zmniejszono rozmiar bufora, aby uniknąć potencjalnych problemów z przepełnieniem + get_config_filename(dst_file, sizeof(dst_file), conf_filename); + memcpy(tmp_file, dst_file, sizeof(tmp_file)); + memcpy(bak_file, dst_file, sizeof(bak_file)); + strncat(tmp_file, ".tmp", sizeof(tmp_file) - cs_strlen(tmp_file) - 1); + strncat(bak_file, ".bak", sizeof(bak_file) - cs_strlen(bak_file) - 1); + if(f) + { + fclose(f); + } + int32_t result = safe_overwrite_with_bak(dst_file, tmp_file, bak_file, cfg.http_overwrite_bak_file); + return result; +} diff --git a/oscam-conf.h b/oscam-conf.h new file mode 100644 index 0000000..780d0c5 --- /dev/null +++ b/oscam-conf.h @@ -0,0 +1,232 @@ +#ifndef OSCAM_CONF_H +#define OSCAM_CONF_H + +#define MAXLINESIZE 16384 + +enum opt_types +{ + OPT_UNKNOWN = 0, + OPT_INT8, + OPT_UINT8, + OPT_INT32, + OPT_UINT32, + OPT_FLOAT, + OPT_STRING, + OPT_SSTRING, + OPT_HEX_ARRAY, + OPT_FUNC, + OPT_FUNC_EXTRA, + OPT_SAVE_FUNC, + OPT_FIXUP_FUNC, +}; + +struct config_list +{ + enum opt_types opt_type; + char *config_name; + size_t var_offset; + unsigned int str_size; + + union { + int8_t d_int8; + uint8_t d_uint8; + int32_t d_int32; + uint32_t d_uint32; + float d_float; + char *d_char; + long d_extra; + uint32_t array_size; + } def; + + union { + void (*process_fn)(const char *token, char *value, void *setting, FILE *config_file); + void (*process_fn_extra)(const char *token, char *value, void *setting, long extra, FILE *config_file); + bool (*should_save_fn)(void *var); + void (*fixup_fn)(void *var); + } ops; + + void (*free_value)(void *setting); +}; + +#define DEF_OPT_INT8(__name, __var_ofs, __default) \ + { \ + .opt_type = OPT_INT8, \ + .config_name = __name, \ + .var_offset = __var_ofs, \ + .str_size = 0, \ + .def.d_int8 = __default, \ + .ops.process_fn = NULL, \ + .free_value = NULL \ + } + +#define DEF_OPT_UINT8(__name, __var_ofs, __default) \ + { \ + .opt_type = OPT_UINT8, \ + .config_name = __name, \ + .var_offset = __var_ofs, \ + .str_size = 0, \ + .def.d_uint8 = __default, \ + .ops.process_fn = NULL, \ + .free_value = NULL \ + } + +#define DEF_OPT_INT32(__name, __var_ofs, __default) \ + { \ + .opt_type = OPT_INT32, \ + .config_name = __name, \ + .var_offset = __var_ofs, \ + .str_size = 0, \ + .def.d_int32 = __default, \ + .ops.process_fn = NULL, \ + .free_value = NULL \ + } + +#define DEF_OPT_UINT32(__name, __var_ofs, __default) \ + { \ + .opt_type = OPT_UINT32, \ + .config_name = __name, \ + .var_offset = __var_ofs, \ + .str_size = 0, \ + .def.d_uint32 = __default, \ + .ops.process_fn = NULL, \ + .free_value = NULL \ + } + +#define DEF_OPT_FLOAT(__name, __var_ofs, __default) \ + { \ + .opt_type = OPT_FLOAT, \ + .config_name = __name, \ + .var_offset = __var_ofs, \ + .str_size = 0, \ + .def.d_float = __default, \ + .ops.process_fn = NULL, \ + .free_value = NULL \ + } + +#define DEF_OPT_STR(__name, __var_ofs, __default) \ + { \ + .opt_type = OPT_STRING, \ + .config_name = __name, \ + .var_offset = __var_ofs, \ + .str_size = 0, \ + .def.d_char = __default, \ + .ops.process_fn = NULL, \ + .free_value = NULL \ + } + +#define DEF_OPT_SSTR(__name, __var_ofs, __default, __str_size) \ + { \ + .opt_type = OPT_SSTRING, \ + .config_name = __name, \ + .var_offset = __var_ofs, \ + .str_size = __str_size, \ + .def.d_char = __default, \ + .ops.process_fn = NULL, \ + .free_value = NULL \ + } + +#define DEF_OPT_HEX(__name, __var_ofs, __array_size) \ + { \ + .opt_type = OPT_HEX_ARRAY, \ + .config_name = __name, \ + .var_offset = __var_ofs, \ + .str_size = 0, \ + .def.array_size = __array_size, \ + .ops.process_fn = NULL, \ + .free_value = NULL \ + } + +#define DEF_OPT_FUNC(__name, __var_ofs, __process_fn, ...) \ + { \ + .opt_type = OPT_FUNC, \ + .config_name = __name, \ + .var_offset = __var_ofs, \ + .str_size = 0, \ + .def.d_int32 = 0, /* Inicjalizacja pola unii, aby uniknąć niezainicjalizowanych wartości */ \ + .ops.process_fn = __process_fn, \ + ##__VA_ARGS__ \ + } + +#define DEF_OPT_FUNC_X(__name, __var_ofs, __process_fn_extra, __extra, ...) \ + { \ + .opt_type = OPT_FUNC_EXTRA, \ + .config_name = __name, \ + .var_offset = __var_ofs, \ + .str_size = 0, \ + .def.d_extra = __extra, \ + .ops.process_fn_extra = __process_fn_extra, \ + ##__VA_ARGS__ \ + } + +#define DEF_OPT_SAVE_FUNC(__fn) \ + { \ + .opt_type = OPT_SAVE_FUNC, \ + .config_name = NULL, \ + .var_offset = 0, \ + .str_size = 0, \ + .def.d_int32 = 0, \ + .ops.should_save_fn = __fn, \ + .free_value = NULL \ + } + +#define DEF_OPT_FIXUP_FUNC(__fn) \ + { \ + .opt_type = OPT_FIXUP_FUNC, \ + .config_name = NULL, \ + .var_offset = 0, \ + .str_size = 0, \ + .def.d_int32 = 0, \ + .ops.fixup_fn = __fn, \ + .free_value = NULL \ + } + +#define DEF_LAST_OPT \ + { \ + .opt_type = OPT_UNKNOWN, \ + .config_name = NULL, \ + .var_offset = 0, \ + .str_size = 0, \ + .def.d_int32 = 0, \ + .ops.process_fn = NULL, \ + .free_value = NULL \ + } + +struct config_sections +{ + const char *section; + const struct config_list *config; +}; + +int32_t strToIntVal(char *value, int32_t defaultvalue); +uint32_t strToUIntVal(char *value, uint32_t defaultvalue); + +void fprintf_conf(FILE *f, const char *varname, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); + +int config_list_parse(const struct config_list *clist, const char *token, char *value, void *config_data); +void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_data, int save_all, + bool (*check_func)(const struct config_list *clist, void *config_data, const char *setting) +); +static inline void config_list_save(FILE *f, const struct config_list *clist, void *config_data, int save_all) +{ + config_list_save_ex(f, clist, config_data, save_all, NULL); +} +void config_list_apply_fixups(const struct config_list *clist, void *var); +bool config_list_should_be_saved(const struct config_list *clist, void *var); +void config_list_set_defaults(const struct config_list *clist, void *config_data); +void config_list_free_values(const struct config_list *clist, void *config_data); +void config_list_gc_values(const struct config_list *clist, void *config_data); + +int config_section_is_active(const struct config_sections *sec); +const struct config_sections *config_find_section(const struct config_sections *conf, char *section_name); +void config_sections_save(const struct config_sections *conf, FILE *f, void *var); +void config_sections_set_defaults(const struct config_sections *conf, void *var); +void config_sections_free(const struct config_sections *conf, void *var); + +void config_set_value(const struct config_sections *conf, char *section, const char *token, char *value, void *var); + +FILE *open_config_file(const char *conf_filename); +FILE *open_config_file_or_die(const char *conf_filename); +FILE *create_config_file(const char *conf_filename); +bool flush_config_file(FILE *f, const char *conf_filename); + +#endif diff --git a/oscam-config-account.c b/oscam-config-account.c new file mode 100644 index 0000000..bfdf1c5 --- /dev/null +++ b/oscam-config-account.c @@ -0,0 +1,707 @@ +#define MODULE_LOG_PREFIX "config" + +#include "globals.h" +#include "module-anticasc.h" +#include "oscam-array.h" +#include "oscam-client.h" +#include "oscam-conf.h" +#include "oscam-conf-chk.h" +#include "oscam-conf-mk.h" +#include "oscam-config.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-string.h" +#ifdef CS_CACHEEX_AIO +#include "module-cacheex.h" +#endif +#define cs_user "oscam.user" + +static void account_tosleep_fn(const char *token, char *value, void *setting, FILE *f) +{ + int32_t *tosleep = setting; + if(value) + { + *tosleep = strToIntVal(value, cfg.tosleep); + return; + } + if(*tosleep != cfg.tosleep || cfg.http_full_cfg) + { fprintf_conf(f, token, "%d\n", *tosleep); } +} + +static void account_c35_suppresscmd08_fn(const char *token, char *value, void *setting, FILE *f) +{ + int8_t *c35_suppresscmd08 = setting; + if(value) + { + *c35_suppresscmd08 = (int8_t)strToIntVal(value, cfg.c35_suppresscmd08); + return; + } + if(*c35_suppresscmd08 != cfg.c35_suppresscmd08 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%d\n", *c35_suppresscmd08); } +} + +/*static void account_umaxidle_fn(const char *token, char *value, void *setting, FILE *f) +{ + int32_t *umaxidle = setting; + if(value) + { + *umaxidle = (int32_t)strToIntVal(value, cfg.umaxidle); + return; + } + if(*umaxidle != cfg.umaxidle || cfg.http_full_cfg) + { fprintf_conf(f, token, "%u\n", *umaxidle); } +} +*/ + +static void account_ncd_keepalive_fn(const char *token, char *value, void *setting, FILE *f) +{ + int8_t *ncd_keepalive = setting; + int8_t def_value = 0; +#ifdef MODULE_NEWCAMD + def_value = cfg.ncd_keepalive; +#endif + if(value) + { + *ncd_keepalive = (int8_t)strToIntVal(value, def_value); + return; + } + if(*ncd_keepalive != def_value || cfg.http_full_cfg) + { fprintf_conf(f, token, "%d\n", *ncd_keepalive); } +} + +static void account_allowedprotocols_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_auth *account = setting; + if(value) + { + account->allowedprotocols = 0; + if(cs_strlen(value) > 3) + { + char *ptr, *saveptr1 = NULL; + for(ptr = strtok_r(value, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1)) + { + if(streq(ptr, "camd33")) { account->allowedprotocols |= LIS_CAMD33TCP; } + else if(streq(ptr, "camd35")) { account->allowedprotocols |= LIS_CAMD35UDP; } + else if(streq(ptr, "cs357x")) { account->allowedprotocols |= LIS_CAMD35UDP; } + else if(streq(ptr, "cs378x")) { account->allowedprotocols |= LIS_CAMD35TCP; } + else if(streq(ptr, "newcamd")) { account->allowedprotocols |= LIS_NEWCAMD; } + else if(streq(ptr, "cccam")) { account->allowedprotocols |= LIS_CCCAM; } + else if(streq(ptr, "csp")) { account->allowedprotocols |= LIS_CSPUDP; } + else if(streq(ptr, "gbox")) { account->allowedprotocols |= LIS_GBOX; } + else if(streq(ptr, "radegast")) { account->allowedprotocols |= LIS_RADEGAST; } + else if(streq(ptr, "scam")) { account->allowedprotocols |= LIS_SCAM; } + // these have no listener ports so it doesn't make sense + else if(streq(ptr, "dvbapi")) { account->allowedprotocols |= LIS_DVBAPI; } + else if(streq(ptr, "constcw")) { account->allowedprotocols |= LIS_CONSTCW; } + else if(streq(ptr, "serial")) { account->allowedprotocols |= LIS_SERIAL; } + } + } + return; + } + if(account->allowedprotocols || cfg.http_full_cfg) + { + value = mk_t_allowedprotocols(account); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +static void account_au_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_auth *account = setting; + if(value) + { + // set default values for usage during runtime from Webif + account->autoau = 0; + if(!account->aureader_list) + { account->aureader_list = ll_create("aureader_list"); } + if(streq(value, "1")) + { account->autoau = 1; } + ll_clear(account->aureader_list); + LL_ITER itr = ll_iter_create(configured_readers); + struct s_reader *rdr; + char *pch, *saveptr1 = NULL; + for(pch = strtok_r(value, ",", &saveptr1); pch != NULL; pch = strtok_r(NULL, ",", &saveptr1)) + { + ll_iter_reset(&itr); + while((rdr = ll_iter_next(&itr))) + { + if(streq(rdr->label, pch) || account->autoau) + { + ll_append(account->aureader_list, rdr); + } + } + } + return; + } + if(account->autoau == 1) + { + fprintf_conf(f, token, "%d\n", account->autoau); + } + else if(account->aureader_list) + { + value = mk_t_aureader(account); + if(cs_strlen(value) > 0) + { fprintf_conf(f, token, "%s\n", value); } + free_mk_t(value); + } + else if(cfg.http_full_cfg) + { + fprintf_conf(f, token, "%s\n", ""); + } +} + +static void account_expdate_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_auth *account = setting; + if(value) + { + if(!value[0]) + { + account->expirationdate = (time_t)0; + return; + } + int i; + struct tm cstime; + char *ptr1, *saveptr1 = NULL; + memset(&cstime, 0, sizeof(cstime)); + for(i = 0, ptr1 = strtok_r(value, "-/", &saveptr1); i < 3 && ptr1; ptr1 = strtok_r(NULL, "-/", &saveptr1), i++) + { + switch(i) + { + case 0: + cstime.tm_year = atoi(ptr1) - 1900; + break; + case 1: + cstime.tm_mon = atoi(ptr1) - 1; + break; + case 2: + cstime.tm_mday = atoi(ptr1); + break; + } + } + cstime.tm_hour = 23; + cstime.tm_min = 59; + cstime.tm_sec = 59; + cstime.tm_isdst = -1; + account->expirationdate = mktime(&cstime); + return; + } + if(account->expirationdate || cfg.http_full_cfg) + { + char buf[16]; + struct tm timeinfo; + localtime_r(&account->expirationdate, &timeinfo); + strftime(buf, 16, "%Y-%m-%d", &timeinfo); + fprintf_conf(f, token, "%s\n", streq(buf, "1970-01-01") ? "" : buf); + } +} + +static void account_allowedtimeframe_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_auth *account = setting; + int32_t i, j, t, startt, endt; + char *dest; + uint8_t day_idx; + int32_t allowed[4]; + uint32_t tempo = 0; + + char *ptr1, *ptr2, *ptr3, *saveptr1 = NULL, *saveptr2 = NULL; + + if(value) + { + //First empty allowedtimeframe array very important otherwise new config won't be properly set + for(i = 0; i < SIZE_SHORTDAY; i++) + { + for(j = 0; j < 24; j++) + { + account->allowedtimeframe[i][j][0] = 0; + account->allowedtimeframe[i][j][1] = 0; + } + } + account->allowedtimeframe_set=0; + strtoupper(value); + + for(i = 0, ptr1 = strtok_r(value, ";", &saveptr1); (ptr1); ptr1 = strtok_r(NULL, ";", &saveptr1), i++) + { + if((ptr2 = strchr(trim(ptr1), '@'))) + { + *ptr2++ = '\0'; // clean up @ symbol + // ptr1 is the day + dest = strstr(weekdstr,ptr1); + day_idx = (dest - weekdstr) / 3; + + for(j = 0, ptr3 = strtok_r(ptr2, ",", &saveptr2); (ptr3); ptr3 = strtok_r(NULL, ",", &saveptr2), j++) + { + if((sscanf(ptr3, "%2d:%2d-%2d:%2d", &allowed[0], &allowed[1], &allowed[2], &allowed[3]) == 4) && (day_idx < SIZE_SHORTDAY)) + { + startt = allowed[0] * 60 + allowed[1]; + endt = allowed[2] * 60 + allowed[3]; + + if(startt == endt) { endt++; } // end time cannot be the same as the star time + if((startt < 0) || (startt > 1439)) { startt = 0; } // could not start later than 23H59, avoid overflow + if((endt < 0) || (endt > 1440)) { endt = 1440; } // could not be higher than 24H00, avoid overflow + + account->allowedtimeframe_set = 1; + + if(startt > endt) + { + for(t = startt; t < 1440; t++) + { + tempo = (1 << (t % 30)); + account->allowedtimeframe[day_idx][t / 60][(t / 30) % 2] = account->allowedtimeframe[day_idx][t / 60][(t / 30) % 2] | tempo; + } + startt = 0; + } + + for(t = startt; t < endt; t++) + { + tempo = (1 << (t % 30)); + account->allowedtimeframe[day_idx][t / 60][((t / 30) % 2)] = account->allowedtimeframe[day_idx][t / 60][(t / 30) % 2] | tempo; + } + } + else + { + fprintf(stderr, "WARNING: Value '%s' is not valid for allowedtimeframe (DAY@HH:MM-HH:MM)\n", value); + } + } + } + else // No day specified so whole week (ALL) + { + if(sscanf(ptr1, "%2d:%2d-%2d:%2d", &allowed[0], &allowed[1], &allowed[2], &allowed[3]) == 4) + { + startt = allowed[0] * 60 + allowed[1]; + endt = allowed[2] * 60 + allowed[3]; + + if(startt == endt) { endt++; } // end time cannot be the same as the star time + if((startt <0) || (startt > 1439)) { startt = 0; } // could not start later than 23H59, avoid overflow + if((endt <0) || (endt > 1440)) { endt = 1440; } // could not be higher than 24H00, avoid overflow + + account->allowedtimeframe_set = 1; + dest = strstr(weekdstr,"ALL"); + day_idx = (dest - weekdstr) / 3; + + if(startt > endt) + { + for(t = startt; t < 1440; t++) + { + tempo = (1 << (t % 30)); + account->allowedtimeframe[day_idx][t / 60][(t / 30) % 2] = account->allowedtimeframe[7][t / 60][(t / 30) % 2] | tempo; + } + startt=0; + } + + for(t = startt; t < endt; t++) + { + tempo = (1 << (t % 30)); + account->allowedtimeframe[day_idx][t / 60][(t / 30) % 2] = account->allowedtimeframe[7][t / 60][(t / 30) % 2] | tempo; + } + } + else + { + fprintf(stderr, "WARNING: Value '%s' is not valid for allowedtimeframe (hh:mm-hh:mm)\n", value); + } + + } + } + return; + } + if(account->allowedtimeframe_set) + { + value = mk_t_allowedtimeframe(account); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } + else if(cfg.http_full_cfg) + { + fprintf_conf(f, token, "%s\n", ""); + } +} + +static void account_tuntab_fn(const char *token, char *value, void *setting, FILE *f) +{ + TUNTAB *ttab = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + tuntab_clear(ttab); + } + else + { + chk_tuntab(value, ttab); + } + return; + } + if((ttab->ttdata && ttab->ttdata[0].bt_caidfrom) || cfg.http_full_cfg) + { + value = mk_t_tuntab(ttab); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +void group_fn(const char *token, char *value, void *setting, FILE *f) +{ + uint64_t *grp = setting; + if(value) + { + char *ptr1, *saveptr1 = NULL; + *grp = 0; + for(ptr1 = strtok_r(value, ",", &saveptr1); ptr1; ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + int32_t g; + g = atoi(ptr1); + if(g > 0 && g < 65) + { *grp |= (((uint64_t)1) << (g - 1)); } + } + return; + } + if(*grp || cfg.http_full_cfg) + { + value = mk_t_group(*grp); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +void services_fn(const char *token, char *value, void *setting, FILE *f) +{ + SIDTABS *sidtabs = setting; + if(value) + { + strtolower(value); + chk_services(value, sidtabs); + return; + } + value = mk_t_service(sidtabs); + if(cs_strlen(value) > 0 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%s\n", value); } + free_mk_t(value); +} + +void class_fn(const char *token, char *value, void *setting, FILE *f) +{ + CLASSTAB *cltab = setting; + if(value) + { + strtolower(value); + chk_cltab(value, cltab); + return; + } + value = mk_t_cltab(cltab); + if(cs_strlen(value) > 0 || cfg.http_full_cfg) + { + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +#if defined(CS_ANTICASC) || defined(CS_CACHEEX_AIO) +static void account_fixups_fn(void *var) +{ + struct s_auth *account = var; +#endif +#if defined(CS_ANTICASC) + if(account->ac_users < -1) { account->ac_users = DEFAULT_AC_USERS; } + if(account->ac_penalty < -1) { account->ac_penalty = DEFAULT_AC_PENALTY; } + if(account->acosc_max_ecms_per_minute < -1) { account->acosc_max_ecms_per_minute = -1; } + if(account->acosc_max_active_sids < -1) { account->acosc_max_active_sids = -1; } + if(account->acosc_zap_limit < -1) { account->acosc_zap_limit = -1; } + if(account->acosc_penalty < -1) { account->acosc_penalty = -1; } + if(account->acosc_penalty_duration < -1) { account->acosc_penalty_duration = -1; } + if(account->acosc_delay < -1) { account->acosc_delay = -1; } + if((account->acosc_penalty == 4) || ((cfg.acosc_penalty == 4) && (account->acosc_penalty == -1))) + { + account->acosc_max_active_sids = -1; // use global value + account->acosc_zap_limit = -1; // use global value + account->acosc_penalty_duration = -1; // use global value + + if((account->acosc_max_ecms_per_minute != -1) && (account->acosc_max_ecms_per_minute != 0)) + { + if(account->acosc_max_ecms_per_minute < 6) { account->acosc_max_ecms_per_minute = 6; } + if(account->acosc_max_ecms_per_minute > 20) { account->acosc_max_ecms_per_minute = 20; } + account->acosc_penalty_duration = (60 / account->acosc_max_ecms_per_minute); + } + } +#endif +#if defined(CS_CACHEEX_AIO) + // lgo-ctab -> lgo-ftab port + caidtab2ftab_add(&account->cacheex.localgenerated_only_in_caidtab, &account->cacheex.lg_only_in_tab); + caidtab_clear(&account->cacheex.localgenerated_only_in_caidtab); + caidtab2ftab_add(&account->cacheex.localgenerated_only_caidtab, &account->cacheex.lg_only_tab); + caidtab_clear(&account->cacheex.localgenerated_only_caidtab); +#endif +#if defined(CS_ANTICASC) || defined(CS_CACHEEX_AIO) +} +#endif + +#define OFS(X) offsetof(struct s_auth, X) +#define SIZEOF(X) sizeof(((struct s_auth *)0)->X) + +static const struct config_list account_opts[] = +{ +#if defined(CS_ANTICASC) || defined(CS_CACHEEX_AIO) + DEF_OPT_FIXUP_FUNC(account_fixups_fn), +#endif + DEF_OPT_INT8("disabled" , OFS(disabled), 0), + DEF_OPT_SSTR("user" , OFS(usr), "", SIZEOF(usr)), + DEF_OPT_STR("pwd" , OFS(pwd), NULL), +#ifdef WEBIF + DEF_OPT_STR("description" , OFS(description), NULL), +#endif + DEF_OPT_STR("hostname" , OFS(dyndns), NULL), + DEF_OPT_FUNC("caid" , OFS(ctab), check_caidtab_fn), + DEF_OPT_INT8("uniq" , OFS(uniq), 0), + DEF_OPT_UINT8("sleepsend" , OFS(c35_sleepsend), 0), + DEF_OPT_INT32("failban" , OFS(failban), 0), + DEF_OPT_INT8("monlevel" , OFS(monlvl), 0), + DEF_OPT_FUNC("sleep" , OFS(tosleep), account_tosleep_fn), + DEF_OPT_FUNC("suppresscmd08" , OFS(c35_suppresscmd08), account_c35_suppresscmd08_fn), + DEF_OPT_INT32("umaxidle" , OFS(umaxidle), -1), + DEF_OPT_FUNC("keepalive" , OFS(ncd_keepalive), account_ncd_keepalive_fn), + DEF_OPT_FUNC("au" , 0, account_au_fn), + DEF_OPT_UINT8("emmreassembly" , OFS(emm_reassembly), 2), + DEF_OPT_FUNC("expdate" , 0, account_expdate_fn), + DEF_OPT_FUNC("allowedprotocols" , 0, account_allowedprotocols_fn), + DEF_OPT_FUNC("allowedtimeframe" , 0, account_allowedtimeframe_fn), + DEF_OPT_FUNC("betatunnel" , OFS(ttab), account_tuntab_fn), + DEF_OPT_FUNC("group" , OFS(grp), group_fn), + DEF_OPT_FUNC("services" , OFS(sidtabs), services_fn), + DEF_OPT_INT8("preferlocalcards" , OFS(preferlocalcards), -1), + DEF_OPT_FUNC_X("ident" , OFS(ftab), ftab_fn, FTAB_ACCOUNT | FTAB_PROVID), + DEF_OPT_FUNC_X("chid" , OFS(fchid), ftab_fn, FTAB_ACCOUNT | FTAB_CHID), + DEF_OPT_FUNC("class" , OFS(cltab), class_fn), + DEF_OPT_UINT32("max_connections" , OFS(max_connections), 1), +#ifdef CS_CACHEEX + DEF_OPT_INT8("cacheex" , OFS(cacheex.mode), 0), + DEF_OPT_INT8("cacheex_maxhop" , OFS(cacheex.maxhop), 0), +#ifdef CS_CACHEEX_AIO + DEF_OPT_INT8("cacheex_maxhop_lg" , OFS(cacheex.maxhop_lg), 0), +#endif + DEF_OPT_FUNC("cacheex_ecm_filter" , OFS(cacheex.filter_caidtab), cacheex_hitvaluetab_fn), + DEF_OPT_UINT8("cacheex_drop_csp" , OFS(cacheex.drop_csp), 0), + DEF_OPT_UINT8("cacheex_allow_request" , OFS(cacheex.allow_request), 0), + DEF_OPT_UINT8("no_wait_time" , OFS(no_wait_time), 0), + DEF_OPT_UINT8("cacheex_allow_filter" , OFS(cacheex.allow_filter), 1), +#ifdef CS_CACHEEX_AIO + DEF_OPT_UINT8("cacheex_allow_maxhop" , OFS(cacheex.allow_maxhop), 0), +#endif + DEF_OPT_UINT8("cacheex_block_fakecws" , OFS(cacheex.block_fakecws), 0), + DEF_OPT_UINT8("disablecrccacheex" , OFS(disablecrccacheex), 0), + DEF_OPT_FUNC_X("disablecrccacheex_only_for", OFS(disablecrccacheex_only_for), ftab_fn, FTAB_ACCOUNT | FTAB_IGNCRCCEX4USERONLYFOR), +#ifdef CS_CACHEEX_AIO + DEF_OPT_UINT8("cacheex_cw_check_for_push" , OFS(cacheex.cw_check_for_push), 0), + DEF_OPT_UINT8("cacheex_lg_only_remote_settings", OFS(cacheex.lg_only_remote_settings), 1), + DEF_OPT_UINT8("cacheex_localgenerated_only", OFS(cacheex.localgenerated_only), 0), + DEF_OPT_FUNC("cacheex_localgenerated_only_caid", OFS(cacheex.localgenerated_only_caidtab), check_caidtab_fn), + DEF_OPT_FUNC_X("cacheex_lg_only_tab" , OFS(cacheex.lg_only_tab), ftab_fn, 0), + DEF_OPT_UINT8("cacheex_lg_only_in_aio_only", OFS(cacheex.lg_only_in_aio_only), 0), + DEF_OPT_UINT8("cacheex_localgenerated_only_in", OFS(cacheex.localgenerated_only_in), 0), + DEF_OPT_FUNC("cacheex_localgenerated_only_in_caid", OFS(cacheex.localgenerated_only_in_caidtab), check_caidtab_fn), + DEF_OPT_FUNC_X("cacheex_lg_only_in_tab" , OFS(cacheex.lg_only_in_tab), ftab_fn, 0), + DEF_OPT_FUNC("cacheex_nopushafter" , OFS(cacheex.cacheex_nopushafter_tab), caidvaluetab_fn), +#endif +#endif +#ifdef MODULE_CCCAM + DEF_OPT_INT32("cccmaxhops" , OFS(cccmaxhops), DEFAULT_CC_MAXHOPS), + DEF_OPT_INT8("cccreshare" , OFS(cccreshare), DEFAULT_CC_RESHARE), + DEF_OPT_INT8("cccignorereshare" , OFS(cccignorereshare), DEFAULT_CC_IGNRSHR), + DEF_OPT_INT8("cccstealth" , OFS(cccstealth), DEFAULT_CC_STEALTH), +#endif +#ifdef CS_ANTICASC + DEF_OPT_INT32("fakedelay" , OFS(ac_fakedelay), -1), + DEF_OPT_INT32("numusers" , OFS(ac_users), DEFAULT_AC_USERS), + DEF_OPT_INT8("penalty" , OFS(ac_penalty), DEFAULT_AC_PENALTY), + DEF_OPT_INT8("acosc_max_ecms_per_minute" , OFS(acosc_max_ecms_per_minute), -1 ), + DEF_OPT_INT8("acosc_max_active_sids" , OFS(acosc_max_active_sids), -1 ), + DEF_OPT_INT8("acosc_zap_limit" , OFS(acosc_zap_limit), -1 ), + DEF_OPT_INT8("acosc_penalty" , OFS(acosc_penalty), -1 ), + DEF_OPT_INT32("acosc_penalty_duration" , OFS(acosc_penalty_duration), -1 ), + DEF_OPT_INT32("acosc_delay" , OFS(acosc_delay), -1 ), +#endif +#ifdef WITH_LB + DEF_OPT_INT32("lb_nbest_readers" , OFS(lb_nbest_readers), -1), + DEF_OPT_INT32("lb_nfb_readers" , OFS(lb_nfb_readers), -1), + DEF_OPT_FUNC("lb_nbest_percaid" , OFS(lb_nbest_readers_tab), caidvaluetab_fn), +#endif +#ifdef CW_CYCLE_CHECK + DEF_OPT_INT8("cwc_disable" , OFS(cwc_disable), 0), +#endif + DEF_LAST_OPT +}; + +void chk_account(const char *token, char *value, struct s_auth *account) +{ + if(config_list_parse(account_opts, token, value, account)) + { return; } + else if(token[0] != '#') + { fprintf(stderr, "Warning: keyword '%s' in account section not recognized\n", token); } +} + +void account_set_defaults(struct s_auth *account) +{ + config_list_set_defaults(account_opts, account); +} + +struct s_auth *init_userdb(void) +{ + FILE *fp = open_config_file(cs_user); + if(!fp) + { return NULL; } + + struct s_auth *authptr = NULL; + int32_t tag = 0, nr = 0, expired = 0, disabled = 0; + char *token; + struct s_auth *account = NULL; + struct s_auth *probe = NULL; + if(!cs_malloc(&token, MAXLINESIZE)) + { return NULL; } + + while(fgets(token, MAXLINESIZE, fp)) + { + int32_t l; + void *ptr; + + if((l = cs_strlen(trim(token))) < 3) + { continue; } + if(token[0] == '[' && token[l - 1] == ']') + { + token[l - 1] = 0; + tag = streq("account", strtolower(token + 1)); + if(!cs_malloc(&ptr, sizeof(struct s_auth))) + { break; } + if(account) + { account->next = ptr; } + else + { authptr = ptr; } + + account = ptr; + account_set_defaults(account); + nr++; + + continue; + } + + if(!tag) + { continue; } + char *value = strchr(token, '='); + if(!value) + { continue; } + + *value++ = '\0'; + + // check for duplicate useraccounts and make the name unique + if(streq(trim(strtolower(token)), "user")) + { + for(probe = authptr; probe; probe = probe->next) + { + if(!strcmp(probe->usr, trim(value))) + { + fprintf(stderr, "Warning: duplicate account '%s'\n", value); + if (!cs_strncat(value, "_x", sizeof(probe->usr))) { + cs_log("WARNING, bug here!"); + } + } + } + } + chk_account(trim(strtolower(token)), trim(value), account); + } + NULLFREE(token); + fclose(fp); + + for(account = authptr; account; account = account->next) + { + if(account->expirationdate && account->expirationdate < time(NULL)) + { ++expired; } + if(account->disabled) + { ++disabled; } + } + cs_log("userdb reloaded: %d accounts loaded, %d expired, %d disabled", nr, expired, disabled); + return authptr; +} + +int32_t init_free_userdb(struct s_auth *ptr) +{ + int32_t nro; + for(nro = 0; ptr; nro++) + { + struct s_auth *ptr_next; + ptr_next = ptr->next; + ll_destroy(&ptr->aureader_list); + ptr->next = NULL; + config_list_gc_values(account_opts, ptr); + ftab_clear(&ptr->ftab); + ftab_clear(&ptr->fchid); + tuntab_clear(&ptr->ttab); + caidtab_clear(&ptr->ctab); + NULLFREE(ptr->cltab.aclass); + NULLFREE(ptr->cltab.bclass); +#ifdef CS_CACHEEX + cecspvaluetab_clear(&ptr->cacheex.filter_caidtab); + ftab_clear(&ptr->disablecrccacheex_only_for); +#ifdef CS_CACHEEX_AIO + caidtab_clear(&ptr->cacheex.localgenerated_only_caidtab); + caidtab_clear(&ptr->cacheex.localgenerated_only_in_caidtab); + ftab_clear(&ptr->cacheex.lg_only_tab); + ftab_clear(&ptr->cacheex.lg_only_in_tab); + caidvaluetab_clear(&ptr->cacheex.cacheex_nopushafter_tab); +#endif +#endif +#ifdef WITH_LB + caidvaluetab_clear(&ptr->lb_nbest_readers_tab); +#endif + add_garbage(ptr); + ptr = ptr_next; + } + cs_log("userdb %d accounts freed", nro); + return nro; +} + +int32_t write_userdb(void) +{ + struct s_auth *account; + FILE *f = create_config_file(cs_user); + if(!f) + { return 1; } + for(account = cfg.account; account; account = account->next) + { + fprintf(f, "[account]\n"); + config_list_apply_fixups(account_opts, account); + config_list_save(f, account_opts, account, cfg.http_full_cfg); + fprintf(f, "\n"); + } + return flush_config_file(f, cs_user); +} + +void cs_accounts_chk(void) +{ + struct s_auth *account1, *account2; + struct s_auth *new_accounts = init_userdb(); + cs_writelock(__func__, &config_lock); + struct s_auth *old_accounts = cfg.account; + for(account1 = cfg.account; account1; account1 = account1->next) + { + for(account2 = new_accounts; account2; account2 = account2->next) + { + if(!strcmp(account1->usr, account2->usr)) + { + account2->cwfound = account1->cwfound; + account2->cwcache = account1->cwcache; + account2->cwnot = account1->cwnot; + account2->cwtun = account1->cwtun; + account2->cwignored = account1->cwignored; + account2->cwtout = account1->cwtout; + account2->emmok = account1->emmok; + account2->emmnok = account1->emmnok; + account2->firstlogin = account1->firstlogin; + ac_copy_vars(account1, account2); + } + } + } + cs_reinit_clients(new_accounts); + cfg.account = new_accounts; + init_free_userdb(old_accounts); + ac_clear(); + cs_writeunlock(__func__, &config_lock); +} diff --git a/oscam-config-global.c b/oscam-config-global.c new file mode 100644 index 0000000..692e235 --- /dev/null +++ b/oscam-config-global.c @@ -0,0 +1,1628 @@ +#define MODULE_LOG_PREFIX "config" + +#include "globals.h" +#include "module-dvbapi.h" +#include "module-gbox.h" +#include "oscam-array.h" +#include "oscam-conf.h" +#include "oscam-conf-chk.h" +#include "oscam-conf-mk.h" +#include "oscam-config.h" +#include "oscam-net.h" +#include "oscam-string.h" +#ifdef CS_CACHEEX_AIO +#include "module-cacheex.h" +#endif +#define cs_conf "oscam.conf" + +#define DEFAULT_HTTP_PORT 8888 +#define DEFAULT_HTTP_ALLOW "127.0.0.1,192.168.0.0-192.168.255.255,10.0.0.0-10.255.255.255,172.16.0.0-172.31.255.255,::1" + +static void disablelog_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + cs_disable_log(strToIntVal(value, 0)); + return; + } + if(cfg.disablelog || cfg.http_full_cfg) + { fprintf_conf(f, token, "%d\n", cfg.disablelog); } +} + +#if defined(WEBIF) || defined(MODULE_MONITOR) +static void loghistorylines_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + uint32_t newsize = strToUIntVal(value, 256); + if(newsize < 64 && newsize != 0) + { + fprintf(stderr, "WARNING: loghistorylines is too small, adjusted to 64\n"); + newsize = 64; + } + cs_reinit_loghist(newsize); + return; + } + if(cfg.loghistorylines != 256 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%u\n", cfg.loghistorylines); } +} +#endif + +static void serverip_fn(const char *token, char *value, void *setting, FILE *f) +{ + IN_ADDR_T srvip = *(IN_ADDR_T *)setting; + if(value) + { + if(cs_strlen(value) == 0) + { + set_null_ip((IN_ADDR_T *)setting); + } + else + { + cs_inet_addr(value, (IN_ADDR_T *)setting); + } + return; + } + if(IP_ISSET(srvip) || cfg.http_full_cfg) + { fprintf_conf(f, token, "%s\n", cs_inet_ntoa(srvip)); } +} + +void iprange_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_ip **ip = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + clear_sip(ip); + } + else + { + chk_iprange(value, ip); + } + return; + } + value = mk_t_iprange(*ip); + if(cs_strlen(value) > 0 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%s\n", value); } + free_mk_t(value); +} + +void iprange_free_fn(void *setting) +{ + clear_sip(setting); +} + +static void logfile_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + char *saveptr1 = NULL; + cfg.logtostdout = 0; + cfg.logtosyslog = 0; + NULLFREE(cfg.logfile); + if(cs_strlen(value) > 0) + { + char *pch; + for(pch = strtok_r(value, ";", &saveptr1); pch != NULL; pch = strtok_r(NULL, ";", &saveptr1)) + { + pch = trim(pch); + if(!strcmp(pch, "stdout")) { cfg.logtostdout = 1; } + else if(!strcmp(pch, "syslog")) { cfg.logtosyslog = 1; } + else + { + NULLFREE(cfg.logfile); + if(!(cfg.logfile = cs_strdup(pch))) + { continue; } + } + } + } + else + { + if(!(cfg.logfile = cs_strdup(CS_LOGFILE))) + { cfg.logtostdout = 1; } + } + return; + } + if(cfg.logfile || cfg.logtostdout == 1 || cfg.logtosyslog == 1 || cfg.http_full_cfg) + { + value = mk_t_logfile(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +void check_caidtab_fn(const char *token, char *value, void *setting, FILE *f) +{ + CAIDTAB *caid_table = setting; + if(value) + { + if(cs_strlen(value)) { + chk_caidtab(value, caid_table); + } else { + caidtab_clear(caid_table); + } + return; + } + if(caid_table->ctnum || cfg.http_full_cfg) + { + value = mk_t_caidtab(caid_table); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +void chk_ftab_fn(const char *token, char *value, void *setting, FILE *f) +{ + FTAB *ftab = setting; + if(value) + { + if(cs_strlen(value)) + chk_ftab(value, ftab); + else + ftab_clear(ftab); + return; + } + value = mk_t_ftab(ftab); + if(cs_strlen(value) > 0 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%s\n", value); } + free_mk_t(value); +} + + +void caidvaluetab_fn(const char *token, char *value, void *setting, FILE *f) +{ + CAIDVALUETAB *caid_value_table = setting; + if(value) + { + if (cs_strlen(value)) { + chk_caidvaluetab(value, caid_value_table); + if (streq(token, "lb_retrylimits")) + { + int32_t i; + for (i = 0; i < caid_value_table->cvnum; i++) + { + if (caid_value_table->cvdata[i].value < 50) + caid_value_table->cvdata[i].value = 50; + } + } + } else { + caidvaluetab_clear(caid_value_table); + } + return; + } + if(caid_value_table->cvnum || cfg.http_full_cfg) + { + value = mk_t_caidvaluetab(caid_value_table); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +#ifdef __CYGWIN__ +#include +#else +#include // for setpriority +#endif + +void cwvote_caidtab_fn(const char *token, char *value, void *setting, FILE *f) +{ + CW_VOTE_CAID_TAB *cwvote_caid_table = setting; + if(value) + { + if(cs_strlen(value)) { + chk_cwvote_caidtab(value, cwvote_caid_table); + } else { + cwvote_caidtab_clear(cwvote_caid_table); + } + return; + } + if(cwvote_caid_table->cvcnum || cfg.http_full_cfg) + { + value = mk_t_cwvote_caidtab(cwvote_caid_table); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +#ifdef CS_CACHEEX +void cacheex_valuetab_fn(const char *token, char *value, void *setting, FILE *f) +{ + CECSPVALUETAB *cacheex_value_table = setting; + if(value) + { + if(cs_strlen(value) == 0) + { clear_cacheextab(cacheex_value_table); } + else + { chk_cacheex_valuetab(value, cacheex_value_table); } + return; + } + if(cacheex_value_table->cevnum || cfg.http_full_cfg) + { + value = mk_t_cacheex_valuetab(cacheex_value_table); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +void cacheex_cwcheck_tab_fn(const char *token, char *value, void *setting, FILE *f) +{ + CWCHECKTAB *cacheex_value_table = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + cacheex_value_table->cwchecknum = 0; + NULLFREE(cacheex_value_table->cwcheckdata); + } + else + { + chk_cacheex_cwcheck_valuetab(value, cacheex_value_table); + } + return; + } + + if(cacheex_value_table->cwchecknum || cfg.http_full_cfg) + { + value = mk_t_cacheex_cwcheck_valuetab(cacheex_value_table); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +void cacheex_hitvaluetab_fn(const char *token, char *value, void *setting, FILE *f) +{ + CECSPVALUETAB *cacheex_value_table = setting; + if(value) + { + if(cs_strlen(value) == 0) + { clear_cacheextab(cacheex_value_table); } + else + { chk_cacheex_hitvaluetab(value, cacheex_value_table); } + return; + } + if(cacheex_value_table->cevnum || cfg.http_full_cfg) + { + value = mk_t_cacheex_hitvaluetab(cacheex_value_table); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} +#endif + +void global_fixups_fn(void *UNUSED(var)) +{ + if(!cfg.usrfile) { cfg.disableuserfile = 1; } + if(!cfg.mailfile) { cfg.disablemail = 1; } + if(cfg.ctimeout < 10) { cfg.ctimeout = cfg.ctimeout * 1000; } // save always in ms + + if(cfg.nice < -20 || cfg.nice > 20) { cfg.nice = 99; } + if(cfg.nice != 99) + { +#ifndef __CYGWIN__ + setpriority(PRIO_PROCESS, 0, cfg.nice); +#else + HANDLE WinId; + uint32_t wprio; + switch((cfg.nice + 20) / 10) + { + case 0: + wprio = REALTIME_PRIORITY_CLASS; + break; + case 1: + wprio = HIGH_PRIORITY_CLASS; + break; + case 2: + wprio = NORMAL_PRIORITY_CLASS; + break; + default: + wprio = IDLE_PRIORITY_CLASS; + break; + } + WinId = GetCurrentProcess(); + SetPriorityClass(WinId, wprio); +#endif + } + if(cfg.netprio <= 0 || cfg.netprio > 20) { cfg.netprio = 0; } + if(cfg.max_log_size != 0 && cfg.max_log_size <= 10) { cfg.max_log_size = 10; } +#ifdef WITH_LB + if(cfg.lb_save > 0 && cfg.lb_save < 100) { cfg.lb_save = 100; } + if(cfg.lb_nbest_readers < 2) { cfg.lb_nbest_readers = DEFAULT_NBEST; } +#endif +} + +#define OFS(X) offsetof(struct s_config, X) +#define SIZEOF(X) sizeof(((struct s_config *)0)->X) + +static const struct config_list global_opts[] = +{ + DEF_OPT_FIXUP_FUNC(global_fixups_fn), +#ifdef LEDSUPPORT + DEF_OPT_INT8("enableled" , OFS(enableled) , 0), +#endif + DEF_OPT_FUNC("disablelog" , OFS(disablelog) , disablelog_fn), +#if defined(WEBIF) || defined(MODULE_MONITOR) + DEF_OPT_FUNC("loghistorylines" , OFS(loghistorylines) , loghistorylines_fn), +#endif + DEF_OPT_FUNC("serverip" , OFS(srvip) , serverip_fn), + DEF_OPT_FUNC("logfile" , OFS(logfile) , logfile_fn), + DEF_OPT_INT32("initial_debuglevel" , OFS(initial_debuglevel) , 0), + DEF_OPT_STR("sysloghost" , OFS(sysloghost) , NULL), + DEF_OPT_INT32("syslogport" , OFS(syslogport) , 514), + DEF_OPT_INT8("logduplicatelines" , OFS(logduplicatelines) , 0), + DEF_OPT_STR("pidfile" , OFS(pidfile) , NULL), + DEF_OPT_INT8("disableuserfile" , OFS(disableuserfile) , 1), + DEF_OPT_INT8("disablemail" , OFS(disablemail) , 1), + DEF_OPT_INT8("usrfileflag" , OFS(usrfileflag) , 0), + DEF_OPT_UINT32("clienttimeout" , OFS(ctimeout) , CS_CLIENT_TIMEOUT), + DEF_OPT_FUNC("clienttimeout_percaid" , OFS(ctimeouttab) , caidvaluetab_fn), + DEF_OPT_UINT32("fallbacktimeout" , OFS(ftimeout) , CS_CLIENT_TIMEOUT / 2), + DEF_OPT_FUNC("fallbacktimeout_percaid" , OFS(ftimeouttab) , caidvaluetab_fn), + DEF_OPT_UINT32("clientmaxidle" , OFS(cmaxidle) , CS_CLIENT_MAXIDLE), + DEF_OPT_INT32("bindwait" , OFS(bindwait) , CS_BIND_TIMEOUT), + DEF_OPT_UINT32("netprio" , OFS(netprio) , 0), + DEF_OPT_INT32("sleep" , OFS(tosleep) , 0), + DEF_OPT_INT32("unlockparental" , OFS(ulparent) , 0), + DEF_OPT_INT32("nice" , OFS(nice) , 99), + DEF_OPT_INT32("maxlogsize" , OFS(max_log_size) , 10), + DEF_OPT_INT8("waitforcards" , OFS(waitforcards) , 1), + DEF_OPT_INT32("waitforcards_extra_delay" , OFS(waitforcards_extra_delay) , 500), + DEF_OPT_INT8("preferlocalcards" , OFS(preferlocalcards) , 0), + DEF_OPT_INT32("readerrestartseconds" , OFS(reader_restart_seconds) , 5), + DEF_OPT_INT8("dropdups" , OFS(dropdups) , 0), + DEF_OPT_INT8("reload_useraccounts" , OFS(reload_useraccounts) , 0), + DEF_OPT_INT8("reload_readers" , OFS(reload_readers) , 0), + DEF_OPT_INT8("reload_provid" , OFS(reload_provid) , 0), + DEF_OPT_INT8("reload_services_ids" , OFS(reload_services_ids) , 0), + DEF_OPT_INT8("reload_tier_ids" , OFS(reload_tier_ids) , 0), + DEF_OPT_INT8("reload_fakecws" , OFS(reload_fakecws) , 0), + DEF_OPT_INT8("reload_ac_stat" , OFS(reload_ac_stat) , 0), + DEF_OPT_INT8("reload_log" , OFS(reload_log) , 0), + DEF_OPT_INT8("block_same_ip" , OFS(block_same_ip) , 1), + DEF_OPT_INT8("block_same_name" , OFS(block_same_name) , 1), + DEF_OPT_STR("usrfile" , OFS(usrfile) , NULL), + DEF_OPT_STR("mailfile" , OFS(mailfile) , NULL), + DEF_OPT_STR("cwlogdir" , OFS(cwlogdir) , NULL), + DEF_OPT_STR("emmlogdir" , OFS(emmlogdir) , NULL), +#ifdef WITH_LB + DEF_OPT_INT32("lb_mode" , OFS(lb_mode) , DEFAULT_LB_MODE), + DEF_OPT_INT32("lb_save" , OFS(lb_save) , 0), + DEF_OPT_INT32("lb_nbest_readers" , OFS(lb_nbest_readers) , DEFAULT_NBEST), + DEF_OPT_INT32("lb_nfb_readers" , OFS(lb_nfb_readers) , DEFAULT_NFB), + DEF_OPT_INT32("lb_min_ecmcount" , OFS(lb_min_ecmcount) , DEFAULT_MIN_ECM_COUNT), + DEF_OPT_INT32("lb_max_ecmcount" , OFS(lb_max_ecmcount) , DEFAULT_MAX_ECM_COUNT), + DEF_OPT_INT32("lb_reopen_seconds" , OFS(lb_reopen_seconds) , DEFAULT_REOPEN_SECONDS), + DEF_OPT_INT8("lb_reopen_invalid" , OFS(lb_reopen_invalid) , 1), + DEF_OPT_INT8("lb_force_reopen_always" , OFS(lb_force_reopen_always) , 0), + DEF_OPT_INT32("lb_retrylimit" , OFS(lb_retrylimit) , DEFAULT_RETRYLIMIT), + DEF_OPT_INT32("lb_stat_cleanup" , OFS(lb_stat_cleanup) , DEFAULT_LB_STAT_CLEANUP), + DEF_OPT_INT32("lb_max_readers" , OFS(lb_max_readers) , 0), + DEF_OPT_INT32("lb_auto_betatunnel" , OFS(lb_auto_betatunnel) , DEFAULT_LB_AUTO_BETATUNNEL), + DEF_OPT_INT32("lb_auto_betatunnel_mode" , OFS(lb_auto_betatunnel_mode) , DEFAULT_LB_AUTO_BETATUNNEL_MODE), + DEF_OPT_INT32("lb_auto_betatunnel_prefer_beta" , OFS(lb_auto_betatunnel_prefer_beta), DEFAULT_LB_AUTO_BETATUNNEL_PREFER_BETA), + DEF_OPT_STR("lb_savepath" , OFS(lb_savepath) , NULL), + DEF_OPT_FUNC("lb_retrylimits" , OFS(lb_retrylimittab) , caidvaluetab_fn), + DEF_OPT_FUNC("lb_nbest_percaid" , OFS(lb_nbest_readers_tab) , caidvaluetab_fn), + DEF_OPT_FUNC("lb_noproviderforcaid" , OFS(lb_noproviderforcaid) , check_caidtab_fn), + DEF_OPT_INT32("lb_auto_timeout" , OFS(lb_auto_timeout) , DEFAULT_LB_AUTO_TIMEOUT), + DEF_OPT_INT32("lb_auto_timeout_p" , OFS(lb_auto_timeout_p) , DEFAULT_LB_AUTO_TIMEOUT_P), + DEF_OPT_INT32("lb_auto_timeout_t" , OFS(lb_auto_timeout_t) , DEFAULT_LB_AUTO_TIMEOUT_T), +#endif + DEF_OPT_FUNC("double_check_caid" , OFS(double_check_caid) , chk_ftab_fn), + DEF_OPT_STR("ecmfmt" , OFS(ecmfmt) , NULL), + DEF_OPT_INT32("resolvegethostbyname" , OFS(resolve_gethostbyname) , 0), + DEF_OPT_INT32("failbantime" , OFS(failbantime) , 0), + DEF_OPT_INT32("failbancount" , OFS(failbancount) , 0), + DEF_OPT_INT8("suppresscmd08" , OFS(c35_suppresscmd08) , 0), + DEF_OPT_INT8("getblockemmauprovid" , OFS(getblockemmauprovid) , 0), + DEF_OPT_INT8("double_check" , OFS(double_check) , 0), + DEF_OPT_INT8("disablecrccws" , OFS(disablecrccws) , 0), + DEF_OPT_FUNC("disablecrccws_only_for" , OFS(disablecrccws_only_for) , chk_ftab_fn), + DEF_OPT_INT8("cwvote_enabled" , OFS(cwvote_enabled) , 0), + DEF_OPT_INT8("cwvote_log_enabled" , OFS(cwvote_log_enabled) , 0), + DEF_OPT_INT32("cwvote_timeout" , OFS(cwvote_timeout) , 0), + DEF_OPT_INT32("cwvote_min_votes" , OFS(cwvote_min_votes) , 2), + DEF_OPT_FLOAT("cwvote_local_weight" , OFS(cwvote_local_weight) , 2.0f), + DEF_OPT_INT32("cwvote_max_candidates" , OFS(cwvote_max_candidates) , 8), + DEF_OPT_INT32("cwvote_compare_len" , OFS(cwvote_compare_len) , 8), + DEF_OPT_INT32("cwvote_fallback" , OFS(cwvote_fallback) , 0), // 0 = czekaj, 1 = weź najlepszy, 2 = weź pierwszy + DEF_OPT_FUNC("cwvote_caids" , OFS(cwvote_caids) , cwvote_caidtab_fn), +#ifdef CS_CACHEEX_AIO + DEF_OPT_INT8("cacheex_srcname_webif" , OFS(cacheex_srcname_webif) , 0), +#endif + DEF_LAST_OPT +}; + +#ifdef CS_ANTICASC +static void anticasc_fixups_fn(void *UNUSED(var)) +{ + if(cfg.ac_users < 0) { cfg.ac_users = 0; } + if(cfg.ac_stime < 0) { cfg.ac_stime = 2; } + if(cfg.ac_samples < 2 || cfg.ac_samples > 10) { cfg.ac_samples = 10; } + if(cfg.ac_penalty < 0 || cfg.ac_penalty > 3) { cfg.ac_penalty = 0; } + if(cfg.ac_fakedelay < 100 || cfg.ac_fakedelay > 3000) { cfg.ac_fakedelay = 1000; } + if(cfg.ac_denysamples < 2 || cfg.ac_denysamples > cfg.ac_samples - 1) { cfg.ac_denysamples = cfg.ac_samples - 1; } + if(cfg.ac_denysamples + 1 > cfg.ac_samples) { cfg.ac_denysamples = cfg.ac_samples - 1; } + if(cfg.acosc_max_ecms_per_minute < 0) { cfg.acosc_max_ecms_per_minute = 0; } + if(cfg.acosc_penalty == 4) + { + cfg.acosc_max_active_sids = 0; // set default + cfg.acosc_zap_limit = 0; // set default + //cfg.acosc_penalty_duration = 0; // set default + + if(cfg.acosc_max_ecms_per_minute != 0) + { + if(cfg.acosc_max_ecms_per_minute < 6) { cfg.acosc_max_ecms_per_minute = 6; } + if(cfg.acosc_max_ecms_per_minute > 20) { cfg.acosc_max_ecms_per_minute = 20; } + cfg.acosc_penalty_duration = (60 / cfg.acosc_max_ecms_per_minute); + } + } + if(cfg.acosc_max_active_sids < 0) { cfg.acosc_max_active_sids = 0; } + if(cfg.acosc_zap_limit < 0) { cfg.acosc_zap_limit = 0; } + if(cfg.acosc_penalty < 0 || cfg.acosc_penalty > 4) { cfg.acosc_penalty = 0; } + if(cfg.acosc_penalty_duration < 0) { cfg.acosc_penalty_duration = 0; } + if(cfg.acosc_delay < 0 || cfg.acosc_delay > 4000) { cfg.acosc_delay = 0; } +} + +static bool anticasc_should_save_fn(void *UNUSED(var)) +{ + return cfg.ac_enabled || cfg.acosc_enabled; +} + +static const struct config_list anticasc_opts[] = +{ + DEF_OPT_SAVE_FUNC(anticasc_should_save_fn), + DEF_OPT_FIXUP_FUNC(anticasc_fixups_fn), + DEF_OPT_INT8("enabled" , OFS(ac_enabled) , 0), + DEF_OPT_INT32("numusers" , OFS(ac_users) , 0), + DEF_OPT_INT32("sampletime" , OFS(ac_stime) , 2), + DEF_OPT_INT32("samples" , OFS(ac_samples) , 10), + DEF_OPT_INT8("penalty" , OFS(ac_penalty) , 0), + DEF_OPT_STR("aclogfile" , OFS(ac_logfile) , NULL), + DEF_OPT_INT32("fakedelay" , OFS(ac_fakedelay) , 3000), + DEF_OPT_INT32("denysamples" , OFS(ac_denysamples) , 8), + DEF_OPT_INT8("acosc_enabled" , OFS(acosc_enabled) , 0 ), + DEF_OPT_INT8("acosc_max_ecms_per_minute" , OFS(acosc_max_ecms_per_minute), 0 ), + DEF_OPT_INT8("acosc_max_active_sids" , OFS(acosc_max_active_sids) , 0 ), + DEF_OPT_INT8("acosc_zap_limit" , OFS(acosc_zap_limit) , 0 ), + DEF_OPT_INT8("acosc_penalty" , OFS(acosc_penalty) , 0 ), + DEF_OPT_INT32("acosc_penalty_duration" , OFS(acosc_penalty_duration), 0 ), + DEF_OPT_INT32("acosc_delay" , OFS(acosc_delay) , 0 ), + DEF_LAST_OPT +}; +#else +static const struct config_list anticasc_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_MONITOR +static bool monitor_should_save_fn(void *UNUSED(var)) +{ + return cfg.mon_port; +} + +static const struct config_list monitor_opts[] = +{ + DEF_OPT_SAVE_FUNC(monitor_should_save_fn), + DEF_OPT_INT32("port" , OFS(mon_port) , 0), + DEF_OPT_FUNC("serverip" , OFS(mon_srvip) , serverip_fn), + DEF_OPT_FUNC("nocrypt" , OFS(mon_allowed) , iprange_fn, .free_value = iprange_free_fn), + DEF_OPT_INT32("aulow" , OFS(aulow) , 30), + DEF_OPT_UINT8("monlevel" , OFS(mon_level) , 2), + DEF_OPT_INT32("hideclient_to" , OFS(hideclient_to), 25), + DEF_LAST_OPT +}; +#else +static const struct config_list monitor_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef WEBIF +static void http_port_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + cfg.http_port = 0; + if(value[0]) + { + if(value[0] == '+') + { + if(config_enabled(WITH_SSL)) + { + cfg.http_use_ssl = 1; + } + else + { + fprintf(stderr, "Warning: OSCam compiled without SSL support.\n"); + } + cfg.http_port = strtoul(value + 1, NULL, 10); + } + else + { + cfg.http_port = strtoul(value, NULL, 10); + } + } + return; + } + fprintf_conf(f, token, "%s%d\n", cfg.http_use_ssl ? "+" : "", cfg.http_port); +} + +static void http_dyndns_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + int i; + if(value) + { + char *ptr, *saveptr1 = NULL; + memset(cfg.http_dyndns, 0, sizeof(cfg.http_dyndns)); + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); (i < MAX_HTTP_DYNDNS) && (ptr); ptr = strtok_r(NULL, ",", &saveptr1), i++) + { + trim(ptr); + cs_strncpy((char *)cfg.http_dyndns[i], ptr, sizeof(cfg.http_dyndns[i])); + } + return; + } + if(cs_strlen((const char *)(cfg.http_dyndns[0])) > 0 || cfg.http_full_cfg) + { + fprintf_conf(f, token, "%s", ""); // it should not have \n at the end + for(i = 0; i < MAX_HTTP_DYNDNS; i++) + { + if(cfg.http_dyndns[i][0]) + { + fprintf(f, "%s%s", i > 0 ? "," : "", cfg.http_dyndns[i]); + } + } + fprintf(f, "\n"); + } +} + +static bool webif_should_save_fn(void *UNUSED(var)) +{ + return cfg.http_port; +} + +static const struct config_list webif_opts[] = +{ + DEF_OPT_SAVE_FUNC(webif_should_save_fn), + DEF_OPT_FUNC("httpport" , OFS(http_port) , http_port_fn), + DEF_OPT_FUNC("serverip" , OFS(http_srvip) , serverip_fn), + DEF_OPT_STR("httpuser" , OFS(http_user) , NULL), + DEF_OPT_STR("httppwd" , OFS(http_pwd) , NULL), + DEF_OPT_STR("httpcss" , OFS(http_css) , NULL), + DEF_OPT_STR("httpjscript" , OFS(http_jscript) , NULL), + DEF_OPT_STR("httpscript" , OFS(http_script) , NULL), + DEF_OPT_STR("httptpl" , OFS(http_tpl) , NULL), + DEF_OPT_STR("httppiconpath" , OFS(http_piconpath) , NULL), + DEF_OPT_STR("httphelplang" , OFS(http_help_lang) , "en"), + DEF_OPT_STR("httplocale" , OFS(http_locale) , NULL), + DEF_OPT_INT8("http_prepend_embedded_css", OFS(http_prepend_embedded_css), 0), + DEF_OPT_INT32("httprefresh" , OFS(http_refresh) , 0), + DEF_OPT_INT32("httppollrefresh" , OFS(poll_refresh) , 60), + DEF_OPT_INT8("httphideidleclients" , OFS(http_hide_idle_clients) , 1), + DEF_OPT_STR("httphidetype" , OFS(http_hide_type) , NULL), + DEF_OPT_INT8("httpshowpicons" , OFS(http_showpicons) , 0), + DEF_OPT_INT8("httppiconsize" , OFS(http_picon_size) , 0), + DEF_OPT_INT8("httpshowmeminfo" , OFS(http_showmeminfo) , 0), + DEF_OPT_INT8("httpshowuserinfo" , OFS(http_showuserinfo) , 0), + DEF_OPT_INT8("httpshowreaderinfo" , OFS(http_showreaderinfo) , 0), + DEF_OPT_INT8("httpshowcacheexinfo" , OFS(http_showcacheexinfo) , 0), + DEF_OPT_INT8("httpshowecminfo" , OFS(http_showecminfo) , 0), + DEF_OPT_INT8("httpshowloadinfo" , OFS(http_showloadinfo) , 0), + DEF_OPT_FUNC("httpallowed" , OFS(http_allowed) , iprange_fn, .free_value = iprange_free_fn), + DEF_OPT_INT8("httpreadonly" , OFS(http_readonly) , 0), + DEF_OPT_INT8("httpsavefullcfg" , OFS(http_full_cfg) , 0), + DEF_OPT_INT8("httpoverwritebakfile" , OFS(http_overwrite_bak_file) , 0), + DEF_OPT_STR("httpcert" , OFS(http_cert) , NULL), + DEF_OPT_INT8("https_force_secure_mode" , OFS(https_force_secure_mode) , 1), + DEF_OPT_INT8("https_auto_create_cert" , OFS(https_auto_create_cert) , 1), + DEF_OPT_FUNC("httpdyndns" , OFS(http_dyndns) , http_dyndns_fn), + DEF_OPT_INT32("aulow" , OFS(aulow) , 30), + DEF_OPT_INT32("hideclient_to" , OFS(hideclient_to) , 25), + DEF_OPT_STR("httposcamlabel" , OFS(http_oscam_label) , "OSCam"), + DEF_OPT_INT32("httpemmuclean" , OFS(http_emmu_clean) , 256), + DEF_OPT_INT32("httpemmsclean" , OFS(http_emms_clean) , -1), + DEF_OPT_INT32("httpemmgclean" , OFS(http_emmg_clean) , -1), +#ifdef WEBIF_LIVELOG + DEF_OPT_INT8("http_status_log" , OFS(http_status_log) , 0), +#else + DEF_OPT_INT8("http_status_log" , OFS(http_status_log) , 1), +#endif +#ifndef WEBIF_JQUERY + DEF_OPT_STR("http_extern_jquery" , OFS(http_extern_jquery) , "//code.jquery.com/jquery-3.7.1.min.js"), +#endif + DEF_LAST_OPT +}; +#else +static const struct config_list webif_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_CAMD33 +static bool camd33_should_save_fn(void *UNUSED(var)) +{ + return cfg.c33_port; +} + +static const struct config_list camd33_opts[] = +{ + DEF_OPT_SAVE_FUNC(camd33_should_save_fn), + DEF_OPT_INT32("port" , OFS(c33_port) , 0), + DEF_OPT_FUNC("serverip", OFS(c33_srvip) , serverip_fn), + DEF_OPT_FUNC("nocrypt" , OFS(c33_plain) , iprange_fn, .free_value = iprange_free_fn), + DEF_OPT_INT32("passive", OFS(c33_passive), 0), + DEF_OPT_HEX("key" , OFS(c33_key) , SIZEOF(c33_key)), + DEF_LAST_OPT +}; +#else +static const struct config_list camd33_opts[] = { DEF_LAST_OPT }; +#endif + + +void cache_fixups_fn(void *UNUSED(var)) +{ + if(cfg.max_cache_time < ((int32_t)(cfg.ctimeout + 500) / 1000 + 3)) { cfg.max_cache_time = ((cfg.ctimeout + 500) / 1000 + 3); } +#ifdef CW_CYCLE_CHECK + if(cfg.maxcyclelist > 4000) { cfg.maxcyclelist = 4000; } + if(cfg.keepcycletime > 240) { cfg.keepcycletime = 240; } + if(cfg.cwcycle_sensitive > 4) { cfg.cwcycle_sensitive = 4; } + if(cfg.cwcycle_sensitive == 1) { cfg.cwcycle_sensitive = 2; } +#endif +#ifdef CS_CACHEEX_AIO + // lgo-ctab -> lgo-ftab port + caidtab2ftab_add(&cfg.cacheex_localgenerated_only_in_caidtab, &cfg.cacheex_lg_only_in_tab); + caidtab_clear(&cfg.cacheex_localgenerated_only_in_caidtab); + caidtab2ftab_add(&cfg.cacheex_localgenerated_only_caidtab, &cfg.cacheex_lg_only_tab); + caidtab_clear(&cfg.cacheex_localgenerated_only_caidtab); +#endif +} + +static bool cache_should_save_fn(void *UNUSED(var)) +{ + return cfg.delay > CS_DELAY || cfg.max_cache_time != DEFAULT_MAX_CACHE_TIME +#ifdef CS_CACHEEX +#ifdef CS_CACHEEX_AIO + || cfg.cw_cache_size > 0 || cfg.cw_cache_memory > 0 || cfg.cw_cache_settings.cwchecknum || cfg.ecm_cache_size > 0 + || cfg.ecm_cache_memory > 0 || cfg.ecm_cache_droptime > 0 || cfg.cacheex_dropdiffs > 0 || cfg.cacheex_push_lg_groups + || cfg.cacheex_lg_only_remote_settings != 1 || cfg.cacheex_localgenerated_only > 0 || cfg.cacheex_localgenerated_only_caidtab.ctnum + || cfg.cacheex_lg_only_tab.nfilts || cfg.cacheex_lg_only_in_aio_only > 0 || cfg.cacheex_localgenerated_only_in > 0 + || cfg.cacheex_localgenerated_only_in_caidtab.ctnum || cfg.cacheex_lg_only_in_tab.nfilts || cfg.cacheex_filter_caidtab.cevnum + || cfg.cacheex_filter_caidtab_aio.cevnum || cfg.cacheex_nopushafter_tab.cvnum || cfg.waittime_block_start > 0 || cfg.waittime_block_time > 0 +#endif + || cfg.max_hitcache_time != DEFAULT_MAX_HITCACHE_TIME || cfg.cacheex_wait_timetab.cevnum || cfg.cacheex_mode1_delay_tab.cvnum + || cfg.cacheex_enable_stats > 0 || cfg.csp_port > 0 || IP_ISSET(cfg.csp_srvip) || cfg.csp.filter_caidtab.cevnum || cfg.csp.allow_request != 1 + || cfg.csp.allow_reforward > 0 || cfg.cacheex_cwcheck_tab.cwchecknum || cfg.wait_until_ctimeout > 0 || cfg.csp.block_fakecws > 0 +#endif +#ifdef CW_CYCLE_CHECK + || cfg.cwcycle_check_enable || cfg.cwcycle_check_caidtab.ctnum || cfg.maxcyclelist != 500 || cfg.keepcycletime != 15 || cfg.onbadcycle != 1 + || cfg.cwcycle_dropold != 1 || cfg.cwcycle_sensitive != 4 || cfg.cwcycle_allowbadfromffb > 0 || cfg.cwcycle_usecwcfromce > 0 +#endif + ; +} + +static const struct config_list cache_opts[] = +{ + DEF_OPT_SAVE_FUNC(cache_should_save_fn), + DEF_OPT_FIXUP_FUNC(cache_fixups_fn), + DEF_OPT_UINT32("delay" , OFS(delay) , CS_DELAY), + DEF_OPT_INT32("max_time" , OFS(max_cache_time) , DEFAULT_MAX_CACHE_TIME), +#ifdef CS_CACHEEX +#ifdef CS_CACHEEX_AIO + DEF_OPT_UINT32("cw_cache_size" , OFS(cw_cache_size) , 0), + DEF_OPT_UINT32("cw_cache_memory" , OFS(cw_cache_memory) , 0), + DEF_OPT_FUNC("cw_cache_settings" , OFS(cw_cache_settings) , cacheex_cwcheck_tab_fn), + DEF_OPT_UINT32("ecm_cache_size" , OFS(ecm_cache_size) , 0), + DEF_OPT_UINT32("ecm_cache_memory" , OFS(ecm_cache_memory) , 0), + DEF_OPT_INT32("ecm_cache_droptime" , OFS(ecm_cache_droptime) , 0), +#endif + DEF_OPT_INT32("max_hit_time" , OFS(max_hitcache_time) , DEFAULT_MAX_HITCACHE_TIME), + DEF_OPT_FUNC("wait_time" , OFS(cacheex_wait_timetab) , cacheex_valuetab_fn), + DEF_OPT_FUNC("cacheex_mode1_delay" , OFS(cacheex_mode1_delay_tab) , caidvaluetab_fn), + DEF_OPT_UINT8("cacheexenablestats" , OFS(cacheex_enable_stats) , 0), +#ifdef CS_CACHEEX_AIO + DEF_OPT_UINT8("cacheex_dropdiffs" , OFS(cacheex_dropdiffs) , 0), + DEF_OPT_FUNC("cacheex_push_lg_groups" , OFS(cacheex_push_lg_groups) , group_fn), + DEF_OPT_UINT8("cacheex_lg_only_remote_settings" , OFS(cacheex_lg_only_remote_settings) , 1), + DEF_OPT_UINT8("cacheex_localgenerated_only" , OFS(cacheex_localgenerated_only) , 0), + DEF_OPT_FUNC("cacheex_localgenerated_only_caid" , OFS(cacheex_localgenerated_only_caidtab) , check_caidtab_fn), + DEF_OPT_FUNC_X("cacheex_lg_only_tab" , OFS(cacheex_lg_only_tab) , ftab_fn, FTAB_ACCOUNT), + DEF_OPT_UINT8("cacheex_lg_only_in_aio_only" , OFS(cacheex_lg_only_in_aio_only) , 0), + DEF_OPT_UINT8("cacheex_localgenerated_only_in" , OFS(cacheex_localgenerated_only_in) , 0), + DEF_OPT_FUNC("cacheex_localgenerated_only_in_caid", OFS(cacheex_localgenerated_only_in_caidtab), check_caidtab_fn), + DEF_OPT_FUNC_X("cacheex_lg_only_in_tab" , OFS(cacheex_lg_only_in_tab) , ftab_fn, FTAB_ACCOUNT), + DEF_OPT_FUNC("cacheex_ecm_filter" , OFS(cacheex_filter_caidtab) , cacheex_hitvaluetab_fn), + DEF_OPT_FUNC("cacheex_ecm_filter_aio" , OFS(cacheex_filter_caidtab_aio) , cacheex_hitvaluetab_fn), +#endif + DEF_OPT_INT32("csp_port" , OFS(csp_port) , 0), + DEF_OPT_FUNC("csp_serverip" , OFS(csp_srvip) , serverip_fn), + DEF_OPT_FUNC("csp_ecm_filter" , OFS(csp.filter_caidtab) , cacheex_hitvaluetab_fn), + DEF_OPT_UINT8("csp_allow_request" , OFS(csp.allow_request) , 1), + DEF_OPT_UINT8("csp_allow_reforward" , OFS(csp.allow_reforward) , 0), + DEF_OPT_FUNC("cacheex_cw_check" , OFS(cacheex_cwcheck_tab) , cacheex_cwcheck_tab_fn), + DEF_OPT_UINT8("wait_until_ctimeout" , OFS(wait_until_ctimeout) , 0), + DEF_OPT_UINT8("csp_block_fakecws" , OFS(csp.block_fakecws) , 0), +#ifdef CS_CACHEEX_AIO + DEF_OPT_FUNC("cacheex_nopushafter" , OFS(cacheex_nopushafter_tab) , caidvaluetab_fn), + DEF_OPT_UINT8("waittime_block_start" , OFS(waittime_block_start) , 0), + DEF_OPT_INT32("waittime_block_time" , OFS(waittime_block_time) , 0), +#endif +#endif +#ifdef CW_CYCLE_CHECK + DEF_OPT_INT8("cwcycle_check_enable" , OFS(cwcycle_check_enable) , 0), + DEF_OPT_FUNC("cwcycle_check_caid" , OFS(cwcycle_check_caidtab) , check_caidtab_fn), + DEF_OPT_INT32("cwcycle_maxlist" , OFS(maxcyclelist) , 500), + DEF_OPT_INT32("cwcycle_keeptime" , OFS(keepcycletime) , 15), + DEF_OPT_INT8("cwcycle_onbad" , OFS(onbadcycle) , 1), + DEF_OPT_INT8("cwcycle_dropold" , OFS(cwcycle_dropold) , 1), + DEF_OPT_INT8("cwcycle_sensitive" , OFS(cwcycle_sensitive) , 4), + DEF_OPT_INT8("cwcycle_allowbadfromffb" , OFS(cwcycle_allowbadfromffb) , 0), + DEF_OPT_INT8("cwcycle_usecwcfromce" , OFS(cwcycle_usecwcfromce) , 0), +#endif + DEF_LAST_OPT +}; + +#ifdef MODULE_CAMD35 +static bool camd35_should_save_fn(void *UNUSED(var)) +{ + return cfg.c35_port; +} + +static const struct config_list camd35_opts[] = +{ + DEF_OPT_SAVE_FUNC(camd35_should_save_fn), + DEF_OPT_INT32("port" , OFS(c35_port) , 0), + DEF_OPT_FUNC("serverip" , OFS(c35_srvip) , serverip_fn), + DEF_OPT_INT8("suppresscmd08", OFS(c35_udp_suppresscmd08), 0), + DEF_LAST_OPT +}; +#else +static const struct config_list camd35_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_NEWCAMD +static void porttab_fn(const char *token, char *value, void *setting, FILE *f) +{ + PTAB *ptab = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + clear_ptab(ptab); + } + else + { + chk_port_tab(value, ptab); + } + return; + } + value = mk_t_newcamd_port(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); +} +#endif + +#ifdef MODULE_CAMD35_TCP +static void porttab_camd35_fn(const char *token, char *value, void *setting, FILE *f) +{ + PTAB *ptab = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + clear_ptab(ptab); + } + else + { + chk_port_camd35_tab(value, ptab); + } + return; + } + value = mk_t_camd35tcp_port(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); +} +#endif + +#if defined(MODULE_NEWCAMD) || defined(MODULE_CAMD35_TCP) +static void porttab_free_fn(void *setting) +{ + clear_ptab(setting); +} +#endif + +#ifdef MODULE_CAMD35_TCP +static bool cs378x_should_save_fn(void *UNUSED(var)) +{ + return cfg.c35_tcp_ptab.nports && cfg.c35_tcp_ptab.ports[0].s_port; +} + +static const struct config_list cs378x_opts[] = +{ + DEF_OPT_SAVE_FUNC(cs378x_should_save_fn), + DEF_OPT_FUNC("port" , OFS(c35_tcp_ptab) , porttab_camd35_fn, .free_value = porttab_free_fn), + DEF_OPT_FUNC("serverip" , OFS(c35_tcp_srvip) , serverip_fn), + DEF_OPT_INT8("suppresscmd08", OFS(c35_tcp_suppresscmd08), 0), + DEF_LAST_OPT +}; +#else +static const struct config_list cs378x_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_NEWCAMD +static bool newcamd_should_save_fn(void *UNUSED(var)) +{ + return cfg.ncd_ptab.nports && cfg.ncd_ptab.ports[0].s_port; +} + +static const struct config_list newcamd_opts[] = +{ + DEF_OPT_SAVE_FUNC(newcamd_should_save_fn), + DEF_OPT_FUNC("port" , OFS(ncd_ptab) , porttab_fn, .free_value = porttab_free_fn), + DEF_OPT_FUNC("serverip" , OFS(ncd_srvip) , serverip_fn), + DEF_OPT_FUNC("allowed" , OFS(ncd_allowed) , iprange_fn, .free_value = iprange_free_fn), + DEF_OPT_HEX("key" , OFS(ncd_key) , SIZEOF(ncd_key)), + DEF_OPT_INT8("keepalive" , OFS(ncd_keepalive), DEFAULT_NCD_KEEPALIVE), + DEF_OPT_INT8("mgclient" , OFS(ncd_mgclient) , 0), + DEF_LAST_OPT +}; +#else +static const struct config_list newcamd_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_CCCAM +static void cccam_port_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + int i; + char *ptr, *saveptr1 = NULL; + memset(cfg.cc_port, 0, sizeof(cfg.cc_port)); + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); ptr && i < CS_MAXPORTS; ptr = strtok_r(NULL, ",", &saveptr1)) + { + cfg.cc_port[i] = strtoul(ptr, NULL, 10); + if(cfg.cc_port[i]) + { i++; } + } + return; + } + value = mk_t_cccam_port(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); +} + +static bool cccam_should_save_fn(void *UNUSED(var)) +{ + return cfg.cc_port[0]; +} + +static const struct config_list cccam_opts[] = +{ + DEF_OPT_SAVE_FUNC(cccam_should_save_fn), + DEF_OPT_FUNC("port" , OFS(cc_port) , cccam_port_fn), + DEF_OPT_FUNC("serverip" , OFS(cc_srvip) , serverip_fn), + DEF_OPT_HEX("nodeid" , OFS(cc_fixed_nodeid) , SIZEOF(cc_fixed_nodeid)), + DEF_OPT_SSTR("version" , OFS(cc_version) , "", SIZEOF(cc_version)), + DEF_OPT_INT8("reshare" , OFS(cc_reshare) , 10), + DEF_OPT_INT8("reshare_mode" , OFS(cc_reshare_services) , 4), + DEF_OPT_INT8("ignorereshare" , OFS(cc_ignore_reshare) , 0), + DEF_OPT_INT8("forward_origin_card", OFS(cc_forward_origin_card), 0), + DEF_OPT_INT8("stealth" , OFS(cc_stealth) , 0), + DEF_OPT_INT32("updateinterval" , OFS(cc_update_interval) , DEFAULT_UPDATEINTERVAL), + DEF_OPT_INT8("minimizecards" , OFS(cc_minimize_cards) , 0), + DEF_OPT_INT8("keepconnected" , OFS(cc_keep_connected) , 1), + DEF_OPT_UINT32("recv_timeout" , OFS(cc_recv_timeout) , DEFAULT_CC_RECV_TIMEOUT), + DEF_LAST_OPT +}; +#else +static const struct config_list cccam_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_PANDORA +static bool pandora_should_save_fn(void *UNUSED(var)) +{ + return cfg.pand_port; +} + +static const struct config_list pandora_opts[] = +{ + DEF_OPT_SAVE_FUNC(pandora_should_save_fn), + DEF_OPT_INT32("pand_port" , OFS(pand_port) , 0), + DEF_OPT_FUNC("pand_srvid" , OFS(pand_srvip) , serverip_fn), + DEF_OPT_STR("pand_usr" , OFS(pand_usr) , NULL), + DEF_OPT_STR("pand_pass" , OFS(pand_pass) , NULL), + DEF_OPT_INT8("pand_ecm" , OFS(pand_ecm) , 0), + DEF_OPT_INT8("pand_skip_send_dw", OFS(pand_skip_send_dw), 0), + DEF_OPT_FUNC("pand_allowed" , OFS(pand_allowed) , iprange_fn, .free_value = iprange_free_fn), + DEF_LAST_OPT +}; +#else +static const struct config_list pandora_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_SCAM +static bool scam_should_save_fn(void *UNUSED(var)) +{ + return cfg.scam_port; +} +static const struct config_list scam_opts[] = +{ + DEF_OPT_SAVE_FUNC(scam_should_save_fn), + DEF_OPT_INT32("port" , OFS(scam_port) , 0), + DEF_OPT_FUNC("serverip" , OFS(scam_srvip) , serverip_fn), + DEF_OPT_FUNC("allowed" , OFS(scam_allowed), iprange_fn, .free_value = iprange_free_fn), + DEF_LAST_OPT +}; +#else +static const struct config_list scam_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_STREAMRELAY +static bool streamrelay_should_save_fn(void *UNUSED(var)) +{ + return cfg.stream_relay_enabled; +} +static const struct config_list streamrelay_opts[] = +{ + DEF_OPT_SAVE_FUNC(streamrelay_should_save_fn), + DEF_OPT_INT8("stream_relay_enabled" , OFS(stream_relay_enabled), 0), + DEF_OPT_INT32("stream_relay_port" , OFS(stream_relay_port), 17999), + DEF_OPT_STR("stream_relay_user" , OFS(stream_relay_user), NULL), + DEF_OPT_FUNC("stream_relay_ctab" , OFS(stream_relay_ctab), check_caidtab_fn), + DEF_OPT_STR("stream_source_host" , OFS(stream_source_host), "127.0.0.1"), + DEF_OPT_INT8("stream_client_source_host" , OFS(stream_client_source_host), 1), + DEF_OPT_INT32("stream_source_port" , OFS(stream_source_port), DEFAULT_STREAM_SOURCE_PORT), + DEF_OPT_STR("stream_source_auth_user" , OFS(stream_source_auth_user), NULL), + DEF_OPT_STR("stream_source_auth_password" , OFS(stream_source_auth_password), NULL), + DEF_OPT_UINT32("stream_relay_buffer_time" , OFS(stream_relay_buffer_time), 0), + DEF_OPT_UINT8("stream_relay_reconnect_count" , OFS(stream_relay_reconnect_count), 0), +#ifdef WITH_EMU + DEF_OPT_INT8("stream_emm_enabled" , OFS(emu_stream_emm_enabled), 0), + DEF_OPT_UINT32("stream_ecm_delay" , OFS(emu_stream_ecm_delay), 600), +#endif + DEF_OPT_INT8("stream_display_client" , OFS(stream_display_client), 0), + DEF_OPT_INT8("stream_reuse_client" , OFS(stream_reuse_client), 0), +#ifdef WEBIF + DEF_OPT_INT8("stream_hide_client" , OFS(stream_hide_client), 0), +#endif + DEF_LAST_OPT +}; +#else +static const struct config_list streamrelay_opts[] = { DEF_LAST_OPT }; +#endif + + +#ifdef MODULE_RADEGAST +static bool radegast_should_save_fn(void *UNUSED(var)) +{ + return cfg.rad_port; +} + +static const struct config_list radegast_opts[] = +{ + DEF_OPT_SAVE_FUNC(radegast_should_save_fn), + DEF_OPT_INT32("port" , OFS(rad_port) , 0), + DEF_OPT_FUNC("serverip", OFS(rad_srvip) , serverip_fn), + DEF_OPT_FUNC("allowed" , OFS(rad_allowed), iprange_fn, .free_value = iprange_free_fn), + DEF_OPT_STR("user" , OFS(rad_usr) , NULL), + DEF_LAST_OPT +}; +#else +static const struct config_list radegast_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_SERIAL +static bool serial_should_save_fn(void *UNUSED(var)) +{ + return cfg.ser_device != NULL; +} + +static const struct config_list serial_opts[] = +{ + DEF_OPT_SAVE_FUNC(serial_should_save_fn), + DEF_OPT_STR("device", OFS(ser_device), NULL), + DEF_LAST_OPT +}; +#else +static const struct config_list serial_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_GBOX + +static void gbox_password_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + const char *s; + s = value; + if (s[strspn(s, "0123456789abcdefABCDEF")] == 0) + { + /* valid Hexa symbol */ + cfg.gbox_password = a2i(value, 8); + return; + } + else + { + cfg.gbox_password = 0; + } + } + if (cfg.gbox_password != 0) + { + fprintf_conf(f, token, "%08X\n", cfg.gbox_password); + } +} + +static void gbox_block_ecm_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + char *ptr1, *saveptr1 = NULL; + const char *s; + memset(cfg.gbox_block_ecm, 0, sizeof(cfg.gbox_block_ecm)); + int n = 0, i; + for (i = 0, ptr1 = strtok_r(value, ",", &saveptr1); (i < 4) && (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s=ptr1; + if ((n < GBOX_MAX_BLOCKED_ECM) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.gbox_block_ecm[n++] = a2i(ptr1, 4); } + } + cfg.gbox_block_ecm_num = n; + return; + } + if (cfg.gbox_block_ecm_num > 0) + { + value = mk_t_gbox_block_ecm(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +static void accept_remm_peer_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + char *ptr1, *saveptr1 = NULL; + const char *s; + memset(cfg.accept_remm_peer, 0, sizeof(cfg.accept_remm_peer)); + int n = 0, i; + for (i = 0, ptr1 = strtok_r(value, ",", &saveptr1); (i < 4) && (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s=ptr1; + if ((n < GBOX_MAX_REMM_PEERS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.accept_remm_peer[n++] = a2i(ptr1, 4); } + } + cfg.accept_remm_peer_num = n; + return; + } + + if (cfg.accept_remm_peer_num > 0) + { + value = mk_t_accept_remm_peer(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +static void gbox_ignored_peer_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + char *ptr1, *saveptr1 = NULL; + const char *s; + memset(cfg.gbox_ignored_peer, 0, sizeof(cfg.gbox_ignored_peer)); + int n = 0, i; + + for (i = 0, ptr1 = strtok_r(value, ",", &saveptr1); (i < 4) && (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s = ptr1; + if ((n < GBOX_MAX_IGNORED_PEERS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.gbox_ignored_peer[n++] = a2i(ptr1, 4); } + } + cfg.gbox_ignored_peer_num = n; + return; + } + + if (cfg.gbox_ignored_peer_num > 0) + { + value = mk_t_gbox_ignored_peer(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +static void gbox_proxy_card_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + char *ptr1, *saveptr1 = NULL; + const char *s; + memset(cfg.gbox_proxy_card, 0, sizeof(cfg.gbox_proxy_card)); + int n = 0, i; + for (i = 0, ptr1 = strtok_r(value, ",", &saveptr1); (i < 8) && (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s = ptr1; + if ((n < GBOX_MAX_PROXY_CARDS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.gbox_proxy_card[n++] = a2i(ptr1, 8); } + } + cfg.gbox_proxy_cards_num = n; + return; + } + + if (cfg.gbox_proxy_cards_num > 0) + { + value = mk_t_gbox_proxy_card(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +static void gbox_port_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + int i; + char *ptr, *saveptr1 = NULL; + memset(cfg.gbox_port, 0, sizeof(cfg.gbox_port)); + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); ptr && i < CS_MAXPORTS; ptr = strtok_r(NULL, ",", &saveptr1)) + { + cfg.gbox_port[i] = strtoul(ptr, NULL, 10); + if(cfg.gbox_port[i]) + { i++; } + } + return; + } + value = mk_t_gbox_port(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); +} + +static void gbox_my_vers_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + const char *s; + s = value; + int32_t len = cs_strlen(value); + + if ((s[strspn(s, "0123456789abcdefABCDEF")] != 0) || (len == 0) || (len > 2)) + { + cfg.gbox_my_vers = GBOX_MY_VERS_DEF; + } + else + { + cfg.gbox_my_vers = a2i(value, 1); + return; + } + } + + if(cfg.gbox_my_vers != GBOX_MY_VERS_DEF) + { + fprintf_conf(f, token, "%02X\n", cfg.gbox_my_vers); + } + else if(cfg.http_full_cfg) + { + fprintf_conf(f, token, "%02X\n", GBOX_MY_VERS_DEF); + } +} + +static void gbox_my_cpu_api_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + const char *s; + s = value; + int32_t len = cs_strlen(value); + + if ((s[strspn(s, "0123456789abcdefABCDEF")] != 0) || (len == 0) || (len > 2)) + { + cfg.gbox_my_cpu_api = GBOX_MY_CPU_API_DEF; + } + else + { + cfg.gbox_my_cpu_api = a2i(value,1); + return; + } + } + + if(cfg.gbox_my_cpu_api != GBOX_MY_CPU_API_DEF) + { + fprintf_conf(f, token, "%02X\n", cfg.gbox_my_cpu_api); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, token, "%02X\n", GBOX_MY_CPU_API_DEF); } +} + +static void gbox_dest_peers_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + char *ptr1, *saveptr1 = NULL; + const char *s; + memset(cfg.gbox_dest_peers, 0, sizeof(cfg.gbox_dest_peers)); + int n = 0; + + for (ptr1 = strtok_r(value, ",", &saveptr1); (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s = trim(ptr1); + if ((n < GBOX_MAX_DEST_PEERS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.gbox_dest_peers[n++] = a2i(trim(ptr1), cs_strlen(trim(ptr1))); } + } + cfg.gbox_dest_peers_num = n; + return; + } + + if ((cfg.gbox_dest_peers_num > 0) && cfg.gbox_save_gsms) + { + value = mk_t_gbox_dest_peers(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +static void gbox_msg_txt_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + cs_strncpy(cfg.gbox_msg_txt, value, sizeof(cfg.gbox_msg_txt)); + return; + } + + if ((cfg.gbox_msg_txt[0] != '\0') && cfg.gbox_save_gsms) + { + fprintf_conf(f, token, "%s\n", cfg.gbox_msg_txt); + } +} + +static bool gbox_should_save_fn(void *UNUSED(var)) +{ + return cfg.gbox_port[0]; +} + +static const struct config_list gbox_opts[] = +{ + DEF_OPT_SAVE_FUNC(gbox_should_save_fn), + DEF_OPT_FUNC("port" , OFS(gbox_port) , gbox_port_fn), + DEF_OPT_STR("hostname" , OFS(gbox_hostname) , NULL), + DEF_OPT_FUNC("my_password" , OFS(gbox_password) , gbox_password_fn ), + DEF_OPT_UINT32("gbox_reconnect", OFS(gbox_reconnect) , DEFAULT_GBOX_RECONNECT), + DEF_OPT_FUNC("my_vers" , OFS(gbox_my_vers) , gbox_my_vers_fn), + DEF_OPT_FUNC("my_cpu_api" , OFS(gbox_my_cpu_api) , gbox_my_cpu_api_fn), + DEF_OPT_UINT8("gsms_disable" , OFS(gsms_dis) , 1), + DEF_OPT_UINT8("dis_attack_txt" , OFS(dis_attack_txt) , 0), + DEF_OPT_UINT8("log_hello" , OFS(log_hello) , 1), + DEF_OPT_STR("tmp_dir" , OFS(gbox_tmp_dir) , NULL ), + DEF_OPT_FUNC("ignore_peer" , OFS(gbox_ignored_peer), gbox_ignored_peer_fn ), + DEF_OPT_FUNC("accept_remm_peer", OFS(accept_remm_peer) , accept_remm_peer_fn ), + DEF_OPT_FUNC("block_ecm" , OFS(gbox_block_ecm) , gbox_block_ecm_fn ), + DEF_OPT_FUNC("proxy_card" , OFS(gbox_proxy_card) , gbox_proxy_card_fn ), + DEF_OPT_UINT8("gbox_save_gsms" , OFS(gbox_save_gsms) , 0), + DEF_OPT_UINT8("gbox_msg_type" , OFS(gbox_msg_type) , 0), + DEF_OPT_FUNC("gbox_dest_peers" , OFS(gbox_dest_peers) , gbox_dest_peers_fn ), + DEF_OPT_FUNC("gbox_msg_txt" , OFS(gbox_msg_txt) , gbox_msg_txt_fn ), + DEF_OPT_UINT8("ccc_reshare" , OFS(cc_gbx_reshare_en), 0), + DEF_LAST_OPT +}; +#else +static const struct config_list gbox_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef HAVE_DVBAPI +extern const char *boxdesc[]; + +static void dvbapi_boxtype_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + int i; + cfg.dvbapi_boxtype = 0; + for(i = 1; i <= BOXTYPES; i++) + { + if(streq(value, boxdesc[i])) + { + cfg.dvbapi_boxtype = i; + break; + } + } + return; + } + + if(cfg.dvbapi_boxtype) + { fprintf_conf(f, token, "%s\n", boxdesc[cfg.dvbapi_boxtype]); } +} + +static void dvbapi_services_fn(const char *UNUSED(token), char *value, void *UNUSED(setting), FILE *UNUSED(f)) +{ + if(value) + { chk_services(value, &cfg.dvbapi_sidtabs); } + // THIS OPTION IS NOT SAVED +} + +static bool dvbapi_should_save_fn(void *UNUSED(var)) +{ + return cfg.dvbapi_enabled; +} + +static const struct config_list dvbapi_opts[] = +{ + DEF_OPT_SAVE_FUNC(dvbapi_should_save_fn), + DEF_OPT_INT8("enabled" , OFS(dvbapi_enabled) , 0), + DEF_OPT_INT8("au" , OFS(dvbapi_au) , 0), + DEF_OPT_INT8("pmt_mode" , OFS(dvbapi_pmtmode) , 0), + DEF_OPT_INT8("request_mode" , OFS(dvbapi_requestmode) , 0), + DEF_OPT_INT32("listen_port" , OFS(dvbapi_listenport) , 0), + DEF_OPT_FUNC("serverip" , OFS(dvbapi_srvip) , serverip_fn), + DEF_OPT_INT32("delayer" , OFS(dvbapi_delayer) , 0), + DEF_OPT_INT8("ecminfo_file" , OFS(dvbapi_ecminfo_file) , 1), + DEF_OPT_INT8("ecminfo_type" , OFS(dvbapi_ecminfo_type) , 0), + DEF_OPT_STR("user" , OFS(dvbapi_usr) , NULL), + DEF_OPT_INT8("read_sdt" , OFS(dvbapi_read_sdt) , 0), + DEF_OPT_INT8("write_sdt_prov" , OFS(dvbapi_write_sdt_prov) , 0), +#ifdef WITH_EXTENDED_CW + DEF_OPT_INT8("extended_cw_api", OFS(dvbapi_extended_cw_api), 0), +#endif +#ifdef MODULE_STREAMRELAY + DEF_OPT_INT8("demuxer_fix" , OFS(dvbapi_demuxer_fix) , 0), +#endif + DEF_OPT_FUNC("boxtype" , OFS(dvbapi_boxtype) , dvbapi_boxtype_fn), + DEF_OPT_FUNC("services" , OFS(dvbapi_sidtabs.ok) , dvbapi_services_fn), + DEF_LAST_OPT +}; +#else +static const struct config_list dvbapi_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef LCDSUPPORT +static void lcd_fixups_fn(void *UNUSED(var)) +{ + if(cfg.lcd_write_intervall < 5) { cfg.lcd_write_intervall = 5; } +} + +static bool lcd_should_save_fn(void *UNUSED(var)) +{ + return cfg.enablelcd; +} + +static const struct config_list lcd_opts[] = +{ + DEF_OPT_SAVE_FUNC(lcd_should_save_fn), + DEF_OPT_FIXUP_FUNC(lcd_fixups_fn), + DEF_OPT_INT8("enablelcd" , OFS(enablelcd) , 0), + DEF_OPT_STR("lcd_outputpath" , OFS(lcd_output_path) , NULL), + DEF_OPT_INT32("lcd_hideidle" , OFS(lcd_hide_idle) , 0), + DEF_OPT_INT32("lcd_writeintervall", OFS(lcd_write_intervall), 10), + DEF_LAST_OPT +}; +#else +static const struct config_list lcd_opts[] = { DEF_LAST_OPT }; +#endif + +static const struct config_sections oscam_conf[] = +{ + { "global", global_opts }, // *** MUST BE FIRST *** + { "anticasc", anticasc_opts }, + { "cache", cache_opts }, + { "lcd", lcd_opts }, + { "camd33", camd33_opts }, + { "cs357x", camd35_opts }, + { "cs378x", cs378x_opts }, + { "newcamd", newcamd_opts }, + { "radegast", radegast_opts }, + { "serial", serial_opts }, + { "gbox", gbox_opts }, + { "cccam", cccam_opts }, + { "pandora", pandora_opts }, + { "scam", scam_opts }, + { "streamrelay", streamrelay_opts }, + { "dvbapi", dvbapi_opts }, + { "monitor", monitor_opts }, + { "webif", webif_opts }, + { NULL, NULL } +}; + +void config_set(char *section, const char *token, char *value) +{ + config_set_value(oscam_conf, section, token, value, &cfg); +} + +void config_free(void) +{ + config_sections_free(oscam_conf, &cfg); + caidvaluetab_clear(&cfg.ftimeouttab); + ftab_clear(&cfg.double_check_caid); + ftab_clear(&cfg.disablecrccws_only_for); + cwvote_caidtab_clear(&cfg.cwvote_caids); +#ifdef WITH_LB + caidvaluetab_clear(&cfg.lb_retrylimittab); + caidvaluetab_clear(&cfg.lb_nbest_readers_tab); + caidtab_clear(&cfg.lb_noproviderforcaid); +#endif +#ifdef CS_CACHEEX +#ifdef CS_CACHEEX_AIO + cwcheckvaluetab_clear(&cfg.cw_cache_settings); +#endif + caidvaluetab_clear(&cfg.cacheex_mode1_delay_tab); +#ifdef CS_CACHEEX_AIO + caidvaluetab_clear(&cfg.cacheex_nopushafter_tab); + caidtab_clear(&cfg.cacheex_localgenerated_only_caidtab); + caidtab_clear(&cfg.cacheex_localgenerated_only_in_caidtab); + ftab_clear(&cfg.cacheex_lg_only_tab); + ftab_clear(&cfg.cacheex_lg_only_in_tab); + cecspvaluetab_clear(&cfg.cacheex_filter_caidtab); + cecspvaluetab_clear(&cfg.cacheex_filter_caidtab_aio); +#endif + cecspvaluetab_clear(&cfg.cacheex_wait_timetab); +#endif +#ifdef CW_CYCLE_CHECK + caidtab_clear(&cfg.cwcycle_check_caidtab); +#endif +} + +int32_t init_config(void) +{ + FILE *fp; + + if(config_enabled(WEBIF)) + { + fp = open_config_file(cs_conf); + } + else + { + fp = open_config_file_or_die(cs_conf); + } + + const struct config_sections *cur_section = oscam_conf; // Global + char *token; + + config_sections_set_defaults(oscam_conf, &cfg); + + if(!fp) + { + // no oscam.conf but webif is included in build, set it up for lan access and tweak defaults +#ifdef WEBIF + cfg.http_port = DEFAULT_HTTP_PORT; + char *default_allowed; + if ((default_allowed = cs_strdup(DEFAULT_HTTP_ALLOW))) + { + chk_iprange(default_allowed, &cfg.http_allowed); + free(default_allowed); + } +#endif + NULLFREE(cfg.logfile); + cfg.logtostdout = 1; +#ifdef HAVE_DVBAPI + cfg.dvbapi_enabled = 1; +#endif + cs_log("DEBUG: init_config - No oscam.conf found, using default settings."); + return 0; + } + + if(!cs_malloc(&token, MAXLINESIZE)) + { return 1; } + + int line = 0; + int valid_section = 1; + while(fgets(token, MAXLINESIZE, fp)) + { + ++line; + int len = cs_strlen(trim(token)); + if(len < 3) // a=b or [a] are at least 3 chars + { continue; } + if(token[0] == '#') // Skip comments + { continue; } + if(token[0] == '[' && token[len - 1] == ']') + { + token[len - 1] = '\0'; + valid_section = 0; + const struct config_sections *newconf = config_find_section(oscam_conf, token + 1); + if(config_section_is_active(newconf) && cur_section) + { + config_list_apply_fixups(cur_section->config, &cfg); + cur_section = newconf; + valid_section = 1; + } + if(!newconf) + { + fprintf(stderr, "WARNING: %s line %d unknown section [%s].\n", + cs_conf, line, token + 1); + continue; + } + if(!config_section_is_active(newconf)) + { + fprintf(stderr, "WARNING: %s line %d section [%s] is ignored (support not compiled in).\n", + cs_conf, line, newconf->section); + } + continue; + } + + if(!valid_section) + { continue; } + char *value = strchr(token, '='); + if(!value) // No = found, well go on + { continue; } + *value++ = '\0'; + char *tvalue = trim(value); + char *ttoken = trim(strtolower(token)); + if(cur_section && !config_list_parse(cur_section->config, ttoken, tvalue, &cfg)) + { + fprintf(stderr, "WARNING: %s line %d section [%s] contains unknown setting '%s=%s'\n", + cs_conf, line, cur_section->section, ttoken, tvalue); + } + } + NULLFREE(token); + fclose(fp); + if(cur_section) { config_list_apply_fixups(cur_section->config, &cfg); } + return 0; +} + +int32_t write_config(void) +{ + FILE *f = create_config_file(cs_conf); + if(!f) + { return 1; } + config_sections_save(oscam_conf, f, &cfg); + return flush_config_file(f, cs_conf); +} + +int32_t reload_global_config(void) +{ + FILE *fp = open_config_file(cs_conf); + if(!fp) + { + cs_log("WARNING: Cannot open oscam.conf for reloading global settings."); + return 1; + } + + char *token; + if(!cs_malloc(&token, MAXLINESIZE)) + { + fclose(fp); + return 1; + } + + const struct config_sections *global_section = config_find_section(oscam_conf, "global"); + if(!global_section || !config_section_is_active(global_section)) + { + cs_log("ERROR: Global section not found or not active during reload."); + NULLFREE(token); + fclose(fp); + return 1; + } + + int in_global_section = 0; + while(fgets(token, MAXLINESIZE, fp)) + { + int len = cs_strlen(trim(token)); + if(len < 3) { continue; } + if(token[0] == '#') { continue; } + + if(token[0] == '[' && token[len - 1] == ']') + { + token[len - 1] = '\0'; + if(streq(token + 1, "global")) + { + in_global_section = 1; + } + else + { + in_global_section = 0; + } + continue; + } + + if(in_global_section) + { + char *value = strchr(token, '='); + if(!value) { continue; } + *value++ = '\0'; + char *tvalue = trim(value); + char *ttoken = trim(strtolower(token)); + + if(!config_list_parse(global_section->config, ttoken, tvalue, &cfg)) + { + cs_log("WARNING: During global config reload, unknown setting '%s=%s' in section [global].", ttoken, tvalue); + } + } + } + + NULLFREE(token); + fclose(fp); + + // Apply fixups after reloading global settings + config_list_apply_fixups(global_section->config, &cfg); + + return 0; +} diff --git a/oscam-config-reader.c b/oscam-config-reader.c new file mode 100644 index 0000000..ef8a60b --- /dev/null +++ b/oscam-config-reader.c @@ -0,0 +1,1694 @@ +#define MODULE_LOG_PREFIX "config" + +#include "globals.h" +#include "module-stat.h" +#include "oscam-aes.h" +#include "oscam-array.h" +#include "oscam-conf.h" +#include "oscam-conf-chk.h" +#include "oscam-conf-mk.h" +#include "oscam-config.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#ifdef MODULE_GBOX +#include "module-gbox.h" +#endif +#ifdef CS_CACHEEX_AIO +#include "module-cacheex.h" +#endif + +#define cs_srvr "oscam.server" + +extern const struct s_cardreader *cardreaders[]; +extern char *RDR_CD_TXT[]; + +static void reader_label_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int i, found = 0; + if(!cs_strlen(value)) + { return; } + for(i = 0; i < (int)cs_strlen(value); i++) + { + if(value[i] == ' ') + { + value[i] = '_'; + found++; + } + } + if(found) + { fprintf(stderr, "Configuration reader: corrected label to %s\n", value); } + cs_strncpy(rdr->label, value, sizeof(rdr->label)); + return; + } + fprintf_conf(f, token, "%s\n", rdr->label); +} + +static void ecmwhitelist_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + if(cs_strlen(value)) + chk_ecm_whitelist(value, &rdr->ecm_whitelist); + else + ecm_whitelist_clear(&rdr->ecm_whitelist); + return; + } + + value = mk_t_ecm_whitelist(&rdr->ecm_whitelist); + if(cs_strlen(value) > 0 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%s\n", value); } + free_mk_t(value); +} + +static void ecmheaderwhitelist_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + if(cs_strlen(value)) + chk_ecm_hdr_whitelist(value, &rdr->ecm_hdr_whitelist); + else + ecm_hdr_whitelist_clear(&rdr->ecm_hdr_whitelist); + return; + } + + value = mk_t_ecm_hdr_whitelist(&rdr->ecm_hdr_whitelist); + if(cs_strlen(value) > 0 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%s\n", value); } + free_mk_t(value); +} + +static void protocol_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + if(cs_strlen(value) == 0) + { return; } + struct protocol_map + { + char *name; + int typ; + } protocols[] = + { + { "serial", R_SERIAL }, + { "camd35", R_CAMD35 }, + { "cs378x", R_CS378X }, + { "cs357x", R_CAMD35 }, + { "camd33", R_CAMD33 }, + { "gbox", R_GBOX }, + { "cccam", R_CCCAM }, + { "cccam_ext", R_CCCAM }, + { "cccam_mcs", R_CCCAM }, + { "constcw", R_CONSTCW }, + { "radegast", R_RADEGAST }, + { "scam", R_SCAM }, + { "ghttp", R_GHTTP }, + { "newcamd", R_NEWCAMD }, + { "newcamd525", R_NEWCAMD }, + { "newcamd524", R_NEWCAMD }, + { "drecas", R_DRECAS }, + { "emu", R_EMU }, + { NULL, 0 } + }, *p; + int i; + // Parse card readers + for(i = 0; cardreaders[i]; i++) + { + if(streq(value, cardreaders[i]->desc)) + { + rdr->crdr = cardreaders[i]; + rdr->typ = cardreaders[i]->typ; + return; + } + } + // Parse protocols + for(i = 0, p = &protocols[0]; p->name; p = &protocols[++i]) + { + if(streq(p->name, value)) + { + rdr->typ = p->typ; + break; + } + } + if(rdr->typ == R_NEWCAMD) + { rdr->ncd_proto = streq(value, "newcamd524") ? NCD_524 : NCD_525; } + if(!rdr->typ) + { + fprintf(stderr, "ERROR: '%s' is unsupported reader protocol!\n", value); + rdr->enable = 0; + } + return; + } + fprintf_conf(f, token, "%s\n", reader_get_type_desc(rdr, 0)); +} + +static void device_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + int32_t isphysical = !is_network_reader(rdr); + if(value) + { + int32_t i; + char *ptr, *saveptr1 = NULL; + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); (i < 3) && (ptr); ptr = strtok_r(NULL, ",", &saveptr1), i++) + { + trim(ptr); + switch(i) + { + case 0: + cs_strncpy(rdr->device, ptr, sizeof(rdr->device)); + break; + case 1: + rdr->r_port = atoi(ptr); + break; + case 2: + rdr->l_port = atoi(ptr); + break; + } + } + return; + } + fprintf_conf(f, token, "%s", rdr->device); // it should not have \n at the end + if((rdr->r_port || cfg.http_full_cfg) && !isphysical) + { fprintf(f, ",%d", rdr->r_port); } + if((rdr->l_port || cfg.http_full_cfg) && !isphysical && strncmp(reader_get_type_desc(rdr, 0), "cccam", 5)) + { fprintf(f, ",%d", rdr->l_port); } + fprintf(f, "\n"); +} + +static void reader_services_fn(const char *token, char *value, void *setting, FILE *f) +{ + services_fn(token, value, setting, f); + if(value) + { + struct s_reader *rdr = container_of(setting, struct s_reader, sidtabs); + if(rdr) + { rdr->changes_since_shareupdate = 1; } + } +} + +static void reader_lb_services_fn(const char *token, char *value, void *setting, FILE *f) +{ + services_fn(token, value, setting, f); + if(value) + { + struct s_reader *rdr = container_of(setting, struct s_reader, lb_sidtabs); + if(rdr) + { rdr->changes_since_shareupdate = 1; } + } +} + +static void reader_caid_fn(const char *token, char *value, void *setting, FILE *f) +{ + check_caidtab_fn(token, value, setting, f); + if(value) + { + struct s_reader *rdr = container_of(setting, struct s_reader, ctab); + if(rdr) + { rdr->changes_since_shareupdate = 1; } + } +} + +#ifdef WITH_CARDREADER +static void boxid_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + rdr->boxid = cs_strlen(value) ? a2i(value, 4) : 0; + return; + } + if(rdr->boxid) + { fprintf_conf(f, token, "%08X\n", rdr->boxid); } + else if(cfg.http_full_cfg) + { fprintf_conf(f, token, "\n"); } +} +#endif + +#ifdef READER_TONGFANG +static void tongfang3_calibsn_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + rdr->tongfang3_calibsn = strlen(value) ? a2i(value, 4) : 0; + return; + } + if(rdr->tongfang3_calibsn) + fprintf_conf(f, token, "%08X\n", rdr->tongfang3_calibsn); + else if(cfg.http_full_cfg) + { fprintf_conf(f, token, "\n"); } +} + +static void tongfang_boxid_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + rdr->tongfang_boxid = cs_strlen(value) ? a2i(value, 4) : 0; + return; + } + if(rdr->tongfang_boxid) + { fprintf_conf(f, token, "%08X\n", rdr->tongfang_boxid); } + else if(cfg.http_full_cfg) + { fprintf_conf(f, token, "\n"); } +} + +static void stbid_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = cs_strlen(value); + if(len != 16) + { + rdr->stbid_length = 0; + memset(rdr->stbid, 0, 8); + } + else + { + if(key_atob_l(value, rdr->stbid, len)) + { + fprintf(stderr, "reader stbid parse error, %s=%s\n", token, value); + rdr->stbid_length = 0; + memset(rdr->stbid, 0, sizeof(rdr->stbid)); + } + else + { + rdr->stbid_length = len/2; + } + } + return; + } + int32_t len = rdr->stbid_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "stbid", "%s\n", cs_hexdump(0, rdr->stbid, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { + fprintf_conf(f, "stbid", "\n"); + } +} + +static void tongfang3_deskey_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = cs_strlen(value); + if(len != 16) + { + rdr->tongfang3_deskey_length = 0; + memset(rdr->tongfang3_deskey, 0, 8); + } + else + { + if(key_atob_l(value, rdr->tongfang3_deskey, len)) + { + fprintf(stderr, "reader tongfang3_deskey parse error, %s=%s\n", token, value); + rdr->tongfang3_deskey_length = 0; + memset(rdr->tongfang3_deskey, 0, sizeof(rdr->tongfang3_deskey)); + } + else + { + rdr->tongfang3_deskey_length = len/2; + } + } + return; + } + int32_t len = rdr->tongfang3_deskey_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "tongfang3_deskey", "%s\n", cs_hexdump(0, rdr->tongfang3_deskey, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { + fprintf_conf(f, "tongfang3_deskey", "\n"); + } +} +#endif + +#ifdef WITH_CARDREADER +static void cwpkkey_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = strlen(value); + // rdr_log(rdr, "CWPK config key length: %16X", len); + if(len == 0 || len > 32) + { + rdr->cwpk_mod_length = 0; + memset(rdr->cwpk_mod, 0, sizeof(rdr->cwpk_mod)); + } + else + { + if(key_atob_l(value, rdr->cwpk_mod, len)) + { + fprintf(stderr, "reader cwpkkey parse error, %s=%s\n", token, value); + rdr->cwpk_mod_length = 0; + memset(rdr->cwpk_mod, 0, sizeof(rdr->cwpk_mod)); + } + else + { + rdr->cwpk_mod_length = len/2; + } + } + return; + } + int32_t len = rdr->cwpk_mod_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "cwpkkey", "%s\n", cs_hexdump(0, rdr->cwpk_mod, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "cwpkkey", "\n"); } +} + +static void rsakey_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = cs_strlen(value); + if(len != 128 && len != 240) + { + rdr->rsa_mod_length = 0; + memset(rdr->rsa_mod, 0, 120); + } + else + { + if(key_atob_l(value, rdr->rsa_mod, len)) + { + fprintf(stderr, "reader rsakey parse error, %s=%s\n", token, value); + rdr->rsa_mod_length = 0; + memset(rdr->rsa_mod, 0, sizeof(rdr->rsa_mod)); + } + else + { + rdr->rsa_mod_length = len/2; + } + } + return; + } + int32_t len = rdr->rsa_mod_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "rsakey", "%s\n", cs_hexdump(0, rdr->rsa_mod, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "rsakey", "\n"); } +} + +static void deskey_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = cs_strlen(value); + if(((len % 16) != 0) || len == 0 || len > 128*2) + { + rdr->des_key_length = 0; + memset(rdr->des_key, 0, sizeof(rdr->des_key)); + } + else + { + if(key_atob_l(value, rdr->des_key, len)) + { + fprintf(stderr, "reader 3DES key parse error, %s=%s\n", token, value); + rdr->des_key_length = 0; + memset(rdr->des_key, 0, sizeof(rdr->des_key)); + } + else + { + rdr->des_key_length = len/2; + } + } + return; + } + int32_t len = rdr->des_key_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "deskey", "%s\n", cs_hexdump(0, rdr->des_key, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "deskey", "\n"); } +} + +static void boxkey_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int32_t len = cs_strlen(value); + if(((len % 8) != 0) || len == 0 || len > 32) + { + rdr->boxkey_length = 0; + memset(rdr->boxkey, 0, sizeof(rdr->boxkey)); + } + else + { + if(key_atob_l(value, rdr->boxkey, len)) + { + fprintf(stderr, "reader boxkey parse error, %s=%s\n", token, value); + rdr->boxkey_length = 0; + memset(rdr->boxkey, 0, sizeof(rdr->boxkey)); + } + else + { + rdr->boxkey_length = len/2; + } + } + return; + } + int32_t len = rdr->boxkey_length; + if(len > 0) + { + char tmp[len * 2 + 1]; + fprintf_conf(f, "boxkey", "%s\n", cs_hexdump(0, rdr->boxkey, len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, "boxkey", "\n"); } +} +#endif + +#if defined(READER_NAGRA) || defined(READER_NAGRA_MERLIN) +static void param_fn(const char *token, char *value, void *setting, long data, FILE *f) +{ + uint8_t *var = setting, valid_len = data & 0xFF; + uint8_t *var_len = (var + (data >> 8)); + if(value) + { + int32_t len = cs_strlen(value); + if(len != valid_len * 2 || key_atob_l(value, var, len)) + { + if(len > 0) + { fprintf(stderr, "reader %s parse error, %s=%s\n", token, token, value); } + *var_len = 0; + memset(var, 0, valid_len); + } + else + { + *var_len = valid_len; // found and correct + } + return; + } + if(*var_len) + { + char tmp[*var_len * 2 + 1]; + fprintf_conf(f, token, "%s\n", cs_hexdump(0, var, *var_len, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, token, "\n"); } +} +#endif + +static void flags_fn(const char *token, char *value, void *setting, long flag, FILE *f) +{ + uint32_t *var = setting; + if(value) + { + int i = atoi(value); + if(!i && (*var & flag)) + { *var -= flag; } + if(i) + { *var |= flag; } + return; + } + if((*var & flag) || cfg.http_full_cfg) + { fprintf_conf(f, token, "%d\n", (*var & flag) ? 1 : 0); } +} + +#ifdef READER_VIDEOGUARD +static void ins7E_fn(const char *token, char *value, void *setting, long var_size, FILE *f) +{ + uint8_t *var = setting; + var_size -= 1; // var_size contains sizeof(var) which is [X + 1] + if(value) + { + int32_t len = cs_strlen(value); + if(len != var_size * 2 || key_atob_l(value, var, len)) + { + if(len > 0) + { fprintf(stderr, "reader %s parse error, %s=%s\n", token, token, value); } + memset(var, 0, var_size + 1); + } + else + { + var[var_size] = 1; // found and correct + } + return; + } + if(var[var_size]) + { + char tmp[var_size * 2 + 1]; + fprintf_conf(f, token, "%s\n", cs_hexdump(0, var, var_size, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, token, "\n"); } +} + +static void ins42_fn(const char *token, char *value, void *setting, long var_size, FILE *f) +{ + uint8_t *var = setting; + var_size -= 1; // var_size contains sizeof(var) which is [X + 1] + if(value) + { + int32_t len = cs_strlen(value); + if(len != var_size * 2 || key_atob_l(value, var, len)) + { + if(len > 0) + { fprintf(stderr, "reader %s parse error, %s=%s\n", token, token, value); } + memset(var, 0, var_size + 1); + } + else + { + var[var_size] = 1; // found and correct + } + return; + } + if(var[var_size]) + { + char tmp[var_size * 2 + 1]; + fprintf_conf(f, token, "%s\n", cs_hexdump(0, var, var_size, tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, token, "\n"); } +} + +static void des_and_3des_key_fn(const char *token, char *value, void *setting, FILE *f) +{ + uint8_t *var = setting; + if(value) + { + int32_t len = cs_strlen(value); + if(((len != 16) && (len != 32)) || (key_atob_l(value, var, len))) + { + if(len > 0) + { fprintf(stderr, "reader %s parse error, %s=%s\n", token, token, value); } + memset(var, 0, 17); + } + else + { + var[16] = len/2; + } + return; + } + if(var[16]) + { + char tmp[var[16] * 2 + 1]; + fprintf_conf(f, token, "%s\n", cs_hexdump(0, var, var[16], tmp, sizeof(tmp))); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, token, "\n"); } +} +#endif + +#ifdef WITH_CARDREADER +static void atr_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + memset(rdr->atr, 0, sizeof(rdr->atr)); + rdr->atrlen = cs_strlen(value); + if(rdr->atrlen) + { + if(rdr->atrlen > (int32_t)sizeof(rdr->atr) * 2) + { rdr->atrlen = (int32_t)sizeof(rdr->atr) * 2; } + key_atob_l(value, rdr->atr, rdr->atrlen); + } + return; + } + if(rdr->atr[0] || cfg.http_full_cfg) + { + int j; + fprintf_conf(f, token, "%s", ""); // it should not have \n at the end + if(rdr->atr[0]) + { + for(j = 0; j < rdr->atrlen / 2; j++) + { + fprintf(f, "%02X", rdr->atr[j]); + } + } + fprintf(f, "\n"); + } +} + +static void detect_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + int i; + for(i = 0; RDR_CD_TXT[i]; i++) + { + if(!strcmp(value, RDR_CD_TXT[i])) + { + rdr->detect = i; + } + else + { + if(value[0] == '!' && streq(value + 1, RDR_CD_TXT[i])) + { rdr->detect = i | 0x80; } + } + } + return; + } + fprintf_conf(f, token, "%s%s\n", rdr->detect & 0x80 ? "!" : "", RDR_CD_TXT[rdr->detect & 0x7f]); +} +#endif + +void ftab_fn(const char *token, char *value, void *setting, long ftab_type, FILE *f) +{ + FTAB *ftab = setting; + if(value) + { + if(cs_strlen(value)) + chk_ftab(value, ftab); + else + ftab_clear(ftab); + return; + } + if(ftab_type & FTAB_READER) + { + struct s_reader *rdr = NULL; + if(ftab_type & FTAB_PROVID) { rdr = container_of(setting, struct s_reader, ftab); } + if(ftab_type & FTAB_CHID) { rdr = container_of(setting, struct s_reader, fchid); } + if(ftab_type & FTAB_FBPCAID) { rdr = container_of(setting, struct s_reader, fallback_percaid); } + if(ftab_type & FTAB_LOCALCARDS) { rdr = container_of(setting, struct s_reader, localcards); } + if(ftab_type & FTAB_IGNCHKSMCAID){ rdr = container_of(setting, struct s_reader, disablecrccws_only_for); } +#ifdef WITH_EMU + if(ftab_type & FTAB_EMUAU) { rdr = container_of(setting, struct s_reader, emu_auproviders); } +#endif +#ifdef MODULE_GBOX + if(ftab_type & FTAB_CCCGBXRESHARE){ rdr = container_of(setting, struct s_reader, ccc_gbx_reshare_ident); } +#endif + if(rdr) + { rdr->changes_since_shareupdate = 1; } + } + value = mk_t_ftab(ftab); + if(cs_strlen(value) > 0 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%s\n", value); } + free_mk_t(value); +} + +#ifdef READER_VIACCESS +static void aeskeys_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + parse_aes_keys(rdr, value); + return; + } + value = mk_t_aeskeys(rdr); + if(cs_strlen(value) > 0 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%s\n", value); } + free_mk_t(value); +} +#endif + +static void emmcache_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + rdr->cachemm = 0; + rdr->rewritemm = 0; + rdr->logemm = 0; + rdr->deviceemm = 0; + if(cs_strlen(value)) + { + int i; + char *ptr, *saveptr1 = NULL; + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); (i < 4) && (ptr); ptr = strtok_r(NULL, ",", &saveptr1), i++) + { + switch(i) + { + case 0: + rdr->cachemm = atoi(ptr); + break; + case 1: + rdr->rewritemm = atoi(ptr); + break; + case 2: + rdr->logemm = atoi(ptr); + break; + case 3: + rdr->deviceemm = atoi(ptr); + } + } + if(rdr->rewritemm <= 0) + { + fprintf(stderr, "Setting reader \"emmcache\" to %i,%d,%i,%i instead of %i,%i,%i,%i.", + rdr->cachemm, 1, rdr->logemm, rdr->deviceemm, + rdr->cachemm, rdr->rewritemm, rdr->logemm, rdr->deviceemm); + fprintf(stderr, "Zero or negative number of rewrites is silly\n"); + rdr->rewritemm = 1; + } + } + return; + } + if(rdr->cachemm || rdr->logemm || cfg.http_full_cfg) + { fprintf_conf(f, token, "%d,%d,%d,%d\n", rdr->cachemm, rdr->rewritemm, rdr->logemm,rdr->deviceemm); } +} + +static void blockemm_bylen_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + char *ptr, *saveptr1 = NULL, dash; + struct s_emmlen_range *blocklen; + uint32_t num; + + if(!cs_strlen(value)) + { + ll_destroy_data(&rdr->blockemmbylen); + return; + } + + if(!rdr->blockemmbylen) + { rdr->blockemmbylen = ll_create("blockemmbylen"); } + else + { ll_clear_data(rdr->blockemmbylen); } + + for(ptr = strtok_r(value, ",", &saveptr1); ptr; + ptr = strtok_r(NULL, ",", &saveptr1)) + { + if(!cs_malloc(&blocklen, sizeof(*blocklen))) + { return; } + num = sscanf(ptr, "%hd%c%hd", &blocklen->min, &dash, &blocklen->max); + if(num <= 0) + { + NULLFREE(blocklen); + fprintf(stderr, "blockemm-bylen parse error: %s\n", value); + continue; + } + if(num == 1) // single values: x1, x2, x3, ... + { blocklen->max = blocklen->min; } + else if(num == 2) // range values with open end: x1- + { blocklen->max = 0; } + ll_append(rdr->blockemmbylen, blocklen); + } + return; + } + value = mk_t_emmbylen(rdr); + if(cs_strlen(value) > 0 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%s\n", value); } + free_mk_t(value); +} + +static void nano_fn(const char *token, char *value, void *setting, FILE *f) +{ + uint16_t *nano = setting; + if(value) + { + *nano = 0; + if(cs_strlen(value) > 0) + { + if(streq(value, "all")) + { + *nano = 0xFFFF; + } + else + { + int32_t i; + char *ptr, *saveptr1 = NULL; + for(ptr = strtok_r(value, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1)) + { + i = (byte_atob(ptr) % 0x80); + if(i >= 0 && i <= 16) + { *nano |= (1 << i); } + } + } + } + return; + } + value = mk_t_nano(*nano); + if(cs_strlen(value) > 0 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%s\n", value); } + free_mk_t(value); +} + +static void auprovid_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + rdr->auprovid = 0; + if(cs_strlen(value)) + { rdr->auprovid = a2i(value, 3); } + return; + } + if(rdr->auprovid) + { fprintf_conf(f, token, "%06X\n", rdr->auprovid); } + else if(cfg.http_full_cfg) + { fprintf_conf(f, token, "\n"); } +} + +static void ratelimitecm_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + rdr->ratelimitecm = 0; + if(cs_strlen(value)) + { + int i; + rdr->ratelimitecm = atoi(value); + for(i = 0; i < MAXECMRATELIMIT; i++) // reset all slots + { + rdr->rlecmh[i].srvid = -1; + rdr->rlecmh[i].last.time = -1; + } + } + return; + } + if(rdr->ratelimitecm || cfg.http_full_cfg) + { fprintf_conf(f, token, "%d\n", rdr->ratelimitecm); } +} + +static void ecmunique_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + rdr->ecmunique = 0; // default + } + else + { + rdr->ecmunique = atoi(value); + if(rdr->ecmunique >= 1) + { rdr->ecmunique = 1; } + else + { rdr->ecmunique = 0; } + } + return; + } + if((rdr->ratelimitecm && rdr->ecmunique != 0) || cfg.http_full_cfg) + { fprintf_conf(f, token, "%d\n", rdr->ecmunique); } +} + +static void ratelimittime_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + if(rdr->ratelimitecm > 0) + { + rdr->ratelimittime = 9000; // default 9 seconds + rdr->srvidholdtime = 2000; // default 2 seconds hold + } + else + { + rdr->ratelimitecm = 0; // in case someone set a negative value + rdr->ratelimittime = 0; + rdr->srvidholdtime = 0; + } + } + else + { + rdr->ratelimittime = atoi(value); + if (rdr->ratelimittime < 60) rdr->ratelimittime *= 1000; + } + return; + } + if(rdr->ratelimitecm || cfg.http_full_cfg) + { fprintf_conf(f, token, "%d\n", rdr->ratelimittime); } +} + +static void srvidholdtime_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + if(rdr->ratelimitecm > 0) + { + rdr->srvidholdtime = 2000; // default 2 seconds hold + } + else + { + rdr->ratelimitecm = 0; // in case someone set a negative value + rdr->srvidholdtime = 0; + } + } + else + { + rdr->srvidholdtime = atoi(value); + if (rdr->srvidholdtime < 60) rdr->srvidholdtime *=1000; + } + return; + } + if(rdr->ratelimitecm || cfg.http_full_cfg) + { fprintf_conf(f, token, "%d\n", rdr->srvidholdtime); } +} + +static void cooldown_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + rdr->cooldown[0] = 0; + rdr->cooldown[1] = 0; + } + else + { + int32_t i; + char *ptr, *saveptr1 = NULL; + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); (i < 2) && (ptr); ptr = strtok_r(NULL, ",", &saveptr1), i++) + { + rdr->cooldown[i] = atoi(ptr); + } + if(rdr->cooldown[0] <= 0 || rdr->cooldown[1] <= 0) + { + fprintf(stderr, "cooldown must have 2 positive values (x,y) set values %d,%d ! cooldown deactivated\n", + rdr->cooldown[0], rdr->cooldown[1]); + rdr->cooldown[0] = 0; + rdr->cooldown[1] = 0; + } + } + return; + } + if(rdr->cooldown[0] || cfg.http_full_cfg) + { + fprintf_conf(f, token, "%d,%d\n", rdr->cooldown[0], rdr->cooldown[1]); + } +} + +static void parallelfactor_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_reader *rdr = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + rdr->parallelfactor = 1.5; // default + } + else + { + // Parse float manually to avoid locale issues (strtof uses locale decimal separator) + // Accept both '.' and ',' as decimal separator + char tmp[32]; + cs_strncpy(tmp, value, sizeof(tmp)); + + // Find decimal separator (either . or ,) + char *sep = strchr(tmp, '.'); + if(!sep) sep = strchr(tmp, ','); + + if(sep) + { + *sep = '\0'; // Split at separator + int32_t integer_part = atoi(tmp); + int32_t decimal_part = atoi(sep + 1); + // Calculate decimal value (assuming 1 decimal place) + rdr->parallelfactor = (float)integer_part + (float)decimal_part / 10.0f; + } + else + { + rdr->parallelfactor = (float)atoi(tmp); + } + + if(rdr->parallelfactor < 0) + rdr->parallelfactor = 1.5; // default on negative + } + return; + } + // Write config: use comma as decimal separator + if(rdr->parallelfactor != 1.5 || cfg.http_full_cfg) + { + char buf[16]; + snprintf(buf, sizeof(buf), "%.1f", rdr->parallelfactor); + char *dot = strchr(buf, '.'); + if(dot) *dot = ','; + fprintf_conf(f, token, "%s\n", buf); + } +} + +static void cooldowndelay_fn(const char *UNUSED(token), char *value, void *setting, FILE *UNUSED(f)) +{ + struct s_reader *rdr = setting; + if(value) + { + rdr->cooldown[0] = cs_strlen(value) ? atoi(value) : 0; + } + // This option is *not* written in the config file. + // It is only set by WebIf as convenience +} + +static void cooldowntime_fn(const char *UNUSED(token), char *value, void *setting, FILE *UNUSED(f)) +{ + struct s_reader *rdr = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + rdr->cooldown[0] = 0; // no cooling down time means no cooling set + rdr->cooldown[1] = 0; + } + else + { + rdr->cooldown[1] = atoi(value); + } + return; + } + // This option is *not* written in the config file. + // It is only set by WebIf as convenience +} + +void reader_fixups_fn(void *var) +{ + struct s_reader *rdr = var; +#ifdef WITH_LB + if(rdr->lb_weight > 1000) + { rdr->lb_weight = 1000; } + else if(rdr->lb_weight <= 0) + { rdr->lb_weight = 100; } +#endif + +#ifdef CS_CACHEEX_AIO + caidtab2ftab_add(&rdr->cacheex.localgenerated_only_in_caidtab, &rdr->cacheex.lg_only_in_tab); + caidtab_clear(&rdr->cacheex.localgenerated_only_in_caidtab); + caidtab2ftab_add(&rdr->cacheex.localgenerated_only_caidtab, &rdr->cacheex.lg_only_tab); + caidtab_clear(&rdr->cacheex.localgenerated_only_caidtab); +#endif + + if(is_cascading_reader(rdr) && (rdr->typ == R_CAMD35 || rdr->typ == R_CS378X)) + { +#ifdef CS_CACHEEX + if(rdr && rdr->cacheex.mode>1) + { rdr->keepalive = 1; } // with cacheex, it is required! + else +#endif + if(rdr->typ == R_CAMD35) + { rdr->keepalive = 0; } // with NO-cacheex, and UDP, keepalive is not required! + } + + // Allocate/reallocate parallel_slots if maxparallel changed + // Two separate arrays: active slots (maxparallel) and pending slots (round(maxparallel * parallelfactor)) + if(rdr->maxparallel > 0) + { + // Set default parallelfactor if negative (not configured) + // parallelfactor = 0 is valid (disables pending slots) + if(rdr->parallelfactor < 0) + rdr->parallelfactor = 1.5; + + // Calculate pending slots size: round(maxparallel * parallelfactor) without math.h + // parallelfactor = 0 means no pending slots (zapping support disabled) + int32_t pending_size = (int32_t)(rdr->maxparallel * rdr->parallelfactor + 0.5); + + // Only allocate if not yet allocated (first time setup) + // Size changes require OSCam restart to take effect + if(!rdr->parallel_slots) + { + if(!cs_malloc(&rdr->parallel_slots, rdr->maxparallel * sizeof(struct s_parallel_slot))) + { rdr->maxparallel = 0; } // disable on allocation failure + else if(pending_size > 0 && !cs_malloc(&rdr->parallel_slots_prov, pending_size * sizeof(struct s_parallel_slot))) + { + NULLFREE(rdr->parallel_slots); // free first array on second failure + rdr->maxparallel = 0; + } + else + { + cs_lock_create(__func__, &rdr->parallel_lock, "parallel_lock", 5000); + rdr->blocked_services = ll_create("blocked_services"); + } + + rdr->parallel_full = 0; // reset full flag + } + } + else if(rdr->parallel_slots || rdr->parallel_slots_prov) + { + // maxparallel was disabled, free the slots + NULLFREE(rdr->parallel_slots); + NULLFREE(rdr->parallel_slots_prov); + ll_destroy_data(&rdr->blocked_services); + rdr->parallel_full = 0; + } +} + +#define OFS(X) offsetof(struct s_reader, X) +#define SIZEOF(X) sizeof(((struct s_reader *)0)->X) + +static const struct config_list reader_opts[] = +{ + DEF_OPT_FIXUP_FUNC(reader_fixups_fn), + DEF_OPT_FUNC("label" , 0, reader_label_fn), +#ifdef WEBIF + DEF_OPT_STR("description" , OFS(description), NULL), +#endif + DEF_OPT_INT8("enable" , OFS(enable), 1), + DEF_OPT_FUNC("protocol" , 0, protocol_fn), + DEF_OPT_FUNC("device" , 0, device_fn), + DEF_OPT_UINT8("ipv4force" , OFS(ipv4force), 0), + DEF_OPT_HEX("key" , OFS(ncd_key), SIZEOF(ncd_key)), + DEF_OPT_SSTR("user" , OFS(r_usr), "", SIZEOF(r_usr)), + DEF_OPT_SSTR("password" , OFS(r_pwd), "", SIZEOF(r_pwd)), + DEF_OPT_SSTR("pincode" , OFS(pincode), "none", SIZEOF(pincode)), +#ifdef MODULE_GBOX + DEF_OPT_UINT8("gbox_max_distance" , OFS(gbox_maxdist), DEFAULT_GBOX_MAX_DIST), + DEF_OPT_UINT8("gbox_max_ecm_send" , OFS(gbox_maxecmsend), DEFAULT_GBOX_MAX_ECM_SEND), + DEF_OPT_UINT8("gbox_reshare" , OFS(gbox_reshare), DEFAULT_GBOX_RESHARE), + DEF_OPT_INT8("cccam_reshare" , OFS(gbox_cccam_reshare), -1), + DEF_OPT_UINT8("force_remm" , OFS(gbox_force_remm), 0), + DEF_OPT_FUNC_X("ccc_gbx_reshare_ident" , OFS(ccc_gbx_reshare_ident), ftab_fn, FTAB_READER | FTAB_CCCGBXRESHARE), + DEF_OPT_UINT8("send_offline_cmd" , OFS(send_offline_cmd), 0), +#endif + DEF_OPT_STR("readnano" , OFS(emmfile), NULL), + DEF_OPT_FUNC("services" , OFS(sidtabs), reader_services_fn), + DEF_OPT_FUNC("lb_whitelist_services" , OFS(lb_sidtabs), reader_lb_services_fn), + DEF_OPT_INT32("inactivitytimeout" , OFS(tcp_ito), DEFAULT_INACTIVITYTIMEOUT), + DEF_OPT_INT32("reconnecttimeout" , OFS(tcp_rto), DEFAULT_TCP_RECONNECT_TIMEOUT), + DEF_OPT_INT32("reconnectdelay" , OFS(tcp_reconnect_delay), 60000), + DEF_OPT_INT32("resetcycle" , OFS(resetcycle), 0), + DEF_OPT_INT8("disableserverfilter" , OFS(ncd_disable_server_filt), 0), + DEF_OPT_INT8("connectoninit" , OFS(ncd_connect_on_init), 0), + DEF_OPT_UINT8("keepalive" , OFS(keepalive), 0), +#ifdef WITH_CARDREADER + DEF_OPT_INT8("smargopatch" , OFS(smargopatch), 0), + DEF_OPT_INT8("autospeed" , OFS(autospeed), 1), + DEF_OPT_UINT8("sc8in1_dtrrts_patch" , OFS(sc8in1_dtrrts_patch), 0), +#endif + DEF_OPT_INT8("fallback" , OFS(fallback), 0), + DEF_OPT_FUNC_X("fallback_percaid" , OFS(fallback_percaid), ftab_fn, FTAB_READER | FTAB_FBPCAID), + DEF_OPT_FUNC_X("localcards" , OFS(localcards), ftab_fn, FTAB_READER | FTAB_LOCALCARDS), + DEF_OPT_FUNC_X("disablecrccws_only_for" , OFS(disablecrccws_only_for), ftab_fn, FTAB_READER | FTAB_IGNCHKSMCAID), +#ifdef CS_CACHEEX + DEF_OPT_INT8("cacheex" , OFS(cacheex.mode), 0), + DEF_OPT_INT8("cacheex_maxhop" , OFS(cacheex.maxhop), 0), +#ifdef CS_CACHEEX_AIO + DEF_OPT_INT8("cacheex_maxhop_lg" , OFS(cacheex.maxhop_lg), 0), +#endif + DEF_OPT_FUNC("cacheex_ecm_filter" , OFS(cacheex.filter_caidtab), cacheex_hitvaluetab_fn), + DEF_OPT_UINT8("cacheex_allow_request" , OFS(cacheex.allow_request), 0), + DEF_OPT_UINT8("cacheex_drop_csp" , OFS(cacheex.drop_csp), 0), + DEF_OPT_UINT8("cacheex_allow_filter" , OFS(cacheex.allow_filter), 1), +#ifdef CS_CACHEEX_AIO + DEF_OPT_UINT8("cacheex_allow_maxhop" , OFS(cacheex.allow_maxhop), 0), +#endif + DEF_OPT_UINT8("cacheex_block_fakecws" , OFS(cacheex.block_fakecws), 0), +#ifdef CS_CACHEEX_AIO + DEF_OPT_UINT8("cacheex_cw_check_for_push" , OFS(cacheex.cw_check_for_push), 0), + DEF_OPT_UINT8("cacheex_lg_only_remote_settings", OFS(cacheex.lg_only_remote_settings), 1), + DEF_OPT_UINT8("cacheex_localgenerated_only" , OFS(cacheex.localgenerated_only), 0), + DEF_OPT_FUNC("cacheex_localgenerated_only_caid", OFS(cacheex.localgenerated_only_caidtab), check_caidtab_fn), + DEF_OPT_FUNC_X("cacheex_lg_only_tab" , OFS(cacheex.lg_only_tab), ftab_fn, FTAB_ACCOUNT), + DEF_OPT_UINT8("cacheex_lg_only_in_aio_only" , OFS(cacheex.lg_only_in_aio_only), 0), + DEF_OPT_UINT8("cacheex_localgenerated_only_in", OFS(cacheex.localgenerated_only_in), 0), + DEF_OPT_FUNC("cacheex_localgenerated_only_in_caid", OFS(cacheex.localgenerated_only_in_caidtab), check_caidtab_fn), + DEF_OPT_FUNC_X("cacheex_lg_only_in_tab" , OFS(cacheex.lg_only_in_tab), ftab_fn, FTAB_ACCOUNT), + DEF_OPT_FUNC("cacheex_nopushafter" , OFS(cacheex.cacheex_nopushafter_tab), caidvaluetab_fn), +#endif +#endif + DEF_OPT_FUNC("caid" , OFS(ctab), reader_caid_fn), +#ifdef WITH_CARDREADER + DEF_OPT_FUNC("atr" , 0, atr_fn), + DEF_OPT_FUNC("boxid" , 0, boxid_fn), +#endif +#ifdef READER_TONGFANG + DEF_OPT_FUNC("tongfang3_calibsn" , 0, tongfang3_calibsn_fn), + DEF_OPT_FUNC("tongfang_boxid" , 0, tongfang_boxid_fn), + DEF_OPT_FUNC("stbid" , 0, stbid_fn), + DEF_OPT_FUNC("tongfang3_deskey" , 0, tongfang3_deskey_fn), +#endif +#ifdef WITH_CARDREADER + DEF_OPT_FUNC("boxkey" , 0, boxkey_fn), + DEF_OPT_FUNC("rsakey" , 0, rsakey_fn), + DEF_OPT_FUNC("cwpkkey" , 0, cwpkkey_fn), + DEF_OPT_FUNC("deskey" , 0, deskey_fn), +#endif +#ifdef READER_NAGRA_MERLIN + DEF_OPT_FUNC_X("mod1" , OFS(mod1), param_fn, SIZEOF(mod1) ^ (OFS(mod1_length) - OFS(mod1)) << 8), + DEF_OPT_FUNC_X("idird" , OFS(idird), param_fn, SIZEOF(idird) ^ (OFS(idird_length) - OFS(idird)) << 8), + DEF_OPT_FUNC_X("cmd0eprov" , OFS(cmd0eprov), param_fn, SIZEOF(cmd0eprov) ^ (OFS(cmd0eprov_length) - OFS(cmd0eprov)) << 8), + DEF_OPT_FUNC_X("mod2" , OFS(mod2), param_fn, SIZEOF(mod2) ^ (OFS(mod2_length) - OFS(mod2)) << 8), + DEF_OPT_FUNC_X("key3588" , OFS(key3588), param_fn, SIZEOF(key3588) ^ (OFS(key3588_length) - OFS(key3588)) << 8), + DEF_OPT_FUNC_X("key3460" , OFS(key3460), param_fn, SIZEOF(key3460) ^ (OFS(key3460_length) - OFS(key3460)) << 8), + DEF_OPT_FUNC_X("key3310" , OFS(key3310), param_fn, SIZEOF(key3310) ^ (OFS(key3310_length) - OFS(key3310)) << 8), + DEF_OPT_FUNC_X("data50" , OFS(data50), param_fn, SIZEOF(data50) ^ (OFS(data50_length) - OFS(data50)) << 8), + DEF_OPT_FUNC_X("mod50" , OFS(mod50), param_fn, SIZEOF(mod50) ^ (OFS(mod50_length) - OFS(mod50)) << 8), + DEF_OPT_FUNC_X("nuid" , OFS(nuid), param_fn, SIZEOF(nuid) ^ (OFS(nuid_length) - OFS(nuid)) << 8), + DEF_OPT_FUNC_X("forcepair" , OFS(forcepair), param_fn, SIZEOF(forcepair) ^ (OFS(forcepair_length) - OFS(forcepair)) << 8), + DEF_OPT_FUNC_X("otpcsc" , OFS(otpcsc), param_fn, SIZEOF(otpcsc) ^ (OFS(otpcsc_length) - OFS(otpcsc)) << 8), + DEF_OPT_FUNC_X("otacsc" , OFS(otacsc), param_fn, SIZEOF(otacsc) ^ (OFS(otacsc_length) - OFS(otacsc)) << 8), + DEF_OPT_FUNC_X("cwpkcaid" , OFS(cwpkcaid), param_fn, SIZEOF(cwpkcaid) ^ (OFS(cwpkcaid_length) - OFS(cwpkcaid)) << 8), + DEF_OPT_FUNC_X("cwekey0" , OFS(cwekey[0]), param_fn, SIZEOF(cwekey[0]) ^ (OFS(cwekey_length[0]) - OFS(cwekey[0])) << 8), + DEF_OPT_FUNC_X("cwekey1" , OFS(cwekey[1]), param_fn, SIZEOF(cwekey[1]) ^ (OFS(cwekey_length[1]) - OFS(cwekey[1])) << 8), + DEF_OPT_FUNC_X("cwekey2" , OFS(cwekey[2]), param_fn, SIZEOF(cwekey[2]) ^ (OFS(cwekey_length[2]) - OFS(cwekey[2])) << 8), + DEF_OPT_FUNC_X("cwekey3" , OFS(cwekey[3]), param_fn, SIZEOF(cwekey[3]) ^ (OFS(cwekey_length[3]) - OFS(cwekey[3])) << 8), + DEF_OPT_FUNC_X("cwekey4" , OFS(cwekey[4]), param_fn, SIZEOF(cwekey[4]) ^ (OFS(cwekey_length[4]) - OFS(cwekey[4])) << 8), + DEF_OPT_FUNC_X("cwekey5" , OFS(cwekey[5]), param_fn, SIZEOF(cwekey[5]) ^ (OFS(cwekey_length[5]) - OFS(cwekey[5])) << 8), + DEF_OPT_FUNC_X("cwekey6" , OFS(cwekey[6]), param_fn, SIZEOF(cwekey[6]) ^ (OFS(cwekey_length[6]) - OFS(cwekey[6])) << 8), + DEF_OPT_FUNC_X("cwekey7" , OFS(cwekey[7]), param_fn, SIZEOF(cwekey[7]) ^ (OFS(cwekey_length[7]) - OFS(cwekey[7])) << 8), + DEF_OPT_FUNC_X("cwekey8" , OFS(cwekey[8]), param_fn, SIZEOF(cwekey[8]) ^ (OFS(cwekey_length[8]) - OFS(cwekey[8])) << 8), + DEF_OPT_FUNC_X("cwekey9" , OFS(cwekey[9]), param_fn, SIZEOF(cwekey[9]) ^ (OFS(cwekey_length[9]) - OFS(cwekey[9])) << 8), + DEF_OPT_FUNC_X("cwekey10" , OFS(cwekey[10]), param_fn, SIZEOF(cwekey[10]) ^ (OFS(cwekey_length[10]) - OFS(cwekey[10])) << 8), + DEF_OPT_FUNC_X("cwekey11" , OFS(cwekey[11]), param_fn, SIZEOF(cwekey[11]) ^ (OFS(cwekey_length[11]) - OFS(cwekey[11])) << 8), + DEF_OPT_FUNC_X("cwekey12" , OFS(cwekey[12]), param_fn, SIZEOF(cwekey[12]) ^ (OFS(cwekey_length[12]) - OFS(cwekey[12])) << 8), + DEF_OPT_FUNC_X("cwekey13" , OFS(cwekey[13]), param_fn, SIZEOF(cwekey[13]) ^ (OFS(cwekey_length[13]) - OFS(cwekey[13])) << 8), + DEF_OPT_FUNC_X("cwekey14" , OFS(cwekey[14]), param_fn, SIZEOF(cwekey[14]) ^ (OFS(cwekey_length[14]) - OFS(cwekey[14])) << 8), + DEF_OPT_FUNC_X("cwekey15" , OFS(cwekey[15]), param_fn, SIZEOF(cwekey[15]) ^ (OFS(cwekey_length[15]) - OFS(cwekey[15])) << 8), + DEF_OPT_FUNC_X("cwekey16" , OFS(cwekey[16]), param_fn, SIZEOF(cwekey[16]) ^ (OFS(cwekey_length[16]) - OFS(cwekey[16])) << 8), + DEF_OPT_INT8("forcecwswap" , OFS(forcecwswap), 0), + DEF_OPT_INT8("evensa" , OFS(evensa), 0), + DEF_OPT_INT8("forceemmg" , OFS(forceemmg), 0), + DEF_OPT_INT8("cwpkota" , OFS(cwpkota), 0), + DEF_OPT_INT8("headermode" , OFS(headermode), 1), + DEF_OPT_INT8("cak7_mode" , OFS(cak7_mode), 0), +#endif +#if defined(READER_NAGRA) + DEF_OPT_FUNC_X("cak63nuid" , OFS(cak63nuid), param_fn, SIZEOF(cak63nuid) ^ (OFS(cak63nuid_length) - OFS(cak63nuid)) << 8), + DEF_OPT_FUNC_X("cak63cwekey" , OFS(cak63cwekey), param_fn, SIZEOF(cak63cwekey) ^ (OFS(cak63cwekey_length) - OFS(cak63cwekey)) << 8), +#endif +#if defined(READER_VIDEOGUARD) + DEF_OPT_INT8("ndsversion" , OFS(ndsversion), 0), + DEF_OPT_INT32("card_startdate_basemonth" , OFS(card_startdate_basemonth), 1), + DEF_OPT_INT32("card_startdate_baseyear" , OFS(card_startdate_baseyear), 1997), + DEF_OPT_INT32("card_expiredate_basemonth" , OFS(card_expiredate_basemonth), 1), + DEF_OPT_INT32("card_expiredate_baseyear" , OFS(card_expiredate_baseyear), 1997), + DEF_OPT_FUNC_X("ins7e" , OFS(ins7E), ins7E_fn, SIZEOF(ins7E)), + DEF_OPT_FUNC_X("ins42" , OFS(ins42), ins42_fn, SIZEOF(ins42)), + DEF_OPT_FUNC_X("ins7e11" , OFS(ins7E11), ins7E_fn, SIZEOF(ins7E11)), + DEF_OPT_FUNC_X("ins2e06" , OFS(ins2e06), ins7E_fn, SIZEOF(ins2e06)), + DEF_OPT_FUNC("k1_generic" , OFS(k1_generic), des_and_3des_key_fn), + DEF_OPT_FUNC("k1_unique" , OFS(k1_unique), des_and_3des_key_fn), + DEF_OPT_INT8("fix07" , OFS(fix_07), 1), + DEF_OPT_INT8("fix9993" , OFS(fix_9993), 0), + DEF_OPT_INT8("readtiers" , OFS(readtiers), 1), +#endif +#ifdef READER_IRDETO + DEF_OPT_INT8("force_irdeto" , OFS(force_irdeto), 0), +#endif +#ifdef READER_CRYPTOWORKS + DEF_OPT_INT8("needsglobalfirst" , OFS(needsglobalfirst), 0), +#endif + DEF_OPT_UINT32("ecmnotfoundlimit" , OFS(ecmnotfoundlimit), 0), + DEF_OPT_FUNC("ecmwhitelist" , 0, ecmwhitelist_fn), + DEF_OPT_FUNC("ecmheaderwhitelist" , 0, ecmheaderwhitelist_fn), +#ifdef WITH_CARDREADER + DEF_OPT_FUNC("detect" , 0, detect_fn), +#ifdef READER_NAGRA + DEF_OPT_INT8("nagra_read" , OFS(nagra_read), 0), + DEF_OPT_INT8("detect_seca_nagra_tunneled_card", OFS(detect_seca_nagra_tunneled_card), 1), +#endif + DEF_OPT_INT32("mhz" , OFS(mhz), 357), + DEF_OPT_INT32("cardmhz" , OFS(cardmhz), 357), +#endif +#ifdef WITH_AZBOX + DEF_OPT_INT32("mode" , OFS(azbox_mode), -1), +#endif + DEF_OPT_FUNC_X("ident" , OFS(ftab), ftab_fn, FTAB_READER | FTAB_PROVID), + DEF_OPT_FUNC_X("chid" , OFS(fchid), ftab_fn, FTAB_READER | FTAB_CHID), + DEF_OPT_FUNC("class" , OFS(cltab), class_fn), +#ifdef READER_VIACCESS + DEF_OPT_FUNC("aeskeys" , 0, aeskeys_fn), +#endif + DEF_OPT_FUNC("group" , OFS(grp), group_fn), + DEF_OPT_FUNC("emmcache" , 0, emmcache_fn), + DEF_OPT_FUNC_X("blockemm-unknown" , OFS(blockemm), flags_fn, EMM_UNKNOWN), + DEF_OPT_FUNC_X("blockemm-u" , OFS(blockemm), flags_fn, EMM_UNIQUE), + DEF_OPT_FUNC_X("blockemm-s" , OFS(blockemm), flags_fn, EMM_SHARED), + DEF_OPT_FUNC_X("blockemm-g" , OFS(blockemm), flags_fn, EMM_GLOBAL), + DEF_OPT_FUNC_X("saveemm-unknown" , OFS(saveemm), flags_fn, EMM_UNKNOWN), + DEF_OPT_FUNC_X("saveemm-u" , OFS(saveemm), flags_fn, EMM_UNIQUE), + DEF_OPT_FUNC_X("saveemm-s" , OFS(saveemm), flags_fn, EMM_SHARED), + DEF_OPT_FUNC_X("saveemm-g" , OFS(saveemm), flags_fn, EMM_GLOBAL), + DEF_OPT_FUNC("blockemm-bylen" , 0, blockemm_bylen_fn), +#ifdef WITH_LB + DEF_OPT_INT32("lb_weight" , OFS(lb_weight), 100), + DEF_OPT_INT8("lb_force_fallback" , OFS(lb_force_fallback), 0), +#endif + DEF_OPT_FUNC("savenano" , OFS(s_nano), nano_fn), + DEF_OPT_FUNC("blocknano" , OFS(b_nano), nano_fn), + DEF_OPT_INT8("dropbadcws" , OFS(dropbadcws), 0), + DEF_OPT_INT8("disablecrccws" , OFS(disablecrccws), 0), +#ifdef WITH_CARDREADER + DEF_OPT_INT32("use_gpio" , OFS(use_gpio), 0), +#endif +#ifdef MODULE_PANDORA + DEF_OPT_UINT8("pand_send_ecm" , OFS(pand_send_ecm), 0), +#endif +#ifdef MODULE_CCCAM + DEF_OPT_SSTR("cccversion" , OFS(cc_version), "", SIZEOF(cc_version)), + DEF_OPT_INT8("cccmaxhops" , OFS(cc_maxhops), DEFAULT_CC_MAXHOPS), + DEF_OPT_INT8("cccmindown" , OFS(cc_mindown), 0), + DEF_OPT_INT8("cccwantemu" , OFS(cc_want_emu), 0), + DEF_OPT_INT8("ccckeepalive" , OFS(cc_keepalive), DEFAULT_CC_KEEPALIVE), + DEF_OPT_INT8("cccreshare" , OFS(cc_reshare), DEFAULT_CC_RESHARE), + DEF_OPT_INT32("cccreconnect" , OFS(cc_reconnect), DEFAULT_CC_RECONNECT), + DEF_OPT_INT8("ccchop" , OFS(cc_hop), 0), +#endif +#ifdef MODULE_GHTTP + DEF_OPT_UINT8("use_ssl" , OFS(ghttp_use_ssl), 0), +#endif +#if defined(READER_DRE) || defined(READER_DRECAS) + DEF_OPT_HEX("force_ua" , OFS(force_ua), 4), + DEF_OPT_STR("exec_cmd_file" , OFS(userscript), NULL), +#endif +#ifdef READER_DRECAS + DEF_OPT_STR("stmkeys" , OFS(stmkeys), NULL), +#endif +#ifdef WITH_EMU + DEF_OPT_FUNC_X("emu_auproviders" , OFS(emu_auproviders), ftab_fn, FTAB_READER | FTAB_EMUAU), + DEF_OPT_INT8("emu_datecodedenabled" , OFS(emu_datecodedenabled), 0), +#endif + DEF_OPT_INT8("resetalways" , OFS(resetalways), 0), +#ifdef WITH_CARDREADER + DEF_OPT_INT8("deprecated" , OFS(deprecated), 0), +#endif + DEF_OPT_INT8("audisabled" , OFS(audisabled), 0), + DEF_OPT_FUNC("auprovid" , 0, auprovid_fn), + DEF_OPT_FUNC("ratelimitecm" , 0, ratelimitecm_fn), + DEF_OPT_FUNC("ecmunique" , 0, ecmunique_fn), + DEF_OPT_FUNC("ratelimittime" , 0, ratelimittime_fn), + DEF_OPT_FUNC("srvidholdtime" , 0, srvidholdtime_fn), + DEF_OPT_FUNC("cooldown" , 0, cooldown_fn), + DEF_OPT_INT32("maxparallel" , OFS(maxparallel), 0), + DEF_OPT_FUNC("parallelfactor" , 0, parallelfactor_fn), + DEF_OPT_INT32("paralleltimeout" , OFS(paralleltimeout), 1000), + DEF_OPT_FUNC("cooldowndelay" , 0, cooldowndelay_fn), + DEF_OPT_FUNC("cooldowntime" , 0, cooldowntime_fn), +#ifdef READER_VIACCESS + DEF_OPT_UINT8("read_old_classes" , OFS(read_old_classes), 1), +#endif + DEF_LAST_OPT +}; + +static inline bool in_list(const char *token, const char *list[]) +{ + int i; + for(i = 0; list[i]; i++) + { + if(streq(token, list[i])) + { return true; } + } + return false; +} + +static bool reader_check_setting(const struct config_list *UNUSED(clist), void *config_data, const char *setting) +{ + struct s_reader *reader = config_data; + // These are written only when the reader is physical reader + static const char *hw_only_settings[] = + { + "readnano", "resetcycle", "smargopatch", "autospeed", "sc8in1_dtrrts_patch", "boxid","fix07", + "fix9993", "rsakey", "deskey", "card_startdate_basemonth", "card_startdate_baseyear", "card_expiredate_basemonth", "card_expiredate_baseyear", "ins7e", "ins42", "ins7e11", "ins2e06", "k1_generic", "k1_unique", "force_irdeto", "boxkey", + "atr", "detect", "nagra_read", "mhz", "cardmhz", "readtiers", "read_old_classes", "use_gpio", "needsglobalfirst", +#ifdef READER_NAGRA_MERLIN + "mod1", "idird", "cmd0eprov", "mod2", "key3588", "key3460", "key3310", "data50", "mod50", "nuid", "forcepair", "otpcsc", "otacsc", "cwpkcaid", "headermode", "cwekey0", "cwekey1", "cwekey2", "cwekey3", "cwekey4", "cwekey5", "cwekey6", "cwekey7", "cwekey8", "cwekey9", "cwekey10", "cwekey11", "cwekey12", "cwekey13", "cwekey14", "cwekey15", "cwekey16", +#endif +#if defined(READER_NAGRA) + "cak63nuid", "cak63cwekey", +#endif +#if defined(READER_DRE) || defined(READER_DRECAS) + "exec_cmd_file", +#endif +#ifdef READER_CONAX + "cwpkkey", +#endif +#if defined(READER_TONGFANG) + "tongfang3_deskey", "tongfang3_calibsn", "stbid", "tongfang_boxid", +#endif +#ifdef WITH_AZBOX + "mode", +#endif + "resetalways", "deprecated", "ndsversion", + 0 + }; + // These are written only when the reader is network reader + static const char *network_only_settings[] = + { + "user", "inactivitytimeout", "reconnecttimeout", + 0 + }; + if(is_network_reader(reader)) + { + if(in_list(setting, hw_only_settings)) + { return false; } + } + else + { + if(in_list(setting, network_only_settings)) + { return false; } + } + + // These are not written in the config file + static const char *deprecated_settings[] = + { + "cooldowndelay", "cooldowntime", + 0 + }; + if(in_list(setting, deprecated_settings)) + { return false; } + + // Special settings for NEWCAMD + static const char *newcamd_settings[] = + { + "disableserverfilter", "connectoninit", + 0 + }; + if(reader->typ != R_NEWCAMD && in_list(setting, newcamd_settings)) + { return false; } +#ifdef MODULE_CCCAM + // These are written only when the reader is CCCAM + static const char *cccam_settings[] = + { + "cccversion", "cccmaxhops", "cccmindown", "cccwantemu", "ccckeepalive", + "cccreconnect", + 0 + }; + // Special settings for CCCAM + if(reader->typ != R_CCCAM) + { + if(in_list(setting, cccam_settings)) + { return false; } + } + else if(streq(setting, "ccchop")) + { + return false; + } +#endif + +#ifdef MODULE_PANDORA + // Special settings for PANDORA + if(reader->typ != R_PANDORA && streq(setting, "pand_send_ecm")) + { return false; } +#endif + +#ifdef MODULE_GBOX + // These are written only when the reader is GBOX + static const char *gbox_settings[] = + { + "gbox_max_distance", "gbox_max_ecm_send", "gbox_reshare", "cccam_reshare", "force_remm","ccc_gbx_reshare_ident","send_offline_cmd", + 0 + }; + if(reader->typ != R_GBOX) + { + if(in_list(setting, gbox_settings)) + { return false; } + } +#endif + + return true; // Write the setting +} + +void chk_reader(char *token, char *value, struct s_reader *rdr) +{ + if(config_list_parse(reader_opts, token, value, rdr)) + { return; } + else if(token[0] != '#') + { fprintf(stderr, "Warning: keyword '%s' in reader section not recognized\n", token); } +} + +void reader_set_defaults(struct s_reader *rdr) +{ + config_list_set_defaults(reader_opts, rdr); +} + +int32_t init_readerdb(void) +{ + configured_readers = ll_create("configured_readers"); + + FILE *fp = open_config_file(cs_srvr); + if(!fp) + { return 1; } + + int32_t tag = 0; + char *value, *token; + + if(!cs_malloc(&token, MAXLINESIZE)) + { return 1; } + + struct s_reader *rdr; + if(!cs_malloc(&rdr, sizeof(struct s_reader))) + { + NULLFREE(token); + return 1; + } + + ll_append(configured_readers, rdr); + while(fgets(token, MAXLINESIZE, fp)) + { + int32_t l; + if((l = cs_strlen(trim(token))) < 3) + { continue; } + if((token[0] == '[') && (token[l - 1] == ']')) + { + token[l - 1] = 0; + tag = (!strcmp("reader", strtolower(token + 1))); + if(rdr->label[0] && rdr->typ) + { + struct s_reader *newreader; + if(cs_malloc(&newreader, sizeof(struct s_reader))) + { + ll_append(configured_readers, newreader); + rdr = newreader; + } + } + reader_set_defaults(rdr); + continue; + } + + if(!tag) + { continue; } + if(!(value = strchr(token, '='))) + { continue; } + *value++ = '\0'; + chk_reader(trim(strtolower(token)), trim(value), rdr); + } + NULLFREE(token); + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) // build active readers list + { + reader_fixups_fn(rdr); + module_reader_set(rdr); + } + fclose(fp); + return (0); +} + +void free_reader(struct s_reader *rdr) +{ + NULLFREE(rdr->emmfile); + if(rdr->parallel_slots || rdr->parallel_slots_prov) + { cs_lock_destroy(__func__, &rdr->parallel_lock); } + NULLFREE(rdr->parallel_slots); + NULLFREE(rdr->parallel_slots_prov); + ll_destroy_data(&rdr->blocked_services); + + ecm_whitelist_clear(&rdr->ecm_whitelist); + ecm_hdr_whitelist_clear(&rdr->ecm_hdr_whitelist); + + ftab_clear(&rdr->fallback_percaid); + ftab_clear(&rdr->localcards); + ftab_clear(&rdr->fchid); + ftab_clear(&rdr->ftab); + ftab_clear(&rdr->disablecrccws_only_for); +#ifdef MODULE_GBOX + ftab_clear(&rdr->ccc_gbx_reshare_ident); +#endif + + NULLFREE(rdr->cltab.aclass); + NULLFREE(rdr->cltab.bclass); + + caidtab_clear(&rdr->ctab); +#ifdef CS_CACHEEX + cecspvaluetab_clear(&rdr->cacheex.filter_caidtab); +#ifdef CS_CACHEEX_AIO + caidtab_clear(&rdr->cacheex.localgenerated_only_caidtab); + caidtab_clear(&rdr->cacheex.localgenerated_only_in_caidtab); + ftab_clear(&rdr->cacheex.lg_only_tab); + ftab_clear(&rdr->cacheex.lg_only_in_tab); + caidvaluetab_clear(&rdr->cacheex.cacheex_nopushafter_tab); +#endif +#endif + lb_destroy_stats(rdr); + + cs_clear_entitlement(rdr); + ll_destroy(&rdr->ll_entitlements); + + if(rdr->csystem && rdr->csystem->card_done) + rdr->csystem->card_done(rdr); + NULLFREE(rdr->csystem_data); + + ll_destroy_data(&rdr->blockemmbylen); + + ll_destroy_data(&rdr->emmstat); +#ifdef READER_VIACCESS + aes_clear_entries(&rdr->aes_list); +#endif + config_list_gc_values(reader_opts, rdr); + add_garbage(rdr); +} + +int32_t free_readerdb(void) +{ + int count = 0; + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + free_reader(rdr); + count++; + } + cs_log("readerdb %d readers freed", count); + ll_destroy(&configured_readers); + return count; +} + +int32_t write_server(void) +{ + FILE *f = create_config_file(cs_srvr); + if(!f) + { return 1; } + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->label[0]) + { + fprintf(f, "[reader]\n"); + config_list_apply_fixups(reader_opts, rdr); + config_list_save_ex(f, reader_opts, rdr, cfg.http_full_cfg, reader_check_setting); + fprintf(f, "\n"); + } + } + return flush_config_file(f, cs_srvr); +} + +void reload_readerdb(void) +{ + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + // disable the current reader + rdr->enable = 0; + restart_cardreader(rdr,1); + } + free_readerdb(); // release the old readerdb + init_readerdb(); // reload the new readerdb + init_cardreader(); // start the readers +} diff --git a/oscam-config.c b/oscam-config.c new file mode 100644 index 0000000..8fd7c32 --- /dev/null +++ b/oscam-config.c @@ -0,0 +1,1494 @@ +#define MODULE_LOG_PREFIX "config" + +//FIXME Not checked on threadsafety yet; after checking please remove this line + +#include "globals.h" + +#include "oscam-conf.h" +#include "oscam-conf-chk.h" +#include "oscam-config.h" +#include "oscam-files.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-string.h" +#include "oscam-time.h" + +#define cs_srid "oscam.srvid" +#define cs_ratelimit "oscam.ratelimit" +#define cs_trid "oscam.tiers" +#define cs_sidt "oscam.services" +#define cs_whitelist "oscam.whitelist" +#define cs_provid "oscam.provid" +#define cs_fakecws "oscam.fakecws" +#define cs_twin "oscam.twin" + +uint32_t cfg_sidtab_generation = 1; +uint32_t caid; + +extern char cs_confdir[]; + +char *get_config_filename(char *dest, size_t destlen, const char *filename) +{ + // cs_confdir is always terminated with / + snprintf(dest, destlen, "%s%s", cs_confdir, filename); + return dest; +} + +int32_t write_services(void) +{ + int32_t i; + struct s_sidtab *sidtab = cfg.sidtab; + char *ptr; + FILE *f = create_config_file(cs_sidt); + if(!f) + { return 1; } + + while(sidtab != NULL) + { + ptr = sidtab->label; + while(*ptr) + { + if(*ptr == ' ') { *ptr = '_'; } + ptr++; + } + fprintf(f, "[%s]\n", sidtab->label); +#ifdef CS_CACHEEX_AIO + fprintf_conf(f, "disablecrccws_only_for_exception", "%u", sidtab->disablecrccws_only_for_exception); // it should not have \n at the end + fputc((int)'\n', f); + fprintf_conf(f, "no_wait_time", "%u", sidtab->no_wait_time); // it should not have \n at the end + fputc((int)'\n', f); + fprintf_conf(f, "lg_only_exception", "%u", sidtab->lg_only_exception); // it should not have \n at the end + fputc((int)'\n', f); +#endif + fprintf_conf(f, "caid", "%s", ""); // it should not have \n at the end + for(i = 0; i < sidtab->num_caid; i++) + { + if(i == 0) { fprintf(f, "%04X", sidtab->caid[i]); } + else { fprintf(f, ",%04X", sidtab->caid[i]); } + } + fputc((int)'\n', f); + fprintf_conf(f, "provid", "%s", ""); // it should not have \n at the end + for(i = 0; i < sidtab->num_provid; i++) + { + if(i == 0) { fprintf(f, "%06X", sidtab->provid[i]); } + else { fprintf(f, ",%06X", sidtab->provid[i]); } + } + fputc((int)'\n', f); + fprintf_conf(f, "srvid", "%s", ""); // it should not have \n at the end + for(i = 0; i < sidtab->num_srvid; i++) + { + if(i == 0) { fprintf(f, "%04X", sidtab->srvid[i]); } + else { fprintf(f, ",%04X", sidtab->srvid[i]); } + } + fprintf(f, "\n\n"); + sidtab = sidtab->next; + } + + return flush_config_file(f, cs_sidt); +} + +void free_sidtab(struct s_sidtab *ptr) +{ + if(!ptr) { return; } + add_garbage(ptr->caid); //no need to check on NULL first, freeing NULL doesnt do anything + add_garbage(ptr->provid); + add_garbage(ptr->srvid); + add_garbage(ptr); +} + +static void chk_entry4sidtab(char *value, struct s_sidtab *sidtab, int32_t what) +{ + int32_t i, b; + char *ptr, *saveptr1 = NULL; + uint16_t *slist = (uint16_t *) 0; + uint32_t *llist = (uint32_t *) 0; +#ifdef CS_CACHEEX_AIO + uint8_t disablecrccws_only_for_exception = 0; + uint8_t no_wait_time = 0; + uint8_t lg_only_exception = 0; +#endif + char buf[cs_strlen(value) + 1]; + cs_strncpy(buf, value, sizeof(buf)); + +#ifdef CS_CACHEEX_AIO + if(what == 5) // lg_only_exception + { + sidtab->lg_only_exception = a2i(buf, sizeof(lg_only_exception)); + return; + } + + if(what == 4) // no_wait_time + { + sidtab->no_wait_time = a2i(buf, sizeof(no_wait_time)); + return; + } + + if(what == 3) // disablecrccws_only_for_exception + { + sidtab->disablecrccws_only_for_exception = a2i(buf, sizeof(disablecrccws_only_for_exception)); + return; + } +#endif + + b = (what == 1) ? sizeof(uint32_t) : sizeof(uint16_t); + + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1)) + { + caid = a2i(ptr, b); + if(!errno) { i++; } + } + //if (!i) return(0); + if(b == sizeof(uint16_t)) + { + if(!cs_malloc(&slist, i * sizeof(uint16_t))) { return; } + } + else + { + if(!cs_malloc(&llist, i * sizeof(uint32_t))) { return; } + } + cs_strncpy(value, buf, sizeof(buf)); + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1)) + { + caid = a2i(ptr, b); + if(errno) { continue; } + if(b == sizeof(uint16_t)) + { slist[i++] = (uint16_t) caid; } + else + { llist[i++] = caid; } + } + switch(what) + { + case 0: + add_garbage(sidtab->caid); + sidtab->caid = slist; + sidtab->num_caid = i; + break; + case 1: + add_garbage(sidtab->provid); + sidtab->provid = llist; + sidtab->num_provid = i; + break; + case 2: + add_garbage(sidtab->srvid); + sidtab->srvid = slist; + sidtab->num_srvid = i; + break; + } +} + +void chk_sidtab(char *token, char *value, struct s_sidtab *sidtab) +{ + if(!strcmp(token, "caid")) + { + chk_entry4sidtab(value, sidtab, 0); + return; + } + if(!strcmp(token, "provid")) + { + chk_entry4sidtab(value, sidtab, 1); + return; + } + if(!strcmp(token, "ident")) + { + chk_entry4sidtab(value, sidtab, 1); + return; + } + if(!strcmp(token, "srvid")) + { + chk_entry4sidtab(value, sidtab, 2); + return; + } +#ifdef CS_CACHEEX_AIO + if(!strcmp(token, "disablecrccws_only_for_exception")) + { + chk_entry4sidtab(value, sidtab, 3); + return; + } + if(!strcmp(token, "no_wait_time")) + { + chk_entry4sidtab(value, sidtab, 4); + return; + } + if(!strcmp(token, "lg_only_exception")) + { + chk_entry4sidtab(value, sidtab, 5); + return; + } +#endif + if(token[0] != '#') + { fprintf(stderr, "Warning: keyword '%s' in sidtab section not recognized\n", token); } +} + +void init_free_sidtab(void) +{ + struct s_sidtab *nxt, *ptr = cfg.sidtab; + while(ptr) + { + nxt = ptr->next; + free_sidtab(ptr); + ptr = nxt; + } + cfg.sidtab = NULL; + ++cfg_sidtab_generation; +} + +//#define DEBUG_SIDTAB 1 +#ifdef DEBUG_SIDTAB +static void show_sidtab(struct s_sidtab *sidtab) +{ + for(; sidtab; sidtab = sidtab->next) + { + int32_t i; + char buf[1024]; + char *saveptr = buf; + cs_log("label=%s", sidtab->label); +#ifdef CS_CACHEEX_AIO + cs_log("disablecrccws_only_for_exception=%u", sidtab->disablecrccws_only_for_exception); + cs_log("no_wait_time=%u", sidtab->no_wait_time); + cs_log("lg_only_exception=%u", sidtab->lg_only_exception); +#endif + snprintf(buf, sizeof(buf), "caid(%d)=", sidtab->num_caid); + for(i = 0; i < sidtab->num_caid; i++) + { snprintf(buf + cs_strlen(buf), 1024 - (buf - saveptr), "%04X ", sidtab->caid[i]); } + cs_log("%s", buf); + snprintf(buf, sizeof(buf), "provider(%d)=", sidtab->num_provid); + for(i = 0; i < sidtab->num_provid; i++) + { snprintf(buf + cs_strlen(buf), 1024 - (buf - saveptr), "%08X ", sidtab->provid[i]); } + cs_log("%s", buf); + snprintf(buf, sizeof(buf), "services(%d)=", sidtab->num_srvid); + for(i = 0; i < sidtab->num_srvid; i++) + { snprintf(buf + cs_strlen(buf), 1024 - (buf - saveptr), "%04X ", sidtab->srvid[i]); } + cs_log("%s", buf); + } +} +#else +static void show_sidtab(struct s_sidtab *UNUSED(sidtab)) { } +#endif + +int32_t init_sidtab(void) +{ + FILE *fp = open_config_file(cs_sidt); + if(!fp) + { return 1; } + + int32_t nr, nro, nrr; + char *value, *token; + if(!cs_malloc(&token, MAXLINESIZE)) + { return 1; } + struct s_sidtab *ptr; + struct s_sidtab *sidtab = (struct s_sidtab *)0; + + for(nro = 0, ptr = cfg.sidtab; ptr; nro++) + { + struct s_sidtab *ptr_next; + ptr_next = ptr->next; + free_sidtab(ptr); + ptr = ptr_next; + } + nr = 0; + nrr = 0; + while(fgets(token, MAXLINESIZE, fp)) + { + int32_t l; + if((l = cs_strlen(trim(token))) < 3) { continue; } + if((token[0] == '[') && (token[l - 1] == ']')) + { + token[l - 1] = 0; + if(nr > MAX_SIDBITS) + { + fprintf(stderr, "Warning: Service No.%d - '%s' ignored. Max allowed Services %d\n", nr, strtolower(token + 1), MAX_SIDBITS); + nr++; + nrr++; + } + else + { + if(!cs_malloc(&ptr, sizeof(struct s_sidtab))) + { + NULLFREE(token); + return (1); + } + if(sidtab) + { sidtab->next = ptr; } + else + { cfg.sidtab = ptr; } + sidtab = ptr; + nr++; + cs_strncpy(sidtab->label, strtolower(token + 1), sizeof(sidtab->label)); + continue; + } + } + if(!sidtab) { continue; } + if(!(value = strchr(token, '='))) { continue; } + *value++ = '\0'; + chk_sidtab(trim(strtolower(token)), trim(strtolower(value)), sidtab); + } + NULLFREE(token); + fclose(fp); + + show_sidtab(cfg.sidtab); + ++cfg_sidtab_generation; + cs_log("services reloaded: %d services freed, %d services loaded, rejected %d", nro, nr, nrr); + return (0); +} + +int32_t init_provid(void) +{ + FILE *fp = open_config_file(cs_provid); + if(!fp) + { return 0; } + + int32_t nr; + char *payload, *saveptr1 = NULL, *token; + if(!cs_malloc(&token, MAXLINESIZE)) + { return 0; } + struct s_provid *provid_ptr = NULL; + struct s_provid *new_cfg_provid = NULL, *last_provid; + + nr = 0; + while(fgets(token, MAXLINESIZE, fp)) + { + int32_t i; + struct s_provid *new_provid = NULL; + char *tmp, *ptr1; + + tmp = trim(token); + + if(tmp[0] == '#') { continue; } + if(cs_strlen(tmp) < 11) { continue; } + if(!(payload = strchr(token, '|'))) { continue; } + + *payload++ = '\0'; + + if(!cs_malloc(&new_provid, sizeof(struct s_provid))) + { + NULLFREE(token); + fclose(fp); + return (1); + } + + new_provid->nprovid = 0; + for(i = 0, ptr1 = strtok_r(token, ":@", &saveptr1); ptr1; ptr1 = strtok_r(NULL, ":@", &saveptr1), i++) + { + if(i==0) + { + new_provid->caid = a2i(ptr1, 3); + continue; + } + + new_provid->nprovid++; + } + + if(!cs_malloc(&new_provid->provid, sizeof(uint32_t) * new_provid->nprovid)) + { + NULLFREE(new_provid); + NULLFREE(token); + fclose(fp); + return (1); + } + + ptr1 = token + cs_strlen(token) + 1; + for(i = 0; i < new_provid->nprovid ; i++) + { + new_provid->provid[i] = a2i(ptr1, 3); + + ptr1 = ptr1 + cs_strlen(ptr1) + 1; + } + + for(i = 0, ptr1 = strtok_r(payload, "|", &saveptr1); ptr1; ptr1 = strtok_r(NULL, "|", &saveptr1), i++) + { + switch(i) + { + case 0: + cs_strncpy(new_provid->prov, trim(ptr1), sizeof(new_provid->prov)); + break; + case 1: + cs_strncpy(new_provid->sat, trim(ptr1), sizeof(new_provid->sat)); + break; + case 2: + cs_strncpy(new_provid->lang, trim(ptr1), sizeof(new_provid->lang)); + break; + } + } + + if(cs_strlen(new_provid->prov) == 0) + { + NULLFREE(new_provid->provid); + NULLFREE(new_provid); + continue; + } + + nr++; + + if(provid_ptr) + { + provid_ptr->next = new_provid; + } + else + { + new_cfg_provid = new_provid; + } + provid_ptr = new_provid; + } + + NULLFREE(token); + fclose(fp); + + if(nr > 0) + { cs_log("%d provid's loaded", nr); } + + if(new_cfg_provid == NULL) + { + if(!cs_malloc(&new_cfg_provid, sizeof(struct s_provid))) + { + return (1); + } + } + + cs_writelock(__func__, &config_lock); + + // this allows reloading of provids, so cleanup of old data is needed: + last_provid = cfg.provid; // old data + cfg.provid = new_cfg_provid; // assign after loading, so everything is in memory + + cs_writeunlock(__func__, &config_lock); + + struct s_client *cl; + for(cl = first_client->next; cl ; cl = cl->next) + { cl->last_providptr = NULL; } + + struct s_provid *ptr, *nptr; + + if(last_provid) + { + ptr = last_provid; + while(ptr) // cleanup old data: + { + add_garbage(ptr->provid); + nptr = ptr->next; + add_garbage(ptr); + ptr = nptr; + } + } + + return (0); +} + +int32_t init_srvid(void) +{ + int8_t new_syntax = 1; + FILE *fp = open_config_file("oscam.srvid2"); + if(!fp) + { + fp = open_config_file(cs_srid); + if(fp) + { + new_syntax = 0; + } + } + + if(!fp) + { + fp = create_config_file("oscam.srvid2"); + if(fp) + { + flush_config_file(fp, "oscam.srvid2"); + } + + return 0; + } + + int32_t nr = 0, i, j; + char *payload, *saveptr1 = NULL, *saveptr2 = NULL, *token; + const char *tmp; + if(!cs_malloc(&token, MAXLINESIZE)) + { return 0; } + struct s_srvid *srvid = NULL, *new_cfg_srvid[16], *last_srvid[16]; + // A cache for strings within srvids. A checksum is calculated which is the start point in the array (some kind of primitive hash algo). + // From this point, a sequential search is done. This greatly reduces the amount of string comparisons. + const char **stringcache[1024]; + int32_t allocated[1024] = { 0 }; + int32_t used[1024] = { 0 }; + struct timeb ts, te; + cs_ftime(&ts); + + memset(last_srvid, 0, sizeof(last_srvid)); + memset(new_cfg_srvid, 0, sizeof(new_cfg_srvid)); + + while(fgets(token, MAXLINESIZE, fp)) + { + int32_t len = 0, len2, srvidtmp; + uint32_t k; + uint32_t pos; + char *srvidasc, *prov; + tmp = trim(token); + + if(tmp[0] == '#') { continue; } + if(cs_strlen(tmp) < 6) { continue; } + if(!(srvidasc = strchr(token, ':'))) { continue; } + if(!(payload = strchr(token, '|'))) { continue; } + *payload++ = '\0'; + + if(!cs_malloc(&srvid, sizeof(struct s_srvid))) + { + NULLFREE(token); + fclose(fp); + return (1); + } + + char tmptxt[128]; + + int32_t offset[4] = { -1, -1, -1, -1 }; + char *ptr1 = NULL, *ptr2 = NULL; + const char *searchptr[4] = { NULL, NULL, NULL, NULL }; + const char **ptrs[4] = { &srvid->prov, &srvid->name, &srvid->type, &srvid->desc }; + uint32_t max_payload_length = MAXLINESIZE - (payload - token); + + if(new_syntax) + { + ptrs[0] = &srvid->name; + ptrs[1] = &srvid->type; + ptrs[2] = &srvid->desc; + ptrs[3] = &srvid->prov; + } + + // allow empty strings as "||" + if(payload[0] == '|' && (cs_strlen(payload) + 2 < max_payload_length)) + { + memmove(payload+1, payload, cs_strlen(payload)+1); + payload[0] = ' '; + } + + for(k = 1; ((k < max_payload_length) && (payload[k] != '\0')); k++) + { + if(payload[k - 1] == '|' && payload[k] == '|') + { + if(cs_strlen(payload + k) + 2 < max_payload_length-k) + { + memmove(payload + k + 1, payload + k, cs_strlen(payload + k) + 1); + payload[k] = ' '; + } + else + { + break; + } + } + } + + for(i = 0, ptr1 = strtok_r(payload, "|", &saveptr1); ptr1 && (i < 4) ; ptr1 = strtok_r(NULL, "|", &saveptr1), ++i) + { + // check if string is in cache + len2 = cs_strlen(ptr1); + pos = 0; + for(j = 0; j < len2; ++j) { pos += (uint8_t)ptr1[j]; } + pos = pos % 1024; + for(j = 0; j < used[pos]; ++j) + { + if(!strcmp(stringcache[pos][j], ptr1)) + { + searchptr[i] = stringcache[pos][j]; + break; + } + } + if(searchptr[i]) { continue; } + + offset[i] = len; + cs_strncpy(tmptxt + len, trim(ptr1), sizeof(tmptxt) - len); + len += cs_strlen(ptr1) + 1; + } + + char *tmpptr = NULL; + if(len > 0 && !cs_malloc(&tmpptr, len)) + { continue; } + + srvid->data = tmpptr; + if(len > 0) { memcpy(tmpptr, tmptxt, len); } + + for(i = 0; i < 4; i++) + { + if(searchptr[i]) + { + *ptrs[i] = searchptr[i]; + continue; + } + if(offset[i] > -1) + { + *ptrs[i] = tmpptr + offset[i]; + // store string in stringcache + if (*ptrs[i]) + { + tmp = *ptrs[i]; + len2 = cs_strlen(tmp); + } + else + { + cs_log("FIXME! len2!"); + len2 = 0; + } + + pos = 0; + for(j = 0; j < len2; ++j) { pos += (uint8_t)tmp[j]; } + if (pos > 0) + { + pos = pos % 1024; + } + if(used[pos] >= allocated[pos]) + { + if(allocated[pos] == 0) + { + if(!cs_malloc(&stringcache[pos], 16 * sizeof(char *))) + { break; } + } + else + { + if(!cs_realloc(&stringcache[pos], (allocated[pos] + 16) * sizeof(char *))) + { break; } + } + allocated[pos] += 16; + } + if (tmp[0]) + { + stringcache[pos][used[pos]] = tmp; + used[pos] += 1; + } + } + } + + *srvidasc++ = '\0'; + if(new_syntax) + { srvidtmp = dyn_word_atob(token) & 0xFFFF; } + else + { srvidtmp = dyn_word_atob(srvidasc) & 0xFFFF; } + + if(srvidtmp < 0) + { + NULLFREE(tmpptr); + NULLFREE(srvid); + continue; + } + else + { + srvid->srvid = srvidtmp; + } + + srvid->ncaid = 0; + for(i = 0, ptr1 = strtok_r(new_syntax ? srvidasc : token, ",", &saveptr1); (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1), i++) + { + srvid->ncaid++; + } + + if(!cs_malloc(&srvid->caid, sizeof(struct s_srvid_caid) * srvid->ncaid)) + { + NULLFREE(tmpptr); + NULLFREE(srvid); + return 0; + } + + ptr1 = new_syntax ? srvidasc : token; + for(i = 0; i < srvid->ncaid; i++) + { + prov = strchr(ptr1,'@'); + + srvid->caid[i].nprovid = 0; + + if(prov) + { + if(prov[1] != '\0') + { + for(j = 0, ptr2 = strtok_r(prov+1, "@", &saveptr2); (ptr2); ptr2 = strtok_r(NULL, "@", &saveptr2), j++) + { + srvid->caid[i].nprovid++; + } + + if(!cs_malloc(&srvid->caid[i].provid, sizeof(uint32_t) * srvid->caid[i].nprovid)) + { + for(j = 0; j < i; j++) + { NULLFREE(srvid->caid[j].provid); } + NULLFREE(srvid->caid); + NULLFREE(tmpptr); + NULLFREE(srvid); + return 0; + } + + ptr2 = prov + 1; + for(j = 0; j < srvid->caid[i].nprovid; j++) + { + srvid->caid[i].provid[j] = dyn_word_atob(ptr2) & 0xFFFFFF; + ptr2 = ptr2 + cs_strlen(ptr2) + 1; + } + } + else + { + ptr2 = prov + 2; + } + + prov[0] = '\0'; + } + + srvid->caid[i].caid = dyn_word_atob(ptr1) & 0xFFFF; + if(prov) + { ptr1 = ptr2; } + else + { ptr1 = ptr1 + cs_strlen(ptr1) + 1; } + } + + nr++; + + if(new_cfg_srvid[srvid->srvid >> 12]) + { last_srvid[srvid->srvid >> 12]->next = srvid; } + else + { new_cfg_srvid[srvid->srvid >> 12] = srvid; } + + last_srvid[srvid->srvid >> 12] = srvid; + } + for(i = 0; i < 1024; ++i) + { + if(allocated[i] > 0) { NULLFREE(stringcache[i]); } + } + NULLFREE(token); + + cs_ftime(&te); + int64_t load_time = comp_timeb(&te, &ts); + + fclose(fp); + if(nr > 0) + { + cs_log("%d service-id's loaded in %"PRId64" ms", nr, load_time); + if(nr > 2000) + { + cs_log("WARNING: You risk high CPU load and high ECM times with more than 2000 service-id's!"); + cs_log("HINT: --> use optimized lists from %s/Srvid", WIKI_URL); + } + } + + cs_writelock(__func__, &config_lock); + // this allows reloading of srvids, so cleanup of old data is needed: + memcpy(last_srvid, cfg.srvid, sizeof(last_srvid)); //old data + memcpy(cfg.srvid, new_cfg_srvid, sizeof(last_srvid)); //assign after loading, so everything is in memory + + cs_writeunlock(__func__, &config_lock); + + struct s_client *cl; + for(cl = first_client->next; cl ; cl = cl->next) + { cl->last_srvidptr = NULL; } + + struct s_srvid *ptr, *nptr; + + for(i = 0; i < 16; i++) + { + ptr = last_srvid[i]; + while(ptr) // cleanup old data: + { + for(j = 0; j < ptr->ncaid; j++) + { add_garbage(ptr->caid[j].provid); } + add_garbage(ptr->caid); + add_garbage(ptr->data); + nptr = ptr->next; + add_garbage(ptr); + ptr = nptr; + } + } + + return (0); +} + +int32_t init_fakecws(void) +{ + int32_t nr = 0, i, j, idx; + uint32_t alloccount[0x100], count[0x100], tmp, max_compares = 0, average_compares = 0; + char *token, cw_string[64]; + uint8_t cw[16], wrong_checksum, c, have_fakecw = 0; + FILE *fp; + + memset(alloccount, 0, sizeof(count)); + memset(count, 0, sizeof(alloccount)); + + cs_writelock(__func__, &config_lock); + for(i = 0; i < 0x100; i++) + { + cfg.fakecws[i].count = 0; + NULLFREE(cfg.fakecws[i].data); + } + cs_writeunlock(__func__, &config_lock); + + fp = open_config_file(cs_fakecws); + if(!fp) + { return 0; } + + if(!cs_malloc(&token, MAXLINESIZE)) + { return 0; } + + while(fgets(token, MAXLINESIZE, fp)) + { + if(sscanf(token, " %62s ", cw_string) == 1) + { + if(cs_strlen(cw_string) == 32) + { + if(cs_atob(cw, cw_string, 16) == 16) + { + wrong_checksum = 0; + + if(wrong_checksum) + { + cs_log("skipping fake cw %s because of wrong checksum!", cw_string); + } + else + { + idx = ((cw[0] & 0xF) << 4) | (cw[8] & 0xF); + alloccount[idx]++; + have_fakecw = 1; + } + } + else + { + cs_log("skipping fake cw %s because it contains invalid characters!", cw_string); + } + } + else + { + cs_log("skipping fake cw %s because of wrong length (%u != 32)!", cw_string, (uint32_t)cs_strlen(cw_string)); + } + } + } + + if(!have_fakecw) + { + NULLFREE(token); + fclose(fp); + return 0; + } + + for(i = 0; i < 0x100; i++) + { + if(alloccount[i] && !cs_malloc(&cfg.fakecws[i].data, sizeof(struct s_cw)*alloccount[i])) + { + alloccount[i] = 0; + } + } + + fseek(fp, 0, SEEK_SET); + + while(fgets(token, MAXLINESIZE, fp)) + { + if(sscanf(token, " %62s ", cw_string) == 1) + { + if(cs_strlen(cw_string) == 32) + { + if(cs_atob(cw, cw_string, 16) == 16) + { + wrong_checksum = 0; + + if(!wrong_checksum) + { + idx = ((cw[0] & 0xF) << 4) | (cw[8] & 0xF); + + if(count[idx] < alloccount[idx]) + { + memcpy(cfg.fakecws[idx].data[count[idx]].cw, cw, 16); + count[idx]++; + nr++; + } + } + } + } + } + } + + NULLFREE(token); + fclose(fp); + + if(nr > 0) + { cs_log("%d fakecws's loaded", nr); } + + + cs_writelock(__func__, &config_lock); + for(i = 0; i < 0x100; i++) + { + cfg.fakecws[i].count = count[i]; + } + cs_writeunlock(__func__, &config_lock); + + + for(i = 0; i < 0x100; i++) + { + if(count[i] > max_compares) + { max_compares = count[i]; } + } + + for(i = 0; i < (0x100 - 1); i++) + { + for(j = i + 1; j < 0x100; j++) + { + if(count[j] < count[i]) + { + tmp = count[i]; + count[i] = count[j]; + count[j] = tmp; + } + } + } + average_compares = ((count[0x100 / 2] + count[0x100 / 2 - 1]) / 2); + + + cs_log("max %d fakecw compares required, on average: %d compares", max_compares, average_compares); + + return 0; +} + +static struct s_rlimit *ratelimit_read_int(void) +{ + FILE *fp = open_config_file(cs_ratelimit); + if(!fp) + { return NULL; } + char token[1024], str1[1024]; + int32_t i, ret, count = 0; + struct s_rlimit *new_rlimit = NULL, *entry, *last = NULL; + + while(fgets(token, sizeof(token), fp)) + { + if(cs_strlen(token) <= 1) { continue; } + if(token[0] == '#' || token[0] == '/') { continue; } + if(cs_strlen(token) > 1024) { continue; } + + for(i = 0; i < (int)cs_strlen(token); i++) + { + if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':') + { + memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1); + token[i + 1] = '0'; + } + if(token[i] == '#' || token[i] == '/') + { + token[i] = '\0'; + break; + } + } + + caid = 0; + uint32_t provid = 0, srvid = 0, chid = 0, ratelimitecm = 0, ratelimittime = 0, srvidholdtime = 0; + memset(str1, 0, sizeof(str1)); + + ret = sscanf(token, "%4x:%6x:%4x:%4x:%d:%d:%d:%1023s", &caid, &provid, &srvid, &chid, &ratelimitecm, &ratelimittime, &srvidholdtime, str1); + if(ret < 1) { + continue; + } + + if (!cs_strncat(str1, ",", sizeof(str1))) { + return new_rlimit; + } + + if(!cs_malloc(&entry, sizeof(struct s_rlimit))) + { + fclose(fp); + return new_rlimit; + } + + count++; + if (ratelimittime < 60) ratelimittime *= 1000; + if (srvidholdtime < 60) srvidholdtime *= 1000; + entry->rl.caid = caid; + entry->rl.provid = provid; + entry->rl.srvid = srvid; + entry->rl.chid = chid; + entry->rl.ratelimitecm = ratelimitecm; + entry->rl.ratelimittime = ratelimittime; + entry->rl.srvidholdtime = srvidholdtime; + + cs_log_dbg(D_TRACE, "ratelimit: %04X@%06X:%04X:%04X:%d:%d:%d", entry->rl.caid, entry->rl.provid, entry->rl.srvid, entry->rl.chid, + entry->rl.ratelimitecm, entry->rl.ratelimittime, entry->rl.srvidholdtime); + + if(!new_rlimit) + { + new_rlimit = entry; + last = new_rlimit; + } + else + { + last->next = entry; + last = entry; + } + } + + if(count) + { cs_log("%d entries read from %s", count, cs_ratelimit); } + + fclose(fp); + + return new_rlimit; +} + +void ratelimit_read(void) +{ + + struct s_rlimit *entry, *old_list; + + old_list = cfg.ratelimit_list; + cfg.ratelimit_list = ratelimit_read_int(); + + while(old_list) + { + entry = old_list->next; + NULLFREE(old_list); + old_list = entry; + } +} + +struct ecmrl get_ratelimit(ECM_REQUEST *er) +{ + + struct ecmrl tmp; + memset(&tmp, 0, sizeof(tmp)); + if(!cfg.ratelimit_list) { return tmp; } + struct s_rlimit *entry = cfg.ratelimit_list; + while(entry) + { + if(entry->rl.caid == er->caid && entry->rl.provid == er->prid && entry->rl.srvid == er->srvid && (!entry->rl.chid || entry->rl.chid == er->chid)) + { + break; + } + entry = entry->next; + } + + if(entry) { tmp = entry->rl; } + + return (tmp); +} + +int32_t init_tierid(void) +{ + FILE *fp = open_config_file(cs_trid); + if(!fp) + { return 0; } + + int32_t nr; + char *payload, *saveptr1 = NULL, *token; + if(!cs_malloc(&token, MAXLINESIZE)) + { return 0; } + static struct s_tierid *tierid = NULL, *new_cfg_tierid = NULL; + + nr = 0; + while(fgets(token, MAXLINESIZE, fp)) + { + void *ptr; + char *tmp, *tieridasc; + tmp = trim(token); + + if(tmp[0] == '#') { continue; } + if(cs_strlen(tmp) < 6) { continue; } + if(!(payload = strchr(token, '|'))) { continue; } + if(!(tieridasc = strchr(token, ':'))) { continue; } + *payload++ = '\0'; + + if(!cs_malloc(&ptr, sizeof(struct s_tierid))) + { + NULLFREE(token); + fclose(fp); + return (1); + } + if(tierid) + { tierid->next = ptr; } + else + { new_cfg_tierid = ptr; } + + tierid = ptr; + + int32_t i; + char *ptr1 = strtok_r(payload, "|", &saveptr1); + if(ptr1) + { cs_strncpy(tierid->name, trim(ptr1), sizeof(tierid->name)); } + + *tieridasc++ = '\0'; + tierid->tierid = dyn_word_atob(tieridasc); + //printf("tierid %s - %d\n",tieridasc,tierid->tierid ); + + tierid->ncaid = 0; + for(i = 0, ptr1 = strtok_r(token, ",", &saveptr1); (ptr1) && (i < 10) ; ptr1 = strtok_r(NULL, ",", &saveptr1), i++) + { + tierid->caid[i] = dyn_word_atob(ptr1); + tierid->ncaid = i + 1; + //cs_log("ld caid: %04X tierid: %04X name: %s",tierid->caid[i],tierid->tierid,tierid->name); + } + nr++; + } + NULLFREE(token); + fclose(fp); + if(nr > 0) + { cs_log("%d tier-id's loaded", nr); } + cs_writelock(__func__, &config_lock); + // reload function: + tierid = cfg.tierid; + cfg.tierid = new_cfg_tierid; + struct s_tierid *ptr; + while(tierid) + { + ptr = tierid->next; + NULLFREE(tierid); + tierid = ptr; + } + cs_writeunlock(__func__, &config_lock); + + return (0); +} + +int32_t match_whitelist(ECM_REQUEST *er, struct s_global_whitelist *entry) +{ + return ((!entry->caid || entry->caid == er->caid) + && (!entry->provid || entry->provid == er->prid) + && (!entry->srvid || entry->srvid == er->srvid) + && (!entry->chid || entry->chid == er->chid) + && (!entry->pid || entry->pid == er->pid) + && (!entry->ecmlen || entry->ecmlen == er->ecmlen)); +} + +int32_t chk_global_whitelist(ECM_REQUEST *er, uint32_t *line) +{ + *line = -1; + if(!cfg.global_whitelist) + { return 1; } + + struct s_global_whitelist *entry; + + // check mapping: + if(cfg.global_whitelist_use_m) + { + entry = cfg.global_whitelist; + while(entry) + { + if(entry->type == 'm') + { + if(match_whitelist(er, entry)) + { + cs_log_dbg(D_TRACE, "whitelist: mapped %04X@%06X to %04X@%06X", er->caid, er->prid, entry->mapcaid, entry->mapprovid); + er->caid = entry->mapcaid; + er->prid = entry->mapprovid; + break; + } + } + entry = entry->next; + } + } + + if(cfg.global_whitelist_use_l) // Check caid/prov/srvid etc matching, except ecm-len: + { + entry = cfg.global_whitelist; + int8_t caidprov_matches = 0; + while(entry) + { + if(entry->type == 'l') + { + if(match_whitelist(er, entry)) + { + *line = entry->line; + return 1; + } + if((!entry->caid || entry->caid == er->caid) + && (!entry->provid || entry->provid == er->prid) + && (!entry->srvid || entry->srvid == er->srvid) + && (!entry->chid || entry->chid == er->chid) + && (!entry->pid || entry->pid == er->pid)) + { + caidprov_matches = 1; + *line = entry->line; + } + } + entry = entry->next; + } + if(caidprov_matches) // ...but not ecm-len! + { return 0; } + } + + entry = cfg.global_whitelist; + while(entry) + { + if(match_whitelist(er, entry)) + { + *line = entry->line; + if(entry->type == 'w') + { return 1; } + else if(entry->type == 'i') + { return 0; } + } + entry = entry->next; + } + return 0; +} + +//Format: +//Whitelist-Entry: +//w:caid:prov:srvid:pid:chid:ecmlen +//Ignore-Entry: +//i:caid:prov:srvid:pid:chid:ecmlen +//ECM len check - Entry: +//l:caid:prov:srvid:pid:chid:ecmlen + +//Mapping: +//m:caid:prov:srvid:pid:chid:ecmlen caidto:provto + +static struct s_global_whitelist *global_whitelist_read_int(void) +{ + FILE *fp = open_config_file(cs_whitelist); + if(!fp) + { return NULL; } + + char token[1024], str1[1024]; + uint8_t type; + int32_t i, ret, count = 0; + struct s_global_whitelist *new_whitelist = NULL, *entry, *last = NULL; + uint32_t line = 0; + + cfg.global_whitelist_use_l = 0; + cfg.global_whitelist_use_m = 0; + + while(fgets(token, sizeof(token), fp)) + { + line++; + if(cs_strlen(token) <= 1) { continue; } + if(token[0] == '#' || token[0] == '/') { continue; } + if(cs_strlen(token) > 1024) { continue; } + + for(i = 0; i < (int)cs_strlen(token); i++) + { + if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':') + { + memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1); + token[i + 1] = '0'; + } + if(token[i] == '#' || token[i] == '/') + { + token[i] = '\0'; + break; + } + } + + type = 'w'; + caid = 0; + uint32_t provid = 0, srvid = 0, pid = 0, chid = 0, ecmlen = 0, mapcaid = 0, mapprovid = 0; + memset(str1, 0, sizeof(str1)); + + ret = sscanf(token, "%c:%4x:%6x:%4x:%4x:%4x:%1023s", &type, &caid, &provid, &srvid, &pid, &chid, str1); + + type = tolower(type); + + //w=whitelist + //i=ignore + //l=len-check + //m=map caid/prov + if(ret < 1 || (type != 'w' && type != 'i' && type != 'l' && type != 'm')) + { continue; } + + if(type == 'm') + { + char *p = strstr(token + 4, " "); + if(!p || sscanf(p + 1, "%4x:%6x", &mapcaid, &mapprovid) < 2) + { + cs_log_dbg(D_TRACE, "whitelist: wrong mapping: %s", token); + continue; + } + str1[0] = 0; + cfg.global_whitelist_use_m = 1; + } + + if (!cs_strncat(str1, ",", sizeof(str1))) { + return new_whitelist; + } + + char *p = str1, *p2 = str1; + + while(*p) + { + if(*p == ',') + { + *p = 0; + ecmlen = 0; + sscanf(p2, "%4x", &ecmlen); + + if(!cs_malloc(&entry, sizeof(struct s_global_whitelist))) + { + fclose(fp); + return new_whitelist; + } + + count++; + entry->line = line; + entry->type = type; + entry->caid = caid; + entry->provid = provid; + entry->srvid = srvid; + entry->pid = pid; + entry->chid = chid; + entry->ecmlen = ecmlen; + entry->mapcaid = mapcaid; + entry->mapprovid = mapprovid; + if(entry->type == 'l') + { cfg.global_whitelist_use_l = 1; } + + if(type == 'm') + cs_log_dbg(D_TRACE, "whitelist: %c: %04X@%06X:%04X:%04X:%04X:%02X map to %04X@%06X", + entry->type, entry->caid, entry->provid, entry->srvid, entry->pid, entry->chid, entry->ecmlen, entry->mapcaid, entry->mapprovid); + else + cs_log_dbg(D_TRACE, "whitelist: %c: %04X@%06X:%04X:%04X:%04X:%02X", + entry->type, entry->caid, entry->provid, entry->srvid, entry->pid, entry->chid, entry->ecmlen); + + if(!new_whitelist) + { + new_whitelist = entry; + last = new_whitelist; + } + else + { + last->next = entry; + last = entry; + } + + p2 = p + 1; + } + p++; + } + } + + if(count) + { cs_log("%d entries read from %s", count, cs_whitelist); } + + fclose(fp); + + return new_whitelist; +} + +void global_whitelist_read(void) +{ + struct s_global_whitelist *entry, *old_list; + + old_list = cfg.global_whitelist; + cfg.global_whitelist = global_whitelist_read_int(); + + while(old_list) + { + entry = old_list->next; + NULLFREE(old_list); + old_list = entry; + } +} + +#ifdef MODULE_SERIAL +static struct s_twin *twin_read_int(void) +{ + FILE *fp = open_config_file(cs_twin); + if(!fp) + { return NULL; } + char token[1024], str1[1024]; + int32_t i, ret, count = 0; + struct s_twin *new_twin = NULL, *entry, *last = NULL; + + while(fgets(token, sizeof(token), fp)) + { + if(cs_strlen(token) <= 1) { continue; } + if(token[0] == '#' || token[0] == '/') { continue; } + if(cs_strlen(token) > 1024) { continue; } + + for(i = 0; i < (int)cs_strlen(token); i++) + { + if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':') + { + memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1); + token[i + 1] = '0'; + } + if(token[i] == '#' || token[i] == '/' || token[i] == '"') + { + token[i] = '\0'; + break; + } + } + + caid = 0; + uint32_t provid = 0, srvid = 0, deg = 0, freq = 0; + //char hdeg[4], hfreq[4], hsrvid[4]; + memset(str1, 0, sizeof(str1)); + + ret = sscanf(token, "%4x:%6x:%d:%d:%d", &caid, &provid, °, &freq, &srvid); + if(ret < 1) { continue; } + + //snprintf(hdeg, 4, "%x", deg); + //sscanf(hdeg, "%4x", °); + //snprintf(hfreq, 4, "%x", freq); + //sscanf(hfreq, "%4x", &freq); + //snprintf(hsrvid, 4, "%x", srvid); + //sscanf(hsrvid, "%4x", &srvid); + if (!cs_strncat(str1, ",", sizeof(str1))) { + return new_twin; + } + + if(!cs_malloc(&entry, sizeof(struct s_twin))) + { + fclose(fp); + return new_twin; + } + + count++; + entry->tw.caid = caid; + entry->tw.provid = provid; + entry->tw.srvid = srvid; + entry->tw.deg = deg; + entry->tw.freq = freq; + + cs_debug_mask(D_TRACE, "channel: %04X:%06X:%d:%d:%d", entry->tw.caid, + entry->tw.provid, entry->tw.deg, entry->tw.freq, entry->tw.srvid); + + if(!new_twin) + { + new_twin = entry; + last = new_twin; + } + else + { + last->next = entry; + last = entry; + } + } + + if(count) + { cs_log("%d entries read from %s", count, cs_twin); } + + fclose(fp); + + return new_twin; +} + +void twin_read(void) +{ + + struct s_twin *entry, *old_list; + + old_list = cfg.twin_list; + cfg.twin_list = twin_read_int(); + + while(old_list) + { + entry = old_list->next; + free(old_list); + old_list = entry; + } +} + +struct ecmtw get_twin(ECM_REQUEST *er) +{ + struct ecmtw tmp; + memset(&tmp, 0, sizeof(tmp)); + if(!cfg.twin_list) + { + cs_log("twin_list not found!"); + return tmp; + } + struct s_twin *entry = cfg.twin_list; + while(entry) + { + if(entry->tw.caid == er->caid && entry->tw.provid == er->prid && entry->tw.srvid == er->srvid) + { + break; + } + entry = entry->next; + } + + if(entry) { tmp = entry->tw; } + + return (tmp); +} +#endif diff --git a/oscam-config.c.orig b/oscam-config.c.orig new file mode 100644 index 0000000..8fd7c32 --- /dev/null +++ b/oscam-config.c.orig @@ -0,0 +1,1494 @@ +#define MODULE_LOG_PREFIX "config" + +//FIXME Not checked on threadsafety yet; after checking please remove this line + +#include "globals.h" + +#include "oscam-conf.h" +#include "oscam-conf-chk.h" +#include "oscam-config.h" +#include "oscam-files.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-string.h" +#include "oscam-time.h" + +#define cs_srid "oscam.srvid" +#define cs_ratelimit "oscam.ratelimit" +#define cs_trid "oscam.tiers" +#define cs_sidt "oscam.services" +#define cs_whitelist "oscam.whitelist" +#define cs_provid "oscam.provid" +#define cs_fakecws "oscam.fakecws" +#define cs_twin "oscam.twin" + +uint32_t cfg_sidtab_generation = 1; +uint32_t caid; + +extern char cs_confdir[]; + +char *get_config_filename(char *dest, size_t destlen, const char *filename) +{ + // cs_confdir is always terminated with / + snprintf(dest, destlen, "%s%s", cs_confdir, filename); + return dest; +} + +int32_t write_services(void) +{ + int32_t i; + struct s_sidtab *sidtab = cfg.sidtab; + char *ptr; + FILE *f = create_config_file(cs_sidt); + if(!f) + { return 1; } + + while(sidtab != NULL) + { + ptr = sidtab->label; + while(*ptr) + { + if(*ptr == ' ') { *ptr = '_'; } + ptr++; + } + fprintf(f, "[%s]\n", sidtab->label); +#ifdef CS_CACHEEX_AIO + fprintf_conf(f, "disablecrccws_only_for_exception", "%u", sidtab->disablecrccws_only_for_exception); // it should not have \n at the end + fputc((int)'\n', f); + fprintf_conf(f, "no_wait_time", "%u", sidtab->no_wait_time); // it should not have \n at the end + fputc((int)'\n', f); + fprintf_conf(f, "lg_only_exception", "%u", sidtab->lg_only_exception); // it should not have \n at the end + fputc((int)'\n', f); +#endif + fprintf_conf(f, "caid", "%s", ""); // it should not have \n at the end + for(i = 0; i < sidtab->num_caid; i++) + { + if(i == 0) { fprintf(f, "%04X", sidtab->caid[i]); } + else { fprintf(f, ",%04X", sidtab->caid[i]); } + } + fputc((int)'\n', f); + fprintf_conf(f, "provid", "%s", ""); // it should not have \n at the end + for(i = 0; i < sidtab->num_provid; i++) + { + if(i == 0) { fprintf(f, "%06X", sidtab->provid[i]); } + else { fprintf(f, ",%06X", sidtab->provid[i]); } + } + fputc((int)'\n', f); + fprintf_conf(f, "srvid", "%s", ""); // it should not have \n at the end + for(i = 0; i < sidtab->num_srvid; i++) + { + if(i == 0) { fprintf(f, "%04X", sidtab->srvid[i]); } + else { fprintf(f, ",%04X", sidtab->srvid[i]); } + } + fprintf(f, "\n\n"); + sidtab = sidtab->next; + } + + return flush_config_file(f, cs_sidt); +} + +void free_sidtab(struct s_sidtab *ptr) +{ + if(!ptr) { return; } + add_garbage(ptr->caid); //no need to check on NULL first, freeing NULL doesnt do anything + add_garbage(ptr->provid); + add_garbage(ptr->srvid); + add_garbage(ptr); +} + +static void chk_entry4sidtab(char *value, struct s_sidtab *sidtab, int32_t what) +{ + int32_t i, b; + char *ptr, *saveptr1 = NULL; + uint16_t *slist = (uint16_t *) 0; + uint32_t *llist = (uint32_t *) 0; +#ifdef CS_CACHEEX_AIO + uint8_t disablecrccws_only_for_exception = 0; + uint8_t no_wait_time = 0; + uint8_t lg_only_exception = 0; +#endif + char buf[cs_strlen(value) + 1]; + cs_strncpy(buf, value, sizeof(buf)); + +#ifdef CS_CACHEEX_AIO + if(what == 5) // lg_only_exception + { + sidtab->lg_only_exception = a2i(buf, sizeof(lg_only_exception)); + return; + } + + if(what == 4) // no_wait_time + { + sidtab->no_wait_time = a2i(buf, sizeof(no_wait_time)); + return; + } + + if(what == 3) // disablecrccws_only_for_exception + { + sidtab->disablecrccws_only_for_exception = a2i(buf, sizeof(disablecrccws_only_for_exception)); + return; + } +#endif + + b = (what == 1) ? sizeof(uint32_t) : sizeof(uint16_t); + + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1)) + { + caid = a2i(ptr, b); + if(!errno) { i++; } + } + //if (!i) return(0); + if(b == sizeof(uint16_t)) + { + if(!cs_malloc(&slist, i * sizeof(uint16_t))) { return; } + } + else + { + if(!cs_malloc(&llist, i * sizeof(uint32_t))) { return; } + } + cs_strncpy(value, buf, sizeof(buf)); + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1)) + { + caid = a2i(ptr, b); + if(errno) { continue; } + if(b == sizeof(uint16_t)) + { slist[i++] = (uint16_t) caid; } + else + { llist[i++] = caid; } + } + switch(what) + { + case 0: + add_garbage(sidtab->caid); + sidtab->caid = slist; + sidtab->num_caid = i; + break; + case 1: + add_garbage(sidtab->provid); + sidtab->provid = llist; + sidtab->num_provid = i; + break; + case 2: + add_garbage(sidtab->srvid); + sidtab->srvid = slist; + sidtab->num_srvid = i; + break; + } +} + +void chk_sidtab(char *token, char *value, struct s_sidtab *sidtab) +{ + if(!strcmp(token, "caid")) + { + chk_entry4sidtab(value, sidtab, 0); + return; + } + if(!strcmp(token, "provid")) + { + chk_entry4sidtab(value, sidtab, 1); + return; + } + if(!strcmp(token, "ident")) + { + chk_entry4sidtab(value, sidtab, 1); + return; + } + if(!strcmp(token, "srvid")) + { + chk_entry4sidtab(value, sidtab, 2); + return; + } +#ifdef CS_CACHEEX_AIO + if(!strcmp(token, "disablecrccws_only_for_exception")) + { + chk_entry4sidtab(value, sidtab, 3); + return; + } + if(!strcmp(token, "no_wait_time")) + { + chk_entry4sidtab(value, sidtab, 4); + return; + } + if(!strcmp(token, "lg_only_exception")) + { + chk_entry4sidtab(value, sidtab, 5); + return; + } +#endif + if(token[0] != '#') + { fprintf(stderr, "Warning: keyword '%s' in sidtab section not recognized\n", token); } +} + +void init_free_sidtab(void) +{ + struct s_sidtab *nxt, *ptr = cfg.sidtab; + while(ptr) + { + nxt = ptr->next; + free_sidtab(ptr); + ptr = nxt; + } + cfg.sidtab = NULL; + ++cfg_sidtab_generation; +} + +//#define DEBUG_SIDTAB 1 +#ifdef DEBUG_SIDTAB +static void show_sidtab(struct s_sidtab *sidtab) +{ + for(; sidtab; sidtab = sidtab->next) + { + int32_t i; + char buf[1024]; + char *saveptr = buf; + cs_log("label=%s", sidtab->label); +#ifdef CS_CACHEEX_AIO + cs_log("disablecrccws_only_for_exception=%u", sidtab->disablecrccws_only_for_exception); + cs_log("no_wait_time=%u", sidtab->no_wait_time); + cs_log("lg_only_exception=%u", sidtab->lg_only_exception); +#endif + snprintf(buf, sizeof(buf), "caid(%d)=", sidtab->num_caid); + for(i = 0; i < sidtab->num_caid; i++) + { snprintf(buf + cs_strlen(buf), 1024 - (buf - saveptr), "%04X ", sidtab->caid[i]); } + cs_log("%s", buf); + snprintf(buf, sizeof(buf), "provider(%d)=", sidtab->num_provid); + for(i = 0; i < sidtab->num_provid; i++) + { snprintf(buf + cs_strlen(buf), 1024 - (buf - saveptr), "%08X ", sidtab->provid[i]); } + cs_log("%s", buf); + snprintf(buf, sizeof(buf), "services(%d)=", sidtab->num_srvid); + for(i = 0; i < sidtab->num_srvid; i++) + { snprintf(buf + cs_strlen(buf), 1024 - (buf - saveptr), "%04X ", sidtab->srvid[i]); } + cs_log("%s", buf); + } +} +#else +static void show_sidtab(struct s_sidtab *UNUSED(sidtab)) { } +#endif + +int32_t init_sidtab(void) +{ + FILE *fp = open_config_file(cs_sidt); + if(!fp) + { return 1; } + + int32_t nr, nro, nrr; + char *value, *token; + if(!cs_malloc(&token, MAXLINESIZE)) + { return 1; } + struct s_sidtab *ptr; + struct s_sidtab *sidtab = (struct s_sidtab *)0; + + for(nro = 0, ptr = cfg.sidtab; ptr; nro++) + { + struct s_sidtab *ptr_next; + ptr_next = ptr->next; + free_sidtab(ptr); + ptr = ptr_next; + } + nr = 0; + nrr = 0; + while(fgets(token, MAXLINESIZE, fp)) + { + int32_t l; + if((l = cs_strlen(trim(token))) < 3) { continue; } + if((token[0] == '[') && (token[l - 1] == ']')) + { + token[l - 1] = 0; + if(nr > MAX_SIDBITS) + { + fprintf(stderr, "Warning: Service No.%d - '%s' ignored. Max allowed Services %d\n", nr, strtolower(token + 1), MAX_SIDBITS); + nr++; + nrr++; + } + else + { + if(!cs_malloc(&ptr, sizeof(struct s_sidtab))) + { + NULLFREE(token); + return (1); + } + if(sidtab) + { sidtab->next = ptr; } + else + { cfg.sidtab = ptr; } + sidtab = ptr; + nr++; + cs_strncpy(sidtab->label, strtolower(token + 1), sizeof(sidtab->label)); + continue; + } + } + if(!sidtab) { continue; } + if(!(value = strchr(token, '='))) { continue; } + *value++ = '\0'; + chk_sidtab(trim(strtolower(token)), trim(strtolower(value)), sidtab); + } + NULLFREE(token); + fclose(fp); + + show_sidtab(cfg.sidtab); + ++cfg_sidtab_generation; + cs_log("services reloaded: %d services freed, %d services loaded, rejected %d", nro, nr, nrr); + return (0); +} + +int32_t init_provid(void) +{ + FILE *fp = open_config_file(cs_provid); + if(!fp) + { return 0; } + + int32_t nr; + char *payload, *saveptr1 = NULL, *token; + if(!cs_malloc(&token, MAXLINESIZE)) + { return 0; } + struct s_provid *provid_ptr = NULL; + struct s_provid *new_cfg_provid = NULL, *last_provid; + + nr = 0; + while(fgets(token, MAXLINESIZE, fp)) + { + int32_t i; + struct s_provid *new_provid = NULL; + char *tmp, *ptr1; + + tmp = trim(token); + + if(tmp[0] == '#') { continue; } + if(cs_strlen(tmp) < 11) { continue; } + if(!(payload = strchr(token, '|'))) { continue; } + + *payload++ = '\0'; + + if(!cs_malloc(&new_provid, sizeof(struct s_provid))) + { + NULLFREE(token); + fclose(fp); + return (1); + } + + new_provid->nprovid = 0; + for(i = 0, ptr1 = strtok_r(token, ":@", &saveptr1); ptr1; ptr1 = strtok_r(NULL, ":@", &saveptr1), i++) + { + if(i==0) + { + new_provid->caid = a2i(ptr1, 3); + continue; + } + + new_provid->nprovid++; + } + + if(!cs_malloc(&new_provid->provid, sizeof(uint32_t) * new_provid->nprovid)) + { + NULLFREE(new_provid); + NULLFREE(token); + fclose(fp); + return (1); + } + + ptr1 = token + cs_strlen(token) + 1; + for(i = 0; i < new_provid->nprovid ; i++) + { + new_provid->provid[i] = a2i(ptr1, 3); + + ptr1 = ptr1 + cs_strlen(ptr1) + 1; + } + + for(i = 0, ptr1 = strtok_r(payload, "|", &saveptr1); ptr1; ptr1 = strtok_r(NULL, "|", &saveptr1), i++) + { + switch(i) + { + case 0: + cs_strncpy(new_provid->prov, trim(ptr1), sizeof(new_provid->prov)); + break; + case 1: + cs_strncpy(new_provid->sat, trim(ptr1), sizeof(new_provid->sat)); + break; + case 2: + cs_strncpy(new_provid->lang, trim(ptr1), sizeof(new_provid->lang)); + break; + } + } + + if(cs_strlen(new_provid->prov) == 0) + { + NULLFREE(new_provid->provid); + NULLFREE(new_provid); + continue; + } + + nr++; + + if(provid_ptr) + { + provid_ptr->next = new_provid; + } + else + { + new_cfg_provid = new_provid; + } + provid_ptr = new_provid; + } + + NULLFREE(token); + fclose(fp); + + if(nr > 0) + { cs_log("%d provid's loaded", nr); } + + if(new_cfg_provid == NULL) + { + if(!cs_malloc(&new_cfg_provid, sizeof(struct s_provid))) + { + return (1); + } + } + + cs_writelock(__func__, &config_lock); + + // this allows reloading of provids, so cleanup of old data is needed: + last_provid = cfg.provid; // old data + cfg.provid = new_cfg_provid; // assign after loading, so everything is in memory + + cs_writeunlock(__func__, &config_lock); + + struct s_client *cl; + for(cl = first_client->next; cl ; cl = cl->next) + { cl->last_providptr = NULL; } + + struct s_provid *ptr, *nptr; + + if(last_provid) + { + ptr = last_provid; + while(ptr) // cleanup old data: + { + add_garbage(ptr->provid); + nptr = ptr->next; + add_garbage(ptr); + ptr = nptr; + } + } + + return (0); +} + +int32_t init_srvid(void) +{ + int8_t new_syntax = 1; + FILE *fp = open_config_file("oscam.srvid2"); + if(!fp) + { + fp = open_config_file(cs_srid); + if(fp) + { + new_syntax = 0; + } + } + + if(!fp) + { + fp = create_config_file("oscam.srvid2"); + if(fp) + { + flush_config_file(fp, "oscam.srvid2"); + } + + return 0; + } + + int32_t nr = 0, i, j; + char *payload, *saveptr1 = NULL, *saveptr2 = NULL, *token; + const char *tmp; + if(!cs_malloc(&token, MAXLINESIZE)) + { return 0; } + struct s_srvid *srvid = NULL, *new_cfg_srvid[16], *last_srvid[16]; + // A cache for strings within srvids. A checksum is calculated which is the start point in the array (some kind of primitive hash algo). + // From this point, a sequential search is done. This greatly reduces the amount of string comparisons. + const char **stringcache[1024]; + int32_t allocated[1024] = { 0 }; + int32_t used[1024] = { 0 }; + struct timeb ts, te; + cs_ftime(&ts); + + memset(last_srvid, 0, sizeof(last_srvid)); + memset(new_cfg_srvid, 0, sizeof(new_cfg_srvid)); + + while(fgets(token, MAXLINESIZE, fp)) + { + int32_t len = 0, len2, srvidtmp; + uint32_t k; + uint32_t pos; + char *srvidasc, *prov; + tmp = trim(token); + + if(tmp[0] == '#') { continue; } + if(cs_strlen(tmp) < 6) { continue; } + if(!(srvidasc = strchr(token, ':'))) { continue; } + if(!(payload = strchr(token, '|'))) { continue; } + *payload++ = '\0'; + + if(!cs_malloc(&srvid, sizeof(struct s_srvid))) + { + NULLFREE(token); + fclose(fp); + return (1); + } + + char tmptxt[128]; + + int32_t offset[4] = { -1, -1, -1, -1 }; + char *ptr1 = NULL, *ptr2 = NULL; + const char *searchptr[4] = { NULL, NULL, NULL, NULL }; + const char **ptrs[4] = { &srvid->prov, &srvid->name, &srvid->type, &srvid->desc }; + uint32_t max_payload_length = MAXLINESIZE - (payload - token); + + if(new_syntax) + { + ptrs[0] = &srvid->name; + ptrs[1] = &srvid->type; + ptrs[2] = &srvid->desc; + ptrs[3] = &srvid->prov; + } + + // allow empty strings as "||" + if(payload[0] == '|' && (cs_strlen(payload) + 2 < max_payload_length)) + { + memmove(payload+1, payload, cs_strlen(payload)+1); + payload[0] = ' '; + } + + for(k = 1; ((k < max_payload_length) && (payload[k] != '\0')); k++) + { + if(payload[k - 1] == '|' && payload[k] == '|') + { + if(cs_strlen(payload + k) + 2 < max_payload_length-k) + { + memmove(payload + k + 1, payload + k, cs_strlen(payload + k) + 1); + payload[k] = ' '; + } + else + { + break; + } + } + } + + for(i = 0, ptr1 = strtok_r(payload, "|", &saveptr1); ptr1 && (i < 4) ; ptr1 = strtok_r(NULL, "|", &saveptr1), ++i) + { + // check if string is in cache + len2 = cs_strlen(ptr1); + pos = 0; + for(j = 0; j < len2; ++j) { pos += (uint8_t)ptr1[j]; } + pos = pos % 1024; + for(j = 0; j < used[pos]; ++j) + { + if(!strcmp(stringcache[pos][j], ptr1)) + { + searchptr[i] = stringcache[pos][j]; + break; + } + } + if(searchptr[i]) { continue; } + + offset[i] = len; + cs_strncpy(tmptxt + len, trim(ptr1), sizeof(tmptxt) - len); + len += cs_strlen(ptr1) + 1; + } + + char *tmpptr = NULL; + if(len > 0 && !cs_malloc(&tmpptr, len)) + { continue; } + + srvid->data = tmpptr; + if(len > 0) { memcpy(tmpptr, tmptxt, len); } + + for(i = 0; i < 4; i++) + { + if(searchptr[i]) + { + *ptrs[i] = searchptr[i]; + continue; + } + if(offset[i] > -1) + { + *ptrs[i] = tmpptr + offset[i]; + // store string in stringcache + if (*ptrs[i]) + { + tmp = *ptrs[i]; + len2 = cs_strlen(tmp); + } + else + { + cs_log("FIXME! len2!"); + len2 = 0; + } + + pos = 0; + for(j = 0; j < len2; ++j) { pos += (uint8_t)tmp[j]; } + if (pos > 0) + { + pos = pos % 1024; + } + if(used[pos] >= allocated[pos]) + { + if(allocated[pos] == 0) + { + if(!cs_malloc(&stringcache[pos], 16 * sizeof(char *))) + { break; } + } + else + { + if(!cs_realloc(&stringcache[pos], (allocated[pos] + 16) * sizeof(char *))) + { break; } + } + allocated[pos] += 16; + } + if (tmp[0]) + { + stringcache[pos][used[pos]] = tmp; + used[pos] += 1; + } + } + } + + *srvidasc++ = '\0'; + if(new_syntax) + { srvidtmp = dyn_word_atob(token) & 0xFFFF; } + else + { srvidtmp = dyn_word_atob(srvidasc) & 0xFFFF; } + + if(srvidtmp < 0) + { + NULLFREE(tmpptr); + NULLFREE(srvid); + continue; + } + else + { + srvid->srvid = srvidtmp; + } + + srvid->ncaid = 0; + for(i = 0, ptr1 = strtok_r(new_syntax ? srvidasc : token, ",", &saveptr1); (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1), i++) + { + srvid->ncaid++; + } + + if(!cs_malloc(&srvid->caid, sizeof(struct s_srvid_caid) * srvid->ncaid)) + { + NULLFREE(tmpptr); + NULLFREE(srvid); + return 0; + } + + ptr1 = new_syntax ? srvidasc : token; + for(i = 0; i < srvid->ncaid; i++) + { + prov = strchr(ptr1,'@'); + + srvid->caid[i].nprovid = 0; + + if(prov) + { + if(prov[1] != '\0') + { + for(j = 0, ptr2 = strtok_r(prov+1, "@", &saveptr2); (ptr2); ptr2 = strtok_r(NULL, "@", &saveptr2), j++) + { + srvid->caid[i].nprovid++; + } + + if(!cs_malloc(&srvid->caid[i].provid, sizeof(uint32_t) * srvid->caid[i].nprovid)) + { + for(j = 0; j < i; j++) + { NULLFREE(srvid->caid[j].provid); } + NULLFREE(srvid->caid); + NULLFREE(tmpptr); + NULLFREE(srvid); + return 0; + } + + ptr2 = prov + 1; + for(j = 0; j < srvid->caid[i].nprovid; j++) + { + srvid->caid[i].provid[j] = dyn_word_atob(ptr2) & 0xFFFFFF; + ptr2 = ptr2 + cs_strlen(ptr2) + 1; + } + } + else + { + ptr2 = prov + 2; + } + + prov[0] = '\0'; + } + + srvid->caid[i].caid = dyn_word_atob(ptr1) & 0xFFFF; + if(prov) + { ptr1 = ptr2; } + else + { ptr1 = ptr1 + cs_strlen(ptr1) + 1; } + } + + nr++; + + if(new_cfg_srvid[srvid->srvid >> 12]) + { last_srvid[srvid->srvid >> 12]->next = srvid; } + else + { new_cfg_srvid[srvid->srvid >> 12] = srvid; } + + last_srvid[srvid->srvid >> 12] = srvid; + } + for(i = 0; i < 1024; ++i) + { + if(allocated[i] > 0) { NULLFREE(stringcache[i]); } + } + NULLFREE(token); + + cs_ftime(&te); + int64_t load_time = comp_timeb(&te, &ts); + + fclose(fp); + if(nr > 0) + { + cs_log("%d service-id's loaded in %"PRId64" ms", nr, load_time); + if(nr > 2000) + { + cs_log("WARNING: You risk high CPU load and high ECM times with more than 2000 service-id's!"); + cs_log("HINT: --> use optimized lists from %s/Srvid", WIKI_URL); + } + } + + cs_writelock(__func__, &config_lock); + // this allows reloading of srvids, so cleanup of old data is needed: + memcpy(last_srvid, cfg.srvid, sizeof(last_srvid)); //old data + memcpy(cfg.srvid, new_cfg_srvid, sizeof(last_srvid)); //assign after loading, so everything is in memory + + cs_writeunlock(__func__, &config_lock); + + struct s_client *cl; + for(cl = first_client->next; cl ; cl = cl->next) + { cl->last_srvidptr = NULL; } + + struct s_srvid *ptr, *nptr; + + for(i = 0; i < 16; i++) + { + ptr = last_srvid[i]; + while(ptr) // cleanup old data: + { + for(j = 0; j < ptr->ncaid; j++) + { add_garbage(ptr->caid[j].provid); } + add_garbage(ptr->caid); + add_garbage(ptr->data); + nptr = ptr->next; + add_garbage(ptr); + ptr = nptr; + } + } + + return (0); +} + +int32_t init_fakecws(void) +{ + int32_t nr = 0, i, j, idx; + uint32_t alloccount[0x100], count[0x100], tmp, max_compares = 0, average_compares = 0; + char *token, cw_string[64]; + uint8_t cw[16], wrong_checksum, c, have_fakecw = 0; + FILE *fp; + + memset(alloccount, 0, sizeof(count)); + memset(count, 0, sizeof(alloccount)); + + cs_writelock(__func__, &config_lock); + for(i = 0; i < 0x100; i++) + { + cfg.fakecws[i].count = 0; + NULLFREE(cfg.fakecws[i].data); + } + cs_writeunlock(__func__, &config_lock); + + fp = open_config_file(cs_fakecws); + if(!fp) + { return 0; } + + if(!cs_malloc(&token, MAXLINESIZE)) + { return 0; } + + while(fgets(token, MAXLINESIZE, fp)) + { + if(sscanf(token, " %62s ", cw_string) == 1) + { + if(cs_strlen(cw_string) == 32) + { + if(cs_atob(cw, cw_string, 16) == 16) + { + wrong_checksum = 0; + + if(wrong_checksum) + { + cs_log("skipping fake cw %s because of wrong checksum!", cw_string); + } + else + { + idx = ((cw[0] & 0xF) << 4) | (cw[8] & 0xF); + alloccount[idx]++; + have_fakecw = 1; + } + } + else + { + cs_log("skipping fake cw %s because it contains invalid characters!", cw_string); + } + } + else + { + cs_log("skipping fake cw %s because of wrong length (%u != 32)!", cw_string, (uint32_t)cs_strlen(cw_string)); + } + } + } + + if(!have_fakecw) + { + NULLFREE(token); + fclose(fp); + return 0; + } + + for(i = 0; i < 0x100; i++) + { + if(alloccount[i] && !cs_malloc(&cfg.fakecws[i].data, sizeof(struct s_cw)*alloccount[i])) + { + alloccount[i] = 0; + } + } + + fseek(fp, 0, SEEK_SET); + + while(fgets(token, MAXLINESIZE, fp)) + { + if(sscanf(token, " %62s ", cw_string) == 1) + { + if(cs_strlen(cw_string) == 32) + { + if(cs_atob(cw, cw_string, 16) == 16) + { + wrong_checksum = 0; + + if(!wrong_checksum) + { + idx = ((cw[0] & 0xF) << 4) | (cw[8] & 0xF); + + if(count[idx] < alloccount[idx]) + { + memcpy(cfg.fakecws[idx].data[count[idx]].cw, cw, 16); + count[idx]++; + nr++; + } + } + } + } + } + } + + NULLFREE(token); + fclose(fp); + + if(nr > 0) + { cs_log("%d fakecws's loaded", nr); } + + + cs_writelock(__func__, &config_lock); + for(i = 0; i < 0x100; i++) + { + cfg.fakecws[i].count = count[i]; + } + cs_writeunlock(__func__, &config_lock); + + + for(i = 0; i < 0x100; i++) + { + if(count[i] > max_compares) + { max_compares = count[i]; } + } + + for(i = 0; i < (0x100 - 1); i++) + { + for(j = i + 1; j < 0x100; j++) + { + if(count[j] < count[i]) + { + tmp = count[i]; + count[i] = count[j]; + count[j] = tmp; + } + } + } + average_compares = ((count[0x100 / 2] + count[0x100 / 2 - 1]) / 2); + + + cs_log("max %d fakecw compares required, on average: %d compares", max_compares, average_compares); + + return 0; +} + +static struct s_rlimit *ratelimit_read_int(void) +{ + FILE *fp = open_config_file(cs_ratelimit); + if(!fp) + { return NULL; } + char token[1024], str1[1024]; + int32_t i, ret, count = 0; + struct s_rlimit *new_rlimit = NULL, *entry, *last = NULL; + + while(fgets(token, sizeof(token), fp)) + { + if(cs_strlen(token) <= 1) { continue; } + if(token[0] == '#' || token[0] == '/') { continue; } + if(cs_strlen(token) > 1024) { continue; } + + for(i = 0; i < (int)cs_strlen(token); i++) + { + if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':') + { + memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1); + token[i + 1] = '0'; + } + if(token[i] == '#' || token[i] == '/') + { + token[i] = '\0'; + break; + } + } + + caid = 0; + uint32_t provid = 0, srvid = 0, chid = 0, ratelimitecm = 0, ratelimittime = 0, srvidholdtime = 0; + memset(str1, 0, sizeof(str1)); + + ret = sscanf(token, "%4x:%6x:%4x:%4x:%d:%d:%d:%1023s", &caid, &provid, &srvid, &chid, &ratelimitecm, &ratelimittime, &srvidholdtime, str1); + if(ret < 1) { + continue; + } + + if (!cs_strncat(str1, ",", sizeof(str1))) { + return new_rlimit; + } + + if(!cs_malloc(&entry, sizeof(struct s_rlimit))) + { + fclose(fp); + return new_rlimit; + } + + count++; + if (ratelimittime < 60) ratelimittime *= 1000; + if (srvidholdtime < 60) srvidholdtime *= 1000; + entry->rl.caid = caid; + entry->rl.provid = provid; + entry->rl.srvid = srvid; + entry->rl.chid = chid; + entry->rl.ratelimitecm = ratelimitecm; + entry->rl.ratelimittime = ratelimittime; + entry->rl.srvidholdtime = srvidholdtime; + + cs_log_dbg(D_TRACE, "ratelimit: %04X@%06X:%04X:%04X:%d:%d:%d", entry->rl.caid, entry->rl.provid, entry->rl.srvid, entry->rl.chid, + entry->rl.ratelimitecm, entry->rl.ratelimittime, entry->rl.srvidholdtime); + + if(!new_rlimit) + { + new_rlimit = entry; + last = new_rlimit; + } + else + { + last->next = entry; + last = entry; + } + } + + if(count) + { cs_log("%d entries read from %s", count, cs_ratelimit); } + + fclose(fp); + + return new_rlimit; +} + +void ratelimit_read(void) +{ + + struct s_rlimit *entry, *old_list; + + old_list = cfg.ratelimit_list; + cfg.ratelimit_list = ratelimit_read_int(); + + while(old_list) + { + entry = old_list->next; + NULLFREE(old_list); + old_list = entry; + } +} + +struct ecmrl get_ratelimit(ECM_REQUEST *er) +{ + + struct ecmrl tmp; + memset(&tmp, 0, sizeof(tmp)); + if(!cfg.ratelimit_list) { return tmp; } + struct s_rlimit *entry = cfg.ratelimit_list; + while(entry) + { + if(entry->rl.caid == er->caid && entry->rl.provid == er->prid && entry->rl.srvid == er->srvid && (!entry->rl.chid || entry->rl.chid == er->chid)) + { + break; + } + entry = entry->next; + } + + if(entry) { tmp = entry->rl; } + + return (tmp); +} + +int32_t init_tierid(void) +{ + FILE *fp = open_config_file(cs_trid); + if(!fp) + { return 0; } + + int32_t nr; + char *payload, *saveptr1 = NULL, *token; + if(!cs_malloc(&token, MAXLINESIZE)) + { return 0; } + static struct s_tierid *tierid = NULL, *new_cfg_tierid = NULL; + + nr = 0; + while(fgets(token, MAXLINESIZE, fp)) + { + void *ptr; + char *tmp, *tieridasc; + tmp = trim(token); + + if(tmp[0] == '#') { continue; } + if(cs_strlen(tmp) < 6) { continue; } + if(!(payload = strchr(token, '|'))) { continue; } + if(!(tieridasc = strchr(token, ':'))) { continue; } + *payload++ = '\0'; + + if(!cs_malloc(&ptr, sizeof(struct s_tierid))) + { + NULLFREE(token); + fclose(fp); + return (1); + } + if(tierid) + { tierid->next = ptr; } + else + { new_cfg_tierid = ptr; } + + tierid = ptr; + + int32_t i; + char *ptr1 = strtok_r(payload, "|", &saveptr1); + if(ptr1) + { cs_strncpy(tierid->name, trim(ptr1), sizeof(tierid->name)); } + + *tieridasc++ = '\0'; + tierid->tierid = dyn_word_atob(tieridasc); + //printf("tierid %s - %d\n",tieridasc,tierid->tierid ); + + tierid->ncaid = 0; + for(i = 0, ptr1 = strtok_r(token, ",", &saveptr1); (ptr1) && (i < 10) ; ptr1 = strtok_r(NULL, ",", &saveptr1), i++) + { + tierid->caid[i] = dyn_word_atob(ptr1); + tierid->ncaid = i + 1; + //cs_log("ld caid: %04X tierid: %04X name: %s",tierid->caid[i],tierid->tierid,tierid->name); + } + nr++; + } + NULLFREE(token); + fclose(fp); + if(nr > 0) + { cs_log("%d tier-id's loaded", nr); } + cs_writelock(__func__, &config_lock); + // reload function: + tierid = cfg.tierid; + cfg.tierid = new_cfg_tierid; + struct s_tierid *ptr; + while(tierid) + { + ptr = tierid->next; + NULLFREE(tierid); + tierid = ptr; + } + cs_writeunlock(__func__, &config_lock); + + return (0); +} + +int32_t match_whitelist(ECM_REQUEST *er, struct s_global_whitelist *entry) +{ + return ((!entry->caid || entry->caid == er->caid) + && (!entry->provid || entry->provid == er->prid) + && (!entry->srvid || entry->srvid == er->srvid) + && (!entry->chid || entry->chid == er->chid) + && (!entry->pid || entry->pid == er->pid) + && (!entry->ecmlen || entry->ecmlen == er->ecmlen)); +} + +int32_t chk_global_whitelist(ECM_REQUEST *er, uint32_t *line) +{ + *line = -1; + if(!cfg.global_whitelist) + { return 1; } + + struct s_global_whitelist *entry; + + // check mapping: + if(cfg.global_whitelist_use_m) + { + entry = cfg.global_whitelist; + while(entry) + { + if(entry->type == 'm') + { + if(match_whitelist(er, entry)) + { + cs_log_dbg(D_TRACE, "whitelist: mapped %04X@%06X to %04X@%06X", er->caid, er->prid, entry->mapcaid, entry->mapprovid); + er->caid = entry->mapcaid; + er->prid = entry->mapprovid; + break; + } + } + entry = entry->next; + } + } + + if(cfg.global_whitelist_use_l) // Check caid/prov/srvid etc matching, except ecm-len: + { + entry = cfg.global_whitelist; + int8_t caidprov_matches = 0; + while(entry) + { + if(entry->type == 'l') + { + if(match_whitelist(er, entry)) + { + *line = entry->line; + return 1; + } + if((!entry->caid || entry->caid == er->caid) + && (!entry->provid || entry->provid == er->prid) + && (!entry->srvid || entry->srvid == er->srvid) + && (!entry->chid || entry->chid == er->chid) + && (!entry->pid || entry->pid == er->pid)) + { + caidprov_matches = 1; + *line = entry->line; + } + } + entry = entry->next; + } + if(caidprov_matches) // ...but not ecm-len! + { return 0; } + } + + entry = cfg.global_whitelist; + while(entry) + { + if(match_whitelist(er, entry)) + { + *line = entry->line; + if(entry->type == 'w') + { return 1; } + else if(entry->type == 'i') + { return 0; } + } + entry = entry->next; + } + return 0; +} + +//Format: +//Whitelist-Entry: +//w:caid:prov:srvid:pid:chid:ecmlen +//Ignore-Entry: +//i:caid:prov:srvid:pid:chid:ecmlen +//ECM len check - Entry: +//l:caid:prov:srvid:pid:chid:ecmlen + +//Mapping: +//m:caid:prov:srvid:pid:chid:ecmlen caidto:provto + +static struct s_global_whitelist *global_whitelist_read_int(void) +{ + FILE *fp = open_config_file(cs_whitelist); + if(!fp) + { return NULL; } + + char token[1024], str1[1024]; + uint8_t type; + int32_t i, ret, count = 0; + struct s_global_whitelist *new_whitelist = NULL, *entry, *last = NULL; + uint32_t line = 0; + + cfg.global_whitelist_use_l = 0; + cfg.global_whitelist_use_m = 0; + + while(fgets(token, sizeof(token), fp)) + { + line++; + if(cs_strlen(token) <= 1) { continue; } + if(token[0] == '#' || token[0] == '/') { continue; } + if(cs_strlen(token) > 1024) { continue; } + + for(i = 0; i < (int)cs_strlen(token); i++) + { + if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':') + { + memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1); + token[i + 1] = '0'; + } + if(token[i] == '#' || token[i] == '/') + { + token[i] = '\0'; + break; + } + } + + type = 'w'; + caid = 0; + uint32_t provid = 0, srvid = 0, pid = 0, chid = 0, ecmlen = 0, mapcaid = 0, mapprovid = 0; + memset(str1, 0, sizeof(str1)); + + ret = sscanf(token, "%c:%4x:%6x:%4x:%4x:%4x:%1023s", &type, &caid, &provid, &srvid, &pid, &chid, str1); + + type = tolower(type); + + //w=whitelist + //i=ignore + //l=len-check + //m=map caid/prov + if(ret < 1 || (type != 'w' && type != 'i' && type != 'l' && type != 'm')) + { continue; } + + if(type == 'm') + { + char *p = strstr(token + 4, " "); + if(!p || sscanf(p + 1, "%4x:%6x", &mapcaid, &mapprovid) < 2) + { + cs_log_dbg(D_TRACE, "whitelist: wrong mapping: %s", token); + continue; + } + str1[0] = 0; + cfg.global_whitelist_use_m = 1; + } + + if (!cs_strncat(str1, ",", sizeof(str1))) { + return new_whitelist; + } + + char *p = str1, *p2 = str1; + + while(*p) + { + if(*p == ',') + { + *p = 0; + ecmlen = 0; + sscanf(p2, "%4x", &ecmlen); + + if(!cs_malloc(&entry, sizeof(struct s_global_whitelist))) + { + fclose(fp); + return new_whitelist; + } + + count++; + entry->line = line; + entry->type = type; + entry->caid = caid; + entry->provid = provid; + entry->srvid = srvid; + entry->pid = pid; + entry->chid = chid; + entry->ecmlen = ecmlen; + entry->mapcaid = mapcaid; + entry->mapprovid = mapprovid; + if(entry->type == 'l') + { cfg.global_whitelist_use_l = 1; } + + if(type == 'm') + cs_log_dbg(D_TRACE, "whitelist: %c: %04X@%06X:%04X:%04X:%04X:%02X map to %04X@%06X", + entry->type, entry->caid, entry->provid, entry->srvid, entry->pid, entry->chid, entry->ecmlen, entry->mapcaid, entry->mapprovid); + else + cs_log_dbg(D_TRACE, "whitelist: %c: %04X@%06X:%04X:%04X:%04X:%02X", + entry->type, entry->caid, entry->provid, entry->srvid, entry->pid, entry->chid, entry->ecmlen); + + if(!new_whitelist) + { + new_whitelist = entry; + last = new_whitelist; + } + else + { + last->next = entry; + last = entry; + } + + p2 = p + 1; + } + p++; + } + } + + if(count) + { cs_log("%d entries read from %s", count, cs_whitelist); } + + fclose(fp); + + return new_whitelist; +} + +void global_whitelist_read(void) +{ + struct s_global_whitelist *entry, *old_list; + + old_list = cfg.global_whitelist; + cfg.global_whitelist = global_whitelist_read_int(); + + while(old_list) + { + entry = old_list->next; + NULLFREE(old_list); + old_list = entry; + } +} + +#ifdef MODULE_SERIAL +static struct s_twin *twin_read_int(void) +{ + FILE *fp = open_config_file(cs_twin); + if(!fp) + { return NULL; } + char token[1024], str1[1024]; + int32_t i, ret, count = 0; + struct s_twin *new_twin = NULL, *entry, *last = NULL; + + while(fgets(token, sizeof(token), fp)) + { + if(cs_strlen(token) <= 1) { continue; } + if(token[0] == '#' || token[0] == '/') { continue; } + if(cs_strlen(token) > 1024) { continue; } + + for(i = 0; i < (int)cs_strlen(token); i++) + { + if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':') + { + memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1); + token[i + 1] = '0'; + } + if(token[i] == '#' || token[i] == '/' || token[i] == '"') + { + token[i] = '\0'; + break; + } + } + + caid = 0; + uint32_t provid = 0, srvid = 0, deg = 0, freq = 0; + //char hdeg[4], hfreq[4], hsrvid[4]; + memset(str1, 0, sizeof(str1)); + + ret = sscanf(token, "%4x:%6x:%d:%d:%d", &caid, &provid, °, &freq, &srvid); + if(ret < 1) { continue; } + + //snprintf(hdeg, 4, "%x", deg); + //sscanf(hdeg, "%4x", °); + //snprintf(hfreq, 4, "%x", freq); + //sscanf(hfreq, "%4x", &freq); + //snprintf(hsrvid, 4, "%x", srvid); + //sscanf(hsrvid, "%4x", &srvid); + if (!cs_strncat(str1, ",", sizeof(str1))) { + return new_twin; + } + + if(!cs_malloc(&entry, sizeof(struct s_twin))) + { + fclose(fp); + return new_twin; + } + + count++; + entry->tw.caid = caid; + entry->tw.provid = provid; + entry->tw.srvid = srvid; + entry->tw.deg = deg; + entry->tw.freq = freq; + + cs_debug_mask(D_TRACE, "channel: %04X:%06X:%d:%d:%d", entry->tw.caid, + entry->tw.provid, entry->tw.deg, entry->tw.freq, entry->tw.srvid); + + if(!new_twin) + { + new_twin = entry; + last = new_twin; + } + else + { + last->next = entry; + last = entry; + } + } + + if(count) + { cs_log("%d entries read from %s", count, cs_twin); } + + fclose(fp); + + return new_twin; +} + +void twin_read(void) +{ + + struct s_twin *entry, *old_list; + + old_list = cfg.twin_list; + cfg.twin_list = twin_read_int(); + + while(old_list) + { + entry = old_list->next; + free(old_list); + old_list = entry; + } +} + +struct ecmtw get_twin(ECM_REQUEST *er) +{ + struct ecmtw tmp; + memset(&tmp, 0, sizeof(tmp)); + if(!cfg.twin_list) + { + cs_log("twin_list not found!"); + return tmp; + } + struct s_twin *entry = cfg.twin_list; + while(entry) + { + if(entry->tw.caid == er->caid && entry->tw.provid == er->prid && entry->tw.srvid == er->srvid) + { + break; + } + entry = entry->next; + } + + if(entry) { tmp = entry->tw; } + + return (tmp); +} +#endif diff --git a/oscam-config.h b/oscam-config.h new file mode 100644 index 0000000..a308790 --- /dev/null +++ b/oscam-config.h @@ -0,0 +1,74 @@ +#ifndef OSCAM_CONFIG_H_ +#define OSCAM_CONFIG_H_ + +char *get_config_filename(char *dest, size_t destlen, const char *filename); + +int32_t init_config(void); +void config_set(char *section, const char *token, char *value); +void config_free(void); +int32_t write_config(void); +int32_t reload_global_config(void); // Dodano deklarację funkcji do ponownego wczytywania globalnej konfiguracji + +void chk_account(const char *token, char *value, struct s_auth *account); +void account_set_defaults(struct s_auth *auth); +int32_t init_free_userdb(struct s_auth *auth); +struct s_auth *init_userdb(void); +int32_t write_userdb(void); +void cs_accounts_chk(void); + +void chk_reader(char *token, char *value, struct s_reader *rdr); +void reader_set_defaults(struct s_reader *rdr); +int32_t init_readerdb(void); +void free_reader(struct s_reader *rdr); +int32_t free_readerdb(void); +int32_t write_server(void); +void reload_readerdb(void); +void reader_fixups_fn(void *var); + +void chk_sidtab(char *token, char *value, struct s_sidtab *sidtab); +int32_t init_sidtab(void); +void init_free_sidtab(void); +void free_sidtab(struct s_sidtab *sidtab); +int32_t write_services(void); + +int32_t chk_global_whitelist(ECM_REQUEST *er, uint32_t *line); +void global_whitelist_read(void); +struct ecmrl get_ratelimit(ECM_REQUEST *er); // get ratelimits for ecm request (if available) +void ratelimit_read(void); +int32_t init_provid(void); +int32_t init_srvid(void); +int32_t init_tierid(void); +int32_t init_fakecws(void); + +#ifdef MODULE_SERIAL +struct ecmtw get_twin(ECM_REQUEST *er); // get twin channel +void twin_read(void); +#endif + +/* Shared parser functions */ +void check_caidtab_fn(const char *token, char *value, void *setting, FILE *f); +void caidvaluetab_fn(const char *token, char *value, void *setting, FILE *f); +void cacheex_valuetab_fn(const char *token, char *value, void *setting, FILE *f); +void cacheex_hitvaluetab_fn(const char *token, char *value, void *setting, FILE *f); +void class_fn(const char *token, char *value, void *setting, FILE *f); +void group_fn(const char *token, char *value, void *setting, FILE *f); +void services_fn(const char *token, char *value, void *setting, FILE *f); +void chk_ftab_fn(const char *token, char *value, void *setting, FILE *f); + +enum ftab_fn +{ + FTAB_ACCOUNT = 0x01, + FTAB_READER = 0x02, + FTAB_PROVID = 0x04, + FTAB_CHID = 0x08, + FTAB_FBPCAID = 0x10, + FTAB_LOCALCARDS = 0x20, + FTAB_IGNCHKSMCAID = 0x40, + FTAB_IGNCRCCEX4USERONLYFOR = 0x80, + FTAB_EMUAU = 0x100, + FTAB_CCCGBXRESHARE = 0x200 +}; + +void ftab_fn(const char *token, char *value, void *setting, long ftab_type, FILE *f); + +#endif diff --git a/oscam-ecm.c b/oscam-ecm.c new file mode 100644 index 0000000..8e68dfa --- /dev/null +++ b/oscam-ecm.c @@ -0,0 +1,4205 @@ +#define MODULE_LOG_PREFIX "ecm" + +#include "globals.h" +#include // Dodano dla definicji size_t +#include +#include "cscrypt/md5.h" +#include "module-anticasc.h" +#include "module-cacheex.h" +#include "module-led.h" +#include "module-stat.h" +#include "module-webif.h" +#include "module-cw-cycle-check.h" +#include "module-gbox.h" +#include "oscam-cache.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-config.h" +#include "oscam-ecm.h" +#include "oscam-garbage.h" +#include "oscam-failban.h" +#include "oscam-net.h" +#include "oscam-time.h" +#include "oscam-lock.h" +#include "oscam-string.h" +#include "oscam-work.h" +#include "reader-common.h" +#include "module-cccam-data.h" +#ifdef CS_CACHEEX_AIO +#include "oscam-hashtable.h" +#endif + +extern CS_MUTEX_LOCK ecmcache_lock; +extern struct ecm_request_t *ecmcwcache; +extern uint32_t ecmcwcache_size; +extern int32_t exit_oscam; + +extern CS_MUTEX_LOCK ecm_pushed_deleted_lock; +extern struct ecm_request_t *ecm_pushed_deleted; + +static pthread_mutex_t cw_process_sleep_cond_mutex; +static pthread_cond_t cw_process_sleep_cond; +static int cw_process_wakeups; +int64_t ecmc_next, cache_next, msec_wait = 3000; + +// Deklaracja wirtualnych czytników +static struct s_reader *virtual_cacheex_reader = NULL; +static struct s_reader *virtual_csp_reader = NULL; + +#ifdef CS_CACHEEX_AIO +// ecm-cache +typedef struct ecm_cache +{ + struct timeb first_recv_time;// time of first cw received + struct timeb upd_time; // updated time. Update time at each cw got + uint32_t csp_hash; + node ht_node; + node ll_node; +} ECM_CACHE; + +static pthread_rwlock_t ecm_cache_lock; +static hash_table ht_ecm_cache; +static list ll_ecm_cache; +static int8_t ecm_cache_init_done = 0; + +void free_ecm_cache(void) +{ + deinitialize_hash_table(&ht_ecm_cache); + pthread_rwlock_destroy(&ecm_cache_lock); +} + +void init_ecm_cache(void) +{ +#ifdef CS_CACHEEX + if(cfg.cw_cache_size > 0 || cfg.cw_cache_memory > 0) + { + init_hash_table(&ht_ecm_cache, &ll_ecm_cache); + if (pthread_rwlock_init(&ecm_cache_lock,NULL) != 0) + { cs_log("Error creating lock ecm_cache_lock!"); } + else + { ecm_cache_init_done = 1; } + } +#endif +} + +static uint8_t time_sort(ECM_CACHE *a, ECM_CACHE *b) +{ + if (((int64_t)(a->upd_time.time) * 1000ull + (int64_t) a->upd_time.millitm) == ((int64_t)(b->upd_time.time) * 1000ull + (int64_t) b->upd_time.millitm)) return 0; + return (((int64_t)(a->upd_time.time) * 1000ull + (int64_t) a->upd_time.millitm) > ((int64_t)(b->upd_time.time) * 1000ull + (int64_t) b->upd_time.millitm)) ? -1 : 1; +} + +static int compare_csp_hash_ecmcache(const void *arg, const void *obj) +{ + uint32_t h = ((const ECM_CACHE*)obj)->csp_hash; + return memcmp(arg, &h, 4); +} + +void ecm_cache_cleanup(bool force) +{ + if(!ecm_cache_init_done) + { return; } + + SAFE_RWLOCK_WRLOCK(&ecm_cache_lock); + + ECM_CACHE *ecm_cache; + node *i, *i_next; + uint32_t ll_c = 0; + uint32_t ll_ten_percent = (uint)tommy_list_count(&ll_ecm_cache)*0.1; // 10 percent of cache + + if(!force) + sort_list(&ll_ecm_cache, time_sort); + + i = get_first_node_list(&ll_ecm_cache); + while(i) + { + i_next = i->next; + + ecm_cache = get_data_from_node(i); + + if(!ecm_cache) + { + i = i_next; + continue; + } + if(!force) + { + ++ll_c; + + if(ll_c < ll_ten_percent) + { + remove_elem_list(&ll_ecm_cache, &ecm_cache->ll_node); + remove_elem_hash_table(&ht_ecm_cache, &ecm_cache->ht_node); + NULLFREE(ecm_cache); + } + else{ + break; + } + } + else{ + remove_elem_list(&ll_ecm_cache, &ecm_cache->ll_node); + remove_elem_hash_table(&ht_ecm_cache, &ecm_cache->ht_node); + NULLFREE(ecm_cache); + } + i = i_next; + } + + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); +} +#endif + +/** + * maxparallel - Helper: Calculate pending slots array size + * Formula: round(maxparallel * parallelfactor) + * parallelfactor <= 0 means no pending slots (zapping disabled) + */ +static inline int32_t get_pending_size(struct s_reader *rdr) +{ + // parallelfactor <= 0 means no pending slots (disabled or not configured) + if(rdr->parallelfactor <= 0.0f) + return 0; + int32_t size = (int32_t)(rdr->maxparallel * rdr->parallelfactor + 0.5f); + return (size < 1) ? 1 : size; +} + +/** + * maxparallel - Helper: Check if slot is expired + * Returns timeout threshold in ms, 0 if no timeout check needed + */ +static int32_t get_slot_timeout(struct s_parallel_slot *slot, int32_t paralleltimeout) +{ + if(slot->ecm_interval > 0) + return slot->ecm_interval + paralleltimeout; + else + return 10000 + paralleltimeout; // Default 10s if no interval measured +} + +/** + * maxparallel - Helper: Clear a slot + */ +static void clear_slot(struct s_parallel_slot *slot) +{ + slot->srvid = 0; + slot->ecm_interval = 0; + slot->client = NULL; + memset(&slot->last_ecm, 0, sizeof(struct timeb)); +} + +/** + * maxparallel - Helper: Add client+service to blocked list + * Replaces any existing entry for the same client + */ +static void block_client_service(struct s_reader *rdr, struct s_client *client, uint16_t srvid) +{ + if(!rdr || !client || !rdr->blocked_services) + return; + + // Check if client already has a blocked entry - if so, update it + LL_ITER it = ll_iter_create(rdr->blocked_services); + struct s_blocked_client *bc; + while((bc = ll_iter_next(&it))) + { + if(bc->client == client) + { + bc->srvid = srvid; + return; + } + } + + // Add new entry + if(!cs_malloc(&bc, sizeof(struct s_blocked_client))) + return; + bc->client = client; + bc->srvid = srvid; + ll_append(rdr->blocked_services, bc); +} + +/** + * maxparallel - Helper: Check if client+service is blocked + * Returns: 1 = blocked, 0 = not blocked + */ +static int8_t is_client_blocked(struct s_reader *rdr, struct s_client *client, uint16_t srvid) +{ + if(!rdr || !client || !rdr->blocked_services) + return 0; + + LL_ITER it = ll_iter_create(rdr->blocked_services); + struct s_blocked_client *bc; + while((bc = ll_iter_next(&it))) + { + if(bc->client == client && bc->srvid == srvid) + return 1; + } + return 0; +} + +/** + * maxparallel - Helper: Remove block for client if zapping to different service + * Only unblocks if the new service is DIFFERENT from the blocked one + */ +static void unblock_client_if_different(struct s_reader *rdr, struct s_client *client, uint16_t new_srvid) +{ + if(!rdr || !client || !rdr->blocked_services) + return; + + LL_ITER it = ll_iter_create(rdr->blocked_services); + struct s_blocked_client *bc; + while((bc = ll_iter_next(&it))) + { + if(bc->client == client) + { + // Only unblock if zapping to a DIFFERENT service + if(bc->srvid != new_srvid) + { + ll_iter_remove_data(&it); + } + return; + } + } +} + +/** + * maxparallel - Helper: Clear all blocked entries (when active slot becomes free) + */ +static void clear_blocked_services(struct s_reader *rdr) +{ + if(!rdr || !rdr->blocked_services) + return; + + ll_clear_data(rdr->blocked_services); +} + +/** + * maxparallel - Cleanup expired slots in both arrays and promote pending + * - Removes expired slots from parallel_slots (active) and parallel_slots_pending (pending) + * - Upgrades pending to active when space becomes available (FIFO) + * - Does NOT drop pending (that's done separately when active services send ECMs) + * MUST be called with parallel_lock held + * Returns: count of active slots after cleanup + */ +static int32_t reader_cleanup_slots_nolock(struct s_reader *rdr, struct timeb *now) +{ + if(!rdr || rdr->maxparallel <= 0) + return 0; + + int32_t i; + int32_t active_count = 0; + int32_t pending_count = 0; + int32_t free_active = -1; // First empty slot in active array + + // Pass 1: Clean expired slots in active array, count active + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid == 0) + { + if(free_active < 0) + free_active = i; + continue; + } + + int64_t gone = comp_timeb(now, &rdr->parallel_slots[i].last_ecm); + int32_t timeout = get_slot_timeout(&rdr->parallel_slots[i], rdr->paralleltimeout); + + if(gone > timeout) + { + cs_log_dbg(D_READER, "reader %s: service %04X expired (no ECM for %"PRId64" ms, timeout %d ms)", + rdr->label, rdr->parallel_slots[i].srvid, gone, timeout); + clear_slot(&rdr->parallel_slots[i]); + if(free_active < 0) + free_active = i; + } + else + { + active_count++; + } + } + + // Pass 2: Clean expired slots in pending array, count active + int32_t pending_size = get_pending_size(rdr); + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid == 0) + continue; + + int64_t gone = comp_timeb(now, &rdr->parallel_slots_prov[i].last_ecm); + int32_t timeout = get_slot_timeout(&rdr->parallel_slots_prov[i], rdr->paralleltimeout); + + if(gone > timeout) + { + cs_log_dbg(D_READER, "reader %s: pending service %04X expired (no ECM for %"PRId64" ms, timeout %d ms)", + rdr->label, rdr->parallel_slots_prov[i].srvid, gone, timeout); + clear_slot(&rdr->parallel_slots_prov[i]); + } + else + { + pending_count++; + } + } + + // Pass 3: Upgrade pending to active if space available (FIFO) + // This handles the zapping case: old service expired, new one can be promoted + while(active_count < rdr->maxparallel && pending_count > 0) + { + // Find oldest pending (largest age = first to arrive = FIFO upgrade) + int32_t oldest_idx = -1; + int64_t oldest_time = -1; + + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + { + int64_t age = comp_timeb(now, &rdr->parallel_slots_prov[i].last_ecm); + if(oldest_idx < 0 || age > oldest_time) + { + oldest_idx = i; + oldest_time = age; + } + } + } + + if(oldest_idx < 0) + break; + + // Find empty slot in active array + if(free_active < 0) + { + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid == 0) + { + free_active = i; + break; + } + } + } + + if(free_active < 0) + break; // Should not happen, but safety check + + // Move pending to active + rdr->parallel_slots[free_active] = rdr->parallel_slots_prov[oldest_idx]; + clear_slot(&rdr->parallel_slots_prov[oldest_idx]); + + cs_log_dbg(D_READER, "reader %s: service %04X promoted from pending to active (slot %d)", + rdr->label, rdr->parallel_slots[free_active].srvid, free_active); + + active_count++; + pending_count--; + free_active = -1; // Need to find next empty slot + } + + // If active slots are now available, clear blocked list + // This allows previously blocked clients to try again + if(active_count < rdr->maxparallel) + { + clear_blocked_services(rdr); + } + + return active_count; +} + +/** + * maxparallel - Drop pending services when active array is full (FIFO) + * Called only when an ACTIVE service receives an ECM, proving it's still active. + * Drops the OLDEST pending first (first in, first out) + * This is fair: first to request overload is first to be denied. + * Dropped clients are added to the blocked list so they fall over to other readers. + * MUST be called with parallel_lock held + */ +static void reader_drop_pending_nolock(struct s_reader *rdr, struct timeb *now) +{ + if(!rdr || rdr->maxparallel <= 0) + return; + + // Count active and pending + int32_t i; + int32_t active_count = 0; + int32_t pending_count = 0; + int32_t pending_size = get_pending_size(rdr); + + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid != 0) + active_count++; + } + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + pending_count++; + } + + // Drop pending if active array is full (FIFO - oldest dropped first) + while(active_count >= rdr->maxparallel && pending_count > 0) + { + // Find oldest pending (largest age = first to arrive = drop first) + int32_t oldest_idx = -1; + int64_t oldest_time = -1; + + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + { + int64_t age = comp_timeb(now, &rdr->parallel_slots_prov[i].last_ecm); + if(oldest_idx < 0 || age > oldest_time) + { + oldest_idx = i; + oldest_time = age; + } + } + } + + if(oldest_idx < 0) + break; + + // Add client to blocked list before clearing slot + struct s_parallel_slot *slot = &rdr->parallel_slots_prov[oldest_idx]; + if(slot->client) + { + block_client_service(rdr, slot->client, slot->srvid); + } + + cs_log("reader %s: dropped pending service %04X (%.3f sec old, active services still running)", + rdr->label, slot->srvid, (float)oldest_time / 1000); + clear_slot(slot); + pending_count--; + } +} + +/** + * maxparallel - Check if reader has capacity for a service (does NOT reserve slot) + * Used during ECM request to decide if reader should be tried. + * Also checks if client+service is blocked (was dropped earlier). + * Returns: 1 = has capacity (or service already registered), 0 = full or blocked (skip reader) + */ +static int8_t reader_has_capacity(struct s_reader *rdr, uint16_t srvid, struct s_client *client) +{ + if(!rdr || rdr->maxparallel <= 0) + return 1; // unlimited + + cs_readlock(__func__, &rdr->parallel_lock); + + struct timeb now; + cs_ftime(&now); + + int32_t i; + int32_t pending_size = get_pending_size(rdr); + int8_t result = 0; + + // Check if client+service is blocked (was dropped earlier) + if(is_client_blocked(rdr, client, srvid)) + { + result = 0; + goto done; + } + + // Check if service already registered in active array + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid == srvid) + { + result = 1; + goto done; + } + } + + // Check if service already registered in pending array + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid == srvid) + { + result = 1; + goto done; + } + } + + // Count active slots (with expiry check) + int32_t active_count = 0; + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid != 0) + { + int64_t gone = comp_timeb(&now, &rdr->parallel_slots[i].last_ecm); + int32_t timeout = get_slot_timeout(&rdr->parallel_slots[i], rdr->paralleltimeout); + if(gone <= timeout) + active_count++; + } + } + + // Space in active array? + if(active_count < rdr->maxparallel) + { + result = 1; + goto done; + } + + // Space in pending array? + if(pending_size > 0) + { + int32_t pending_count = 0; + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + { + int64_t gone = comp_timeb(&now, &rdr->parallel_slots_prov[i].last_ecm); + int32_t timeout = get_slot_timeout(&rdr->parallel_slots_prov[i], rdr->paralleltimeout); + if(gone <= timeout) + pending_count++; + } + } + if(pending_count < pending_size) + { + result = 1; + goto done; + } + } + + // No capacity available + result = 0; + +done: + cs_readunlock(__func__, &rdr->parallel_lock); + return result; +} + +/** + * maxparallel - Register a service on a reader (called when CW is found) + * Uses dual-array architecture: + * - parallel_slots: active services (the limit) + * - parallel_slots_pending: pending services during zapping + * New services go to active if space available, otherwise pending. + * Returns: 1 = registered, 0 = failed + */ +static int8_t reader_register_service(struct s_reader *rdr, uint16_t srvid, struct s_client *client) +{ + if(!rdr || rdr->maxparallel <= 0) + return 1; // unlimited, always OK + + cs_writelock(__func__, &rdr->parallel_lock); + + // If client is registering a DIFFERENT service, unblock them + // (they zapped away from the blocked service) + unblock_client_if_different(rdr, client, srvid); + + struct timeb now; + cs_ftime(&now); + + // Cleanup expired slots and handle promotions/drops + int32_t active_count = reader_cleanup_slots_nolock(rdr, &now); + + int32_t i; + int32_t pending_size = get_pending_size(rdr); + + // Search for existing slot in both arrays + int32_t existing_active = -1; + int32_t existing_pending = -1; + int32_t free_active = -1; + int32_t free_pending = -1; + + // Search active array + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid == srvid) + existing_active = i; + else if(free_active < 0 && rdr->parallel_slots[i].srvid == 0) + free_active = i; + } + + // Search pending array + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid == srvid) + existing_pending = i; + else if(free_pending < 0 && rdr->parallel_slots_prov[i].srvid == 0) + free_pending = i; + } + + struct s_parallel_slot *slot = NULL; + int8_t is_new = 0; + int8_t is_pending = 0; + int32_t slot_idx = -1; + const char *array_name = "active"; + + if(existing_active >= 0) + { + // Already in active array - just update + slot = &rdr->parallel_slots[existing_active]; + slot_idx = existing_active; + is_new = 0; + } + else if(existing_pending >= 0) + { + // Already in pending array - update there + slot = &rdr->parallel_slots_prov[existing_pending]; + slot_idx = existing_pending; + is_new = 0; + is_pending = 1; + array_name = "pending"; + } + else if(active_count < rdr->maxparallel && free_active >= 0) + { + // New service, space in active array + slot = &rdr->parallel_slots[free_active]; + slot_idx = free_active; + slot->srvid = srvid; + slot->ecm_interval = 0; + slot->client = client; + is_new = 1; + } + else if(free_pending >= 0) + { + // New service, active full -> put in pending + slot = &rdr->parallel_slots_prov[free_pending]; + slot_idx = free_pending; + slot->srvid = srvid; + slot->ecm_interval = 0; + slot->client = client; + is_new = 1; + is_pending = 1; + array_name = "pending"; + } + else + { + // No slot available in either array + cs_writeunlock(__func__, &rdr->parallel_lock); + cs_log("reader %s: no slot available for service %04X (all %d+%d slots used)", + rdr->label, srvid, rdr->maxparallel, pending_size); + return 0; + } + + // Update interval for existing slots + if(!is_new && slot->last_ecm.time > 0) + { + int64_t interval = comp_timeb(&now, &slot->last_ecm); + if(interval > 0 && interval < 30000) // Sanity: < 30 seconds + { + if(slot->ecm_interval == 0) + slot->ecm_interval = (int32_t)interval; + else + slot->ecm_interval = (slot->ecm_interval + (int32_t)interval) / 2; + } + } + + slot->last_ecm = now; + + // If this is an ACTIVE service (not pending), drop pending if needed + // This ensures pending are only dropped when active services prove they're still active + if(!is_pending) + { + reader_drop_pending_nolock(rdr, &now); + } + + // Recount for logging (drop may have changed counts) + int32_t final_active = 0, final_pending = 0; + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid != 0) + final_active++; + } + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + final_pending++; + } + + if(is_new) + { + if(final_pending > 0) + { + cs_log_dbg(D_READER, "reader %s: registered service %04X in %s slot %d (%d/%d active, +%d pending)", + rdr->label, srvid, array_name, slot_idx, final_active, rdr->maxparallel, final_pending); + } + else + { + cs_log_dbg(D_READER, "reader %s: registered service %04X in %s slot %d (%d/%d active)", + rdr->label, srvid, array_name, slot_idx, final_active, rdr->maxparallel); + } + + // Log "now full" only once per state change, and only for active services + if(!is_pending && final_active >= rdr->maxparallel && !rdr->parallel_full) + { + rdr->parallel_full = 1; + cs_log("reader %s: now full (%d/%d active)", + rdr->label, final_active, rdr->maxparallel); + } + } + + // Reset full flag when capacity becomes available + if(final_active < rdr->maxparallel && rdr->parallel_full) + { + rdr->parallel_full = 0; + cs_log_dbg(D_READER, "reader %s: capacity available (%d/%d active)", + rdr->label, final_active, rdr->maxparallel); + } + + cs_writeunlock(__func__, &rdr->parallel_lock); + return 1; +} + +void fallback_timeout(ECM_REQUEST *er) +{ + if(er->rc >= E_UNHANDLED && er->stage < 4) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} fallback timeout! (stage: %d)", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, er->stage); + debug_ecm(D_TRACE, "fallback for %s %s", username(er->client), buf); + while(er->stage < 4) // if preferlocalcards=1 and no answer from locals, initial stage will be 2! We need to reach stage=4 to call fallback's. + { + request_cw_from_readers(er, 0); + } + } +} + +void ecm_timeout(ECM_REQUEST *er) +{ + if(!er->readers_timeout_check) + { + er->readers_timeout_check = 1; + + if(check_client(er->client) && er->rc >= E_UNHANDLED) + { + // Wymuś decyzję o głosowaniu, jeśli limit czasu upłynął, a ECM jest nadal nieobsługiwany + if (cfg.cwvote_enabled && is_cwvote_caid(er) && er->rc == E_UNHANDLED) + { + if (cfg.cwvote_log_enabled) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [ecm_timeout] CW Vote enabled and CAID matches, attempting to decide.", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + } + int vote_result = cw_vote_decide(er); + if (vote_result == 1) // Znaleziono zwycięzcę + { + er->rc = E_FOUND; + er->rcEx = 0; + send_dcw(er->client, er); + return; // ECM obsłużony przez głosowanie + } + } + + debug_ecm(D_TRACE, "timeout for %s %s", username(er->client), buf); + + // set timeout for readers not answering + struct s_ecm_answer *ea_list; + for(ea_list = er->matching_rdr; ea_list; ea_list = ea_list->next) + { + if((ea_list->status & (REQUEST_SENT | REQUEST_ANSWERED)) == REQUEST_SENT) // Request sent, but no answer! + { + write_ecm_answer(ea_list->reader, er, E_TIMEOUT, 0, NULL, NULL, 0, NULL); // set timeout for readers not answered! + } + } + + // send timeout to client! + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} client timeout! ", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + er->rc = E_TIMEOUT; + er->rcEx = 0; + send_dcw(er->client, er); + } + } +} + +void increment_n_request(struct s_client *cl) +{ + if(check_client(cl)) + { + cl->n_request[1]++; + first_client->n_request[1]++; + } +} + +uint8_t checkCWpart(uint8_t *cw, int8_t part) +{ + uint8_t eo = part ? 8 : 0; + int8_t i; + for(i = 0; i < 8; i++) + if(cw[i + eo]) { return 1; } + return 0; +} + +void update_n_request(void) +{ + struct s_client *cl; + + cs_readlock(__func__, &clientlist_lock); + for(cl = first_client->next; cl; cl = cl->next) + { +#ifdef CS_CACHEEX + if(check_client(cl) && get_module(cl)->num != R_CSP && cl->typ == 'c' && !cl->dup && cl->account && cl->account->cacheex.mode<=1) //no cacheex 2/3 client +#else + if(check_client(cl) && get_module(cl)->num != R_CSP && cl->typ == 'c' && !cl->dup) +#endif + { + cl->n_request[0] = cl->n_request[1]; + cl->n_request[1] = 0; + } + else + { + cl->n_request[0] = 0; + cl->n_request[1] = 0; + } + } + + first_client->n_request[0] = first_client->n_request[1]; + first_client->n_request[1] = 0; + + cs_readunlock(__func__, &clientlist_lock); +} + +// Funkcja inicjalizująca wirtualne czytniki +static void init_virtual_readers(void) +{ + if (!virtual_cacheex_reader) { + if (cs_malloc(&virtual_cacheex_reader, sizeof(struct s_reader))) { + memset(virtual_cacheex_reader, 0, sizeof(struct s_reader)); + cs_strncpy(virtual_cacheex_reader->label, "CACHEEX_VIRTUAL", sizeof(virtual_cacheex_reader->label)); + virtual_cacheex_reader->grp = 0; + virtual_cacheex_reader->client = NULL; + cs_log_dbg(D_TRACE, "Virtual CACHEEX reader initialized."); + } else { + cs_log("ERROR: Failed to allocate virtual_cacheex_reader!"); + } + } + + if (!virtual_csp_reader) { + if (cs_malloc(&virtual_csp_reader, sizeof(struct s_reader))) { + memset(virtual_csp_reader, 0, sizeof(struct s_reader)); + cs_strncpy(virtual_csp_reader->label, "CSP_VIRTUAL", sizeof(virtual_csp_reader->label)); + virtual_csp_reader->grp = 0; + virtual_csp_reader->client = NULL; + cs_log_dbg(D_TRACE, "Virtual CSP reader initialized."); + } else { + cs_log("ERROR: Failed to allocate virtual_csp_reader!"); + } + } +} + + +static void *cw_process(void) +{ + set_thread_name(__func__); + int64_t time_to_check_fbtimeout, time_to_check_ctimeout, next_check, n_request_next; + struct timeb t_now, tbc, ecmc_time, cache_time, n_request_time; + ECM_REQUEST *er = NULL; + time_t ecm_maxcachetime; + +#ifdef CS_CACHEEX + int64_t time_to_check_cacheex_wait_time; + int64_t time_to_check_cacheex_mode1_delay; +#endif + + cs_pthread_cond_init(__func__, &cw_process_sleep_cond_mutex, &cw_process_sleep_cond); + +#ifdef CS_ANTICASC + int32_t ac_next; + struct timeb ac_time; + cs_ftime(&ac_time); + add_ms_to_timeb(&ac_time, cfg.ac_stime * 60 * 1000); +#endif + + cs_ftime(&ecmc_time); + add_ms_to_timeb(&ecmc_time, 1000); + cs_ftime(&cache_time); + add_ms_to_timeb(&cache_time, 3000); + cs_ftime(&n_request_time); + add_ms_to_timeb(&n_request_time, 60 * 1000); + + while(!exit_oscam) + { + if(cw_process_wakeups == 0) // No waiting wakeups, proceed to sleep + { + sleepms_on_cond(__func__, &cw_process_sleep_cond_mutex, &cw_process_sleep_cond, msec_wait); + } + cw_process_wakeups = 0; // We've been woken up, reset the counter + if(exit_oscam) + { break; } + + next_check = 0; +#ifdef CS_ANTICASC + ac_next = 0; +#endif + ecmc_next = 0; + cache_next = 0; + msec_wait = 0; + + cs_ftime(&t_now); + cs_readlock(__func__, &ecmcache_lock); + for(er = ecmcwcache; er; er = er->next) + { + + if((er->from_cacheex || er->from_csp) // ignore ecms from cacheex/csp + || er->readers_timeout_check // ignore already checked + || !check_client(er->client)) // ignore ecm of killed clients + { + continue; + } + + if(er->rc >= E_UNHANDLED) + { +#ifdef CS_CACHEEX + // cacheex_wait_time + if(er->cacheex_wait_time && !er->cacheex_wait_time_expired) + { + tbc = er->tps; + time_to_check_cacheex_mode1_delay = 0; + time_to_check_ctimeout = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, er->client_timeout)); + + + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_CACHEEX_TIMEOUT, (void *)er, 0); + time_to_check_cacheex_wait_time = 0; + + } + else if(er->cacheex_mode1_delay && !er->stage && er->cacheex_reader_count>0) + { + // check for cacheex_mode1_delay + tbc = er->tps; + time_to_check_cacheex_mode1_delay = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, er->cacheex_mode1_delay)); + + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_CACHEEX1_DELAY, (void *)er, 0); + time_to_check_cacheex_mode1_delay = 0; + } + } + + if(!next_check || (time_to_check_cacheex_wait_time > 0 && time_to_check_cacheex_wait_time < next_check)) + { next_check = time_to_check_cacheex_wait_time; } + + if(!next_check || (time_to_check_cacheex_mode1_delay > 0 && time_to_check_cacheex_mode1_delay < next_check)) + { next_check = time_to_check_cacheex_mode1_delay; } + } +#endif + if(er->stage < 4) + { + // fbtimeout + tbc = er->tps; + time_to_check_fbtimeout = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, get_fallbacktimeout(er->caid))); + + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_FALLBACK_TIMEOUT, (void *)er, 0); + time_to_check_fbtimeout = 0; + } + + if(!next_check || (time_to_check_fbtimeout > 0 && time_to_check_fbtimeout < next_check)) + { next_check = time_to_check_fbtimeout; } + } + + // clienttimeout + if(!er->readers_timeout_check) // ecm stays in cache at least ctimeout+2seconds! + { + tbc = er->tps; + time_to_check_ctimeout = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, er->client_timeout)); + + + + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_CLIENT_TIMEOUT, (void *)er, 0); + time_to_check_ctimeout = 0; + } + + if(!next_check || (time_to_check_ctimeout > 0 && time_to_check_ctimeout < next_check)) + { next_check = time_to_check_ctimeout; } + } + } + } + + + cs_readunlock(__func__, &ecmcache_lock); +#ifdef CS_ANTICASC + if(cfg.ac_enabled && (ac_next = comp_timeb(&ac_time, &t_now)) <= 10) + { + ac_do_stat(); + cs_ftime(&ac_time); + ac_next = add_ms_to_timeb_diff(&ac_time, cfg.ac_stime * 60 * 1000); + } +#endif + if((ecmc_next = comp_timeb(&ecmc_time, &t_now)) <= 10) + { + uint32_t count = 0; + struct ecm_request_t *ecm, *ecmt = NULL, *prv; + + cs_readlock(__func__, &ecmcache_lock); + for(ecm = ecmcwcache, prv = NULL; ecm; prv = ecm, ecm = ecm->next, count++) + { + ecm_maxcachetime = t_now.time - ((ecm->client_timeout + 500) / 1000 + 3); + + + + if(ecm->tps.time < ecm_maxcachetime) + { + cs_readunlock(__func__, &ecmcache_lock); + cs_writelock(__func__, &ecmcache_lock); + ecmt = ecm; + if(prv) + { prv->next = NULL; } + else + { ecmcwcache = NULL; } + cs_writeunlock(__func__, &ecmcache_lock); + break; + } + } + if(!ecmt) + { cs_readunlock(__func__, &ecmcache_lock); } + ecmcwcache_size = count; + + while(ecmt) + { + ecm = ecmt->next; + free_ecm(ecmt); + ecmt = ecm; + } + +#ifdef CS_CACHEEX + ecmt=NULL; + cs_readlock(__func__, &ecm_pushed_deleted_lock); + for(ecm = ecm_pushed_deleted, prv = NULL; ecm; prv = ecm, ecm = ecm->next) + { + ecm_maxcachetime = t_now.time - ((ecm->client_timeout + 500) / 1000 + 3); + + + + if(ecm->tps.time < ecm_maxcachetime) + { + cs_readunlock(__func__, &ecm_pushed_deleted_lock); + cs_writelock(__func__, &ecm_pushed_deleted_lock); + ecmt = ecm; + if(prv) + { prv->next = NULL; } + else + { ecm_pushed_deleted = NULL; } + cs_writeunlock(__func__, &ecm_pushed_deleted_lock); + break; + } + } + if(!ecmt) + { cs_readunlock(__func__, &ecm_pushed_deleted_lock); } + + while(ecmt) + { + ecm = ecmt->next; + free_push_in_ecm(ecmt); + ecmt = ecm; + } +#endif + + cs_ftime(&ecmc_time); + ecmc_next = add_ms_to_timeb_diff(&ecmc_time, 1000); + } + + if((cache_next = comp_timeb(&cache_time, &t_now)) <= 10) + { + cleanup_cache(false); + cacheex_cleanup_hitcache(false); + + cs_ftime(&cache_time); + cache_next = add_ms_to_timeb_diff(&cache_time, 3000); + } + + if((n_request_next = comp_timeb(&n_request_time, &t_now)) <= 10) + { + update_n_request(); + cs_ftime(&n_request_time); + n_request_next = add_ms_to_timeb_diff(&n_request_time, 60 * 1000); + } + + msec_wait = next_check; +#ifdef CS_ANTICASC + if(!msec_wait || (ac_next > 0 && ac_next < msec_wait)) + { msec_wait = ac_next; } +#endif + if(!msec_wait || (ecmc_next > 0 && ecmc_next < msec_wait)) + { msec_wait = ecmc_next; } + + if(!msec_wait || (cache_next > 0 && cache_next < msec_wait)) + { msec_wait = cache_next; } + + if(!msec_wait || (n_request_next > 0 && n_request_next < msec_wait)) + { msec_wait = n_request_next; } + + if(!msec_wait) + { msec_wait = 3000; } + + cleanupcwcycle(); + } + + return NULL; +} + +void cw_process_thread_start(void) +{ + init_virtual_readers(); // Inicjalizacja wirtualnych czytników + start_thread("cw_process", (void *) &cw_process, NULL, NULL, 1, 1); +} + +void cw_process_thread_wakeup(void) +{ + cw_process_wakeups++; // Do not sleep... + SAFE_COND_SIGNAL(&cw_process_sleep_cond); +} + +void convert_to_beta(struct s_client *cl, ECM_REQUEST *er, uint16_t caidto) +{ + static uint8_t headerN3[10] = { 0xc7, 0x00, 0x00, 0x00, 0x01, 0x10, 0x10, 0x00, 0x87, 0x12 }; + static uint8_t headerN2[10] = { 0xc9, 0x00, 0x00, 0x00, 0x01, 0x10, 0x10, 0x00, 0x48, 0x12 }; + + er->ocaid = er->caid; + er->caid = caidto; + er->prid = 0; + er->ecmlen = er->ecm[2] + 3; + + memmove(er->ecm + 13, er->ecm + 3, er->ecmlen - 3); + + if(er->ecmlen > 0x88) + { + memcpy(er->ecm + 3, headerN3, 10); + if(er->ecm[0] == 0x81) + { er->ecm[12] += 1; } + er->ecm[1] = 0x70; + } + else + { + memcpy(er->ecm + 3, headerN2, 10); + } + + er->ecmlen += 10; + er->ecm[2] = er->ecmlen - 3; + er->btun = 1; + + cl->cwtun++; + cl->account->cwtun++; + first_client->cwtun++; + + cs_log_dbg(D_TRACE, "ECM converted ocaid from 0x%04X to BetaCrypt caid 0x%04X for service id 0x%04X", + er->ocaid, caidto, er->srvid); +} + +void convert_to_nagra(struct s_client *cl, ECM_REQUEST *er, uint16_t caidto) +{ + cs_log_dbg(D_TRACE, "convert_to_nagra"); + er->ocaid = er->caid; + er->caid = caidto; + er->prid = 0; + er->ecmlen = er->ecm[2] + 3; + + // not sure + if(er->ecmlen < 0x52) + { er->ecm[1] = 0x30; } + + memmove(er->ecm + 3, er->ecm + 13, er->ecmlen - 3); + + er->ecmlen -= 10; + er->ecm[2] = er->ecmlen - 3; + er->btun = 1; + + cl->cwtun++; + cl->account->cwtun++; + first_client->cwtun++; + + cs_log_dbg(D_TRACE, "ECM converted ocaid from: 0x%04X to Nagra: 0x04%X for service id:0x04%X", + er->ocaid, caidto, er->srvid); +} + +void cs_betatunnel(ECM_REQUEST *er) +{ + int32_t i; + struct s_client *cl = cur_client(); + uint32_t mask_all = 0xFFFF; + TUNTAB *ttab = &cl->ttab; + + for(i = 0; i < ttab->ttnum; i++) + { + if((er->caid == ttab->ttdata[i].bt_caidfrom) && ((er->srvid == ttab->ttdata[i].bt_srvid) || (ttab->ttdata[i].bt_srvid) == mask_all)) + { + if(chk_is_betatunnel_caid(er->caid) == 1 && er->ocaid == 0x0000) + { + convert_to_nagra(cl, er, ttab->ttdata[i].bt_caidto); + } + else if(er->ocaid == 0x0000) + { + convert_to_beta(cl, er, ttab->ttdata[i].bt_caidto); + } + return; + } + } +} + +static void remove_ecm_from_reader(ECM_REQUEST *ecm) +{ + int32_t i; + struct s_ecm_answer *ea = ecm->matching_rdr; + while(ea) + { + if((ea->status & REQUEST_SENT) && !(ea->status & REQUEST_ANSWERED)) + { + // we found a outstanding reader, clean it: + struct s_reader *rdr = ea->reader; + if(rdr) + { + struct s_client *cl = rdr->client; + if(check_client(cl)) + { + ECM_REQUEST *ecmtask = cl->ecmtask; + if(ecmtask) + { + for(i = 0; i < cfg.max_pending; ++i) + { + if(ecmtask[i].parent == ecm) + { + ecmtask[i].client = NULL; + cacheex_set_csp_lastnode(&ecmtask[i]); + } + } + } + } + } + } + ea = ea->next; + } +} + +void free_ecm(ECM_REQUEST *ecm) +{ + struct s_ecm_answer *ea, *nxt; + cacheex_free_csp_lastnodes(ecm); + gbox_free_cards_pending(ecm); + // remove this ecm from reader queue to avoid segfault on very late answers (when ecm is already disposed) + // first check for outstanding answers: + remove_ecm_from_reader(ecm); + // free matching_rdr list: + ea = ecm->matching_rdr; + ecm->matching_rdr = NULL; + while(ea) + { + nxt = ea->next; + cs_lock_destroy(__func__, &ea->ecmanswer_lock); + add_garbage(ea); + ea = nxt; + } + if(ecm->src_data) + { add_garbage(ecm->src_data); } + add_garbage(ecm); +} + + +void free_push_in_ecm(ECM_REQUEST *ecm) +{ + cacheex_free_csp_lastnodes(ecm); + gbox_free_cards_pending(ecm); + if(ecm->src_data) + { NULLFREE(ecm->src_data); } + NULLFREE(ecm); +} + +ECM_REQUEST *get_ecmtask(void) +{ + ECM_REQUEST *er = NULL; + struct s_client *cl = cur_client(); + if(!cl) + { return NULL; } + if(!cs_malloc(&er, sizeof(ECM_REQUEST))) + { return NULL; } + memset(er->vote_pool, 0, sizeof(er->vote_pool)); // Initialize vote_pool to zero + cs_ftime(&er->tps); + er->rc = E_UNHANDLED; + er->client = cl; + er->grp = 0; // no readers/cacheex-clients answers yet + //cs_log("client %s ECMTASK %d module %s", username(cl), n, get_module(cl)->desc); + return er; +} + +void cleanup_ecmtasks(struct s_client *cl) +{ + if(!cl) { return; } + + ECM_REQUEST *ecm; + + // remove this clients ecm from queue. because of cache, just null the client: + cs_readlock(__func__, &ecmcache_lock); + for(ecm = ecmcwcache; ecm && cl; ecm = ecm->next) + { + if(ecm->client == cl) + { + ecm->client = NULL; + } + } + cs_readunlock(__func__, &ecmcache_lock); + + // remove client from rdr ecm-queue: + cs_readlock(__func__, &readerlist_lock); + struct s_reader *rdr = first_active_reader; + while(rdr) + { + if(check_client(rdr->client) && rdr->client->ecmtask) + { + int i; + for(i = 0; (i < cfg.max_pending) && cl; i++) + { + ecm = &rdr->client->ecmtask[i]; + if(ecm->client == cl) + { + ecm->client = NULL; + } + } + } + rdr = rdr->next; + } + cs_readunlock(__func__, &readerlist_lock); + +} + +static void add_cascade_data(struct s_client *client, ECM_REQUEST *er) +{ + if(!client->cascadeusers) + { client->cascadeusers = ll_create("cascade_data"); } + LLIST *l = client->cascadeusers; + LL_ITER it = ll_iter_create(l); + time_t now = time(NULL); + struct s_cascadeuser *cu; + int8_t found = 0; + while((cu = ll_iter_next(&it))) + { + if(er->caid == cu->caid && er->prid == cu->prid && er->srvid == cu->srvid) // found it + { + if(cu->time < now) + { cu->cwrate = now - cu->time; } + cu->time = now; + found = 1; + } + else if(cu->time + 60 < now) // old + { ll_iter_remove_data(&it); } + } + + if(!found) // add it if not found + { + if(!cs_malloc(&cu, sizeof(struct s_cascadeuser))) + { return; } + cu->caid = er->caid; + cu->prid = er->prid; + cu->srvid = er->srvid; + cu->time = now; + ll_append(l, cu); + } +} + +int32_t is_double_check_caid(ECM_REQUEST *er, FTAB *double_check_caid) +{ + if(!double_check_caid->nfilts) { return 1; } + + int32_t i, k; + for(i = 0; i < double_check_caid->nfilts; i++) + { + uint16_t tcaid = double_check_caid->filts[i].caid; + if(tcaid && (tcaid == er->caid || (tcaid < 0x0100 && (er->caid >> 8) == tcaid))) // caid match + { + int32_t nprids = double_check_caid->filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid = double_check_caid->filts[i].prids[k]; + if(prid == er->prid) // Provider matches + { return 1; } + } + } + } + + return 0; +} + +struct s_ecm_answer *get_ecm_answer(struct s_reader *reader, ECM_REQUEST *er) +{ + if(!er || !reader) { return NULL; } + + struct s_ecm_answer *ea; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if(ea->reader == reader) + { + return ea; + } + } + return NULL; +} + +void distribute_ea(struct s_ecm_answer *ea) +{ + struct s_ecm_answer *ea_temp; + + for(ea_temp = ea->pending; ea_temp; ea_temp = ea_temp->pending_next) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [distribute_ea] send ea (%s) by reader %s answering for client %s", (check_client(ea_temp->er->client) ? ea_temp->er->client->account->usr : "-"), ea_temp->er->caid, ea_temp->er->prid, ea_temp->er->srvid, ea->rc==E_FOUND?"OK":"NOK", ea_temp->reader->label, (check_client(ea->er->client) ? ea->er->client->account->usr : "-")); +#ifdef CS_CACHEEX_AIO + if(ea->rc==E_FOUND && ea->er->localgenerated) + ea_temp->er->localgenerated = 1; +#endif + // e.g. we cannot send timeout, because "ea_temp->er->client" could wait/ask other readers! Simply set not_found if different from E_FOUND! + write_ecm_answer(ea_temp->reader, ea_temp->er, (ea->rc==E_FOUND? E_FOUND : E_NOTFOUND), ea->rcEx, ea->cw, NULL, ea->tier, &ea->cw_ex); + } +} + +int32_t send_dcw(struct s_client *client, ECM_REQUEST *er) +{ + if(!check_client(client) || client->typ != 'c') + { return 0; } + + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [send_dcw] rc %d from reader %s", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, er->rc, er->selected_reader ? er->selected_reader->label : "-"); + + static const char stageTxt[] = { '0', 'C', 'L', 'P', 'F', 'X' }; + static const char *stxt[] = { "found", "cache1", "cache2", "cache3", "not found", "timeout", "sleeping", + "fake", "invalid", "corrupt", "no card", "expdate", "disabled", "stopped" }; + + static const char *stxtEx[16] = {"", "group", "caid", "ident", "class", "chid", "queue", "peer", "sid", "", "", "", "", "", "", ""}; + static const char *stxtWh[16] = {"", "user ", "reader ", "server ", "lserver ", "", "", "", "", "", "", "", "" , "" , "", ""}; +#ifdef CS_CACHEEX_AIO + char sby[100] = "", sreason[100] = "", scwcinfo[32] = "", schaninfo[CS_SERVICENAME_SIZE] = "", srealecmtime[50]=""; +#else + char sby[100] = "", sreason[70] = "", scwcinfo[32] = "", schaninfo[CS_SERVICENAME_SIZE] = "", srealecmtime[50]=""; +#endif + char erEx[32] = ""; + char usrname[38] = ""; + char channame[28]; + struct timeb tpe; + + snprintf(usrname, sizeof(usrname) - 1, "%s", username(client)); + +#ifdef WITH_DEBUG + if(cs_dblevel & D_CLIENTECM) + { + char buf[ECM_FMT_LEN]; + char ecmd5[17 * 3]; + char cwstr[17 * 3]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); + cs_hexdump(0, er->cw, 16, cwstr, sizeof(cwstr)); +#ifdef CS_CACHEEX + char csphash[5 * 3]; + cs_hexdump(0, (void *)&er->csp_hash, 4, csphash, sizeof(csphash)); + cs_log_dbg(D_CLIENTECM, "Client %s csphash %s cw %s rc %d %s", username(client), csphash, cwstr, er->rc, buf); +#else + cs_log_dbg(D_CLIENTECM, "Client %s cw %s rc %d %s", username(client), cwstr, er->rc, buf); +#endif + } +#endif + + struct s_reader *er_reader = er->selected_reader; // responding reader + struct s_ecm_answer *ea_orig = get_ecm_answer(er_reader, er); + + + // check if ecm_answer from pending's + if(ea_orig && ea_orig->is_pending && er->rc == E_FOUND) + { er->rc = E_CACHE2; } + + + // check if answer from cacheex-1 reader + if(er->rc == E_FOUND && er_reader && cacheex_reader(er_reader)) // so add hit to cacheex mode 1 readers + { + er->rc = E_CACHEEX; + } + + // real ecm time + if(ea_orig && !ea_orig->is_pending && er->rc == E_FOUND + && ( +#ifdef CS_CACHEEX + er->cacheex_wait_time || +#endif + (ea_orig->status & READER_FALLBACK))) + { + snprintf(srealecmtime, sizeof(srealecmtime) - 1, " (real %d ms)", ea_orig->ecm_time); + } + + + if(er->rc == E_TIMEOUT) + { +#ifdef CS_CACHEEX + if(!er->from_cacheex1_client) // cosmetic: show "by" readers only for "normal" clients + { +#endif + struct s_ecm_answer *ea_list; + int32_t ofs = 0; + + for(ea_list = er->matching_rdr; ea_list; ea_list = ea_list->next) + { + if(ea_list->reader && ofs < (int32_t)sizeof(sby) && ((ea_list->status & REQUEST_SENT) && (ea_list->rc == E_TIMEOUT || ea_list->rc >= E_99))) //Request send, but no cw answered! + { + ofs += snprintf(sby + ofs, sizeof(sby) - ofs - 1, "%s%s", ofs ? "," : " by ", ea_list->reader->label); + } + } + + if(er->ocaid && ofs < (int32_t)sizeof(sby)) + { snprintf(sby + ofs, sizeof(sby) - ofs - 1, "(btun %04X)", er->ocaid); } + +#ifdef CS_CACHEEX + } +#endif + } + else if(er_reader) + { + // add marker to reader if ECM_REQUEST was betatunneled + if(er->ocaid) + { snprintf(sby, sizeof(sby) - 1, " by %s(btun %04X)", er_reader->label, er->ocaid); } + else + { snprintf(sby, sizeof(sby) - 1, " by %s", er_reader->label); } + } +#ifdef CS_CACHEEX + else if(er->cacheex_src) // only for cacheex mode-3 clients (no mode-1 or mode-2 because reader is set!) and csp + { + char *cex_name = "-"; + if(check_client(er->cacheex_src) && er->cacheex_src->account) + { + if(er->cacheex_src->account->usr[0] != '\0') + cex_name = er->cacheex_src->account->usr; + else + cex_name = "csp"; + } + + if(er->ocaid) + { + snprintf(sby, sizeof(sby) - 1, " by %s(btun %04X)", cex_name, er->ocaid); + } + else + { + snprintf(sby, sizeof(sby) - 1, " by %s", cex_name); + } + } +#endif + + if(er->rc < E_NOTFOUND) + { + er->rcEx = 0; + // memset(er->msglog, 0, MSGLOGSIZE); // remove reader msglog from previous requests that failed, founds never give back msglog! + } + + if(er->rcEx) + { snprintf(erEx, sizeof(erEx) - 1, "rejected %s%s", stxtWh[er->rcEx >> 4], stxtEx[er->rcEx & 0xf]); } + + get_servicename_or_null(client, er->srvid, er->prid, er->caid, channame, sizeof(channame)); + if(!channame[0]) + { + schaninfo[0] = '\0'; + } + else + { + snprintf(schaninfo, sizeof(schaninfo) - 1, " - %s", channame); + } + +#ifdef CS_CACHEEX + int cx = 0; + if(er->msglog[0]) + { + cx = snprintf(sreason, sizeof(sreason) - 1, " (%s)", er->msglog); + } +#else + if(er->msglog[0]) + { + snprintf(sreason, sizeof(sreason) - 1, " (%s)", er->msglog); + } +#endif +#ifdef CW_CYCLE_CHECK + if(er->cwc_msg_log[0]) + { snprintf(scwcinfo, sizeof(scwcinfo) - 1, " (%.26s)", er->cwc_msg_log); } +#endif + + cs_ftime(&tpe); + +#ifdef CS_CACHEEX + int cx2 = 0; + if(er->rc >= E_CACHEEX && er->cacheex_wait_time && er->cacheex_wait_time_expired) + { + cx2 = snprintf(sreason+cx, (sizeof sreason)-cx, " (wait_time over)"); + } + else + { + cx2 = cx; + } + + if(er->cw_count>1) + { +#ifdef CS_CACHEEX_AIO + if(er->cw_count > 0x0F000000 || er->localgenerated) + { + uint32_t cw_count_cleaned = er->cw_count ^ 0x0F000000; + if(cw_count_cleaned > 1) + snprintf(sreason+cx2, (sizeof sreason)-cx2, " (cw count %d) (lg)", cw_count_cleaned); + else + snprintf(sreason+cx2, (sizeof sreason)-cx2, " (lg)"); + } + else + { +#endif + + snprintf (sreason+cx2, (sizeof sreason)-cx2, " (cw count %d)", er->cw_count); + +#ifdef CS_CACHEEX_AIO + } + + } + else + { + if(er->localgenerated) + snprintf(sreason+cx2, (sizeof sreason)-cx2, " (lg)"); +#endif + } + +#endif + + client->cwlastresptime = comp_timeb(&tpe, &er->tps); + + time_t now = time(NULL); + webif_client_add_lastresponsetime(client, client->cwlastresptime, now, er->rc); // add to ringbuffer + + if(er_reader) + { + struct s_client *er_cl = er_reader->client; + if(check_client(er_cl)) + { + er_cl->cwlastresptime = client->cwlastresptime; + webif_client_add_lastresponsetime(er_cl, client->cwlastresptime, now, er->rc); + er_cl->last_providptr = client->last_providptr; + er_cl->last_srvidptr = client->last_srvidptr; + } + } + + webif_client_init_lastreader(client, er, er_reader, stxt); + + client->last = now; + + //cs_log_dbg(D_TRACE, "CHECK rc=%d er->cacheex_src=%s", er->rc, username(er->cacheex_src)); + switch(er->rc) + { + case E_FOUND: + { + client->cwfound++; + client->account->cwfound++; + first_client->cwfound++; + break; + } + case E_CACHE1: + case E_CACHE2: + case E_CACHEEX: + { + client->cwcache++; + client->account->cwcache++; + first_client->cwcache++; +#ifdef CS_CACHEEX + if(check_client(er->cacheex_src)) + { + first_client->cwcacheexhit++; + er->cacheex_src->cwcacheexhit++; + if(er->cacheex_src->account) + { er->cacheex_src->account->cwcacheexhit++; } + } +#endif + break; + } + case E_NOTFOUND: + case E_CORRUPT: + case E_NOCARD: + { + if(er->rcEx) + { + client->cwignored++; + client->account->cwignored++; + first_client->cwignored++; + } + else + { + client->cwnot++; + client->account->cwnot++; + first_client->cwnot++; + } + break; + } + case E_TIMEOUT: + { + client->cwtout++; + client->account->cwtout++; + first_client->cwtout++; + break; + } + default: + { + client->cwignored++; + client->account->cwignored++; + first_client->cwignored++; + } + } + +#ifdef CS_ANTICASC +// [zaplist] ACoSC anticascading + if(cfg.acosc_enabled) + { + int8_t max_active_sids = 0; + int8_t zap_limit = 0; + int8_t penalty = 0; + int32_t penalty_duration = 0; + int32_t delay = 0; + int8_t max_ecms_per_minute = 0; + char *info1 = NULL; + char *info2 = NULL; + char *info3 = NULL; + char *info4 = NULL; + char *info5 = NULL; + char *info6 = NULL; + + // **global or user value? + cs_writelock(__func__, &clientlist_lock); + + max_active_sids = client->account->acosc_max_active_sids == -1 ? cfg.acosc_max_active_sids : client->account->acosc_max_active_sids; + info1 = client->account->acosc_max_active_sids == -1 ? "Globalvalue" : "Uservalue"; + + zap_limit = client->account->acosc_zap_limit == -1 ? cfg.acosc_zap_limit : client->account->acosc_zap_limit; + info5 = client->account->acosc_zap_limit == -1 ? "Globalvalue" : "Uservalue"; + + penalty = client->account->acosc_penalty == -1 ? cfg.acosc_penalty : client->account->acosc_penalty; + info2 = client->account->acosc_penalty == -1 ? "Globalvalue" : "Uservalue"; + + penalty_duration = client->account->acosc_penalty_duration == -1 ? cfg.acosc_penalty_duration : client->account->acosc_penalty_duration; + info3 = client->account->acosc_penalty_duration == -1 ? "Globalvalue" : "Uservalue"; + + delay = client->account->acosc_delay == -1 ? cfg.acosc_delay : client->account->acosc_delay; + info4 = client->account->acosc_delay == -1 ? "Globalvalue" : "Uservalue"; + + max_ecms_per_minute = client->account->acosc_max_ecms_per_minute == -1 ? cfg.acosc_max_ecms_per_minute : client->account->acosc_max_ecms_per_minute; + info6 = client->account->acosc_max_ecms_per_minute == -1 ? "Globalvalue" : "Uservalue"; + + //** + + if((er->rc < E_NOTFOUND && max_active_sids > 0) || zap_limit > 0 || max_ecms_per_minute > 0) + { + int8_t k = 0; + int8_t active_sid_count = 0; + time_t zaptime = time(NULL); + + if(client->account->acosc_penalty_active == 4 && client->account->acosc_penalty_until <= zaptime) // reset penalty_active + { + client->account->acosc_penalty_active = 0; + client->account->acosc_penalty_until = 0; + } + + if(client->account->acosc_penalty_active == 0 && max_active_sids > 0) + { + for(k=0; k<15 ; k++) + { + if(zaptime-30 < client->client_zap_list[k].lasttime && client->client_zap_list[k].request_stage == 10) + { + cs_log_dbg(D_TRACE, "[zaplist] ACoSC for Client: %s more then 10 ECM's for %04X@%06X/%04X/%04X", username(client), client->client_zap_list[k].caid, client->client_zap_list[k].provid, client->client_zap_list[k].chid, client->client_zap_list[k].sid); + active_sid_count ++; + } + } + cs_log_dbg(D_TRACE, "[zaplist] ACoSC for Client: %s active_sid_count= %i with more than 10 followed ECM's (mas:%i (%s))", username(client), active_sid_count, max_active_sids, info1); + } + + if(client->account->acosc_penalty_active == 0 && max_active_sids > 0 && active_sid_count > max_active_sids) //max_active_sids reached + { + client->account->acosc_penalty_active = 1; + client->account->acosc_penalty_until = zaptime + penalty_duration; + } + + if(client->account->acosc_penalty_active == 0 && zap_limit > 0 && client->account->acosc_user_zap_count > zap_limit) // zap_limit reached + { + client->account->acosc_penalty_active = 2; + client->account->acosc_penalty_until = zaptime + penalty_duration; + } + + if(client->account->acosc_penalty_active == 0 && max_ecms_per_minute > 0 && client->n_request[1] >= max_ecms_per_minute && penalty != 4) // max ecms per minute reached + { + client->account->acosc_penalty_active = 3; + client->account->acosc_penalty_until = zaptime + penalty_duration; + } + + if(client->account->acosc_penalty_active == 0 && max_ecms_per_minute > 0 && client->n_request[1] > 0 && penalty == 4) // max ecms per minute with hidecards penalty + { + client->account->acosc_penalty_active = 3; + client->account->acosc_penalty_until = zaptime + penalty_duration; + } + + if(client->account->acosc_penalty_active > 0) + { + if(client->account->acosc_penalty_active == 4) + { cs_log_dbg(D_TRACE, "[zaplist] ACoSC for Client: %s penalty_duration: %" PRId64 " seconds left(%s)", username(client), (int64_t)(client->account->acosc_penalty_until - zaptime), info3); } + + int16_t lt = get_module(client)->listenertype; + switch(penalty) + { + case 1: // NULL CW + er->rc = E_FAKE; // E_FAKE give only a status fake not a NULL cw + er->rcEx = E2_WRONG_CHKSUM; + if(client->account->acosc_penalty_active == 1) + { cs_log("[zaplist] ACoSC for Client: %s max_activ_sids reached: %i:%i(%s) penalty: 1(%s) send null CW", username(client), active_sid_count, max_active_sids, info1, info2); } + if(client->account->acosc_penalty_active == 2) + { cs_log("[zaplist] ACoSC for Client: %s zap_limit reached: %i:%i(%s) penalty: 1(%s) send null CW", username(client), client->account->acosc_user_zap_count, zap_limit, info5, info2); } + if(client->account->acosc_penalty_active == 3) + { cs_log("[maxecms] ACoSC for Client: %s max_ecms_per_minute reached: ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 1(%s) send null CW", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2); } + break; + + case 2: // ban + if(lt != LIS_DVBAPI) + { + if(client->account->acosc_penalty_active == 1) + { cs_log("[zaplist] ACoSC for Client: %s max_activ_sids reached: %i:%i(%s) penalty: 2(%s) BAN Client - Kill and set Client to failban list for %i sec.", username(client), active_sid_count, max_active_sids, info1, info2, penalty_duration); } + if(client->account->acosc_penalty_active == 2) + { cs_log("[zaplist] ACoSC for Client: %s zap_limit reached: %i:%i(%s) penalty: 2(%s) BAN Client - Kill and set Client to failban list for %i sec.", username(client), client->account->acosc_user_zap_count, zap_limit, info5, info2, penalty_duration); } + if(client->account->acosc_penalty_active == 3) + { cs_log("[maxecms] ACoSC for Client: %s max_ecms_per_minute reached: ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 2(%s) BAN Client - Kill and set Client to failban list for %i sec.", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2, penalty_duration); } + cs_add_violation_acosc(client, client->account->usr, penalty_duration); + add_job(client, ACTION_CLIENT_KILL, NULL, 0); + } + else + { + cs_log("[zaplist] ACoSC for Client: %s %i:%i(%s) penalty: 2(%s) BAN Client - don't Ban dvbapi user only stop decoding", username(client), active_sid_count, max_active_sids, info1, info2); + } + er->rc = E_DISABLED; + break; + + case 3: // delay + if(client->account->acosc_penalty_active == 1) + { cs_log("[zaplist] ACoSC for Client: %s max_activ_sids reached: %i:%i(%s) penalty: 3(%s) delay CW: %ims(%s)", username(client), active_sid_count, max_active_sids, info1, info2, delay, info4); } + if(client->account->acosc_penalty_active == 2) + { cs_log("[zaplist] ACoSC for Client: %s zap_limit reached: %i:%i(%s) penalty: 3(%s) delay CW: %ims(%s)", username(client), client->account->acosc_user_zap_count, zap_limit, info5, info2, delay, info4); } + if(client->account->acosc_penalty_active == 3) + { cs_log("[maxecms] ACoSC for Client: %s max_ecms_per_minute reached: ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 3(%s) delay CW: %ims(%s)", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2, delay, info4); } + cs_writeunlock(__func__, &clientlist_lock); + cs_sleepms(delay); + cs_writelock(__func__, &clientlist_lock); + client->cwlastresptime += delay; + snprintf(sreason, sizeof(sreason)-1, " (%d ms penalty delay)", delay); + break; + case 4: // hidecards + if(client->account->acosc_penalty_active == 3) + { + cs_log("[maxecms] ACoSC for Client: %s ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 4(%s) hidecards - hidecards to the client for %i sec", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2, penalty_duration); + client->start_hidecards = 1; + } + break; + default: // logging + if(client->account->acosc_penalty_active == 1) + { cs_log("[zaplist] ACoSC for Client: %s max_activ_sids reached: %i:%i(%s) penalty: 0(%s) only logging", username(client), active_sid_count, max_active_sids, info1, info2); } + if(client->account->acosc_penalty_active == 2) + { cs_log("[zaplist] ACoSC for Client: %s zap_limit reached: %i:%i(%s) penalty: 0(%s) only logging", username(client), client->account->acosc_user_zap_count, zap_limit, info5, info2); } + if(client->account->acosc_penalty_active == 3) + { cs_log("[maxecms] ACoSC for Client: %s max_ecms_per_minute reached: ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 0(%s) only logging", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2); } + break; + } + client->account->acosc_user_zap_count = 0; // we got already a penalty + client->account->acosc_penalty_active = 3; + client->account->acosc_penalty_active = 4; + } + } + cs_writeunlock(__func__, &clientlist_lock); + } +#endif + + if(cfg.double_check && er->rc <= E_CACHE2 && er->selected_reader && is_double_check_caid(er, &cfg.double_check_caid)) + { + if(er->checked == 0) // First CW, save it and wait for next one + { + er->checked = 1; + er->origin_reader = er->selected_reader; + memcpy(er->cw_checked, er->cw, sizeof(er->cw)); + cs_log("DOUBLE CHECK FIRST CW by %s idx %d cpti %d", er->origin_reader->label, er->idx, er->msgid); + } + else if(er->origin_reader != er->selected_reader) // Second (or third and so on) cw. We have to compare + { + if(memcmp(er->cw_checked, er->cw, sizeof(er->cw)) == 0) + { + er->checked++; + cs_log("DOUBLE CHECKED! %d. CW by %s idx %d cpti %d", er->checked, er->selected_reader->label, er->idx, er->msgid); + } + else + { + cs_log("DOUBLE CHECKED NONMATCHING! %d. CW by %s idx %d cpti %d", er->checked, er->selected_reader->label, er->idx, er->msgid); + } + } + if(er->checked < 2) // less as two same cw? mark as pending! + { + er->rc = E_UNHANDLED; + goto ESC; + } + } + + ac_chk(client, er, 1); + int32_t is_fake = 0; + if(er->rc == E_FAKE) + { + is_fake = 1; + er->rc = E_FOUND; + } + + get_module(client)->send_dcw(client, er); + + add_cascade_data(client, er); + + if(is_fake) + { er->rc = E_FAKE; } + +#ifdef CS_ANTICASC + cs_writelock(__func__, &clientlist_lock); + if(client->start_hidecards) + { + client->start_hidecards = 0; + add_job(client, ACTION_CLIENT_HIDECARDS, NULL, 0); + } + cs_writeunlock(__func__, &clientlist_lock); +#endif + + if(!(er->rc == E_SLEEPING && client->cwlastresptime == 0)) + { + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + if(er->reader_avail == 1 || er->stage == 0) + { + cs_log("%s (%s): %s (%d ms)%s%s%s%s", usrname, buf, er->rcEx ? erEx : stxt[er->rc], + client->cwlastresptime, sby, schaninfo, sreason, scwcinfo); + } + else + { + cs_log("%s (%s): %s (%d ms)%s (%c/%d/%d/%d)%s%s%s%s", usrname, buf, er->rcEx ? erEx : stxt[er->rc], + client->cwlastresptime, sby, stageTxt[er->stage], er->reader_requested, + (er->reader_count + er->fallback_reader_count), er->reader_avail, schaninfo, + srealecmtime, sreason, scwcinfo); + } + } + + cs_log_dump_dbg(D_ATR, er->cw, 16, "cw:"); + led_status_cw_not_found(er); + +ESC: + + return 0; +} + +/* + * write_ecm_request(): + */ +static int32_t write_ecm_request(struct s_reader *rdr, ECM_REQUEST *er) +{ + add_job(rdr->client, ACTION_READER_ECM_REQUEST, (void *)er, 0); + return 1; +} + +/** + * sends the ecm request to the readers + * ECM_REQUEST er : the ecm + * er->stage: 0 = no reader asked yet + * 2 = ask only local reader (skipped without preferlocalcards) + * 3 = ask any non fallback reader + * 4 = ask fallback reader + **/ +void request_cw_from_readers(ECM_REQUEST *er, uint8_t stop_stage) +{ + struct s_ecm_answer *ea; + int8_t sent = 0; + + if(er->stage >= 4) { return; } + + while(1) + { + if(stop_stage && er->stage >= stop_stage) { return; } + + er->stage++; + +#ifdef CS_CACHEEX + if(er->stage == 1 && er->preferlocalcards==2) + { er->stage++; } +#else + if(er->stage == 1) + { er->stage++; } +#endif + + if(er->stage == 2 && !er->preferlocalcards) + { er->stage++; } + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + switch(er->stage) + { +#ifdef CS_CACHEEX + case 1: + { + // Cache-Exchange + if((ea->status & REQUEST_SENT) || + (ea->status & (READER_ACTIVE | READER_FALLBACK | READER_LOCAL)) != (READER_ACTIVE | READER_LOCAL)) + { continue; } + break; + } +#endif + case 2: + { + // only local reader + if((ea->status & REQUEST_SENT) || + (ea->status & (READER_ACTIVE | READER_FALLBACK | READER_LOCAL)) != (READER_ACTIVE | READER_LOCAL)) + { continue; } + break; + } + case 3: + { + // any non fallback reader not asked yet + if((ea->status & REQUEST_SENT) || + (ea->status & (READER_ACTIVE | READER_FALLBACK)) != READER_ACTIVE) + { continue; } + break; + } + default: + { + // only fallbacks + if((ea->status & REQUEST_SENT) || + (ea->status & (READER_ACTIVE | READER_FALLBACK)) != (READER_ACTIVE | READER_FALLBACK)) + { continue; } + break; + } + } + + struct s_reader *rdr = ea->reader; +#ifdef WITH_DEBUG + if (cs_dblevel & (D_TRACE | D_CSP)) + { + char ecmd5[17 * 3]; + cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); + cs_log_dbg(D_TRACE | D_CSP, "request_cw stage=%d to reader %s ecm hash=%s", er->stage, rdr ? rdr->label : "", ecmd5); + } +#endif + // maxparallel: check if reader has capacity (slot is reserved later when CW found) + if(rdr->maxparallel > 0 && !reader_has_capacity(rdr, er->srvid, er->client)) + { + cs_log_dbg(D_LB, "reader %s skipped for %s srvid %04X (maxparallel %d reached)", + rdr->label, check_client(er->client) ? er->client->account->usr : "-", + er->srvid, rdr->maxparallel); + continue; + } + + ea->status |= REQUEST_SENT; + cs_ftime(&ea->time_request_sent); + + er->reader_requested++; + + write_ecm_request(ea->reader, er); + + // set sent=1 only if reader is active/connected. If not, switch to next stage! + if(!sent && rdr) + { + struct s_client *rcl = rdr->client; + if(check_client(rcl)) + { + if(rcl->typ == 'r' && rdr->card_status == CARD_INSERTED) + { sent = 1; } + else if(rcl->typ == 'p' && (rdr->card_status == CARD_INSERTED || rdr->tcp_connected)) + { sent = 1; } + } + } + + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_request] reader %s --> SENT %d", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, rdr ? ea->reader->label : "-", sent); + } + + if(sent || er->stage >= 4) + { break; } + } +} + +void add_cache_from_reader(ECM_REQUEST *er, struct s_reader *rdr, uint32_t csp_hash, uint8_t *ecmd5, uint8_t *cw, int16_t caid, int32_t prid, int16_t srvid +#ifdef CS_CACHEEX_AIO + , int32_t ecm_time +#endif +) +{ + ECM_REQUEST *ecm; + if (cs_malloc(&ecm, sizeof(ECM_REQUEST))) + { + memset(ecm, 0, sizeof(ECM_REQUEST)); + cs_ftime(&ecm->tps); + + ecm->cwc_cycletime = er->cwc_cycletime; + ecm->cwc_next_cw_cycle = er->cwc_next_cw_cycle; + memcpy(ecm->ecm, er->ecm, sizeof(ecm->ecm)); // ecm[0] is pushed to cacheexclients so we need a copy from it + ecm->caid = caid; + ecm->prid = prid; + ecm->srvid = srvid; + memcpy(ecm->ecmd5, ecmd5, CS_ECMSTORESIZE); + ecm->csp_hash = csp_hash; + ecm->rc = E_FOUND; + memcpy(ecm->cw, cw, sizeof(ecm->cw)); + ecm->grp = rdr->grp; + ecm->selected_reader = rdr; + ecm->from_csp = er->from_csp; // Propagacja flagi from_csp +#ifdef CS_CACHEEX_AIO + ecm->ecm_time = ecm_time; + ecm->localgenerated = er->localgenerated; +#endif +#ifdef CS_CACHEEX + if(rdr && cacheex_reader(rdr)) + { ecm->cacheex_src = rdr->client; } //so adds hits to reader +#endif + + add_cache(ecm); //add cw to cache + +#ifdef CS_CACHEEX + cs_writelock(__func__, &ecm_pushed_deleted_lock); + ecm->next = ecm_pushed_deleted; + ecm_pushed_deleted = ecm; + cs_writeunlock(__func__, &ecm_pushed_deleted_lock); +#else + NULLFREE(ecm); +#endif + } +} + +void chk_dcw(struct s_ecm_answer *ea) +{ + if(!ea || !ea->er) + { return; } + + ECM_REQUEST *ert = ea->er; + struct s_ecm_answer *ea_list; + struct s_reader *eardr = ea->reader; + if(!ert || !eardr) + { return; } + + // ecm request already answered! + if(ert->rc < E_99) + { +#ifdef CS_CACHEEX + if(ea && ert->rc < E_NOTFOUND && ea->rc < E_NOTFOUND && memcmp(ea->cw, ert->cw, sizeof(ert->cw)) != 0) + { + char cw1[16 * 3 + 2], cw2[16 * 3 + 2]; +#ifdef WITH_DEBUG + if(cs_dblevel & D_TRACE) + { + cs_hexdump(0, ea->cw, 16, cw1, sizeof(cw1)); + cs_hexdump(0, ert->cw, 16, cw2, sizeof(cw2)); + } +#endif + char ip1[20] = "", ip2[20] = ""; + if(ea->reader && check_client(ea->reader->client)) { cs_strncpy(ip1, cs_inet_ntoa(ea->reader->client->ip), sizeof(ip1)); } + if(ert->cacheex_src) { cs_strncpy(ip2, cs_inet_ntoa(ert->cacheex_src->ip), sizeof(ip2)); } + else if(ert->selected_reader && check_client(ert->selected_reader->client)) { cs_strncpy(ip2, cs_inet_ntoa(ert->selected_reader->client->ip), sizeof(ip2)); } + + ECM_REQUEST *er = ert; + debug_ecm(D_TRACE, "WARNING2: Different CWs %s from %s(%s)<>%s(%s): %s<>%s", buf, + username(ea->reader ? ea->reader->client : ert->client), ip1, + er->cacheex_src ? username(er->cacheex_src) : (ert->selected_reader ? ert->selected_reader->label : "unknown/csp"), ip2, + cw1, cw2); + } +#endif + + return; + } + + // Logika głosowania CW + if (cfg.cwvote_enabled && is_cwvote_caid(ert) && ert->rc == E_UNHANDLED) + { + if (cfg.cwvote_log_enabled) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [chk_dcw] CW Vote enabled and CAID matches, attempting to decide.", + (check_client(ert->client) ? ert->client->account->usr : "-"), ert->caid, ert->prid, ert->srvid); + } + int vote_result = cw_vote_decide(ert); + if (vote_result == 1) // Znaleziono zwycięzcę + { + ert->rc = E_FOUND; + ert->rcEx = 0; + send_dcw(ert->client, ert); + return; // ECM obsłużony przez głosowanie + } + // Jeśli vote_result wynosi 0 (brak wyraźnego zwycięzcy) lub -1 (za mało głosów), + // kontynuuj z istniejącą logiką, aby umożliwić więcej czytników/przekroczenie limitu czasu. + } + +#ifdef CS_CACHEEX + /* jeśli odpowiedź od czytnika cacheex-1, nie wysyłaj odpowiedzi do klienta! + * wątek check_cache sprawdzi licznik i wyśle odpowiedź do klienta! + * W każdym razie, powinniśmy sprawdzić, czy musimy przejść do innego etapu (>1) + */ + + if(eardr && cacheex_reader(eardr)) + { + // if wait_time, and not wait_time expired and wait_time due to hitcache(or awtime>0), + // we have to wait cacheex timeout before call other readers (stage>1) + if(cacheex_reader(eardr) && !ert->cacheex_wait_time_expired && ert->cacheex_hitcache) + { return; } + + int8_t cacheex_left = 0; + uint8_t has_cacheex = 0; + if(ert->stage == 1) + { + for(ea_list = ert->matching_rdr; ea_list; ea_list = ea_list->next) + { + cs_readlock(__func__, &ea_list->ecmanswer_lock); + if(((ea_list->status & (READER_CACHEEX | READER_FALLBACK | READER_ACTIVE))) == (READER_CACHEEX | READER_ACTIVE)) + { has_cacheex = 1; } + if((!(ea_list->status & READER_FALLBACK) && ((ea_list->status & (REQUEST_SENT | REQUEST_ANSWERED | READER_CACHEEX | READER_ACTIVE)) == (REQUEST_SENT | READER_CACHEEX | READER_ACTIVE))) || ea_list->rc < E_NOTFOUND) + { cacheex_left++; } + cs_readunlock(__func__, &ea_list->ecmanswer_lock); + } + + if(has_cacheex && !cacheex_left) { request_cw_from_readers(ert, 0); } + } + + return; + } +#endif + + int32_t reader_left = 0, local_left = 0, reader_not_flb_left = 0, has_not_fallback = 0, has_local = 0; + ert->selected_reader = eardr; + + switch(ea->rc) + { + case E_FOUND: + // maxparallel: register this service on the winning reader + if(eardr->maxparallel > 0) + { reader_register_service(eardr, ert->srvid, ert->client); } + + memcpy(ert->cw, ea->cw, 16); + ert->cw_ex = ea->cw_ex; + ert->rcEx = 0; + ert->rc = ea->rc; + ert->grp |= eardr->grp; + cs_strncpy(ert->msglog, ea->msglog, sizeof(ert->msglog)); +#ifdef HAVE_DVBAPI + ert->adapter_index = ea->er->adapter_index; +#endif + break; + + case E_INVALID: + case E_NOTFOUND: + { + // check if there are other readers to ask, and if not send NOT_FOUND to client + ert->rcEx = ea->rcEx; + cs_strncpy(ert->msglog, ea->msglog, sizeof(ert->msglog)); + + for(ea_list = ert->matching_rdr; ea_list; ea_list = ea_list->next) + { + cs_readlock(__func__, &ea_list->ecmanswer_lock); + + if((!(ea_list->status & READER_FALLBACK) && ((ea_list->status & (REQUEST_SENT | REQUEST_ANSWERED | READER_LOCAL | READER_ACTIVE)) == (REQUEST_SENT | READER_LOCAL | READER_ACTIVE))) || ea_list->rc < E_NOTFOUND) + { local_left++; } + + if((!(ea_list->status & READER_FALLBACK) && ((ea_list->status & (REQUEST_SENT | REQUEST_ANSWERED | READER_ACTIVE)) == (REQUEST_SENT | READER_ACTIVE))) || ea_list->rc < E_NOTFOUND) + { reader_not_flb_left++; } + + if(((ea_list->status & (REQUEST_ANSWERED | READER_ACTIVE)) == (READER_ACTIVE)) || ea_list->rc < E_NOTFOUND) + { reader_left++; } + + if(((ea_list->status & (READER_FALLBACK | READER_ACTIVE))) == (READER_ACTIVE)) + { has_not_fallback = 1; } + if(((ea_list->status & (READER_LOCAL | READER_FALLBACK | READER_ACTIVE))) == (READER_LOCAL | READER_ACTIVE)) + { has_local = 1; } + + cs_readunlock(__func__, &ea_list->ecmanswer_lock); + } + + switch(ert->stage) + { + case 2: // only local reader (used only if preferlocalcards=1) + { + if(has_local && !local_left) { request_cw_from_readers(ert, 0); } + break; + } + case 3: + { + // any fallback reader not asked yet + if(has_not_fallback && !reader_not_flb_left) { request_cw_from_readers(ert, 0); } + break; + } + } + + if(!reader_left // no more matching reader +#ifdef CS_CACHEEX + && !cfg.wait_until_ctimeout +#endif + ) + { ert->rc = E_NOTFOUND; } // so we set the return code + + break; + } + + case E_TIMEOUT: // if timeout, we have to send timeout to client: this is done by ecm_timeout callback + return; + break; + + case E_UNHANDLED: + return; + break; + + default: + cs_log("unexpected ecm answer rc=%d.", ea->rc); + return; + break; + } + + if(ert->rc < E_99) + send_dcw(ert->client, ert); +} + +uint32_t chk_provid(uint8_t *ecm, uint16_t caid) +{ + int32_t i, len, descriptor_length = 0; + uint32_t provid = 0; + + switch(caid >> 8) + { + case 0x01: // seca + provid = b2i(2, ecm + 3); + break; + + case 0x05: // viaccess + i = (ecm[4] == 0xD2) ? ecm[5] + 2 : 0; // skip d2 nano + if((ecm[5 + i] == 3) && ((ecm[4 + i] == 0x90) || (ecm[4 + i] == 0x40))) + { provid = (b2i(3, ecm + 6 + i) & 0xFFFFF0); } + + i = (ecm[6] == 0xD2) ? ecm[7] + 2 : 0; // skip d2 nano long ecm + if((ecm[7 + i] == 7) && ((ecm[6 + i] == 0x90) || (ecm[6 + i] == 0x40))) + { provid = (b2i(3, ecm + 8 + i) & 0xFFFFF0); } + break; + + case 0x0D: // cryptoworks + len = (((ecm[1] & 0xf) << 8) | ecm[2]) + 3; + for(i = 8; i < len; i += descriptor_length + 2) + { + descriptor_length = ecm[i + 1]; + if(ecm[i] == 0x83) + { + provid = (uint32_t)ecm[i + 2] & 0xFE; + break; + } + } + break; + + case 0x18: // nagra2 + if (caid == 0x1801) // more safety + provid = b2i(2, ecm + 5); + break; + } + + return provid; +} + +void update_chid(ECM_REQUEST *er) +{ + er->chid = get_subid(er); +} + + + +int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, uint8_t rcEx, uint8_t *cw, char *msglog, uint16_t used_cardtier, EXTENDED_CW* cw_ex) +{ + if(!reader || !er || !er->tps.time) { return 0; } + + // drop too late answers, to avoid seg fault --> only answer until tps.time+((cfg.ctimeout+500)/1000+1) is accepted + time_t timeout = time(NULL) - ((cfg.ctimeout + 500) / 1000 + 1); + + + if(er->tps.time < timeout) // < and NOT <= + { return 0; } + + struct timeb now; + cs_ftime(&now); + +#ifdef CS_CACHEEX_AIO + uint8_t dontsetAnswered = 0; +#endif + uint8_t dontwriteStats = 0; + + if(er && er->parent) + { + // parent is only set on reader->client->ecmtask[], but we want original er + ECM_REQUEST *er_reader_cp = er; + er = er->parent; // Now er is "original" ecm, before it was the reader-copy + er_reader_cp->rc = rc; + er_reader_cp->idx = 0; +#ifdef CS_CACHEEX_AIO + er->localgenerated = er_reader_cp->localgenerated; +#endif + + timeout = time(NULL) - ((cfg.ctimeout + 500) / 1000 + 1); + if(er->tps.time < timeout) + { return 0; } + } + +#ifdef CS_CACHEEX_AIO + if(rc < E_NOTFOUND && !er->localgenerated && (reader->cacheex.localgenerated_only_in || chk_lg_only(er, &reader->cacheex.lg_only_in_tab)) && !chk_srvid_localgenerated_only_exception(er)) + { + cs_log_dbg(D_CACHEEX, "reader: %s !er->localgenerated - rc: E_NOTFOUND set, no stats written for reader", reader ? reader->label : "-"); + rc = E_NOTFOUND; + dontsetAnswered = 1; + dontwriteStats = 1; + } +#endif + + struct s_ecm_answer *ea = get_ecm_answer(reader, er); + if(!ea) { return 0; } + + cs_writelock(__func__, &ea->ecmanswer_lock); + + if((ea->status & REQUEST_ANSWERED)) + { + cs_log_dbg(D_READER, "Reader %s already answer, skip this ecm answer!", reader ? reader->label : "-"); + cs_writeunlock(__func__, &ea->ecmanswer_lock); + return 0; + } + + // Special checks for rc + // Skip check for BISS1 - cw could be zero but still catch cw=0 by anticascading + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + + // bad/wrong chksum/ecm + if(rc == E_NOTFOUND && rcEx == E2_WRONG_CHKSUM) + { + cs_log_dbg(D_READER, "Ai_vote: FAKE DCW DETECTED from reader %s (bad/wrong checksum)", reader ? reader->label : "-"); + rc = E_INVALID; + rcEx = E2_WRONG_CHKSUM; + er->stage = 5; + + // dont write stats for bad/wrong chksum/ecm + dontwriteStats = 1; + + // set all other matching_readers => inactive to skip them and dont spread the bad ecm + struct s_ecm_answer *ea_list; + for(ea_list = er->matching_rdr; ea_list; ea_list = ea_list->next) + { + ea_list->status &= ~(READER_ACTIVE | READER_FALLBACK); + } + } + + if(rc < E_NOTFOUND && cw && chk_is_null_CW(cw) && !caid_is_biss(er->caid)) + { + rc = E_NOTFOUND; + cs_log_dbg(D_TRACE | D_LB, "Ai_vote: FAKE DCW DETECTED from reader %s (null CW)", reader ? reader->label : "-"); + } + + if(rc < E_NOTFOUND && cw && !chk_halfCW(er,cw)) + { + rc = E_NOTFOUND; + cs_log_dbg(D_TRACE | D_LB, "Ai_vote: FAKE DCW DETECTED from reader %s (wrong swapped NDS CW)", reader ? reader->label : "-"); + } + + if(reader && cw && rc < E_NOTFOUND) + { + if(!cfg.disablecrccws && !reader->disablecrccws) + { + if(!chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for) && !chk_if_ignore_checksum(er, &reader->disablecrccws_only_for)) + { + uint8_t i, c; + for(i = 0; i < 16; i += 4) + { + c = ((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff); + + if(cw[i + 3] != c) + { + uint8_t nano = 0x00; + if(er->caid == 0x100 && er->ecm[5] > 0x00) + { + nano = er->ecm[5]; // seca nano protection + } + + if(reader->dropbadcws && !nano) // only drop controlword if no cw encryption is applied + { + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + break; + } + else + { + if(!nano) // only fix checksum if no cw encryption is applied (nano = 0) + { + cs_log_dbg(D_TRACE, "notice: changed dcw checksum byte cw[%i] from %02x to %02x", i + 3, cw[i + 3], c); + cw[i + 3] = c; + } + else + { + if(i == 12) // there are servers delivering correct controlwords but with failing last cw checksum (on purpose?!) + { + cs_log_dbg(D_TRACE,"NANO%02d: BAD PEER DETECTED, oscam has fixed the last cw crc that wasn't matching!", nano); + cw[i + 3] = c; // fix the last controlword + } + else + { + cs_log_dbg(D_TRACE,"NANO%02d: not fixing the crc of this cw since its still encrypted!", nano); + break; // crc failed so stop! + } + } + } + } + } + } + else + { + cs_log_dbg(D_TRACE, "notice: CW checksum check disabled for %04X:%06X", er->caid, er->prid); + } + } + else + { + cs_log_dbg(D_TRACE, "notice: CW checksum check disabled"); + } + + if(chk_if_ignore_checksum(er, &reader->disablecrccws_only_for) && caid_is_videoguard(er->caid) +#ifdef CS_CACHEEX_AIO + && !chk_srvid_disablecrccws_only_for_exception(er) +#endif + ) + { + uint8_t k, csum; + uint8_t hit = 0; + uint8_t oe = checkCWpart(cw, 0) ? 0 : 8; + for(k = 0; k < 8; k += 4) + { + csum = ((cw[k + oe] + cw[k + oe + 1] + cw[k + oe + 2]) & 0xff); + if(cw[k + oe + 3] == csum) + { + hit++; + } + } + if(hit > 1) + { + char ecmd5s[17 * 3]; + cs_hexdump(0, er->ecmd5, 16, ecmd5s, sizeof(ecmd5s)); + if(reader->dropbadcws) + { + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + cs_log("Probably got bad CW from reader: %s, caid %04X, srvid %04X (%s) - dropping CW, lg: %i", reader->label, er->caid, er->srvid, ecmd5s +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + , 0); +#endif + } + else + { + cs_log("Probably got bad CW from reader: %s, caid %04X, srvid %04X (%s), lg: %i", reader->label, er->caid, er->srvid, ecmd5s +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + , 0); +#endif + } + } + } + + } + +#ifdef CW_CYCLE_CHECK + uint8_t cwc_ct = er->cwc_cycletime > 0 ? er->cwc_cycletime : 0; + uint8_t cwc_ncwc = er->cwc_next_cw_cycle < 2 ? er->cwc_next_cw_cycle : 2; + + if(!checkcwcycle(er->client, er, reader, cw, rc, cwc_ct, cwc_ncwc)) + { + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + + cs_log_dbg(D_CACHEEX | D_CWC | D_LB, + "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck FAILED → DROP CW from reader: %s", + (er->client ? er->client->account->usr : "-"), + er->caid, + er->srvid, + reader ? reader->label : "-"); + } + else + { + cs_log_dbg(D_CACHEEX | D_CWC | D_LB, + "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck PASSED → CW allowed from reader: %s", + (er->client ? er->client->account->usr : "-"), + er->caid, + er->srvid, + reader ? reader->label : "-"); + } +#endif + + //END -- SPECIAL CHECKs for rc + +#ifdef CS_CACHEEX_AIO + if(!dontsetAnswered) + { +#endif + ea->status |= REQUEST_ANSWERED; +#ifdef CS_CACHEEX_AIO + } +#endif + + // Update reader stats BEFORE vote logic (with original rc), because after vote ea->rc is set to E_UNHANDLED + if(!dontwriteStats && rc < E_NOTFOUND) + { + ea->ecm_time = comp_timeb(&now, &ea->time_request_sent); + if(ea->ecm_time < 1) { ea->ecm_time = 1; } + send_reader_stat(reader, er, ea, rc, ea->ecm_time); + } + else + { + ea->ecm_time = comp_timeb(&now, &ea->time_request_sent); + if(ea->ecm_time < 1) { ea->ecm_time = 1; } + } + + // Update reader ECM counters BEFORE vote logic (with original rc) + // These counters (ecmsok, ecmsnok, etc.) are used for webif statistics + if(!ea->is_pending && rc < E_NOTFOUND) + { + reader->ecmsok++; + reader->webif_ecmsok++; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + reader->ecmsoklg++; +#endif +#ifdef CS_CACHEEX + struct s_client *eacl = reader->client; + if(cacheex_reader(reader) && check_client(eacl)) + { + eacl->cwcacheexgot++; + cacheex_add_stats(eacl, ea->er->caid, ea->er->srvid, ea->er->prid, 1 +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + ); +#endif + first_client->cwcacheexgot++; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + eacl->cwcacheexgotlg++; + first_client->cwcacheexgotlg++; + } +#endif + } +#endif + } + + // Update reader ECM counters for E_NOTFOUND BEFORE vote logic + if(!ea->is_pending && rc == E_NOTFOUND) + { + reader->ecmsnok++; + reader->webif_ecmsnok++; + if(reader->ecmnotfoundlimit && reader->ecmsnok >= reader->ecmnotfoundlimit) + { + rdr_log(reader, "ECM not found limit reached %u. Restarting the reader.", + reader->ecmsnok); + reader->ecmsnok = 0; // Reset the variable + reader->ecmshealthnok = 0; // Reset the variable + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + } + + // Update reader ECM counters for E_TIMEOUT BEFORE vote logic + if(!ea->is_pending && rc == E_TIMEOUT) + { +#ifdef WITH_LB + STAT_QUERY q; + readerinfofix_get_stat_query(er, &q); + READER_STAT *s; + s = readerinfofix_get_add_stat(reader, &q); + if (s) + { + cs_log_dbg(D_LB, "inc fail {client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer] reader %s rc %d, ecm time %d ms", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, reader ? reader->label : "-", rc, ea->ecm_time); + readerinfofix_inc_fail(s); // now increase fail factor for unhandled timeouts + } +#endif + reader->ecmstout++; // now append timeouts to the readerinfo timeout count + reader->webif_ecmstout++; + } + + // If a valid CW is found, and CW Vote is enabled for this CAID, add it to the vote pool and mark this answer as unhandled for now + if(cfg.cwvote_enabled && is_cwvote_caid(er) && rc < E_NOTFOUND && cw && !chk_is_null_CW(cw) && !caid_is_biss(er->caid)) + { + if (cfg.cwvote_log_enabled) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer] CW Vote enabled, adding CW to vote pool from reader %s.", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, reader->label); + } + cw_vote_add(er, cw, reader); // Add to vote pool + ea->rc = E_UNHANDLED; // Mark as unhandled, waiting for vote decision + } + else + { + ea->rc = rc; // For non-CW answers or invalid CWs, set the actual RC + } + + ea->rcEx = rcEx; + if(cw) { memcpy(ea->cw, cw, 16); } // Store the CW in ea->cw for voting + if(msglog) { memcpy(ea->msglog, msglog, MSGLOGSIZE); } + ea->tier = used_cardtier; + if(cw_ex) + { + ea->cw_ex = *cw_ex; + } + + cs_writeunlock(__func__, &ea->ecmanswer_lock); + + struct timeb tpe; + cs_ftime(&tpe); + int32_t ntime = comp_timeb(&tpe, &er->tps); + if(ntime < 1) { ntime = 1; } + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer] reader %s rc %d, ecm time %d ms (%d ms)", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, reader ? reader->label : "-", rc, ea->ecm_time, ntime); + + // send ea for ecm request + int32_t res = 0; + struct s_client *cl = er->client; + if(check_client(cl)) + { + res = 1; + add_job(er->client, ACTION_ECM_ANSWER_READER, ea, 0); // chk_dcw + } + + // distribute ea for pendings + if(ea->pending) // has pending ea + { distribute_ea(ea); } + + + if(!ea->is_pending) // not for pending ea - only once for ea + { + // cache update + // Skip check for BISS1 - cw could be indeed zero + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + if(ea && (ea->rc < E_NOTFOUND) && (!chk_is_null_CW(ea->cw) && !caid_is_biss(er->caid))) + { +#ifdef CS_CACHEEX_AIO + int32_t ecmtime = ea->ecm_time; + + if(er->cacheex_wait_time_expired && er->cacheex_wait_time) + ecmtime = ea->ecm_time + er->cacheex_wait_time; +#endif + add_cache_from_reader(er, reader, er->csp_hash, er->ecmd5, ea->cw, er->caid, er->prid, er->srvid +#ifdef CS_CACHEEX_AIO + , ecmtime); +#else + ); +#endif + } + + // reader checks +#ifdef WITH_DEBUG + if(cs_dblevel & D_TRACE) + { + char ecmd5[17 * 3]; + cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); + rdr_log_dbg(reader, D_TRACE, "ecm answer for ecm hash %s rc=%d", ecmd5, ea->rc); + } +#endif + // Update reader stats: + // Old reader ECM counter updates removed - now handled before vote logic + + // reader checks + if(reader->ecmsok != 0 || reader->ecmsnok != 0 || reader->ecmstout != 0) + { + reader->ecmshealthok = ((double) reader->ecmsok / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; +#ifdef CS_CACHEEX_AIO + reader->ecmshealthoklg = ((double) reader->ecmsoklg / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; +#endif + reader->ecmshealthnok = ((double) reader->ecmsnok / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; + reader->ecmshealthtout = ((double) reader->ecmstout / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; + } + + if(rc == E_FOUND && reader->resetcycle > 0) + { + reader->resetcounter++; + if(reader->resetcounter > reader->resetcycle) + { + reader->resetcounter = 0; + rdr_log(reader, "Resetting reader, resetcyle of %d ecms reached", reader->resetcycle); + reader->card_status = CARD_NEED_INIT; + cardreader_reset(cl); + } + } + } + + return res; +} + +// chid calculation from module stat to here +// to improve the quickfix concerning ecm chid info and extend it +// to all client requests wereby the chid is known in module stat + +uint32_t get_subid(ECM_REQUEST *er) +{ + if(!er->ecmlen) + { return 0; } + + uint32_t id = 0; + switch(er->caid >> 8) + { + case 0x01: // seca + id = b2i(2, er->ecm + 7); + break; + + case 0x05: // viaccess + id = b2i(2, er->ecm + 8); + break; + + case 0x06: // irdeto + id = b2i(2, er->ecm + 6); + break; + + case 0x09: // videoguard + id = b2i(2, er->ecm + 11); + break; + + case 0x4A: // DRE-Crypt, Bulcrypt, Tongfang and others? + if(!caid_is_bulcrypt(er->caid) && !caid_is_dre(er->caid)) + { id = b2i(2, er->ecm + 6); } + break; + } + return id; +} + +static void set_readers_counter(ECM_REQUEST *er) +{ + struct s_ecm_answer *ea; + + er->reader_count = 0; + er->fallback_reader_count = 0; + er->localreader_count = 0; + er->cacheex_reader_count = 0; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if(ea->status & READER_ACTIVE) + { + if(!(ea->status & READER_FALLBACK)) + { er->reader_count++; } + else + { er->fallback_reader_count++; } + + if(cacheex_reader(ea->reader)) + { er->cacheex_reader_count++; } + else if(is_localreader(ea->reader, er)) + { er->localreader_count++; } + } + } +} + +void write_ecm_answer_fromcache(struct s_write_from_cache *wfc) +{ + ECM_REQUEST *er = NULL; + ECM_REQUEST *ecm = NULL; + + er = wfc->er_new; + ecm = wfc->er_cache; + +#ifdef CS_CACHEEX_AIO + if(ecm->localgenerated || (ecm->cw_count > 0x0F000000)) + er->localgenerated = 1; +#endif + + int8_t rc_orig = er->rc; + + er->grp |= ecm->grp; // update group +#ifdef CS_CACHEEX + if(ecm->from_csp) { er->csp_answered = 1; } // update er as answered by csp (csp have no group) +#endif + + if(er->rc >= E_NOTFOUND || er->rc == E_UNHANDLED) + { + // CW Vote: If enabled and CAID matches, add to vote pool instead of sending DCW directly + if (cfg.cwvote_enabled && is_cwvote_caid(er) && !chk_is_null_CW(ecm->cw) && !caid_is_biss(er->caid)) + { + // Use cacheex_src or from_csp directly if selected_reader is NULL + // This fixes the issue where virtual readers are NULL + struct s_reader *source_rdr = NULL; + if (ecm->selected_reader) { + source_rdr = ecm->selected_reader; + } + + // Pass the actual cacheex source or csp info for voting + // Use ecm->cacheex_src or ecm->from_csp when virtual readers are not available + if (source_rdr || ecm->cacheex_src || ecm->from_csp) { + if (cfg.cwvote_log_enabled) { + const char *source_label = "unknown"; + if (source_rdr) { + source_label = source_rdr->label; + } else if (ecm->cacheex_src && check_client(ecm->cacheex_src) && ecm->cacheex_src->account) { + source_label = ecm->cacheex_src->account->usr; + } else if (ecm->from_csp) { + source_label = "CSP"; + } + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer_fromcache] CW Vote enabled, adding CW from cache (source: %s) to vote pool.", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, source_label); + } + // Always copy cacheex_src and from_csp so cw_vote_decide can use it + if (ecm->cacheex_src) { + er->cacheex_src = ecm->cacheex_src; + } + if (ecm->from_csp) { + er->from_csp = ecm->from_csp; + } + cw_vote_add(er, ecm->cw, source_rdr); + // Keep er->rc as E_UNHANDLED to allow voting to proceed + // Do NOT call send_dcw here. It will be called by cw_vote_decide or ecm_timeout. + } else { + // Fallback if no source could be determined for voting + if (cfg.cwvote_log_enabled) { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer_fromcache] CW Vote enabled, but source for cache entry could not be determined. Sending DCW directly.", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + } + send_dcw(er->client, er); + } + } + else if (rc_orig == E_UNHANDLED) // Original condition for sending DCW if not voting + { +#ifdef CS_CACHEEX + if(ecm->cacheex_src) // from cacheex or csp + { + er->rc = E_CACHEEX; + } + else +#endif + { er->rc=E_CACHE1; } // from normal readers + + memcpy(er->cw, ecm->cw, 16); + er->selected_reader = ecm->selected_reader; + er->cw_count = ecm->cw_count; + +#ifdef CS_CACHEEX + // here we should be sure cex client has not been freed! + if(ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill) + { + er->cacheex_src = ecm->cacheex_src; + er->cwc_cycletime = ecm->cwc_cycletime; + er->cwc_next_cw_cycle = ecm->cwc_next_cw_cycle; + } + else + { + er->cacheex_src = NULL; + } + + int8_t cacheex = check_client(er->client) && er->client->account ? er->client->account->cacheex.mode : 0; + if(cacheex == 1 && check_client(er->client)) + { + cacheex_add_stats(er->client, er->caid, er->srvid, er->prid, 0 +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + ); +#endif + er->client->cwcacheexpush++; + if(er->client->account) + { er->client->account->cwcacheexpush++; } + first_client->cwcacheexpush++; + +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + er->client->cwcacheexpushlg++; + first_client->cwcacheexpushlg++; + } +#endif + + } +#endif + +#ifdef CS_CACHEEX + if(cfg.delay && cacheex!=1) // No delay on cacheexchange mode 1 client! + { cs_sleepms(cfg.delay); } +#else + if(cfg.delay) + { cs_sleepms(cfg.delay); } +#endif + + if(rc_orig == E_UNHANDLED) + { + cs_log_dbg(D_LB,"{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer_fromcache] found cw in CACHE (count %d)!", (check_client(er->client)?er->client->account->usr:"-"),er->caid, er->prid, er->srvid, +#ifdef CS_CACHEEX_AIO + (er->cw_count > 0x0F000000) ? er->cw_count ^= 0x0F000000 : er->cw_count); +#else + er->cw_count); +#endif + send_dcw(er->client, er); + } + } + } +} + +#ifdef CS_CACHEEX_AIO +static bool ecm_cache_check(ECM_REQUEST *er) +{ + if(ecm_cache_init_done && cfg.ecm_cache_droptime > 0) + { + ECM_CACHE *ecm_cache = NULL; + SAFE_RWLOCK_WRLOCK(&ecm_cache_lock); + ecm_cache = find_hash_table(&ht_ecm_cache, &er->csp_hash, sizeof(uint32_t), &compare_csp_hash_ecmcache); + if(!ecm_cache) + { + // ecm_cache-size(count/memory) pre-check + if( + (cfg.ecm_cache_size && (cfg.ecm_cache_size > tommy_hashlin_count(&ht_ecm_cache))) + || (cfg.ecm_cache_memory && (cfg.ecm_cache_memory*1024*1024 > tommy_hashlin_memory_usage(&ht_ecm_cache))) + ) + { + if(cs_malloc(&ecm_cache, sizeof(ECM_CACHE))) + { + ecm_cache->csp_hash = er->csp_hash; + cs_ftime(&ecm_cache->first_recv_time); + cs_ftime(&ecm_cache->upd_time); + + tommy_hashlin_insert(&ht_ecm_cache, &ecm_cache->ht_node, ecm_cache, tommy_hash_u32(0, &er->csp_hash, sizeof(er->csp_hash))); + tommy_list_insert_tail(&ll_ecm_cache, &ecm_cache->ll_node, ecm_cache); + + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + return true; + } + else{ + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + cs_log("[ecm_cache] ERROR: NO added HASH to ecm_cache!!"); + return false; + } + } + else{ + // clean cache call; + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + ecm_cache_cleanup(true); + return false; + } + } + // ecm found + else{ + int64_t gone_diff = 0; + gone_diff = comp_timeb(&er->tps, &ecm_cache->first_recv_time); + cs_ftime(&ecm_cache->upd_time); + + if(gone_diff >= cfg.ecm_cache_droptime * 1000) + { + cs_log_dbg(D_CW_CACHE, "[ecm_cache] ECM drop, current ecm_cache_size: %i - ecm_cache-mem-size: %i MiB", count_hash_table(&ht_ecm_cache), (int)(tommy_hashlin_memory_usage(&ht_ecm_cache)/1024/1024)); + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + return false; + } + } + + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + return true; + } + else{ + cs_log_dbg(D_CW_CACHE,"[ecm_cache] ecm_cache_init_done %i cfg.ecm_cache_size: %"PRIu32" cfg.ecm_cache_memory %"PRIu32" MiB", ecm_cache_init_done, cfg.ecm_cache_size, cfg.ecm_cache_memory); + return true; + } +} +#endif + +void get_cw(struct s_client *client, ECM_REQUEST *er) +{ + // clienttimeout per CAID override + uint32_t client_timeout = cfg.ctimeout; + int32_t percaid_timeout = caidvaluetab_get_value(&cfg.ctimeouttab, er->caid, 0); + + if(percaid_timeout > 0) + { + client_timeout = percaid_timeout; + } + + er->client_timeout = client_timeout; + +#ifdef CS_CACHEEX_AIO + + + cacheex_update_hash(er); + if(!ecm_cache_check(er)) + { + er->rc = E_INVALID; + send_dcw(client, er); + free_ecm(er); + return; + } +#endif + cs_log_dump_dbg(D_ATR, er->ecm, er->ecmlen, "get cw for ecm:"); + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] NEW REQUEST!", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + increment_n_request(client); + + int32_t i, j, m; + time_t now = time((time_t *)0); + uint32_t line = 0; + uint16_t sct_len; + + er->client = client; + er->rc = E_UNHANDLED; // set default rc status to unhandled + er->cwc_next_cw_cycle = 2; // set it to: we dont know + + // user was on freetv or didn't request for some time + // so we reset lastswitch to get correct stats/webif display + if(now - client->lastecm > cfg.hideclient_to) { client->lastswitch = 0; } + client->lastecm = now; + + if(client == first_client || !client ->account || client->account == first_client->account) + { + // DVBApi+serial is allowed to request anonymous accounts: + int16_t listenertype = get_module(client)->listenertype; + if(listenertype != LIS_DVBAPI && listenertype != LIS_SERIAL) + { + er->rc = E_INVALID; + er->rcEx = E2_GLOBAL; + snprintf(er->msglog, sizeof(er->msglog), "invalid user account %s", username(client)); + } + } + + // ecmlen must be 0 (no ecm) or >2 (because SCT_LEN() needs at least 3 bytes) + if(er->ecmlen < 0 || er->ecmlen == 1 || er->ecmlen == 2) + { + er->rc = E_INVALID; + er->rcEx = E2_GLOBAL; + snprintf(er->msglog, sizeof(er->msglog), "ECM size %d invalid, ignored! client %s", er->ecmlen, username(client)); + } + + if(er->ecmlen > MAX_ECM_SIZE) + { + er->rc = E_INVALID; + er->rcEx = E2_GLOBAL; + snprintf(er->msglog, sizeof(er->msglog), "ECM size %d > Max ECM size %d, ignored! client %s", er->ecmlen, MAX_ECM_SIZE, username(client)); + } + + if(er->ecmlen > 2) + { + sct_len = SCT_LEN(er->ecm); + if(sct_len > er->ecmlen || sct_len < 4) + { + er->rc = E_INVALID; + er->rcEx = E2_GLOBAL; + snprintf(er->msglog, sizeof(er->msglog), "Real ECM size %d > ECM size %d, ignored! client %s", sct_len, er->ecmlen, username(client)); + } + er->ecmlen = sct_len; + } + + if(!client->grp) + { + er->rc = E_INVALID; + er->rcEx = E2_GROUP; + snprintf(er->msglog, sizeof(er->msglog), "invalid user group %s", username(client)); + } + + // add chid for all client requests as in module stat + update_chid(er); + + // betacrypt ecm with nagra header + if(chk_is_betatunnel_caid(er->caid) == 1 && (er->ecmlen == 0x89 || er->ecmlen == 0x4A) && er->ecm[3] == 0x07 && (er->ecm[4] == 0x84 || er->ecm[4] == 0x45)) + { + if(er->caid == 0x1702) + { + er->caid = 0x1833; + } + else + { + check_lb_auto_betatunnel_mode(er); + } + cs_log_dbg(D_TRACE, "Quickfix remap beta->nagra: 0x%X, 0x%X, 0x%X, 0x%X", er->caid, er->ecmlen, er->ecm[3], er->ecm[4]); + } + + // nagra ecm with betacrypt header 1801, 1833, 1834, 1835 + if(chk_is_betatunnel_caid(er->caid) == 2 && (er->ecmlen == 0x93 || er->ecmlen == 0x54) && er->ecm[13] == 0x07 && (er->ecm[14] == 0x84 || er->ecm[14] == 0x45)) + { + if(er->caid == 0x1833) + { + er->caid = 0x1702; + } + else + { + er->caid = 0x1722; + } + cs_log_dbg(D_TRACE, "Quickfix remap nagra->beta: 0x%X, 0x%X, 0x%X, 0x%X", er->caid, er->ecmlen, er->ecm[13], er->ecm[44]); + } + + // Ariva quickfix (invalid nagra provider) + if(((er->caid & 0xFF00) == 0x1800) && er->prid > 0x00FFFF) + { er->prid = 0; } + + // Check for invalid provider, extract provider out of ecm: + uint32_t prid = chk_provid(er->ecm, er->caid); + if(!er->prid) + { + er->prid = prid; + } + else + { + if(prid && prid != er->prid) + { + cs_log_dbg(D_TRACE, "provider fixed: %04X@%06X to %04X@%06X", er->caid, er->prid, er->caid, prid); + er->prid = prid; + } + } + +#ifdef MODULE_NEWCAMD + // Set providerid for newcamd clients if none is given + if(!er->prid && client->ncd_server) + { + int32_t pi = client->port_idx; + if(pi >= 0 && cfg.ncd_ptab.nports && cfg.ncd_ptab.nports >= pi && cfg.ncd_ptab.ports[pi].ncd) + { er->prid = cfg.ncd_ptab.ports[pi].ncd->ncd_ftab.filts[0].prids[0]; } + } +#endif + + // CAID not supported or found + if(!er->caid) + { + er->rc = E_INVALID; + er->rcEx = E2_CAID; + snprintf(er->msglog, MSGLOGSIZE, "CAID not supported or found"); + } + + // user expired + if(client->expirationdate && client->expirationdate < client->lastecm) + { er->rc = E_EXPDATE; } + + // out of timeframe + if(client->allowedtimeframe_set) + { + struct tm acttm; + localtime_r(&now, &acttm); + int32_t curday = acttm.tm_wday; + char *dest = strstr(weekdstr,"ALL"); + int32_t all_idx = (dest - weekdstr) / 3; + uint8_t allowed = 0; + + // checkout if current time is allowed in the current day + allowed = CHECK_BIT(client->allowedtimeframe[curday][acttm.tm_hour][acttm.tm_min / 30], (acttm.tm_min % 30)); + + // or checkout if current time is allowed for all days + allowed |= CHECK_BIT(client->allowedtimeframe[all_idx][acttm.tm_hour][acttm.tm_min / 30], (acttm.tm_min % 30)); + + if(!(allowed)) + { + er->rc = E_EXPDATE; + } + cs_log_dbg(D_TRACE, "Check Timeframe - result: %d, day:%s time: %02dH%02d, allowed: %s\n", er->rc, shortDay[curday], acttm.tm_hour, acttm.tm_min, allowed ? "true" : "false"); + } + + // user disabled + if(client->disabled != 0) + { + if(client->failban & BAN_DISABLED) + { + cs_add_violation(client, client->account->usr); + cs_disconnect_client(client); + } + er->rc = E_DISABLED; + } + + if(!chk_global_whitelist(er, &line)) + { + debug_ecm(D_TRACE, "whitelist filtered: %s (%s) line %d", username(client), buf, line); + er->rc = E_INVALID; + } + +#ifdef CS_CACHEEX + if(client->account && client->account->cacheex.mode == 2 && !client->account->cacheex.allow_request) + { + er->rc = E_INVALID; + snprintf(er->msglog, MSGLOGSIZE, "invalid request from cacheex-2 client"); + } +#endif + + // rc < 100 -> ecm error + if(er->rc >= E_UNHANDLED) + { + m = er->caid; + i = er->srvid; + + if(i != client->last_srvid || !client->lastswitch) + { + if(cfg.usrfileflag) + { cs_statistics(client); } + client->lastswitch = now; + } + + // user sleeping + if(client->tosleep && (now - client->lastswitch > client->tosleep)) + { + if(client->failban & BAN_SLEEPING) + { + cs_add_violation(client, client->account->usr); + cs_disconnect_client(client); + } + if(client->c35_sleepsend != 0) + { + er->rc = E_STOPPED; // send sleep command CMD08 {00 255} + } + else + { + er->rc = E_SLEEPING; + } + } + + client->last_srvid = i; + client->last_caid = m; + client->last_provid = er->prid; + + int32_t ecm_len = (((er->ecm[1] & 0x0F) << 8) | er->ecm[2]) + 3; + + for(j = 0; (j < 6) && (er->rc >= E_UNHANDLED); j++) + { + switch(j) + { + case 0: + // fake (uniq) + if(client->dup) + { er->rc = E_FAKE; } + break; + + case 1: + // invalid (caid) + if(!chk_bcaid(er, &client->ctab)) + { + er->rc = E_INVALID; + er->rcEx = E2_CAID; + snprintf(er->msglog, MSGLOGSIZE, "invalid caid 0x%04X", er->caid); + } + break; + + case 2: + // invalid (srvid) + // matching srvids (or 0000) specified in betatunnel will bypass this filter + if(!chk_srvid(client, er)) + { + if(!chk_on_btun(SRVID_ZERO, client, er)) + { + er->rc = E_INVALID; + snprintf(er->msglog, MSGLOGSIZE, "invalid SID"); + } + } + break; + + case 3: + // invalid (ufilters) + if(!chk_ufilters(er)) + { er->rc = E_INVALID; } + break; + + case 4: + // invalid (sfilter) + if(!chk_sfilter(er, &get_module(client)->ptab)) + { er->rc = E_INVALID; } + break; + + case 5: + // corrupt + if((i = er->ecmlen - ecm_len)) + { + if(i > 0) + { + cs_log_dbg(D_TRACE, "warning: ecm size adjusted from %d to %d", er->ecmlen, ecm_len); + er->ecmlen = ecm_len; + } + else + { er->rc = E_CORRUPT; } + } + break; + } + } + } + + // Check for odd/even byte + // Don't check for BISS1 and BISS2 mode 1/E or fake caid (ECM is fake for them) + // Don't check for BISS2 mode CA (ECM table is always 0x80) + if(!caid_is_biss(er->caid) && !caid_is_fake(er->caid) && get_odd_even(er) == 0) + { + cs_log_dbg(D_TRACE, "warning: ecm with null odd/even byte from %s", (check_client(er->client) ? er->client->account->usr : "-")); + er->rc = E_INVALID; + } + + // not continue, send rc to client + if(er->rc < E_UNHANDLED) + { + send_dcw(client, er); + free_ecm(er); + return; + } + + // CW Vote: If enabled and CAID matches, keep as E_UNHANDLED to allow voting + if (cfg.cwvote_enabled && is_cwvote_caid(er)) + { + if (cfg.cwvote_log_enabled) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] CW Vote enabled for this CAID, keeping ECM as E_UNHANDLED for voting. Logging enabled.", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + } + else + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] CW Vote enabled for this CAID, keeping ECM as E_UNHANDLED for voting. Logging disabled.", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + } + er->rc = E_UNHANDLED; // Ensure it's unhandled for voting + } + else if (er->rc < E_UNHANDLED) // If not for voting, and already an error, send it + { + send_dcw(client, er); + free_ecm(er); + return; + } + + +#ifdef CS_CACHEEX + int8_t cacheex = client->account ? client->account->cacheex.mode : 0; + er->from_cacheex1_client = 0; + if(cacheex == 1) {er->from_cacheex1_client = 1;} +#endif + + + // set preferlocalcards for this ecm request (actually, paramter + // is per user based, maybe in fiture it will be caid based too) + er->preferlocalcards = cfg.preferlocalcards; + if(client->account && client->account->preferlocalcards > -1) + { + er->preferlocalcards = client->account->preferlocalcards; + } + if(er->preferlocalcards <0 || er->preferlocalcards >2) {er->preferlocalcards=0;} + + + if(chk_is_betatunnel_caid(er->caid) && client->ttab.ttnum) + { + cs_log_dump_dbg(D_TRACE, er->ecm, 13, "betatunnel? ecmlen=%d", er->ecmlen); + cs_betatunnel(er); + } + + + // ignore ecm... + int32_t offset = 3; + + // ...and betacrypt header for cache md5 calculation + if(caid_is_betacrypt(er->caid)) + { offset = 13; } + + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + + // store ECM in cache + memcpy(er->ecmd5, MD5(er->ecm + offset, er->ecmlen - offset, md5tmp), CS_ECMSTORESIZE); + cacheex_update_hash(er); + ac_chk(client, er, 0); + + + //******** CHECK IF FOUND ECM IN CACHE + struct ecm_request_t *ecm = NULL; + ecm = check_cache(er, client); + if(ecm) // found in cache + { + cs_log_dbg(D_LB,"{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] cw found immediately in cache! ", (check_client(er->client)?er->client->account->usr:"-"),er->caid, er->prid, er->srvid); + + struct s_write_from_cache *wfc = NULL; + if(!cs_malloc(&wfc, sizeof(struct s_write_from_cache))) + { + NULLFREE(ecm); + free_ecm(er); + return; + } + + wfc->er_new = er; + wfc->er_cache = ecm; + write_ecm_answer_fromcache(wfc); + NULLFREE(wfc); + NULLFREE(ecm); + free_ecm(er); + + return; + } + +// zaplist ACoSC +#ifdef CS_ANTICASC + if(cfg.acosc_enabled) + { + cs_writelock(__func__, &clientlist_lock); + insert_zaplist(er, client); + cs_writeunlock(__func__, &clientlist_lock); + } +#endif + + er->reader_avail = 0; + er->readers = 0; + + struct s_ecm_answer *ea, *prv = NULL; + struct s_reader *rdr; + + cs_readlock(__func__, &readerlist_lock); + cs_readlock(__func__, &clientlist_lock); + + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + uint8_t is_fallback = chk_is_fixed_fallback(rdr, er); + int8_t match = matching_reader(er, rdr); + + if(!match) // if this reader does not match, check betatunnel for it + match = lb_check_auto_betatunnel(er, rdr); + + if(match) + { + er->reader_avail++; + +#ifdef CS_CACHEEX + if(cacheex == 1 && !cacheex_reader(rdr)) // ex1-cl only ask ex1-rdr + { continue; } +#endif + + if(!cs_malloc(&ea, sizeof(struct s_ecm_answer))) + { goto OUT; } + +#ifdef WITH_EXTENDED_CW + // Correct CSA mode is CBC - default to that instead + ea->cw_ex.algo_mode = CW_ALGO_MODE_CBC; +#endif + + er->readers++; + + ea->reader = rdr; + ea->er = er; + ea->rc = E_UNHANDLED; + if(prv) + { prv->next = ea; } + else + { er->matching_rdr = ea; } + prv = ea; + + ea->status = READER_ACTIVE; + if(cacheex_reader(rdr)) + { ea->status |= READER_CACHEEX; } + else if(is_localreader(rdr, er)) + { ea->status |= READER_LOCAL; } + + if(is_fallback && (!is_localreader(rdr, er) || (is_localreader(rdr, er) && !er->preferlocalcards))) + { ea->status |= READER_FALLBACK; } + + ea->pending = NULL; + ea->is_pending = false; + cs_lock_create(__func__, &ea->ecmanswer_lock, "ecmanswer_lock", 5000); + } + } + +OUT: + cs_readunlock(__func__, &clientlist_lock); + cs_readunlock(__func__, &readerlist_lock); + + lb_set_best_reader(er); + + // set reader_count and fallback_reader_count + set_readers_counter(er); + + // if preferlocalcards > 0, check if we have local readers selected: + // if not, switch to preferlocalcards = 0 for this ecm + if(er->preferlocalcards > 0) + { + if(er->localreader_count == 0) + { + er->preferlocalcards = 0; + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} NO local readers, set preferlocalcards = %d", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, er->preferlocalcards); + } + } + +#ifdef CS_CACHEEX + // WAIT_TIME + uint32_t cacheex_wait_time = 0; + uint32_t wait_time_no_hitcache = 0; + uint32_t wait_time_hitcache = 0; + + if(client->account && !client->account->no_wait_time +#ifdef CS_CACHEEX_AIO + && !chk_srvid_no_wait_time(er) +#endif + && er->preferlocalcards<2) + { + wait_time_no_hitcache = get_cacheex_wait_time(er,NULL); // NO check hitcache. Wait_time is dwtime, or, if 0, awtime. + wait_time_hitcache = get_cacheex_wait_time(er,client); // check hitcache for calculating wait_time! If hitcache wait_time is biggest value between dwtime and awtime, else it's awtime. + + if( + // If "normal" client and ex1-rdr>0, we cannot use hitcache for calculating wait_time because we cannot know if cw is available or not on ex1 server! + (cacheex != 1 && er->cacheex_reader_count) + || + /* Cw for ex1-cl comes from: INT. cache by "normal" readers (normal clients that ask normal readers), ex1-rdr and ex2-rdr and ex3-rdr. + * If readers, we have to wait cws generating by normal clients asking normal readers and answers by ex1-rdr (cannot use hitcache). + * If no readers, use hitcache for calculating wait_time. + */ + (cacheex == 1 && er->reader_avail) + ) + { cacheex_wait_time = wait_time_no_hitcache; } + else + { cacheex_wait_time = wait_time_hitcache; } + } + + cs_log_dbg(D_TRACE | D_CACHEEX, "[GET_CW] wait_time %d caid %04X prov %06X srvid %04X rc %d cacheex cl mode %d ex1rdr %d", cacheex_wait_time, er->caid, er->prid, er->srvid, er->rc, cacheex, er->cacheex_reader_count); + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] wait_time %d - client cacheex mode %d, reader avail for ecm %d, hitcache %d, preferlocalcards %d", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, cacheex_wait_time, cacheex == 1 ? 1 : 0, er->reader_avail, wait_time_hitcache ? 1 : 0, er->preferlocalcards); + // END WAIT_TIME calculation + + if(!cacheex_wait_time && (er->reader_count + er->fallback_reader_count) == 0) +#else + if((er->reader_count + er->fallback_reader_count) == 0) +#endif + { + er->rc = E_NOTFOUND; + if(!er->rcEx) + { er->rcEx = E2_GROUP; } + snprintf(er->msglog, MSGLOGSIZE, "no matching reader"); + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] NO Readers and NO wait_time... not_found! ", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + send_dcw(client, er); + free_ecm(er); + return; + } + + //insert it in ecmcwcache! + cs_writelock(__func__, &ecmcache_lock); + er->next = ecmcwcache; + ecmcwcache = er; + ecmcwcache_size++; + cs_writeunlock(__func__, &ecmcache_lock); + + er->rcEx = 0; +#ifdef CS_CACHEEX + er->cacheex_wait_time = 0; + er->cacheex_wait_time_expired = 1; + er->cacheex_hitcache = 0; + er->cacheex_mode1_delay = 0; + + if(cacheex_wait_time) // wait time for cacheex + { + er->cacheex_wait_time = cacheex_wait_time; + er->cacheex_wait_time_expired = 0; + er->cacheex_hitcache = wait_time_hitcache ? 1 : 0; // usefull only when cacheex mode 1 readers answers before wait_time and we have to decide if we have to wait until wait_time expires. + er->cacheex_mode1_delay = get_cacheex_mode1_delay(er); + + if(!er->cacheex_mode1_delay && er->cacheex_reader_count > 0) + { + request_cw_from_readers(er, 1); // setting stop_stage=1, we request only cacheex mode 1 readers. Others are requested at cacheex timeout! + } + } + else +#endif + request_cw_from_readers(er, 0); + +#ifdef WITH_DEBUG + if(D_CLIENTECM & cs_dblevel) + { + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_log_dump_dbg(D_CLIENTECM, er->ecm, er->ecmlen, "Client %s ECM dump %s", username(client), buf); + } +#endif + + cw_process_thread_wakeup(); +} + +int32_t ecmfmt(char *result, size_t size, uint16_t caid, uint16_t onid, uint32_t prid, uint16_t chid, uint16_t pid, + uint16_t srvid, uint16_t l, char *ecmd5hex, char *csphash, char *cw, uint16_t origin_peer, uint8_t distance, char *payload, char *tier) +{ + if(!cfg.ecmfmt) + { + if(tier && payload) + { + return snprintf(result, size, "%04X@%06X/%04X/%04X/%02X:%s:0F06%s:%s", caid, prid, chid, srvid, l, ecmd5hex, payload, tier); + } + else if(tier) + { + return snprintf(result, size, "%04X@%06X/%04X/%04X/%02X:%s:%s", caid, prid, chid, srvid, l, ecmd5hex, tier); + } + else if(payload) + { + return snprintf(result, size, "%04X@%06X/%04X/%04X/%02X:%s:0F06%s", caid, prid, chid, srvid, l, ecmd5hex, payload); + } + else + { + return snprintf(result, size, "%04X@%06X/%04X/%04X/%02X:%s", caid, prid, chid, srvid, l, ecmd5hex); + } + } + +#define ECMFMT_NUMBER 0 +#define ECMFMT_STRING 1 +#define ECMFMT_CHAR 2 + + uint8_t type = 0; + uint32_t ivalue = 0; + char *ifmt = NULL, *sfmt = NULL; + char *svalue = NULL, cvalue = '\0'; + uint8_t hide_if_zero = 0; + char *c; + uint32_t s = 0; + + for(c = cfg.ecmfmt; *c; c++) + { + if(*c == '0') + { + hide_if_zero = 1; + continue; + } + + sfmt = NULL; + + switch(*c) + { + case 't': + type = ECMFMT_STRING; + svalue = tier; + if(tier == NULL && !hide_if_zero) + { + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = 0; + } + break; + + case 'c': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = caid; + break; + + case 'o': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = onid; + break; + + case 'p': + type = ECMFMT_NUMBER; + ifmt = "%06X"; + ivalue = prid; + break; + + case 'i': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = chid; + break; + + case 'd': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = pid; + break; + + case 's': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = srvid; + break; + + case 'l': + type = ECMFMT_NUMBER; + ifmt = "%02X"; + ivalue = l; + break; + + case 'h': + type = ECMFMT_STRING; + svalue = ecmd5hex; + break; + + case 'e': + type = ECMFMT_STRING; + svalue = csphash; + break; + + case 'w': + type = ECMFMT_STRING; + svalue = cw; + break; + + case 'j': + type = ECMFMT_NUMBER; + ifmt = "%02X"; + ivalue = distance; + break; + + case 'g': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = origin_peer; + break; + + case '\\': + c++; + type = ECMFMT_CHAR; + cvalue = *c; + + if(cvalue == '\0') + { return s; } + break; + + case 'y': + type = ECMFMT_STRING; + svalue = payload; + sfmt = "0F06%.06s"; + if(payload == NULL && !hide_if_zero) + { + type = ECMFMT_NUMBER; + ifmt = "0F06%06X"; + ivalue = 0; + } + break; + + case 'Y': + type = ECMFMT_STRING; + svalue = payload; + sfmt = "0F06%s"; + if(payload == NULL && !hide_if_zero) + { + type = ECMFMT_NUMBER; + ifmt = "0F06%12X"; + ivalue = 0; + } + break; + + default: + type = ECMFMT_CHAR; + cvalue = *c; + break; + } + + if(hide_if_zero) + { + if(type == ECMFMT_NUMBER && ivalue == 0) + { + hide_if_zero = 0; + continue; + } + else if(type == ECMFMT_STRING && svalue == NULL) + { + hide_if_zero = 0; + continue; + } + } + + switch(type) + { + case ECMFMT_NUMBER: + s += snprintf(result + s, size - s, ifmt, ivalue); + break; + + case ECMFMT_STRING: + s += snprintf(result + s, size - s , sfmt != NULL ? sfmt : "%s", svalue); + break; + + case ECMFMT_CHAR: + if(size - s > 1) + { + result[s] = cvalue; + result[s+1] = '\0'; + s++; + } + break; + + default: + break; + } + } + + return s; +} + +int32_t format_ecm(ECM_REQUEST *ecm, char *result, size_t size) +{ + char ecmd5hex[(16*2)+1]; + char csphash[(4*2)+1] = { 0 }; + char cwhex[(16*2)+1]; + char *payload = NULL; + char *tier = NULL; +#ifdef READER_VIDEOGUARD + char payload_string[(6*2)+1]; + char tier_string[83]; + struct s_ecm_answer *ea; + + if(ecm->selected_reader && caid_is_videoguard(ecm->selected_reader->caid) && !is_network_reader(ecm->selected_reader)) + { + for(ea = ecm->matching_rdr; ea; ea = ea->next) + { + if(ea->tier && (ea->status & REQUEST_ANSWERED) && !is_network_reader(ea->reader)) + { + get_tiername_defaultid(ea->tier, ecm->selected_reader->caid, tier_string); + tier = tier_string; + break; + } + } + + cs_hexdump(0, ecm->selected_reader->VgLastPayload, 6, payload_string, sizeof(payload_string)); + payload = payload_string; + } +#endif + cs_hexdump(0, ecm->ecmd5, 16, ecmd5hex, sizeof(ecmd5hex)); +#ifdef CS_CACHEEX + cs_hexdump(0, (void *)&ecm->csp_hash, 4, csphash, sizeof(csphash)); +#endif + cs_hexdump(0, ecm->cw, 16, cwhex, sizeof(cwhex)); +#ifdef MODULE_GBOX + if(check_client(ecm->client) && get_module(ecm->client)->num == R_GBOX && ecm->gbox_ecm_dist) + { return ecmfmt(result, size, ecm->caid, ecm->onid, ecm->prid, ecm->chid, ecm->pid, ecm->srvid, ecm->ecmlen, ecmd5hex, csphash, cwhex, ecm->gbox_ecm_src_peer, ecm->gbox_ecm_dist, payload, tier); } + else if (ecm->selected_reader && ecm->selected_reader->typ == R_GBOX && !ecm->gbox_ecm_dist) + { return ecmfmt(result, size, ecm->caid, ecm->onid, ecm->prid, ecm->chid, ecm->pid, ecm->srvid, ecm->ecmlen, ecmd5hex, csphash, cwhex, ecm->selected_reader->gbox_cw_src_peer, ecm->selected_reader->currenthops, payload, tier); } + else +#endif + return ecmfmt(result, size, ecm->caid, ecm->onid, ecm->prid, ecm->chid, ecm->pid, ecm->srvid, ecm->ecmlen, ecmd5hex, csphash, cwhex, 0, + ((ecm->selected_reader && ecm->selected_reader->currenthops) ? ecm->selected_reader->currenthops : 0), payload, tier); +} + +/* ========================= + * Ai Fake Dcw Detector + * ========================= */ + +int cw_vote_add(struct ecm_request_t *er, uint8_t *cw, struct s_reader *rdr) +{ + int i, free_idx = -1; + int is_local = 0; + char cw_hex[33]; + const char *source_label = "unknown"; + + if (rdr) { + source_label = rdr->label; + // Wirtualne czytniki nie są lokalne + if (rdr == virtual_cacheex_reader || rdr == virtual_csp_reader) { + is_local = 0; + } else { + is_local = is_localreader(rdr, er); + } + } else if (er->cacheex_src) { + if (check_client(er->cacheex_src) && er->cacheex_src->account) { + source_label = er->cacheex_src->account->usr; + } else { + source_label = "CACHEEX_CLIENT"; + } + } else if (er->from_csp) { + source_label = "CSP"; + } + + + cs_hexdump(0, cw, 16, cw_hex, sizeof(cw_hex)); + + +// Sprawdź czy vote_pool jest pusta - jeśli nie, wyczyść ją +// Używamy er->tps.time jako identyfikatora sesji ECM +if (er->vote_pool_session != er->tps.time) { + memset(er->vote_pool, 0, sizeof(er->vote_pool)); + er->vote_pool_session = er->tps.time; +} + + // Logowanie wyłączone - odkomentuj jeśli potrzebne + // if (cfg.cwvote_log_enabled) { + // cs_log("[Ai_vote_add] Adding CW from client %s, source %s (CAID: %04X, PRID: %06X, SRVID: %04X). CW: %s", + // (check_client(er->client) ? er->client->account->usr : "-"), + // source_label, er->caid, er->prid, er->srvid, cw_hex); + // } + + int max_cand = cfg.cwvote_max_candidates; + + // Sprawdź czy wpis jest poprawnie zainicjalizowany (votes > 0) i w granicach tablicy + // Porównujemy cały CW (16 bajtów), nie tylko compare_len + for (i = 0; i < max_cand; i++) { + if (er->vote_pool[i].votes == 0 && free_idx < 0) + free_idx = i; + + if (i < MAX_VOTE_CANDIDATES && er->vote_pool[i].votes > 0) { + int cmp = memcmp(er->vote_pool[i].cw, cw, 16); + + if (cmp == 0) { + // CW już istnieje w puli - agreguj głosy niezależnie od źródła + // To pozwala na agregację głosów z różnych cacheex peerów dla tego samego CW + if (er->vote_pool[i].votes < MAX_VOTE_CANDIDATES) { + er->vote_pool[i].voters[er->vote_pool[i].votes] = rdr; + er->vote_pool[i].votes++; + if (is_local) er->vote_pool[i].local_votes++; + } + // cs_log("[Ai_vote_add] Aggregated vote for existing CW → Votes: %d (local: %d) from %s", + // er->vote_pool[i].votes, er->vote_pool[i].local_votes, source_label); + return 0; + } + } + } + + if (free_idx < 0) { + // cs_log("[Ai_vote_add] Voting pool full!"); + return -1; + } + + // Nowy CW + memcpy(er->vote_pool[free_idx].cw, cw, 16); + er->vote_pool[free_idx].votes = 1; + er->vote_pool[free_idx].local_votes = is_local ? 1 : 0; + // Store the actual reader if available, otherwise NULL + er->vote_pool[free_idx].voters[0] = rdr; + + if (cfg.cwvote_log_enabled) { + cs_log("[Ai_vote_add] New CW in slot %d → Votes: %d (local: %d) from %s", + free_idx, 1, er->vote_pool[free_idx].local_votes, source_label); + } + + return 0; +} + +int cw_vote_decide(struct ecm_request_t *er) +{ + if (!cfg.cwvote_enabled) + return 0; + + int i, total_votes = 0, best = -1; + int best_score = -1; + char cw_hex[33]; + struct timeb now_tb; + cs_ftime(&now_tb); // aktualny czas + + // Logowanie wyłączone - odkomentuj jeśli potrzebne + // if (cfg.cwvote_log_enabled) { + // cs_log("[Ai_vote_decide] Starting vote for client %s (CAID: %04X, SRVID: %04X)", + // (check_client(er->client) ? er->client->account->usr : "-"), + // er->caid, er->srvid); + // } + + int max_cand = cfg.cwvote_max_candidates; + float local_weight = cfg.cwvote_local_weight; + int min_votes = cfg.cwvote_min_votes; + int timeout = cfg.cwvote_timeout; + int fallback = cfg.cwvote_fallback; + + // Liczymy total_votes i logujemy kandydatów + for (i = 0; i < max_cand; i++) { + if (er->vote_pool[i].votes > 0) { + total_votes += er->vote_pool[i].votes; + + if (cfg.cwvote_log_enabled) { + cs_hexdump(0, er->vote_pool[i].cw, 16, cw_hex, sizeof(cw_hex)); + cs_log("[Ai_vote_decide] Candidate %d: CW: %s | Votes: %d (local: %d)", + i, cw_hex, er->vote_pool[i].votes, er->vote_pool[i].local_votes); + } + } + } + + // Logowanie podsumowania - tylko gdy są jakieś głosy + if (cfg.cwvote_log_enabled && total_votes > 0) { + cs_log("[Ai_vote_decide] Total votes: %d | Min required: %d | Timeout: %d ms", + total_votes, min_votes, timeout); + } + + // Szukamy najlepszego (z effective score) - MUSI być przed sprawdzeniem pojedynczego kandydata + for (i = 0; i < max_cand; i++) { + if (er->vote_pool[i].votes == 0) continue; + + int effective_score = er->vote_pool[i].votes + + (int)(er->vote_pool[i].local_votes * local_weight); + + if (effective_score > best_score) { + best_score = effective_score; + best = i; + } + } + + // Sprawdź czy jest tylko jeden kandydat (jeden unikalny CW) + // Jeśli timeout == 0, przyjmij od razu - nie ma sensu czekać + // Jeśli timeout > 0, czekaj - może przyjdą inne klucze + if (total_votes == 1 && best >= 0 && timeout == 0) { + memcpy(er->cw, er->vote_pool[best].cw, 16); + + // Update cacheex hit stats if the winning CW came from cacheex + if (cfg.cwvote_enabled && er->cacheex_src) { + struct s_client *src_cl = er->cacheex_src; + if (src_cl && src_cl->cwcacheexhit >= 0) { + src_cl->cwcacheexhit++; + if (src_cl->account) { + src_cl->account->cwcacheexhit++; + } + first_client->cwcacheexhit++; + } + } + + if (cfg.cwvote_log_enabled) { + cs_hexdump(0, er->cw, 16, cw_hex, sizeof(cw_hex)); + cs_log("[Ai_vote_decide] SINGLE CANDIDATE → Accepting CW: %s | Votes: %d (local: %d)", + cw_hex, er->vote_pool[best].votes, er->vote_pool[best].local_votes); + } + return 1; + } + + // Za mało głosów + if (total_votes < min_votes) { + // Jeśli timeout minął, zaakceptuj pojedynczy głos jako fallback + if (timeout > 0 && comp_timeb(&now_tb, &er->tps) >= timeout) { + if (cfg.cwvote_log_enabled) + cs_log("[Ai_vote_decide] Timeout reached with too few votes (%d/%d) → accepting single candidate as fallback", total_votes, min_votes); + + if (best >= 0) { + memcpy(er->cw, er->vote_pool[best].cw, 16); + + // Update cacheex hit stats if the winning CW came from cacheex + if (cfg.cwvote_enabled && er->cacheex_src) { + struct s_client *src_cl = er->cacheex_src; + if (src_cl && src_cl->cwcacheexhit >= 0) { + src_cl->cwcacheexhit++; + if (src_cl->account) { + src_cl->account->cwcacheexhit++; + } + first_client->cwcacheexhit++; + } + } + + if (cfg.cwvote_log_enabled) { + cs_hexdump(0, er->cw, 16, cw_hex, sizeof(cw_hex)); + cs_log("[Ai_vote_decide] SINGLE CANDIDATE (fallback) → Accepting CW: %s | Votes: %d (local: %d)", + cw_hex, er->vote_pool[best].votes, er->vote_pool[best].local_votes); + } + return 1; + } + } + return 0; + } + + if (best < 0) + return 0; + + int majority_needed = total_votes / 2 + 1; // > 50% + + bool has_majority = er->vote_pool[best].votes >= majority_needed; + bool timeout_reached = (timeout > 0 && comp_timeb(&now_tb, &er->tps) >= timeout); + + if (has_majority || (timeout_reached && fallback == 1)) { + memcpy(er->cw, er->vote_pool[best].cw, 16); + + // Update cacheex hit stats if the winning CW came from cacheex + if (cfg.cwvote_enabled && er->cacheex_src) { + struct s_client *src_cl = er->cacheex_src; + if (src_cl && src_cl->cwcacheexhit >= 0) { + src_cl->cwcacheexhit++; + if (src_cl->account) { + src_cl->account->cwcacheexhit++; + } + first_client->cwcacheexhit++; + } + } + + if (cfg.cwvote_log_enabled) { + cs_hexdump(0, er->cw, 16, cw_hex, sizeof(cw_hex)); + cs_log("[Ai_vote_decide] WINNER → CW: %s | Votes: %d (local: %d) | Effective: %d", + cw_hex, er->vote_pool[best].votes, er->vote_pool[best].local_votes, best_score); + } + return 1; // decyzja podjęta + } + + if (timeout_reached && fallback == 2) { + // fallback = weź pierwszy CW (slot 0) + if (er->vote_pool[0].votes > 0) { + memcpy(er->cw, er->vote_pool[0].cw, 16); + + // Update cacheex hit stats if the winning CW came from cacheex + if (cfg.cwvote_enabled && er->cacheex_src) { + struct s_client *src_cl = er->cacheex_src; + if (src_cl && src_cl->cwcacheexhit >= 0) { + src_cl->cwcacheexhit++; + if (src_cl->account) { + src_cl->account->cwcacheexhit++; + } + first_client->cwcacheexhit++; + } + } + + if (cfg.cwvote_log_enabled) cs_log("[Ai_vote_decide] Fallback: taking first CW"); + return 1; + } + } + + if (cfg.cwvote_log_enabled) + cs_log("[Ai_vote_decide] No clear winner yet... waiting"); + + return 0; +} + +/* ========================= + * Ai VOTING HELPER FUNCTIONS + * ========================= */ + +int32_t is_cwvote_caid(ECM_REQUEST *er) +{ + if (!cfg.cwvote_caids.cvcnum) // If no CAIDs are specified, it applies to all CAIDs + { + return 1; + } + + for (int32_t i = 0; i < cfg.cwvote_caids.cvcnum; i++) + { + if (cfg.cwvote_caids.cvcdata[i].caid == er->caid) + { + return 1; + } + } + return 0; +} + +/* ===== END CW VOTING ===== */ diff --git a/oscam-ecm.c.orig b/oscam-ecm.c.orig new file mode 100644 index 0000000..ecf3c16 --- /dev/null +++ b/oscam-ecm.c.orig @@ -0,0 +1,3788 @@ +#define MODULE_LOG_PREFIX "ecm" + +#include "globals.h" +#include +#include "cscrypt/md5.h" +#include "module-anticasc.h" +#include "module-cacheex.h" +#include "module-led.h" +#include "module-stat.h" +#include "module-webif.h" +#include "module-cw-cycle-check.h" +#include "module-gbox.h" +#include "oscam-cache.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-config.h" +#include "oscam-ecm.h" +#include "oscam-garbage.h" +#include "oscam-failban.h" +#include "oscam-net.h" +#include "oscam-time.h" +#include "oscam-lock.h" +#include "oscam-string.h" +#include "oscam-work.h" +#include "reader-common.h" +#include "module-cccam-data.h" +#ifdef CS_CACHEEX_AIO +#include "oscam-hashtable.h" +#endif + +extern CS_MUTEX_LOCK ecmcache_lock; +extern struct ecm_request_t *ecmcwcache; +extern uint32_t ecmcwcache_size; +extern int32_t exit_oscam; + +extern CS_MUTEX_LOCK ecm_pushed_deleted_lock; +extern struct ecm_request_t *ecm_pushed_deleted; + +static pthread_mutex_t cw_process_sleep_cond_mutex; +static pthread_cond_t cw_process_sleep_cond; +static int cw_process_wakeups; +int64_t ecmc_next, cache_next, msec_wait = 3000; + +#ifdef CS_CACHEEX_AIO +// ecm-cache +typedef struct ecm_cache +{ + struct timeb first_recv_time;// time of first cw received + struct timeb upd_time; // updated time. Update time at each cw got + uint32_t csp_hash; + node ht_node; + node ll_node; +} ECM_CACHE; + +static pthread_rwlock_t ecm_cache_lock; +static hash_table ht_ecm_cache; +static list ll_ecm_cache; +static int8_t ecm_cache_init_done = 0; + +void free_ecm_cache(void) +{ + deinitialize_hash_table(&ht_ecm_cache); + pthread_rwlock_destroy(&ecm_cache_lock); +} + +void init_ecm_cache(void) +{ +#ifdef CS_CACHEEX + if(cfg.cw_cache_size > 0 || cfg.cw_cache_memory > 0) + { + init_hash_table(&ht_ecm_cache, &ll_ecm_cache); + if (pthread_rwlock_init(&ecm_cache_lock,NULL) != 0) + { cs_log("Error creating lock ecm_cache_lock!"); } + else + { ecm_cache_init_done = 1; } + } +#endif +} + +static uint8_t time_sort(ECM_CACHE *a, ECM_CACHE *b) +{ + if (((int64_t)(a->upd_time.time) * 1000ull + (int64_t) a->upd_time.millitm) == ((int64_t)(b->upd_time.time) * 1000ull + (int64_t) b->upd_time.millitm)) return 0; + return (((int64_t)(a->upd_time.time) * 1000ull + (int64_t) a->upd_time.millitm) > ((int64_t)(b->upd_time.time) * 1000ull + (int64_t) b->upd_time.millitm)) ? -1 : 1; +} + +static int compare_csp_hash_ecmcache(const void *arg, const void *obj) +{ + uint32_t h = ((const ECM_CACHE*)obj)->csp_hash; + return memcmp(arg, &h, 4); +} + +void ecm_cache_cleanup(bool force) +{ + if(!ecm_cache_init_done) + { return; } + + SAFE_RWLOCK_WRLOCK(&ecm_cache_lock); + + ECM_CACHE *ecm_cache; + node *i, *i_next; + uint32_t ll_c = 0; + uint32_t ll_ten_percent = (uint)tommy_list_count(&ll_ecm_cache)*0.1; // 10 percent of cache + + if(!force) + sort_list(&ll_ecm_cache, time_sort); + + i = get_first_node_list(&ll_ecm_cache); + while(i) + { + i_next = i->next; + + ecm_cache = get_data_from_node(i); + + if(!ecm_cache) + { + i = i_next; + continue; + } + if(!force) + { + ++ll_c; + + if(ll_c < ll_ten_percent) + { + remove_elem_list(&ll_ecm_cache, &ecm_cache->ll_node); + remove_elem_hash_table(&ht_ecm_cache, &ecm_cache->ht_node); + NULLFREE(ecm_cache); + } + else{ + break; + } + } + else{ + remove_elem_list(&ll_ecm_cache, &ecm_cache->ll_node); + remove_elem_hash_table(&ht_ecm_cache, &ecm_cache->ht_node); + NULLFREE(ecm_cache); + } + i = i_next; + } + + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); +} +#endif + +/** + * maxparallel - Helper: Calculate pending slots array size + * Formula: round(maxparallel * parallelfactor) + * parallelfactor <= 0 means no pending slots (zapping disabled) + */ +static inline int32_t get_pending_size(struct s_reader *rdr) +{ + // parallelfactor <= 0 means no pending slots (disabled or not configured) + if(rdr->parallelfactor <= 0.0f) + return 0; + int32_t size = (int32_t)(rdr->maxparallel * rdr->parallelfactor + 0.5f); + return (size < 1) ? 1 : size; +} + +/** + * maxparallel - Helper: Check if slot is expired + * Returns timeout threshold in ms, 0 if no timeout check needed + */ +static int32_t get_slot_timeout(struct s_parallel_slot *slot, int32_t paralleltimeout) +{ + if(slot->ecm_interval > 0) + return slot->ecm_interval + paralleltimeout; + else + return 10000 + paralleltimeout; // Default 10s if no interval measured +} + +/** + * maxparallel - Helper: Clear a slot + */ +static void clear_slot(struct s_parallel_slot *slot) +{ + slot->srvid = 0; + slot->ecm_interval = 0; + slot->client = NULL; + memset(&slot->last_ecm, 0, sizeof(struct timeb)); +} + +/** + * maxparallel - Helper: Add client+service to blocked list + * Replaces any existing entry for the same client + */ +static void block_client_service(struct s_reader *rdr, struct s_client *client, uint16_t srvid) +{ + if(!rdr || !client || !rdr->blocked_services) + return; + + // Check if client already has a blocked entry - if so, update it + LL_ITER it = ll_iter_create(rdr->blocked_services); + struct s_blocked_client *bc; + while((bc = ll_iter_next(&it))) + { + if(bc->client == client) + { + bc->srvid = srvid; + return; + } + } + + // Add new entry + if(!cs_malloc(&bc, sizeof(struct s_blocked_client))) + return; + bc->client = client; + bc->srvid = srvid; + ll_append(rdr->blocked_services, bc); +} + +/** + * maxparallel - Helper: Check if client+service is blocked + * Returns: 1 = blocked, 0 = not blocked + */ +static int8_t is_client_blocked(struct s_reader *rdr, struct s_client *client, uint16_t srvid) +{ + if(!rdr || !client || !rdr->blocked_services) + return 0; + + LL_ITER it = ll_iter_create(rdr->blocked_services); + struct s_blocked_client *bc; + while((bc = ll_iter_next(&it))) + { + if(bc->client == client && bc->srvid == srvid) + return 1; + } + return 0; +} + +/** + * maxparallel - Helper: Remove block for client if zapping to different service + * Only unblocks if the new service is DIFFERENT from the blocked one + */ +static void unblock_client_if_different(struct s_reader *rdr, struct s_client *client, uint16_t new_srvid) +{ + if(!rdr || !client || !rdr->blocked_services) + return; + + LL_ITER it = ll_iter_create(rdr->blocked_services); + struct s_blocked_client *bc; + while((bc = ll_iter_next(&it))) + { + if(bc->client == client) + { + // Only unblock if zapping to a DIFFERENT service + if(bc->srvid != new_srvid) + { + ll_iter_remove_data(&it); + } + return; + } + } +} + +/** + * maxparallel - Helper: Clear all blocked entries (when active slot becomes free) + */ +static void clear_blocked_services(struct s_reader *rdr) +{ + if(!rdr || !rdr->blocked_services) + return; + + ll_clear_data(rdr->blocked_services); +} + +/** + * maxparallel - Cleanup expired slots in both arrays and promote pending + * - Removes expired slots from parallel_slots (active) and parallel_slots_pending (pending) + * - Upgrades pending to active when space becomes available (FIFO) + * - Does NOT drop pending (that's done separately when active services send ECMs) + * MUST be called with parallel_lock held + * Returns: count of active slots after cleanup + */ +static int32_t reader_cleanup_slots_nolock(struct s_reader *rdr, struct timeb *now) +{ + if(!rdr || rdr->maxparallel <= 0) + return 0; + + int32_t i; + int32_t active_count = 0; + int32_t pending_count = 0; + int32_t free_active = -1; // First empty slot in active array + + // Pass 1: Clean expired slots in active array, count active + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid == 0) + { + if(free_active < 0) + free_active = i; + continue; + } + + int64_t gone = comp_timeb(now, &rdr->parallel_slots[i].last_ecm); + int32_t timeout = get_slot_timeout(&rdr->parallel_slots[i], rdr->paralleltimeout); + + if(gone > timeout) + { + cs_log_dbg(D_READER, "reader %s: service %04X expired (no ECM for %"PRId64" ms, timeout %d ms)", + rdr->label, rdr->parallel_slots[i].srvid, gone, timeout); + clear_slot(&rdr->parallel_slots[i]); + if(free_active < 0) + free_active = i; + } + else + { + active_count++; + } + } + + // Pass 2: Clean expired slots in pending array, count active + int32_t pending_size = get_pending_size(rdr); + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid == 0) + continue; + + int64_t gone = comp_timeb(now, &rdr->parallel_slots_prov[i].last_ecm); + int32_t timeout = get_slot_timeout(&rdr->parallel_slots_prov[i], rdr->paralleltimeout); + + if(gone > timeout) + { + cs_log_dbg(D_READER, "reader %s: pending service %04X expired (no ECM for %"PRId64" ms, timeout %d ms)", + rdr->label, rdr->parallel_slots_prov[i].srvid, gone, timeout); + clear_slot(&rdr->parallel_slots_prov[i]); + } + else + { + pending_count++; + } + } + + // Pass 3: Upgrade pending to active if space available (FIFO) + // This handles the zapping case: old service expired, new one can be promoted + while(active_count < rdr->maxparallel && pending_count > 0) + { + // Find oldest pending (largest age = first to arrive = FIFO upgrade) + int32_t oldest_idx = -1; + int64_t oldest_time = -1; + + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + { + int64_t age = comp_timeb(now, &rdr->parallel_slots_prov[i].last_ecm); + if(oldest_idx < 0 || age > oldest_time) + { + oldest_idx = i; + oldest_time = age; + } + } + } + + if(oldest_idx < 0) + break; + + // Find empty slot in active array + if(free_active < 0) + { + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid == 0) + { + free_active = i; + break; + } + } + } + + if(free_active < 0) + break; // Should not happen, but safety check + + // Move pending to active + rdr->parallel_slots[free_active] = rdr->parallel_slots_prov[oldest_idx]; + clear_slot(&rdr->parallel_slots_prov[oldest_idx]); + + cs_log_dbg(D_READER, "reader %s: service %04X promoted from pending to active (slot %d)", + rdr->label, rdr->parallel_slots[free_active].srvid, free_active); + + active_count++; + pending_count--; + free_active = -1; // Need to find next empty slot + } + + // If active slots are now available, clear blocked list + // This allows previously blocked clients to try again + if(active_count < rdr->maxparallel) + { + clear_blocked_services(rdr); + } + + return active_count; +} + +/** + * maxparallel - Drop pending services when active array is full (FIFO) + * Called only when an ACTIVE service receives an ECM, proving it's still active. + * Drops the OLDEST pending first (first in, first out) + * This is fair: first to request overload is first to be denied. + * Dropped clients are added to the blocked list so they fall over to other readers. + * MUST be called with parallel_lock held + */ +static void reader_drop_pending_nolock(struct s_reader *rdr, struct timeb *now) +{ + if(!rdr || rdr->maxparallel <= 0) + return; + + // Count active and pending + int32_t i; + int32_t active_count = 0; + int32_t pending_count = 0; + int32_t pending_size = get_pending_size(rdr); + + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid != 0) + active_count++; + } + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + pending_count++; + } + + // Drop pending if active array is full (FIFO - oldest dropped first) + while(active_count >= rdr->maxparallel && pending_count > 0) + { + // Find oldest pending (largest age = first to arrive = drop first) + int32_t oldest_idx = -1; + int64_t oldest_time = -1; + + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + { + int64_t age = comp_timeb(now, &rdr->parallel_slots_prov[i].last_ecm); + if(oldest_idx < 0 || age > oldest_time) + { + oldest_idx = i; + oldest_time = age; + } + } + } + + if(oldest_idx < 0) + break; + + // Add client to blocked list before clearing slot + struct s_parallel_slot *slot = &rdr->parallel_slots_prov[oldest_idx]; + if(slot->client) + { + block_client_service(rdr, slot->client, slot->srvid); + } + + cs_log("reader %s: dropped pending service %04X (%.3f sec old, active services still running)", + rdr->label, slot->srvid, (float)oldest_time / 1000); + clear_slot(slot); + pending_count--; + } +} + +/** + * maxparallel - Check if reader has capacity for a service (does NOT reserve slot) + * Used during ECM request to decide if reader should be tried. + * Also checks if client+service is blocked (was dropped earlier). + * Returns: 1 = has capacity (or service already registered), 0 = full or blocked (skip reader) + */ +static int8_t reader_has_capacity(struct s_reader *rdr, uint16_t srvid, struct s_client *client) +{ + if(!rdr || rdr->maxparallel <= 0) + return 1; // unlimited + + cs_readlock(__func__, &rdr->parallel_lock); + + struct timeb now; + cs_ftime(&now); + + int32_t i; + int32_t pending_size = get_pending_size(rdr); + int8_t result = 0; + + // Check if client+service is blocked (was dropped earlier) + if(is_client_blocked(rdr, client, srvid)) + { + result = 0; + goto done; + } + + // Check if service already registered in active array + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid == srvid) + { + result = 1; + goto done; + } + } + + // Check if service already registered in pending array + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid == srvid) + { + result = 1; + goto done; + } + } + + // Count active slots (with expiry check) + int32_t active_count = 0; + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid != 0) + { + int64_t gone = comp_timeb(&now, &rdr->parallel_slots[i].last_ecm); + int32_t timeout = get_slot_timeout(&rdr->parallel_slots[i], rdr->paralleltimeout); + if(gone <= timeout) + active_count++; + } + } + + // Space in active array? + if(active_count < rdr->maxparallel) + { + result = 1; + goto done; + } + + // Space in pending array? + if(pending_size > 0) + { + int32_t pending_count = 0; + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + { + int64_t gone = comp_timeb(&now, &rdr->parallel_slots_prov[i].last_ecm); + int32_t timeout = get_slot_timeout(&rdr->parallel_slots_prov[i], rdr->paralleltimeout); + if(gone <= timeout) + pending_count++; + } + } + if(pending_count < pending_size) + { + result = 1; + goto done; + } + } + + // No capacity available + result = 0; + +done: + cs_readunlock(__func__, &rdr->parallel_lock); + return result; +} + +/** + * maxparallel - Register a service on a reader (called when CW is found) + * Uses dual-array architecture: + * - parallel_slots: active services (the limit) + * - parallel_slots_pending: pending services during zapping + * New services go to active if space available, otherwise pending. + * Returns: 1 = registered, 0 = failed + */ +static int8_t reader_register_service(struct s_reader *rdr, uint16_t srvid, struct s_client *client) +{ + if(!rdr || rdr->maxparallel <= 0) + return 1; // unlimited, always OK + + cs_writelock(__func__, &rdr->parallel_lock); + + // If client is registering a DIFFERENT service, unblock them + // (they zapped away from the blocked service) + unblock_client_if_different(rdr, client, srvid); + + struct timeb now; + cs_ftime(&now); + + // Cleanup expired slots and handle promotions/drops + int32_t active_count = reader_cleanup_slots_nolock(rdr, &now); + + int32_t i; + int32_t pending_size = get_pending_size(rdr); + + // Search for existing slot in both arrays + int32_t existing_active = -1; + int32_t existing_pending = -1; + int32_t free_active = -1; + int32_t free_pending = -1; + + // Search active array + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid == srvid) + existing_active = i; + else if(free_active < 0 && rdr->parallel_slots[i].srvid == 0) + free_active = i; + } + + // Search pending array + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid == srvid) + existing_pending = i; + else if(free_pending < 0 && rdr->parallel_slots_prov[i].srvid == 0) + free_pending = i; + } + + struct s_parallel_slot *slot = NULL; + int8_t is_new = 0; + int8_t is_pending = 0; + int32_t slot_idx = -1; + const char *array_name = "active"; + + if(existing_active >= 0) + { + // Already in active array - just update + slot = &rdr->parallel_slots[existing_active]; + slot_idx = existing_active; + is_new = 0; + } + else if(existing_pending >= 0) + { + // Already in pending array - update there + slot = &rdr->parallel_slots_prov[existing_pending]; + slot_idx = existing_pending; + is_new = 0; + is_pending = 1; + array_name = "pending"; + } + else if(active_count < rdr->maxparallel && free_active >= 0) + { + // New service, space in active array + slot = &rdr->parallel_slots[free_active]; + slot_idx = free_active; + slot->srvid = srvid; + slot->ecm_interval = 0; + slot->client = client; + is_new = 1; + } + else if(free_pending >= 0) + { + // New service, active full -> put in pending + slot = &rdr->parallel_slots_prov[free_pending]; + slot_idx = free_pending; + slot->srvid = srvid; + slot->ecm_interval = 0; + slot->client = client; + is_new = 1; + is_pending = 1; + array_name = "pending"; + } + else + { + // No slot available in either array + cs_writeunlock(__func__, &rdr->parallel_lock); + cs_log("reader %s: no slot available for service %04X (all %d+%d slots used)", + rdr->label, srvid, rdr->maxparallel, pending_size); + return 0; + } + + // Update interval for existing slots + if(!is_new && slot->last_ecm.time > 0) + { + int64_t interval = comp_timeb(&now, &slot->last_ecm); + if(interval > 0 && interval < 30000) // Sanity: < 30 seconds + { + if(slot->ecm_interval == 0) + slot->ecm_interval = (int32_t)interval; + else + slot->ecm_interval = (slot->ecm_interval + (int32_t)interval) / 2; + } + } + + slot->last_ecm = now; + + // If this is an ACTIVE service (not pending), drop pending if needed + // This ensures pending are only dropped when active services prove they're still active + if(!is_pending) + { + reader_drop_pending_nolock(rdr, &now); + } + + // Recount for logging (drop may have changed counts) + int32_t final_active = 0, final_pending = 0; + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid != 0) + final_active++; + } + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + final_pending++; + } + + if(is_new) + { + if(final_pending > 0) + { + cs_log_dbg(D_READER, "reader %s: registered service %04X in %s slot %d (%d/%d active, +%d pending)", + rdr->label, srvid, array_name, slot_idx, final_active, rdr->maxparallel, final_pending); + } + else + { + cs_log_dbg(D_READER, "reader %s: registered service %04X in %s slot %d (%d/%d active)", + rdr->label, srvid, array_name, slot_idx, final_active, rdr->maxparallel); + } + + // Log "now full" only once per state change, and only for active services + if(!is_pending && final_active >= rdr->maxparallel && !rdr->parallel_full) + { + rdr->parallel_full = 1; + cs_log("reader %s: now full (%d/%d active)", + rdr->label, final_active, rdr->maxparallel); + } + } + + // Reset full flag when capacity becomes available + if(final_active < rdr->maxparallel && rdr->parallel_full) + { + rdr->parallel_full = 0; + cs_log_dbg(D_READER, "reader %s: capacity available (%d/%d active)", + rdr->label, final_active, rdr->maxparallel); + } + + cs_writeunlock(__func__, &rdr->parallel_lock); + return 1; +} + +void fallback_timeout(ECM_REQUEST *er) +{ + if(er->rc >= E_UNHANDLED && er->stage < 4) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} fallback timeout! (stage: %d)", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, er->stage); + debug_ecm(D_TRACE, "fallback for %s %s", username(er->client), buf); + while(er->stage < 4) // if preferlocalcards=1 and no answer from locals, initial stage will be 2! We need to reach stage=4 to call fallback's. + { + request_cw_from_readers(er, 0); + } + } +} + +void ecm_timeout(ECM_REQUEST *er) +{ + if(!er->readers_timeout_check) + { + er->readers_timeout_check = 1; + + if(check_client(er->client) && er->rc >= E_UNHANDLED) + { + debug_ecm(D_TRACE, "timeout for %s %s", username(er->client), buf); + + // set timeout for readers not answering + struct s_ecm_answer *ea_list; + for(ea_list = er->matching_rdr; ea_list; ea_list = ea_list->next) + { + if((ea_list->status & (REQUEST_SENT | REQUEST_ANSWERED)) == REQUEST_SENT) // Request sent, but no answer! + { + write_ecm_answer(ea_list->reader, er, E_TIMEOUT, 0, NULL, NULL, 0, NULL); // set timeout for readers not answered! + } + } + + // send timeout to client! + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} client timeout! ", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + er->rc = E_TIMEOUT; + er->rcEx = 0; + send_dcw(er->client, er); + } + } +} + +void increment_n_request(struct s_client *cl) +{ + if(check_client(cl)) + { + cl->n_request[1]++; + first_client->n_request[1]++; + } +} + +uint8_t checkCWpart(uint8_t *cw, int8_t part) +{ + uint8_t eo = part ? 8 : 0; + int8_t i; + for(i = 0; i < 8; i++) + if(cw[i + eo]) { return 1; } + return 0; +} + +void update_n_request(void) +{ + struct s_client *cl; + + cs_readlock(__func__, &clientlist_lock); + for(cl = first_client->next; cl; cl = cl->next) + { +#ifdef CS_CACHEEX + if(check_client(cl) && get_module(cl)->num != R_CSP && cl->typ == 'c' && !cl->dup && cl->account && cl->account->cacheex.mode<=1) //no cacheex 2/3 client +#else + if(check_client(cl) && get_module(cl)->num != R_CSP && cl->typ == 'c' && !cl->dup) +#endif + { + cl->n_request[0] = cl->n_request[1]; + cl->n_request[1] = 0; + } + else + { + cl->n_request[0] = 0; + cl->n_request[1] = 0; + } + } + + first_client->n_request[0] = first_client->n_request[1]; + first_client->n_request[1] = 0; + + cs_readunlock(__func__, &clientlist_lock); +} + +static void *cw_process(void) +{ + set_thread_name(__func__); + int64_t time_to_check_fbtimeout, time_to_check_ctimeout, next_check, n_request_next; + struct timeb t_now, tbc, ecmc_time, cache_time, n_request_time; + ECM_REQUEST *er = NULL; + time_t ecm_maxcachetime; + +#ifdef CS_CACHEEX + int64_t time_to_check_cacheex_wait_time; + int64_t time_to_check_cacheex_mode1_delay; +#endif + + cs_pthread_cond_init(__func__, &cw_process_sleep_cond_mutex, &cw_process_sleep_cond); + +#ifdef CS_ANTICASC + int32_t ac_next; + struct timeb ac_time; + cs_ftime(&ac_time); + add_ms_to_timeb(&ac_time, cfg.ac_stime * 60 * 1000); +#endif + + cs_ftime(&ecmc_time); + add_ms_to_timeb(&ecmc_time, 1000); + cs_ftime(&cache_time); + add_ms_to_timeb(&cache_time, 3000); + cs_ftime(&n_request_time); + add_ms_to_timeb(&n_request_time, 60 * 1000); + + while(!exit_oscam) + { + if(cw_process_wakeups == 0) // No waiting wakeups, proceed to sleep + { + sleepms_on_cond(__func__, &cw_process_sleep_cond_mutex, &cw_process_sleep_cond, msec_wait); + } + cw_process_wakeups = 0; // We've been woken up, reset the counter + if(exit_oscam) + { break; } + + next_check = 0; +#ifdef CS_ANTICASC + ac_next = 0; +#endif + ecmc_next = 0; + cache_next = 0; + msec_wait = 0; + + cs_ftime(&t_now); + cs_readlock(__func__, &ecmcache_lock); + for(er = ecmcwcache; er; er = er->next) + { + + if((er->from_cacheex || er->from_csp) // ignore ecms from cacheex/csp + || er->readers_timeout_check // ignore already checked + || !check_client(er->client)) // ignore ecm of killed clients + { + continue; + } + + if(er->rc >= E_UNHANDLED) + { +#ifdef CS_CACHEEX + // cacheex_wait_time + if(er->cacheex_wait_time && !er->cacheex_wait_time_expired) + { + tbc = er->tps; + time_to_check_cacheex_mode1_delay = 0; + time_to_check_cacheex_wait_time = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, er->cacheex_wait_time)); + + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_CACHEEX_TIMEOUT, (void *)er, 0); + time_to_check_cacheex_wait_time = 0; + + } + else if(er->cacheex_mode1_delay && !er->stage && er->cacheex_reader_count>0) + { + // check for cacheex_mode1_delay + tbc = er->tps; + time_to_check_cacheex_mode1_delay = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, er->cacheex_mode1_delay)); + + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_CACHEEX1_DELAY, (void *)er, 0); + time_to_check_cacheex_mode1_delay = 0; + } + } + + if(!next_check || (time_to_check_cacheex_wait_time > 0 && time_to_check_cacheex_wait_time < next_check)) + { next_check = time_to_check_cacheex_wait_time; } + + if(!next_check || (time_to_check_cacheex_mode1_delay > 0 && time_to_check_cacheex_mode1_delay < next_check)) + { next_check = time_to_check_cacheex_mode1_delay; } + } +#endif + if(er->stage < 4) + { + // fbtimeout + tbc = er->tps; + time_to_check_fbtimeout = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, get_fallbacktimeout(er->caid))); + + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_FALLBACK_TIMEOUT, (void *)er, 0); + time_to_check_fbtimeout = 0; + } + + if(!next_check || (time_to_check_fbtimeout > 0 && time_to_check_fbtimeout < next_check)) + { next_check = time_to_check_fbtimeout; } + } + } + + // clienttimeout + if(!er->readers_timeout_check) // ecm stays in cache at least ctimeout+2seconds! + { + tbc = er->tps; + time_to_check_ctimeout = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, cfg.ctimeout)); + + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_CLIENT_TIMEOUT, (void *)er, 0); + time_to_check_ctimeout = 0; + } + + if(!next_check || (time_to_check_ctimeout > 0 && time_to_check_ctimeout < next_check)) + { next_check = time_to_check_ctimeout; } + } + } + cs_readunlock(__func__, &ecmcache_lock); +#ifdef CS_ANTICASC + if(cfg.ac_enabled && (ac_next = comp_timeb(&ac_time, &t_now)) <= 10) + { + ac_do_stat(); + cs_ftime(&ac_time); + ac_next = add_ms_to_timeb_diff(&ac_time, cfg.ac_stime * 60 * 1000); + } +#endif + if((ecmc_next = comp_timeb(&ecmc_time, &t_now)) <= 10) + { + uint32_t count = 0; + struct ecm_request_t *ecm, *ecmt = NULL, *prv; + + cs_readlock(__func__, &ecmcache_lock); + for(ecm = ecmcwcache, prv = NULL; ecm; prv = ecm, ecm = ecm->next, count++) + { + ecm_maxcachetime = t_now.time - ((cfg.ctimeout + 500) / 1000 + 3); // to be sure no more access er! + + if(ecm->tps.time < ecm_maxcachetime) + { + cs_readunlock(__func__, &ecmcache_lock); + cs_writelock(__func__, &ecmcache_lock); + ecmt = ecm; + if(prv) + { prv->next = NULL; } + else + { ecmcwcache = NULL; } + cs_writeunlock(__func__, &ecmcache_lock); + break; + } + } + if(!ecmt) + { cs_readunlock(__func__, &ecmcache_lock); } + ecmcwcache_size = count; + + while(ecmt) + { + ecm = ecmt->next; + free_ecm(ecmt); + ecmt = ecm; + } + +#ifdef CS_CACHEEX + ecmt=NULL; + cs_readlock(__func__, &ecm_pushed_deleted_lock); + for(ecm = ecm_pushed_deleted, prv = NULL; ecm; prv = ecm, ecm = ecm->next) + { + ecm_maxcachetime = t_now.time - ((cfg.ctimeout + 500) / 1000 + 3); + if(ecm->tps.time < ecm_maxcachetime) + { + cs_readunlock(__func__, &ecm_pushed_deleted_lock); + cs_writelock(__func__, &ecm_pushed_deleted_lock); + ecmt = ecm; + if(prv) + { prv->next = NULL; } + else + { ecm_pushed_deleted = NULL; } + cs_writeunlock(__func__, &ecm_pushed_deleted_lock); + break; + } + } + if(!ecmt) + { cs_readunlock(__func__, &ecm_pushed_deleted_lock); } + + while(ecmt) + { + ecm = ecmt->next; + free_push_in_ecm(ecmt); + ecmt = ecm; + } +#endif + + cs_ftime(&ecmc_time); + ecmc_next = add_ms_to_timeb_diff(&ecmc_time, 1000); + } + + if((cache_next = comp_timeb(&cache_time, &t_now)) <= 10) + { + cleanup_cache(false); + cacheex_cleanup_hitcache(false); + + cs_ftime(&cache_time); + cache_next = add_ms_to_timeb_diff(&cache_time, 3000); + } + + if((n_request_next = comp_timeb(&n_request_time, &t_now)) <= 10) + { + update_n_request(); + cs_ftime(&n_request_time); + n_request_next = add_ms_to_timeb_diff(&n_request_time, 60 * 1000); + } + + msec_wait = next_check; +#ifdef CS_ANTICASC + if(!msec_wait || (ac_next > 0 && ac_next < msec_wait)) + { msec_wait = ac_next; } +#endif + if(!msec_wait || (ecmc_next > 0 && ecmc_next < msec_wait)) + { msec_wait = ecmc_next; } + + if(!msec_wait || (cache_next > 0 && cache_next < msec_wait)) + { msec_wait = cache_next; } + + if(!msec_wait || (n_request_next > 0 && n_request_next < msec_wait)) + { msec_wait = n_request_next; } + + if(!msec_wait) + { msec_wait = 3000; } + + cleanupcwcycle(); + } + + return NULL; +} + +void cw_process_thread_start(void) +{ + start_thread("cw_process", (void *) &cw_process, NULL, NULL, 1, 1); +} + +void cw_process_thread_wakeup(void) +{ + cw_process_wakeups++; // Do not sleep... + SAFE_COND_SIGNAL(&cw_process_sleep_cond); +} + +void convert_to_beta(struct s_client *cl, ECM_REQUEST *er, uint16_t caidto) +{ + static uint8_t headerN3[10] = { 0xc7, 0x00, 0x00, 0x00, 0x01, 0x10, 0x10, 0x00, 0x87, 0x12 }; + static uint8_t headerN2[10] = { 0xc9, 0x00, 0x00, 0x00, 0x01, 0x10, 0x10, 0x00, 0x48, 0x12 }; + + er->ocaid = er->caid; + er->caid = caidto; + er->prid = 0; + er->ecmlen = er->ecm[2] + 3; + + memmove(er->ecm + 13, er->ecm + 3, er->ecmlen - 3); + + if(er->ecmlen > 0x88) + { + memcpy(er->ecm + 3, headerN3, 10); + if(er->ecm[0] == 0x81) + { er->ecm[12] += 1; } + er->ecm[1] = 0x70; + } + else + { + memcpy(er->ecm + 3, headerN2, 10); + } + + er->ecmlen += 10; + er->ecm[2] = er->ecmlen - 3; + er->btun = 1; + + cl->cwtun++; + cl->account->cwtun++; + first_client->cwtun++; + + cs_log_dbg(D_TRACE, "ECM converted ocaid from 0x%04X to BetaCrypt caid 0x%04X for service id 0x%04X", + er->ocaid, caidto, er->srvid); +} + +void convert_to_nagra(struct s_client *cl, ECM_REQUEST *er, uint16_t caidto) +{ + cs_log_dbg(D_TRACE, "convert_to_nagra"); + er->ocaid = er->caid; + er->caid = caidto; + er->prid = 0; + er->ecmlen = er->ecm[2] + 3; + + // not sure + if(er->ecmlen < 0x52) + { er->ecm[1] = 0x30; } + + memmove(er->ecm + 3, er->ecm + 13, er->ecmlen - 3); + + er->ecmlen -= 10; + er->ecm[2] = er->ecmlen - 3; + er->btun = 1; + + cl->cwtun++; + cl->account->cwtun++; + first_client->cwtun++; + + cs_log_dbg(D_TRACE, "ECM converted ocaid from: 0x%04X to Nagra: 0x04%X for service id:0x04%X", + er->ocaid, caidto, er->srvid); +} + +void cs_betatunnel(ECM_REQUEST *er) +{ + int32_t i; + struct s_client *cl = cur_client(); + uint32_t mask_all = 0xFFFF; + TUNTAB *ttab = &cl->ttab; + + for(i = 0; i < ttab->ttnum; i++) + { + if((er->caid == ttab->ttdata[i].bt_caidfrom) && ((er->srvid == ttab->ttdata[i].bt_srvid) || (ttab->ttdata[i].bt_srvid) == mask_all)) + { + if(chk_is_betatunnel_caid(er->caid) == 1 && er->ocaid == 0x0000) + { + convert_to_nagra(cl, er, ttab->ttdata[i].bt_caidto); + } + else if(er->ocaid == 0x0000) + { + convert_to_beta(cl, er, ttab->ttdata[i].bt_caidto); + } + return; + } + } +} + +static void remove_ecm_from_reader(ECM_REQUEST *ecm) +{ + int32_t i; + struct s_ecm_answer *ea = ecm->matching_rdr; + while(ea) + { + if((ea->status & REQUEST_SENT) && !(ea->status & REQUEST_ANSWERED)) + { + // we found a outstanding reader, clean it: + struct s_reader *rdr = ea->reader; + if(rdr) + { + struct s_client *cl = rdr->client; + if(check_client(cl)) + { + ECM_REQUEST *ecmtask = cl->ecmtask; + if(ecmtask) + { + for(i = 0; i < cfg.max_pending; ++i) + { + if(ecmtask[i].parent == ecm) + { + ecmtask[i].client = NULL; + cacheex_set_csp_lastnode(&ecmtask[i]); + } + } + } + } + } + } + ea = ea->next; + } +} + +void free_ecm(ECM_REQUEST *ecm) +{ + struct s_ecm_answer *ea, *nxt; + cacheex_free_csp_lastnodes(ecm); + gbox_free_cards_pending(ecm); + // remove this ecm from reader queue to avoid segfault on very late answers (when ecm is already disposed) + // first check for outstanding answers: + remove_ecm_from_reader(ecm); + // free matching_rdr list: + ea = ecm->matching_rdr; + ecm->matching_rdr = NULL; + while(ea) + { + nxt = ea->next; + cs_lock_destroy(__func__, &ea->ecmanswer_lock); + add_garbage(ea); + ea = nxt; + } + if(ecm->src_data) + { add_garbage(ecm->src_data); } + add_garbage(ecm); +} + + +void free_push_in_ecm(ECM_REQUEST *ecm) +{ + cacheex_free_csp_lastnodes(ecm); + gbox_free_cards_pending(ecm); + if(ecm->src_data) + { NULLFREE(ecm->src_data); } + NULLFREE(ecm); +} + +ECM_REQUEST *get_ecmtask(void) +{ + ECM_REQUEST *er = NULL; + struct s_client *cl = cur_client(); + if(!cl) + { return NULL; } + if(!cs_malloc(&er, sizeof(ECM_REQUEST))) + { return NULL; } + cs_ftime(&er->tps); + er->rc = E_UNHANDLED; + er->client = cl; + er->grp = 0; // no readers/cacheex-clients answers yet + //cs_log("client %s ECMTASK %d module %s", username(cl), n, get_module(cl)->desc); + return er; +} + +void cleanup_ecmtasks(struct s_client *cl) +{ + if(!cl) { return; } + + ECM_REQUEST *ecm; + + // remove this clients ecm from queue. because of cache, just null the client: + cs_readlock(__func__, &ecmcache_lock); + for(ecm = ecmcwcache; ecm && cl; ecm = ecm->next) + { + if(ecm->client == cl) + { + ecm->client = NULL; + } + } + cs_readunlock(__func__, &ecmcache_lock); + + // remove client from rdr ecm-queue: + cs_readlock(__func__, &readerlist_lock); + struct s_reader *rdr = first_active_reader; + while(rdr) + { + if(check_client(rdr->client) && rdr->client->ecmtask) + { + int i; + for(i = 0; (i < cfg.max_pending) && cl; i++) + { + ecm = &rdr->client->ecmtask[i]; + if(ecm->client == cl) + { + ecm->client = NULL; + } + } + } + rdr = rdr->next; + } + cs_readunlock(__func__, &readerlist_lock); + +} + +static void add_cascade_data(struct s_client *client, ECM_REQUEST *er) +{ + if(!client->cascadeusers) + { client->cascadeusers = ll_create("cascade_data"); } + LLIST *l = client->cascadeusers; + LL_ITER it = ll_iter_create(l); + time_t now = time(NULL); + struct s_cascadeuser *cu; + int8_t found = 0; + while((cu = ll_iter_next(&it))) + { + if(er->caid == cu->caid && er->prid == cu->prid && er->srvid == cu->srvid) // found it + { + if(cu->time < now) + { cu->cwrate = now - cu->time; } + cu->time = now; + found = 1; + } + else if(cu->time + 60 < now) // old + { ll_iter_remove_data(&it); } + } + + if(!found) // add it if not found + { + if(!cs_malloc(&cu, sizeof(struct s_cascadeuser))) + { return; } + cu->caid = er->caid; + cu->prid = er->prid; + cu->srvid = er->srvid; + cu->time = now; + ll_append(l, cu); + } +} + +int32_t is_double_check_caid(ECM_REQUEST *er, FTAB *double_check_caid) +{ + if(!double_check_caid->nfilts) { return 1; } + + int32_t i, k; + for(i = 0; i < double_check_caid->nfilts; i++) + { + uint16_t tcaid = double_check_caid->filts[i].caid; + if(tcaid && (tcaid == er->caid || (tcaid < 0x0100 && (er->caid >> 8) == tcaid))) // caid match + { + int32_t nprids = double_check_caid->filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid = double_check_caid->filts[i].prids[k]; + if(prid == er->prid) // Provider matches + { return 1; } + } + } + } + + return 0; +} + +struct s_ecm_answer *get_ecm_answer(struct s_reader *reader, ECM_REQUEST *er) +{ + if(!er || !reader) { return NULL; } + + struct s_ecm_answer *ea; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if(ea->reader == reader) + { + return ea; + } + } + return NULL; +} + +void distribute_ea(struct s_ecm_answer *ea) +{ + struct s_ecm_answer *ea_temp; + + for(ea_temp = ea->pending; ea_temp; ea_temp = ea_temp->pending_next) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [distribute_ea] send ea (%s) by reader %s answering for client %s", (check_client(ea_temp->er->client) ? ea_temp->er->client->account->usr : "-"), ea_temp->er->caid, ea_temp->er->prid, ea_temp->er->srvid, ea->rc==E_FOUND?"OK":"NOK", ea_temp->reader->label, (check_client(ea->er->client) ? ea->er->client->account->usr : "-")); +#ifdef CS_CACHEEX_AIO + if(ea->rc==E_FOUND && ea->er->localgenerated) + ea_temp->er->localgenerated = 1; +#endif + // e.g. we cannot send timeout, because "ea_temp->er->client" could wait/ask other readers! Simply set not_found if different from E_FOUND! + write_ecm_answer(ea_temp->reader, ea_temp->er, (ea->rc==E_FOUND? E_FOUND : E_NOTFOUND), ea->rcEx, ea->cw, NULL, ea->tier, &ea->cw_ex); + } +} + +int32_t send_dcw(struct s_client *client, ECM_REQUEST *er) +{ + if(!check_client(client) || client->typ != 'c') + { return 0; } + + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [send_dcw] rc %d from reader %s", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, er->rc, er->selected_reader ? er->selected_reader->label : "-"); + + static const char stageTxt[] = { '0', 'C', 'L', 'P', 'F', 'X' }; + static const char *stxt[] = { "found", "cache1", "cache2", "cache3", "not found", "timeout", "sleeping", + "fake", "invalid", "corrupt", "no card", "expdate", "disabled", "stopped" }; + + static const char *stxtEx[16] = {"", "group", "caid", "ident", "class", "chid", "queue", "peer", "sid", "", "", "", "", "", "", ""}; + static const char *stxtWh[16] = {"", "user ", "reader ", "server ", "lserver ", "", "", "", "", "", "", "", "" , "" , "", ""}; +#ifdef CS_CACHEEX_AIO + char sby[100] = "", sreason[100] = "", scwcinfo[32] = "", schaninfo[CS_SERVICENAME_SIZE] = "", srealecmtime[50]=""; +#else + char sby[100] = "", sreason[70] = "", scwcinfo[32] = "", schaninfo[CS_SERVICENAME_SIZE] = "", srealecmtime[50]=""; +#endif + char erEx[32] = ""; + char usrname[38] = ""; + char channame[28]; + struct timeb tpe; + + snprintf(usrname, sizeof(usrname) - 1, "%s", username(client)); + +#ifdef WITH_DEBUG + if(cs_dblevel & D_CLIENTECM) + { + char buf[ECM_FMT_LEN]; + char ecmd5[17 * 3]; + char cwstr[17 * 3]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); + cs_hexdump(0, er->cw, 16, cwstr, sizeof(cwstr)); +#ifdef CS_CACHEEX + char csphash[5 * 3]; + cs_hexdump(0, (void *)&er->csp_hash, 4, csphash, sizeof(csphash)); + cs_log_dbg(D_CLIENTECM, "Client %s csphash %s cw %s rc %d %s", username(client), csphash, cwstr, er->rc, buf); +#else + cs_log_dbg(D_CLIENTECM, "Client %s cw %s rc %d %s", username(client), cwstr, er->rc, buf); +#endif + } +#endif + + struct s_reader *er_reader = er->selected_reader; // responding reader + struct s_ecm_answer *ea_orig = get_ecm_answer(er_reader, er); + + + // check if ecm_answer from pending's + if(ea_orig && ea_orig->is_pending && er->rc == E_FOUND) + { er->rc = E_CACHE2; } + + + // check if answer from cacheex-1 reader + if(er->rc == E_FOUND && er_reader && cacheex_reader(er_reader)) // so add hit to cacheex mode 1 readers + { + er->rc = E_CACHEEX; + } + + // real ecm time + if(ea_orig && !ea_orig->is_pending && er->rc == E_FOUND + && ( +#ifdef CS_CACHEEX + er->cacheex_wait_time || +#endif + (ea_orig->status & READER_FALLBACK))) + { + snprintf(srealecmtime, sizeof(srealecmtime) - 1, " (real %d ms)", ea_orig->ecm_time); + } + + + if(er->rc == E_TIMEOUT) + { +#ifdef CS_CACHEEX + if(!er->from_cacheex1_client) // cosmetic: show "by" readers only for "normal" clients + { +#endif + struct s_ecm_answer *ea_list; + int32_t ofs = 0; + + for(ea_list = er->matching_rdr; ea_list; ea_list = ea_list->next) + { + if(ea_list->reader && ofs < (int32_t)sizeof(sby) && ((ea_list->status & REQUEST_SENT) && (ea_list->rc == E_TIMEOUT || ea_list->rc >= E_99))) //Request send, but no cw answered! + { + ofs += snprintf(sby + ofs, sizeof(sby) - ofs - 1, "%s%s", ofs ? "," : " by ", ea_list->reader->label); + } + } + + if(er->ocaid && ofs < (int32_t)sizeof(sby)) + { snprintf(sby + ofs, sizeof(sby) - ofs - 1, "(btun %04X)", er->ocaid); } + +#ifdef CS_CACHEEX + } +#endif + } + else if(er_reader) + { + // add marker to reader if ECM_REQUEST was betatunneled + if(er->ocaid) + { snprintf(sby, sizeof(sby) - 1, " by %s(btun %04X)", er_reader->label, er->ocaid); } + else + { snprintf(sby, sizeof(sby) - 1, " by %s", er_reader->label); } + } +#ifdef CS_CACHEEX + else if(er->cacheex_src) // only for cacheex mode-3 clients (no mode-1 or mode-2 because reader is set!) and csp + { + char *cex_name = "-"; + if(check_client(er->cacheex_src) && er->cacheex_src->account) + { + if(er->cacheex_src->account->usr[0] != '\0') + cex_name = er->cacheex_src->account->usr; + else + cex_name = "csp"; + } + + if(er->ocaid) + { + snprintf(sby, sizeof(sby) - 1, " by %s(btun %04X)", cex_name, er->ocaid); + } + else + { + snprintf(sby, sizeof(sby) - 1, " by %s", cex_name); + } + } +#endif + + if(er->rc < E_NOTFOUND) + { + er->rcEx = 0; + // memset(er->msglog, 0, MSGLOGSIZE); // remove reader msglog from previous requests that failed, founds never give back msglog! + } + + if(er->rcEx) + { snprintf(erEx, sizeof(erEx) - 1, "rejected %s%s", stxtWh[er->rcEx >> 4], stxtEx[er->rcEx & 0xf]); } + + get_servicename_or_null(client, er->srvid, er->prid, er->caid, channame, sizeof(channame)); + if(!channame[0]) + { + schaninfo[0] = '\0'; + } + else + { + snprintf(schaninfo, sizeof(schaninfo) - 1, " - %s", channame); + } + +#ifdef CS_CACHEEX + int cx = 0; + if(er->msglog[0]) + { + cx = snprintf(sreason, sizeof(sreason) - 1, " (%s)", er->msglog); + } +#else + if(er->msglog[0]) + { + snprintf(sreason, sizeof(sreason) - 1, " (%s)", er->msglog); + } +#endif +#ifdef CW_CYCLE_CHECK + if(er->cwc_msg_log[0]) + { snprintf(scwcinfo, sizeof(scwcinfo) - 1, " (%.26s)", er->cwc_msg_log); } +#endif + + cs_ftime(&tpe); + +#ifdef CS_CACHEEX + int cx2 = 0; + if(er->rc >= E_CACHEEX && er->cacheex_wait_time && er->cacheex_wait_time_expired) + { + cx2 = snprintf(sreason+cx, (sizeof sreason)-cx, " (wait_time over)"); + } + else + { + cx2 = cx; + } + + if(er->cw_count>1) + { +#ifdef CS_CACHEEX_AIO + if(er->cw_count > 0x0F000000 || er->localgenerated) + { + uint32_t cw_count_cleaned = er->cw_count ^ 0x0F000000; + if(cw_count_cleaned > 1) + snprintf(sreason+cx2, (sizeof sreason)-cx2, " (cw count %d) (lg)", cw_count_cleaned); + else + snprintf(sreason+cx2, (sizeof sreason)-cx2, " (lg)"); + } + else + { +#endif + + snprintf (sreason+cx2, (sizeof sreason)-cx2, " (cw count %d)", er->cw_count); + +#ifdef CS_CACHEEX_AIO + } + + } + else + { + if(er->localgenerated) + snprintf(sreason+cx2, (sizeof sreason)-cx2, " (lg)"); +#endif + } + +#endif + + client->cwlastresptime = comp_timeb(&tpe, &er->tps); + + time_t now = time(NULL); + webif_client_add_lastresponsetime(client, client->cwlastresptime, now, er->rc); // add to ringbuffer + + if(er_reader) + { + struct s_client *er_cl = er_reader->client; + if(check_client(er_cl)) + { + er_cl->cwlastresptime = client->cwlastresptime; + webif_client_add_lastresponsetime(er_cl, client->cwlastresptime, now, er->rc); + er_cl->last_providptr = client->last_providptr; + er_cl->last_srvidptr = client->last_srvidptr; + } + } + + webif_client_init_lastreader(client, er, er_reader, stxt); + + client->last = now; + + //cs_log_dbg(D_TRACE, "CHECK rc=%d er->cacheex_src=%s", er->rc, username(er->cacheex_src)); + switch(er->rc) + { + case E_FOUND: + { + client->cwfound++; + client->account->cwfound++; + first_client->cwfound++; + break; + } + case E_CACHE1: + case E_CACHE2: + case E_CACHEEX: + { + client->cwcache++; + client->account->cwcache++; + first_client->cwcache++; +#ifdef CS_CACHEEX + if(check_client(er->cacheex_src)) + { + first_client->cwcacheexhit++; + er->cacheex_src->cwcacheexhit++; + if(er->cacheex_src->account) + { er->cacheex_src->account->cwcacheexhit++; } + } +#endif + break; + } + case E_NOTFOUND: + case E_CORRUPT: + case E_NOCARD: + { + if(er->rcEx) + { + client->cwignored++; + client->account->cwignored++; + first_client->cwignored++; + } + else + { + client->cwnot++; + client->account->cwnot++; + first_client->cwnot++; + } + break; + } + case E_TIMEOUT: + { + client->cwtout++; + client->account->cwtout++; + first_client->cwtout++; + break; + } + default: + { + client->cwignored++; + client->account->cwignored++; + first_client->cwignored++; + } + } + +#ifdef CS_ANTICASC +// [zaplist] ACoSC anticascading + if(cfg.acosc_enabled) + { + int8_t max_active_sids = 0; + int8_t zap_limit = 0; + int8_t penalty = 0; + int32_t penalty_duration = 0; + int32_t delay = 0; + int8_t max_ecms_per_minute = 0; + char *info1 = NULL; + char *info2 = NULL; + char *info3 = NULL; + char *info4 = NULL; + char *info5 = NULL; + char *info6 = NULL; + + // **global or user value? + cs_writelock(__func__, &clientlist_lock); + + max_active_sids = client->account->acosc_max_active_sids == -1 ? cfg.acosc_max_active_sids : client->account->acosc_max_active_sids; + info1 = client->account->acosc_max_active_sids == -1 ? "Globalvalue" : "Uservalue"; + + zap_limit = client->account->acosc_zap_limit == -1 ? cfg.acosc_zap_limit : client->account->acosc_zap_limit; + info5 = client->account->acosc_zap_limit == -1 ? "Globalvalue" : "Uservalue"; + + penalty = client->account->acosc_penalty == -1 ? cfg.acosc_penalty : client->account->acosc_penalty; + info2 = client->account->acosc_penalty == -1 ? "Globalvalue" : "Uservalue"; + + penalty_duration = client->account->acosc_penalty_duration == -1 ? cfg.acosc_penalty_duration : client->account->acosc_penalty_duration; + info3 = client->account->acosc_penalty_duration == -1 ? "Globalvalue" : "Uservalue"; + + delay = client->account->acosc_delay == -1 ? cfg.acosc_delay : client->account->acosc_delay; + info4 = client->account->acosc_delay == -1 ? "Globalvalue" : "Uservalue"; + + max_ecms_per_minute = client->account->acosc_max_ecms_per_minute == -1 ? cfg.acosc_max_ecms_per_minute : client->account->acosc_max_ecms_per_minute; + info6 = client->account->acosc_max_ecms_per_minute == -1 ? "Globalvalue" : "Uservalue"; + + //** + + if((er->rc < E_NOTFOUND && max_active_sids > 0) || zap_limit > 0 || max_ecms_per_minute > 0) + { + int8_t k = 0; + int8_t active_sid_count = 0; + time_t zaptime = time(NULL); + + if(client->account->acosc_penalty_active == 4 && client->account->acosc_penalty_until <= zaptime) // reset penalty_active + { + client->account->acosc_penalty_active = 0; + client->account->acosc_penalty_until = 0; + } + + if(client->account->acosc_penalty_active == 0 && max_active_sids > 0) + { + for(k=0; k<15 ; k++) + { + if(zaptime-30 < client->client_zap_list[k].lasttime && client->client_zap_list[k].request_stage == 10) + { + cs_log_dbg(D_TRACE, "[zaplist] ACoSC for Client: %s more then 10 ECM's for %04X@%06X/%04X/%04X", username(client), client->client_zap_list[k].caid, client->client_zap_list[k].provid, client->client_zap_list[k].chid, client->client_zap_list[k].sid); + active_sid_count ++; + } + } + cs_log_dbg(D_TRACE, "[zaplist] ACoSC for Client: %s active_sid_count= %i with more than 10 followed ECM's (mas:%i (%s))", username(client), active_sid_count, max_active_sids, info1); + } + + if(client->account->acosc_penalty_active == 0 && max_active_sids > 0 && active_sid_count > max_active_sids) //max_active_sids reached + { + client->account->acosc_penalty_active = 1; + client->account->acosc_penalty_until = zaptime + penalty_duration; + } + + if(client->account->acosc_penalty_active == 0 && zap_limit > 0 && client->account->acosc_user_zap_count > zap_limit) // zap_limit reached + { + client->account->acosc_penalty_active = 2; + client->account->acosc_penalty_until = zaptime + penalty_duration; + } + + if(client->account->acosc_penalty_active == 0 && max_ecms_per_minute > 0 && client->n_request[1] >= max_ecms_per_minute && penalty != 4) // max ecms per minute reached + { + client->account->acosc_penalty_active = 3; + client->account->acosc_penalty_until = zaptime + penalty_duration; + } + + if(client->account->acosc_penalty_active == 0 && max_ecms_per_minute > 0 && client->n_request[1] > 0 && penalty == 4) // max ecms per minute with hidecards penalty + { + client->account->acosc_penalty_active = 3; + client->account->acosc_penalty_until = zaptime + penalty_duration; + } + + if(client->account->acosc_penalty_active > 0) + { + if(client->account->acosc_penalty_active == 4) + { cs_log_dbg(D_TRACE, "[zaplist] ACoSC for Client: %s penalty_duration: %" PRId64 " seconds left(%s)", username(client), (int64_t)(client->account->acosc_penalty_until - zaptime), info3); } + + int16_t lt = get_module(client)->listenertype; + switch(penalty) + { + case 1: // NULL CW + er->rc = E_FAKE; // E_FAKE give only a status fake not a NULL cw + er->rcEx = E2_WRONG_CHKSUM; + if(client->account->acosc_penalty_active == 1) + { cs_log("[zaplist] ACoSC for Client: %s max_activ_sids reached: %i:%i(%s) penalty: 1(%s) send null CW", username(client), active_sid_count, max_active_sids, info1, info2); } + if(client->account->acosc_penalty_active == 2) + { cs_log("[zaplist] ACoSC for Client: %s zap_limit reached: %i:%i(%s) penalty: 1(%s) send null CW", username(client), client->account->acosc_user_zap_count, zap_limit, info5, info2); } + if(client->account->acosc_penalty_active == 3) + { cs_log("[maxecms] ACoSC for Client: %s max_ecms_per_minute reached: ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 1(%s) send null CW", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2); } + break; + + case 2: // ban + if(lt != LIS_DVBAPI) + { + if(client->account->acosc_penalty_active == 1) + { cs_log("[zaplist] ACoSC for Client: %s max_activ_sids reached: %i:%i(%s) penalty: 2(%s) BAN Client - Kill and set Client to failban list for %i sec.", username(client), active_sid_count, max_active_sids, info1, info2, penalty_duration); } + if(client->account->acosc_penalty_active == 2) + { cs_log("[zaplist] ACoSC for Client: %s zap_limit reached: %i:%i(%s) penalty: 2(%s) BAN Client - Kill and set Client to failban list for %i sec.", username(client), client->account->acosc_user_zap_count, zap_limit, info5, info2, penalty_duration); } + if(client->account->acosc_penalty_active == 3) + { cs_log("[maxecms] ACoSC for Client: %s max_ecms_per_minute reached: ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 2(%s) BAN Client - Kill and set Client to failban list for %i sec.", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2, penalty_duration); } + cs_add_violation_acosc(client, client->account->usr, penalty_duration); + add_job(client, ACTION_CLIENT_KILL, NULL, 0); + } + else + { + cs_log("[zaplist] ACoSC for Client: %s %i:%i(%s) penalty: 2(%s) BAN Client - don't Ban dvbapi user only stop decoding", username(client), active_sid_count, max_active_sids, info1, info2); + } + er->rc = E_DISABLED; + break; + + case 3: // delay + if(client->account->acosc_penalty_active == 1) + { cs_log("[zaplist] ACoSC for Client: %s max_activ_sids reached: %i:%i(%s) penalty: 3(%s) delay CW: %ims(%s)", username(client), active_sid_count, max_active_sids, info1, info2, delay, info4); } + if(client->account->acosc_penalty_active == 2) + { cs_log("[zaplist] ACoSC for Client: %s zap_limit reached: %i:%i(%s) penalty: 3(%s) delay CW: %ims(%s)", username(client), client->account->acosc_user_zap_count, zap_limit, info5, info2, delay, info4); } + if(client->account->acosc_penalty_active == 3) + { cs_log("[maxecms] ACoSC for Client: %s max_ecms_per_minute reached: ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 3(%s) delay CW: %ims(%s)", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2, delay, info4); } + cs_writeunlock(__func__, &clientlist_lock); + cs_sleepms(delay); + cs_writelock(__func__, &clientlist_lock); + client->cwlastresptime += delay; + snprintf(sreason, sizeof(sreason)-1, " (%d ms penalty delay)", delay); + break; + case 4: // hidecards + if(client->account->acosc_penalty_active == 3) + { + cs_log("[maxecms] ACoSC for Client: %s ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 4(%s) hidecards - hidecards to the client for %i sec", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2, penalty_duration); + client->start_hidecards = 1; + } + break; + default: // logging + if(client->account->acosc_penalty_active == 1) + { cs_log("[zaplist] ACoSC for Client: %s max_activ_sids reached: %i:%i(%s) penalty: 0(%s) only logging", username(client), active_sid_count, max_active_sids, info1, info2); } + if(client->account->acosc_penalty_active == 2) + { cs_log("[zaplist] ACoSC for Client: %s zap_limit reached: %i:%i(%s) penalty: 0(%s) only logging", username(client), client->account->acosc_user_zap_count, zap_limit, info5, info2); } + if(client->account->acosc_penalty_active == 3) + { cs_log("[maxecms] ACoSC for Client: %s max_ecms_per_minute reached: ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 0(%s) only logging", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2); } + break; + } + client->account->acosc_user_zap_count = 0; // we got already a penalty + client->account->acosc_penalty_active = 3; + client->account->acosc_penalty_active = 4; + } + } + cs_writeunlock(__func__, &clientlist_lock); + } +#endif + + if(cfg.double_check && er->rc <= E_CACHE2 && er->selected_reader && is_double_check_caid(er, &cfg.double_check_caid)) + { + if(er->checked == 0) // First CW, save it and wait for next one + { + er->checked = 1; + er->origin_reader = er->selected_reader; + memcpy(er->cw_checked, er->cw, sizeof(er->cw)); + cs_log("DOUBLE CHECK FIRST CW by %s idx %d cpti %d", er->origin_reader->label, er->idx, er->msgid); + } + else if(er->origin_reader != er->selected_reader) // Second (or third and so on) cw. We have to compare + { + if(memcmp(er->cw_checked, er->cw, sizeof(er->cw)) == 0) + { + er->checked++; + cs_log("DOUBLE CHECKED! %d. CW by %s idx %d cpti %d", er->checked, er->selected_reader->label, er->idx, er->msgid); + } + else + { + cs_log("DOUBLE CHECKED NONMATCHING! %d. CW by %s idx %d cpti %d", er->checked, er->selected_reader->label, er->idx, er->msgid); + } + } + if(er->checked < 2) // less as two same cw? mark as pending! + { + er->rc = E_UNHANDLED; + goto ESC; + } + } + + ac_chk(client, er, 1); + int32_t is_fake = 0; + if(er->rc == E_FAKE) + { + is_fake = 1; + er->rc = E_FOUND; + } + + get_module(client)->send_dcw(client, er); + + add_cascade_data(client, er); + + if(is_fake) + { er->rc = E_FAKE; } + +#ifdef CS_ANTICASC + cs_writelock(__func__, &clientlist_lock); + if(client->start_hidecards) + { + client->start_hidecards = 0; + add_job(client, ACTION_CLIENT_HIDECARDS, NULL, 0); + } + cs_writeunlock(__func__, &clientlist_lock); +#endif + + if(!(er->rc == E_SLEEPING && client->cwlastresptime == 0)) + { + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + if(er->reader_avail == 1 || er->stage == 0) + { + cs_log("%s (%s): %s (%d ms)%s%s%s%s", usrname, buf, er->rcEx ? erEx : stxt[er->rc], + client->cwlastresptime, sby, schaninfo, sreason, scwcinfo); + } + else + { + cs_log("%s (%s): %s (%d ms)%s (%c/%d/%d/%d)%s%s%s%s", usrname, buf, er->rcEx ? erEx : stxt[er->rc], + client->cwlastresptime, sby, stageTxt[er->stage], er->reader_requested, + (er->reader_count + er->fallback_reader_count), er->reader_avail, schaninfo, + srealecmtime, sreason, scwcinfo); + } + } + + cs_log_dump_dbg(D_ATR, er->cw, 16, "cw:"); + led_status_cw_not_found(er); + +ESC: + + return 0; +} + +/* + * write_ecm_request(): + */ +static int32_t write_ecm_request(struct s_reader *rdr, ECM_REQUEST *er) +{ + add_job(rdr->client, ACTION_READER_ECM_REQUEST, (void *)er, 0); + return 1; +} + +/** + * sends the ecm request to the readers + * ECM_REQUEST er : the ecm + * er->stage: 0 = no reader asked yet + * 2 = ask only local reader (skipped without preferlocalcards) + * 3 = ask any non fallback reader + * 4 = ask fallback reader + **/ +void request_cw_from_readers(ECM_REQUEST *er, uint8_t stop_stage) +{ + struct s_ecm_answer *ea; + int8_t sent = 0; + + if(er->stage >= 4) { return; } + + while(1) + { + if(stop_stage && er->stage >= stop_stage) { return; } + + er->stage++; + +#ifdef CS_CACHEEX + if(er->stage == 1 && er->preferlocalcards==2) + { er->stage++; } +#else + if(er->stage == 1) + { er->stage++; } +#endif + + if(er->stage == 2 && !er->preferlocalcards) + { er->stage++; } + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + switch(er->stage) + { +#ifdef CS_CACHEEX + case 1: + { + // Cache-Exchange + if((ea->status & REQUEST_SENT) || + (ea->status & (READER_CACHEEX | READER_ACTIVE)) != (READER_CACHEEX | READER_ACTIVE)) + { continue; } + break; + } +#endif + case 2: + { + // only local reader + if((ea->status & REQUEST_SENT) || + (ea->status & (READER_ACTIVE | READER_FALLBACK | READER_LOCAL)) != (READER_ACTIVE | READER_LOCAL)) + { continue; } + break; + } + case 3: + { + // any non fallback reader not asked yet + if((ea->status & REQUEST_SENT) || + (ea->status & (READER_ACTIVE | READER_FALLBACK)) != READER_ACTIVE) + { continue; } + break; + } + default: + { + // only fallbacks + if((ea->status & REQUEST_SENT) || + (ea->status & (READER_ACTIVE | READER_FALLBACK)) != (READER_ACTIVE | READER_FALLBACK)) + { continue; } + break; + } + } + + struct s_reader *rdr = ea->reader; +#ifdef WITH_DEBUG + if (cs_dblevel & (D_TRACE | D_CSP)) + { + char ecmd5[17 * 3]; + cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); + cs_log_dbg(D_TRACE | D_CSP, "request_cw stage=%d to reader %s ecm hash=%s", er->stage, rdr ? rdr->label : "", ecmd5); + } +#endif + // maxparallel: check if reader has capacity (slot is reserved later when CW found) + if(rdr->maxparallel > 0 && !reader_has_capacity(rdr, er->srvid, er->client)) + { + cs_log_dbg(D_LB, "reader %s skipped for %s srvid %04X (maxparallel %d reached)", + rdr->label, check_client(er->client) ? er->client->account->usr : "-", + er->srvid, rdr->maxparallel); + continue; + } + + ea->status |= REQUEST_SENT; + cs_ftime(&ea->time_request_sent); + + er->reader_requested++; + + write_ecm_request(ea->reader, er); + + // set sent=1 only if reader is active/connected. If not, switch to next stage! + if(!sent && rdr) + { + struct s_client *rcl = rdr->client; + if(check_client(rcl)) + { + if(rcl->typ == 'r' && rdr->card_status == CARD_INSERTED) + { sent = 1; } + else if(rcl->typ == 'p' && (rdr->card_status == CARD_INSERTED || rdr->tcp_connected)) + { sent = 1; } + } + } + + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_request] reader %s --> SENT %d", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, rdr ? ea->reader->label : "-", sent); + } + + if(sent || er->stage >= 4) + { break; } + } +} + +void add_cache_from_reader(ECM_REQUEST *er, struct s_reader *rdr, uint32_t csp_hash, uint8_t *ecmd5, uint8_t *cw, int16_t caid, int32_t prid, int16_t srvid +#ifdef CS_CACHEEX_AIO + , int32_t ecm_time +#endif +) +{ + ECM_REQUEST *ecm; + if (cs_malloc(&ecm, sizeof(ECM_REQUEST))) + { + cs_ftime(&ecm->tps); + + ecm->cwc_cycletime = er->cwc_cycletime; + ecm->cwc_next_cw_cycle = er->cwc_next_cw_cycle; + memcpy(ecm->ecm, er->ecm, sizeof(ecm->ecm)); // ecm[0] is pushed to cacheexclients so we need a copy from it + ecm->caid = caid; + ecm->prid = prid; + ecm->srvid = srvid; + memcpy(ecm->ecmd5, ecmd5, CS_ECMSTORESIZE); + ecm->csp_hash = csp_hash; + ecm->rc = E_FOUND; + memcpy(ecm->cw, cw, sizeof(ecm->cw)); + ecm->grp = rdr->grp; + ecm->selected_reader = rdr; +#ifdef CS_CACHEEX_AIO + ecm->ecm_time = ecm_time; + ecm->localgenerated = er->localgenerated; +#endif +#ifdef CS_CACHEEX + if(rdr && cacheex_reader(rdr)) + { ecm->cacheex_src = rdr->client; } //so adds hits to reader +#endif + + add_cache(ecm); //add cw to cache + +#ifdef CS_CACHEEX + cs_writelock(__func__, &ecm_pushed_deleted_lock); + ecm->next = ecm_pushed_deleted; + ecm_pushed_deleted = ecm; + cs_writeunlock(__func__, &ecm_pushed_deleted_lock); +#else + NULLFREE(ecm); +#endif + } +} + +void chk_dcw(struct s_ecm_answer *ea) +{ + if(!ea || !ea->er) + { return; } + + ECM_REQUEST *ert = ea->er; + struct s_ecm_answer *ea_list; + struct s_reader *eardr = ea->reader; + if(!ert || !eardr) + { return; } + + // ecm request already answered! + if(ert->rc < E_99) + { +#ifdef CS_CACHEEX + if(ea && ert->rc < E_NOTFOUND && ea->rc < E_NOTFOUND && memcmp(ea->cw, ert->cw, sizeof(ert->cw)) != 0) + { + char cw1[16 * 3 + 2], cw2[16 * 3 + 2]; +#ifdef WITH_DEBUG + if(cs_dblevel & D_TRACE) + { + cs_hexdump(0, ea->cw, 16, cw1, sizeof(cw1)); + cs_hexdump(0, ert->cw, 16, cw2, sizeof(cw2)); + } +#endif + char ip1[20] = "", ip2[20] = ""; + if(ea->reader && check_client(ea->reader->client)) { cs_strncpy(ip1, cs_inet_ntoa(ea->reader->client->ip), sizeof(ip1)); } + if(ert->cacheex_src) { cs_strncpy(ip2, cs_inet_ntoa(ert->cacheex_src->ip), sizeof(ip2)); } + else if(ert->selected_reader && check_client(ert->selected_reader->client)) { cs_strncpy(ip2, cs_inet_ntoa(ert->selected_reader->client->ip), sizeof(ip2)); } + + ECM_REQUEST *er = ert; + debug_ecm(D_TRACE, "WARNING2: Different CWs %s from %s(%s)<>%s(%s): %s<>%s", buf, + username(ea->reader ? ea->reader->client : ert->client), ip1, + er->cacheex_src ? username(er->cacheex_src) : (ert->selected_reader ? ert->selected_reader->label : "unknown/csp"), ip2, + cw1, cw2); + } +#endif + + return; + } + +#ifdef CS_CACHEEX + /* if answer from cacheex-1 reader, not send answer to client! + * thread check_cache will check counter and send answer to client! + * Anyway, we should check if we have to go to oher stage (>1) + */ + + if(eardr && cacheex_reader(eardr)) + { + // if wait_time, and not wait_time expired and wait_time due to hitcache(or awtime>0), + // we have to wait cacheex timeout before call other readers (stage>1) + if(cacheex_reader(eardr) && !ert->cacheex_wait_time_expired && ert->cacheex_hitcache) + { return; } + + int8_t cacheex_left = 0; + uint8_t has_cacheex = 0; + if(ert->stage == 1) + { + for(ea_list = ert->matching_rdr; ea_list; ea_list = ea_list->next) + { + cs_readlock(__func__, &ea_list->ecmanswer_lock); + if(((ea_list->status & (READER_CACHEEX | READER_FALLBACK | READER_ACTIVE))) == (READER_CACHEEX | READER_ACTIVE)) + { has_cacheex = 1; } + if((!(ea_list->status & READER_FALLBACK) && ((ea_list->status & (REQUEST_SENT | REQUEST_ANSWERED | READER_CACHEEX | READER_ACTIVE)) == (REQUEST_SENT | READER_CACHEEX | READER_ACTIVE))) || ea_list->rc < E_NOTFOUND) + { cacheex_left++; } + cs_readunlock(__func__, &ea_list->ecmanswer_lock); + } + + if(has_cacheex && !cacheex_left) { request_cw_from_readers(ert, 0); } + } + + return; + } +#endif + + int32_t reader_left = 0, local_left = 0, reader_not_flb_left = 0, has_not_fallback = 0, has_local = 0; + ert->selected_reader = eardr; + + switch(ea->rc) + { + case E_FOUND: + // maxparallel: register this service on the winning reader + if(eardr->maxparallel > 0) + { reader_register_service(eardr, ert->srvid, ert->client); } + + memcpy(ert->cw, ea->cw, 16); + ert->cw_ex = ea->cw_ex; + ert->rcEx = 0; + ert->rc = ea->rc; + ert->grp |= eardr->grp; + cs_strncpy(ert->msglog, ea->msglog, sizeof(ert->msglog)); +#ifdef HAVE_DVBAPI + ert->adapter_index = ea->er->adapter_index; +#endif + break; + + case E_INVALID: + case E_NOTFOUND: + { + // check if there are other readers to ask, and if not send NOT_FOUND to client + ert->rcEx = ea->rcEx; + cs_strncpy(ert->msglog, ea->msglog, sizeof(ert->msglog)); + + for(ea_list = ert->matching_rdr; ea_list; ea_list = ea_list->next) + { + cs_readlock(__func__, &ea_list->ecmanswer_lock); + + if((!(ea_list->status & READER_FALLBACK) && ((ea_list->status & (REQUEST_SENT | REQUEST_ANSWERED | READER_LOCAL | READER_ACTIVE)) == (REQUEST_SENT | READER_LOCAL | READER_ACTIVE))) || ea_list->rc < E_NOTFOUND) + { local_left++; } + + if((!(ea_list->status & READER_FALLBACK) && ((ea_list->status & (REQUEST_SENT | REQUEST_ANSWERED | READER_ACTIVE)) == (REQUEST_SENT | READER_ACTIVE))) || ea_list->rc < E_NOTFOUND) + { reader_not_flb_left++; } + + if(((ea_list->status & (REQUEST_ANSWERED | READER_ACTIVE)) == (READER_ACTIVE)) || ea_list->rc < E_NOTFOUND) + { reader_left++; } + + if(((ea_list->status & (READER_FALLBACK | READER_ACTIVE))) == (READER_ACTIVE)) + { has_not_fallback = 1; } + if(((ea_list->status & (READER_LOCAL | READER_FALLBACK | READER_ACTIVE))) == (READER_LOCAL | READER_ACTIVE)) + { has_local = 1; } + + cs_readunlock(__func__, &ea_list->ecmanswer_lock); + } + + switch(ert->stage) + { + case 2: // only local reader (used only if preferlocalcards=1) + { + if(has_local && !local_left) { request_cw_from_readers(ert, 0); } + break; + } + case 3: + { + // any fallback reader not asked yet + if(has_not_fallback && !reader_not_flb_left) { request_cw_from_readers(ert, 0); } + break; + } + } + + if(!reader_left // no more matching reader +#ifdef CS_CACHEEX + && !cfg.wait_until_ctimeout +#endif + ) + { ert->rc = E_NOTFOUND; } // so we set the return code + + break; + } + + case E_TIMEOUT: // if timeout, we have to send timeout to client: this is done by ecm_timeout callback + return; + break; + + case E_UNHANDLED: + return; + break; + + default: + cs_log("unexpected ecm answer rc=%d.", ea->rc); + return; + break; + } + + if(ert->rc < E_99) + send_dcw(ert->client, ert); +} + +uint32_t chk_provid(uint8_t *ecm, uint16_t caid) +{ + int32_t i, len, descriptor_length = 0; + uint32_t provid = 0; + + switch(caid >> 8) + { + case 0x01: // seca + provid = b2i(2, ecm + 3); + break; + + case 0x05: // viaccess + i = (ecm[4] == 0xD2) ? ecm[5] + 2 : 0; // skip d2 nano + if((ecm[5 + i] == 3) && ((ecm[4 + i] == 0x90) || (ecm[4 + i] == 0x40))) + { provid = (b2i(3, ecm + 6 + i) & 0xFFFFF0); } + + i = (ecm[6] == 0xD2) ? ecm[7] + 2 : 0; // skip d2 nano long ecm + if((ecm[7 + i] == 7) && ((ecm[6 + i] == 0x90) || (ecm[6 + i] == 0x40))) + { provid = (b2i(3, ecm + 8 + i) & 0xFFFFF0); } + break; + + case 0x0D: // cryptoworks + len = (((ecm[1] & 0xf) << 8) | ecm[2]) + 3; + for(i = 8; i < len; i += descriptor_length + 2) + { + descriptor_length = ecm[i + 1]; + if(ecm[i] == 0x83) + { + provid = (uint32_t)ecm[i + 2] & 0xFE; + break; + } + } + break; + + case 0x18: // nagra2 + if (caid == 0x1801) // more safety + provid = b2i(2, ecm + 5); + break; + } + + return provid; +} + +void update_chid(ECM_REQUEST *er) +{ + er->chid = get_subid(er); +} + +/* + * This function writes the current CW from ECM struct to a cwl file. + * The filename is re-calculated and file re-opened every time. + * This will consume a bit cpu time, but nothing has to be stored between + * each call. If not file exists, a header is prepended + */ +static void logCWtoFile(ECM_REQUEST *er, uint8_t *cw) +{ + FILE *pfCWL; + char srvname[CS_SERVICENAME_SIZE]; + /* %s / %s _I %04X _ %s .cwl */ + char buf[256 + sizeof(srvname)]; + char date[9]; + uint8_t i, parity, writeheader = 0; + struct tm timeinfo; + + /* + * search service name for that id and change characters + * causing problems in file name + */ + + get_servicename(cur_client(), er->srvid, er->prid, er->caid, srvname, sizeof(srvname)); + + for(i = 0; srvname[i]; i++) + if(srvname[i] == ' ') { srvname[i] = '_'; } + + /* calc log file name */ + time_t walltime = cs_time(); + localtime_r(&walltime, &timeinfo); + strftime(date, sizeof(date), "%Y%m%d", &timeinfo); + snprintf(buf, sizeof(buf), "%s/%s_I%04X_%s.cwl", cfg.cwlogdir, date, er->srvid, srvname); + + /* open failed, assuming file does not exist, yet */ + if((pfCWL = fopen(buf, "r")) == NULL) + { + writeheader = 1; + } + else + { + /* we need to close the file if it was opened correctly */ + fclose(pfCWL); + } + + if((pfCWL = fopen(buf, "a+")) == NULL) + { + /* maybe this fails because the subdir does not exist. Is there a common function to create it? + for the moment do not print32_t to log on every ecm + cs_log(""error opening cw logfile for writing: %s (errno=%d %s)", buf, errno, strerror(errno)); */ + return; + } + if(writeheader) + { + /* no global macro for cardserver name :( */ + fprintf(pfCWL, "# OSCam cardserver v%s - %s\n", CS_VERSION, SCM_URL); + fprintf(pfCWL, "# control word log file for use with tsdec offline decrypter\n"); + strftime(buf, sizeof(buf), "DATE %Y-%m-%d, TIME %H:%M:%S, TZ %Z\n", &timeinfo); + fprintf(pfCWL, "# %s", buf); + fprintf(pfCWL, "# CAID 0x%04X, SID 0x%04X, SERVICE \"%s\"\n", er->caid, er->srvid, srvname); + } + + parity = er->ecm[0] & 1; + fprintf(pfCWL, "%d ", parity); + for(i = parity * 8; i < 8 + parity * 8; i++) + { fprintf(pfCWL, "%02X ", cw[i]); } + /* better use incoming time er->tps rather than current time? */ + strftime(buf, sizeof(buf), "%H:%M:%S\n", &timeinfo); + fprintf(pfCWL, "# %s", buf); + fflush(pfCWL); + fclose(pfCWL); +} + +int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, uint8_t rcEx, uint8_t *cw, char *msglog, uint16_t used_cardtier, EXTENDED_CW* cw_ex) +{ + if(!reader || !er || !er->tps.time) { return 0; } + + // drop too late answers, to avoid seg fault --> only answer until tps.time+((cfg.ctimeout+500)/1000+1) is accepted + time_t timeout = time(NULL) - ((cfg.ctimeout + 500) / 1000 + 1); + if(er->tps.time < timeout) // < and NOT <= + { return 0; } + + struct timeb now; + cs_ftime(&now); + +#ifdef CS_CACHEEX_AIO + uint8_t dontsetAnswered = 0; +#endif + uint8_t dontwriteStats = 0; + + if(er && er->parent) + { + // parent is only set on reader->client->ecmtask[], but we want original er + ECM_REQUEST *er_reader_cp = er; + er = er->parent; // Now er is "original" ecm, before it was the reader-copy + er_reader_cp->rc = rc; + er_reader_cp->idx = 0; +#ifdef CS_CACHEEX_AIO + er->localgenerated = er_reader_cp->localgenerated; +#endif + + timeout = time(NULL) - ((cfg.ctimeout + 500) / 1000 + 1); + if(er->tps.time < timeout) + { return 0; } + } + +#ifdef CS_CACHEEX_AIO + if(rc < E_NOTFOUND && !er->localgenerated && (reader->cacheex.localgenerated_only_in || chk_lg_only(er, &reader->cacheex.lg_only_in_tab)) && !chk_srvid_localgenerated_only_exception(er)) + { + cs_log_dbg(D_CACHEEX, "reader: %s !er->localgenerated - rc: E_NOTFOUND set, no stats written for reader", reader ? reader->label : "-"); + rc = E_NOTFOUND; + dontsetAnswered = 1; + dontwriteStats = 1; + } +#endif + + struct s_ecm_answer *ea = get_ecm_answer(reader, er); + if(!ea) { return 0; } + + cs_writelock(__func__, &ea->ecmanswer_lock); + + if((ea->status & REQUEST_ANSWERED)) + { + cs_log_dbg(D_READER, "Reader %s already answer, skip this ecm answer!", reader ? reader->label : "-"); + cs_writeunlock(__func__, &ea->ecmanswer_lock); + return 0; + } + + // Special checks for rc + // Skip check for BISS1 - cw could be zero but still catch cw=0 by anticascading + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + + // bad/wrong chksum/ecm + if(rc == E_NOTFOUND && rcEx == E2_WRONG_CHKSUM) + { + cs_log_dbg(D_READER, "ECM for reader %s was bad/has a wrong chksum!", reader ? reader->label : "-"); + rc = E_INVALID; + rcEx = E2_WRONG_CHKSUM; + er->stage = 5; + + // dont write stats for bad/wrong chksum/ecm + dontwriteStats = 1; + + // set all other matching_readers => inactive to skip them and dont spread the bad ecm + struct s_ecm_answer *ea_list; + for(ea_list = er->matching_rdr; ea_list; ea_list = ea_list->next) + { + ea_list->status &= ~(READER_ACTIVE | READER_FALLBACK); + } + } + + if(rc < E_NOTFOUND && cw && chk_is_null_CW(cw) && !caid_is_biss(er->caid)) + { + rc = E_NOTFOUND; + cs_log_dbg(D_TRACE | D_LB, "WARNING: reader %s send fake cw, set rc=E_NOTFOUND!", reader ? reader->label : "-"); + } + + if(rc < E_NOTFOUND && cw && !chk_halfCW(er,cw)) + { + rc = E_NOTFOUND; + cs_log_dbg(D_TRACE | D_LB, "WARNING: reader %s send wrong swapped NDS cw, set rc=E_NOTFOUND!", reader ? reader->label : "-"); + } + + if(reader && cw && rc < E_NOTFOUND) + { + if(!cfg.disablecrccws && !reader->disablecrccws) + { + if(!chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for) && !chk_if_ignore_checksum(er, &reader->disablecrccws_only_for)) + { + uint8_t i, c; + for(i = 0; i < 16; i += 4) + { + c = ((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff); + + if(cw[i + 3] != c) + { + uint8_t nano = 0x00; + if(er->caid == 0x100 && er->ecm[5] > 0x00) + { + nano = er->ecm[5]; // seca nano protection + } + + if(reader->dropbadcws && !nano) // only drop controlword if no cw encryption is applied + { + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + break; + } + else + { + if(!nano) // only fix checksum if no cw encryption is applied (nano = 0) + { + cs_log_dbg(D_TRACE, "notice: changed dcw checksum byte cw[%i] from %02x to %02x", i + 3, cw[i + 3], c); + cw[i + 3] = c; + } + else + { + if(i == 12) // there are servers delivering correct controlwords but with failing last cw checksum (on purpose?!) + { + cs_log_dbg(D_TRACE,"NANO%02d: BAD PEER DETECTED, oscam has fixed the last cw crc that wasn't matching!", nano); + cw[i + 3] = c; // fix the last controlword + } + else + { + cs_log_dbg(D_TRACE,"NANO%02d: not fixing the crc of this cw since its still encrypted!", nano); + break; // crc failed so stop! + } + } + } + } + } + } + else + { + cs_log_dbg(D_TRACE, "notice: CW checksum check disabled for %04X:%06X", er->caid, er->prid); + } + } + else + { + cs_log_dbg(D_TRACE, "notice: CW checksum check disabled"); + } + + if(chk_if_ignore_checksum(er, &reader->disablecrccws_only_for) && caid_is_videoguard(er->caid) +#ifdef CS_CACHEEX_AIO + && !chk_srvid_disablecrccws_only_for_exception(er) +#endif + ) + { + uint8_t k, csum; + uint8_t hit = 0; + uint8_t oe = checkCWpart(cw, 0) ? 0 : 8; + for(k = 0; k < 8; k += 4) + { + csum = ((cw[k + oe] + cw[k + oe + 1] + cw[k + oe + 2]) & 0xff); + if(cw[k + oe + 3] == csum) + { + hit++; + } + } + if(hit > 1) + { + char ecmd5s[17 * 3]; + cs_hexdump(0, er->ecmd5, 16, ecmd5s, sizeof(ecmd5s)); + if(reader->dropbadcws) + { + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + cs_log("Probably got bad CW from reader: %s, caid %04X, srvid %04X (%s) - dropping CW, lg: %i", reader->label, er->caid, er->srvid, ecmd5s +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + , 0); +#endif + } + else + { + cs_log("Probably got bad CW from reader: %s, caid %04X, srvid %04X (%s), lg: %i", reader->label, er->caid, er->srvid, ecmd5s +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + , 0); +#endif + } + } + } + + } + +#ifdef CW_CYCLE_CHECK + uint8_t cwc_ct = er->cwc_cycletime > 0 ? er->cwc_cycletime : 0; + uint8_t cwc_ncwc = er->cwc_next_cw_cycle < 2 ? er->cwc_next_cw_cycle : 2; + if(!checkcwcycle(er->client, er, reader, cw, rc, cwc_ct, cwc_ncwc)) + { +#ifdef CS_CACHEEX_AIO + if(!er->localgenerated) + { +#endif + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + cs_log_dbg(D_CACHEEX | D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck failed! Reader: %s set rc: %i", (er->client ? er->client->account->usr : "-"), er->caid, er->srvid, reader ? reader->label : "-", rc); + +#ifdef CS_CACHEEX_AIO + } + else + { + cs_log_dbg(D_CACHEEX | D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck failed! Reader: %s set rc: %i -> lg-flagged CW -> do nothing", (er->client ? er->client->account->usr : "-"), er->caid, er->srvid, reader ? reader->label : "-", rc); + } +#endif + } + else { cs_log_dbg(D_CACHEEX | D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck passed! Reader: %s rc: %i", (er->client ? er->client->account->usr : "-"), er->caid, er->srvid, reader ? reader->label : "-", rc); } +#endif + //END -- SPECIAL CHECKs for rc + +#ifdef CS_CACHEEX_AIO + if(!dontsetAnswered) + { +#endif + ea->status |= REQUEST_ANSWERED; +#ifdef CS_CACHEEX_AIO + } +#endif + ea->rc = rc; + ea->ecm_time = comp_timeb(&now, &ea->time_request_sent); + if(ea->ecm_time < 1) { ea->ecm_time = 1; } // set ecm_time 1 if answer immediately + ea->rcEx = rcEx; + if(cw) { memcpy(ea->cw, cw, 16); } + if(msglog) { memcpy(ea->msglog, msglog, MSGLOGSIZE); } + ea->tier = used_cardtier; + if(cw_ex) + { + ea->cw_ex = *cw_ex; + } + + cs_writeunlock(__func__, &ea->ecmanswer_lock); + + struct timeb tpe; + cs_ftime(&tpe); + int32_t ntime = comp_timeb(&tpe, &er->tps); + if(ntime < 1) { ntime = 1; } + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer] reader %s rc %d, ecm time %d ms (%d ms)", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, reader ? reader->label : "-", rc, ea->ecm_time, ntime); + + // send ea for ecm request + int32_t res = 0; + struct s_client *cl = er->client; + if(check_client(cl)) + { + res = 1; + add_job(er->client, ACTION_ECM_ANSWER_READER, ea, 0); // chk_dcw + } + + // distribute ea for pendings + if(ea->pending) // has pending ea + { distribute_ea(ea); } + + + if(!ea->is_pending) // not for pending ea - only once for ea + { + // cache update + // Skip check for BISS1 - cw could be indeed zero + // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero + if(ea && (ea->rc < E_NOTFOUND) && (!chk_is_null_CW(ea->cw) && !caid_is_biss(er->caid))) + { +#ifdef CS_CACHEEX_AIO + int32_t ecmtime = ea->ecm_time; + + if(er->cacheex_wait_time_expired && er->cacheex_wait_time) + ecmtime = ea->ecm_time + er->cacheex_wait_time; +#endif + add_cache_from_reader(er, reader, er->csp_hash, er->ecmd5, ea->cw, er->caid, er->prid, er->srvid +#ifdef CS_CACHEEX_AIO + , ecmtime); +#else + ); +#endif + } + + if(!dontwriteStats) + { + // readers stats for LB + send_reader_stat(reader, er, ea, ea->rc); + } + + // reader checks +#ifdef WITH_DEBUG + if(cs_dblevel & D_TRACE) + { + char ecmd5[17 * 3]; + cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); + rdr_log_dbg(reader, D_TRACE, "ecm answer for ecm hash %s rc=%d", ecmd5, ea->rc); + } +#endif + // Update reader stats: + if(ea->rc == E_FOUND) + { + if(cfg.cwlogdir != NULL) + { logCWtoFile(er, ea->cw); } // CWL logging only if cwlogdir is set in config + + reader->ecmsok++; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + reader->ecmsoklg++; +#endif + reader->webif_ecmsok++; +#ifdef CS_CACHEEX + struct s_client *eacl = reader->client; + if(cacheex_reader(reader) && check_client(eacl)) + { + eacl->cwcacheexgot++; + cacheex_add_stats(eacl, ea->er->caid, ea->er->srvid, ea->er->prid, 1 +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + ); +#endif + first_client->cwcacheexgot++; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + eacl->cwcacheexgotlg++; + first_client->cwcacheexgotlg++; + } +#endif + } +#endif + } + else if(ea->rc == E_NOTFOUND) + { + reader->ecmsnok++; + reader->webif_ecmsnok++; + if(reader->ecmnotfoundlimit && reader->ecmsnok >= reader->ecmnotfoundlimit) + { + rdr_log(reader, "ECM not found limit reached %u. Restarting the reader.", + reader->ecmsnok); + reader->ecmsnok = 0; // Reset the variable + reader->ecmshealthnok = 0; // Reset the variable + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + } + + // this fixes big oscam mistake + // wrong reader status on web info aka not counted timeouts which dispalyed + // reader info 100 percent OK but reader had a ton of unhandled timeouts! + else if(ea->rc == E_TIMEOUT) + { +#ifdef WITH_LB + STAT_QUERY q; + readerinfofix_get_stat_query(er, &q); + READER_STAT *s; + s = readerinfofix_get_add_stat(reader, &q); + if (s) + { + cs_log_dbg(D_LB, "inc fail {client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer] reader %s rc %d, ecm time %d ms (%d ms)", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, reader ? reader->label : "-", rc, ea->ecm_time, ntime); + readerinfofix_inc_fail(s); // now increase fail factor for unhandled timeouts + } +#endif + reader->ecmstout++; // now append timeouts to the readerinfo timeout count + reader->webif_ecmstout++; + } + + // Reader ECMs Health Try (by Pickser) + if(reader->ecmsok != 0 || reader->ecmsnok != 0 || reader->ecmstout != 0) + { + reader->ecmshealthok = ((double) reader->ecmsok / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; +#ifdef CS_CACHEEX_AIO + reader->ecmshealthoklg = ((double) reader->ecmsoklg / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; +#endif + reader->ecmshealthnok = ((double) reader->ecmsnok / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; + reader->ecmshealthtout = ((double) reader->ecmstout / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; + } + + if(rc == E_FOUND && reader->resetcycle > 0) + { + reader->resetcounter++; + if(reader->resetcounter > reader->resetcycle) + { + reader->resetcounter = 0; + rdr_log(reader, "Resetting reader, resetcyle of %d ecms reached", reader->resetcycle); + reader->card_status = CARD_NEED_INIT; + cardreader_reset(cl); + } + } + } + + return res; +} + +// chid calculation from module stat to here +// to improve the quickfix concerning ecm chid info and extend it +// to all client requests wereby the chid is known in module stat + +uint32_t get_subid(ECM_REQUEST *er) +{ + if(!er->ecmlen) + { return 0; } + + uint32_t id = 0; + switch(er->caid >> 8) + { + case 0x01: // seca + id = b2i(2, er->ecm + 7); + break; + + case 0x05: // viaccess + id = b2i(2, er->ecm + 8); + break; + + case 0x06: // irdeto + id = b2i(2, er->ecm + 6); + break; + + case 0x09: // videoguard + id = b2i(2, er->ecm + 11); + break; + + case 0x4A: // DRE-Crypt, Bulcrypt, Tongfang and others? + if(!caid_is_bulcrypt(er->caid) && !caid_is_dre(er->caid)) + { id = b2i(2, er->ecm + 6); } + break; + } + return id; +} + +static void set_readers_counter(ECM_REQUEST *er) +{ + struct s_ecm_answer *ea; + + er->reader_count = 0; + er->fallback_reader_count = 0; + er->localreader_count = 0; + er->cacheex_reader_count = 0; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if(ea->status & READER_ACTIVE) + { + if(!(ea->status & READER_FALLBACK)) + { er->reader_count++; } + else + { er->fallback_reader_count++; } + + if(cacheex_reader(ea->reader)) + { er->cacheex_reader_count++; } + else if(is_localreader(ea->reader, er)) + { er->localreader_count++; } + } + } +} + +void write_ecm_answer_fromcache(struct s_write_from_cache *wfc) +{ + ECM_REQUEST *er = NULL; + ECM_REQUEST *ecm = NULL; + + er = wfc->er_new; + ecm = wfc->er_cache; + +#ifdef CS_CACHEEX_AIO + if(ecm->localgenerated || (ecm->cw_count > 0x0F000000)) + er->localgenerated = 1; +#endif + + int8_t rc_orig = er->rc; + + er->grp |= ecm->grp; // update group +#ifdef CS_CACHEEX + if(ecm->from_csp) { er->csp_answered = 1; } // update er as answered by csp (csp have no group) +#endif + + if(er->rc >= E_NOTFOUND) + { +#ifdef CS_CACHEEX + if(ecm->cacheex_src) // from cacheex or csp + { + er->rc = E_CACHEEX; + } + else +#endif + { er->rc=E_CACHE1; } // from normal readers + + memcpy(er->cw, ecm->cw, 16); + er->selected_reader = ecm->selected_reader; + er->cw_count = ecm->cw_count; + +#ifdef CS_CACHEEX + // here we should be sure cex client has not been freed! + if(ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill) + { + er->cacheex_src = ecm->cacheex_src; + er->cwc_cycletime = ecm->cwc_cycletime; + er->cwc_next_cw_cycle = ecm->cwc_next_cw_cycle; + } + else + { + er->cacheex_src = NULL; + } + + int8_t cacheex = check_client(er->client) && er->client->account ? er->client->account->cacheex.mode : 0; + if(cacheex == 1 && check_client(er->client)) + { + cacheex_add_stats(er->client, er->caid, er->srvid, er->prid, 0 +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + ); +#endif + er->client->cwcacheexpush++; + if(er->client->account) + { er->client->account->cwcacheexpush++; } + first_client->cwcacheexpush++; + +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + er->client->cwcacheexpushlg++; + first_client->cwcacheexpushlg++; + } +#endif + + } +#endif + +#ifdef CS_CACHEEX + if(cfg.delay && cacheex!=1) // No delay on cacheexchange mode 1 client! + { cs_sleepms(cfg.delay); } +#else + if(cfg.delay) + { cs_sleepms(cfg.delay); } +#endif + + if(rc_orig == E_UNHANDLED) + { + cs_log_dbg(D_LB,"{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer_fromcache] found cw in CACHE (count %d)!", (check_client(er->client)?er->client->account->usr:"-"),er->caid, er->prid, er->srvid, +#ifdef CS_CACHEEX_AIO + (er->cw_count > 0x0F000000) ? er->cw_count ^= 0x0F000000 : er->cw_count); +#else + er->cw_count); +#endif + send_dcw(er->client, er); + } + } +} + +#ifdef CS_CACHEEX_AIO +static bool ecm_cache_check(ECM_REQUEST *er) +{ + if(ecm_cache_init_done && cfg.ecm_cache_droptime > 0) + { + ECM_CACHE *ecm_cache = NULL; + SAFE_RWLOCK_WRLOCK(&ecm_cache_lock); + ecm_cache = find_hash_table(&ht_ecm_cache, &er->csp_hash, sizeof(uint32_t), &compare_csp_hash_ecmcache); + if(!ecm_cache) + { + // ecm_cache-size(count/memory) pre-check + if( + (cfg.ecm_cache_size && (cfg.ecm_cache_size > tommy_hashlin_count(&ht_ecm_cache))) + || (cfg.ecm_cache_memory && (cfg.ecm_cache_memory*1024*1024 > tommy_hashlin_memory_usage(&ht_ecm_cache))) + ) + { + if(cs_malloc(&ecm_cache, sizeof(ECM_CACHE))) + { + ecm_cache->csp_hash = er->csp_hash; + cs_ftime(&ecm_cache->first_recv_time); + cs_ftime(&ecm_cache->upd_time); + + tommy_hashlin_insert(&ht_ecm_cache, &ecm_cache->ht_node, ecm_cache, tommy_hash_u32(0, &er->csp_hash, sizeof(er->csp_hash))); + tommy_list_insert_tail(&ll_ecm_cache, &ecm_cache->ll_node, ecm_cache); + + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + return true; + } + else{ + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + cs_log("[ecm_cache] ERROR: NO added HASH to ecm_cache!!"); + return false; + } + } + else{ + // clean cache call; + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + ecm_cache_cleanup(true); + return false; + } + } + // ecm found + else{ + int64_t gone_diff = 0; + gone_diff = comp_timeb(&er->tps, &ecm_cache->first_recv_time); + cs_ftime(&ecm_cache->upd_time); + + if(gone_diff >= cfg.ecm_cache_droptime * 1000) + { + cs_log_dbg(D_CW_CACHE, "[ecm_cache] ECM drop, current ecm_cache_size: %i - ecm_cache-mem-size: %i MiB", count_hash_table(&ht_ecm_cache), (int)(tommy_hashlin_memory_usage(&ht_ecm_cache)/1024/1024)); + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + return false; + } + } + + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + return true; + } + else{ + cs_log_dbg(D_CW_CACHE,"[ecm_cache] ecm_cache_init_done %i cfg.ecm_cache_size: %"PRIu32" cfg.ecm_cache_memory %"PRIu32" MiB", ecm_cache_init_done, cfg.ecm_cache_size, cfg.ecm_cache_memory); + return true; + } +} +#endif + +void get_cw(struct s_client *client, ECM_REQUEST *er) +{ +#ifdef CS_CACHEEX_AIO + cacheex_update_hash(er); + if(!ecm_cache_check(er)) + { + er->rc = E_INVALID; + send_dcw(client, er); + free_ecm(er); + return; + } +#endif + cs_log_dump_dbg(D_ATR, er->ecm, er->ecmlen, "get cw for ecm:"); + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] NEW REQUEST!", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + increment_n_request(client); + + int32_t i, j, m; + time_t now = time((time_t *)0); + uint32_t line = 0; + uint16_t sct_len; + + er->client = client; + er->rc = E_UNHANDLED; // set default rc status to unhandled + er->cwc_next_cw_cycle = 2; // set it to: we dont know + + // user was on freetv or didn't request for some time + // so we reset lastswitch to get correct stats/webif display + if(now - client->lastecm > cfg.hideclient_to) { client->lastswitch = 0; } + client->lastecm = now; + + if(client == first_client || !client ->account || client->account == first_client->account) + { + // DVBApi+serial is allowed to request anonymous accounts: + int16_t listenertype = get_module(client)->listenertype; + if(listenertype != LIS_DVBAPI && listenertype != LIS_SERIAL) + { + er->rc = E_INVALID; + er->rcEx = E2_GLOBAL; + snprintf(er->msglog, sizeof(er->msglog), "invalid user account %s", username(client)); + } + } + + // ecmlen must be 0 (no ecm) or >2 (because SCT_LEN() needs at least 3 bytes) + if(er->ecmlen < 0 || er->ecmlen == 1 || er->ecmlen == 2) + { + er->rc = E_INVALID; + er->rcEx = E2_GLOBAL; + snprintf(er->msglog, sizeof(er->msglog), "ECM size %d invalid, ignored! client %s", er->ecmlen, username(client)); + } + + if(er->ecmlen > MAX_ECM_SIZE) + { + er->rc = E_INVALID; + er->rcEx = E2_GLOBAL; + snprintf(er->msglog, sizeof(er->msglog), "ECM size %d > Max ECM size %d, ignored! client %s", er->ecmlen, MAX_ECM_SIZE, username(client)); + } + + if(er->ecmlen > 2) + { + sct_len = SCT_LEN(er->ecm); + if(sct_len > er->ecmlen || sct_len < 4) + { + er->rc = E_INVALID; + er->rcEx = E2_GLOBAL; + snprintf(er->msglog, sizeof(er->msglog), "Real ECM size %d > ECM size %d, ignored! client %s", sct_len, er->ecmlen, username(client)); + } + er->ecmlen = sct_len; + } + + if(!client->grp) + { + er->rc = E_INVALID; + er->rcEx = E2_GROUP; + snprintf(er->msglog, sizeof(er->msglog), "invalid user group %s", username(client)); + } + + // add chid for all client requests as in module stat + update_chid(er); + + // betacrypt ecm with nagra header + if(chk_is_betatunnel_caid(er->caid) == 1 && (er->ecmlen == 0x89 || er->ecmlen == 0x4A) && er->ecm[3] == 0x07 && (er->ecm[4] == 0x84 || er->ecm[4] == 0x45)) + { + if(er->caid == 0x1702) + { + er->caid = 0x1833; + } + else + { + check_lb_auto_betatunnel_mode(er); + } + cs_log_dbg(D_TRACE, "Quickfix remap beta->nagra: 0x%X, 0x%X, 0x%X, 0x%X", er->caid, er->ecmlen, er->ecm[3], er->ecm[4]); + } + + // nagra ecm with betacrypt header 1801, 1833, 1834, 1835 + if(chk_is_betatunnel_caid(er->caid) == 2 && (er->ecmlen == 0x93 || er->ecmlen == 0x54) && er->ecm[13] == 0x07 && (er->ecm[14] == 0x84 || er->ecm[14] == 0x45)) + { + if(er->caid == 0x1833) + { + er->caid = 0x1702; + } + else + { + er->caid = 0x1722; + } + cs_log_dbg(D_TRACE, "Quickfix remap nagra->beta: 0x%X, 0x%X, 0x%X, 0x%X", er->caid, er->ecmlen, er->ecm[13], er->ecm[44]); + } + + // Ariva quickfix (invalid nagra provider) + if(((er->caid & 0xFF00) == 0x1800) && er->prid > 0x00FFFF) + { er->prid = 0; } + + // Check for invalid provider, extract provider out of ecm: + uint32_t prid = chk_provid(er->ecm, er->caid); + if(!er->prid) + { + er->prid = prid; + } + else + { + if(prid && prid != er->prid) + { + cs_log_dbg(D_TRACE, "provider fixed: %04X@%06X to %04X@%06X", er->caid, er->prid, er->caid, prid); + er->prid = prid; + } + } + +#ifdef MODULE_NEWCAMD + // Set providerid for newcamd clients if none is given + if(!er->prid && client->ncd_server) + { + int32_t pi = client->port_idx; + if(pi >= 0 && cfg.ncd_ptab.nports && cfg.ncd_ptab.nports >= pi && cfg.ncd_ptab.ports[pi].ncd) + { er->prid = cfg.ncd_ptab.ports[pi].ncd->ncd_ftab.filts[0].prids[0]; } + } +#endif + + // CAID not supported or found + if(!er->caid) + { + er->rc = E_INVALID; + er->rcEx = E2_CAID; + snprintf(er->msglog, MSGLOGSIZE, "CAID not supported or found"); + } + + // user expired + if(client->expirationdate && client->expirationdate < client->lastecm) + { er->rc = E_EXPDATE; } + + // out of timeframe + if(client->allowedtimeframe_set) + { + struct tm acttm; + localtime_r(&now, &acttm); + int32_t curday = acttm.tm_wday; + char *dest = strstr(weekdstr,"ALL"); + int32_t all_idx = (dest - weekdstr) / 3; + uint8_t allowed = 0; + + // checkout if current time is allowed in the current day + allowed = CHECK_BIT(client->allowedtimeframe[curday][acttm.tm_hour][acttm.tm_min / 30], (acttm.tm_min % 30)); + + // or checkout if current time is allowed for all days + allowed |= CHECK_BIT(client->allowedtimeframe[all_idx][acttm.tm_hour][acttm.tm_min / 30], (acttm.tm_min % 30)); + + if(!(allowed)) + { + er->rc = E_EXPDATE; + } + cs_log_dbg(D_TRACE, "Check Timeframe - result: %d, day:%s time: %02dH%02d, allowed: %s\n", er->rc, shortDay[curday], acttm.tm_hour, acttm.tm_min, allowed ? "true" : "false"); + } + + // user disabled + if(client->disabled != 0) + { + if(client->failban & BAN_DISABLED) + { + cs_add_violation(client, client->account->usr); + cs_disconnect_client(client); + } + er->rc = E_DISABLED; + } + + if(!chk_global_whitelist(er, &line)) + { + debug_ecm(D_TRACE, "whitelist filtered: %s (%s) line %d", username(client), buf, line); + er->rc = E_INVALID; + } + +#ifdef CS_CACHEEX + if(client->account && client->account->cacheex.mode == 2 && !client->account->cacheex.allow_request) + { + er->rc = E_INVALID; + snprintf(er->msglog, MSGLOGSIZE, "invalid request from cacheex-2 client"); + } +#endif + + // rc < 100 -> ecm error + if(er->rc >= E_UNHANDLED) + { + m = er->caid; + i = er->srvid; + + if(i != client->last_srvid || !client->lastswitch) + { + if(cfg.usrfileflag) + { cs_statistics(client); } + client->lastswitch = now; + } + + // user sleeping + if(client->tosleep && (now - client->lastswitch > client->tosleep)) + { + if(client->failban & BAN_SLEEPING) + { + cs_add_violation(client, client->account->usr); + cs_disconnect_client(client); + } + if(client->c35_sleepsend != 0) + { + er->rc = E_STOPPED; // send sleep command CMD08 {00 255} + } + else + { + er->rc = E_SLEEPING; + } + } + + client->last_srvid = i; + client->last_caid = m; + client->last_provid = er->prid; + + int32_t ecm_len = (((er->ecm[1] & 0x0F) << 8) | er->ecm[2]) + 3; + + for(j = 0; (j < 6) && (er->rc >= E_UNHANDLED); j++) + { + switch(j) + { + case 0: + // fake (uniq) + if(client->dup) + { er->rc = E_FAKE; } + break; + + case 1: + // invalid (caid) + if(!chk_bcaid(er, &client->ctab)) + { + er->rc = E_INVALID; + er->rcEx = E2_CAID; + snprintf(er->msglog, MSGLOGSIZE, "invalid caid 0x%04X", er->caid); + } + break; + + case 2: + // invalid (srvid) + // matching srvids (or 0000) specified in betatunnel will bypass this filter + if(!chk_srvid(client, er)) + { + if(!chk_on_btun(SRVID_ZERO, client, er)) + { + er->rc = E_INVALID; + snprintf(er->msglog, MSGLOGSIZE, "invalid SID"); + } + } + break; + + case 3: + // invalid (ufilters) + if(!chk_ufilters(er)) + { er->rc = E_INVALID; } + break; + + case 4: + // invalid (sfilter) + if(!chk_sfilter(er, &get_module(client)->ptab)) + { er->rc = E_INVALID; } + break; + + case 5: + // corrupt + if((i = er->ecmlen - ecm_len)) + { + if(i > 0) + { + cs_log_dbg(D_TRACE, "warning: ecm size adjusted from %d to %d", er->ecmlen, ecm_len); + er->ecmlen = ecm_len; + } + else + { er->rc = E_CORRUPT; } + } + break; + } + } + } + + // Check for odd/even byte + // Don't check for BISS1 and BISS2 mode 1/E or fake caid (ECM is fake for them) + // Don't check for BISS2 mode CA (ECM table is always 0x80) + if(!caid_is_biss(er->caid) && !caid_is_fake(er->caid) && get_odd_even(er) == 0) + { + cs_log_dbg(D_TRACE, "warning: ecm with null odd/even byte from %s", (check_client(er->client) ? er->client->account->usr : "-")); + er->rc = E_INVALID; + } + + // not continue, send rc to client + if(er->rc < E_UNHANDLED) + { + send_dcw(client, er); + free_ecm(er); + return; + } + + +#ifdef CS_CACHEEX + int8_t cacheex = client->account ? client->account->cacheex.mode : 0; + er->from_cacheex1_client = 0; + if(cacheex == 1) {er->from_cacheex1_client = 1;} +#endif + + + // set preferlocalcards for this ecm request (actually, paramter + // is per user based, maybe in fiture it will be caid based too) + er->preferlocalcards = cfg.preferlocalcards; + if(client->account && client->account->preferlocalcards > -1) + { + er->preferlocalcards = client->account->preferlocalcards; + } + if(er->preferlocalcards <0 || er->preferlocalcards >2) {er->preferlocalcards=0;} + + + if(chk_is_betatunnel_caid(er->caid) && client->ttab.ttnum) + { + cs_log_dump_dbg(D_TRACE, er->ecm, 13, "betatunnel? ecmlen=%d", er->ecmlen); + cs_betatunnel(er); + } + + + // ignore ecm... + int32_t offset = 3; + + // ...and betacrypt header for cache md5 calculation + if(caid_is_betacrypt(er->caid)) + { offset = 13; } + + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + + // store ECM in cache + memcpy(er->ecmd5, MD5(er->ecm + offset, er->ecmlen - offset, md5tmp), CS_ECMSTORESIZE); + cacheex_update_hash(er); + ac_chk(client, er, 0); + + + //******** CHECK IF FOUND ECM IN CACHE + struct ecm_request_t *ecm = NULL; + ecm = check_cache(er, client); + if(ecm) // found in cache + { + cs_log_dbg(D_LB,"{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] cw found immediately in cache! ", (check_client(er->client)?er->client->account->usr:"-"),er->caid, er->prid, er->srvid); + + struct s_write_from_cache *wfc = NULL; + if(!cs_malloc(&wfc, sizeof(struct s_write_from_cache))) + { + NULLFREE(ecm); + free_ecm(er); + return; + } + + wfc->er_new = er; + wfc->er_cache = ecm; + write_ecm_answer_fromcache(wfc); + NULLFREE(wfc); + NULLFREE(ecm); + free_ecm(er); + + return; + } + +// zaplist ACoSC +#ifdef CS_ANTICASC + if(cfg.acosc_enabled) + { + cs_writelock(__func__, &clientlist_lock); + insert_zaplist(er, client); + cs_writeunlock(__func__, &clientlist_lock); + } +#endif + + er->reader_avail = 0; + er->readers = 0; + + struct s_ecm_answer *ea, *prv = NULL; + struct s_reader *rdr; + + cs_readlock(__func__, &readerlist_lock); + cs_readlock(__func__, &clientlist_lock); + + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + uint8_t is_fallback = chk_is_fixed_fallback(rdr, er); + int8_t match = matching_reader(er, rdr); + + if(!match) // if this reader does not match, check betatunnel for it + match = lb_check_auto_betatunnel(er, rdr); + + if(match) + { + er->reader_avail++; + +#ifdef CS_CACHEEX + if(cacheex == 1 && !cacheex_reader(rdr)) // ex1-cl only ask ex1-rdr + { continue; } +#endif + + if(!cs_malloc(&ea, sizeof(struct s_ecm_answer))) + { goto OUT; } + +#ifdef WITH_EXTENDED_CW + // Correct CSA mode is CBC - default to that instead + ea->cw_ex.algo_mode = CW_ALGO_MODE_CBC; +#endif + + er->readers++; + + ea->reader = rdr; + ea->er = er; + ea->rc = E_UNHANDLED; + if(prv) + { prv->next = ea; } + else + { er->matching_rdr = ea; } + prv = ea; + + ea->status = READER_ACTIVE; + if(cacheex_reader(rdr)) + { ea->status |= READER_CACHEEX; } + else if(is_localreader(rdr, er)) + { ea->status |= READER_LOCAL; } + + if(is_fallback && (!is_localreader(rdr, er) || (is_localreader(rdr, er) && !er->preferlocalcards))) + { ea->status |= READER_FALLBACK; } + + ea->pending = NULL; + ea->is_pending = false; + cs_lock_create(__func__, &ea->ecmanswer_lock, "ecmanswer_lock", 5000); + } + } + +OUT: + cs_readunlock(__func__, &clientlist_lock); + cs_readunlock(__func__, &readerlist_lock); + + lb_set_best_reader(er); + + // set reader_count and fallback_reader_count + set_readers_counter(er); + + // if preferlocalcards > 0, check if we have local readers selected: + // if not, switch to preferlocalcards = 0 for this ecm + if(er->preferlocalcards > 0) + { + if(er->localreader_count == 0) + { + er->preferlocalcards = 0; + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} NO local readers, set preferlocalcards = %d", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, er->preferlocalcards); + } + } + +#ifdef CS_CACHEEX + // WAIT_TIME + uint32_t cacheex_wait_time = 0; + uint32_t wait_time_no_hitcache = 0; + uint32_t wait_time_hitcache = 0; + + if(client->account && !client->account->no_wait_time +#ifdef CS_CACHEEX_AIO + && !chk_srvid_no_wait_time(er) +#endif + && er->preferlocalcards<2) + { + wait_time_no_hitcache = get_cacheex_wait_time(er,NULL); // NO check hitcache. Wait_time is dwtime, or, if 0, awtime. + wait_time_hitcache = get_cacheex_wait_time(er,client); // check hitcache for calculating wait_time! If hitcache wait_time is biggest value between dwtime and awtime, else it's awtime. + + if( + // If "normal" client and ex1-rdr>0, we cannot use hitcache for calculating wait_time because we cannot know if cw is available or not on ex1 server! + (cacheex != 1 && er->cacheex_reader_count) + || + /* Cw for ex1-cl comes from: INT. cache by "normal" readers (normal clients that ask normal readers), ex1-rdr and ex2-rdr and ex3-rdr. + * If readers, we have to wait cws generating by normal clients asking normal readers and answers by ex1-rdr (cannot use hitcache). + * If no readers, use hitcache for calculating wait_time. + */ + (cacheex == 1 && er->reader_avail) + ) + { cacheex_wait_time = wait_time_no_hitcache; } + else + { cacheex_wait_time = wait_time_hitcache; } + } + + cs_log_dbg(D_TRACE | D_CACHEEX, "[GET_CW] wait_time %d caid %04X prov %06X srvid %04X rc %d cacheex cl mode %d ex1rdr %d", cacheex_wait_time, er->caid, er->prid, er->srvid, er->rc, cacheex, er->cacheex_reader_count); + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] wait_time %d - client cacheex mode %d, reader avail for ecm %d, hitcache %d, preferlocalcards %d", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, cacheex_wait_time, cacheex == 1 ? 1 : 0, er->reader_avail, wait_time_hitcache ? 1 : 0, er->preferlocalcards); + // END WAIT_TIME calculation + + if(!cacheex_wait_time && (er->reader_count + er->fallback_reader_count) == 0) +#else + if((er->reader_count + er->fallback_reader_count) == 0) +#endif + { + er->rc = E_NOTFOUND; + if(!er->rcEx) + { er->rcEx = E2_GROUP; } + snprintf(er->msglog, MSGLOGSIZE, "no matching reader"); + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] NO Readers and NO wait_time... not_found! ", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + send_dcw(client, er); + free_ecm(er); + return; + } + + //insert it in ecmcwcache! + cs_writelock(__func__, &ecmcache_lock); + er->next = ecmcwcache; + ecmcwcache = er; + ecmcwcache_size++; + cs_writeunlock(__func__, &ecmcache_lock); + + er->rcEx = 0; +#ifdef CS_CACHEEX + er->cacheex_wait_time = 0; + er->cacheex_wait_time_expired = 1; + er->cacheex_hitcache = 0; + er->cacheex_mode1_delay = 0; + + if(cacheex_wait_time) // wait time for cacheex + { + er->cacheex_wait_time = cacheex_wait_time; + er->cacheex_wait_time_expired = 0; + er->cacheex_hitcache = wait_time_hitcache ? 1 : 0; // usefull only when cacheex mode 1 readers answers before wait_time and we have to decide if we have to wait until wait_time expires. + er->cacheex_mode1_delay = get_cacheex_mode1_delay(er); + + if(!er->cacheex_mode1_delay && er->cacheex_reader_count > 0) + { + request_cw_from_readers(er, 1); // setting stop_stage=1, we request only cacheex mode 1 readers. Others are requested at cacheex timeout! + } + } + else +#endif + request_cw_from_readers(er, 0); + +#ifdef WITH_DEBUG + if(D_CLIENTECM & cs_dblevel) + { + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_log_dump_dbg(D_CLIENTECM, er->ecm, er->ecmlen, "Client %s ECM dump %s", username(client), buf); + } +#endif + + cw_process_thread_wakeup(); +} + +int32_t ecmfmt(char *result, size_t size, uint16_t caid, uint16_t onid, uint32_t prid, uint16_t chid, uint16_t pid, + uint16_t srvid, uint16_t l, char *ecmd5hex, char *csphash, char *cw, uint16_t origin_peer, uint8_t distance, char *payload, char *tier) +{ + if(!cfg.ecmfmt) + { + if(tier && payload) + { + return snprintf(result, size, "%04X@%06X/%04X/%04X/%02X:%s:0F06%s:%s", caid, prid, chid, srvid, l, ecmd5hex, payload, tier); + } + else if(tier) + { + return snprintf(result, size, "%04X@%06X/%04X/%04X/%02X:%s:%s", caid, prid, chid, srvid, l, ecmd5hex, tier); + } + else if(payload) + { + return snprintf(result, size, "%04X@%06X/%04X/%04X/%02X:%s:0F06%s", caid, prid, chid, srvid, l, ecmd5hex, payload); + } + else + { + return snprintf(result, size, "%04X@%06X/%04X/%04X/%02X:%s", caid, prid, chid, srvid, l, ecmd5hex); + } + } + +#define ECMFMT_NUMBER 0 +#define ECMFMT_STRING 1 +#define ECMFMT_CHAR 2 + + uint8_t type = 0; + uint32_t ivalue = 0; + char *ifmt = NULL, *sfmt = NULL; + char *svalue = NULL, cvalue = '\0'; + uint8_t hide_if_zero = 0; + char *c; + uint32_t s = 0; + + for(c = cfg.ecmfmt; *c; c++) + { + if(*c == '0') + { + hide_if_zero = 1; + continue; + } + + sfmt = NULL; + + switch(*c) + { + case 't': + type = ECMFMT_STRING; + svalue = tier; + if(tier == NULL && !hide_if_zero) + { + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = 0; + } + break; + + case 'c': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = caid; + break; + + case 'o': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = onid; + break; + + case 'p': + type = ECMFMT_NUMBER; + ifmt = "%06X"; + ivalue = prid; + break; + + case 'i': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = chid; + break; + + case 'd': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = pid; + break; + + case 's': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = srvid; + break; + + case 'l': + type = ECMFMT_NUMBER; + ifmt = "%02X"; + ivalue = l; + break; + + case 'h': + type = ECMFMT_STRING; + svalue = ecmd5hex; + break; + + case 'e': + type = ECMFMT_STRING; + svalue = csphash; + break; + + case 'w': + type = ECMFMT_STRING; + svalue = cw; + break; + + case 'j': + type = ECMFMT_NUMBER; + ifmt = "%02X"; + ivalue = distance; + break; + + case 'g': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = origin_peer; + break; + + case '\\': + c++; + type = ECMFMT_CHAR; + cvalue = *c; + + if(cvalue == '\0') + { return s; } + break; + + case 'y': + type = ECMFMT_STRING; + svalue = payload; + sfmt = "0F06%.06s"; + if(payload == NULL && !hide_if_zero) + { + type = ECMFMT_NUMBER; + ifmt = "0F06%06X"; + ivalue = 0; + } + break; + + case 'Y': + type = ECMFMT_STRING; + svalue = payload; + sfmt = "0F06%s"; + if(payload == NULL && !hide_if_zero) + { + type = ECMFMT_NUMBER; + ifmt = "0F06%12X"; + ivalue = 0; + } + break; + + default: + type = ECMFMT_CHAR; + cvalue = *c; + break; + } + + if(hide_if_zero) + { + if(type == ECMFMT_NUMBER && ivalue == 0) + { + hide_if_zero = 0; + continue; + } + else if(type == ECMFMT_STRING && svalue == NULL) + { + hide_if_zero = 0; + continue; + } + } + + switch(type) + { + case ECMFMT_NUMBER: + s += snprintf(result + s, size - s, ifmt, ivalue); + break; + + case ECMFMT_STRING: + s += snprintf(result + s, size - s , sfmt != NULL ? sfmt : "%s", svalue); + break; + + case ECMFMT_CHAR: + if(size - s > 1) + { + result[s] = cvalue; + result[s+1] = '\0'; + s++; + } + break; + + default: + break; + } + } + + return s; +} + +int32_t format_ecm(ECM_REQUEST *ecm, char *result, size_t size) +{ + char ecmd5hex[(16*2)+1]; + char csphash[(4*2)+1] = { 0 }; + char cwhex[(16*2)+1]; + char *payload = NULL; + char *tier = NULL; +#ifdef READER_VIDEOGUARD + char payload_string[(6*2)+1]; + char tier_string[83]; + struct s_ecm_answer *ea; + + if(ecm->selected_reader && caid_is_videoguard(ecm->selected_reader->caid) && !is_network_reader(ecm->selected_reader)) + { + for(ea = ecm->matching_rdr; ea; ea = ea->next) + { + if(ea->tier && (ea->status & REQUEST_ANSWERED) && !is_network_reader(ea->reader)) + { + get_tiername_defaultid(ea->tier, ecm->selected_reader->caid, tier_string); + tier = tier_string; + break; + } + } + + cs_hexdump(0, ecm->selected_reader->VgLastPayload, 6, payload_string, sizeof(payload_string)); + payload = payload_string; + } +#endif + cs_hexdump(0, ecm->ecmd5, 16, ecmd5hex, sizeof(ecmd5hex)); +#ifdef CS_CACHEEX + cs_hexdump(0, (void *)&ecm->csp_hash, 4, csphash, sizeof(csphash)); +#endif + cs_hexdump(0, ecm->cw, 16, cwhex, sizeof(cwhex)); +#ifdef MODULE_GBOX + if(check_client(ecm->client) && get_module(ecm->client)->num == R_GBOX && ecm->gbox_ecm_dist) + { return ecmfmt(result, size, ecm->caid, ecm->onid, ecm->prid, ecm->chid, ecm->pid, ecm->srvid, ecm->ecmlen, ecmd5hex, csphash, cwhex, ecm->gbox_ecm_src_peer, ecm->gbox_ecm_dist, payload, tier); } + else if (ecm->selected_reader && ecm->selected_reader->typ == R_GBOX && !ecm->gbox_ecm_dist) + { return ecmfmt(result, size, ecm->caid, ecm->onid, ecm->prid, ecm->chid, ecm->pid, ecm->srvid, ecm->ecmlen, ecmd5hex, csphash, cwhex, ecm->selected_reader->gbox_cw_src_peer, ecm->selected_reader->currenthops, payload, tier); } + else +#endif + return ecmfmt(result, size, ecm->caid, ecm->onid, ecm->prid, ecm->chid, ecm->pid, ecm->srvid, ecm->ecmlen, ecmd5hex, csphash, cwhex, 0, + ((ecm->selected_reader && ecm->selected_reader->currenthops) ? ecm->selected_reader->currenthops : 0), payload, tier); +} diff --git a/oscam-ecm.h b/oscam-ecm.h new file mode 100644 index 0000000..f40bfab --- /dev/null +++ b/oscam-ecm.h @@ -0,0 +1,63 @@ +#ifndef OSCAM_ECM_H_ +#define OSCAM_ECM_H_ + +void cw_process_thread_start(void); +void cw_process_thread_wakeup(void); + +void convert_to_beta(struct s_client *cl, ECM_REQUEST *er, uint16_t caidto); +void convert_to_nagra(struct s_client *cl, ECM_REQUEST *er, uint16_t caidto); + +int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, uint8_t rcEx, uint8_t *cw, char *msglog, uint16_t used_cardtier, EXTENDED_CW* cw_ex); + +void get_cw(struct s_client *, ECM_REQUEST *); + +void update_chid(ECM_REQUEST *ecm); +uint32_t get_subid(ECM_REQUEST *er); +uint32_t chk_provid(uint8_t *ecm, uint16_t caid); + +int32_t send_dcw(struct s_client *client, ECM_REQUEST *er); +void free_ecm(ECM_REQUEST *ecm); +void free_push_in_ecm(ECM_REQUEST *ecm); +void write_ecm_answer_fromcache(struct s_write_from_cache *wfc); +void fallback_timeout(ECM_REQUEST *er); +void ecm_timeout(ECM_REQUEST *er); +void reader_get_ecm(struct s_reader *reader, ECM_REQUEST *er); +ECM_REQUEST *get_ecmtask(void); +struct s_ecm_answer *get_ecm_answer(struct s_reader *reader, ECM_REQUEST *er); +void cleanup_ecmtasks(struct s_client *cl); +void remove_reader_from_ecm(struct s_reader *rdr); + +void chk_dcw(struct s_ecm_answer *ea); +void request_cw_from_readers(ECM_REQUEST *er, uint8_t stop_stage); + +void checkCW(ECM_REQUEST *er); +uint8_t checkCWpart(uint8_t *cw, int8_t part); + +// CW VOTING +int cw_vote_add(struct ecm_request_t *er, uint8_t *cw, struct s_reader *rdr); +int cw_vote_decide(struct ecm_request_t *er); +int32_t is_cwvote_caid(ECM_REQUEST *er); +void refresh_cw_vote_config(void); + +#ifdef CS_CACHEEX_AIO +void init_ecm_cache(void); +void free_ecm_cache(void); + +void ecm_cache_cleanup(bool force); +#endif + +#define debug_ecm(mask, args...) \ + do { \ + if (config_enabled(WITH_DEBUG) && ((mask) & cs_dblevel)) { \ + char buf[ECM_FMT_LEN]; \ + format_ecm(er, buf, ECM_FMT_LEN); \ + cs_log_dbg(mask, ##args); \ + } \ + } while(0) + +int32_t ecmfmt(char *result, size_t size, uint16_t caid, uint16_t onid, uint32_t prid, uint16_t chid, uint16_t pid, + uint16_t srvid, uint16_t l, char *ecmd5hex, char *csphash, char *cw, uint16_t origin_peer, uint8_t distance, char *payload, char *tier); + +int32_t format_ecm(ECM_REQUEST *ecm, char *result, size_t size); + +#endif diff --git a/oscam-emm-cache.c b/oscam-emm-cache.c new file mode 100644 index 0000000..2c3e09a --- /dev/null +++ b/oscam-emm-cache.c @@ -0,0 +1,534 @@ +#define MODULE_LOG_PREFIX "emmcache" + +#include "globals.h" +#include "oscam-config.h" +#include "oscam-string.h" +#include "oscam-emm-cache.h" +#include "oscam-files.h" +#include "oscam-time.h" +#include "oscam-lock.h" +#include "cscrypt/md5.h" +#define LINESIZE 1024 +#define DEFAULT_LOCK_TIMEOUT 1000000 + +static LLIST *emm_cache; + +bool emm_cache_configured(void) +{ + struct s_reader *rdr; + bool enable = false; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->cachemm == 1) + { + enable = true; + } + } + return enable; +} + +static char *get_emmcache_filename(char *dest, size_t destlen, const char *filename) +{ + const char *slash = ""; + if(cfg.emmlogdir[strlen(cfg.emmlogdir) - 1] != '/') + { + slash = "/"; + } + snprintf(dest, destlen, "%s%s%s", cfg.emmlogdir, slash, filename); + return dest; +} + +void emm_save_cache(void) +{ + if(boxtype_is("dbox2")) return; // don't save emmcache on these boxes, they lack resources and will crash! + + if(!emm_cache_configured()){ + cs_log("saving emmcache disabled since no reader is using it!"); + return; + } + + char fname[256]; + struct timeb ts, te; + + if(cfg.emmlogdir) + { + get_emmcache_filename(fname, sizeof(fname), "oscam.emmcache"); + } + else + { + get_config_filename(fname, sizeof(fname), "oscam.emmcache"); + } + FILE *file = fopen(fname, "w"); + + if(!file) + { + cs_log("can't write emmcache to file %s", fname); + return; + } + + cs_ftime(&ts); + int32_t count = 0, result = 0; + LL_ITER it = ll_iter_create(emm_cache); + struct s_emmcache *c; + + while((c = ll_iter_next(&it))) + { + uint8_t tmp_emmd5[MD5_DIGEST_LENGTH * 2 + 1]; + char_to_hex(c->emmd5, MD5_DIGEST_LENGTH, tmp_emmd5); + uint8_t tmp_emm[c->len * 2 + 1]; + char_to_hex(c->emm, c->len, tmp_emm); + + result = fprintf(file, "%s,%" PRId64 ",%" PRId64 ",%02X,%04X,%s\n", tmp_emmd5, (int64_t)c->firstseen.time, (int64_t)c->lastseen.time, c->type, c->len, tmp_emm); + if(result < 0) + { + fclose(file); + result = remove(fname); + if(!result) + { + cs_log("error writing cache -> cache file removed!"); + } + else + { + cs_log("error writing cache -> cache file could not be removed either!"); + } + return; + } + count++; + } + + fclose(file); + cs_ftime(&te); + int64_t load_time = comp_timeb(&te, &ts); + cs_log("saved %d emmcache records to %s in %"PRId64" ms", count, fname, load_time); +} + +void load_emmstat_from_file(void) +{ + if(boxtype_is("dbox2")) return; // dont load emmstat on these boxes, they lack resources and will crash! + + if(!emm_cache_configured()){ + cs_log("loading emmstats disabled since no reader is using it!"); + return; + } + + char buf[256]; + char fname[256]; + char *line; + FILE *file; + + if(cfg.emmlogdir) + { + get_emmcache_filename(fname, sizeof(fname), "oscam.emmstat"); + } + else + { + get_config_filename(fname, sizeof(fname), "oscam.emmstat"); + } + + file = fopen(fname, "r"); + if(!file) + { + cs_log_dbg(D_TRACE, "can't read emmstats from file %s", fname); + return; + } + + if(!cs_malloc(&line, LINESIZE)) + { + fclose(file); + return; + } + + struct timeb ts, te; + cs_ftime(&ts); + + struct s_reader *rdr = NULL; + struct s_emmstat *s; + + int32_t i = 1; + int32_t valid = 0; + int32_t count = 0; + char *ptr, *saveptr1 = NULL; + char *split[7]; + + while(fgets(line, LINESIZE, file)) + { + if(!line[0] || line[0] == '#' || line[0] == ';') + { continue; } + + if(!cs_malloc(&s, sizeof(struct s_emmstat))) + { continue; } + + for(i = 0, ptr = strtok_r(line, ",", &saveptr1); ptr && i < 7 ; ptr = strtok_r(NULL, ",", &saveptr1), i++) + { split[i] = ptr; } + + valid = (i == 6); + if(valid) + { + cs_strncpy(buf, split[0], sizeof(buf)); + key_atob_l(split[1], s->emmd5, MD5_DIGEST_LENGTH * 2); + s->firstwritten.time = atol(split[2]); + s->lastwritten.time = atol(split[3]); + s->type = a2i(split[4], 2); + s->count = a2i(split[5], 4); + + LL_ITER itr = ll_iter_create(configured_readers); + + while((rdr = ll_iter_next(&itr))) + { + if(rdr->cachemm !=1) // skip: emmcache save is disabled + { + continue; + } + + if(strcmp(rdr->label, buf) == 0) + { + break; + } + } + + if(rdr != NULL) + { + if(!rdr->emmstat) + { + rdr->emmstat = ll_create("emmstat"); + cs_lock_create(__func__, &rdr->emmstat_lock, rdr->label, DEFAULT_LOCK_TIMEOUT); + } + + ll_append(rdr->emmstat, s); + count++; + } + else + { + cs_log("emmstats could not be loaded for %s", buf); + NULLFREE(s); + } + } + else + { + cs_log_dbg(D_EMM, "emmstat ERROR: %s count=%d type=%d", buf, s->count, s->type); + NULLFREE(s); + } + } + + fclose(file); + NULLFREE(line); + + cs_ftime(&te); + int64_t load_time = comp_timeb(&te, &ts); + cs_log("loaded %d emmstat records from %s in %"PRId64" ms", count, fname, load_time); +} + +void save_emmstat_to_file(void) +{ + if(boxtype_is("dbox2")) return; // don't save emmstat on these boxes, they lack resources and will crash! + + if(!emm_cache_configured()) + { + cs_log("saving emmstats disabled since no reader is using it!"); + return; + } + + char fname[256]; + + if(cfg.emmlogdir) + { + get_emmcache_filename(fname, sizeof(fname), "oscam.emmstat"); + } + else + { + get_config_filename(fname, sizeof(fname), "oscam.emmstat"); + } + FILE *file = fopen(fname, "w"); + + if(!file) + { + cs_log("can't write to file %s", fname); + return; + } + + struct timeb ts, te; + cs_ftime(&ts); + + int32_t count = 0, result = 0; + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(!rdr->cachemm || rdr->cachemm == 2) + { + cs_log("reader %s skipped since emmcache save is disabled", rdr->label); + continue; + } + + if(rdr->emmstat) + { + cs_writelock(__func__, &rdr->emmstat_lock); + LL_ITER it = ll_iter_create(rdr->emmstat); + struct s_emmstat *s; + while((s = ll_iter_next(&it))) + { + uint8_t tmp_emmd5[MD5_DIGEST_LENGTH * 2 + 1]; + char_to_hex(s->emmd5, MD5_DIGEST_LENGTH, tmp_emmd5); + result = fprintf(file, "%s,%s,%" PRId64 ",%" PRId64 ",%02X,%04X\n", rdr->label, tmp_emmd5, (int64_t)s->firstwritten.time, (int64_t)s->lastwritten.time, s->type, s->count); + if(result < 0) + { + cs_writeunlock(__func__, &rdr->emmstat_lock); + fclose(file); + result = remove(fname); + if(!result) + { + cs_log("error writing stats -> stat file removed!"); + } + else + { + cs_log("error writing stats -> stat file could not be removed either!"); + } + return; + } + count++; + } + cs_writeunlock(__func__, &rdr->emmstat_lock); + } + } + + fclose(file); + + cs_ftime(&te); + int64_t load_time = comp_timeb(&te, &ts); + + cs_log("saved %d emmstat records to %s in %"PRId64" ms", count, fname, load_time); +} + +void emm_load_cache(void) +{ + if(boxtype_is("dbox2")) return; // don't load emmcache on these boxes, they lack resources and will crash! + + if(!emm_cache_configured()){ + cs_log("loading emmcache disabled since no reader is using it!"); + return; + } + + char fname[256]; + char line[1024]; + FILE *file; + struct s_emmcache *c; + + if(cfg.emmlogdir) + { + get_emmcache_filename(fname, sizeof(fname), "oscam.emmcache"); + } + else + { + get_config_filename(fname, sizeof(fname), "oscam.emmcache"); + } + + file = fopen(fname, "r"); + if(!file) + { + cs_log_dbg(D_TRACE, "can't read emmcache from file %s", fname); + return; + } + + struct timeb ts, te; + cs_ftime(&ts); + + int32_t count = 0; + int32_t i = 1; + int32_t valid = 0; + char *ptr, *saveptr1 = NULL; + char *split[7]; + + memset(line, 0, sizeof(line)); + while(fgets(line, sizeof(line), file)) + { + if(!line[0] || line[0] == '#' || line[0] == ';') + { continue; } + + for(i = 0, ptr = strtok_r(line, ",", &saveptr1); ptr && i < 7 ; ptr = strtok_r(NULL, ",", &saveptr1), i++) + { + split[i] = ptr; + } + + valid = (i == 6); + if(valid) + { + if(!cs_malloc(&c, sizeof(struct s_emmcache))) + { continue; } + key_atob_l(split[0], c->emmd5, MD5_DIGEST_LENGTH*2); + c->firstseen.time = atol(split[1]); + c->lastseen.time = atol(split[2]); + c->type = a2i(split[3], 2); + c->len = a2i(split[4], 4); + key_atob_l(split[5], c->emm, c->len*2); + + if(valid && c->len != 0) + { + if(!emm_cache) + { + emm_cache = ll_create("emm cache"); + } + + ll_append(emm_cache, c); + count++; + } + else + { + NULLFREE(c); + } + } + } + fclose(file); + cs_ftime(&te); + int64_t load_time = comp_timeb(&te, &ts); + cs_log("loaded %d emmcache records from %s in %"PRId64" ms", count, fname, load_time); +} + +struct s_emmcache *find_emm_cache(uint8_t *emmd5) +{ + struct s_emmcache *c; + LL_ITER it; + + if(!emm_cache) + { emm_cache = ll_create("emm cache"); } + + it = ll_iter_create(emm_cache); + while((c = ll_iter_next(&it))) + { + if(!memcmp(emmd5, c->emmd5, MD5_DIGEST_LENGTH)) + { + cs_log_dump_dbg(D_EMM, c->emmd5, MD5_DIGEST_LENGTH, "found emmcache match"); + return c; + } + } + return NULL; +} + +int32_t clean_stale_emm_cache_and_stat(uint8_t *emmd5, int64_t gone) +{ + struct timeb now; + cs_ftime(&now); + int32_t count = 0; + + struct s_emmcache *c; + LL_ITER it; + + if(!emm_cache) + { emm_cache = ll_create("emm cache"); } + + it = ll_iter_create(emm_cache); + while((c = ll_iter_next(&it))) + { + if(comp_timeb(&now, &c->lastseen) > gone && memcmp(c->emmd5, emmd5, MD5_DIGEST_LENGTH)) // clean older than gone ms and dont clean if its the current emm! + { + struct s_reader *rdr; + LL_ITER rdr_itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&rdr_itr))) + { + if(rdr->emmstat && !(caid_is_irdeto(rdr->caid) || caid_is_videoguard(rdr->caid))) + { + remove_emm_stat(rdr, c->emmd5); // clean stale entry from stats + count++; + } + } + ll_iter_remove_data(&it); // clean stale entry from emmcache + } + } + return count; +} + +int32_t emm_edit_cache(uint8_t *emmd5, EMM_PACKET *ep, bool add) +{ + struct s_emmcache *c; + LL_ITER it; + int32_t count = 0; + + if(!emm_cache) + { emm_cache = ll_create("emm cache"); } + + it = ll_iter_create(emm_cache); + while((c = ll_iter_next(&it))) + { + if(!memcmp(emmd5, c->emmd5, MD5_DIGEST_LENGTH)) + { + if(add) + { + return 0; // already added + } + ll_iter_remove_data(&it); + count++; + } + } + + if(add) + { + if(!cs_malloc(&c, sizeof(struct s_emmcache))) + { return count; } + memcpy(c->emmd5, emmd5, MD5_DIGEST_LENGTH); + c->type = ep->type; + c->len = SCT_LEN(ep->emm); + cs_ftime(&c->firstseen); + c->lastseen = c->firstseen; + memcpy(c->emm, ep->emm, c->len); + ll_append(emm_cache, c); +#ifdef WITH_DEBUG + cs_log_dump_dbg(D_EMM, c->emmd5, MD5_DIGEST_LENGTH, "added emm to cache:"); +#endif + count++; + } + + return count; +} + +int32_t remove_emm_stat(struct s_reader *rdr, uint8_t *emmd5) +{ + int32_t count = 0; + if(rdr && rdr->emmstat) + { + cs_writelock(__func__, &rdr->emmstat_lock); + struct s_emmstat *c; + LL_ITER itr = ll_iter_create(rdr->emmstat); + while((c = ll_iter_next(&itr))) + { + if(!memcmp(emmd5, c->emmd5, MD5_DIGEST_LENGTH)) + { + ll_iter_remove_data(&itr); + count++; + break; + } + } + + cs_writeunlock(__func__, &rdr->emmstat_lock); + } + return count; +} + +struct s_emmstat *get_emm_stat(struct s_reader *rdr, uint8_t *emmd5, uint8_t emmtype) +{ + if(!rdr->cachemm) return NULL; + + struct s_emmstat *c; + LL_ITER it; + + if(!rdr->emmstat) + { rdr->emmstat = ll_create("emm stat"); } + + it = ll_iter_create(rdr->emmstat); + while((c = ll_iter_next(&it))) + { + if(!memcmp(emmd5, c->emmd5, MD5_DIGEST_LENGTH)) + { + cs_log_dump_dbg(D_EMM, c->emmd5, MD5_DIGEST_LENGTH, "found emmstat match (reader:%s, count:%d)", rdr->label, c->count); + return c; + } + } + + if(cs_malloc(&c, sizeof(struct s_emmstat))) + { + memcpy(c->emmd5, emmd5, MD5_DIGEST_LENGTH); + c->type = emmtype; + ll_append(rdr->emmstat, c); + cs_log_dump_dbg(D_EMM, c->emmd5, MD5_DIGEST_LENGTH, "added emmstat (reader:%s, count:%d)", rdr->label, c->count); + } + return c; +} diff --git a/oscam-emm-cache.h b/oscam-emm-cache.h new file mode 100644 index 0000000..8408466 --- /dev/null +++ b/oscam-emm-cache.h @@ -0,0 +1,21 @@ +#ifndef OSCAM_EMM_CACHE_H_ +#define OSCAM_EMM_CACHE_H_ + +void emm_save_cache(void); +void load_emmstat_from_file(void); +void save_emmstat_to_file(void); +void emm_load_cache(void); + +// all these functions below use emms md5 hash as indexkey +struct s_emmcache *find_emm_cache(uint8_t *emmd5); // find a certain emm, e.g. to resend it to reader, returns null if nothing found +int32_t emm_edit_cache(uint8_t *emmd5, EMM_PACKET *ep, bool add); // add = false: delete a certain emm from cache add = true: update lastseen or add emm to cache +struct s_emmstat *get_emm_stat(struct s_reader *rdr, uint8_t *emmd5, uint8_t emmtype); // find a certain emmstat +int32_t remove_emm_stat(struct s_reader *rdr, uint8_t *emmd5); // remove a certain emmstat +int32_t clean_stale_emm_cache_and_stat(uint8_t *emmd5, int64_t gone); // remove stale global emmcache + emmstat where emm lastseen is older than gone ms + +#else +static inline void load_emmstat_from_file(void) { } +static inline void save_emmstat_to_file(void) { } +static inline void emm_load_cache(void) { } +static inline void emm_save_cache(void) { } +#endif diff --git a/oscam-emm.c b/oscam-emm.c new file mode 100644 index 0000000..1bf94e5 --- /dev/null +++ b/oscam-emm.c @@ -0,0 +1,899 @@ +#define MODULE_LOG_PREFIX "emm" + +#include "globals.h" +#include "cscrypt/md5.h" +#include "module-dvbapi.h" +#include "module-led.h" +#include "oscam-client.h" +#include "oscam-config.h" +#include "oscam-emm.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" +#include "reader-common.h" +#include "oscam-chk.h" +#include "oscam-emm-cache.h" + +const char *entitlement_type[] = { "", "package", "PPV-Event", "chid", "tier", "class", "PBM", "admin" }; + +static struct timeb last_emm_clean; +static int8_t cs_emmlen_is_blocked(struct s_reader *rdr, int16_t len) +{ + struct s_emmlen_range *blocklen; + if(!rdr->blockemmbylen) + { return 0; } + LL_ITER it = ll_iter_create(rdr->blockemmbylen); + while((blocklen = ll_iter_next(&it))) + { + if(blocklen->min <= len && (len <= blocklen->max || blocklen->max == 0)) + { return 1; } + } + return 0; +} + +/** + * Function to filter emm by cardsystem. + * Every cardsystem can export a function "get_emm_filter" + * + * the emm is checked against it and returns 1 for a valid emm or 0 if not + */ +static int8_t do_simple_emm_filter(struct s_reader *rdr, const struct s_cardsystem *csystem, EMM_PACKET *ep, int8_t cl_dvbapi) +{ + if(is_network_reader(rdr)) { return 1; } // don't evaluate on network readers, server with local reader will check it + if(rdr->typ == R_EMU) { return 1; } // don't evalutate on emu reader + + //copied and enhanced from module-dvbapi.c + //dvbapi_start_emm_filter() + int32_t i, k, match; + uint8_t flt, mask; + struct s_csystem_emm_filter *dmx_filter = NULL; + unsigned int j, filter_count = 0; + + // Call cardsystems emm filter + csystem->get_emm_filter(rdr, &dmx_filter, &filter_count); + + if(!dmx_filter) + { return 0; } + + // Only check matching emmtypes: + uint8_t org_emmtype; + if(ep->type == UNKNOWN) + { org_emmtype = EMM_UNKNOWN; } + else + { org_emmtype = 1 << (ep->type - 1); } + + // Now check all filter values + + + for(j = 0; j < filter_count; j++) + { + if(dmx_filter[j].enabled == 0) + { continue; } + + uint8_t emmtype = dmx_filter[j].type; + if(emmtype != org_emmtype) + { continue; } + + match = 1; + for(i = 0, k = 0; i < 16 && k < ep->emmlen && match; i++, k++) + { + mask = dmx_filter[j].mask[i]; + if(k == 1 && cl_dvbapi) // fixup for emms send by dvbapi + { k += 2; } // skip emm len bytes + if(!mask) + { continue; } + //cs_log("**** filter %d [%d] = %02X, filter mask[%d] = %02X, flt&mask = %02X , ep->emm[%d] = %02X, ep->emm[%d] & mask = %02X ****", j, i, + // dmx_filter[j].filter[i], i, dmx_filter[j].mask[i], flt&mask, k, ep->emm[k], k, ep->emm[k] & mask); + flt = (dmx_filter[j].filter[i] & mask); + match = (flt == (ep->emm[k] & mask)); + if(!match) + { break; } + } + if(match) + { + NULLFREE(dmx_filter); + return 1; // valid emm + } + } + + NULLFREE(dmx_filter); + + return 0; // emm filter does not match, illegal emm, return +} + +static void reader_log_emm(struct s_reader *reader, EMM_PACKET *ep, int32_t count, int32_t rc, struct timeb *tps) +{ + char *rtxt[] = + { + "error", + is_network_reader(reader) ? "sent" : "written", + "skipped", + "blocked" + }; + char *typedesc[] = { "unknown", "unique", "shared", "global" }; + struct s_client *cl = reader->client; + struct timeb tpe; + + if(reader->logemm & (1 << rc)) + { + cs_ftime(&tpe); + if(!tps) + { tps = &tpe; } + + rdr_log(reader, "%s emmtype=%s, len=%d (hex: 0x%.2X), cnt=%d: %s (%"PRId64" ms)", + username(ep->client), typedesc[ep->type], SCT_LEN(ep->emm)-3, SCT_LEN(ep->emm)-3, count, rtxt[rc], comp_timeb(&tpe, tps)); + } + + if(rc) + { + cl->lastemm = time(NULL); + led_status_emm_ok(); + } + +#if defined(WEBIF) || defined(LCDSUPPORT) + // counting results + switch(rc) + { + case 0: + reader->emmerror[ep->type]++; + reader->webif_emmerror[ep->type]++; + break; + + case 1: + reader->emmwritten[ep->type]++; + reader->webif_emmwritten[ep->type]++; + break; + + case 2: + reader->emmskipped[ep->type]++; + reader->webif_emmskipped[ep->type]++; + break; + + case 3: + reader->emmblocked[ep->type]++; + reader->webif_emmblocked[ep->type]++; + break; + } +#endif +} + +int32_t emm_reader_match(struct s_reader *reader, uint16_t caid, uint32_t provid) +{ + int32_t i, j; + FTAB *ftab = &reader->ftab; + + // if physical reader a card needs to be inserted + if(!is_network_reader(reader) && reader->card_status != CARD_INSERTED) + { return 0; } + + if(reader->audisabled) + { return 0; } + +#ifdef READER_NAGRA_MERLIN + if(reader->cwpkcaid_length && reader->nuid_length) + { + uint8_t check[1]; + check[0] = caid & 0xFF; + if(check[0] == reader->cwpkcaid[1]) + { + return 1; + } + } +#endif + + uint16_t emmcaid; + if(reader->caid == 0x186D) + { + emmcaid = reader->caid - 0x03; + } + else if (reader->caid == 0x1856) + { + emmcaid = reader->caid + 0x28; + } + else + { + emmcaid = reader->caid; + } + + if(emmcaid != caid) + { + int caid_found = 0; + if (!reader->csystem) + return 0; + for(i = 0; reader->csystem->caids[i]; i++) + { + uint16_t cs_caid = reader->csystem->caids[i]; + if (emmcaid && cs_caid == caid) + { + caid_found = 1; + break; + } + + if ((emmcaid == 0) && chk_ctab_ex(caid, &reader->ctab)) + { + caid_found = 1; + break; + } + + } + if(!caid_found) + { + rdr_log_dbg(reader, D_EMM, "reader_caid %04X != emmpid caid %04X -> SKIP!", emmcaid, caid); + return 0; + } + } + + //if(!hexserialset(reader)) // There are cards without serial, they should get emm of type global and shared! + //{ + // rdr_log_dbg(reader, D_EMM, "no hexserial is set"); + // return 0; + //} + + if(!provid) + { + rdr_log_dbg(reader, D_EMM, "reader %04X match since emmpid has no provid -> SEND!", caid); + return 1; + } + + uint32_t prid = reader->auprovid; + + if(caid_is_viaccess(caid) && (prid != 0) && ((prid &0xFFFFF0) != prid)) // viaccess fixup last digit of provid is a dont care! + { + prid &= 0xFFFFF0; + rdr_log_dbg(reader, D_EMM, "reader auprovid = %06X fixup to %06X (ignoring last digit)", reader->auprovid, prid); + } + +#ifdef WITH_EMU + if(reader->typ == R_EMU) + { + FILTER *emu_provids = get_emu_prids_for_caid(reader, caid); + if(emu_provids != NULL) + { + for(i = 0; i < emu_provids->nprids; i++) + { + if(provid == emu_provids->prids[i]) + { + return 1; + } + } + } + return 0; + } +#endif + + if(prid == provid) + { + rdr_log_dbg(reader, D_EMM, "reader auprovid = %06X matching with emm provid = %06X -> SEND!", prid, provid); + return 1; + } + + for(i = 0; i < reader->nprov; i++) + { + prid = b2i(4, reader->prid[i]); + + if(caid_is_viaccess(caid) && (prid != 0) && ((prid &0xFFFFF0) != prid)) // viaccess fixup last digit of provid is a dont care! + { + rdr_log_dbg(reader, D_EMM, "reader provid = %06X fixup to %06X (ignoring last digit)", prid, (prid &0xFFFFF0)); + prid &= 0xFFFFF0; + } + + if(prid == provid) + { + rdr_log_dbg(reader, D_EMM, "reader provid %06X matching with emm provid %06X -> SEND!", prid, provid); + return 1; + } + + if((reader->typ == R_CAMD35 || reader->typ == R_CS378X) && (prid & 0xFFFF) == (provid & 0xFFFF)) + { + rdr_log_dbg(reader, D_EMM, "CS378: Match after fixing reader provid %06X to ??%04X and emm provid %06X to ??%04X -> SEND!", prid, prid&0xFFFF, provid, provid&0xFFFF); + return 1; + } + + rdr_log_dbg(reader, D_EMM, "reader provid %06X no match with emm provid %06X -> SKIP!", prid, provid); + } + + if(ftab->nfilts) + { + for(j = 0; j < ftab->filts[0].nprids; j++) + { + prid = ftab->filts[0].prids[j]; + + if(prid == provid) + { + rdr_log_dbg(reader, D_EMM, "reader provid %06X matching with emm provid %06X -> SEND!", prid, provid); + return 1; + } + } + } + return 0; +} + +static char *get_emmlog_filename(char *dest, size_t destlen, const char *basefilename, const char *type, const char *ext) +{ + char filename[64 + 16]; + snprintf(filename, sizeof(filename), "%s_%s_emm.%s", basefilename, type, ext); + if(!cfg.emmlogdir) + { + get_config_filename(dest, destlen, filename); + } + else + { + const char *slash = "/"; + if(cfg.emmlogdir[cs_strlen(cfg.emmlogdir) - 1] == '/') { slash = ""; } + snprintf(dest, destlen, "%s%s%s", cfg.emmlogdir, slash, filename); + } + return dest; +} + +static void saveemm(struct s_reader *aureader, EMM_PACKET *ep, const char *proceded) +{ + FILE *fp_log; + char tmp[17]; + char buf[80]; + char token_log[256]; + char *tmp2; + time_t rawtime; + uint32_t emmtype; + struct tm timeinfo; + if(ep->type == UNKNOWN) + { emmtype = EMM_UNKNOWN; } + else + { emmtype = 1 << (ep->type - 1); } + // should this nano be saved? + if(((1 << (ep->emm[0] % 0x80)) & aureader->s_nano) || (aureader->saveemm & emmtype)) + { + time(&rawtime); + localtime_r(&rawtime, &timeinfo); // to access LOCAL date/time info + int32_t emm_length = SCT_LEN(ep->emm); + strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S", &timeinfo); + + switch(ep->type) + { + case GLOBAL: + fp_log = fopen(get_emmlog_filename(token_log, sizeof(token_log), aureader->label, "global", "log"), "a"); + break; + + case SHARED: + fp_log = fopen(get_emmlog_filename(token_log, sizeof(token_log), aureader->label, "shared", "log"), "a"); + break; + + case UNIQUE: + fp_log = fopen(get_emmlog_filename(token_log, sizeof(token_log), aureader->label, "unique", "log"), "a"); + break; + + case UNKNOWN: + default: + fp_log = fopen(get_emmlog_filename(token_log, sizeof(token_log), aureader->label, "unknown", "log"), "a"); + } + + if(!fp_log) + { + rdr_log(aureader, "ERROR: Cannot open file '%s' (errno=%d: %s)\n", token_log, errno, strerror(errno)); + } + else + { + if(cs_malloc(&tmp2, emm_length * 2 + 1)) + { + fprintf(fp_log, "%s %s ", buf, cs_hexdump(0, ep->hexserial, 8, tmp, sizeof(tmp))); + fprintf(fp_log, "%s %s\n", cs_hexdump(0, ep->emm, emm_length, tmp2, emm_length * 2 + 1), proceded); + NULLFREE(tmp2); + rdr_log(aureader, "Successfully added EMM to %s", token_log); + } + fclose(fp_log); + } + } +} + +void do_emm(struct s_client *client, EMM_PACKET *ep) +{ + int32_t writeemm = 1; // 0= dont write emm, 1=write emm, default = write + char *typtext[] = {"unknown", "unique", "shared", "global"}; + char tmp[17]; + int32_t emmnok = 0; + bool lastseendone = false; + + struct s_reader *aureader = NULL; + uint16_t sct_len; + + if(ep->emmlen < 3) + { + cs_log("EMM size %d invalid, ignored! client %s", ep->emmlen, username(client)); + return; + } + + if(ep->emmlen > MAX_EMM_SIZE) + { + cs_log("EMM size %d > Max EMM size %d, ignored! client %s", ep->emmlen, MAX_EMM_SIZE, username(client)); + return; + } + + sct_len = SCT_LEN(ep->emm); + if(sct_len > ep->emmlen) + { + cs_log("Real EMM size %d > EMM size %d, ignored! client %s", sct_len, ep->emmlen, username(client)); + return; + } + ep->emmlen = sct_len; + + cs_log_dump_dbg(D_EMM, ep->emm, ep->emmlen, "emm:"); + + int8_t assemble = 0; + bool cl_dvbapi = is_dvbapi_usr(client->account->usr); + if(client->account->emm_reassembly > 1 || (client->account->emm_reassembly && cl_dvbapi)) + { assemble = 1; } + + LL_ITER itr = ll_iter_create(client->aureader_list); + while((aureader = ll_iter_next(&itr))) + { + if(!aureader->enable) + { continue; } + + uint16_t caid = b2i(2, ep->caid); + uint32_t provid = b2i(4, ep->provid); + + if(caid_is_viaccess(caid)) // viaccess fixup last digit is a dont care! + { + provid &= 0xFFFFF0; + } + + if(aureader->audisabled) + { + rdr_log_dbg(aureader, D_EMM, "AU is disabled"); + /* we have to write the log for blocked EMM here because + this EMM never reach the reader module where the rest + of EMM log is done. */ + if(aureader->logemm & 0x10) + { + rdr_log(aureader, "%s emmtype=%s, len=%d (hex: 0x%02X), idx=0, cnt=1: audisabled (0 ms)", + client->account->usr, + typtext[ep->type], + SCT_LEN(ep->emm) - 3, + SCT_LEN(ep->emm) - 3); + } + continue; + } + + if(!(aureader->grp & client->grp)) + { + rdr_log_dbg(aureader, D_EMM, "skip emm, group mismatch"); + continue; + } + + // TODO: provider possibly not set yet, this is done in get_emm_type() + if(!emm_reader_match(aureader, caid, provid)) + { continue; } + + const struct s_cardsystem *csystem = NULL; + + if(is_network_reader(aureader)) // network reader (R_CAMD35 R_NEWCAMD R_CS378X R_CCCAM) + { + if(!aureader->ph.c_send_emm) // no emm support + { continue; } + + csystem = get_cardsystem_by_caid(caid); + if(!csystem) + { + rdr_log_dbg(aureader, D_EMM, "unable to find cardsystem for caid %04X", caid); + continue; + } + } + else // local reader + { + if(aureader->csystem_active) + { csystem = aureader->csystem; } + } + + if(csystem && csystem->get_emm_type) + { + if(!csystem->get_emm_type(ep, aureader)) + { + rdr_log_dbg(aureader, D_EMM, "emm skipped, get_emm_type() returns error"); + emmnok++; + continue; + } + } + + if(!ep->skip_filter_check && csystem && csystem->get_emm_filter) + { + if(!do_simple_emm_filter(aureader, csystem, ep, 1)) // do check with dvbapi fixup enabled + { + if(!do_simple_emm_filter(aureader, csystem, ep, 0)) // do check with dvbapi fixup disabled + { + rdr_log_dbg(aureader, D_EMM, "emm skipped, do_simple_emm_filter() returns invalid"); + emmnok++; + continue; + } + } + } + + if(csystem && csystem->do_emm_reassembly) + { + if(assemble) + { + if(!csystem->do_emm_reassembly(aureader, client, ep)) + { continue; } // skip this reader + } + else + { + rdr_log_dbg(aureader, D_EMM, "processing raw emm"); + } + } + + rdr_log_dbg_sensitive(aureader, D_EMM, "emmtype %s. Reader serial {%s}.", typtext[ep->type], + cs_hexdump(0, aureader->hexserial, 8, tmp, sizeof(tmp))); + rdr_log_dbg_sensitive(aureader, D_EMM, "emm UA/SA: {%s}.", + cs_hexdump(0, ep->hexserial, 8, tmp, sizeof(tmp))); + + client->last = time(NULL); + + int32_t is_blocked = 0; + +#ifdef READER_VIDEOGUARD + if (aureader->fix_07 == 1 && ep->type == UNIQUE) + { + if((caid == 0x098D || caid == 0x098C) && ep->emm[1] == 0x70 && (ep->emm[8] * 0x100 + ep->emm[9] != 0x200)) + { + rdr_log(aureader,"emmtype 0x%04X marked as unknown for caid 0x%04X", (ep->emm[8] * 0x100 + ep->emm[9]),caid); + ep->type = UNKNOWN; + } + + if((caid == 0x098D || caid == 0x098C) && ep->emm[1] == 0 && (ep->emm[4] * 0x100 + ep->emm[5] != 0x200)) + { + rdr_log(aureader,"emmtype 0x%04X marked as unknown for caid 0x%04X", (ep->emm[4] * 0x100 + ep->emm[5]),caid); + ep->type = UNKNOWN; + } + + if(caid == 0x09AF && ep->emm[1] == 0x70 && ep->emm[11] != 2) + { + rdr_log(aureader,"emmtype 0x%02X marked as unknown for caid 0x%04X", ep->emm[11],caid); + ep->type = UNKNOWN; + } + + if(caid == 0x09AF && ep->emm[1] == 0 && ep->emm[7] != 2) + { + rdr_log(aureader,"emmtype 0x%02X marked as unknown for caid 0x%04X", ep->emm[7],caid); + ep->type = UNKNOWN; + } + } +#endif + +#ifdef READER_CRYPTOWORKS + if ((ep->type == GLOBAL) && ((caid == 0x0D96) || (caid == 0x0D98)) && ((aureader->blockemm & EMM_GLOBAL) != EMM_GLOBAL) && ((aureader->blockemm & EMM_SHARED) != EMM_SHARED) && (aureader->needsglobalfirst == 1)) + { + // save global EMM + cs_log_dbg(D_EMM,"save global EMM for caid 0x%04X",caid); + ep->client = client; + memcpy(aureader->last_g_emm, ep, sizeof(EMM_PACKET)); + aureader->last_g_emm_valid = true; + +#ifdef WEBIF + aureader->emmblocked[ep->type]++; + aureader->webif_emmblocked[ep->type]++; + is_blocked = aureader->emmblocked[ep->type]; +#endif + + if(aureader->logemm & 0x08) + { + rdr_log(aureader, "%s emmtype=%s, len=%d (hex: 0x%02X), idx=0, cnt=%d: blocked & saved (0 ms)", + client->account->usr, + typtext[ep->type], + SCT_LEN(ep->emm)-3, + SCT_LEN(ep->emm)-3, + is_blocked); + } + saveemm(aureader, ep, "blocked & saved"); + continue; + } +#endif + + switch(ep->type) + { + case UNKNOWN: + is_blocked = (aureader->blockemm & EMM_UNKNOWN) == EMM_UNKNOWN; + break; + + case UNIQUE : + is_blocked = (aureader->blockemm & EMM_UNIQUE) == EMM_UNIQUE; + break; + + case SHARED : + is_blocked = (aureader->blockemm & EMM_SHARED) == EMM_SHARED; + break; + + case GLOBAL : + is_blocked = (aureader->blockemm & EMM_GLOBAL) == EMM_GLOBAL; + break; + } + + // if not already blocked we check for block by len + if(!is_blocked) { is_blocked = cs_emmlen_is_blocked(aureader, SCT_LEN(ep->emm)-3) ; } + + if(is_blocked != 0) + { +#ifdef WEBIF + aureader->emmblocked[ep->type]++; + aureader->webif_emmblocked[ep->type]++; + is_blocked = aureader->emmblocked[ep->type]; +#endif + /* we have to write the log for blocked EMM here because + this EMM never reach the reader module where the rest + of EMM log is done. */ + if(aureader->logemm & 0x08) + { + rdr_log(aureader, "%s emmtype=%s, len=%d (hex: 0x%02X), idx=0, cnt=%d: blocked (0 ms)", + client->account->usr, + typtext[ep->type], + SCT_LEN(ep->emm)-3, + SCT_LEN(ep->emm)-3, + is_blocked); + } + saveemm(aureader, ep, "blocked"); + continue; + } + + client->lastemm = time((time_t *)0); + + client->emmok++; + if(client->account) + { client->account->emmok++; } + first_client->emmok++; + + ep->client = client; + + + if(aureader->cachemm && !(caid_is_irdeto(caid) || caid_is_videoguard(caid))) // Check emmcache early: + { + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + + MD5(ep->emm, SCT_LEN(ep->emm), md5tmp); + + struct s_emmcache *emmcache = find_emm_cache(md5tmp); // check emm cache + if(emmcache && !lastseendone) + { + cs_ftime(&emmcache->lastseen); + lastseendone = true; // in case several aureaders, only do lastseen once! + } + + struct s_emmstat *emmstat = get_emm_stat(aureader, md5tmp, ep->type); + if(emmstat) + { + rdr_log_dbg(aureader, D_EMM, "emm count %d rewrite %d", emmstat->count, aureader->rewritemm); + + if(emmstat->count >= aureader->rewritemm) + { + reader_log_emm(aureader, ep, emmstat->count, 2, NULL); + writeemm = 0; // don't write emm! + saveemm(aureader, ep, "emmcache"); + continue; // found emm match needs no further handling, proceed with next reader! + } + } + } + + if(writeemm) // only write on no cache hit or cache hit that needs further rewrite + { + EMM_PACKET *emm_pack; + if(cs_malloc(&emm_pack, sizeof(EMM_PACKET))) + { +#ifdef READER_CRYPTOWORKS + if ((ep->type == SHARED) && ((caid == 0x0D96) || (caid == 0x0D98)) && (aureader->last_g_emm_valid == true) && (aureader->needsglobalfirst == 1)) + { + EMM_PACKET *emm_pack_global; + if(cs_malloc(&emm_pack_global, sizeof(EMM_PACKET))) + { + rdr_log_dbg(aureader, D_EMM, "Last stored global EMM for caid 0x%04X is being sent to Reader first", caid); + memcpy(emm_pack_global, aureader->last_g_emm, sizeof(EMM_PACKET)); + add_job(aureader->client, ACTION_READER_EMM, emm_pack_global, sizeof(EMM_PACKET)); + saveemm(aureader, aureader->last_g_emm, "written stored global"); + cs_log_dump_dbg(D_EMM,emm_pack_global->emm, emm_pack_global->emmlen, "Last stored global EMM to be written before shared EMM:"); + } + } +#endif + rdr_log_dbg(aureader, D_EMM, "emm is being sent to reader"); + memcpy(emm_pack, ep, sizeof(EMM_PACKET)); + add_job(aureader->client, ACTION_READER_EMM, emm_pack, sizeof(EMM_PACKET)); + saveemm(aureader, ep, "written"); + } + } + + } // done with this reader, process next reader! + + if(emmnok > 0 && emmnok == ll_count(client->aureader_list)) + { + client->emmnok++; + if(client->account) + { client->account->emmnok++; } + first_client->emmnok++; + } +} + + +int32_t reader_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + int32_t rc, ecs = 0,count = 0; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + struct timeb tps; + + cs_ftime(&tps); + uint16_t caid = b2i(2, ep->caid); + if(reader->cachemm && !(caid_is_irdeto(caid) || caid_is_videoguard(caid))) + { + MD5(ep->emm, SCT_LEN(ep->emm), md5tmp); + int64_t gone = comp_timeb(&tps, &last_emm_clean); + if(gone > (int64_t)1000 * 60 * 60 * 24 * 30 || gone < 0) // dont run every time, only on first emm oscam is started and then every 30 days + { + last_emm_clean = tps; + count = clean_stale_emm_cache_and_stat(md5tmp, (int64_t)1000 * 60 * 60 *24 * 30); // clean global all emms from all readers after 30 days emm is last seen! + cs_log_dbg(D_EMM, "Cleaned %d emm stale stats and cache entries", count); + } + + struct s_emmcache *emmcache = find_emm_cache(md5tmp); // check emm cache + if(!emmcache) + { + emm_edit_cache(md5tmp, ep, true); + } + + struct s_emmstat *emmstat = get_emm_stat(reader, md5tmp, ep->type); + if(emmstat) + { + if(reader->cachemm && emmstat->count >= reader->rewritemm) + { + ecs = 2; // skip emm + } + else + { + ecs = 1; // rewrite emm + if(!emmstat->count) + { + cs_ftime(&emmstat->firstwritten); + emmstat->lastwritten = emmstat->firstwritten; + } + else + { + cs_ftime(&emmstat->lastwritten); + } + count = ++emmstat->count; + } + } + else + { + cs_log("abort: oscam seems out of resources!"); + return 0; + } + } + + // Ecs=0 not found in cache + // Ecs=1 found in cache, rewrite emm + // Ecs=2 skip + if((rc = ecs) < 2) + { + if(is_network_reader(reader)) + { + rdr_log_dbg(reader, D_READER, "network emm reader"); + if(reader->ph.c_send_emm) + { + rc = reader->ph.c_send_emm(ep); + } + else + { + rdr_log_dbg(reader, D_READER, "send_emm() support missing"); + rc = 0; + } + } + else + { + rdr_log_dbg(reader, D_READER, "local emm reader"); + rc = cardreader_do_emm(reader, ep); + } + } + + reader_log_emm(reader, ep, count, rc, &tps); + + return rc; +} + +void do_emm_from_file(struct s_reader *reader) +{ + if(!reader->emmfile) + { return; } + + char token[256]; + FILE *fp; + + if(reader->emmfile[0] == '/') + { snprintf(token, sizeof(token), "%s", reader->emmfile); } // pathname included + else + { get_config_filename(token, sizeof(token), reader->emmfile); } // only file specified, look in confdir for this file + + if(!(fp = fopen(token, "rb"))) + { + rdr_log(reader, "ERROR: Cannot open EMM file '%s' (errno=%d %s)\n", token, errno, strerror(errno)); + return; + } + + EMM_PACKET *eptmp; + if(!cs_malloc(&eptmp, sizeof(EMM_PACKET))) + { + fclose(fp); + return; + } + + size_t ret = fread(eptmp, sizeof(EMM_PACKET), 1, fp); + if(ret < 1 && ferror(fp)) + { + rdr_log(reader, "ERROR: Can't read EMM from file '%s' (errno=%d %s)", token, errno, strerror(errno)); + NULLFREE(eptmp); + fclose(fp); + return; + } + fclose(fp); + + if (eptmp) { + eptmp->caid[0] = (reader->caid >> 8) & 0xFF; + eptmp->caid[1] = reader->caid & 0xFF; + if(reader->nprov > 0) + { memcpy(eptmp->provid, reader->prid[0], sizeof(eptmp->provid)); } + eptmp->emmlen = SCT_LEN(eptmp->emm); +} + const struct s_cardsystem *csystem = get_cardsystem_by_caid(reader->caid); + if(csystem && csystem->get_emm_type && !csystem->get_emm_type(eptmp, reader)) + { + rdr_log_dbg(reader, D_EMM, "emm skipped, get_emm_type() returns error"); + NULLFREE(eptmp); + return; + } + + // save old b_nano value + // clear lsb and lsb+1, so no blocking, and no saving for this nano + uint16_t save_s_nano = reader->s_nano; + uint16_t save_b_nano = reader->b_nano; + uint32_t save_saveemm = reader->saveemm; + + reader->s_nano = reader->b_nano = 0; + reader->saveemm = 0; + + int32_t rc = 0; + rc = cardreader_do_emm(reader, eptmp); + if(rc == OK) + { rdr_log(reader, "EMM from file %s was successfully written.", token); } + else + { rdr_log(reader, "ERROR: EMM read from file %s NOT processed correctly! (rc=%d)", token, rc); } + + // restore old block/save settings + reader->s_nano = save_s_nano; + reader->b_nano = save_b_nano; + reader->saveemm = save_saveemm; + + NULLFREE(eptmp); +} + +void emm_sort_nanos(uint8_t *dest, const uint8_t *src, int32_t len) +{ + int32_t w = 0, c = -1, j = 0; + while(1) + { + int32_t n = 256; + for(j = 0; j < len;) + { + int32_t l = src[j + 1] + 2; + if(src[j] == c) + { + if(w + l > len) + { + cs_log_dbg(D_EMM, "sortnanos: sanity check failed. Exceeding memory area. Probably corrupted nanos!"); + memset(dest, 0, len); // zero out everything + return; + } + memcpy(&dest[w], &src[j], l); + w += l; + } + else if(src[j] > c && src[j] < n) + { + n = src[j]; + } + j += l; + } + if(n >= 256) + { break; } + c = n; + } +} diff --git a/oscam-emm.h b/oscam-emm.h new file mode 100644 index 0000000..eb1a626 --- /dev/null +++ b/oscam-emm.h @@ -0,0 +1,10 @@ +#ifndef OSCAM_EMM_H_ +#define OSCAM_EMM_H_ + +int32_t emm_reader_match(struct s_reader *reader, uint16_t caid, uint32_t provid); +void do_emm(struct s_client *client, EMM_PACKET *ep); +int32_t reader_do_emm(struct s_reader *reader, EMM_PACKET *ep); +void do_emm_from_file(struct s_reader *reader); +void emm_sort_nanos(uint8_t *dest, const uint8_t *src, int32_t len); + +#endif diff --git a/oscam-failban.c b/oscam-failban.c new file mode 100644 index 0000000..312f418 --- /dev/null +++ b/oscam-failban.c @@ -0,0 +1,141 @@ +#define MODULE_LOG_PREFIX "failban" + +#include "globals.h" +#include "module-anticasc.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-time.h" + +static int32_t cs_check_v(IN_ADDR_T ip, int32_t port, int32_t add, char *info, int32_t acosc_penalty_duration) +{ + int32_t result = 0; + + if(!(cfg.failbantime || acosc_enabled())) + return 0; + + if(!cfg.v_list) + { cfg.v_list = ll_create("v_list"); } + + struct timeb (now); + cs_ftime(&now); + LL_ITER itr = ll_iter_create(cfg.v_list); + V_BAN *v_ban_entry; + int32_t ftime = cfg.failbantime * 60 * 1000; + + // run over all banned entries to do housekeeping: + while((v_ban_entry = ll_iter_next(&itr))) + { + // housekeeping: + int64_t gone = comp_timeb(&now, &v_ban_entry->v_time); + if(((gone >= ftime) && !v_ban_entry->acosc_entry) || (v_ban_entry->acosc_entry && ((gone/1000) >= v_ban_entry->acosc_penalty_dur))) // entry out of time->remove + { + NULLFREE(v_ban_entry->info); + ll_iter_remove_data(&itr); + continue; + } + + if(IP_EQUAL(ip, v_ban_entry->v_ip) && port == v_ban_entry->v_port) + { + result = 1; + if(!info) + { info = v_ban_entry->info; } + else if(!v_ban_entry->info) + { + v_ban_entry->info = cs_strdup(info); + } + + if(!add) + { + if(v_ban_entry->v_count >= cfg.failbancount) + { + if(!v_ban_entry->acosc_entry) + { + cs_log_dbg(D_TRACE, "failban: banned ip %s:%d - %"PRId64" seconds left %s%s", + cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port, + (ftime - gone) / 1000, info ? ", info: " : "", info ? info : ""); + } + else + { + cs_log_dbg(D_TRACE, "failban: banned ip %s:%d - %"PRId64" seconds left %s%s", + cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port, + (v_ban_entry->acosc_penalty_dur - (gone / 1000)), + info ? ", info: " : "", info ? info : ""); + } + + } + else + { + cs_log_dbg(D_TRACE, "failban: ip %s:%d chance %d of %d%s%s", + cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port, + v_ban_entry->v_count, cfg.failbancount, + info ? ", info: " : "", info ? info : ""); + + v_ban_entry->v_count++; + } + } + else + { + cs_log_dbg(D_TRACE, "failban: banned ip %s:%d - already exist in list %s%s", + cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port, + info ? ", info: " : "", info ? info : ""); + } + } + } + + if(add && !result) + { + if(cs_malloc(&v_ban_entry, sizeof(V_BAN))) + { + cs_ftime(&v_ban_entry->v_time); + v_ban_entry->v_ip = ip; + v_ban_entry->v_port = port; + v_ban_entry->v_count = 1; + v_ban_entry->acosc_entry = false; + v_ban_entry->acosc_penalty_dur = 0; + + if(acosc_penalty_duration > 0) + { + v_ban_entry->v_count = cfg.failbancount +1; // set it to a higher level + v_ban_entry->acosc_entry = true; + v_ban_entry->acosc_penalty_dur = acosc_penalty_duration; + } + + if(info) + { v_ban_entry->info = cs_strdup(info); } + + ll_iter_insert(&itr, v_ban_entry); + cs_log_dbg(D_TRACE, "failban: ban ip %s:%d with timestamp %" PRId64 "%s%s", + cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port, (int64_t)v_ban_entry->v_time.time, + info ? ", info: " : "", info ? info : ""); + } + } + + return result; +} + +int32_t cs_check_violation(IN_ADDR_T ip, int32_t port) +{ + return cs_check_v(ip, port, 0, NULL, 0); +} + +int32_t cs_add_violation_by_ip(IN_ADDR_T ip, int32_t port, char *info) +{ + return cs_check_v(ip, port, 1, info, 0); +} + +int32_t cs_add_violation_by_ip_acosc(IN_ADDR_T ip, int32_t port, char *info, int32_t acosc_penalty_duration) +{ + return cs_check_v(ip, port, 1, info, acosc_penalty_duration); +} + +void cs_add_violation(struct s_client *cl, char *info) +{ + struct s_module *module = get_module(cl); + cs_add_violation_by_ip(cl->ip, module->ptab.ports[cl->port_idx].s_port, info); +} + +void cs_add_violation_acosc(struct s_client *cl, char *info, int32_t acosc_penalty_duration) +{ + struct s_module *module = get_module(cl); + cs_add_violation_by_ip_acosc(cl->ip, module->ptab.ports[cl->port_idx].s_port, info, acosc_penalty_duration); +} diff --git a/oscam-failban.h b/oscam-failban.h new file mode 100644 index 0000000..33c01e4 --- /dev/null +++ b/oscam-failban.h @@ -0,0 +1,9 @@ +#ifndef OSCAM_FAILBAN_H_ +#define OSCAM_FAILBAN_H_ + +extern int32_t cs_check_violation(IN_ADDR_T ip, int32_t port); +int32_t cs_add_violation_by_ip(IN_ADDR_T ip, int32_t port, char *info); +extern void cs_add_violation(struct s_client *cl, char *info); +extern void cs_add_violation_acosc(struct s_client *cl, char *info, int32_t acosc_penalty_duration); + +#endif diff --git a/oscam-files.c b/oscam-files.c new file mode 100644 index 0000000..ba9236c --- /dev/null +++ b/oscam-files.c @@ -0,0 +1,220 @@ +#define MODULE_LOG_PREFIX "files" + +#include "globals.h" + +#include "oscam-files.h" +#include "oscam-lock.h" +#include "oscam-string.h" + +extern CS_MUTEX_LOCK readdir_lock; +extern char cs_tmpdir[200]; + +/* Gets the tmp dir */ +char *get_tmp_dir(void) +{ + if(cs_tmpdir[0]) + { + return cs_tmpdir; + } +#if defined(__CYGWIN__) + + char *d = getenv("TMPDIR"); + + if(!d || !d[0]) + { + d = getenv("TMP"); + } + + if(!d || !d[0]) + { + d = getenv("TEMP"); + } + + if(!d || !d[0]) + { + getcwd(cs_tmpdir, sizeof(cs_tmpdir) - 1); + } + + cs_strncpy(cs_tmpdir, d, sizeof(cs_tmpdir)); + char *p = cs_tmpdir; + while(*p) { p++; } + p--; + if(*p != '/' && *p != '\\') + { + cs_strncat(cs_tmpdir, "/", sizeof(cs_tmpdir)); + } + cs_strncat(cs_tmpdir, "_oscam", sizeof(cs_tmpdir)); +#else + cs_strncpy(cs_tmpdir, "/tmp/.oscam", sizeof(cs_tmpdir)); +#endif + mkdir(cs_tmpdir, S_IRWXU); + return cs_tmpdir; +} + +char *get_tmp_dir_filename(char *dest, size_t destlen, const char *filename) +{ + char *tmp_dir = get_tmp_dir(); + const char *slash = "/"; + if(tmp_dir[cs_strlen(tmp_dir) - 1] == '/') + { + slash = ""; + } + snprintf(dest, destlen, "%s%s%s", tmp_dir, slash, filename); + return dest; +} + +/* Return 1 if the file exists, else 0 */ +bool file_exists(const char *filename) +{ + return access(filename, R_OK) == 0; +} + +/* Finds a file in the system PATH. + Returns a newly allocated string with the full path, or NULL if not found. + Caller must free() the returned string. */ +char *find_in_path(const char *filename) +{ + // If filename already has '/', don't search PATH + if (strchr(filename, '/')) { + return strdup(filename); + } + + char *paths; const char *path = getenv("PATH"); if (!path || !(paths = strdup(path))) return NULL; + + char *saveptr = NULL; + char *dir = strtok_r(paths, ":", &saveptr); + char fullpath[PATH_MAX]; + char *result = NULL; + + while (dir) { + if (*dir == '\0') dir = "."; + snprintf(fullpath, sizeof(fullpath), "%s/%s", dir, filename); + if (file_exists(fullpath)) { + result = strdup(fullpath); + break; + } + dir = strtok_r(NULL, ":", &saveptr); + } + + free(paths); + return result; +} + +/* Copies a file from srcfile to destfile. If an error occured before writing, + -1 is returned, else -2. On success, 0 is returned.*/ + +int32_t file_copy(char *srcfile, char *destfile) +{ + FILE *src, *dest; + int32_t ch; + + src = fopen(srcfile, "r"); + if(!src) + { + cs_log("Error opening file %s for reading (errno=%d %s)!", srcfile, errno, strerror(errno)); + return -1; + } + + dest = fopen(destfile, "w"); + if(!dest) + { + cs_log("Error opening file %s for writing (errno=%d %s)!", destfile, errno, strerror(errno)); + fclose(src); + return -1; + } + + while(1) + { + ch = fgetc(src); + if(ch == EOF) + { + break; + } + else + { + fputc(ch, dest); + if(ferror(dest)) + { + cs_log("Error while writing to file %s (errno=%d %s)!", destfile, errno, strerror(errno)); + fclose(src); + fclose(dest); + return -2; + } + } + } + fclose(src); + if(fsync(fileno(dest)) == -1) // Dodano fsync, aby wymusić zapis na dysk + { + cs_log("ERROR: file_copy - fsync failed for %s (errno=%d %s)!", destfile, errno, strerror(errno)); + } + fclose(dest); + return (0); +} + +/* Overwrites destfile with temp_file. If forceBakOverWrite = 0, + the bakfile will not be overwritten if it exists, else it will be.*/ + +int32_t safe_overwrite_with_bak(char *destfile, char *temp_file, char *bakfile, int32_t forceBakOverWrite) +{ + int32_t rc; + + if(file_exists(destfile)) + { + if(forceBakOverWrite != 0 || !file_exists(bakfile)) + { + if(file_copy(destfile, bakfile) < 0) + { + cs_log("ERROR: safe_overwrite_with_bak - Error copying original config file %s to %s. The original config will be left untouched!", destfile, bakfile); + if(unlink(temp_file) < 0) + { + cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno)); + } + return 1; + } + } + } + + rc = file_copy(temp_file, destfile); + if(rc < 0) + { + cs_log("ERROR: safe_overwrite_with_bak - An error occured while writing the new config file %s. Return code: %d, errno: %d (%s).", destfile, rc, errno, strerror(errno)); + if(rc == -2) + { + cs_log("ERROR: safe_overwrite_with_bak - The config will be missing or only partly filled upon next startup as this is a non-recoverable error! Please restore from backup or try again."); + } + if(unlink(temp_file) < 0) + { + cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno)); + } + return 1; + } + + if(unlink(temp_file) < 0) + { + cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno)); + } + return 0; +} + +#ifdef MODULE_GBOX +char *get_gbox_filename(char *dest, size_t destlen, const char *filename) +{ + char *tmp_dir = get_tmp_dir(); + const char *slash = "/"; + + if(cfg.gbox_tmp_dir != NULL) + { + if(cfg.gbox_tmp_dir[cs_strlen(cfg.gbox_tmp_dir) - 1] == '/') + { + slash = ""; + } + snprintf(dest, destlen, "%s%s%s", cfg.gbox_tmp_dir, slash, filename); + } + else + { + if(tmp_dir[cs_strlen(tmp_dir) - 1] == '/') { slash = ""; } + snprintf(dest, destlen, "%s%s%s", tmp_dir, slash, filename); + } + return dest; +} +#endif diff --git a/oscam-files.h b/oscam-files.h new file mode 100644 index 0000000..25257f5 --- /dev/null +++ b/oscam-files.h @@ -0,0 +1,14 @@ +#ifndef OSCAM_FILES_H_ +#define OSCAM_FILES_H_ + +char *get_tmp_dir(void); +char *get_tmp_dir_filename(char *dest, size_t destlen, const char *filename); +bool file_exists(const char *filename); +char *find_in_path(const char *filename); +int32_t file_copy(char *srcfile, char *destfile); +int32_t safe_overwrite_with_bak(char *destfile, char *temp_file, char *bakfile, int32_t forceBakOverWrite); +#ifdef MODULE_GBOX +char *get_gbox_filename(char *dest, size_t destlen, const char *filename); +#endif + +#endif diff --git a/oscam-garbage.c b/oscam-garbage.c new file mode 100644 index 0000000..1b0373b --- /dev/null +++ b/oscam-garbage.c @@ -0,0 +1,218 @@ +#define MODULE_LOG_PREFIX "gc" + +#include "globals.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-string.h" +#include "oscam-time.h" + +#define HASH_BUCKETS 250 + +struct cs_garbage +{ + time_t time; + void *data; +#ifdef WITH_DEBUG + char *file; + uint32_t line; +#endif + struct cs_garbage *next; +}; + +static int32_t counter = 0; +static pthread_mutex_t add_lock; +static struct cs_garbage *garbage_first[HASH_BUCKETS]; +static CS_MUTEX_LOCK garbage_lock[HASH_BUCKETS]; +static pthread_t garbage_thread; +static int32_t garbage_collector_active; +static int32_t garbage_debug; + +#ifdef WITH_DEBUG +void add_garbage_debug(void *data, char *file, uint32_t line) +{ +#else +void add_garbage(void *data) +{ +#endif + if(!data) + { return; } + + if(!garbage_collector_active || garbage_debug == 1) + { + NULLFREE(data); + return; + } + + SAFE_MUTEX_LOCK(&add_lock); + + int32_t bucket = counter++; + + if(counter >= HASH_BUCKETS) + { + counter = 0; + } + + SAFE_MUTEX_UNLOCK(&add_lock); + + struct cs_garbage *garbage = (struct cs_garbage*)malloc(sizeof(struct cs_garbage)); + if(garbage == NULL) + { + cs_log("*** MEMORY FULL -> FREEING DIRECT MAY LEAD TO INSTABILITY!!! ***"); + NULLFREE(data); + return; + } + garbage->time = time(NULL); + garbage->data = data; + garbage->next = NULL; +#ifdef WITH_DEBUG + garbage->file = file; + garbage->line = line; +#endif + + cs_writelock(__func__, &garbage_lock[bucket]); + +#ifdef WITH_DEBUG + if(garbage_debug == 2) + { + struct cs_garbage *garbagecheck = garbage_first[bucket]; + while(garbagecheck) + { + if(garbagecheck->data == data) + { + cs_log("Found a try to add garbage twice. Not adding the element to garbage list..."); + cs_log("Current garbage addition: %s, line %d.", file, line); + cs_log("Original garbage addition: %s, line %d.", garbagecheck->file, garbagecheck->line); + cs_writeunlock(__func__, &garbage_lock[bucket]); + NULLFREE(garbage); + return; + } + garbagecheck = garbagecheck->next; + } + } +#endif + + garbage->next = garbage_first[bucket]; + garbage_first[bucket] = garbage; + + cs_writeunlock(__func__, &garbage_lock[bucket]); +} + +static pthread_cond_t sleep_cond; +static pthread_mutex_t sleep_cond_mutex; + +static void garbage_collector(void) +{ + int32_t i,j; + struct cs_garbage *garbage, *next, *prev, *first; + set_thread_name(__func__); + int32_t timeout_time = 2 * cfg.ctimeout / 1000 + 6; + + while(garbage_collector_active) + { + time_t deltime = time(NULL) - timeout_time; + + for(i = 0; i < HASH_BUCKETS; ++i) + { + j = 0; + cs_writelock(__func__, &garbage_lock[i]); + first = garbage_first[i]; + + for(garbage = first, prev = NULL; garbage; prev = garbage, garbage = garbage->next, j++) + { + if(j == 2) + { + j++; + cs_writeunlock(__func__, &garbage_lock[i]); + } + + if(garbage->time < deltime) // all following elements are too new + { + if(prev) + { + prev->next = NULL; + } + else + { + garbage_first[i] = NULL; + } + break; + } + } + + cs_writeunlock(__func__, &garbage_lock[i]); + + // list has been taken out before so we don't need a lock here anymore! + while(garbage) + { + next = garbage->next; + free(garbage->data); + free(garbage); + garbage = next; + } + } + sleepms_on_cond(__func__, &sleep_cond_mutex, &sleep_cond, 500); + } + pthread_exit(NULL); +} + +void start_garbage_collector(int32_t debug) +{ + garbage_debug = debug; + int32_t i; + + SAFE_MUTEX_INIT(&add_lock, NULL); + + for(i = 0; i < HASH_BUCKETS; ++i) + { + cs_lock_create(__func__, &garbage_lock[i], "garbage_lock", 9000); + + garbage_first[i] = NULL; + } + cs_pthread_cond_init(__func__, &sleep_cond_mutex, &sleep_cond); + + garbage_collector_active = 1; + + int32_t ret = start_thread("garbage", (void *)&garbage_collector, NULL, &garbage_thread, 0, 1); + if(ret) + { + cs_exit(1); + } +} + +void stop_garbage_collector(void) +{ + if(garbage_collector_active) + { + int32_t i; + + garbage_collector_active = 0; + SAFE_COND_SIGNAL(&sleep_cond); + cs_sleepms(300); + SAFE_COND_SIGNAL(&sleep_cond); + SAFE_THREAD_JOIN(garbage_thread, NULL); + + for(i = 0; i < HASH_BUCKETS; ++i) + { cs_writelock(__func__, &garbage_lock[i]); } + + for(i = 0; i < HASH_BUCKETS; ++i) + { + while(garbage_first[i]) + { + struct cs_garbage *next = garbage_first[i]->next; + NULLFREE(garbage_first[i]->data); + NULLFREE(garbage_first[i]); + garbage_first[i] = next; + } + } + + for(i = 0; i < HASH_BUCKETS; ++i) + { + cs_writeunlock(__func__, &garbage_lock[i]); + cs_lock_destroy(__func__, &garbage_lock[i]); + } + + pthread_mutex_destroy(&add_lock); + pthread_cond_destroy(&sleep_cond); + pthread_mutex_destroy(&sleep_cond_mutex); + } +} diff --git a/oscam-garbage.h b/oscam-garbage.h new file mode 100644 index 0000000..0a5d8af --- /dev/null +++ b/oscam-garbage.h @@ -0,0 +1,13 @@ +#ifndef OSCAM_GARBAGE_H_ +#define OSCAM_GARBAGE_H_ + +#ifdef WITH_DEBUG +extern void add_garbage_debug(void *data, char *file, uint32_t line); +#define add_garbage(x) add_garbage_debug(x, __FILE__, __LINE__) +#else +extern void add_garbage(void *data); +#endif +extern void start_garbage_collector(int32_t); +extern void stop_garbage_collector(void); + +#endif diff --git a/oscam-hashtable.c b/oscam-hashtable.c new file mode 100644 index 0000000..9ddde08 --- /dev/null +++ b/oscam-hashtable.c @@ -0,0 +1,76 @@ +#include "tommyDS_hashlin/tommychain.h" +#include "tommyDS_hashlin/tommyhash.h" +#include "tommyDS_hashlin/tommyhashlin.h" +#include "tommyDS_hashlin/tommylist.h" +#include "tommyDS_hashlin/tommytypes.h" +#include "tommyDS_hashlin/tommyhash.c" +#include "tommyDS_hashlin/tommyhashlin.c" +#include "tommyDS_hashlin/tommylist.c" + +void init_hash_table(void *ht, void *ll) +{ + tommy_hashlin_init(ht); + tommy_list_init(ll); +} + +void add_hash_table(void *ht, void *ht_node, void *ll, void *ll_node, void *obj, void *key, int key_len) +{ + tommy_hashlin_insert(ht, ht_node, obj, tommy_hash_u32(0, key, key_len)); + tommy_list_insert_tail(ll, ll_node, obj); +} + +void *find_hash_table(void *ht, void *key, int key_len, void *compare) +{ + return tommy_hashlin_search(ht, compare, key, tommy_hash_u32(0, key, key_len)); +} + +void *search_remove_elem_hash_table(void *ht, void *key, int key_len, void *compare) +{ + return tommy_hashlin_remove(ht, compare, key, tommy_hash_u32(0, key, key_len)); +} + +void remove_elem_hash_table(void *ht, void *ht_node) +{ + return tommy_hashlin_remove_existing(ht, ht_node); +} + +int count_hash_table(void *ht) +{ + return tommy_hashlin_count(ht); +} + +void deinitialize_hash_table(void *ht) +{ + tommy_hashlin_done(ht); +} + +void sort_list(void *ll, void *cmp) +{ + tommy_list_sort (ll, cmp); +} + +void remove_elem_list(void *ll, void *ll_node) +{ + return tommy_list_remove_existing(ll, ll_node); +} + +void *get_first_node_list(void *ll) +{ + return tommy_list_head(ll); +} + +void *get_first_elem_list(void *ll) +{ + if (tommy_list_head(ll)) + return tommy_list_head(ll)->data; + else + return NULL; +} + +void *get_data_from_node(void *node) +{ + if (node) + return ((tommy_node *)node)->data; + else + return NULL; +} diff --git a/oscam-hashtable.h b/oscam-hashtable.h new file mode 100644 index 0000000..a44e296 --- /dev/null +++ b/oscam-hashtable.h @@ -0,0 +1,20 @@ +#include "tommyDS_hashlin/tommytypes.h" +#include "tommyDS_hashlin/tommyhashlin.h" +#include "tommyDS_hashlin/tommylist.h" + +typedef tommy_node node; +typedef tommy_hashlin hash_table; +typedef tommy_list list; + +void init_hash_table(void *ht, void *ll); +void add_hash_table(void *ht, void *ht_node, void *ll, void *ll_node, void *obj, void *key, int key_len); +void *find_hash_table(void *ht, void *key, int key_len, void *compare); +void *search_remove_elem_hash_table(void *ht, void *key, int key_len, void *compare); +void *remove_elem_hash_table(void *ht, void *ht_node); +int count_hash_table(void *ht); +void deinitialize_hash_table(void *ht); +void sort_list(void *ll, void *cmp); +void *remove_elem_list(void *ll, void *ll_node); +void *get_first_node_list(void *ll); +void *get_first_elem_list(void *ll); +void *get_data_from_node(void *_node); diff --git a/oscam-llist.c b/oscam-llist.c new file mode 100644 index 0000000..07df639 --- /dev/null +++ b/oscam-llist.c @@ -0,0 +1,706 @@ +/* singularly linked-list */ + +#include "globals.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-string.h" + +extern char *LOG_LIST; + +/* + Locking rules: + + mutex lock is needed when... + 1. l->initial + l->last is modified/accessed + 2. LL_NODE nxt modified/accessed +*/ + +#ifdef WITH_DEBUG +static int8_t chk_debuglog(LLIST *l) +{ + return (l && l->lock.name != LOG_LIST); +} +#endif + +static void _destroy(LLIST *l) +{ + if(!l) { return; } + if(!l->flag++) + { + cs_writelock(__func__, &l->lock); // just getting sure noone is using it + cs_writeunlock(__func__, &l->lock); + + cs_lock_destroy(__func__, &l->lock); + add_garbage(l); + } +} + +LLIST *ll_create(const char *name) +{ + LLIST *l; + if(!cs_malloc(&l, sizeof(LLIST))) + { return NULL; } + cs_lock_create(__func__, &l->lock, name, 5000); + return l; +} + +void ll_destroy(LLIST **pl) +{ + LLIST *l = *pl; + if(!l || l->flag) { return; } + *pl = NULL; + ll_clear(l); + + _destroy(l); +} + +void ll_destroy_data(LLIST **pl) +{ + LLIST *l = *pl; + if(!l) { return; } + *pl = NULL; + ll_clear_data(l); + + _destroy(l); +} + +void ll_destroy_free_data(LLIST **pl) +{ + LLIST *l = *pl; + if(!l||l->flag) { return; } + *pl = NULL; + + //********************************* + cs_writelock(__func__, &l->lock); + + LL_NODE *n=l->initial, *nxt; + while(n) + { + nxt = n->nxt; + NULLFREE(n->obj); + NULLFREE(n); + n = nxt; + } + l->version++; + l->count = 0; + l->initial = 0; + l->last = 0; + cs_writeunlock(__func__, &l->lock); + //********************************** + + if(!l->flag++) + { + cs_writelock(__func__, &l->lock); //just getting sure noone is using it + cs_writeunlock(__func__, &l->lock); + + cs_lock_destroy(__func__, &l->lock); + NULLFREE(l); + } +} + + +/* Internal iteration function. Make sure that you don't have a lock and that it and it->l are set. */ +static void *ll_iter_next_nolock(LL_ITER *it) +{ + if(it->l->version != it->ll_version) + { +#ifdef WITH_DEBUG + if(chk_debuglog(it->l)) + { cs_log_dbg(D_TRACE, "list changed, searching new position"); } +#endif + + LL_NODE *ptr; + //cs_readlock(__func__, &it->l->lock); + if(!it->cur && !it->prv) + { + it->cur = it->l->initial; + } + else + { + for(ptr = it->l->initial; ptr; ptr = ptr->nxt) + { + if(ptr == it->cur) + { + it->prv = ptr; + it->cur = ptr->nxt; + break; + } + } + if(!ptr) + { + ll_iter_reset(it); // restart iteration + it->cur = it->l->initial; + } + } + it->ll_version = it->l->version; + //cs_readunlock(__func__, &it->l->lock); + + if(it->cur) + { return it->cur->obj; } + + } + else + { + if(it->cur) + { + it->prv = it->cur; + it->cur = it->cur->nxt; + } + else if(it->l->initial && !it->prv) + { it->cur = it->l->initial; } + + if(it->cur) + { return it->cur->obj; } + } + return NULL; +} + +static void ll_clear_int(LLIST *l, int32_t clear_data) +{ + if(!l || l->flag) { return; } + + cs_writelock(__func__, &l->lock); + + LL_NODE *n = l->initial, *nxt; + while(n) + { + nxt = n->nxt; + if(clear_data) + { add_garbage(n->obj); } + add_garbage(n); + n = nxt; + } + l->version++; + l->count = 0; + l->initial = 0; + l->last = 0; + cs_writeunlock(__func__, &l->lock); +} + +void ll_clear(LLIST *l) +{ + ll_clear_int(l, 0); +} + + +void ll_clear_data(LLIST *l) +{ + ll_clear_int(l, 1); +} + +/* Appends to the list. Do not call this from outside without having a lock! */ +static LL_NODE *ll_append_nolock(LLIST *l, void *obj) +{ + if(l && obj && !l->flag) + { + LL_NODE *new; + if(!cs_malloc(&new, sizeof(LL_NODE))) + { return NULL; } + new->obj = obj; + + if(l->last) + { l->last->nxt = new; } + else + { l->initial = new; } + l->last = new; + + l->count++; + return new; + } + + return NULL; +} + +LL_NODE *ll_append(LLIST *l, void *obj) +{ + if(l && obj && !l->flag) + { + cs_writelock(__func__, &l->lock); + + LL_NODE *n = ll_append_nolock(l, obj); + cs_writeunlock(__func__, &l->lock); + return n; + } + return NULL; +} + +LL_NODE *ll_prepend(LLIST *l, void *obj) +{ + if(l && obj && !l->flag) + { + LL_NODE *new; + if(!cs_malloc(&new, sizeof(LL_NODE))) + { return NULL; } + new->obj = obj; + + cs_writelock(__func__, &l->lock); + + new->nxt = l->initial; + + l->initial = new; + if(!l->last) + { l->last = l->initial; } + l->count++; + cs_writeunlock(__func__, &l->lock); + + return new; + } + + return NULL; +} + +LL_ITER ll_iter_create(LLIST *l) +{ + LL_ITER it; + memset(&it, 0, sizeof(it)); + it.l = l; + if(it.l) + { it.ll_version = it.l->version; } + return it; +} + +void *ll_iter_next(LL_ITER *it) +{ + if(it && it->l && !it->l->flag) + { + cs_readlock(__func__, &it->l->lock); + void *res = ll_iter_next_nolock(it); + cs_readunlock(__func__, &it->l->lock); + return res; + } + return NULL; +} + +void *ll_iter_remove_nolock(LL_ITER *it) +{ + void *obj = NULL; + if(it) + { + LL_NODE *del = it->cur; + if(del) + { + obj = del->obj; + LL_NODE *prv = it->prv; + if(it->ll_version != it->l->version || !prv) // List has been modified so it->prv might be wrong! + { + LL_NODE *n = it->l->initial; + prv = NULL; + while(n && n != del) + { + prv = n; + n = n->nxt; + } + if(n != del) + { return NULL; } + } + + if(prv) + { prv->nxt = del->nxt; } + else + { it->l->initial = del->nxt; } + if(!it->l->initial) + { it->l->last = NULL; } + else if(del == it->l->last) + { it->l->last = prv; } + + it->cur = it->l->initial; + it->prv = NULL; + if(prv != NULL) + { + while(it->cur && it->cur != prv) + { + it->prv = it->cur; + it->cur = it->cur->nxt; + } + } + else + { it->cur = NULL; } + it->l->count--; + it->ll_version = ++it->l->version; + + add_garbage(del); + } + } + return obj; +} + +void *ll_iter_next_remove(LL_ITER *it) +{ + if(it && it->l && !it->l->flag) + { + cs_writelock(__func__, &it->l->lock); + void *res = ll_iter_next_nolock(it); + ll_iter_remove_nolock(it); + cs_writeunlock(__func__, &it->l->lock); + return res; + } + return NULL; +} + +void *ll_iter_move(LL_ITER *it, int32_t offset) +{ + if(it && it->l && !it->l->flag) + { + int32_t i; + void *res = NULL; + for(i = 0; i < offset; i++) + { + res = ll_iter_next_nolock(it); + if(!res) { break; } + } + + return res; + } + return NULL; +} + +void *ll_iter_peek(const LL_ITER *it, int32_t offset) +{ + if(it && it->l && !it->l->flag) + { + cs_readlock(__func__, &((LL_ITER *)it)->l->lock); + + LL_NODE *n = it->cur; + int32_t i; + + for(i = 0; i < offset; i++) + { + if(n) + { n = n->nxt; } + else + { break; } + } + cs_readunlock(__func__, &((LL_ITER *)it)->l->lock); + + if(!n) + { return NULL; } + return n->obj; + } + return NULL; +} + +void ll_iter_reset(LL_ITER *it) +{ + if(it) + { + it->prv = NULL; + it->cur = NULL; + } +} + +void ll_iter_insert(LL_ITER *it, void *obj) +{ + if(it && obj && !it->l->flag) + { + cs_writelock(__func__, &it->l->lock); + + if(!it->cur || !it->cur->nxt) + { ll_append_nolock(it->l, obj); } + else + { + LL_NODE *n; + if(!cs_malloc(&n, sizeof(LL_NODE))) + { + cs_writeunlock(__func__, &it->l->lock); + return; + } + + n->obj = obj; + n->nxt = it->cur->nxt; + it->cur->nxt = n; + + it->l->count++; + it->ll_version = ++it->l->version; + } + cs_writeunlock(__func__, &it->l->lock); + } +} + +/* Removes the element to which the iterator currently points. */ +void *ll_iter_remove(LL_ITER *it) +{ + void *obj = NULL; + if(it && it->l && !it->l->flag) + { + LL_NODE *del = it->cur; + if(del) + { + cs_writelock(__func__, &it->l->lock); + obj = ll_iter_remove_nolock(it); + cs_writeunlock(__func__, &it->l->lock); + } + } + + return obj; +} + +/* Moves the element which is currently pointed to by the iterator to the head of the list.*/ +int32_t ll_iter_move_first(LL_ITER *it) +{ + int32_t moved = 0; + if(it && it->l && !it->l->flag) + { + LL_NODE *move = it->cur; + if(move) + { + if(move == it->l->initial) //Can't move self to first + { return 1; } + + LL_NODE *prv = it->prv; + cs_writelock(__func__, &it->l->lock); + if(it->ll_version != it->l->version || !prv) // List has been modified so it->prv might be wrong! + { + LL_NODE *n = it->l->initial; + prv = NULL; + while(n && n != move) + { + prv = n; + n = n->nxt; + } + if(n != move) + { + cs_writeunlock(__func__, &it->l->lock); + return moved; + } + } + + if(prv) + { prv->nxt = move->nxt; } + else + { it->l->initial = move->nxt; } + + if(prv && it->l->last == move) + { it->l->last = prv; } + move->nxt = it->l->initial; + it->l->initial = move; + + it->ll_version = ++it->l->version; + it->prv = NULL; + cs_writeunlock(__func__, &it->l->lock); + moved = 1; + } + } + return moved; +} + +void ll_iter_remove_data(LL_ITER *it) +{ + void *obj = ll_iter_remove(it); + add_garbage(obj); +} + +void *ll_has_elements(const LLIST *l) +{ + if(!l || !l->initial || l->flag) + { return NULL; } + return l->initial->obj; +} + +void *ll_last_element(const LLIST *l) +{ + if(!l || !l->last || l->flag) + { return NULL; } + return l->last->obj; +} + +int32_t ll_contains(const LLIST *l, const void *obj) +{ + if(!l || !obj || l->flag) + { return 0; } + LL_ITER it = ll_iter_create((LLIST *) l); + const void *data; + while((data = ll_iter_next(&it))) + { + if(data == obj) + { break; } + } + return (data == obj); +} + +const void *ll_contains_data(const LLIST *l, const void *obj, uint32_t size) +{ + if(!l || !obj || l->flag) + { return NULL; } + LL_ITER it = ll_iter_create((LLIST *) l); + const void *data; + while((data = ll_iter_next(&it))) + { + if(!memcmp(data, obj, size)) + { break; } + } + return data; +} + +int32_t ll_remove(LLIST *l, const void *obj) +{ + int32_t n = 0; + LL_ITER it = ll_iter_create(l); + void *data; + while((data = ll_iter_next(&it))) + { + if(data == obj) + { + ll_iter_remove(&it); + n++; + } + } + return n; +} + +void ll_remove_data(LLIST *l, void *obj) +{ + LL_ITER it = ll_iter_create(l); + void *data; + while((data = ll_iter_next(&it))) + { + if(data == obj) + { ll_iter_remove_data(&it); } + } +} + +// removes all elements from l where elements are in elements_to_remove +int32_t ll_remove_all(LLIST *l, const LLIST *elements_to_remove) +{ + int32_t count = 0; + LL_ITER it1 = ll_iter_create(l); + LL_ITER it2 = ll_iter_create((LLIST *) elements_to_remove); + + const void *data1, *data2; + while((data1 = ll_iter_next(&it1))) + { + ll_iter_reset(&it2); + while((data2 = ll_iter_next(&it2))) + { + if(data1 == data2) + { + ll_iter_remove(&it1); + count++; + break; + } + } + } + + return count; +} + +/* Returns an array with all elements sorted, the amount of elements is stored in size. We do not sort the original linked list + as this might harm running iterations. Furthermore, we need the array anyway for qsort() to work. Remember to free() the result. */ +void **ll_sort(const LLIST *l, void *compare, int32_t *size) +{ + if(!l || !l->initial || !compare) + { + *size = 0; + return NULL; + } + int32_t i = 0; + LL_NODE *n; + + cs_readlock(__func__, &((LLIST *)l)->lock); + *size = l->count; + void **p; + if(!cs_malloc(&p, l->count * sizeof(p[0]))) + { + cs_readunlock(__func__, &((LLIST *)l)->lock); + return NULL; + } + for(n = l->initial; n; n = n->nxt) + { + p[i++] = n->obj; + } + cs_readunlock(__func__, &((LLIST *)l)->lock); +#ifdef WITH_DEBUG + //if (chk_debugLog(it->l)) + //cs_log_dbg(D_TRACE, "sort: count %d size %d", l->count, sizeof(p[0])); +#endif + qsort(p, l->count, sizeof(p[0]), compare); + + return p; +} + +void ll_putall(LLIST *dest, LLIST *src) +{ + LL_ITER it = ll_iter_create(src); + void *data; + while((data = ll_iter_next(&it))) + { + ll_append(dest, data); + } +} + +// New Iterator: +LL_LOCKITER *ll_li_create(LLIST *l, int32_t writelock) +{ + if(!l || l->flag) { return NULL; } + + LL_LOCKITER *li; + if(!cs_malloc(&li, sizeof(LL_LOCKITER))) + { return NULL; } + + li->l = l; + li->writelock = writelock; + if(writelock) + { cs_writelock(__func__, &l->lock); } + else + { cs_readlock(__func__, &l->lock); } + li->it = ll_iter_create(l); + return li; +} + +void ll_li_destroy(LL_LOCKITER *li) +{ + if(li && li->l) + { + if(li->writelock) + { cs_writeunlock(__func__, &li->l->lock); } + else + { cs_readunlock(__func__, &li->l->lock); } + li->l = NULL; + add_garbage(li); + } +} + +void *ll_li_next(LL_LOCKITER *li) +{ + if(li && li->l) + { + return ll_iter_next_nolock(&li->it); + } + return NULL; +} + +LLIST *ll_clone(LLIST *l, uint32_t copysize) +{ + if(!l || l->flag) { return NULL; } + + LLIST *cloned = ll_create(l->lock.name); + LL_LOCKITER *li = ll_li_create(l, 0); + void *data; + while((data = ll_li_next(li))) + { + void *new_data; + if(!cs_malloc(&new_data, copysize)) + { break; } + memcpy(new_data, data, copysize); + ll_append_nolock(cloned, new_data); + } + ll_li_destroy(li); + return cloned; +} + +void *ll_remove_first(LLIST *l) +{ + if(l && !l->flag) + { + LL_ITER it = ll_iter_create(l); + void *data = ll_iter_next(&it); + if(data) { ll_iter_remove(&it); } + return data; + } + return NULL; +} + +void ll_remove_first_data(LLIST *l) +{ + void *data = ll_remove_first(l); + if(data) { NULLFREE(data); } +} diff --git a/oscam-llist.h b/oscam-llist.h new file mode 100644 index 0000000..2e93d69 --- /dev/null +++ b/oscam-llist.h @@ -0,0 +1,92 @@ +/* singularly linked-list */ + +#ifndef OSCAM_LLIST_H_ +#define OSCAM_LLIST_H_ + +typedef struct llnode LL_NODE; +struct llnode +{ + void *obj; + LL_NODE *nxt; +}; + +typedef struct llist LLIST; +struct llist +{ + //void *obj; + LL_NODE *initial; + LL_NODE *last; + int32_t count; + CS_MUTEX_LOCK lock; + int32_t flag; + uint32_t version; // updated on every modification of the list - exception is on prepends and appends as this should not have impacts on iterations! +}; + +typedef struct lliter LL_ITER; +struct lliter +{ + LLIST *l; + LL_NODE *cur, *prv; + uint32_t ll_version; +}; + +typedef struct llistlockiter LL_LOCKITER; +struct llistlockiter +{ + LLIST *l; + int32_t writelock; + LL_ITER it; +}; + +LLIST *ll_create(const char *name); // create llist, return ptr to llist +void ll_destroy(LLIST **pl); // same as ll_clear_abstract() but frees up LLIST mem as well +void ll_destroy_data(LLIST **pl); // same as ll_clear_data() but frees up obj allocations as well +void ll_destroy_free_data(LLIST **pl); // same as ll_clear_data() but frees up obj allocations as well. More, really free node without use GBC + +void ll_clear(LLIST *l); // frees up all llnodes nodes but not data held in obj ptrs +void ll_clear_data(LLIST *l); // same as ll_clear_data() but frees up obj allocations as well + +void **ll_sort(const LLIST *l, void *compare, int32_t *size); // sorts the list, compare = int func(const T *a, const T *b) +LL_NODE *ll_append(LLIST *l, void *obj); // append obj to llist +LL_NODE *ll_prepend(LLIST *l, void *obj); // prepend obj to llist + +// clones a list, duplicates data +LLIST *ll_clone(LLIST *l, uint32_t copysize); + +// New type of lock, list is locked during iterate! create=lock, destroy=unlock +LL_LOCKITER *ll_li_create(LLIST *l, int32_t writelock); +void ll_li_destroy(LL_LOCKITER *li); +void *ll_li_next(LL_LOCKITER *li); + +// Old Iterators: +LL_ITER ll_iter_create(LLIST *l); // return ptr to iterator obj +void *ll_iter_next(LL_ITER *it); // iterate to and return next llnode obj, returns NULL at end +void *ll_iter_next_remove(LL_ITER *it); // iterate to and return next llnode obj, returns NULL at end, removing it +void *ll_iter_peek(const LL_ITER *it, int32_t offset); // return obj at offset from iterator but do not iterate +void ll_iter_reset(LL_ITER *it); // reset itrerator to first llnode +void ll_iter_insert(LL_ITER *it, void *obj); // insert obj at iterator node +void *ll_iter_remove(LL_ITER *it); // remove llnode at iterator, returns ptr to the llnode obj removed +void ll_iter_remove_data(LL_ITER *it); // remove llnode and free llnode obj +void *ll_iter_move(LL_ITER *it, int32_t offset); // moves the iterator position +int32_t ll_iter_move_first(LL_ITER *it); // moves an entry to top +static inline int32_t ll_count(const LLIST *l) // return number of items in list +{ + if(!l || l->flag) + { return 0; } + + return l->count; +} +void *ll_has_elements(const LLIST *l); // returns first obj if has one +void *ll_last_element(const LLIST *l); +int32_t ll_contains(const LLIST *l, const void *obj); +const void *ll_contains_data(const LLIST *l, const void *obj, uint32_t size); +int32_t ll_remove(LLIST *l, const void *obj); +void ll_remove_data(LLIST *l, void *obj); +int32_t ll_remove_all(LLIST *l, const LLIST *elements_to_remove); // removes all elements from l where elements are in elements_to_remove + +void ll_putall(LLIST *dest, LLIST *src); + +void *ll_remove_first(LLIST *l); +void ll_remove_first_data(LLIST *l); + +#endif diff --git a/oscam-lock.c b/oscam-lock.c new file mode 100644 index 0000000..f1676da --- /dev/null +++ b/oscam-lock.c @@ -0,0 +1,271 @@ +#define MODULE_LOG_PREFIX "lock" + +#include "globals.h" +#include "oscam-lock.h" +#include "oscam-time.h" + +extern char *LOG_LIST; + +/** + * creates a lock + **/ +void cs_lock_create(const char *n, CS_MUTEX_LOCK *l, const char *name, uint32_t timeout_ms) +{ + memset(l, 0, sizeof(CS_MUTEX_LOCK)); + l->timeout = timeout_ms / 1000; + l->name = name; + SAFE_MUTEX_INIT_R(&l->lock, NULL, n); + __cs_pthread_cond_init(n, &l->writecond); + __cs_pthread_cond_init(n, &l->readcond); +#ifdef WITH_MUTEXDEBUG + cs_log_dbg(D_TRACE, "lock %s created", name); +#endif +} + +/** + * creates a lock + **/ +void cs_lock_create_nolog(const char *n, CS_MUTEX_LOCK *l, const char *name, uint32_t timeout_ms) +{ + memset(l, 0, sizeof(CS_MUTEX_LOCK)); + l->timeout = timeout_ms / 1000; + l->name = name; + SAFE_MUTEX_INIT_NOLOG_R(&l->lock, NULL, n); + __cs_pthread_cond_init(n, &l->writecond); + __cs_pthread_cond_init(n, &l->readcond); +#ifdef WITH_MUTEXDEBUG + cs_log_dbg(D_TRACE, "lock %s created", name); +#endif +} + +void cs_lock_destroy(const char *pn, CS_MUTEX_LOCK *l) +{ + if(!l || !l->name || l->flag) { return; } + + cs_rwlock_int(pn, l, WRITELOCK); +#ifdef WITH_DEBUG + const char *old_name = l->name; +#endif + l->name = NULL; // No new locks! + cs_rwunlock_int(pn, l, WRITELOCK); + + // Do not destroy when having pending locks! + int32_t n = (l->timeout / 10) + 2; + while((--n > 0) && (l->writelock || l->readlock)) { cs_sleepms(10); } + + cs_rwlock_int(pn, l, WRITELOCK); + l->flag++; // No new unlocks! + cs_rwunlock_int(pn, l, WRITELOCK); + +#ifdef WITH_DEBUG + if(!n && old_name != LOG_LIST) + { cs_log("WARNING lock %s destroy timed out.", old_name); } +#endif + + pthread_mutex_destroy(&l->lock); + pthread_cond_destroy(&l->writecond); + pthread_cond_destroy(&l->readcond); +#ifdef WITH_MUTEXDEBUG + cs_log_dbg(D_TRACE, "lock %s destroyed", l->name); +#endif +} + +void cs_rwlock_int(const char *n, CS_MUTEX_LOCK *l, int8_t type) +{ + struct timespec ts; + int8_t ret = 0; + + if(!l || !l->name || l->flag) + { return; } + + SAFE_MUTEX_LOCK_R(&l->lock, n); + + add_ms_to_timespec(&ts, l->timeout * 1000); + ts.tv_nsec = 0; // 100% resemble previous code, I consider it wrong + if(type == WRITELOCK) + { + l->writelock++; + // if read- or writelock is busy, wait for unlock + if(l->writelock > 1 || l->readlock > 0) + { ret = pthread_cond_timedwait(&l->writecond, &l->lock, &ts); } + } + else + { + l->readlock++; + // if writelock is busy, wait for unlock + if(l->writelock > 0) + { ret = pthread_cond_timedwait(&l->readcond, &l->lock, &ts); } + } + + if(ret > 0) + { + // lock wasn't returned within time, assume locking thread to + // be stuck or finished, so enforce lock. + l->writelock = (type == WRITELOCK) ? 1 : 0; + l->readlock = (type == WRITELOCK) ? 0 : 1; +#ifdef WITH_DEBUG + if(l->name != LOG_LIST) + { cs_log("WARNING lock %s (%s) timed out.", l->name, (type == WRITELOCK) ? "WRITELOCK" : "READLOCK"); } +#endif + } + + SAFE_MUTEX_UNLOCK_R(&l->lock, n); +#ifdef WITH_MUTEXDEBUG + //cs_log_dbg(D_TRACE, "lock %s locked", l->name); +#endif + return; +} + +void cs_rwlock_int_nolog(const char *n, CS_MUTEX_LOCK *l, int8_t type) +{ + struct timespec ts; + int8_t ret = 0; + + if(!l || !l->name || l->flag) + { return; } + + SAFE_MUTEX_LOCK_NOLOG_R(&l->lock, n); + + add_ms_to_timespec(&ts, l->timeout * 1000); + ts.tv_nsec = 0; // 100% resemble previous code, I consider it wrong + if(type == WRITELOCK) + { + l->writelock++; + // if read- or writelock is busy, wait for unlock + if(l->writelock > 1 || l->readlock > 0) + { ret = pthread_cond_timedwait(&l->writecond, &l->lock, &ts); } + } + else + { + l->readlock++; + // if writelock is busy, wait for unlock + if(l->writelock > 0) + { ret = pthread_cond_timedwait(&l->readcond, &l->lock, &ts); } + } + + if(ret > 0) + { + // lock wasn't returned within time, assume locking thread to + // be stuck or finished, so enforce lock. + l->writelock = (type == WRITELOCK) ? 1 : 0; + l->readlock = (type == WRITELOCK) ? 0 : 1; +#ifdef WITH_DEBUG + if(l->name != LOG_LIST) + { cs_log("WARNING lock %s (%s) timed out.", l->name, (type == WRITELOCK) ? "WRITELOCK" : "READLOCK"); } +#endif + } + + SAFE_MUTEX_UNLOCK_NOLOG_R(&l->lock, n); +#ifdef WITH_MUTEXDEBUG + //cs_log_dbg(D_TRACE, "lock %s locked", l->name); +#endif + return; +} + +void cs_rwunlock_int(const char *n, CS_MUTEX_LOCK *l, int8_t type) +{ + + if(!l || l->flag) { return; } + + SAFE_MUTEX_LOCK_R(&l->lock, n); + + if(type == WRITELOCK) + { l->writelock--; } + else + { l->readlock--; } + + if(l->writelock < 0) { l->writelock = 0; } + if(l->readlock < 0) { l->readlock = 0; } + + // waiting writelocks always have priority. If one is waiting, signal it + if(l->writelock) + { SAFE_COND_SIGNAL_R(&l->writecond, n); } + // Otherwise signal a waiting readlock (if any) + else if(l->readlock && type != READLOCK) + { SAFE_COND_BROADCAST_R(&l->readcond, n); } + + SAFE_MUTEX_UNLOCK_R(&l->lock, n); + +#ifdef WITH_MUTEXDEBUG +#ifdef WITH_DEBUG + if(l->name != LOG_LIST) + { + const char *typetxt[] = { "", "write", "read" }; + cs_log_dbg(D_TRACE, "%slock %s: released", typetxt[type], l->name); + } +#endif +#endif +} + +void cs_rwunlock_int_nolog(const char *n, CS_MUTEX_LOCK *l, int8_t type) +{ + + if(!l || l->flag) { return; } + + SAFE_MUTEX_LOCK_NOLOG_R(&l->lock, n); + + if(type == WRITELOCK) + { l->writelock--; } + else + { l->readlock--; } + + if(l->writelock < 0) { l->writelock = 0; } + if(l->readlock < 0) { l->readlock = 0; } + + // waiting writelocks always have priority. If one is waiting, signal it + if(l->writelock) + { SAFE_COND_SIGNAL_R(&l->writecond, n); } + // Otherwise signal a waiting readlock (if any) + else if(l->readlock && type != READLOCK) + { SAFE_COND_BROADCAST_R(&l->readcond, n); } + + SAFE_MUTEX_UNLOCK_NOLOG_R(&l->lock, n); + +#ifdef WITH_MUTEXDEBUG +#ifdef WITH_DEBUG + if(l->name != LOG_LIST) + { + const char *typetxt[] = { "", "write", "read" }; + cs_log_dbg(D_TRACE, "%slock %s: released", typetxt[type], l->name); + } +#endif +#endif +} + +int8_t cs_try_rwlock_int(const char *n, CS_MUTEX_LOCK *l, int8_t type) +{ + if(!l || !l->name || l->flag) + { return 0; } + + int8_t status = 0; + + SAFE_MUTEX_LOCK_R(&l->lock, n); + + if(type == WRITELOCK) + { + if(l->writelock || l->readlock) + { status = 1; } + else + { l->writelock++; } + } + else + { + if(l->writelock) + { status = 1; } + else + { l->readlock++; } + } + + SAFE_MUTEX_UNLOCK_R(&l->lock, n); + +#ifdef WITH_MUTEXDEBUG +#ifdef WITH_DEBUG + if(l->name != LOG_LIST) + { + const char *typetxt[] = { "", "write", "read" }; + cs_log_dbg(D_TRACE, "try_%slock %s: status=%d", typetxt[type], l->name, status); + } +#endif +#endif + return status; +} diff --git a/oscam-lock.h b/oscam-lock.h new file mode 100644 index 0000000..36e06dd --- /dev/null +++ b/oscam-lock.h @@ -0,0 +1,28 @@ +#ifndef OSCAM_LOCK_H_ +#define OSCAM_LOCK_H_ + +// Lock types +#define WRITELOCK 1 +#define READLOCK 2 + +void cs_lock_create(const char *n, CS_MUTEX_LOCK *l, const char *name, uint32_t timeout_ms); +void cs_lock_destroy(const char *n, CS_MUTEX_LOCK *l); +void cs_rwlock_int(const char *n, CS_MUTEX_LOCK *l, int8_t type); +void cs_rwunlock_int(const char *n, CS_MUTEX_LOCK *l, int8_t type); +int8_t cs_try_rwlock_int(const char *n, CS_MUTEX_LOCK *l, int8_t type); + +void cs_lock_create_nolog(const char *n, CS_MUTEX_LOCK *l, const char *name, uint32_t timeout_ms); +void cs_rwlock_int_nolog(const char *n, CS_MUTEX_LOCK *l, int8_t type); +void cs_rwunlock_int_nolog(const char *n, CS_MUTEX_LOCK *l, int8_t type); + +#define cs_writelock(n, l) cs_rwlock_int(n, l, WRITELOCK) +#define cs_readlock(n, l) cs_rwlock_int(n, l, READLOCK) +#define cs_writeunlock(n, l) cs_rwunlock_int(n, l, WRITELOCK) +#define cs_readunlock(n, l) cs_rwunlock_int(n, l, READLOCK) +#define cs_try_writelock(n, l) cs_try_rwlock_int(n, l, WRITELOCK) +#define cs_try_readlock(n, l) cs_try_rwlock_int(n, l, READLOCK) + +#define cs_writelock_nolog(n, l) cs_rwlock_int_nolog(n, l, WRITELOCK) +#define cs_writeunlock_nolog(n, l) cs_rwunlock_int_nolog(n, l, WRITELOCK) + +#endif diff --git a/oscam-log-reader.c b/oscam-log-reader.c new file mode 100644 index 0000000..ce850f2 --- /dev/null +++ b/oscam-log-reader.c @@ -0,0 +1,151 @@ +#define MODULE_LOG_PREFIX "reader" + +#include "globals.h" + +#include "oscam-log.h" +#include "oscam-log-reader.h" +#include "oscam-reader.h" +#include "oscam-string.h" + +extern int log_remove_sensitive; + +static char *debug_mask_txt(int mask) +{ + switch(mask) + { + case D_EMM: + return "EMM: "; + + case D_IFD: + return "IFD: "; + + case D_TRACE: + return "TRACE: "; + + case D_DEVICE: + return "IO: "; + + default: + return ""; + } +} + +static const char *reader_desc_txt(struct s_reader *reader) +{ + if(reader->csystem && reader->csystem->desc) + { return reader->csystem->desc; } + else if(reader->crdr && reader->crdr->desc) + { return reader->crdr->desc; } + else if(reader->ph.desc) + { return reader->ph.desc; } + else + { return reader_get_type_desc(reader, 1); } +} + +static char *format_sensitive(char *result, int remove_sensitive) +{ + // Filter sensitive information + int i, n = cs_strlen(result), p = 0; + if(remove_sensitive) + { + int in_sens = 0; + for(i = 0; i < n; i++) + { + switch(result[i]) + { + case '{': + in_sens = 1; + continue; + + case '}': + in_sens = 0; + break; + } + + if(in_sens) + { result[i] = '#'; } + } + } + + // Filter sensitive markers + for(i = 0; i < n; i++) + { + if(result[i] == '{' || result[i] == '}') + { continue; } + + result[p++] = result[i]; + } + result[p] = '\0'; + return result; +} + +void rdr_log(struct s_reader *reader, char *fmt, ...) +{ + char txt[256]; + va_list args; + va_start(args, fmt); + vsnprintf(txt, sizeof(txt), fmt, args); + va_end(args); + cs_log("%s [%s] %s", reader->label, reader_desc_txt(reader), txt); +} + +void rdr_log_sensitive(struct s_reader *reader, char *fmt, ...) +{ + char txt[256]; + va_list args; + va_start(args, fmt); + vsnprintf(txt, sizeof(txt), fmt, args); + va_end(args); + format_sensitive(txt, log_remove_sensitive); + rdr_log(reader, "%s", txt); +} + +void rdr_log_dbg(struct s_reader *reader, uint16_t mask, char *fmt, ...) +{ + if(config_enabled(WITH_DEBUG)) + { + char txt[2048]; + va_list args; + va_start(args, fmt); + vsnprintf(txt, sizeof(txt), fmt, args); + va_end(args); + cs_log_dbg(mask, "%s [%s] %s%s", reader->label, reader_desc_txt(reader), debug_mask_txt(mask), txt); + } +} + +void rdr_log_dbg_sensitive(struct s_reader *reader, uint16_t mask, char *fmt, ...) +{ + if(config_enabled(WITH_DEBUG)) + { + char txt[2048]; + va_list args; + va_start(args, fmt); + vsnprintf(txt, sizeof(txt), fmt, args); + va_end(args); + format_sensitive(txt, log_remove_sensitive); + rdr_log_dbg(reader, mask, "%s", txt); + } +} + +void rdr_log_dump(struct s_reader *reader, const uint8_t *buf, int n, char *fmt, ...) +{ + char txt[2048]; + va_list args; + va_start(args, fmt); + vsnprintf(txt, sizeof(txt), fmt, args); + va_end(args); + cs_log_dump(buf, n, "%s [%s] %s", reader->label, reader_desc_txt(reader), txt); +} + +void rdr_log_dump_dbg(struct s_reader *reader, uint16_t mask, const uint8_t *buf, int n, char *fmt, ...) +{ + if(config_enabled(WITH_DEBUG)) + { + char txt[2048]; + va_list args; + va_start(args, fmt); + vsnprintf(txt, sizeof(txt), fmt, args); + va_end(args); + cs_log_dump_dbg(mask, buf, n, "%s [%s] %s%s", reader->label, reader_desc_txt(reader), debug_mask_txt(mask), txt); + } +} diff --git a/oscam-log-reader.h b/oscam-log-reader.h new file mode 100644 index 0000000..1c64b06 --- /dev/null +++ b/oscam-log-reader.h @@ -0,0 +1,13 @@ +#ifndef OSCAM_LOG_READER_H_ +#define OSCAM_LOG_READER_H_ + +void rdr_log(struct s_reader *reader, char *, ...) __attribute__((format(printf, 2, 3))); +void rdr_log_sensitive(struct s_reader *reader, char *, ...) __attribute__((format(printf, 2, 3))); + +void rdr_log_dbg(struct s_reader *reader, uint16_t mask, char *fmt, ...) __attribute__((format(printf, 3, 4))); +void rdr_log_dbg_sensitive(struct s_reader *reader, uint16_t mask, char *fmt, ...) __attribute__((format(printf, 3, 4))); + +void rdr_log_dump(struct s_reader *reader, const uint8_t *buf, int n, char *fmt, ...) __attribute__((format(printf, 4, 5))); +void rdr_log_dump_dbg(struct s_reader *reader, uint16_t mask, const uint8_t *buf, int n, char *fmt, ...) __attribute__((format(printf, 5, 6))); + +#endif diff --git a/oscam-log.c b/oscam-log.c new file mode 100644 index 0000000..a335369 --- /dev/null +++ b/oscam-log.c @@ -0,0 +1,854 @@ +#include "globals.h" +#include +#include "module-anticasc.h" +#include "module-monitor.h" +#include "oscam-client.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-log.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-time.h" + +// Do not allow log_list to grow bigger than that many entries +#define MAX_LOG_LIST_BACKLOG 10000 + +extern char *syslog_ident; +extern int32_t exit_oscam; + +char *LOG_LIST = "log_list"; +int8_t logStarted = 0; + +static FILE *fp; +static FILE *fps; +static LLIST *log_list; +static bool log_running; +static int log_list_queued; +static pthread_t log_thread; +static pthread_cond_t log_thread_sleep_cond; +static pthread_mutex_t log_thread_sleep_cond_mutex; +static int32_t syslog_socket = -1; +static struct SOCKADDR syslog_addr; + + +struct s_log +{ + char *txt; + uint8_t header_len; + uint8_t header_logcount_offset; + uint8_t header_date_offset; + uint8_t header_time_offset; + uint8_t header_info_offset; + int8_t direct_log; + int8_t cl_typ; + char *cl_usr; + char *cl_text; +}; + +#define LOG_BUF_SIZE 512 + +static void switch_log(char *file, FILE **f, int32_t (*pfinit)(void)) +{ + // only 1 thread needs to switch the log; even if anticasc, statistics and normal log are running + // at the same time, it is ok to have the other logs switching 1 entry later + if(cfg.max_log_size && file) + { + if(*f != NULL && ftell(*f) >= cfg.max_log_size * 1024) + { + int32_t rc; + char prev_log[cs_strlen(file) + 6]; + snprintf(prev_log, sizeof(prev_log), "%s-prev", file); + fprintf(*f, "switch log file\n"); + fflush(*f); + fclose(*f); + *f = (FILE *)0; + rc = rename(file, prev_log); + + if(rc != 0) + { + fprintf(stderr, "rename(%s, %s) failed (errno=%d %s)\n", file, prev_log, errno, strerror(errno)); + } + else if(pfinit()) + { + fprintf(stderr, "Initialisation of log file failed, continuing without logging thread %8lX. Log will be output to stdout!", (unsigned long)pthread_self()); + cfg.logtostdout = 1; + } + } + } +} + +void cs_reopen_log(void) +{ + if(cfg.logfile) + { + if(fp) + { + fprintf(fp, "flush and re-open log file\n"); + fflush(fp); + fclose(fp); + fp = NULL; + } + + if(cs_open_logfiles()) + { + fprintf(stderr, "Initialisation of log file failed, continuing without logging thread %8luX. Log will be output to stdout!", (unsigned long)pthread_self()); + cfg.logtostdout = 1; + } + } + + if(cfg.usrfile) + { + if(fps) + { + fprintf(fps, "flush and re-open user log file\n"); + fflush(fps); + fclose(fps); + fps = NULL; + } + + if(cs_init_statistics()) + { + fprintf(stderr, "Initialisation of user log file failed, continuing without logging thread %8luX.", (unsigned long)pthread_self()); + } + } +} + +static void cs_write_log(char *txt, int8_t do_flush, uint8_t hdr_date_offset, uint8_t hdr_time_offset) +{ + // filter out entries with leading 's' and forward to statistics + if(txt[hdr_date_offset] == 's') + { + if(fps) + { + switch_log(cfg.usrfile, &fps, cs_init_statistics); + if(fps) + { + fputs(txt + hdr_date_offset + 1, fps); // remove the leading 's' and write to file + if(do_flush) { fflush(fps); } + } + } + } + else + { + if(!cfg.disablelog) + { + if(fp) + { + switch_log(cfg.logfile, &fp, cs_open_logfiles); // only call the switch code if lock = 1 is specified as otherwise we are calling it internally + if(fp) + { + fputs(txt + hdr_date_offset, fp); + if(do_flush) { fflush(fp); } + } + } + + if(cfg.logtostdout) + { + fputs(txt + hdr_time_offset, stdout); + if(do_flush) { fflush(stdout); } + } + } + } +} + +static void log_list_flush(void) +{ + if(logStarted == 0) + { return; } + + SAFE_COND_SIGNAL_NOLOG(&log_thread_sleep_cond); + int32_t i = 0; + while(ll_count(log_list) > 0 && i < 200) + { + cs_sleepms(5); + ++i; + } +} + +static void log_list_add(struct s_log *log) +{ + if(logStarted == 0) + { return; } + + int32_t count = ll_count(log_list); + log_list_queued++; + if(count < MAX_LOG_LIST_BACKLOG) + { + ll_append(log_list, log); + } + else // We have too much backlog + { + NULLFREE(log->txt); + NULLFREE(log); + cs_write_log("-------------> Too much data in log_list, dropping log message.\n", 1, 0, 0); + } + SAFE_COND_SIGNAL_NOLOG(&log_thread_sleep_cond); +} + +static void cs_write_log_int(char *txt) +{ + if(exit_oscam == 1) + { + cs_write_log(txt, 1, 0, 0); + } + else + { + char *newtxt = cs_strdup(txt); + if(!newtxt) + { return; } + struct s_log *log; + if(!cs_malloc(&log, sizeof(struct s_log))) + { + NULLFREE(newtxt); + return; + } + log->txt = newtxt; + log->header_len = 0; + log->direct_log = 1; + log_list_add(log); + } +} + +int32_t cs_open_logfiles(void) +{ + char *starttext; + if(logStarted) { starttext = "log switched"; } + else { starttext = "started"; } + if(!fp && cfg.logfile) // log to file + { + if((fp = fopen(cfg.logfile, "a+")) <= (FILE *)0) + { + fp = (FILE *)0; + fprintf(stderr, "couldn't open logfile: %s (errno %d %s)\n", cfg.logfile, errno, strerror(errno)); + } + else + { + char line[80]; + memset(line, '-', sizeof(line)); + line[(sizeof(line) / sizeof(char)) - 1] = '\0'; + time_t walltime = cs_time(); + if(!cfg.disablelog) + { + char buf[28]; + cs_ctime_r(&walltime, buf); + fprintf(fp, "\n%s\n>> OSCam << cardserver %s at %s%s\n", line, starttext, buf, line); + } + } + } + // according to syslog docu: calling closelog is not necessary and calling openlog multiple times is safe + // We use openlog to set the default syslog settings so that it's possible to allow switching syslog on and off + openlog(syslog_ident, LOG_NDELAY | LOG_PID, LOG_DAEMON); + cs_log(">> OSCam << cardserver %s, version " CS_VERSION "@" CS_GIT_COMMIT " (" CS_TARGET ")", starttext); + + return (fp <= (FILE *)0); +} + +#if defined(WEBIF) || defined(MODULE_MONITOR) + +static uint64_t counter = 0; +LLIST *log_history = NULL; + +/* + This function allows to reinit the in-memory loghistory with a new size. +*/ +void cs_reinit_loghist(uint32_t size) +{ + cfg.loghistorylines = size; +} +#endif + +static struct timeb log_ts; + +static uint8_t get_log_header(char *txt, int32_t txt_size, uint8_t* hdr_logcount_offset, + uint8_t* hdr_date_offset, uint8_t* hdr_time_offset, uint8_t* hdr_info_offset) +{ + struct s_client *cl = cur_client(); + struct tm lt; + int32_t tmp; + + cs_ftime(&log_ts); + time_t walltime = log_ts.time; + localtime_r(&walltime, <); + + tmp = snprintf(txt, txt_size, "[LOG000]%04d/%02d/%02d %02d:%02d:%02d %08X %c ", + lt.tm_year + 1900, + lt.tm_mon + 1, + lt.tm_mday, + lt.tm_hour, + lt.tm_min, + lt.tm_sec, + cl ? cl->tid : 0, + cl ? cl->typ : ' ' + ); + + if(tmp == 39) + { + if(hdr_logcount_offset != NULL) + { + // depends on snprintf(...) format + (*hdr_logcount_offset) = 4; + } + + if(hdr_date_offset != NULL) + { + // depends on snprintf(...) format + (*hdr_date_offset) = *hdr_logcount_offset + 4; + } + + if(hdr_time_offset != NULL) + { + // depends on snprintf(...) format + (*hdr_time_offset) = *hdr_date_offset + 11; + } + + if(hdr_info_offset != NULL) + { + // depends on snprintf(...) format + (*hdr_info_offset) = *hdr_time_offset + 9; + } + + return (uint8_t)tmp; + } + + if(hdr_logcount_offset != NULL) + { + (*hdr_logcount_offset) = 0; + } + + if(hdr_date_offset != NULL) + { + (*hdr_date_offset) = 0; + } + + if(hdr_time_offset != NULL) + { + (*hdr_time_offset) = 0; + } + + if(hdr_info_offset != NULL) + { + (*hdr_info_offset) = 0; + } + + return 0; +} + +static void write_to_log(char *txt, struct s_log *log, int8_t do_flush) +{ + if(logStarted == 0) + { return; } + + (void)log; // Prevent warning when WEBIF, MODULE_MONITOR and CS_ANTICASC are disabled + + // anticascading messages go to their own log + if (!anticasc_logging(txt + log->header_date_offset)) + { + if(cfg.logtosyslog) + { + syslog(LOG_INFO, "%s", txt + log->header_info_offset); + } + + if (cfg.sysloghost != NULL && syslog_socket != -1) + { + char tmp[128 + LOG_BUF_SIZE]; + static char hostname[64]; + static uint8_t have_hostname = 0; + time_t walltime; + struct tm lt; + char timebuf[32]; + + if(!have_hostname) + { + if(gethostname(hostname, 64) != 0) + { + cs_strncpy(hostname, "unknown", 64); + } + + have_hostname = 1; + } + + walltime = cs_time(); + localtime_r(&walltime, <); + + if(strftime(timebuf, 32, "%b %d %H:%M:%S", <) == 0) + { + cs_strncpy(timebuf, "unknown", 32); + } + + snprintf(tmp, sizeof(tmp), "%s %s oscam[%u]: %s", timebuf, hostname, getpid(), txt + log->header_info_offset); + sendto(syslog_socket, tmp, cs_strlen(tmp), 0, (struct sockaddr*) &syslog_addr, sizeof(syslog_addr)); + } + } + + if (!cs_strncat(txt, "\n", LOG_BUF_SIZE)) { + cs_log("FIXME!"); + } + cs_write_log(txt, do_flush, log->header_date_offset, log->header_time_offset); + +#if defined(WEBIF) || defined(MODULE_MONITOR) + if(!exit_oscam && cfg.loghistorylines && log_history) + { + struct s_log_history *hist; + + while((uint32_t)ll_count(log_history) >= cfg.loghistorylines) + { + hist = ll_remove_first(log_history); + if(hist) + { + add_garbage(hist->txt); + add_garbage(hist); + hist = NULL; + } + } + + if(cs_malloc(&hist, sizeof(struct s_log_history))) + { + int32_t target_len = cs_strlen(log->cl_text) + cs_strlen(txt+log->header_date_offset) + 1; + + if(cs_malloc(&hist->txt, sizeof(char) * (target_len + 1))) + { + hist->counter = counter++; + snprintf(hist->txt, target_len + 1, "%s\t%s", log->cl_text, txt + log->header_date_offset); + + ll_append(log_history, hist); + } + else + { + NULLFREE(hist); + } + } + } +#endif + +#if defined(MODULE_MONITOR) + char sbuf[16]; + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if((cl->typ == 'm') && (cl->monlvl > 0) && cl->log) // this variable is only initialized for cl->typ = 'm' + { + if(cl->monlvl < 2) + { + if(log->cl_typ != 'c' && log->cl_typ != 'm') + { continue; } + + if(log->cl_usr && cl->account && strcmp(log->cl_usr, cl->account->usr)) + { continue; } + } + + if(log->header_len > 0) + { + snprintf(sbuf, sizeof(sbuf), "%03d", cl->logcounter); + cl->logcounter = (cl->logcounter + 1) % 1000; + memcpy(txt + log->header_logcount_offset, sbuf, 3); + monitor_send_idx(cl, txt); + } + else + { + char tmp_log[8+LOG_BUF_SIZE]; + snprintf(tmp_log, sizeof(tmp_log), "[LOG%03d]%s", cl->logcounter, txt); + cl->logcounter = (cl->logcounter + 1) % 1000; + monitor_send_idx(cl, tmp_log); + } + } + } +#endif +} + +static void write_to_log_int(char *txt, uint8_t header_len, uint8_t hdr_logcount_offset, uint8_t hdr_date_offset, uint8_t hdr_time_offset, uint8_t hdr_info_offset) +{ +#if !defined(WEBIF) && !defined(MODULE_MONITOR) + if(cfg.disablelog) { return; } +#endif + char *newtxt = cs_strdup(txt); + if(!newtxt) + { return; } + + struct s_log *log; + if(!cs_malloc(&log, sizeof(struct s_log))) + { + NULLFREE(newtxt); + return; + } + + log->txt = newtxt; + log->header_len = header_len; + log->header_logcount_offset = hdr_logcount_offset; + log->header_date_offset = hdr_date_offset; + log->header_time_offset = hdr_time_offset; + log->header_info_offset = hdr_info_offset; + log->direct_log = 0; + struct s_client *cl = cur_client(); + log->cl_usr = ""; + + if(!cl) + { + log->cl_text = "undef"; + log->cl_typ = ' '; + } + else + { + switch(cl->typ) + { + case 'c': + case 'm': + if(cl->account) + { + log->cl_text = cl->account->usr; + log->cl_usr = cl->account->usr; + } + else + { + log->cl_text = ""; + } + break; + + case 'p': + case 'r': + log->cl_text = cl->reader ? cl->reader->label : ""; + break; + + default: + log->cl_text = "server"; + break; + } + log->cl_typ = cl->typ; + } + + if(exit_oscam == 1 || cfg.disablelog) // Exit or log disabled. if disabled, just display on webif/monitor + { + char buf[LOG_BUF_SIZE]; + cs_strncpy(buf, log->txt, LOG_BUF_SIZE); + write_to_log(buf, log, 1); + NULLFREE(log->txt); + NULLFREE(log); + } + else + { log_list_add(log); } +} + +static pthread_mutex_t log_mutex; +static char log_txt[LOG_BUF_SIZE]; +static char dupl[LOG_BUF_SIZE / 4]; +static char last_log_txt[LOG_BUF_SIZE]; +static struct timeb last_log_ts; +static unsigned int last_log_duplicates; + +static void __cs_log_check_duplicates(uint8_t hdr_len, uint8_t hdr_logcount_offset, uint8_t hdr_date_offset, uint8_t hdr_time_offset, uint8_t hdr_info_offset) +{ + bool repeated_line = strcmp(last_log_txt, log_txt + hdr_len) == 0; + if (last_log_duplicates > 0) + { + if (!cs_valid_time(&last_log_ts)) // Must be initialized once + last_log_ts = log_ts; + + // Report duplicated lines when the new log line is different + // than the old or 60 seconds have passed. + int64_t gone = comp_timeb(&log_ts, &last_log_ts); + + if (!repeated_line || gone >= 60 * 1000) + { + uint8_t dupl_hdr_logcount_offset = 0, dupl_hdr_date_offset = 0, dupl_hdr_time_offset = 0, dupl_hdr_info_offset = 0; + uint8_t dupl_header_len = get_log_header(dupl, sizeof(dupl), &dupl_hdr_logcount_offset, &dupl_hdr_date_offset, &dupl_hdr_time_offset, &dupl_hdr_info_offset); + snprintf(dupl + dupl_header_len - 1, sizeof(dupl) - dupl_header_len, " (-) -- Skipped %u duplicated log lines --", last_log_duplicates); + write_to_log_int(dupl, dupl_header_len, dupl_hdr_logcount_offset, dupl_hdr_date_offset, dupl_hdr_time_offset, dupl_hdr_info_offset); + last_log_duplicates = 0; + last_log_ts = log_ts; + } + } + + if (!repeated_line) + { + memcpy(last_log_txt, log_txt + hdr_len, LOG_BUF_SIZE - hdr_len); + write_to_log_int(log_txt, hdr_len, hdr_logcount_offset, hdr_date_offset, hdr_time_offset, hdr_info_offset); + } + else + { + last_log_duplicates++; + } +} + +#define __init_log_prefix(fmt) \ + uint8_t hdr_logcount_offset = 0, hdr_date_offset = 0, hdr_time_offset = 0, hdr_info_offset = 0; \ + uint8_t hdr_len = get_log_header(log_txt, sizeof(log_txt), &hdr_logcount_offset, &hdr_date_offset, &hdr_time_offset, &hdr_info_offset); \ + int32_t log_prefix_len = 0; \ + do { \ + if (log_prefix) { \ + char _lp[16]; \ + snprintf(_lp, sizeof(_lp), "(%s)", log_prefix); \ + log_prefix_len = snprintf(log_txt + hdr_len, sizeof(log_txt) - hdr_len, fmt, _lp); \ + } \ + } while(0) + +#define __do_log() \ + do { \ + va_list params; \ + va_start(params, fmt); \ + __init_log_prefix("%10s "); \ + vsnprintf(log_txt + hdr_len + log_prefix_len, sizeof(log_txt) - (hdr_len + log_prefix_len), fmt, params); \ + va_end(params); \ + if (cfg.logduplicatelines) \ + { \ + memcpy(last_log_txt, log_txt + hdr_len, LOG_BUF_SIZE - hdr_len); \ + write_to_log_int(log_txt, hdr_len, hdr_logcount_offset, hdr_date_offset, hdr_time_offset, hdr_info_offset); \ + } else { \ + __cs_log_check_duplicates(hdr_len, hdr_logcount_offset, hdr_date_offset, hdr_time_offset, hdr_info_offset); \ + } \ + } while(0) + +void cs_log_txt(const char *log_prefix, const char *fmt, ...) +{ + if(logStarted == 0) + { return; } + + SAFE_MUTEX_LOCK_NOLOG(&log_mutex); + __do_log(); + SAFE_MUTEX_UNLOCK_NOLOG(&log_mutex); +} + +void cs_log_hex(const char *log_prefix, const uint8_t *buf, int32_t n, const char *fmt, ...) +{ + if(logStarted == 0) + { return; } + + SAFE_MUTEX_LOCK_NOLOG(&log_mutex); + __do_log(); + if(buf) + { + int32_t i; + __init_log_prefix("%10s "); + for(i = 0; i < n; i += 16) + { + cs_hexdump(1, buf + i, (n - i > 16) ? 16 : n - i, log_txt + hdr_len + log_prefix_len, sizeof(log_txt) - (hdr_len + log_prefix_len)); + write_to_log_int(log_txt, hdr_len, hdr_logcount_offset, hdr_date_offset, hdr_time_offset, hdr_info_offset); + } + } + SAFE_MUTEX_UNLOCK_NOLOG(&log_mutex); +} + +static void cs_close_log(void) +{ + log_list_flush(); + if(fp) + { + fclose(fp); + fp = (FILE *)0; + } +} + +int32_t cs_init_statistics(void) +{ + if((!fps) && (cfg.usrfile != NULL)) + { + if((fps = fopen(cfg.usrfile, "a+")) <= (FILE *)0) + { + fps = (FILE *)0; + cs_log("couldn't open statistics file: %s", cfg.usrfile); + } + } + return (fps <= (FILE *)0); +} + +void cs_statistics(struct s_client *client) +{ + if(!cfg.disableuserfile) + { + struct tm lt; + char buf[LOG_BUF_SIZE]; + + float cwps; + + time_t walltime = cs_time(); + localtime_r(&walltime, <); + if(client->cwfound + client->cwnot > 0) + { + cwps = client->last - client->login; + cwps /= client->cwfound + client->cwnot; + } + else + { cwps = 0; } + + char channame[CS_SERVICENAME_SIZE]; + get_servicename(client, client->last_srvid, client->last_provid, client->last_caid, channame, sizeof(channame)); + + int32_t lsec; + if((client->last_caid == NO_CAID_VALUE) && (client->last_srvid == NO_SRVID_VALUE)) + { lsec = client->last - client->login; } //client leave calc total duration + else + { lsec = client->last - client->lastswitch; } + + int32_t secs = 0, fullmins = 0, mins = 0, fullhours = 0; + + if((lsec > 0) && (lsec < 1000000)) + { + secs = lsec % 60; + if(lsec > 60) + { + fullmins = lsec / 60; + mins = fullmins % 60; + if(fullmins > 60) + { + fullhours = fullmins / 60; + } + } + } + + /* statistics entry start with 's' to filter it out on other end of pipe + * so we can use the same Pipe as Log + */ + snprintf(buf, sizeof(buf), "s%02d.%02d.%02d %02d:%02d:%02d %3.1f %s %s %d %d %d %d %d %d %d %" PRId64 " %" PRId64 " %02d:%02d:%02d %s %04X@%06X:%04X %s\n", + lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100, + lt.tm_hour, lt.tm_min, lt.tm_sec, cwps, + client->account->usr, + cs_inet_ntoa(client->ip), + client->port, + client->cwfound, + client->cwcache, + client->cwnot, + client->cwignored, + client->cwtout, + client->cwtun, + (int64_t)client->login, + (int64_t)client->last, + fullhours, mins, secs, + get_module(client)->desc, + client->last_caid, + client->last_provid, + client->last_srvid, + channame); + + cs_write_log_int(buf); + } +} + +void log_list_thread(void) +{ + char buf[LOG_BUF_SIZE]; + log_running = 1; + set_thread_name(__func__); + do + { + log_list_queued = 0; + LL_ITER it = ll_iter_create(log_list); + struct s_log *log; + while((log = ll_iter_next_remove(&it))) + { + int8_t do_flush = ll_count(log_list) == 0; // flush on writing last element + + cs_strncpy(buf, log->txt, LOG_BUF_SIZE); + if(log->direct_log) + { cs_write_log(buf, do_flush, log->header_date_offset, log->header_time_offset); } + else + { write_to_log(buf, log, do_flush); } + NULLFREE(log->txt); + NULLFREE(log); + } + if(!log_list_queued) // The list is empty, sleep until new data comes in and we are woken up + sleepms_on_cond(__func__, &log_thread_sleep_cond_mutex, &log_thread_sleep_cond, 60 * 1000); + } + while(log_running); + ll_destroy(&log_list); +} + +static void init_syslog_socket(void) +{ + if(cfg.sysloghost != NULL && syslog_socket == -1) + { + IN_ADDR_T in_addr; + + if ((syslog_socket = socket(DEFAULT_AF, SOCK_DGRAM, IPPROTO_UDP)) == -1) + { + perror("Socket create error!"); + } + + memset((char *) &syslog_addr, 0, sizeof(syslog_addr)); + SIN_GET_FAMILY(syslog_addr) = DEFAULT_AF; + SIN_GET_PORT(syslog_addr) = htons(cfg.syslogport); + cs_resolve(cfg.sysloghost, &in_addr, NULL, NULL); + SIN_GET_ADDR(syslog_addr) = in_addr; + } +} + +int32_t cs_init_log(void) +{ + if(logStarted == 0) + { + init_syslog_socket(); + SAFE_MUTEX_INIT_NOLOG(&log_mutex, NULL); + + cs_pthread_cond_init_nolog(__func__, &log_thread_sleep_cond_mutex, &log_thread_sleep_cond); + +#if defined(WEBIF) || defined(MODULE_MONITOR) + log_history = ll_create("log history"); +#endif + + log_list = ll_create(LOG_LIST); + + int32_t ret = start_thread_nolog("logging", (void *)&log_list_thread, NULL, &log_thread, 0, 1); + if(ret) + { + cs_exit(1); + } + + logStarted = 1; + } + + int32_t rc = 0; + if(!cfg.disablelog) { rc = cs_open_logfiles(); } + logStarted = 1; + + if(cfg.initial_debuglevel > 0) + { + cs_dblevel = cfg.initial_debuglevel; + cs_log("debug_level=%d", cs_dblevel); + } + + return rc; +} + +void cs_disable_log(int8_t disabled) +{ + if(cfg.disablelog != disabled) + { + if(disabled && logStarted) + { + cs_log("Stopping log..."); + log_list_flush(); + } + + cfg.disablelog = disabled; + if(disabled) + { + if(logStarted) + { + if(syslog_socket != -1) + { + close(syslog_socket); + syslog_socket = -1; + } + + cs_sleepms(20); + cs_close_log(); + } + } + else + { + init_syslog_socket(); + cs_open_logfiles(); + } + } +} + +void log_free(void) +{ + if(syslog_socket != -1) + { + close(syslog_socket); + syslog_socket = -1; + } + + cs_close_log(); + log_running = 0; + SAFE_COND_SIGNAL_NOLOG(&log_thread_sleep_cond); + SAFE_THREAD_JOIN_NOLOG(log_thread, NULL); +} diff --git a/oscam-log.h b/oscam-log.h new file mode 100644 index 0000000..8e0646d --- /dev/null +++ b/oscam-log.h @@ -0,0 +1,49 @@ +#ifndef OSCAM_LOG_H_ +#define OSCAM_LOG_H_ + +#ifndef MODULE_LOG_PREFIX +#define MODULE_LOG_PREFIX NULL +#endif + +int32_t cs_init_log(void); +void cs_reopen_log(void); +int32_t cs_open_logfiles(void); +void cs_disable_log(int8_t disabled); +void cs_reinit_loghist(uint32_t size); + +void cs_log_txt(const char *log_prefix, const char *fmt, ...) __attribute__((format(printf, 2, 3))); +void cs_log_hex(const char *log_prefix, const uint8_t *buf, int32_t n, const char *fmt, ...) __attribute__((format(printf, 4, 5))); + +#define cs_log(fmt, params...) cs_log_txt(MODULE_LOG_PREFIX, fmt, ##params) +#define cs_log_dump(buf, n, fmt, params...) cs_log_hex(MODULE_LOG_PREFIX, buf, n, fmt, ##params) + +#define cs_log_dbg(mask, fmt, params...) do { if (config_enabled(WITH_DEBUG) && ((mask) & cs_dblevel)) cs_log_txt(MODULE_LOG_PREFIX, fmt, ##params); } while(0) +#define cs_log_dump_dbg(mask, buf, n, fmt, params...) do { if (config_enabled(WITH_DEBUG) && ((mask) & cs_dblevel)) cs_log_hex(MODULE_LOG_PREFIX, buf , n, fmt, ##params); } while(0) + +int32_t cs_init_statistics(void); +void cs_statistics(struct s_client *client); + +void log_free(void); + +// Compatability with older function names. If your code uses these +// it must migrate to the new names using find + replace +// *** DO NOT USE OLD NAMES NEW CODE! *** +#define cs_debug_mask cs_log_dbg +#define rdr_debug_mask rdr_log_dbg +#define rdr_debug_mask_sensitive rdr_log_dbg_sensitive +#define cs_ddump_mask cs_log_dump_dbg +#define rdr_ddump_mask rdr_log_dump_dbg + +#if defined(WEBIF) || defined(MODULE_MONITOR) + +extern LLIST *log_history; + +struct s_log_history +{ + char *txt; + uint64_t counter; +}; + +#endif + +#endif diff --git a/oscam-net.c b/oscam-net.c new file mode 100644 index 0000000..21a0efe --- /dev/null +++ b/oscam-net.c @@ -0,0 +1,926 @@ +#define MODULE_LOG_PREFIX "net" + +#include "globals.h" +#include "oscam-client.h" +#include "oscam-failban.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" + +extern CS_MUTEX_LOCK gethostbyname_lock; +extern int32_t exit_oscam; + +#ifndef IPV6SUPPORT +static int32_t inet_byteorder = 0; + +static in_addr_t cs_inet_order(in_addr_t n) +{ + if(!inet_byteorder) + { inet_byteorder = (inet_addr("1.2.3.4") + 1 == inet_addr("1.2.3.5")) ? 1 : 2; } + switch(inet_byteorder) + { + case 1: + break; + case 2: + n = ((n & 0xff000000) >> 24) | + ((n & 0x00ff0000) >> 8) | + ((n & 0x0000ff00) << 8) | + ((n & 0x000000ff) << 24); + break; + } + return n; +} +#endif + +char *cs_inet_ntoa(IN_ADDR_T addr) +{ +#ifdef IPV6SUPPORT + static char buff[INET6_ADDRSTRLEN]; + if(IN6_IS_ADDR_V4MAPPED(&addr) || IN6_IS_ADDR_V4COMPAT(&addr)) + { + snprintf(buff, sizeof(buff), "%d.%d.%d.%d", + addr.s6_addr[12], addr.s6_addr[13], addr.s6_addr[14], addr.s6_addr[15]); + } + else + { + inet_ntop(AF_INET6, &(addr.s6_addr), buff, INET6_ADDRSTRLEN); + } + return buff; +#else + struct in_addr in; + in.s_addr = addr; + return (char *)inet_ntoa(in); +#endif +} + +void cs_inet_addr(char *txt, IN_ADDR_T *out) +{ +#ifdef IPV6SUPPORT + char buff[INET6_ADDRSTRLEN]; + //trying as IPv6 address + if(inet_pton(AF_INET6, txt, out->s6_addr) == 0) + { + //now trying as mapped IPv4 + snprintf(buff, sizeof(buff), "::ffff:%s", txt); + inet_pton(AF_INET6, buff, out->s6_addr); + } +#else + *out = inet_addr(txt); +#endif +} + +void cs_resolve(const char *hostname, IN_ADDR_T *ip, struct SOCKADDR *sock, socklen_t *sa_len) +{ +#ifdef IPV6SUPPORT + cs_getIPv6fromHost(hostname, ip, sock, sa_len, 0); +#else + *ip = cs_getIPfromHost(hostname); + if(sa_len) + { *sa_len = sizeof(*sock); } +#endif +} + +#ifdef IPV6SUPPORT +void cs_resolve_v4(const char *hostname, IN_ADDR_T *ip, struct SOCKADDR *sock, socklen_t *sa_len) +{ + cs_getIPv6fromHost(hostname, ip, sock, sa_len, 1); +} +#endif + +#ifdef IPV6SUPPORT +int32_t cs_in6addr_equal(struct in6_addr *a1, struct in6_addr *a2) +{ + return memcmp(a1, a2, 16) == 0; +} + +int32_t cs_in6addr_lt(struct in6_addr *a, struct in6_addr *b) +{ + int i; + for(i = 0; i < 4; i++) + { + if((i == 2) && ((IN6_IS_ADDR_V4COMPAT(a) && IN6_IS_ADDR_V4MAPPED(b)) || + (IN6_IS_ADDR_V4COMPAT(b) && IN6_IS_ADDR_V4MAPPED(a)))) + { continue; } // skip comparing this part + + if(a->s6_addr32[i] != b->s6_addr32[i]) + { return ntohl(a->s6_addr32[i]) < ntohl(b->s6_addr32[i]); } + } + + return 0; +} + +int32_t cs_in6addr_isnull(struct in6_addr *addr) +{ + int i; + for(i = 0; i < 16; i++) + if(addr->s6_addr[i]) + { return 0; } + return 1; +} + +void cs_in6addr_copy(struct in6_addr *dst, struct in6_addr *src) +{ + memcpy(dst, src, 16); +} + +void cs_in6addr_ipv4map(struct in6_addr *dst, in_addr_t src) +{ + memset(dst->s6_addr, 0, 16); + dst->s6_addr[10] = 0xff; + dst->s6_addr[11] = 0xff; + memcpy(dst->s6_addr + 12, &src, 4); +} +#endif + +IN_ADDR_T get_null_ip(void) +{ + IN_ADDR_T ip; +#ifdef IPV6SUPPORT + cs_inet_addr("::", &ip); +#else + ip = 0; +#endif + return ip; +} + +void set_null_ip(IN_ADDR_T *ip) +{ +#ifdef IPV6SUPPORT + cs_inet_addr("::", ip); +#else + *ip = 0; +#endif +} + +void set_localhost_ip(IN_ADDR_T *ip) +{ +#ifdef IPV6SUPPORT + cs_inet_addr("::1", ip); +#else + cs_inet_addr("127.0.0.1", ip); +#endif +} + +int32_t check_ip(struct s_ip *ip, IN_ADDR_T n) +{ + struct s_ip *p_ip; + int32_t ok = 0; +#ifdef IPV6SUPPORT + for(p_ip = ip; (p_ip) && (!ok); p_ip = p_ip->next) + { + ok = cs_in6addr_lt(&n, &p_ip->ip[0]); + ok |= cs_in6addr_lt(&p_ip->ip[1], &n); + ok = !ok; + } +#else + for(p_ip = ip; (p_ip) && (!ok); p_ip = p_ip->next) + { ok = ((cs_inet_order(n) >= cs_inet_order(p_ip->ip[0])) && (cs_inet_order(n) <= cs_inet_order(p_ip->ip[1]))); } +#endif + return ok; +} + +/* Returns the ip from the given hostname. If gethostbyname is configured in the config file, a lock + will be held until the ip has been resolved. */ +uint32_t cs_getIPfromHost(const char *hostname) +{ + uint32_t result = 0; + // Resolve with gethostbyname: + if(cfg.resolve_gethostbyname) + { + cs_writelock(__func__, &gethostbyname_lock); + struct hostent *rht = gethostbyname(hostname); + if(!rht) + { cs_log("can't resolve %s", hostname); } + else + { result = ((struct in_addr *)rht->h_addr)->s_addr; } + cs_writeunlock(__func__, &gethostbyname_lock); + } + else // Resolve with getaddrinfo: + { + struct addrinfo hints, *res = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_INET; + hints.ai_protocol = IPPROTO_TCP; + + int32_t err = getaddrinfo(hostname, NULL, &hints, &res); + if(err != 0 || !res || !res->ai_addr) + { + cs_log("can't resolve %s, error: %s", hostname, err ? gai_strerror(err) : "unknown"); + } + else + { + result = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr; + } + if(res) { freeaddrinfo(res); } + } + return result; +} + +#ifdef IPV6SUPPORT +void cs_getIPv6fromHost(const char *hostname, struct in6_addr *addr, struct sockaddr_storage *sa, socklen_t *sa_len, uint8_t forceV4) +{ + uint32_t ipv4addr = 0; + uint8_t ipv6_found = 0; + struct addrinfo hints, *res = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + + if (forceV4) + { + hints.ai_family = AF_INET; + } + else + { + hints.ai_family = AF_UNSPEC; + } + + hints.ai_protocol = IPPROTO_TCP; + int32_t err = getaddrinfo(hostname, NULL, &hints, &res); + if(err != 0 || !res || !res->ai_addr) + { + cs_log("can't resolve %s, error: %s", hostname, err ? gai_strerror(err) : "unknown"); + } + else + { + while (res) + { + if ((!forceV4 && !ipv6_found) || (forceV4)) + { + ipv4addr = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr; + if(res->ai_family == AF_INET) + { cs_in6addr_ipv4map(addr, ipv4addr); } + else + { + IP_ASSIGN(*addr, SIN_GET_ADDR(*res->ai_addr)); + ipv6_found = 1; + } + if(sa) + { memcpy(sa, res->ai_addr, res->ai_addrlen); } + if(sa_len) + { *sa_len = res->ai_addrlen; } + + res = res->ai_next; + } + else + { + res = res->ai_next; + } + } + } + + if(res) + { freeaddrinfo(res); } +} +#endif + +int set_socket_priority(int fd, int priority) +{ +#if defined(IP_TOS) || defined(SO_PRIORITY) + if (priority == 0) { return -1; } // default value, therefore leave it untouched (IPP=0; DSCP=CS0) + + int ret = 0; + int cos __attribute__ ((unused)) = 0; + int tos __attribute__ ((unused)) = 0x00; + + switch(priority) + { + case 1: // IPP=1; DSCP=CS1 + cos = 1; + tos = 0x20; + break; + + case 2: // IPP=1; DSCP=AF11 + cos = 1; + tos = 0x28; + break; + + case 3: // IPP=1; DSCP=AF12 + cos = 1; + tos = 0x30; + break; + + case 4: // IPP=1; DSCP=AF13 + cos = 1; + tos = 0x38; + break; + + case 5: // IPP=2; DSCP=CS2 + cos = 2; + tos = 0x40; + break; + + case 6: // IPP=2; DSCP=AF21 + cos = 2; + tos = 0x48; + break; + + case 7: // IPP=2; DSCP=AF22 + cos = 2; + tos = 0x50; + break; + + case 8: // IPP=2; DSCP=AF23 + cos = 2; + tos = 0x58; + break; + + case 9: // IPP=3; DSCP=CS3 + cos = 3; + tos = 0x60; + break; + + case 10: // IPP=3; DSCP=AF31 + cos = 3; + tos = 0x68; + break; + + case 11: // IPP=3; DSCP=AF32 + cos = 3; + tos = 0x70; + break; + + case 12: // IPP=3; DSCP=AF33 + cos = 3; + tos = 0x78; + break; + + case 13: // IPP=4; DSCP=CS4 + cos = 4; + tos = 0x80; + break; + + case 14: // IPP=4; DSCP=AF41 + cos = 4; + tos = 0x88; + break; + + case 15: // IPP=4; DSCP=AF42 + cos = 4; + tos = 0x90; + break; + + case 16: // IPP=4; DSCP=AF43 + cos = 4; + tos = 0x98; + break; + + case 17: // IPP=5; DSCP=CS5 + cos = 5; + tos = 0xa0; + break; + + case 18: // IPP=5; DSCP=EF + cos = 5; + tos = 0xb8; + break; + + case 19: // IPP=6; DSCP=CS6 + cos = 6; + tos = 0xc0; + break; + + case 20: // IPP=7; DSCP=CS7 + cos = 7; + tos = 0xe0; + break; + } + +#ifdef IP_TOS + if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void *)&tos, sizeof(tos)) < 0) + { + cs_log("Setting IP_TOS failed, errno=%d, %s", errno, strerror(errno)); + } + else + { + ret = ret ^ 0x01; + } + +#if defined(IPV6SUPPORT) && defined(IPV6_TCLASS) + int32_t family = 0; + socklen_t length = sizeof(int); +#ifndef SO_DOMAIN +#define SO_DOMAIN 39 +#endif + if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &family, &length) <0) + { + cs_log("getsockopt err - set_socket_priority()"); + } + else + { + if (family == AF_INET6) + { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, (void *)&tos, sizeof(tos)) < 0) + { + cs_log("Setting IPV6_TCLASS failed, errno=%d, %s", errno, strerror(errno)); + } + else + { + ret = ret ^ 0x02; + } + } + } +#endif +#endif + +#ifdef SO_PRIORITY + if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void *)&cos, sizeof(cos)) < 0) + { + cs_log("Setting SO_PRIORITY failed, errno=%d, %s", errno, strerror(errno)); + } + else + { + ret = ret ^ 0x04; + } +#endif + + return ret; +#else + (void)fd; + (void)priority; + return -1; +#endif +} + +void setTCPTimeouts(int32_t sock) +{ + int32_t flag = 1; + // this is not only for a real keepalive but also to detect closed connections so it should not be configurable + if(setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag)) && errno != EBADF) + { + cs_log("Setting SO_KEEPALIVE failed, errno=%d, %s", errno, strerror(errno)); + } + +#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPCNT) && defined(TCP_KEEPINTVL) + flag = 10; + + if(setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &flag, sizeof(flag)) && errno != EBADF) // send first keepalive packet after 10 seconds of last package received (keepalive packets included) + { + cs_log("Setting TCP_KEEPIDLE failed, errno=%d, %s", errno, strerror(errno)); + } + + flag = 3; + + if(setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &flag, sizeof(flag)) && errno != EBADF) // send up to 3 keepalive packets out (in interval TCP_KEEPINTVL), then disconnect if no response + { + cs_log("Setting TCP_KEEPCNT failed, errno=%d, %s", errno, strerror(errno)); + } + + flag = 1; + + if(setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &flag, sizeof(flag)) && errno != EBADF) + { + ; // send a keepalive packet out every second (until answer has been received or TCP_KEEPCNT has been reached) + cs_log("Setting TCP_KEEPINTVL failed, errno=%d, %s", errno, strerror(errno)); + } +#endif + + struct timeval tv; + tv.tv_sec = 60; + tv.tv_usec = 0; + + if(setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(struct timeval)) && errno != EBADF) + { + ; + cs_log("Setting SO_SNDTIMEO failed, errno=%d, %s", errno, strerror(errno)); + } + + tv.tv_sec = 600; + tv.tv_usec = 0; + + if(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)) && errno != EBADF) + { + ; + cs_log("Setting SO_RCVTIMEO failed, errno=%d, %s", errno, strerror(errno)); + } + +#if defined(TCP_USER_TIMEOUT) + int timeout = 60000; // RFC 5482 user timeout in milliseconds + setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, (char *) &timeout, sizeof(timeout)); +#endif +} + +int set_nonblock(int32_t fd, bool nonblock) +{ + int32_t flags = fcntl(fd, F_GETFL); + if (flags == -1) + return -1; + if (nonblock) + flags |= O_NONBLOCK; + else + flags &= (~O_NONBLOCK); + return fcntl(fd, F_SETFL, flags); +} + +int8_t check_fd_for_data(int32_t fd) +{ + int32_t rc; + struct pollfd pfd[1]; + + pfd[0].fd = fd; + pfd[0].events = (POLLIN | POLLPRI); + rc = poll(pfd, 1, 0); + + if(rc == -1) + { cs_log("check_fd_for_data(fd=%d) failed: (errno=%d %s)", fd, errno, strerror(errno)); } + + if(rc == -1 || rc == 0) + { return rc; } + + if(pfd[0].revents & (POLLHUP | POLLNVAL | POLLERR)) + { return -2; } + + return 1; +} + +int32_t recv_from_udpipe(uint8_t *buf) +{ + uint16_t n; + if(buf[0] != 'U') + { + cs_log("INTERNAL PIPE-ERROR"); + cs_exit(1); + } + memcpy(&n, buf + 1, 2); + memmove(buf, buf + 3, n); + return n; +} + +int32_t process_input(uint8_t *buf, int32_t buflen, int32_t timeout) +{ + int32_t rc, i, pfdcount; + int64_t polltime, timeoutms; + struct pollfd pfd[2]; + struct s_client *cl = cur_client(); + + struct timeb starttime; + struct timeb currenttime; + timeoutms = 1000 * timeout; + cs_ftime(&starttime); + polltime = timeoutms; // initial polltime = timeoutms + while(1) + { + pfdcount = 0; + if(cl->pfd) + { + pfd[pfdcount].fd = cl->pfd; + pfd[pfdcount++].events = POLLIN | POLLPRI; + } + int32_t p_rc = poll(pfd, pfdcount, polltime); + + cs_ftime(¤ttime); + int64_t gone = comp_timeb(¤ttime, &starttime); + polltime = timeoutms - gone; // calculate polltime left + if(polltime < 0) + { + polltime = 0; + } + if(p_rc < 0) + { + if(errno == EINTR) + { continue; } + else + { return 0; } + } + + if((p_rc == 0) && (timeout != 0) && (gone >= timeoutms)) // client maxidle reached? timeout = 0, idle disconnect disabled + { + rc = -9; + break; + } + + for(i = 0; i < pfdcount && p_rc > 0; i++) + { + if(pfd[i].revents & POLLHUP) // POLLHUP is only valid in revents so it doesn't need to be set above in events + { + return 0; + } + + if(!(pfd[i].revents & (POLLIN | POLLPRI))) + { continue; } + + if(pfd[i].fd == cl->pfd) + { return get_module(cl)->recv(cl, buf, buflen); } + } + } + return rc; +} + +static struct s_client *find_client_by_ip(IN_ADDR_T ip, in_port_t port) +{ + struct s_client *cl; + for(cl = first_client; cl; cl = cl->next) + { + if(!cl->kill && IP_EQUAL(cl->ip, ip) && cl->port == port && (cl->typ == 'c' || cl->typ == 'm')) + { + return cl; + } + } + return NULL; +} + +int32_t accept_connection(struct s_module *module, int8_t module_idx, int8_t port_idx) +{ + struct SOCKADDR cad; + int32_t scad = sizeof(cad), n; + struct s_client *cl; + struct s_port *port = &module->ptab.ports[port_idx]; + + memset(&cad, 0, sizeof(struct SOCKADDR)); + + if(module->type == MOD_CONN_UDP) + { + uint8_t *buf; + if(!cs_malloc(&buf, 1024)) + { return -1; } + + if((n = recvfrom(port->fd, buf + 3, 1024 - 3, 0, (struct sockaddr *)&cad, (socklen_t *)&scad)) > 0) + { + uint16_t rl; + cl = find_client_by_ip(SIN_GET_ADDR(cad), ntohs(SIN_GET_PORT(cad))); + rl = n; + buf[0] = 'U'; + memcpy(buf + 1, &rl, 2); + + if(cs_check_violation(SIN_GET_ADDR(cad), port->s_port)) + { + NULLFREE(buf); + return 0; + } + + cs_log_dbg(D_TRACE, "got %d bytes on port %d from ip %s:%d client %s", + n, port->s_port, + cs_inet_ntoa(SIN_GET_ADDR(cad)), SIN_GET_PORT(cad), + username(cl)); + + if(!cl) + { + cl = create_client(SIN_GET_ADDR(cad)); + if(!cl) { return 0; } + + cl->module_idx = module_idx; + cl->port_idx = port_idx; + cl->udp_fd = port->fd; + cl->udp_sa = cad; + cl->udp_sa_len = sizeof(cl->udp_sa); + + cl->port = ntohs(SIN_GET_PORT(cad)); + cl->typ = 'c'; + + add_job(cl, ACTION_CLIENT_INIT, NULL, 0); + } + add_job(cl, ACTION_CLIENT_UDP, buf, n + 3); + } + else + { NULLFREE(buf); } + } + else // TCP + { + int32_t pfd3; + if((pfd3 = accept(port->fd, (struct sockaddr *)&cad, (socklen_t *)&scad)) > 0) + { + + if(cs_check_violation(SIN_GET_ADDR(cad), port->s_port)) + { + close(pfd3); + return 0; + } + + cl = create_client(SIN_GET_ADDR(cad)); + if(cl == NULL) + { + close(pfd3); + return 0; + } + + int32_t flag = 1; + setsockopt(pfd3, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); + setTCPTimeouts(pfd3); + + cl->module_idx = module_idx; + cl->udp_fd = pfd3; + cl->port_idx = port_idx; + + cl->pfd = pfd3; + cl->port = ntohs(SIN_GET_PORT(cad)); + cl->typ = 'c'; + + add_job(cl, ACTION_CLIENT_INIT, NULL, 0); + } + } + return 0; +} + +void set_so_reuseport(int fd) { +#ifdef SO_REUSEPORT + // See: http://stackoverflow.com/questions/3261965/so-reuseport-on-linux + int32_t on = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)); +#else + fd = fd; // Do nothing +#endif +} + +int32_t start_listener(struct s_module *module, struct s_port *port) +{ + int32_t ov = 1, timeout, is_udp, i; + char ptxt[2][45]; + struct SOCKADDR sad; // structure to hold server's address + socklen_t sad_len; + + ptxt[0][0] = ptxt[1][0] = '\0'; + if(!port->s_port) + { + cs_log_dbg(D_TRACE, "%s: disabled", module->desc); + return 0; + } + is_udp = (module->type == MOD_CONN_UDP); + + memset(&sad, 0 , sizeof(sad)); +#ifdef IPV6SUPPORT + SIN_GET_FAMILY(sad) = AF_INET6; + SIN_GET_ADDR(sad) = in6addr_any; + sad_len = sizeof(struct sockaddr_in6); +#else + sad.sin_family = AF_INET; + sad_len = sizeof(struct sockaddr); + + if(!module->s_ip) + { module->s_ip = cfg.srvip; } + + if(module->s_ip) + { + sad.sin_addr.s_addr = module->s_ip; + snprintf(ptxt[0], sizeof(ptxt[0]), ", ip=%s", inet_ntoa(sad.sin_addr)); + } + else + { + sad.sin_addr.s_addr = INADDR_ANY; + } +#endif + + timeout = cfg.bindwait; + port->fd = 0; + + if(port->s_port > 0) // test for illegal value + { + SIN_GET_PORT(sad) = htons((uint16_t)port->s_port); + } + else + { + cs_log("%s: Bad port %d", module->desc, port->s_port); + return 0; + } + + int s_type = (is_udp ? SOCK_DGRAM : SOCK_STREAM); + int s_proto = (is_udp ? IPPROTO_UDP : IPPROTO_TCP); + + if((port->fd = socket(DEFAULT_AF, s_type, s_proto)) < 0) + { + cs_log("%s: Cannot create IPv6 socket (errno=%d: %s)", module->desc, errno, strerror(errno)); +#ifdef IPV6SUPPORT + SIN_GET_FAMILY(sad) = AF_INET; + sad_len = sizeof(struct sockaddr_in); + cs_log("%s: Trying fallback to IPv4", module->desc); + if((port->fd = socket(AF_INET, s_type, s_proto)) < 0) + { + cs_log("%s: Cannot create IPv4 socket (errno=%d: %s)", module->desc, errno, strerror(errno)); + return 0; + } +#else + return 0; +#endif + } +#ifdef IPV6SUPPORT + // azbox toolchain do not have this define +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 26 +#endif + else + { + // set the server socket option to listen on IPv4 and IPv6 simultaneously + int val = 0; + if(setsockopt(port->fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&val, sizeof(val)) < 0) + { + cs_log("%s: setsockopt(IPV6_V6ONLY) failed (errno=%d: %s)", module->desc, errno, strerror(errno)); + } + } +#endif + + ov = 1; + if(setsockopt(port->fd, SOL_SOCKET, SO_REUSEADDR, (void *)&ov, sizeof(ov)) < 0) + { + cs_log("%s: setsockopt failed (errno=%d: %s)", module->desc, errno, strerror(errno)); + close(port->fd); + port->fd = 0; + return 0; + } + + set_so_reuseport(port->fd); + + int prio_ret = set_socket_priority(port->fd, cfg.netprio); + if (prio_ret > -1) { + snprintf(ptxt[1], sizeof(ptxt[1]), ", prio=%d [%s%s%s ]", cfg.netprio, prio_ret&0x04 ? " SO_PRIORITY" : "", prio_ret&0x01 ? " IP_TOS" : "", prio_ret&0x02 ? " IPV6_TCLASS" : ""); + } + + if(!is_udp) + { + int32_t keep_alive = 1; + setsockopt(port->fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keep_alive, sizeof(keep_alive)); + } + + while(timeout-- && !exit_oscam) + { + if(bind(port->fd, (struct sockaddr *)&sad, sad_len) < 0) + { + if(timeout) + { + cs_log("%s: Bind request failed (%s), waiting another %d seconds", + module->desc, strerror(errno), timeout); + cs_sleepms(1000); + } + else + { + cs_log("%s: Bind request failed (%s), giving up", module->desc, strerror(errno)); + close(port->fd); + port->fd = 0; + return 0; + } + } + else + { + timeout = 0; + } + } + + if(!is_udp) + { + if(listen(port->fd, CS_QLEN) < 0) + { + cs_log("%s: Cannot start listen mode (errno=%d: %s)", module->desc, errno, strerror(errno)); + close(port->fd); + port->fd = 0; + return 0; + } + } + + cs_log("%s: initialized (fd=%d, port=%d%s%s)", module->desc, port->fd, port->s_port, ptxt[0], ptxt[1]); + + for(i = 0; port->ncd && i < port->ncd->ncd_ftab.nfilts; i++) + { + int32_t j, pos = 0; + char buf[30 + (8 * port->ncd->ncd_ftab.filts[i].nprids)]; + pos += snprintf(buf, sizeof(buf), "-> CAID: %04X PROVID: ", port->ncd->ncd_ftab.filts[i].caid); + + for(j = 0; j < port->ncd->ncd_ftab.filts[i].nprids; j++) + { pos += snprintf(buf + pos, sizeof(buf) - pos, "%06X, ", port->ncd->ncd_ftab.filts[i].prids[j]); } + + if(pos > 2 && j > 0) + { buf[pos - 2] = '\0'; } + + cs_log("%s", buf); + } + + return port->fd; +} + +#ifdef __CYGWIN__ +/** + * Workaround missing MSG_WAITALL implementation under Cygwin. + */ +ssize_t cygwin_recv(int sock, void *buf, int count, int tflags) +{ + char *bp = buf; + int n = 0; + + if ((n = recv(sock, bp, count, tflags)) < 0) + { + return(n); + } + + if (n < count && (tflags & MSG_WAITALL)) + { + cs_log_dbg(D_TRACE, "Cygwin socket read retry. Got %d expected %d", n, count); + + int n2 = recv(sock, bp + n, count - n, tflags); + if (n2 < 0 || n + n2 != count) + { + cs_log_dbg(D_TRACE, "Cygwin socket read retry failed. Got %d", n2); + if (n2 < 0) + { + return(n2); + } + } + else + { + cs_log_dbg(D_TRACE, "Cygwin socket read retry success. Got %d - Total: %d", n2, n + n2); + } + + n+= n2; + } + + return n; +} +#endif /* __CYGWIN__ */ diff --git a/oscam-net.h b/oscam-net.h new file mode 100644 index 0000000..2b450b4 --- /dev/null +++ b/oscam-net.h @@ -0,0 +1,53 @@ +#ifndef OSCAM_NET_H_ +#define OSCAM_NET_H_ + +#ifdef IPV6SUPPORT +#define GET_IP() *(struct in6_addr *)pthread_getspecific(getip) +#define IP_ISSET(a) !cs_in6addr_isnull(&a) +#define IP_EQUAL(a, b) cs_in6addr_equal(&a, &b) +#define IP_ASSIGN(a, b) cs_in6addr_copy(&a, &b) +#define SIN_GET_ADDR(a) ((struct sockaddr_in6 *)&a)->sin6_addr +#define SIN_GET_PORT(a) ((struct sockaddr_in6 *)&a)->sin6_port +#define SIN_GET_FAMILY(a) ((struct sockaddr_in6 *)&a)->sin6_family +extern int32_t cs_in6addr_equal(struct in6_addr *a1, struct in6_addr *a2); +extern int32_t cs_in6addr_isnull(struct in6_addr *addr); +extern int32_t cs_in6addr_lt(struct in6_addr *a, struct in6_addr *b); +extern void cs_in6addr_copy(struct in6_addr *dst, struct in6_addr *src); +extern void cs_in6addr_ipv4map(struct in6_addr *dst, in_addr_t src); +extern void cs_getIPv6fromHost(const char *hostname, struct in6_addr *addr, struct sockaddr_storage *sa, socklen_t *sa_len, uint8_t forceV4); +#else +#define GET_IP() *(in_addr_t *)pthread_getspecific(getip) +#define IP_ISSET(a) (a) +#define IP_EQUAL(a, b) (a == b) +#define IP_ASSIGN(a, b) (a = b) +#define SIN_GET_ADDR(a) (a.sin_addr.s_addr) +#define SIN_GET_PORT(a) (a.sin_port) +#define SIN_GET_FAMILY(a) (a.sin_family) +#endif + +char *cs_inet_ntoa(IN_ADDR_T addr); +void cs_inet_addr(char *txt, IN_ADDR_T *out); +void cs_resolve(const char *hostname, IN_ADDR_T *ip, struct SOCKADDR *sock, socklen_t *sa_len); +#ifdef IPV6SUPPORT +void cs_resolve_v4(const char *hostname, IN_ADDR_T *ip, struct SOCKADDR *sock, socklen_t *sa_len); +#endif +IN_ADDR_T get_null_ip(void); +void set_null_ip(IN_ADDR_T *ip); +void set_localhost_ip(IN_ADDR_T *ip); +int32_t check_ip(struct s_ip *ip, IN_ADDR_T n); +uint32_t cs_getIPfromHost(const char *hostname); +int set_socket_priority(int fd, int priority); +void setTCPTimeouts(int32_t sock); +int set_nonblock(int32_t fd, bool nonblock); +void set_so_reuseport(int fd); +int8_t check_fd_for_data(int32_t fd); +int32_t recv_from_udpipe(uint8_t *); +int32_t process_input(uint8_t *buf, int32_t buflen, int32_t timeout); +int32_t accept_connection(struct s_module *module, int8_t module_idx, int8_t port_idx); +int32_t start_listener(struct s_module *module, struct s_port *port); + +#ifdef __CYGWIN__ +ssize_t cygwin_recv(int sock, void *buf, int count, int tflags); +#endif + +#endif diff --git a/oscam-reader.c b/oscam-reader.c new file mode 100644 index 0000000..ee10064 --- /dev/null +++ b/oscam-reader.c @@ -0,0 +1,1558 @@ +#define MODULE_LOG_PREFIX "reader" + +#include "globals.h" +#include "module-cccam.h" +#include "module-led.h" +#include "module-stat.h" +#include "module-dvbapi.h" +#include "oscam-cache.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" +#include "reader-common.h" +#include "oscam-config.h" + +extern CS_MUTEX_LOCK system_lock; +extern CS_MUTEX_LOCK ecmcache_lock; +extern struct ecm_request_t *ecmcwcache; +extern const struct s_cardsystem *cardsystems[]; + +const char *RDR_CD_TXT[] = +{ + "cd", "dsr", "cts", "ring", "none", + "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + NULL +}; + +/* Overide ratelimit priority for dvbapi request */ +static int32_t dvbapi_override_prio(struct s_reader *reader, ECM_REQUEST *er, int32_t maxecms, struct timeb *actualtime) +{ + if (!module_dvbapi_enabled() || !is_dvbapi_usr(er->client->account->usr)) + return -1; + + int32_t foundspace = -1; + int64_t gone = 0; + + if (reader->lastdvbapirateoverride.time == 0) // fixup for first run! + gone = comp_timeb(actualtime, &reader->lastdvbapirateoverride); + + if (gone > reader->ratelimittime) + { + int32_t h; + struct timeb minecmtime = *actualtime; + + for (h = 0; h < MAXECMRATELIMIT; h++) + { + gone = comp_timeb(&minecmtime, &reader->rlecmh[h].last); + if (gone > 0) { + minecmtime = reader->rlecmh[h].last; + foundspace = h; + } + } + reader->lastdvbapirateoverride = *actualtime; + + cs_log_dbg(D_CLIENT, "prioritizing DVBAPI user %s over other watching client", + er->client->account->usr); + + cs_log_dbg(D_CLIENT, "ratelimiter forcing srvid %04X into slot %d/%d of reader %s", + er->srvid, foundspace + 1, maxecms, reader->label); + } + else + { + cs_log_dbg(D_CLIENT, "DVBAPI User %s is switching too fast for ratelimit and can't be prioritized!", + er->client->account->usr); + } + return foundspace; +} + +static int32_t ecm_ratelimit_findspace(struct s_reader *reader, ECM_REQUEST *er, struct ecmrl rl, int32_t reader_mode) +{ + int32_t h, foundspace = -1; + int32_t maxecms = MAXECMRATELIMIT; // init maxecms + int32_t totalecms = 0; // init totalecms + struct timeb actualtime; + cs_ftime(&actualtime); + + for(h = 0; h < MAXECMRATELIMIT; h++) // release slots with srvid that are overtime, even if not called from reader module to maximize available slots! + { + if(reader->rlecmh[h].last.time == -1) { continue; } + + int64_t gone = comp_timeb(&actualtime, &reader->rlecmh[h].last); + if(gone >= (reader->rlecmh[h].ratelimittime + reader->rlecmh[h].srvidholdtime) || gone < 0) // gone <0 fixup for bad systemtime on dvb receivers while changing transponders + { + cs_log_dbg(D_CLIENT, "ratelimiter srvid %04X released from slot %d/%d of reader %s (%"PRId64">=%d ratelimit ms + %d ms srvidhold!)", + reader->rlecmh[h].srvid, h + 1, MAXECMRATELIMIT, reader->label, gone, + reader->rlecmh[h].ratelimittime, reader->rlecmh[h].srvidholdtime); + + reader->rlecmh[h].last.time = -1; + reader->rlecmh[h].srvid = -1; + reader->rlecmh[h].kindecm = 0; + reader->rlecmh[h].once = 0; + } + + if(reader->rlecmh[h].last.time == -1) { continue; } + if(reader->rlecmh[h].ratelimitecm < maxecms) { maxecms = reader->rlecmh[h].ratelimitecm; } // we found a more critical ratelimit srvid + totalecms++; + } + + cs_log_dbg(D_CLIENT, "ratelimiter found total of %d srvid for reader %s most critical is limited to %d requests", + totalecms, reader->label, maxecms); + + if(reader->cooldown[0] && reader->cooldownstate != 1) { maxecms = MAXECMRATELIMIT; } // dont apply ratelimits if cooldown isnt in use or not in effect + + for(h = 0; h < MAXECMRATELIMIT; h++) // check if srvid is already in a slot + { + if(reader->rlecmh[h].last.time == -1) { continue; } + + if(reader->rlecmh[h].srvid == er->srvid && reader->rlecmh[h].caid == rl.caid && reader->rlecmh[h].provid == rl.provid + && (!reader->rlecmh[h].chid || (reader->rlecmh[h].chid == rl.chid))) + { + int64_t gone = 0; +#ifdef WITH_DEBUG + if(cs_dblevel & D_CLIENT) + { + gone = comp_timeb(&actualtime, &reader->rlecmh[h].last); + cs_log_dbg(D_CLIENT, "ratelimiter found srvid %04X for %"PRId64" ms in slot %d/%d of reader %s", er->srvid, gone, h + 1, MAXECMRATELIMIT, reader->label); + } +#endif + // check ecmunique if enabled and ecmunique time is done + if(reader_mode && reader->ecmunique) + { + gone = comp_timeb(&actualtime, &reader->rlecmh[h].last); + if(gone < reader->ratelimittime) + { + // some boxes seem to send different ecms but asking in fact for same cw! + if(memcmp(reader->rlecmh[h].ecmd5, er->ecmd5, CS_ECMSTORESIZE)) + { + // different ecm request than one in the slot! + if(er->ecm[0] == reader->rlecmh[h].kindecm) + { + // same ecm type! +#ifdef WITH_DEBUG + if(cs_dblevel & D_CLIENT) + { + char ecmd5[17 * 3]; + cs_hexdump(0, reader->rlecmh[h].ecmd5, 16, ecmd5, sizeof(ecmd5)); + cs_log_dbg(D_CLIENT, "ratelimiter ecm %s in this slot for next %d ms!", ecmd5, + (int)(reader->rlecmh[h].ratelimittime - gone)); + } +#endif + struct ecm_request_t *erold = NULL; + if(!cs_malloc(&erold, sizeof(struct ecm_request_t))) + { return -2; } + + memcpy(erold, er, sizeof(struct ecm_request_t)); // copy ecm all + memcpy(erold->ecmd5, reader->rlecmh[h].ecmd5, CS_ECMSTORESIZE); // replace md5 hash + struct ecm_request_t *ecm = NULL; + ecm = check_cache(erold, erold->client); //CHECK IF FOUND ECM IN CACHE + NULLFREE(erold); + + if(ecm) // found in cache + { + // return controlword of the ecm sitting in the slot! + write_ecm_answer(reader, er, ecm->rc, ecm->rcEx, ecm->cw, NULL, 0, &ecm->cw_ex); + } + else + { + write_ecm_answer(reader, er, E_NOTFOUND, E2_RATELIMIT, NULL, "Ratelimiter: no slots free!", 0, NULL); + } + + NULLFREE(ecm); + return -2; + } + } + } + + if((er->ecm[0] != reader->rlecmh[h].kindecm) && (gone <= reader->ratelimittime)) + { + if(!reader->rlecmh[h].once) // 1 premature ecmtype change is allowed (useful right after zapping to a channel!) + { + reader->rlecmh[h].once = 1; + cs_log_dbg(D_CLIENT, "ratelimiter changing slot %d srvid %04X ecmtype once from %s to %s!", h+1, er->srvid, + (reader->rlecmh[h].kindecm == 0x80 ? "even":"odd"), (er->ecm[0] == 0x80 ? "even":"odd")); + } + else + { + cs_log_dbg(D_CLIENT, "ratelimiter srvid %04X only allowing ecmtype %s for next %d ms in slot %d/%d of reader %s -> skipping this slot!", + reader->rlecmh[h].srvid, (reader->rlecmh[h].kindecm == 0x80 ? "even" : "odd"), + (int)(reader->rlecmh[h].ratelimittime - gone), h + 1, maxecms, reader->label); + continue; + } + } + } + + if(h > 0) + { + for(foundspace = 0; foundspace < h; foundspace++) // check for free lower slot + { + if(reader->rlecmh[foundspace].last.time == -1) + { + reader->rlecmh[foundspace] = reader->rlecmh[h]; // replace ecm request info + reader->rlecmh[h].last.time = -1; + reader->rlecmh[h].srvid = -1; + reader->rlecmh[h].kindecm = 0; + reader->rlecmh[h].once = 0; + + if(foundspace < maxecms) + { + cs_log_dbg(D_CLIENT, "ratelimiter moved srvid %04X to slot %d/%d of reader %s", + er->srvid, foundspace + 1, maxecms, reader->label); + + return foundspace; // moving to lower free slot! + } + else + { + cs_log_dbg(D_CLIENT, "ratelimiter removed srvid %04X from slot %d/%d of reader %s", + er->srvid, foundspace + 1, maxecms, reader->label); + + reader->rlecmh[foundspace].last.time = -1; // free this slot since we are over ratelimit! + return -1; // sorry, ratelimit! + } + } + } + } + + if(h < maxecms) // found but cant move to lower position! + { + return h; // return position if within ratelimits! + } + else + { + reader->rlecmh[h].last.time = -1; // free this slot since we are over ratelimit! + reader->rlecmh[h].srvid = -1; + reader->rlecmh[h].kindecm = 0; + reader->rlecmh[h].once = 0; + + cs_log_dbg(D_CLIENT, "ratelimiter removed srvid %04X from slot %d/%d of reader %s", + er->srvid, h + 1, maxecms, reader->label); + + return -1; // sorry, ratelimit! + } + } + } + + // srvid not found in slots! + + // do we use cooldown at all, are we in cooldown fase? + if((reader->cooldown[0] && reader->cooldownstate == 1) || !reader->cooldown[0]) + { + // we are in cooldown or no cooldown configured! + if(totalecms + 1 > maxecms || totalecms + 1 > rl.ratelimitecm) // check if this channel fits in! + { + cs_log_dbg(D_CLIENT, "ratelimiter for reader %s has no free slots!", reader->label); + return -1; + } + } + else + { + maxecms = MAXECMRATELIMIT; // no limits right now! + } + + for(h = 0; h < maxecms; h++) // check for free slot + { + if(reader->rlecmh[h].last.time == -1) + { + if(reader_mode) + { + cs_log_dbg(D_CLIENT, "ratelimiter added srvid %04X to slot %d/%d of reader %s", + er->srvid, h + 1, maxecms, reader->label); + } + return h; // free slot found -> assign it! + } + else // occupied slots + { +#ifdef WITH_DEBUG + if(cs_dblevel & D_CLIENT) + { + int64_t gone = comp_timeb(&actualtime, &reader->rlecmh[h].last); + cs_log_dbg(D_CLIENT, "ratelimiter srvid %04X for %"PRId64" ms present in slot %d/%d of reader %s", + reader->rlecmh[h].srvid, gone , h + 1, maxecms, reader->label); + } +#endif + } + } + + foundspace = dvbapi_override_prio(reader, er, maxecms, &actualtime); + if (foundspace > -1) + return foundspace; + + return (-1); // no slot found +} + +static void sort_ecmrl(struct s_reader *reader) +{ + int32_t i, j, loc; + struct ecmrl tmp; + + for(i = 0; i < reader->ratelimitecm; i++) // inspect all slots + { + if(reader->rlecmh[i].last.time == -1) { continue; } // skip empty slots + + loc = i; + tmp = reader->rlecmh[i]; // tmp is ecm in slot to evaluate + + for(j = i + 1; j < MAXECMRATELIMIT; j++) // inspect all slots above the slot to be inspected + { + if(reader->rlecmh[j].last.time == -1) { continue; } // skip empty slots + + int32_t gone = comp_timeb(&reader->rlecmh[i].last, &tmp.last); + if(gone > 0) // is higher slot holding a younger ecmrequest? + { + loc = j; // found a younger one + tmp = reader->rlecmh[j]; // copy the ecm in younger slot + } + } + + if(loc != i) // Did we find a younger ecmrequest? + { + reader->rlecmh[loc] = reader->rlecmh[i]; // place older request in slot of younger one we found + reader->rlecmh[i] = tmp; // place younger request in slot of older request + } + } + + // release all slots above ratelimit ecm + for(i = reader->ratelimitecm; i < MAXECMRATELIMIT; i++) + { + reader->rlecmh[i].last.time = -1; + reader->rlecmh[i].srvid = -1; + reader->rlecmh[i].kindecm = 0; + reader->rlecmh[i].once = 0; + } + +} + +// If reader_mode is 1, ECM_REQUEST need to be assigned to reader and slot. +// Else just report if a free slot is available. +int32_t maxslots = MAXECMRATELIMIT; +int32_t ecm_ratelimit_check(struct s_reader *reader, ECM_REQUEST *er, int32_t reader_mode) +{ + // No rate limit set + if(!reader->ratelimitecm) + { + return OK; + } + + int32_t foundspace = -1, h; // init slots to oscam global maximums + struct ecmrl rl; + struct timeb now; + rl = get_ratelimit(er); + + if(rl.ratelimitecm > 0) + { + cs_log_dbg(D_CLIENT, "ratelimit found for CAID: %04X PROVID: %06X SRVID: %04X CHID: %04X maxecms: %d cycle: %d ms srvidhold: %d ms", + rl.caid, rl.provid, rl.srvid, rl.chid, rl.ratelimitecm, rl.ratelimittime, rl.srvidholdtime); + } + else // nothing found: apply general reader limits + { + rl.ratelimitecm = reader->ratelimitecm; + rl.ratelimittime = reader->ratelimittime; + rl.srvidholdtime = reader->srvidholdtime; + rl.caid = er->caid; + rl.provid = er->prid; + rl.chid = er->chid; + rl.srvid = er->srvid; + cs_log_dbg(D_CLIENT, "ratelimiter apply readerdefault for CAID: %04X PROVID: %06X SRVID: %04X CHID: %04X maxecms: %d cycle: %d ms srvidhold: %d ms", + rl.caid, rl.provid, rl.srvid, rl.chid, rl.ratelimitecm, rl.ratelimittime, rl.srvidholdtime); + } + + // Below this line: rate limit functionality. + // No cooldown set + if(!reader->cooldown[0]) + { + cs_log_dbg(D_CLIENT, "ratelimiter find a slot for srvid %04X on reader %s", er->srvid, reader->label); + foundspace = ecm_ratelimit_findspace(reader, er, rl, reader_mode); + if(foundspace < 0) + { + if(reader_mode) + { + if(foundspace != -2) + { + cs_log_dbg(D_CLIENT, "ratelimiter no free slot for srvid %04X on reader %s -> dropping!", er->srvid, reader->label); + write_ecm_answer(reader, er, E_NOTFOUND, E2_RATELIMIT, NULL, "Ratelimiter: no slots free!", 0, NULL); + } + } + + return ERROR; // not even trowing an error... obvious reason ;) + } + else // we are within ecmratelimits + { + if(reader_mode) + { + // Register new slot + //reader->rlecmh[foundspace].srvid=er->srvid; // register srvid + reader->rlecmh[foundspace] = rl; // register this srvid ratelimit params + cs_ftime(&reader->rlecmh[foundspace].last); // register request time + memcpy(reader->rlecmh[foundspace].ecmd5, er->ecmd5, CS_ECMSTORESIZE); // register ecmhash + reader->rlecmh[foundspace].kindecm = er->ecm[0]; // register kind of ecm + } + + return OK; + } + } + + // Below this line: rate limit functionality with cooldown option. + + // Cooldown state cycle: + // state = 0: Cooldown setup phase. No rate limit set. + // If number of ecm request exceed reader->ratelimitecm, cooldownstate goes to 2. + // state = 2: Cooldown delay phase. No rate limit set. + // If number of ecm request still exceed reader->ratelimitecm at end of cooldown delay phase, + // cooldownstate goes to 1 (rate limit phase). + // Else return back to setup phase (state 0). + // state = 1: Cooldown ratelimit phase. Rate limit set. + // If cooldowntime reader->cooldown[1] is elapsed, return to cooldown setup phase (state 0). + + cs_ftime(&now); + int32_t gone = comp_timeb(&now, &reader->cooldowntime); + if(reader->cooldownstate == 1) // Cooldown in ratelimit phase + { + if(gone <= reader->cooldown[1] * 1000) // check if cooldowntime is elapsed + { maxslots = reader->ratelimitecm; } // use user defined ratelimitecm + else // Cooldown time is elapsed + { + reader->cooldownstate = 0; // set cooldown setup phase + reader->cooldowntime.time = -1; // reset cooldowntime + maxslots = MAXECMRATELIMIT; //use oscam defined max slots + cs_log("Reader: %s ratelimiter returning to setup phase cooling down period of %d seconds is done!", reader->label, reader->cooldown[1]); + } + } // if cooldownstate == 1 + + if(reader->cooldownstate == 2 && gone > reader->cooldown[0] * 1000) + { + // Need to check if the otherslots are not exceeding the ratelimit at the moment that + // cooldown[0] time was exceeded! + // time_t actualtime = reader->cooldowntime + reader->cooldown[0]; + maxslots = 0; // maxslots is used as counter + for(h = 0; h < MAXECMRATELIMIT; h++) + { + if(reader->rlecmh[h].last.time == -1) { continue; } // skip empty slots + // how many active slots are registered at end of cooldown delay period + + gone = comp_timeb(&now, &reader->rlecmh[h].last); + if(gone <= (reader->ratelimittime + reader->srvidholdtime)) + { + maxslots++; + if(maxslots >= reader->ratelimitecm) { break; } // Need to go cooling down phase + } + } + + if(maxslots < reader->ratelimitecm) + { + reader->cooldownstate = 0; // set cooldown setup phase + reader->cooldowntime.time = -1; // reset cooldowntime + maxslots = MAXECMRATELIMIT; // maxslots is maxslots again + cs_log("Reader: %s ratelimiter returning to setup phase after %d seconds cooldowndelay!", reader->label, reader->cooldown[0]); + } else + { + reader->cooldownstate = 1; // Entering ratelimit for cooldown ratelimitseconds + cs_ftime(&reader->cooldowntime); // set time to enforce ecmratelimit for defined cooldowntime + maxslots = reader->ratelimitecm; // maxslots is maxslots again + sort_ecmrl(reader); // keep youngest ecm requests in list + housekeeping + cs_log("Reader: %s ratelimiter starting cooling down period of %d seconds!", reader->label, reader->cooldown[1]); + } + } // if cooldownstate == 2 + + cs_log_dbg(D_CLIENT, "ratelimiter cooldownphase %d find a slot for srvid %04X on reader %s", reader->cooldownstate, er->srvid, reader->label); + + foundspace = ecm_ratelimit_findspace(reader, er, rl, reader_mode); + + if(foundspace < 0) + { + if(reader_mode) + { + if(foundspace != -2) + { + cs_log_dbg(D_CLIENT, "ratelimiter cooldownphase %d no free slot for srvid %04X on reader %s -> dropping!", reader->cooldownstate, er->srvid, reader->label); + write_ecm_answer(reader, er, E_NOTFOUND, E2_RATELIMIT, NULL, "Ratelimiter: cooldown no slots free!", 0, NULL); + } + } + + return ERROR; // not even trowing an error... obvious reason ;) + } + else // we are within ecmratelimits + { + if(reader_mode) + { + // Register new slot + //reader->rlecmh[foundspace].srvid=er->srvid; // register srvid + reader->rlecmh[foundspace] = rl; // register this srvid ratelimit params + cs_ftime(&reader->rlecmh[foundspace].last); // register request time + memcpy(reader->rlecmh[foundspace].ecmd5, er->ecmd5, CS_ECMSTORESIZE);// register ecmhash + reader->rlecmh[foundspace].kindecm = er->ecm[0]; // register kind of ecm + } + } + + if(reader->cooldownstate == 0 && foundspace >= reader->ratelimitecm) + { + if(!reader_mode) // No actual ecm request, just check + { + return OK; + } + + cs_log("Reader: %s ratelimiter cooldown detected overrun ecmratelimit of %d during setup phase!", + reader->label, (foundspace - reader->ratelimitecm + 1)); + reader->cooldownstate = 2; // Entering cooldowndelay phase + cs_ftime(&reader->cooldowntime); // Set cooldowntime to calculate delay + cs_log_dbg(D_CLIENT, "ratelimiter cooldowndelaying %d seconds", reader->cooldown[0]); + } + + // Cooldown state housekeeping is done. There is a slot available. + if(reader_mode) + { + // Register new slot + //reader->rlecmh[foundspace].srvid=er->srvid; // register srvid + reader->rlecmh[foundspace] = rl; // register this srvid ratelimit params + cs_ftime(&reader->rlecmh[foundspace].last); // register request time + memcpy(reader->rlecmh[foundspace].ecmd5, er->ecmd5, CS_ECMSTORESIZE);// register ecmhash + reader->rlecmh[foundspace].kindecm = er->ecm[0]; // register kind of ecm + } + + return OK; +} + +const struct s_cardsystem *get_cardsystem_by_caid(uint16_t caid) +{ + int32_t i, j; + for(i = 0; cardsystems[i]; i++) + { + const struct s_cardsystem *csystem = cardsystems[i]; + for(j = 0; csystem->caids[j]; j++) + { + uint16_t cs_caid = csystem->caids[j]; + if(!cs_caid) + { continue; } + if(cs_caid == caid || cs_caid == caid >> 8) + { return csystem; } + } + } + return NULL; +} + +struct s_reader *get_reader_by_label(char *lbl) +{ + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(streq(lbl, rdr->label)) + { break; } + } + return rdr; +} + +const char *reader_get_type_desc(struct s_reader *rdr, int32_t extended) +{ + const char *desc = "unknown"; + if(rdr->crdr && rdr->crdr->desc) + { return rdr->crdr->desc; } + if(is_network_reader(rdr) || rdr->typ == R_SERIAL) + { + if(rdr->ph.desc) + { desc = rdr->ph.desc; } + } + if(rdr->typ == R_NEWCAMD && rdr->ncd_proto == NCD_524) + { desc = "newcamd524"; } + else if(rdr->typ == R_CCCAM) + { + desc = "cccam"; + if(extended && cccam_client_extended_mode(rdr->client)) desc = "cccam_ext"; + if(cccam_client_multics_mode(rdr->client)) desc = "cccam_mcs"; + } + return desc; +} + +bool hexserialset(struct s_reader *rdr) +{ + int i; + if(!rdr) + { return false; } + for(i = 0; i < 8; i++) + { + if(rdr->hexserial[i]) + { return true; } + } + return false; +} + +void hexserial_to_newcamd(uint8_t *source, uint8_t *dest, uint16_t caid) +{ + if(caid_is_bulcrypt(caid)) + { + dest[0] = 0x00; + dest[1] = 0x00; + memcpy(dest + 2, source, 4); + } + else if(caid_is_irdeto(caid) || caid_is_betacrypt(caid)) + { + // only 4 Bytes Hexserial for newcamd clients (Hex Base + Hex Serial) + // first 2 Byte always 00 + dest[0] = 0x00; //serial only 4 bytes + dest[1] = 0x00; //serial only 4 bytes + // 1 Byte Hex Base (see reader-irdeto.c how this is stored in "source") + dest[2] = source[3]; + // 3 Bytes Hex Serial (see reader-irdeto.c how this is stored in "source") + dest[3] = source[0]; + dest[4] = source[1]; + dest[5] = source[2]; + } + else if(caid_is_viaccess(caid) || caid_is_cryptoworks(caid)) + { + dest[0] = 0x00; + memcpy(dest + 1, source, 5); + } + else + { + memcpy(dest, source, 6); + } +} + +void newcamd_to_hexserial(uint8_t *source, uint8_t *dest, uint16_t caid) +{ + if(caid_is_bulcrypt(caid)) + { + memcpy(dest, source + 2, 4); + dest[4] = 0x00; + dest[5] = 0x00; + } + else if(caid_is_irdeto(caid) || caid_is_betacrypt(caid)) + { + memcpy(dest, source + 3, 3); + dest[3] = source[2]; + dest[4] = 0; + dest[5] = 0; + } + else if(caid_is_viaccess(caid) || caid_is_cryptoworks(caid)) + { + memcpy(dest, source + 1, 5); + dest[5] = 0; + } + else + { + memcpy(dest, source, 6); + } +} + +/** + * add or find one entitlement item to entitlements of reader + * use add = 0 for find only, or add > 0 to find and add if not found + **/ +S_ENTITLEMENT *cs_add_entitlement(struct s_reader *rdr, uint16_t caid, uint32_t provid, uint64_t id, uint32_t class, time_t start, time_t end, uint8_t type, uint8_t add) +{ + if(!rdr->ll_entitlements) + { + rdr->ll_entitlements = ll_create("ll_entitlements"); + } + + S_ENTITLEMENT *item = NULL; + LL_ITER it; + + it = ll_iter_create(rdr->ll_entitlements); + while((item = ll_iter_next(&it)) != NULL) + { + if((caid && item->caid != caid) || (provid && item->provid != provid) || + (id && item->id != id) || (class && item->class != class) || + (start && ((!add && item->start < start) || (add && item->start !=start))) || + (end && ((!add && item->end < end) || (add && item->end != end))) || + (type && item->type != type)) + { + continue; // no match, try next! + } + break; // match found! + } + + if(add && item == NULL) + { + if(cs_malloc(&item, sizeof(S_ENTITLEMENT))) + { + // fill item + item->caid = caid; + item->provid = provid; + item->id = id; + item->class = class; + item->start = start; + item->end = end; + item->type = type; + + // add item + ll_append(rdr->ll_entitlements, item); + // cs_log_dbg(D_TRACE, "entitlement: Add caid %4X id %4X %s - %s ", item->caid, item->id, item->start, item->end); + } + else + { + cs_log("ERROR: Can't allocate entitlement to reader!"); + } + } + + return item; +} + +/** + * clears entitlements of reader. + **/ +void cs_clear_entitlement(struct s_reader *rdr) +{ + if(!rdr->ll_entitlements) + { return; } + + ll_clear_data(rdr->ll_entitlements); +} + + +void casc_check_dcw(struct s_reader *reader, int32_t idx, int32_t rc, uint8_t *cw) +{ + int32_t i, pending = 0; + time_t t = time(NULL); + ECM_REQUEST *ecm; + struct s_client *cl = reader->client; + + if(!check_client(cl)) { return; } + + for(i = 0; i < cfg.max_pending; i++) + { + ecm = &cl->ecmtask[i]; + if((ecm->rc >= E_NOCARD) && ecm->caid == cl->ecmtask[idx].caid && (!memcmp(ecm->ecmd5, cl->ecmtask[idx].ecmd5, CS_ECMSTORESIZE))) + { + if(rc == 2) // E_INVALID from camd35 CMD08 + { + write_ecm_answer(reader, ecm, E_INVALID, 0, cw, NULL, 0, NULL); + } + else if(rc) + { +#ifdef CS_CACHEEX_AIO + if(rc == 0x86) // lg-flagged rc + { + ecm->localgenerated = 1; + } +#endif + write_ecm_answer(reader, ecm, E_FOUND, 0, cw, NULL, 0, NULL); + } + else + { + write_ecm_answer(reader, ecm, E_NOTFOUND, 0 , NULL, NULL, 0, NULL); + } + ecm->idx = 0; + ecm->rc = E_FOUND; + } + + if(ecm->rc >= E_NOCARD && (t - (uint32_t)ecm->tps.time > ((cfg.ctimeout + 500) / 1000) + 1)) // drop timeouts + { + ecm->rc = E_FOUND; + } + + if(ecm->rc >= E_NOCARD) + { pending++; } + } + cl->pending = pending; +} + +int32_t hostResolve(struct s_reader *rdr) +{ + struct s_client *cl = rdr->client; + + if(!cl) { return 0; } + + IN_ADDR_T last_ip; + IP_ASSIGN(last_ip, cl->ip); +/* + force v4/v6 hostResolve +*/ +#ifdef IPV6SUPPORT + if (rdr->ipv4force || rdr->ipv6_connect_failed) + { + cs_resolve_v4(rdr->device, &cl->ip, &cl->udp_sa, &cl->udp_sa_len); + rdr->ipv6_connect_failed = 0; // reset ipv6 connection fail to retry on next connect cycle + } + else + { +#endif + cs_resolve(rdr->device, &cl->ip, &cl->udp_sa, &cl->udp_sa_len); +#ifdef IPV6SUPPORT + } +#endif + IP_ASSIGN(SIN_GET_ADDR(cl->udp_sa), cl->ip); + + if(!IP_EQUAL(cl->ip, last_ip)) + { + cs_log("%s: resolved ip=%s", rdr->device, cs_inet_ntoa(cl->ip)); + } + + return IP_ISSET(cl->ip); +} + +void clear_block_delay(struct s_reader *rdr) +{ + rdr->tcp_block_delay = 0; + cs_ftime(&rdr->tcp_block_connect_till); +} + +void block_connect(struct s_reader *rdr) +{ + if(!rdr->tcp_block_delay) + { rdr->tcp_block_delay = 100; } // starting blocking time, 100ms + + cs_ftime(&rdr->tcp_block_connect_till); + add_ms_to_timeb(&rdr->tcp_block_connect_till, rdr->tcp_block_delay); + rdr->tcp_block_delay *= 4; // increment timeouts + + if(rdr->tcp_block_delay >= rdr->tcp_reconnect_delay) + { rdr->tcp_block_delay = rdr->tcp_reconnect_delay; } + + rdr_log_dbg(rdr, D_TRACE, "tcp connect blocking delay set to %d", rdr->tcp_block_delay); +} + +int32_t is_connect_blocked(struct s_reader *rdr) +{ + struct timeb cur_time; + cs_ftime(&cur_time); + int32_t diff = comp_timeb(&cur_time, &rdr->tcp_block_connect_till); + int32_t blocked = rdr->tcp_block_delay && diff < 0; + + if(blocked) + rdr_log_dbg(rdr, D_TRACE, "connection blocked, retrying in %d ms", -diff); + + return blocked; +} + +int32_t network_tcp_connection_open(struct s_reader *rdr) +{ + if(!rdr) { return -1; } + struct s_client *client = rdr->client; + struct SOCKADDR loc_sa; + + memset((char *)&client->udp_sa, 0, sizeof(client->udp_sa)); + + IN_ADDR_T last_ip; + IP_ASSIGN(last_ip, client->ip); + if(!hostResolve(rdr)) + { return -1; } + + if(!IP_EQUAL(last_ip, client->ip)) // clean blocking delay on ip change: + { clear_block_delay(rdr); } + + if(is_connect_blocked(rdr)) // inside of blocking delay, do not connect! + { + return -1; + } + + if(client->reader->r_port <= 0) + { + rdr_log(client->reader, "invalid port %d for server %s", client->reader->r_port, client->reader->device); + return -1; + } + + client->is_udp = (rdr->typ == R_CAMD35); + + rdr_log(rdr, "connecting to %s:%d", rdr->device, rdr->r_port); + + if(client->udp_fd) + { rdr_log(rdr, "WARNING: client->udp_fd was not 0"); } + + int s_domain = PF_INET; + int s_family = AF_INET; +#ifdef IPV6SUPPORT + if(!IN6_IS_ADDR_V4MAPPED(&rdr->client->ip) && !IN6_IS_ADDR_V4COMPAT(&rdr->client->ip)) + { + s_domain = PF_INET6; + s_family = AF_INET6; + } +#endif + int s_type = client->is_udp ? SOCK_DGRAM : SOCK_STREAM; + int s_proto = client->is_udp ? IPPROTO_UDP : IPPROTO_TCP; + + if((client->udp_fd = socket(s_domain, s_type, s_proto)) < 0) + { + rdr_log(rdr, "Socket creation failed (errno=%d %s)", errno, strerror(errno)); + client->udp_fd = 0; + block_connect(rdr); + return -1; + } + + set_socket_priority(client->udp_fd, cfg.netprio); + + int32_t keep_alive = 1; + setsockopt(client->udp_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keep_alive, sizeof(keep_alive)); + + int32_t flag = 1; + setsockopt(client->udp_fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flag, sizeof(flag)); + + if(setsockopt(client->udp_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag)) < 0) + { + rdr_log(rdr, "setsockopt failed (errno=%d: %s)", errno, strerror(errno)); + client->udp_fd = 0; + block_connect(rdr); + return -1; + } + + set_so_reuseport(client->udp_fd); + + memset((char *)&loc_sa, 0, sizeof(loc_sa)); + SIN_GET_FAMILY(loc_sa) = s_family; + + if(IP_ISSET(cfg.srvip)) + { IP_ASSIGN(SIN_GET_ADDR(loc_sa), cfg.srvip); } + else + { SIN_GET_ADDR(loc_sa) = ADDR_ANY; } + + if(client->reader->l_port) + { SIN_GET_PORT(loc_sa) = htons(client->reader->l_port); } + + if(client->is_udp && bind(client->udp_fd, (struct sockaddr *)&loc_sa, sizeof(loc_sa)) < 0) + { + rdr_log(rdr, "bind failed (errno=%d %s)", errno, strerror(errno)); + close(client->udp_fd); + client->udp_fd = 0; + block_connect(rdr); + return -1; + } + +#ifdef IPV6SUPPORT + if(IN6_IS_ADDR_V4MAPPED(&rdr->client->ip) || IN6_IS_ADDR_V4COMPAT(&rdr->client->ip)) + { + ((struct sockaddr_in *)(&client->udp_sa))->sin_family = AF_INET; + ((struct sockaddr_in *)(&client->udp_sa))->sin_port = htons((uint16_t)client->reader->r_port); + } + else + { + ((struct sockaddr_in6 *)(&client->udp_sa))->sin6_family = AF_INET6; + ((struct sockaddr_in6 *)(&client->udp_sa))->sin6_port = htons((uint16_t)client->reader->r_port); + } +#else + client->udp_sa.sin_family = AF_INET; + client->udp_sa.sin_port = htons((uint16_t)client->reader->r_port); +#endif + + rdr_log_dbg(rdr, D_TRACE, "socket open fd=%d", client->udp_fd); + + if(client->is_udp) + { + rdr->tcp_connected = 1; + return client->udp_fd; + } + + set_nonblock(client->udp_fd, true); + + int32_t res = connect(client->udp_fd, (struct sockaddr *)&client->udp_sa, client->udp_sa_len); + if(res == -1) + { + int32_t r = -1; + if(errno == EINPROGRESS || errno == EALREADY) + { + struct pollfd pfd; + pfd.fd = client->udp_fd; + pfd.events = POLLOUT; + int32_t rc = poll(&pfd, 1, 3000); + if(rc > 0) + { + uint32_t l = sizeof(r); + if(getsockopt(client->udp_fd, SOL_SOCKET, SO_ERROR, &r, (socklen_t *)&l) != 0) + { r = -1; } + else + { errno = r; } + } + else + { + errno = ETIMEDOUT; + } + } + if(r != 0) + { + rdr_log(rdr, "connect failed: %s", strerror(errno)); +#ifdef IPV6SUPPORT + if (!IN6_IS_ADDR_V4MAPPED(&rdr->client->ip) && !IN6_IS_ADDR_V4COMPAT(&rdr->client->ip) && !rdr->ipv6_connect_failed) + { + rdr->ipv6_connect_failed = 1; + rdr_log(rdr, "connect via IPv6(%s) failed - try IPv4 fallback", cs_inet_ntoa(rdr->client->ip)); + } + else + { +#endif + block_connect(rdr); // connect has failed. Block connect for a while +#ifdef IPV6SUPPORT + } +#endif + close(client->udp_fd); + client->udp_fd = 0; + return -1; + } + } + + set_nonblock(client->udp_fd, false); // restore blocking mode + + setTCPTimeouts(client->udp_fd); + clear_block_delay(rdr); + client->last = client->login = time((time_t *)0); + client->last_caid = NO_CAID_VALUE; + client->last_provid = NO_PROVID_VALUE; + client->last_srvid = NO_SRVID_VALUE; + client->pfd = client->udp_fd; + rdr->tcp_connected = 1; + rdr_log_dbg(rdr, D_TRACE, "connect successful fd=%d", client->udp_fd); + return client->udp_fd; +} + +void network_tcp_connection_close(struct s_reader *reader, char *reason) +{ + if(!reader) + { + // only proxy reader should call this, client connections are closed on thread cleanup + cs_log("WARNING: invalid client"); + cs_disconnect_client(cur_client()); + return; + } + + struct s_client *cl = reader->client; + if(!cl) { return; } + int32_t fd = cl->udp_fd; + + int32_t i; + + if(fd) + { + rdr_log(reader, "disconnected: reason %s", reason ? reason : "undef"); + close(fd); + + cl->udp_fd = 0; + cl->pfd = 0; + } + + reader->tcp_connected = 0; + reader->card_status = UNKNOWN; + cl->logout = time((time_t *)0); + + if(cl->ecmtask) + { + for(i = 0; i < cfg.max_pending; i++) + { + cl->ecmtask[i].idx = 0; + cl->ecmtask[i].rc = E_FOUND; + } + } + // newcamd message ids are stored as a reference in ecmtask[].idx + // so we need to reset them aswell + if(reader->typ == R_NEWCAMD) + { cl->ncd_msgid = 0; } +} + +int32_t casc_process_ecm(struct s_reader *reader, ECM_REQUEST *er) +{ + int32_t rc, n, i, sflag, pending = 0; + time_t t;//, tls; + struct s_client *cl = reader->client; + + if(!cl || !cl->ecmtask) + { + rdr_log(reader, "WARNING: ecmtask not available"); + return -1; + } + + t = time((time_t *)0); + ECM_REQUEST *ecm; + for(i = 0; i < cfg.max_pending; i++) + { + ecm = &cl->ecmtask[i]; + if((ecm->rc >= E_NOCARD) && (t - (uint32_t)ecm->tps.time > ((cfg.ctimeout + 500) / 1000) + 1)) // drop timeouts + { + ecm->rc = E_FOUND; + } + } + + for(n = -1, i = 0, sflag = 1; i < cfg.max_pending; i++) + { + ecm = &cl->ecmtask[i]; + if(n < 0 && (ecm->rc < E_NOCARD)) // free slot found + { n = i; } + + // ecm already pending + // ...this level at least + if((ecm->rc >= E_NOCARD) && er->caid == ecm->caid && (!memcmp(er->ecmd5, ecm->ecmd5, CS_ECMSTORESIZE))) + { sflag = 0; } + + if(ecm->rc >= E_NOCARD) + { pending++; } + } + cl->pending = pending; + + if(n < 0) + { + rdr_log(reader, "WARNING: reader ecm pending table overflow !!"); + return (-2); + } + + memcpy(&cl->ecmtask[n], er, sizeof(ECM_REQUEST)); + cl->ecmtask[n].matching_rdr = NULL; // This avoids double free of matching_rdr! +#ifdef CS_CACHEEX + cl->ecmtask[n].csp_lastnodes = NULL; // This avoids double free of csp_lastnodes! +#endif + cl->ecmtask[n].parent = er; + + if(reader->typ == R_NEWCAMD) + { cl->ecmtask[n].idx = (cl->ncd_msgid == 0) ? 2 : cl->ncd_msgid + 1; } + else + { + if(!cl->idx) + { cl->idx = 1; } + cl->ecmtask[n].idx = cl->idx++; + } + + cl->ecmtask[n].rc = E_NOCARD; + cs_log_dbg(D_TRACE, "---- ecm_task %d, idx %d, sflag=%d", n, cl->ecmtask[n].idx, sflag); + + cs_log_dump_dbg(D_ATR, er->ecm, er->ecmlen, "casc ecm (%s):", (reader) ? reader->label : "n/a"); + rc = 0; + + if(sflag) + { + rc = reader->ph.c_send_ecm(cl, &cl->ecmtask[n]); + if(rc != 0) + { + casc_check_dcw(reader, n, 0, cl->ecmtask[n].cw); // simulate "not found" + } + else + { cl->last_idx = cl->ecmtask[n].idx; } + reader->last_s = t; // used for inactive_timeout and reconnect_timeout in TCP reader + } + + if(cl->idx > 0x1ffe) { cl->idx = 1; } + + return (rc); +} + +void reader_get_ecm(struct s_reader *reader, ECM_REQUEST *er) +{ + if(!reader) { return; } + struct s_client *cl = reader->client; + if(!check_client(cl)) { return; } + + if(!chk_bcaid(er, &reader->ctab)) + { + rdr_log_dbg(reader, D_READER, "caid %04X filtered", er->caid); + write_ecm_answer(reader, er, E_NOTFOUND, E2_CAID, NULL, NULL, 0, NULL); + return; + } + + // CHECK if ecm already sent to reader + struct s_ecm_answer *ea_er = get_ecm_answer(reader, er); + if(!ea_er) { return; } + + struct s_ecm_answer *ea = NULL, *ea_prev = NULL; + struct ecm_request_t *ecm; + time_t timeout; + + cs_readlock(__func__, &ecmcache_lock); + + for(ecm = ecmcwcache; ecm; ecm = ecm->next) + { + timeout = time(NULL) - ((cfg.ctimeout+500)/1000+1); + if(ecm->tps.time <= timeout) + { break; } + + if(!ecm->matching_rdr || ecm == er || ecm->rc == E_99) { continue; } + + // match same ecm + if(er->caid == ecm->caid && !memcmp(er->ecmd5, ecm->ecmd5, CS_ECMSTORESIZE)) + { + //check if ask this reader + ea = get_ecm_answer(reader, ecm); + if(ea && !ea->is_pending && (ea->status & REQUEST_SENT) && ea->rc != E_TIMEOUT && ea->rcEx != E2_RATELIMIT) { break; } + ea = NULL; + } + } + + cs_readunlock(__func__, &ecmcache_lock); + + if(ea) // found ea in cached ecm, asking for this reader + { + ea_er->is_pending = true; + + cs_readlock(__func__, &ea->ecmanswer_lock); + if(ea->rc < E_99) + { + cs_readunlock(__func__, &ea->ecmanswer_lock); + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [reader_get_ecm] ecm already sent to reader %s (%s)", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, reader ? reader->label : "-", ea->rc==E_FOUND?"OK":"NOK"); + + //e.g. we cannot send timeout, because "ea_temp->er->client" could wait/ask other readers! Simply set not_found if different from E_FOUND! + write_ecm_answer(reader, er, (ea->rc==E_FOUND? E_FOUND : E_NOTFOUND), ea->rcEx, ea->cw, NULL, ea->tier, &ea->cw_ex); + return; + } + else + { + ea_prev = ea->pending; + ea->pending = ea_er; + ea->pending->pending_next = ea_prev; + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [reader_get_ecm] ecm already sent to reader %s... set as pending", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, reader ? reader->label : "-"); + } + cs_readunlock(__func__, &ea->ecmanswer_lock); + return; + } + + lb_update_last(ea_er, reader); + + if(ecm_ratelimit_check(reader, er, 1) != OK) + { + rdr_log_dbg(reader, D_READER, "ratelimiter has no space left -> skip!"); + return; + } + + if(is_cascading_reader(reader)) // forward request to proxy reader + { + cl->last_srvid = er->srvid; + cl->last_caid = er->caid; + cl->last_provid = er->prid; + casc_process_ecm(reader, er); + cl->lastecm = time((time_t *)0); + return; + } + + cardreader_process_ecm(reader, cl, er); // forward request to physical reader +} + +void reader_do_card_info(struct s_reader *reader) +{ + cardreader_get_card_info(reader); + if(reader->ph.c_card_info) + { reader->ph.c_card_info(); } +} + +void reader_do_idle(struct s_reader *reader) +{ + if(reader->ph.c_idle) + { reader->ph.c_idle(); } + else if (reader->tcp_ito > 0) + { + time_t now; + int32_t time_diff; + time(&now); + time_diff = llabs(now - reader->last_s); + if(time_diff > reader->tcp_ito) + { + struct s_client *cl = reader->client; + if(check_client(cl) && reader->tcp_connected && reader->ph.type == MOD_CONN_TCP) + { + rdr_log_dbg(reader, D_READER, "inactive_timeout, close connection (fd=%d)", cl->pfd); + network_tcp_connection_close(reader, "inactivity"); + } + else + { reader->last_s = now; } + } + } +} + +int32_t reader_init(struct s_reader *reader) +{ + struct s_client *client = reader->client; + + if(is_cascading_reader(reader)) + { + client->typ = 'p'; + client->port = reader->r_port; + set_null_ip(&client->ip); + + if(!(reader->ph.c_init)) + { + rdr_log(reader, "FATAL: protocol not supporting cascading"); + return 0; + } + + if(reader->ph.c_init(client)) + { + //proxy reader start failed + return 0; + } + + if(client->ecmtask) + { + add_garbage(client->ecmtask); + client->ecmtask = NULL; + } + + if(!cs_malloc(&client->ecmtask, cfg.max_pending * sizeof(ECM_REQUEST))) + { return 0; } + + rdr_log(reader, "proxy initialized, server %s:%d", reader->device, reader->r_port); + } + else + { + if(!cardreader_init(reader)) + { return 0; } + } + + ll_destroy_data(&reader->emmstat); + + client->login = time((time_t *)0); + client->init_done = 1; + + return 1; +} + +#if !defined(WITH_CARDREADER) && (defined(WITH_STAPI) || defined(WITH_STAPI5)) +/* Dummy function stub for stapi compiles without cardreader as libstapi needs it. */ +int32_t ATR_InitFromArray(ATR *atr, const uint8_t atr_buffer[ATR_MAX_SIZE], uint32_t length) +{ + (void)atr; + (void)atr_buffer; + (void)length; + return 0; +} +#endif + +void cs_card_info(void) +{ + struct s_client *cl; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->typ == 'r' && cl->reader) + { add_job(cl, ACTION_READER_CARDINFO, NULL, 0); } + } +} + + +/* Adds a reader to the list of active readers so that it can serve ecms. */ +static void add_reader_to_active(struct s_reader *rdr) +{ + struct s_reader *rdr2, *rdr_prv = NULL, *rdr_tmp = NULL; + int8_t at_first = 1; + + if(rdr->next) + { remove_reader_from_active(rdr); } + + cs_writelock(__func__, &readerlist_lock); + cs_writelock(__func__, &clientlist_lock); + + // search configured position: + LL_ITER it = ll_iter_create(configured_readers); + while((rdr2 = ll_iter_next(&it))) + { + if(rdr2 == rdr) + { break; } + if(rdr2->client && rdr2->enable) + { + rdr_prv = rdr2; + at_first = 0; + } + } + + // insert at configured position: + if(first_active_reader) + { + if(at_first) + { + rdr->next = first_active_reader; + first_active_reader = rdr; + + // resort client list: + struct s_client *prev, *cl; + + for(prev = first_client, cl = first_client->next; + prev->next != NULL; prev = prev->next, cl = cl->next) + { + if(rdr->client == cl) + { break; } + } + + if(cl && rdr->client == cl) + { + prev->next = cl->next; // remove client from list + cl->next = first_client->next; + first_client->next = cl; + } + } + else + { + for(rdr2 = first_active_reader; rdr2->next && rdr2 != rdr_prv ; rdr2 = rdr2->next) { ; } // search last element + + rdr_prv = rdr2; + rdr_tmp = rdr2->next; + rdr2->next = rdr; + rdr->next = rdr_tmp; + + // resort client list: + struct s_client *prev, *cl; + + for(prev = first_client, cl = first_client->next; + prev->next != NULL; prev = prev->next, cl = cl->next) + { + if(rdr->client == cl) + { break; } + } + + if(cl && rdr->client == cl) + { + prev->next = cl->next; // remove client from list + cl->next = rdr_prv->client->next; + rdr_prv->client->next = cl; + } + } + } + else + { + first_active_reader = rdr; + } + rdr->active = 1; + cs_writeunlock(__func__, &clientlist_lock); + cs_writeunlock(__func__, &readerlist_lock); +} + +/* Removes a reader from the list of active readers so that no ecms can be requested anymore. */ +void remove_reader_from_active(struct s_reader *rdr) +{ + struct s_reader *rdr2, *prv = NULL; + //rdr_log(rdr, "CHECK: REMOVE READER FROM ACTIVE"); + cs_writelock(__func__, &readerlist_lock); + for(rdr2 = first_active_reader; rdr2 ; prv = rdr2, rdr2 = rdr2->next) + { + if(rdr2 == rdr) + { + if(prv) { prv->next = rdr2->next; } + else { first_active_reader = rdr2->next; } + break; + } + } + rdr->next = NULL; + rdr->active = 0; + cs_writeunlock(__func__, &readerlist_lock); +} + +/* Starts or restarts a cardreader without locking. If restart=1, the existing thread is killed before restarting, + if restart=0 the cardreader is only started. */ +static int32_t restart_cardreader_int(struct s_reader *rdr, int32_t restart) +{ + struct s_client *cl = rdr->client; + if(restart) + { + uint16_t waitme = 1500; + remove_reader_from_active(rdr); // remove from list + kill_thread(cl); // kill old thread + + // wait a bit if socket not closed and is_valid_client, othervise safe for reload? + do + { + if (!is_valid_client(cl)) + { + // 100 mS I think is enought for freeing garbage + cs_sleepms(100); + break; + } + else + { + // If we quick disable+enable a reader (webif), remove_reader_from_active is called from + // cleanup. this could happen AFTER reader is restarted, so oscam crashes or reader is hidden + // rdr_log(rdr, "CHECK: WAITING FOR CLEANUP"); + cs_sleepms(500); // we have to wait a bit so free_client is ended and socket closed too! + waitme -= 500; + } + } while(waitme || cl->pfd); + } + + rdr->client = NULL; + rdr->tcp_connected = 0; + rdr->card_status = UNKNOWN; + rdr->tcp_block_delay = 100; + cs_ftime(&rdr->tcp_block_connect_till); + + if(rdr->device[0] && is_cascading_reader(rdr)) + { + if(!rdr->ph.num) + { + rdr_log(rdr, "Protocol Support missing. (typ=%d)", rdr->typ); + return 0; + } + } + + if(!rdr->enable) + { return 0; } + + if(rdr->device[0]) + { + if(restart) + { + rdr_log(rdr, "Restarting reader"); + } + cl = create_client(first_client->ip); + if(cl == NULL) + { return 0; } + cl->reader = rdr; + rdr_log(rdr, "creating thread for device %s", rdr->device); + + cl->sidtabs.ok = rdr->sidtabs.ok; + cl->sidtabs.no = rdr->sidtabs.no; + cl->lb_sidtabs.ok = rdr->lb_sidtabs.ok; + cl->lb_sidtabs.no = rdr->lb_sidtabs.no; + cl->grp = rdr->grp; + + rdr->client = cl; + + cl->typ = 'r'; + + add_job(cl, ACTION_READER_INIT, NULL, 0); + add_reader_to_active(rdr); + + return 1; + } + return 0; +} + +/* Starts or restarts a cardreader with locking. If restart=1, the existing thread is killed before restarting, + if restart=0 the cardreader is only started. */ +int32_t restart_cardreader(struct s_reader *rdr, int32_t restart) +{ + cs_writelock(__func__, &system_lock); + int32_t result = restart_cardreader_int(rdr, restart); + cs_writeunlock(__func__, &system_lock); + return result; +} + +void init_cardreader(void) +{ + cs_log_dbg(D_TRACE, "cardreader: Initializing"); + cs_writelock(__func__, &system_lock); + struct s_reader *rdr; + + cardreader_init_locks(); + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->enable) + { + restart_cardreader_int(rdr, 0); + } + } + + load_stat_from_file(); + cs_writeunlock(__func__, &system_lock); +} + +void kill_all_readers(void) +{ + struct s_reader *rdr; + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + struct s_client *cl = rdr->client; + if(!cl) + { continue; } + rdr_log(rdr, "Killing reader"); + kill_thread(cl); +#ifdef CS_CACHEEX_AIO + ll_destroy_data(&cl->ll_cacheex_stats); +#endif + } + first_active_reader = NULL; +} + +int32_t reader_slots_available(struct s_reader *reader, ECM_REQUEST *er) +{ + if(ecm_ratelimit_check(reader, er, 0) != OK) // check ratelimiter & cooldown -> in check mode: dont register srvid!!! + { + return 0; // no slot free + } + else + { + return 1; // slots available! + } +} diff --git a/oscam-reader.h b/oscam-reader.h new file mode 100644 index 0000000..f798bb1 --- /dev/null +++ b/oscam-reader.h @@ -0,0 +1,33 @@ +#ifndef _OSCAM_READER_H_ +#define _OSCAM_READER_H_ + +const struct s_cardsystem *get_cardsystem_by_caid(uint16_t caid); +struct s_reader *get_reader_by_label(char *lbl); +const char *reader_get_type_desc(struct s_reader *rdr, int32_t extended); + +bool hexserialset(struct s_reader *rdr); +void hexserial_to_newcamd(uint8_t *source, uint8_t *dest, uint16_t caid); +void newcamd_to_hexserial(uint8_t *source, uint8_t *dest, uint16_t caid); + +S_ENTITLEMENT *cs_add_entitlement(struct s_reader *rdr, uint16_t caid, uint32_t provid, uint64_t id, uint32_t class, time_t start, time_t end, uint8_t type, uint8_t add); +void cs_clear_entitlement(struct s_reader *rdr); + +int32_t hostResolve(struct s_reader *reader); +int32_t network_tcp_connection_open(struct s_reader *); +void network_tcp_connection_close(struct s_reader *, char *); +void block_connect(struct s_reader *rdr); +int32_t is_connect_blocked(struct s_reader *rdr); + +void reader_do_idle(struct s_reader *reader); +void casc_check_dcw(struct s_reader *reader, int32_t idx, int32_t rc, uint8_t *cw); +void reader_do_card_info(struct s_reader *reader); +int32_t reader_slots_available(struct s_reader *reader, ECM_REQUEST *er); + +void cs_card_info(void); +int32_t reader_init(struct s_reader *reader); +void remove_reader_from_active(struct s_reader *rdr); +int32_t restart_cardreader(struct s_reader *rdr, int32_t restart); +void init_cardreader(void); +void kill_all_readers(void); + +#endif diff --git a/oscam-signing.c b/oscam-signing.c new file mode 100644 index 0000000..1240589 --- /dev/null +++ b/oscam-signing.c @@ -0,0 +1,565 @@ +#define MODULE_LOG_PREFIX "signing" + +#include "globals.h" +#include "oscam-signing.h" +#include "cscrypt/sha256.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-files.h" + +#ifndef CERT_ALGO_RSAENCRYPTION // warning about using OpenSSL versions before 1.0.0 with non RSA public key algorithm +#if OPENSSL_VERSION_NUMBER < 0x10000000L +#pragma message "WARNING: Due to lack of full support for elliptic curve signature algorithms in OpenSSL versions before 1.0.0, \ +make sure using RSA public key algorithm. Otherwise binary signature validation at runtime will not work!" +#endif +#endif + +extern char *config_cert; +struct o_sign_info osi; + +static char* _X509_NAME_oneline_utf8(X509_NAME *name) +{ + if (!name) + return NULL; + + BIO *bio_out = BIO_new(BIO_s_mem()); + if (!bio_out) + return NULL; + + // Ensure buffer is updated and readable + X509_NAME_print_ex(bio_out, name, 0, + (ASN1_STRFLGS_RFC2253 | XN_FLAG_SEP_COMMA_PLUS | + XN_FLAG_FN_SN | XN_FLAG_DUMP_UNKNOWN_FIELDS) & ~ASN1_STRFLGS_ESC_MSB); + + (void)BIO_flush(bio_out); + + BUF_MEM *bio_buf = NULL; + BIO_get_mem_ptr(bio_out, &bio_buf); + if (!bio_buf || !bio_buf->data || bio_buf->length == 0) + { + BIO_free(bio_out); + return NULL; + } + + char *line = (char *)malloc(bio_buf->length + 1); + if (!line) + { + BIO_free(bio_out); + return NULL; + } + + memcpy(line, bio_buf->data, bio_buf->length); + line[bio_buf->length] = '\0'; + + BIO_free(bio_out); + return line; +} + +static void hex_encode(const unsigned char *readbuf, void *writebuf, size_t len) +{ + char *out = (char *)writebuf; + size_t i; + + for (i = 0; i < len; i++) + { + /* 3 = two hex digits + null terminator (overwritten next iteration) */ + snprintf(out + (i * 2), 3, "%02x", readbuf[i]); + } + + out[len * 2] = '\0'; +} + +static time_t posix_time(unsigned int year, unsigned int month, unsigned int day, + unsigned int hour, unsigned int min, unsigned int sec) +{ + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || + hour > 23 || min > 59 || sec > 60) + { + return -1; + } + + // Cumulative days to month (non-leap years) + static const unsigned int month_day[13] = {0, 0, 31, 59, 90, 120, 151, 181, + 212, 243, 273, 304, 334}; + + unsigned int full_year = year; + year -= 1900; + + // Leap-year correction + bool is_leap = (full_year % 4 == 0 && (full_year % 100 != 0 || full_year % 400 == 0)); + unsigned int leap_correction = (is_leap && month > 2) ? 1 : 0; + + // Number of Februaries since 1900 + const unsigned int year_for_leap = (month > 2) ? year + 1 : year; + + return (time_t)( + sec + min * 60 + hour * 3600 + + (month_day[month] + day - 1 + leap_correction) * 86400 + + (year - 70) * 31536000 + + ((year_for_leap - 69) / 4) * 86400 - + ((year_for_leap - 1) / 100) * 86400 + + ((year_for_leap + 299) / 400) * 86400 + ); +} + +static unsigned int two_digits_to_uint(const char **s) { + unsigned int n = 10 * (**s - '0'); + (*s)++; + n += (**s - '0'); + (*s)++; + return n; +} + +static time_t ASN1_TIME_to_posix_time(const ASN1_TIME *t) { + if (!t) return -1; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L // changed in OpenSSL 1.1.0+ + const unsigned char *us = ASN1_STRING_get0_data((const ASN1_STRING*)t); + const char *s = (const char*)us; +#else + const char *s = (const char*)t->data; +#endif + if (!s) return -1; + + unsigned int year, month, day, hour, min, sec; + switch(t->type) // https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.5.1 + { + case V_ASN1_UTCTIME: // YYMMDDHHMMSSZ + year = two_digits_to_uint(&s); + year += year < 50 ? 2000 : 1900; + break; + case V_ASN1_GENERALIZEDTIME: // YYYYMMDDHHMMSSZ + year = 100 * two_digits_to_uint(&s); + year += two_digits_to_uint(&s); + break; + default: + return -1; // error + } + month = two_digits_to_uint(&s); + day = two_digits_to_uint(&s); + hour = two_digits_to_uint(&s); + min = two_digits_to_uint(&s); + sec = two_digits_to_uint(&s); + if (*s != 'Z') return -1; + if (year == 9999 && month == 12 && day == 31 && hour == 23 && min == 59 + && sec == 59) // 99991231235959Z rfc 5280 + { + return -1; + } + return posix_time(year, month, day, hour, min, sec); +} + +static void convert_ASN1TIME(const ASN1_TIME *t, char *buf, size_t len) { + struct tm timeinfo; + + time_t ct = ASN1_TIME_to_posix_time(t); + localtime_r(&ct, &timeinfo); + strftime(buf, len, "%d.%m.%Y %H:%M:%S", &timeinfo); +} + +static EVP_PKEY *verify_cert(void) +{ + int ret = 0; + char system_ca_file[MAX_LEN]; + char ptype[MAX_LEN]; + X509 *pCert = NULL; + BIO *pBio = NULL; + EVP_PKEY *pKey = NULL; + + // Add all digest algorithms to the table +#if OPENSSL_VERSION_NUMBER < 0x30000000L // no-op in OpenSSL 3.0+ + OpenSSL_add_all_algorithms(); +#endif + + pBio = BIO_new(BIO_s_mem()); + if (!pBio) + { + cs_log("Error: BIO_new() failed"); + return NULL; + } + + // Load built-in cert from memory into BIO object + if (!BIO_puts(pBio, config_cert)) + { + cs_log("Error: BIO_puts() failed"); + BIO_free(pBio); + return NULL; + } + + // Read cert in PEM format from BIO (allocates X509 internally) + pCert = PEM_read_bio_X509(pBio, NULL, NULL, NULL); + if (!pCert) + { + cs_log("Error: PEM_read_bio_X509() failed"); + BIO_free(pBio); + return NULL; + } + + // version + osi.cert_version = (int)X509_get_version(pCert) + 1; + + // valid from / to + ASN1_TIME *not_before = X509_get_notBefore(pCert); + convert_ASN1TIME(not_before, osi.cert_valid_from, sizeof(osi.cert_valid_from)); + ASN1_TIME *not_after = X509_get_notAfter(pCert); + convert_ASN1TIME(not_after, osi.cert_valid_to, sizeof(osi.cert_valid_to)); + + // expiration + osi.cert_is_expired = !(X509_cmp_current_time(not_before) < 0 && + X509_cmp_current_time(not_after) > 0); + + // serial number + ASN1_INTEGER *serial = X509_get_serialNumber(pCert); + BIGNUM *bn = ASN1_INTEGER_to_BN(serial, NULL); + char *hex = BN_bn2hex(bn); + osi.cert_serial = NULL; + if (cs_malloc(&osi.cert_serial, cs_strlen(hex) + 1)) + cs_strncpy(osi.cert_serial, strtolower(hex), cs_strlen(hex) + 1); + OPENSSL_free(hex); + BN_free(bn); + + // fingerprint (SHA1) + unsigned char buf[SHA_DIGEST_LENGTH]; + const EVP_MD *digest = EVP_sha1(); + unsigned len = 0; + ret = X509_digest(pCert, digest, buf, &len); + if (ret && len == SHA_DIGEST_LENGTH) + { + char strbuf[2 * SHA_DIGEST_LENGTH + 1]; + hex_encode(buf, strbuf, SHA_DIGEST_LENGTH); + osi.cert_fingerprint = NULL; + if (cs_malloc(&osi.cert_fingerprint, cs_strlen(strbuf) + 1)) + cs_strncpy(osi.cert_fingerprint, strtolower(strbuf), cs_strlen(strbuf) + 1); + } + + // subject + issuer + char *subj = _X509_NAME_oneline_utf8(X509_get_subject_name(pCert)); + char *issuer = _X509_NAME_oneline_utf8(X509_get_issuer_name(pCert)); + osi.cert_subject = NULL; + osi.cert_issuer = NULL; + if (cs_malloc(&osi.cert_subject, cs_strlen(subj) + 1)) + cs_strncpy(osi.cert_subject, subj, cs_strlen(subj) + 1); + if (cs_malloc(&osi.cert_issuer, cs_strlen(issuer) + 1)) + cs_strncpy(osi.cert_issuer, issuer, cs_strlen(issuer) + 1); + + // self-signed check + osi.cert_is_cacert = false; + if (strncmp(subj, issuer, cs_strlen(issuer)) != 0) + osi.cert_is_cacert = true; + free(subj); + free(issuer); + + // check provided certificate chain in built-in cert + osi.cert_is_valid_self = false; + X509_STORE *store_pem = X509_STORE_new(); + if (store_pem) + { + X509 *crt = NULL; + while ((crt = PEM_read_bio_X509(pBio, NULL, NULL, NULL))) + { + if (!X509_STORE_add_cert(store_pem, crt)) + cs_log("Error: X509_STORE_add_cert() failed"); + X509_free(crt); + } + + X509_STORE_CTX *ctx_store_pem = X509_STORE_CTX_new(); + if (ctx_store_pem) + { + if (X509_STORE_CTX_init(ctx_store_pem, store_pem, pCert, NULL)) + { + ret = X509_verify_cert(ctx_store_pem); + if (ret >= 0) + osi.cert_is_valid_self = ret; + } + X509_STORE_CTX_free(ctx_store_pem); + } + X509_STORE_free(store_pem); + } + + // check system ca-certificates.crt + osi.cert_is_valid_system = false; + osi.system_ca_file = NULL; + if (!osi.cert_is_valid_self) + { + X509_STORE *store_system = X509_STORE_new(); + if (store_system) + { + const char *ca_path; + snprintf(system_ca_file, sizeof(system_ca_file), "%s/%s", CA_SYSTEM_LOCATION, CA_FILE_NAME); + if (!file_exists(system_ca_file)) + { + if (!(ca_path = getenv(X509_get_default_cert_dir_env()))) + ca_path = X509_get_default_cert_dir(); + snprintf(system_ca_file, sizeof(system_ca_file), "%s/%s", ca_path, CA_FILE_NAME); + } + + if (cs_malloc(&osi.system_ca_file, cs_strlen(system_ca_file) + 1)) + cs_strncpy(osi.system_ca_file, system_ca_file, cs_strlen(system_ca_file) + 1); + + if (X509_STORE_load_locations(store_system, system_ca_file, NULL)) + { + X509_STORE_CTX *ctx_store_system = X509_STORE_CTX_new(); + if (ctx_store_system) + { + if (X509_STORE_CTX_init(ctx_store_system, store_system, pCert, NULL)) + { + ret = X509_verify_cert(ctx_store_system); + if (ret >= 0) + osi.cert_is_valid_system = ret; + } + X509_STORE_CTX_free(ctx_store_system); + } + } + else + { + cs_log("Error: X509_STORE_load_locations() failed. Unable to load CA certs from %s", system_ca_file); + } + X509_STORE_free(store_system); + } + } + + // extract public key + pKey = X509_get_pubkey(pCert); + osi.pkey_type = NULL; + if (pKey) + { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L // changed in OpenSSL 1.1.0+ + switch (EVP_PKEY_id(pKey)) +#else + switch (EVP_PKEY_type(pKey->type)) +#endif + { + case EVP_PKEY_RSA: + snprintf(ptype, sizeof(ptype), "%d bit RSA Key", EVP_PKEY_bits(pKey)); + break; + case EVP_PKEY_DSA: + snprintf(ptype, sizeof(ptype), "%d bit DSA Key", EVP_PKEY_bits(pKey)); + break; + case EVP_PKEY_EC: + snprintf(ptype, sizeof(ptype), "%d bit ECDSA Key", EVP_PKEY_bits(pKey)); + break; + default: + snprintf(ptype, sizeof(ptype), "%d bit non-RSA/DSA Key", EVP_PKEY_bits(pKey)); + break; + } + if (cs_malloc(&osi.pkey_type, cs_strlen(ptype) + 1)) + cs_strncpy(osi.pkey_type, ptype, cs_strlen(ptype) + 1); + } + else + { + cs_log("Error: X509_get_pubkey() failed"); + } + + BIO_free(pBio); + X509_free(pCert); + return pKey; +} + +static DIGEST hashBinary(const char *binfile, DIGEST *sign) +{ + DIGEST arRetval = {NULL, 0}; + struct stat *fi; + unsigned char *signature_enc; + size_t file_size = 0, offset = 0, end = 0, signature_size = 0; + unsigned char *data = NULL, *signature_start = NULL, *signature_end = NULL, *p = NULL; + + if (cs_malloc(&fi, sizeof(struct stat))) + { + if (!stat(binfile, fi)) + { + file_size = fi->st_size; + // Read binary into memory + int fd = open(binfile, O_RDONLY); + if (fd >= 0) + { + data = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (data != MAP_FAILED) + { + end = file_size; + + // Find last OBSM marker + p = data; + while ((p = memmem(p, file_size - offset, OBSM, cs_strlen(OBSM)))) + { + offset = p - data; + p += cs_strlen(OBSM); + signature_start = p; + } + + // Find next UPXM marker + p = signature_start ? memmem(signature_start, file_size - offset, UPXM, cs_strlen(UPXM)) : NULL; + if (p != NULL) + { + end = p - data; + signature_end = p; + } + else + { + signature_end = data + end; // default to EOF + } + + // Extract encrypted signature + if (offset > 0 && end > offset + cs_strlen(OBSM)) + { + signature_size = end - offset - cs_strlen(OBSM); + if (cs_malloc(&signature_enc, signature_size)) + { + memcpy(signature_enc, signature_start, signature_size); + sign->data = signature_enc; + sign->size = signature_size; + } + } + else + { + offset = file_size; + } + + // SHA256 hash binary (excluding signature) + mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + mbedtls_sha256_starts(&ctx, 0); + mbedtls_sha256_update(&ctx, data, offset); + mbedtls_sha256_update(&ctx, signature_end, file_size - end); + mbedtls_sha256_finish(&ctx, arRetval.data = (unsigned char *)OPENSSL_malloc(SHA256_DIGEST_LENGTH)); + arRetval.size = SHA256_DIGEST_LENGTH; + mbedtls_sha256_free(&ctx); + + munmap(data, file_size); + } + close(fd); + } + } + free(fi); + } + + return arRetval; +} + +static bool verifyBin(const char *binfile, EVP_PKEY *pubkey) +{ + int bResult = 0; + osi.is_verified = false; + osi.sign_digest_size = 0; + osi.hash_digest_size = 0; + osi.hash_size = 0; + osi.hash_sha256 = NULL; + EVP_MD_CTX *mctx = NULL; + DIGEST sign = {NULL, 0}; + + // Get binfile hash digest and encrypted signature + DIGEST hash = hashBinary(binfile, &sign); + osi.sign_digest_size = sign.size; + + if (hash.data) + { + char shaVal[2 * hash.size + 1]; + hex_encode(hash.data, shaVal, hash.size); + + osi.hash_digest_size = hash.size; + osi.hash_size = cs_strlen(shaVal); + + if (cs_malloc(&osi.hash_sha256, osi.hash_size + 1)) + { + cs_strncpy(osi.hash_sha256, strtolower(shaVal), osi.hash_size + 1); + } + OPENSSL_free(hash.data); + + if (pubkey && sign.data) + { + // Create message digest context +#if OPENSSL_VERSION_NUMBER >= 0x10100000L // changed in OpenSSL 1.1.0+ + mctx = EVP_MD_CTX_new(); +#else + mctx = EVP_MD_CTX_create(); +#endif + if (!mctx) + { + cs_log("Error: EVP_MD_CTX allocation failed"); + } + else + { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L // changed in OpenSSL 1.1.0+ + EVP_PKEY_CTX *pkctx = NULL; + if (EVP_DigestVerifyInit(mctx, &pkctx, EVP_sha256(), NULL, pubkey) <= 0) + { + cs_log("Error: EVP_DigestVerifyInit() failed"); + } + else if (EVP_DigestVerifyUpdate(mctx, shaVal, cs_strlen(shaVal)) <= 0) + { + cs_log("Error: EVP_DigestVerifyUpdate() failed"); + } + else + { + bResult = (EVP_DigestVerifyFinal(mctx, sign.data, sign.size) == 1); + osi.is_verified = (bResult == 1); + } +#else + if (!EVP_VerifyInit(mctx, EVP_sha256())) + { + cs_log("Error: EVP_VerifyInit() failed"); + } + else if (!EVP_VerifyUpdate(mctx, shaVal, cs_strlen(shaVal))) + { + cs_log("Error: EVP_VerifyUpdate() failed"); + } + else + { + bResult = EVP_VerifyFinal(mctx, sign.data, sign.size, pubkey); + osi.is_verified = (bResult == 1); + } +#endif + } + } + } + + if (sign.data) free(sign.data); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L // changed in OpenSSL 1.1.0+ + EVP_MD_CTX_free(mctx); +#else + EVP_MD_CTX_destroy(mctx); +#endif + return (bResult == 1); +} + +bool init_signing_info(const char *binfile) +{ + EVP_PKEY *pubkey = NULL; + memset(&osi, 0, sizeof(struct o_sign_info)); + + // verify signing certificate and extract public key + pubkey = verify_cert(); + + // resolve binfile in PATH + char *tmp = find_in_path(binfile); + osi.binfile_exists = (tmp != NULL); + snprintf(osi.resolved_binfile, sizeof(osi.resolved_binfile), "%s", tmp ? tmp : binfile); + + cs_log ("Binary = %s file %s%s", + (osi.binfile_exists ? "Verifying" : "Unable to access"), + osi.resolved_binfile, + (osi.binfile_exists ? "..." : "!")); + + // verify binfile using public key + bool ret = verifyBin(osi.resolved_binfile, pubkey); + + cs_log ("Signature = %s", (ret ? "Valid - Binary's signature was successfully verified using the built-in Public Key" + : "Error: Binary's signature is invalid! Shutting down...")); + + if (pubkey) + { + cs_log("Certificate = %s %s Certificate, %s %s", + ((osi.cert_is_valid_self || osi.cert_is_valid_system) ? "Trusted" : "Untrusted"), + (osi.cert_is_cacert ? "CA" : "Self Signed"), + (osi.cert_is_expired ? "expired since" : "valid until"), + osi.cert_valid_to); + } + else + { + cs_log("Certificate = Error: Built-in Public Key could not be extracted!"); + } + + if (tmp) free(tmp); + EVP_PKEY_free(pubkey); + + return ret; +} diff --git a/oscam-signing.h b/oscam-signing.h new file mode 100644 index 0000000..7bd6b5e --- /dev/null +++ b/oscam-signing.h @@ -0,0 +1,56 @@ +#ifndef OSCAM_SIGNING_H_ +#define OSCAM_SIGNING_H_ + +#ifdef WITH_SSL +#include +#include +#endif + +#define OBSM "!OBSM!" //Oscam Binary Signature Marker +#define UPXM "UPX!" //UPX Marker + +//System Certificates Trust Store location and filename +#if defined(__APPLE__) +#define CA_SYSTEM_LOCATION "/usr/local/share/ca-certificates" +#define CA_FILE_NAME "cacert.pem" +#elif defined(__FreeBSD__) || defined(__OpenBSD__) +#define CA_SYSTEM_LOCATION "/usr/local/share/certs" +#define CA_FILE_NAME "ca-root-nss.crt" +#else +#define CA_SYSTEM_LOCATION "/etc/ssl/certs" +#define CA_FILE_NAME "ca-certificates.crt" +#endif + +typedef struct { + unsigned char *data; + size_t size; +} DIGEST; + +struct o_sign_info +{ + bool is_verified; + int cert_version; + char cert_valid_from[40]; + char cert_valid_to[40]; + bool cert_is_expired; + char *cert_serial; + char *cert_fingerprint; + char *cert_subject; + char *cert_issuer; + bool cert_is_cacert; + bool cert_is_valid_self; + bool cert_is_valid_system; + char *system_ca_file; + char *pkey_type; + int sign_digest_size; + int hash_digest_size; + int hash_size; + char *hash_sha256; + bool binfile_exists; + char resolved_binfile[512]; +}; + +extern struct o_sign_info osi; +bool init_signing_info(const char *binfile); + +#endif diff --git a/oscam-simples.c b/oscam-simples.c new file mode 100644 index 0000000..6e1c713 --- /dev/null +++ b/oscam-simples.c @@ -0,0 +1,484 @@ +#include "globals.h" +#include "oscam-string.h" + +static void cl_set_last_providptr(struct s_client *cl, uint32_t provid, uint16_t caid) +{ + int32_t i; + struct s_provid *this = cfg.provid; + struct s_provid *zero_match = NULL; + + cl->last_providptr = NULL; + + if(!caid) + { + return; + } + + for(; this; this = this->next) + { + if(this->caid == caid) + { + if(this->nprovid == 0 ) + { + zero_match = this; + } + + for(i = 0; i < this->nprovid; i++) + { + if(this->provid[i] == 0 ) + { + zero_match = this; + } + + if(this->provid[i] == provid) + { + cl->last_providptr = this; + return; + } + } + } + } + + if(zero_match != NULL) + { + cl->last_providptr = zero_match; + } +} + +/* Gets the servicename. */ +static char *__get_servicename(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen, bool return_unknown) +{ + int32_t i, j; + struct s_srvid *this, *provid_zero_match = NULL, *provid_any_match = NULL; + buf[0] = '\0'; + + if(!srvid || (srvid >> 12) >= 16) // cfg.srvid[16] + { + return (buf); + } + + if(cl && cl->last_srvidptr && cl->last_srvidptr->srvid == srvid) + { + for(i = 0; i < cl->last_srvidptr->ncaid; i++) + { + if(cl->last_srvidptr->caid[i].caid == caid && cl->last_srvidptr_search_provid == provid && cl->last_srvidptr->name) + { + if(cl->last_providptr == NULL) + { + cl_set_last_providptr(cl, provid, caid); + } + cs_strncpy(buf, cl->last_srvidptr->name, buflen); + return (buf); + } + } + } + + for(this = cfg.srvid[srvid >> 12]; this; this = this->next) + { + if(this->srvid == srvid) + { + for(i = 0; i < this->ncaid; i++) + { + if(this->caid[i].caid == caid && this->name) + { + provid_any_match = this; + if(this->caid[i].nprovid == 0) + { + provid_zero_match = this; + + if(0 == provid) + { + if(cl) + { + cl_set_last_providptr(cl, provid, caid); + cl->last_srvidptr = this; + cl->last_srvidptr_search_provid = provid; + } + cs_strncpy(buf, this->name, buflen); + return (buf); + } + } + + for(j = 0; j < this->caid[i].nprovid; j++) + { + if(this->caid[i].provid[j] == 0) + { provid_zero_match = this; } + + if(this->caid[i].provid[j] == provid) + { + if(cl) + { + cl_set_last_providptr(cl, provid, caid); + cl->last_srvidptr = this; + cl->last_srvidptr_search_provid = provid; + } + cs_strncpy(buf, this->name, buflen); + return (buf); + } + } + } + } + } + } + + if(!buf[0]) + { + if(provid != 0 && provid_zero_match != NULL) + { + if(cl) + { + cl_set_last_providptr(cl, provid, caid); + cl->last_srvidptr = provid_zero_match; + cl->last_srvidptr_search_provid = provid; + } + cs_strncpy(buf, provid_zero_match->name, buflen); + return (buf); + } + else if(provid == 0 && provid_any_match != NULL) + { + if(cl) + { + cl_set_last_providptr(cl, provid, caid); + cl->last_srvidptr = provid_any_match; + cl->last_srvidptr_search_provid = provid; + } + cs_strncpy(buf, provid_any_match->name, buflen); + return (buf); + } + + if(return_unknown) + { + snprintf(buf, buflen, "%04X@%06X:%04X unknown", caid, provid, srvid); + } + + if(cl) + { + cl->last_providptr = NULL; + cl->last_srvidptr = NULL; + cl->last_srvidptr_search_provid = provid; + } + } + return (buf); +} + +char *get_servicename(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen) +{ + return __get_servicename(cl, srvid, provid, caid, buf, buflen, true); +} + +char *get_servicename_or_null(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen) +{ + return __get_servicename(cl, srvid, provid, caid, buf, buflen, false); +} + +char *get_picon_servicename_or_null(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen) +{ + uint32_t i, j; + __get_servicename(cl, srvid, provid, caid, buf, buflen, false); + char *tmp_buf; + + if(buf[0]) + { + if(!cs_malloc(&tmp_buf, buflen)) + { + buf[0] = '\0'; + return (buf); + } + j = 0; + + for(i = 0; i < buflen && buf[i] && j + 4 < buflen; i++) + { + if(isalnum((int)buf[i])) + { + tmp_buf[j] = (char)tolower((int)buf[i]); + j++; + } + else if(buf[i] == '*') + { + tmp_buf[j] = 's'; + tmp_buf[j + 1] = 't'; + tmp_buf[j + 2] = 'a'; + tmp_buf[j + 3] = 'r'; + j += 4; + } + else if(buf[i] == '+') + { + tmp_buf[j] = 'p'; + tmp_buf[j + 1] = 'l'; + tmp_buf[j + 2] = 'u'; + tmp_buf[j + 3] = 's'; + j += 4; + } + else if(buf[i] == '&') + { + tmp_buf[j] = 'a'; + tmp_buf[j + 1] = 'n'; + tmp_buf[j + 2] = 'd'; + j += 3; + } + } + + tmp_buf[buflen - 1] = '\0'; + cs_strncpy(buf, tmp_buf, buflen); + NULLFREE(tmp_buf); + } + + return (buf); +} + +int32_t picon_servicename_remve_hd(char *buf, uint32_t UNUSED(buflen)) +{ + int32_t l = cs_strlen(buf); + + if(l < 3) + { + return 0; + } + + if(buf[l - 2] == 'h' && buf[l - 1] == 'd') + { + buf[l - 2] = '\0'; + buf[l - 1] = '\0'; + return 1; + } + + return 0; +} + +/* Gets the tier name. Make sure that buf is at least 83 bytes long. */ +char *get_tiername(uint16_t tierid, uint16_t caid, char *buf) +{ + uint8_t found = 0; + int32_t i; + struct s_tierid *this = cfg.tierid; + + for(buf[0] = 0; this && !found; this = this->next) + { + if(this->tierid == tierid) + { + for(i = 0; i < this->ncaid && !found; i++) + { + if(this->caid[i] == caid) + { + cs_strncpy(buf, this->name, 32); found = 1; + } + } + } + } + + if(!tierid) + { + buf[0] = '\0'; + } + + return (buf); +} + +/* Gets the tier name. Make sure that buf is at least 83 bytes long. */ +char *get_tiername_defaultid(uint16_t tierid, uint16_t caid, char *buf) +{ + uint8_t found = 0; + int32_t i; + struct s_tierid *this = cfg.tierid; + + for(buf[0] = 0; this && !found; this = this->next) + { + if(this->tierid == tierid) + { + for(i = 0; i < this->ncaid && !found; i++) + { + if(this->caid[i] == caid) + { + cs_strncpy(buf, this->name, 32); found = 1; + } + } + } + } + + if(!tierid) + { + snprintf(buf, 82, "%04X", tierid); + } + + return (buf); +} + +/* Gets the provider name. */ +char *get_provider(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen) +{ + uint8_t found = 0; + int32_t i; + struct s_provid *this = cfg.provid; + + if(!caid) + { + buf[0] = '\0'; + return (buf); + } + + for(buf[0] = 0; this && !found; this = this->next) + { + if(this->caid == caid) + { + for(i = 0; i < this->nprovid && !found; i++) + { + if(this->provid[i] == provid) + { + snprintf(buf, buflen, "%s%s%s%s%s", + this->prov, + *this->sat && this->sat[0] ? " / " : "", + this->sat, this->lang[0] ? " / " : "", + this->lang); + found = 1; + } + } + } + } + + if(!buf[0]) + { + snprintf(buf, buflen, "%04X@%06X unknown", caid, provid); + } + return (buf); +} + +char *__get_providername(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen, bool return_unknown) +{ + uint8_t found = 0; + int32_t i; + struct s_provid *this = cfg.provid; + struct s_provid *zero_match = NULL; + + if(!caid) + { + buf[0] = '\0'; + return (buf); + } + + for(buf[0] = 0; this && !found; this = this->next) + { + if(this->caid == caid) + { + if(this->nprovid == 0 ) + { + zero_match = this; + } + + for(i = 0; i < this->nprovid && !found; i++) + { + if(this->provid[i] == 0 ) + { + zero_match = this; + } + + if(this->provid[i] == provid) + { + cs_strncpy(buf, this->prov, buflen); + found = 1; + } + } + } + } + + if(!found && zero_match != NULL) + { + cs_strncpy(buf, zero_match->prov, buflen); + } + + if(!buf[0] && return_unknown) + { + snprintf(buf, buflen, "%04X@%06X unknown", caid, provid); + } + + return (buf); +} + +char *get_providername(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen) +{ + return __get_providername(provid, caid, buf, buflen, true); +} + +char *get_providername_or_null(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen) +{ + return __get_providername(provid, caid, buf, buflen, false); +} + +const char *get_cl_lastprovidername(struct s_client *cl) +{ + if(!cl->last_srvidptr || !cl->last_srvidptr->prov || cl->last_srvidptr->prov[0] == '\0' || !strcmp(cl->last_srvidptr->prov, " ")) + { + if(!cl->last_providptr) + { + return ""; + } + else + { + return cl->last_providptr->prov; + } + } + return cl->last_srvidptr->prov; +} + +// Add provider description. If provider was already present, do nothing. +void add_provider(uint16_t caid, uint32_t provid, const char *name, const char *sat, const char *lang) +{ + int32_t i; + struct s_provid **ptr; + + for(ptr = &cfg.provid; *ptr; ptr = &(*ptr)->next) + { + if((*ptr)->caid == caid) + { + for(i = 0; i < (*ptr)->nprovid; i++) + { + if((*ptr)->provid[i] == provid) + { + return; + } + } + } + } + + struct s_provid *prov; + if(!cs_malloc(&prov, sizeof(struct s_provid))) + { + return; + } + + if(!cs_malloc(&prov->provid, sizeof(uint32_t))) + { + NULLFREE(prov); + return; + } + + prov->nprovid = 1; + prov->provid[0] = provid; + prov->caid = caid; + cs_strncpy(prov->prov, name, sizeof(prov->prov)); + cs_strncpy(prov->sat, sat, sizeof(prov->sat)); + cs_strncpy(prov->lang, lang, sizeof(prov->lang)); + *ptr = prov; +} + +// Get a cardsystem name based on caid +// used in webif/CCcam share and in dvbapi/ecminfo +const char *get_cardsystem_desc_by_caid(uint16_t caid) +{ + if(caid_is_seca(caid)) { return "seca"; } + if(caid_is_viaccess(caid)) { return "viaccess"; } + if(caid_is_irdeto(caid)) { return "irdeto"; } + if(caid_is_videoguard(caid)) { return "videoguard"; } + if(caid_is_powervu(caid)) { return "powervu"; } + if(caid_is_director(caid)) { return "director"; } + if(caid_is_conax(caid)) { return "conax"; } + if(caid_is_cryptoworks(caid)) { return "cryptoworks"; } + if(caid_is_betacrypt(caid)) { return "betacrypt"; } + if(caid_is_nagra(caid)) { return "nagra"; } + if(caid_is_tongfang(caid)) { return "tongfang"; } + if(caid >= 0x5501 && caid <= 0x551A) { return "griffin"; } + if(caid_is_dre(caid)) { return "drecrypt"; } + if(caid_is_bulcrypt(caid)) { return "bulcrypt"; } + if(caid_is_biss(caid)) { return "biss"; } + if(caid == 0x4ABF) { return "dgcrypt"; } + return "unknown"; +} diff --git a/oscam-string-isotables.h b/oscam-string-isotables.h new file mode 100644 index 0000000..2810a8e --- /dev/null +++ b/oscam-string-isotables.h @@ -0,0 +1,18 @@ + +static uint16_t iso_8859_to_unicode[14][95] = +{ + { 0x0104, 0x02d8, 0x0141, 0x00a4, 0x013d, 0x015a, 0x00a7, 0x00a8, 0x0160, 0x015e, 0x0164, 0x0179, 0x00ad, 0x017d, 0x017b, 0x00b0, 0x0105, 0x02db, 0x0142, 0x00b4, 0x013e, 0x015b, 0x02c7, 0x00b8, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, }, + { 0x0126, 0x02d8, 0x00a3, 0x00a4, 0x0000, 0x0124, 0x00a7, 0x00a8, 0x0130, 0x015e, 0x011e, 0x0134, 0x00ad, 0x0000, 0x017b, 0x00b0, 0x0127, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x0125, 0x00b7, 0x00b8, 0x0131, 0x015f, 0x011f, 0x0135, 0x00bd, 0x0000, 0x017c, 0x00c0, 0x00c1, 0x00c2, 0x0000, 0x00c4, 0x010a, 0x0108, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x0000, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x0120, 0x00d6, 0x00d7, 0x011c, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x016c, 0x015c, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x0000, 0x00e4, 0x010b, 0x0109, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x0000, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x0121, 0x00f6, 0x00f7, 0x011d, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x016d, 0x015d, 0x02d9, }, + { 0x0104, 0x0138, 0x0156, 0x00a4, 0x0128, 0x013b, 0x00a7, 0x00a8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00ad, 0x017d, 0x00af, 0x00b0, 0x0105, 0x02db, 0x0157, 0x00b4, 0x0129, 0x013c, 0x02c7, 0x00b8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014a, 0x017e, 0x014b, 0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e, 0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x012a, 0x0110, 0x0145, 0x014c, 0x0136, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x0168, 0x016a, 0x00df, 0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f, 0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x012b, 0x0111, 0x0146, 0x014d, 0x0137, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x0169, 0x016b, 0x02d9, }, + { 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x00ad, 0x040e, 0x040f, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x00a7, 0x045e, 0x045f, }, + { 0x0000, 0x0000, 0x0000, 0x00a4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x060c, 0x00ad, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x061b, 0x0000, 0x0000, 0x0000, 0x061f, 0x0000, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062a, 0x062b, 0x062c, 0x062d, 0x062e, 0x062f, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064a, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f, 0x0650, 0x0651, 0x0652, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }, + { 0x2018, 0x2019, 0x00a3, 0x0000, 0x0000, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x0000, 0x00ab, 0x00ac, 0x00ad, 0x0000, 0x2015, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x0384, 0x0385, 0x0386, 0x00b7, 0x0388, 0x0389, 0x038a, 0x00bb, 0x038c, 0x00bd, 0x038e, 0x038f, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, 0x03a0, 0x03a1, 0x0000, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, 0x0000, }, + { 0x0000, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00d7, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00f7, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2017, 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df, 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, 0x0000, 0x0000, 0x200e, 0x200f, 0x0000, }, + { 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x011e, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0130, 0x015e, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x011f, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0131, 0x015f, 0x00ff, }, + { 0x0104, 0x0112, 0x0122, 0x012a, 0x0128, 0x0136, 0x00a7, 0x013b, 0x0110, 0x0160, 0x0166, 0x017d, 0x00ad, 0x016a, 0x014a, 0x00b0, 0x0105, 0x0113, 0x0123, 0x012b, 0x0129, 0x0137, 0x00b7, 0x013c, 0x0111, 0x0161, 0x0167, 0x017e, 0x2015, 0x016b, 0x014b, 0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e, 0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x0145, 0x014c, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x0168, 0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f, 0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x0146, 0x014d, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x0169, 0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x0138, }, + { 0x0e01, 0x0e02, 0x0e03, 0x0e04, 0x0e05, 0x0e06, 0x0e07, 0x0e08, 0x0e09, 0x0e0a, 0x0e0b, 0x0e0c, 0x0e0d, 0x0e0e, 0x0e0f, 0x0e10, 0x0e11, 0x0e12, 0x0e13, 0x0e14, 0x0e15, 0x0e16, 0x0e17, 0x0e18, 0x0e19, 0x0e1a, 0x0e1b, 0x0e1c, 0x0e1d, 0x0e1e, 0x0e1f, 0x0e20, 0x0e21, 0x0e22, 0x0e23, 0x0e24, 0x0e25, 0x0e26, 0x0e27, 0x0e28, 0x0e29, 0x0e2a, 0x0e2b, 0x0e2c, 0x0e2d, 0x0e2e, 0x0e2f, 0x0e30, 0x0e31, 0x0e32, 0x0e33, 0x0e34, 0x0e35, 0x0e36, 0x0e37, 0x0e38, 0x0e39, 0x0e3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0e3f, 0x0e40, 0x0e41, 0x0e42, 0x0e43, 0x0e44, 0x0e45, 0x0e46, 0x0e47, 0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x0e4d, 0x0e4e, 0x0e4f, 0x0e50, 0x0e51, 0x0e52, 0x0e53, 0x0e54, 0x0e55, 0x0e56, 0x0e57, 0x0e58, 0x0e59, 0x0e5a, 0x0e5b, 0x0000, 0x0000, 0x0000, 0x0000, }, + { 0x201d, 0x00a2, 0x00a3, 0x00a4, 0x201e, 0x00a6, 0x00a7, 0x00d8, 0x00a9, 0x0156, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00c6, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x201c, 0x00b5, 0x00b6, 0x00b7, 0x00f8, 0x00b9, 0x0157, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00e6, 0x0104, 0x012e, 0x0100, 0x0106, 0x00c4, 0x00c5, 0x0118, 0x0112, 0x010c, 0x00c9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012a, 0x013b, 0x0160, 0x0143, 0x0145, 0x00d3, 0x014c, 0x00d5, 0x00d6, 0x00d7, 0x0172, 0x0141, 0x015a, 0x016a, 0x00dc, 0x017b, 0x017d, 0x00df, 0x0105, 0x012f, 0x0101, 0x0107, 0x00e4, 0x00e5, 0x0119, 0x0113, 0x010d, 0x00e9, 0x017a, 0x0117, 0x0123, 0x0137, 0x012b, 0x013c, 0x0161, 0x0144, 0x0146, 0x00f3, 0x014d, 0x00f5, 0x00f6, 0x00f7, 0x0173, 0x0142, 0x015b, 0x016b, 0x00fc, 0x017c, 0x017e, 0x2019, }, + { 0x1e02, 0x1e03, 0x00a3, 0x010a, 0x010b, 0x1e0a, 0x00a7, 0x1e80, 0x00a9, 0x1e82, 0x1e0b, 0x1ef2, 0x00ad, 0x00ae, 0x0178, 0x1e1e, 0x1e1f, 0x0120, 0x0121, 0x1e40, 0x1e41, 0x00b6, 0x1e56, 0x1e81, 0x1e57, 0x1e83, 0x1e60, 0x1ef3, 0x1e84, 0x1e85, 0x1e61, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x0174, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x1e6a, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x0176, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x0175, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x1e6b, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x0177, 0x00ff, }, + { 0x00a1, 0x00a2, 0x00a3, 0x20ac, 0x00a5, 0x0160, 0x00a7, 0x0161, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x017d, 0x00b5, 0x00b6, 0x00b7, 0x017e, 0x00b9, 0x00ba, 0x00bb, 0x0152, 0x0153, 0x0178, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff, }, + { 0x0104, 0x0105, 0x0141, 0x20ac, 0x201e, 0x0160, 0x00a7, 0x0161, 0x00a9, 0x0218, 0x00ab, 0x0179, 0x00ad, 0x017a, 0x017b, 0x00b0, 0x00b1, 0x010c, 0x0142, 0x017d, 0x201d, 0x00b6, 0x00b7, 0x017e, 0x010d, 0x0219, 0x00bb, 0x0152, 0x0153, 0x0178, 0x017c, 0x00c0, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0106, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x0110, 0x0143, 0x00d2, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x015a, 0x0170, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0118, 0x021a, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x0107, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x0111, 0x0144, 0x00f2, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x015b, 0x0171, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0119, 0x021b, 0x00ff, }, +}; diff --git a/oscam-string.c b/oscam-string.c new file mode 100644 index 0000000..16329e2 --- /dev/null +++ b/oscam-string.c @@ -0,0 +1,1470 @@ +#include "globals.h" +#include "oscam-string.h" + +/* This function encapsulates malloc. It automatically adds an error message + to the log if it failed and calls cs_exit(quiterror) if quiterror > -1. + result will be automatically filled with the new memory position or NULL + on failure. */ +bool cs_malloc(void *result, size_t size) +{ + void **tmp = result; + *tmp = malloc(size); + + if(*tmp == NULL) + { + fprintf(stderr, "%s: ERROR: Can't allocate %zu bytes!", __func__, size); + } + else + { + memset(*tmp, 0, size); + } + return !!*tmp; +} + +/* This function encapsulates realloc. It automatically adds an error message + to the log if it failed and calls cs_exit(quiterror) if quiterror > -1. + result will be automatically filled with the new memory position or NULL + on failure. If a failure occured, the existing memory in result will + be freed. */ +bool cs_realloc(void *result, size_t size) +{ + void **tmp = result, **tmp2 = result; + *tmp = realloc(*tmp, size); + + if(*tmp == NULL) + { + fprintf(stderr, "%s: ERROR: Can't allocate %zu bytes!", __func__, size); + NULLFREE(*tmp2); + } + return !!*tmp; +} + +/* strlen is wrongly used in oscam. Calling strlen directly + * e.g. strlen(somechar) can cause segmentation fault if pointer to somechar is NULL! + * This one looks better? + */ +size_t cs_strlen(const char *c) +{ + return c ? strlen(c) : 0; +} + +/* Allocates a new empty string and copies str into it. You need to free() the result. */ +char *cs_strdup(const char *str) +{ + char *newstr; + if(!str) + { return NULL; } + + if(cs_malloc(&newstr, cs_strlen(str) + 1)) + { + cs_strncpy(newstr, str, cs_strlen(str) + 1); + return newstr; + } + return NULL; +} + +/* Ordinary strncpy does not terminate the string if the source is exactly + as long or longer as the specified size. This can raise security issues. + This function is a replacement which makes sure that a \0 is always added. + num should be the real size of char array (do not subtract -1). */ +void cs_strncpy(char *destination, const char *source, size_t num) +{ + if(!source) + { + destination[0] = '\0'; + return; + } + + uint32_t l, size = cs_strlen(source); + if(size > num - 1) + { l = num - 1; } + else + { l = size; } + + memcpy(destination, source, l); + destination[l] = '\0'; +} + +bool cs_strncat(char *destination, char *source, size_t destination_size) +{ + uint32_t dest_sz = 0; + uint32_t source_sz = 0; + + if (!destination_size) + { + cs_log("ERROR, destination_size 0!"); + return false; + } + + if (destination) + { + dest_sz += cs_strlen(destination); + } + else + { + cs_log("ERROR, destination pointer NULL!"); + return false; + } + + if (source) + { + source_sz += cs_strlen(source); + } + else + { + cs_log("ERROR, source pointer NULL!"); + return false; + } + + if ((dest_sz + source_sz) == 0) + { + cs_log("ERROR, booth destination and source with zero size!"); + return false; + } + + if ((dest_sz + source_sz) < destination_size) + { + if (dest_sz) + { + void *dest = (void *)destination; + memcpy(destination, dest, dest_sz); + } + + if (source_sz) + memcpy(destination + dest_sz, source, source_sz); + + destination[dest_sz + source_sz] = '\0'; + } + else + { + cs_log("ERROR, buffer overflow!"); + return false; + } + + return true; +} + +/* Converts the string txt to it's lower case representation. */ +char *strtolower(char *txt) +{ + char *p; + for(p = txt; *p; p++) + { + *p = tolower((uint8_t) *p); + } + return txt; +} + +/* Converts the string txt to it's upper case representation. */ +char *strtoupper(char *txt) +{ + char *p; + for(p = txt; *p; p++) + { + *p = toupper((uint8_t) *p); + } + return txt; +} + +char *trim(char *txt) +{ + int32_t l; + char *p1, *p2; + + if(*txt == ' ') + { + for(p1 = p2 = txt; (*p1 == ' ') || (*p1 == '\t') || (*p1 == '\n') || (*p1 == '\r'); p1++) + { ; } + + while(*p1) + { *p2++ = *p1++; } + + *p2 = '\0'; + } + + l = cs_strlen(txt); + if(l > 0) + { + for(p1 = txt + l - 1; l > 0 && ((*p1 == ' ') || (*p1 == '\t') || (*p1 == '\n') || (*p1 == '\r')); *p1-- = '\0', l--) + { ; } + } + return txt; +} + +char *trim2(char *txt) +{ + int32_t i, n; + + for(i = n = 0; i < (int32_t)cs_strlen(txt); i++) + { + if(txt[i] == ' ' || txt[i] == '\t') continue; + if(txt[i] == '#') {break;} + txt[n] = txt[i]; + n++; + } + txt[n] = '\0'; + + return txt; +} + +char *remove_white_chars(char *txt) +{ + char *p1 = txt, *p2 = txt; + + if(NULL != p1) + { + while('\0' != *p1) + { + if((' ' != *p1) && ('\t' != *p1) && ('\n' != *p1) && ('\r' != *p1)) + { + *p2++ = *p1; + } + p1++; + } + *p2 = '\0'; + } + return txt; +} + +/* Allocates a new empty string and copies txt into it, + * then replace all newline occurances with whitspace. + * You need to free() the result. */ +char *remove_newline_chars(const char *txt) +{ + if(!txt) + { return NULL; } + + char *result = cs_strdup(txt), *p = NULL; + + if(!result) + { return NULL; } + for (p = result; *p; p++) *p = *p == '\r' ? ' ' : *p == '\n' ? ' ' : *p; + + return result; +} + +bool streq(const char *s1, const char *s2) +{ + if(!s1 && s2) { return 0; } + if(s1 && !s2) { return 0; } + if(!s1 && !s2) { return 1; } + return strcmp(s1, s2) == 0; +} + +char *cs_hexdump(int32_t m, const uint8_t *buf, int32_t n, char *target, int32_t len) +{ + int32_t i = 0; + if(target == NULL || buf == NULL) + { + return NULL; + } + target[0] = '\0'; + m = m ? 3 : 2; + + if(m * n >= len) + { + n = (len / m) - 1; + } + + while(i < n) + { + snprintf(target + (m * i), len - (m * i), "%02X%s", *buf++, m > 2 ? " " : ""); + i++; + } + return target; +} + +/* + * For using gethexval we must check if char c is within range othervise gethexval return + * negative value so we must ensure we not shift left those negative value! So before + * using gethexval function you need to check char c with function gethexval_within_range to + * ensure char c is within accepted range! + */ +bool gethexval_within_range(char c) +{ + if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) + { + return true; + } + return false; +} + +int32_t gethexval(char c) +{ + if(c >= '0' && c <= '9') { return c - '0'; } + if(c >= 'A' && c <= 'F') { return c - 'A' + 10; } + if(c >= 'a' && c <= 'f') { return c - 'a' + 10; } + return -1; +} + +int32_t cs_atob(uint8_t *buf, char *asc, int32_t n) +{ + int32_t i, rc; + if(buf == NULL || asc == NULL) + { + return -1; + } + for(i = 0; i < n; i++) + { + if(!gethexval_within_range(asc[i << 1]) || !gethexval_within_range(asc[(i << 1) + 1])) + { + return -1; + } + rc = (gethexval(asc[i << 1]) << 4) | gethexval(asc[(i << 1) + 1]); + if(rc & 0x100) + { + return -1; + } + buf[i] = rc; + } + return n; +} + +uint32_t cs_atoi(char *asc, int32_t l, int32_t val_on_err) +{ + int32_t i, n = 0; + uint32_t rc = 0; + if(asc == NULL) + { + errno = EINVAL; + rc = val_on_err ? 0xFFFFFFFF : 0; + return rc; + } + for(i = ((l - 1) << 1), errno = 0; i >= 0 && n < 4; i -= 2) + { + if(!gethexval_within_range(asc[i]) || !gethexval_within_range(asc[i + 1])) + { + errno = EINVAL; + rc = val_on_err ? 0xFFFFFFFF : 0; + break; + } + int32_t b = (gethexval(asc[i]) << 4) | gethexval(asc[i + 1]); + if(b < 0) + { + errno = EINVAL; + rc = val_on_err ? 0xFFFFFFFF : 0; + break; + } + rc |= b << (n << 3); + n++; + } + return rc; +} + +int32_t byte_atob(char *asc) +{ + int32_t rc = (-1); + if(asc == NULL) + { + return rc; + } + if(cs_strlen(trim(asc)) != 2) + { + rc = -1; + } + else + { + if(!gethexval_within_range(asc[0]) || !gethexval_within_range(asc[1])) + { + return rc; + } + else + { + rc = (gethexval(asc[0]) << 4) | gethexval(asc[1]); + if(rc & 0x100) + { + rc = -1; + } + } + } + return rc; +} + +int32_t word_atob(char *asc) +{ + int32_t rc = (-1); + + if(asc == NULL) + { + return rc; + } + + if(cs_strlen(trim(asc)) != 4) + { + rc = -1; + } + else + { + if(!gethexval_within_range(asc[0]) || !gethexval_within_range(asc[1]) || !gethexval_within_range(asc[2]) || !gethexval_within_range(asc[3])) + { + return rc; + } + else + { + rc = gethexval(asc[0]) << 12 | gethexval(asc[1]) << 8 | gethexval(asc[2]) << 4 | gethexval(asc[3]); + if(rc & 0x10000) + { + rc = -1; + } + } + } + return rc; +} + +/* + * dynamic word_atob + * converts an 1-6 digit asc hexstring + */ +int32_t dyn_word_atob(char *asc) +{ + int32_t rc = (-1); + int32_t i, len; + + if(asc == NULL) + { + return rc; + } + len = cs_strlen(trim(asc)); + if(len <= 6 && len > 0) + { + for(i = 0, rc = 0; i < len; i++) + { + if(!gethexval_within_range(asc[i])) + { + return -1; + } + rc = rc << 4 | gethexval(asc[i]); + } + if(rc & 0x1000000) + { + rc = -1; + } + } + return rc; +} + +int32_t key_atob_l(char *asc, uint8_t *bin, int32_t l) +{ + int32_t i, n1, n2, rc; + + if(asc == NULL || bin == NULL) + { + return -1; + } + for(i = rc = 0; i < l; i += 2) + { + if(!gethexval_within_range(asc[i]) || !gethexval_within_range(asc[i + 1])) + { + rc = -1; + } + else + { + n1 = gethexval(asc[i]); + n2 = gethexval(asc[i + 1]); + bin[i >> 1] = (n1 << 4) + (n2 & 0xff); + } + } + return rc; +} + +uint32_t b2i(int32_t n, const uint8_t *b) +{ + if(b == NULL) + { + return 0; + } + switch(n) + { + case 1: + return b[0]; + case 2: + return (b[0] << 8) | b[1]; + + case 3: + return (b[0] << 16) | (b[1] << 8) | b[2]; + + case 4: + return ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]) & 0xffffffffL; + + default: + cs_log("Error in b2i, n=%i", n); + } + return 0; +} + +uint64_t b2ll(int32_t n, const uint8_t *b) +{ + int32_t i; + uint64_t k = 0; + if(b == NULL) + { + return k; + } + for(i = 0; i < n; k += b[i++]) + { + k <<= 8; + } + return k; +} + +uint8_t *i2b_buf(int32_t n, uint32_t i, uint8_t *b) +{ + switch(n) + { + case 2: + b[0] = (i >> 8) & 0xff; + b[1] = (i) & 0xff; + break; + + case 3: + b[0] = (i >> 16) & 0xff; + b[1] = (i >> 8) & 0xff; + b[2] = (i) & 0xff; + break; + + case 4: + b[0] = (i >> 24) & 0xff; + b[1] = (i >> 16) & 0xff; + b[2] = (i >> 8) & 0xff; + b[3] = (i) & 0xff; + break; + } + return b; +} + +uint8_t *ull2b_buf(uint64_t i, uint8_t *b) +{ + b[0] = (i >> 56) & 0xff; + b[1] = (i >> 48) & 0xff; + b[2] = (i >> 40) & 0xff; + b[3] = (i >> 32) & 0xff; + b[4] = (i >> 24) & 0xff; + b[5] = (i >> 16) & 0xff; + b[6] = (i >> 8) & 0xff; + b[7] = (i) & 0xff; + return b; +} + +uint32_t a2i(char *asc, int32_t bytes) +{ + int32_t i, n; + uint32_t rc; + if(asc == NULL) + { + errno = EINVAL; + return 0x1f1f1f; + } + for(rc = i = 0, n = cs_strlen(trim(asc)) - 1; i < abs(bytes) << 1; n--, i++) + { + if(n >= 0) + { + int32_t rcl; + if(!gethexval_within_range(asc[n])) + { + errno = EINVAL; + return 0x1f1f1f; + } + else + { + rcl = gethexval(asc[n]); + rc |= rcl << (i << 2); + } + } + else + { + if(bytes < 0) + { + rc |= 0xf << (i << 2); + } + } + } + errno = 0; + return rc; +} + +int32_t boundary(int32_t exp, int32_t n) +{ + return (((n - 1) >> exp) + 1) << exp; +} + +/* Checks whether an array has at least one non-zero byte. + length specifies the maximum length to check for. */ +int32_t array_has_nonzero_byte(uint8_t *value, int32_t length) +{ + if(!value) + { + return 0; + } + + int32_t i; + for(i = 0; i < length; i++) + { + if(value[i] > 0) + { + return 1; + } + } + return 0; +} + +#define RAND_POOL_SIZE 64 + +// The last bytes are used to init random seed +static uint8_t rand_pool[RAND_POOL_SIZE + sizeof(uint32_t)]; + +void get_random_bytes_init(void) +{ + srand(time(NULL)); + int fd = open("/dev/urandom", O_RDONLY); + + if(fd < 0) + { + fd = open("/dev/random", O_RDONLY); + if(fd < 0) + { + return; + } + } + + if(read(fd, rand_pool, RAND_POOL_SIZE + sizeof(uint32_t)) > -1) + { + uint32_t pool_seed = b2i(4, rand_pool + RAND_POOL_SIZE); + srand(pool_seed); + } + close(fd); +} + +void get_random_bytes(uint8_t *dst, uint32_t dst_len) +{ + static uint32_t rand_pool_pos; // *MUST* be static + uint32_t i; + + for(i = 0; i < dst_len; i++) + { + rand_pool_pos++; // Races are welcome... + dst[i] = rand() ^ rand_pool[rand_pool_pos % RAND_POOL_SIZE]; + } +} + +static uint32_t crc_table[256] = +{ + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +/* + * crc32 -- compute the CRC-32 of a data stream + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ +#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); + +uint32_t crc32(uint32_t crc, const uint8_t *buf, uint32_t len) +{ + if(!buf) + { + return 0L; + } + crc = crc ^ 0xffffffffL; + while(len >= 8) + { + DO8(buf); + len -= 8; + } + if(len) + { + do + { + DO1(buf); + } + while(--len); + } + return crc ^ 0xffffffffL; +} + +static uint16_t ccitt_crc_table [256] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, + 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, + 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, + 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, + 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, + 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, + 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, + 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, + 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, + 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, + 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, + 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, + 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, + 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, + 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, + 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, + 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, + 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, + 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, + 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, + 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, + 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, + 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, + 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, + 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, + 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, + 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, + 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, + 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, + 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, + 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, + 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, + 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +uint16_t ccitt_crc(uint8_t *data, size_t length, uint16_t seed, uint16_t final) +{ + size_t count; + uint32_t crc = seed; + uint32_t temp; + + for (count = 0; count < length; ++count) + { + temp = (*data++ ^ (crc >> 8)) & 0xff; + crc = ccitt_crc_table[temp] ^ (crc << 8); + } + return (uint16_t)(crc ^ final); +} + +static const uint32_t ccitt32_crc_table[256] = +{ + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4, +}; + +uint32_t ccitt32_crc(uint8_t *data, size_t len) +{ + uint32_t crc = 0xffffffff; + while(len--) + { + crc = ((crc << 8) & 0xffffff00) ^ ccitt32_crc_table[((crc >> 24) & 0xff) ^ *data++]; + } + return(crc); +} + +// https://en.wikipedia.org/wiki/Jenkins_hash_function +uint32_t jhash(const char *key, size_t len) +{ + uint32_t hash, i; + for(hash = i = 0; i < len; i++) + { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; +} + +/* Converts a char to it's hex representation. See char_to_hex on how to use it. */ +char to_hex(char code) +{ + static const char hex[] = "0123456789abcdef"; + return hex[(int)code & 15]; +} + +/* Converts a char array to a char array with hex values (needed for example for md5). + Note that result needs to be at least (p_array_len * 2) + 1 large. */ +void char_to_hex(const uint8_t *p_array, uint32_t p_array_len, uint8_t *result) +{ + result[p_array_len * 2] = '\0'; + const uint8_t *p_end = p_array + p_array_len; + uint32_t pos = 0; + const uint8_t *p; + + for(p = p_array; p != p_end; p++, pos += 2) + { + result[pos] = to_hex(*p >> 4); + result[pos + 1] = to_hex(*p & 15); + } +} + +static inline uint8_t to_uchar(char ch) +{ + return ch; +} + +void base64_encode(const char *in, size_t inlen, char *out, size_t outlen) +{ + static const char b64str[64] +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L //gcc-15+ + __attribute__((nonstring)) +#endif + = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + while(inlen && outlen) + { + *out++ = b64str[(to_uchar(in[0]) >> 2) & 0x3f]; + if(!--outlen) { break; } + *out++ = b64str[((to_uchar(in[0]) << 4) + (--inlen ? to_uchar(in[1]) >> 4 : 0)) & 0x3f]; + if(!--outlen) { break; } + *out++ = (inlen ? b64str[((to_uchar(in[1]) << 2) + (--inlen ? to_uchar(in[2]) >> 6 : 0)) & 0x3f] : '='); + if(!--outlen) { break; } + *out++ = inlen ? b64str[to_uchar(in[2]) & 0x3f] : '='; + if(!--outlen) { break; } + if(inlen) { inlen--; } + if(inlen) { in += 3; } + if(outlen) { *out = '\0'; } + } +} + +size_t b64encode(const char *in, size_t inlen, char **out) +{ + size_t outlen = 1 + BASE64_LENGTH(inlen); + if(inlen > outlen) + { + *out = NULL; + return 0; + } + if(!cs_malloc(out, outlen)) + { + return -1; + } + base64_encode(in, inlen, *out, outlen); + return outlen - 1; +} + +static int8_t b64decoder[256]; + +/* Prepares the base64 decoding array */ +void b64prepare(void) +{ + const uint8_t alphabet[64] +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L //gcc-15+ + __attribute__((nonstring)) +#endif + = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int32_t i; + + for(i = sizeof(b64decoder) - 1; i >= 0; --i) + { + b64decoder[i] = -1; + } + + for(i = sizeof(alphabet) - 1; i >= 0; --i) + { + b64decoder[alphabet[i]] = i; + } +} + +/* Decodes a base64-encoded string. The given array will be used directly for output and is thus modified! */ +int32_t b64decode(uint8_t *result) +{ + int32_t i, len = cs_strlen((char *)result), j = 0, bits = 0, char_count = 0; + + if(!b64decoder[0]) + { + b64prepare(); + } + + for(i = 0; i < len; ++i) + { + if(result[i] == '=') { break; } + int8_t tmp = b64decoder[result[i]]; + if(tmp == -1) { continue; } + bits += tmp; + ++char_count; + if(char_count == 4) + { + result[j++] = bits >> 16; + result[j++] = (bits >> 8) & 0xff; + result[j++] = bits & 0xff; + bits = 0; + char_count = 0; + } + else + { + bits <<= 6; + } + } + if(i == len) + { + if(char_count) + { + result[j] = '\0'; + return 0; + } + } + else + { + switch(char_count) + { + case 1: + result[j] = '\0'; + return 0; + case 2: + result[j++] = bits >> 10; + result[j] = '\0'; + break; + case 3: + result[j++] = bits >> 16; + result[j++] = (bits >> 8) & 0xff; + result[j] = '\0'; + break; + } + } + return j; +} + + +#ifdef READ_SDT_CHARSETS + +// ISO_6937 function taken from VLC +/***************************************************************************** + * Local conversion routine from ISO_6937 to UTF-8 charset. Support for this + * is still missing in libiconv, hence multiple operating systems lack it. + * The conversion table adds Euro sign (0xA4) as per ETSI EN 300 468 Annex A + *****************************************************************************/ + +static const uint16_t iso_6937_to_ucs4[128] = +{ + /* 0x80 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + /* 0x88 */ 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, + /* 0x90 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + /* 0x98 */ 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, + /* 0xa0 */ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x20ac, 0x00a5, 0x0000, 0x00a7, + /* 0xa8 */ 0x00a4, 0x2018, 0x201c, 0x00ab, 0x2190, 0x2191, 0x2192, 0x2193, + /* 0xb0 */ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00d7, 0x00b5, 0x00b6, 0x00b7, + /* 0xb8 */ 0x00f7, 0x2019, 0x201d, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, + /* 0xc0 */ 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + /* 0xc8 */ 0xffff, 0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, + /* 0xd0 */ 0x2014, 0x00b9, 0x00ae, 0x00a9, 0x2122, 0x266a, 0x00ac, 0x00a6, + /* 0xd8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, + /* 0xe0 */ 0x2126, 0x00c6, 0x00d0, 0x00aa, 0x0126, 0x0000, 0x0132, 0x013f, + /* 0xe8 */ 0x0141, 0x00d8, 0x0152, 0x00ba, 0x00de, 0x0166, 0x014a, 0x0149, + /* 0xf0 */ 0x0138, 0x00e6, 0x0111, 0x00f0, 0x0127, 0x0131, 0x0133, 0x0140, + /* 0xf8 */ 0x0142, 0x00f8, 0x0153, 0x00df, 0x00fe, 0x0167, 0x014b, 0x00ad +}; + +/* The outer array range runs from 0xc1 to 0xcf, the inner range from 0x40 + to 0x7f. */ +static const uint16_t iso_6937_to_ucs4_comb[15][64] = +{ +/* 0xc1 */ + { + /* 0x40 */ 0x0000, 0x00c0, 0x0000, 0x0000, 0x0000, 0x00c8, 0x0000, 0x0000, + /* 0x48 */ 0x0000, 0x00cc, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d2, + /* 0x50 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d9, 0x0000, 0x0000, + /* 0x58 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x00e0, 0x0000, 0x0000, 0x0000, 0x00e8, 0x0000, 0x0000, + /* 0x68 */ 0x0000, 0x00ec, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f2, + /* 0x70 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f9, 0x0000, 0x0000, + /* 0x78 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, +/* 0xc2 */ + { + /* 0x40 */ 0x0000, 0x00c1, 0x0000, 0x0106, 0x0000, 0x00c9, 0x0000, 0x0000, + /* 0x48 */ 0x0000, 0x00cd, 0x0000, 0x0000, 0x0139, 0x0000, 0x0143, 0x00d3, + /* 0x50 */ 0x0000, 0x0000, 0x0154, 0x015a, 0x0000, 0x00da, 0x0000, 0x0000, + /* 0x58 */ 0x0000, 0x00dd, 0x0179, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x00e1, 0x0000, 0x0107, 0x0000, 0x00e9, 0x0000, 0x0000, + /* 0x68 */ 0x0000, 0x00ed, 0x0000, 0x0000, 0x013a, 0x0000, 0x0144, 0x00f3, + /* 0x70 */ 0x0000, 0x0000, 0x0155, 0x015b, 0x0000, 0x00fa, 0x0000, 0x0000, + /* 0x78 */ 0x0000, 0x00fd, 0x017a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, +/* 0xc3 */ + { + /* 0x40 */ 0x0000, 0x00c2, 0x0000, 0x0108, 0x0000, 0x00ca, 0x0000, 0x011c, + /* 0x48 */ 0x0124, 0x00ce, 0x0134, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d4, + /* 0x50 */ 0x0000, 0x0000, 0x0000, 0x015c, 0x0000, 0x00db, 0x0000, 0x0174, + /* 0x58 */ 0x0000, 0x0176, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x00e2, 0x0000, 0x0109, 0x0000, 0x00ea, 0x0000, 0x011d, + /* 0x68 */ 0x0125, 0x00ee, 0x0135, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f4, + /* 0x70 */ 0x0000, 0x0000, 0x0000, 0x015d, 0x0000, 0x00fb, 0x0000, 0x0175, + /* 0x78 */ 0x0000, 0x0177, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, +/* 0xc4 */ + { + /* 0x40 */ 0x0000, 0x00c3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x48 */ 0x0000, 0x0128, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d1, 0x00d5, + /* 0x50 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0168, 0x0000, 0x0000, + /* 0x58 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x00e3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x68 */ 0x0000, 0x0129, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f1, 0x00f5, + /* 0x70 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0169, 0x0000, 0x0000, + /* 0x78 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, +/* 0xc5 */ + { + /* 0x40 */ 0x0000, 0x0100, 0x0000, 0x0000, 0x0000, 0x0112, 0x0000, 0x0000, + /* 0x48 */ 0x0000, 0x012a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x014c, + /* 0x50 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016a, 0x0000, 0x0000, + /* 0x58 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x0101, 0x0000, 0x0000, 0x0000, 0x0113, 0x0000, 0x0000, + /* 0x68 */ 0x0000, 0x012b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x014d, + /* 0x70 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016b, 0x0000, 0x0000, + /* 0x78 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, +/* 0xc6 */ + { + /* 0x40 */ 0x0000, 0x0102, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x011e, + /* 0x48 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x50 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016c, 0x0000, 0x0000, + /* 0x58 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x0103, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x011f, + /* 0x68 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x70 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016d, 0x0000, 0x0000, + /* 0x78 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, +/* 0xc7 */ + { + /* 0x40 */ 0x0000, 0x0000, 0x0000, 0x010a, 0x0000, 0x0116, 0x0000, 0x0120, + /* 0x48 */ 0x0000, 0x0130, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x50 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x58 */ 0x0000, 0x0000, 0x017b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x0000, 0x0000, 0x010b, 0x0000, 0x0117, 0x0000, 0x0121, + /* 0x68 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x70 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x78 */ 0x0000, 0x0000, 0x017c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, +/* 0xc8 */ + { + /* 0x40 */ 0x0000, 0x00c4, 0x0000, 0x0000, 0x0000, 0x00cb, 0x0000, 0x0000, + /* 0x48 */ 0x0000, 0x00cf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d6, + /* 0x50 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00dc, 0x0000, 0x0000, + /* 0x58 */ 0x0000, 0x0178, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x00e4, 0x0000, 0x0000, 0x0000, 0x00eb, 0x0000, 0x0000, + /* 0x68 */ 0x0000, 0x00ef, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f6, + /* 0x70 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00fc, 0x0000, 0x0000, + /* 0x78 */ 0x0000, 0x00ff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, +/* 0xc9 */ + { + 0x0000, + }, +/* 0xca */ + { + /* 0x40 */ 0x0000, 0x00c5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x48 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x50 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016e, 0x0000, 0x0000, + /* 0x58 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x00e5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x68 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x70 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016f, 0x0000, 0x0000, + /* 0x78 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, +/* 0xcb */ + { + /* 0x40 */ 0x0000, 0x0000, 0x0000, 0x00c7, 0x0000, 0x0000, 0x0000, 0x0122, + /* 0x48 */ 0x0000, 0x0000, 0x0000, 0x0136, 0x013b, 0x0000, 0x0145, 0x0000, + /* 0x50 */ 0x0000, 0x0000, 0x0156, 0x015e, 0x0162, 0x0000, 0x0000, 0x0000, + /* 0x58 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x0000, 0x0000, 0x00e7, 0x0000, 0x0000, 0x0000, 0x0123, + /* 0x68 */ 0x0000, 0x0000, 0x0000, 0x0137, 0x013c, 0x0000, 0x0146, 0x0000, + /* 0x70 */ 0x0000, 0x0000, 0x0157, 0x015f, 0x0163, 0x0000, 0x0000, 0x0000, + /* 0x78 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, +/* 0xcc */ + { + 0x0000, + }, +/* 0xcd */ + { + /* 0x40 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x48 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0150, + /* 0x50 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0170, 0x0000, 0x0000, + /* 0x58 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x68 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0151, + /* 0x70 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0171, 0x0000, 0x0000, + /* 0x78 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, +/* 0xce */ + { + /* 0x40 */ 0x0000, 0x0104, 0x0000, 0x0000, 0x0000, 0x0118, 0x0000, 0x0000, + /* 0x48 */ 0x0000, 0x012e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x50 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0172, 0x0000, 0x0000, + /* 0x58 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x0105, 0x0000, 0x0000, 0x0000, 0x0119, 0x0000, 0x0000, + /* 0x68 */ 0x0000, 0x012f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x70 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0173, 0x0000, 0x0000, + /* 0x78 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, +/* 0xcf */ + { + /* 0x40 */ 0x0000, 0x0000, 0x0000, 0x010c, 0x010e, 0x011a, 0x0000, 0x0000, + /* 0x48 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x013d, 0x0000, 0x0147, 0x0000, + /* 0x50 */ 0x0000, 0x0000, 0x0158, 0x0160, 0x0164, 0x0000, 0x0000, 0x0000, + /* 0x58 */ 0x0000, 0x0000, 0x017d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x60 */ 0x0000, 0x0000, 0x0000, 0x010d, 0x010f, 0x011b, 0x0000, 0x0000, + /* 0x68 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x013e, 0x0000, 0x0148, 0x0000, + /* 0x70 */ 0x0000, 0x0000, 0x0159, 0x0161, 0x0165, 0x0000, 0x0000, 0x0000, + /* 0x78 */ 0x0000, 0x0000, 0x017e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + } +}; + +size_t ISO6937toUTF8(const uint8_t **inbuf, size_t *inbytesleft, uint8_t **outbuf, size_t *outbytesleft) +{ + if(!inbuf || !(*inbuf)) + { + return (size_t)(0); /* Reset state requested */ + } + + const uint8_t *iptr = *inbuf; + const uint8_t *iend = iptr + *inbytesleft; + uint8_t *optr = *outbuf; + uint8_t *oend = optr + *outbytesleft; + uint16_t ch; + int err = 0; + + while (iptr < iend) + { + if(*iptr < 0x80) + { + if(optr >= oend) + { + err = E2BIG; + break; /* No space in outbuf */ + } + *optr++ = *iptr++; + continue; + } + + if (optr + 2 >= oend) + { + err = E2BIG; + break; /* No space in outbuf for multibyte char */ + } + ch = iso_6937_to_ucs4[*iptr - 0x80]; + + if(ch == 0xffff) + { + /* Composed character */ + if (iptr + 1 >= iend) + { + err = EINVAL; + break; /* No next character */ + } + if (iptr[1] < 0x40 || iptr[1] >= 0x80 || !(ch = iso_6937_to_ucs4_comb[iptr[0] - 0xc1][iptr[1] - 0x40])) + { + err = EILSEQ; + break; /* Illegal combination */ + } + iptr += 2; + } + else + { + if (!ch) + { + err = EILSEQ; + break; + } + iptr++; + } + + if (ch < 0x800) + { + optr[1] = 0x80 | (ch & 0x3f); + optr[0] = 0xc0 | (ch >> 6); + optr +=2; + } + else + { + optr[2] = 0x80 | (ch & 0x3f); + ch >>= 6; + optr[1] = 0x80 | (ch & 0x3f); + optr[0] = 0xe0 | (ch >> 6); + optr += 3; + } + + } + *inbuf = iptr; + *outbuf = optr; + *inbytesleft = iend - iptr; + *outbytesleft = oend - optr; + if(err) + { + errno = err; + return (size_t)(-1); + } + return (size_t)(0); +} + +#include "oscam-string-isotables.h" + +static const uint16_t *get_iso8859_table(int8_t iso_table_number) +{ + if(iso_table_number > 1 && iso_table_number < 17 && iso_table_number != 12) + { + if(iso_table_number < 12) + { + return iso_8859_to_unicode[iso_table_number - 2]; + } + else + { + return iso_8859_to_unicode[iso_table_number - 3]; + } + } + else + { + return NULL; + } +} + +size_t ISO8859toUTF8(int8_t iso_table_number, const uint8_t **inbuf, size_t *inbytesleft, uint8_t **outbuf, size_t *outbytesleft) +{ + if(!inbuf || !(*inbuf)) + { + return (size_t)(0); /* Reset state requested */ + } + + const uint8_t *iptr = *inbuf; + const uint8_t *iend = iptr + *inbytesleft; + uint8_t *optr = *outbuf; + uint8_t *oend = optr + *outbytesleft; + uint16_t ch; + int err = 0; + const uint16_t *iso_table = NULL; + + if( iso_table_number != 1 ) + { + iso_table = get_iso8859_table(iso_table_number); + if ( iso_table == NULL ) + { + errno = EINVAL; + return (size_t)(-1); + } + } + + while (iptr < iend) + { + if(*iptr < 0x80) + { + if(optr >= oend) + { + err = E2BIG; + break; /* No space in outbuf */ + } + *optr++ = *iptr++; + continue; + } + + if( iso_table_number == 1 || *iptr < 0xA1 ) + { + ch = *iptr; + } + else + { + ch = iso_table[*iptr - 0xA1]; + } + iptr++; + + if (ch < 0x80) + { + if (optr >= oend) + { + err = E2BIG; + break; /* No space in outbuf for char */ + } + optr[0] = ch & 0xff; + optr += 1; + } + else if (ch < 0x800) + { + if (optr + 1 >= oend) + { + err = E2BIG; + break; /* No space in outbuf for multibyte char */ + } + optr[1] = 0x80 | (ch & 0x3f); + optr[0] = 0xc0 | (ch >> 6); + optr += 2; + } + else + { + if (optr + 2 >= oend) + { + err = E2BIG; + break; /* No space in outbuf for multibyte char */ + } + optr[2] = 0x80 | (ch & 0x3f); + ch >>= 6; + optr[1] = 0x80 | (ch & 0x3f); + optr[0] = 0xe0 | (ch >> 6); + optr += 3; + } + } + *inbuf = iptr; + *outbuf = optr; + *inbytesleft = iend - iptr; + *outbytesleft = oend - optr; + + if(err) + { + errno = err; + return (size_t)(-1); + } + return (size_t)(0); +} + +#endif + +size_t UnicodetoUTF8(const uint8_t **inbuf, size_t *inbytesleft, uint8_t **outbuf, size_t *outbytesleft) +{ + if(!inbuf || !(*inbuf)) + { + return (size_t)(0); /* Reset state requested */ + } + + const uint8_t *iptr = *inbuf; + const uint8_t *iend = iptr + *inbytesleft; + uint8_t*optr = *outbuf; + uint8_t *oend = optr + *outbytesleft; + uint16_t ch; + int err = 0; + + while (iptr + 1 < iend) + { + ch = (iptr[0] << 8) | iptr[1]; + iptr += 2; + + if (ch < 0x80) + { + if (optr >= oend) + { + err = E2BIG; + break; /* No space in outbuf for char */ + } + optr[0] = ch & 0xff; + optr += 1; + } + else if (ch < 0x800) + { + if (optr + 1 >= oend) + { + err = E2BIG; + break; /* No space in outbuf for multibyte char */ + } + optr[1] = 0x80 | (ch & 0x3f); + optr[0] = 0xc0 | (ch >> 6); + optr += 2; + } + else + { + if (optr + 2 >= oend) + { + err = E2BIG; + break; /* No space in outbuf for multibyte char */ + } + optr[2] = 0x80 | (ch & 0x3f); + ch >>= 6; + optr[1] = 0x80 | (ch & 0x3f); + optr[0] = 0xe0 | (ch >> 6); + optr += 3; + } + } + + *inbuf = iptr; + *outbuf = optr; + *inbytesleft = iend - iptr; + *outbytesleft = oend - optr; + + if(err) + { + errno = err; + return (size_t)(-1); + } + return (size_t)(0); +} diff --git a/oscam-string.h b/oscam-string.h new file mode 100644 index 0000000..4bf1cfe --- /dev/null +++ b/oscam-string.h @@ -0,0 +1,58 @@ +#ifndef OSCAM_STRING_H_ +#define OSCAM_STRING_H_ + +bool cs_malloc(void *result, size_t size) MUST_CHECK_RESULT; +bool cs_realloc(void *result, size_t size) MUST_CHECK_RESULT; +char *cs_strdup(const char *str); +size_t cs_strlen(const char *c); +void cs_strncpy(char *destination, const char *source, size_t num); +bool cs_strncat(char *destination, char *source, size_t destination_size); +char *strtolower(char *txt); +char *strtoupper(char *txt); +char *trim(char *txt); +char *trim2(char *txt); +char *remove_white_chars(char *txt); +char *remove_newline_chars(const char *txt); +bool streq(const char *s1, const char *s2); + +char *cs_hexdump(int32_t m, const uint8_t *buf, int32_t n, char *target, int32_t len); +bool gethexval_within_range(char c); +int32_t gethexval(char c); +int32_t cs_atob(uint8_t *buf, char *asc, int32_t n); +uint32_t cs_atoi(char *asc, int32_t l, int32_t val_on_err); +int32_t byte_atob(char *asc); +int32_t word_atob(char *asc); +int32_t dyn_word_atob(char *asc); +int32_t key_atob_l(char *asc, uint8_t *bin, int32_t l); +uint32_t b2i(int32_t n, const uint8_t *b); +uint64_t b2ll(int32_t n, const uint8_t *b); +uint8_t *i2b_buf(int32_t n, uint32_t i, uint8_t *b); +uint8_t *ull2b_buf(uint64_t i, uint8_t *b); +uint32_t a2i(char *asc, int32_t bytes); +int32_t boundary(int32_t exp, int32_t n); +int32_t array_has_nonzero_byte(uint8_t *value, int32_t length); + +void get_random_bytes_init(void); +void get_random_bytes(uint8_t *dst, uint32_t dst_len); + +uint32_t crc32(uint32_t crc, const uint8_t *buf, uint32_t len); +uint16_t ccitt_crc(uint8_t *data, size_t length, uint16_t seed, uint16_t final); +uint32_t ccitt32_crc(uint8_t *data, size_t len); +uint32_t jhash(const char *key, size_t len); + +char to_hex(char code); +void char_to_hex(const uint8_t *p_array, uint32_t p_array_len, uint8_t *result); + +#define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4) +void base64_encode(const char *in, size_t inlen, char *out, size_t outlen); +size_t b64encode(const char *in, size_t inlen, char **out); + +void b64prepare(void); +int32_t b64decode(uint8_t *result); + +#ifdef READ_SDT_CHARSETS +size_t ISO6937toUTF8(const uint8_t **inbuf, size_t *inbytesleft, uint8_t **outbuf, size_t *outbytesleft); +size_t ISO8859toUTF8(int8_t iso_table_number, const uint8_t **inbuf, size_t *inbytesleft, uint8_t **outbuf, size_t *outbytesleft); +#endif +size_t UnicodetoUTF8(const uint8_t **inbuf, size_t *inbytesleft, uint8_t **outbuf, size_t *outbytesleft); +#endif diff --git a/oscam-time.c b/oscam-time.c new file mode 100644 index 0000000..996c77b --- /dev/null +++ b/oscam-time.c @@ -0,0 +1,310 @@ +#include "globals.h" +#include "oscam-time.h" + +#if defined(CLOCKFIX) +struct timeval lasttime; // holds previous time to detect systemtime adjustments due to eg transponder change on dvb receivers +#endif + +int64_t comp_timeb(struct timeb *tpa, struct timeb *tpb) +{ + return (int64_t)(((int64_t)(tpa->time - tpb->time) * 1000ull) + ((int64_t) tpa->millitm - (int64_t) tpb->millitm)); +} + +int64_t comp_timebus(struct timeb *tpa, struct timeb *tpb) +{ + return (int64_t)(((int64_t)(tpa->time - tpb->time) * 1000000ull) + ((int64_t) tpa->millitm - (int64_t) tpb->millitm)); +} + +/* Checks if year is a leap year. If so, 1 is returned, else 0. */ +static int8_t is_leap(unsigned int y) +{ + return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0); +} + +/* Drop-in replacement for timegm function as some plattforms strip the function from their libc.. */ +time_t cs_timegm(struct tm *tm) +{ + time_t result = 0; + int32_t i; + + if(tm->tm_mon > 12 || tm->tm_mon < 0 || tm->tm_mday > 31 || tm->tm_min > 60 || tm->tm_sec > 60 || tm->tm_hour > 24) + { + return 0; + } + + for(i = 70; i < tm->tm_year; ++i) + { + result += is_leap(i + 1900) ? 366 : 365; + } + + for(i = 0; i < tm->tm_mon; ++i) + { + if(i == 0 || i == 2 || i == 4 || i == 6 || i == 7 || i == 9 || i == 11) { result += 31; } + else if(i == 3 || i == 5 || i == 8 || i == 10) { result += 30; } + else if(is_leap(tm->tm_year + 1900)) { result += 29; } + else { result += 28; } + } + + result += tm->tm_mday - 1; + result *= 24; + result += tm->tm_hour; + result *= 60; + result += tm->tm_min; + result *= 60; + result += tm->tm_sec; + return result; +} + +/* Drop-in replacement for gmtime_r as some plattforms strip the function from their libc. */ +struct tm *cs_gmtime_r(const time_t *timep, struct tm *r) +{ + static const int16_t daysPerMonth[13] = + { + 0, + 31, + 31 + 28, + 31 + 28 + 31, + 31 + 28 + 31 + 30, + 31 + 28 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 + }; + + time_t i; + time_t work = * timep % 86400; + r->tm_sec = work % 60; + work /= 60; + r->tm_min = work % 60; + r->tm_hour = work / 60; + work = * timep / 86400; + r->tm_wday = (4 + work) % 7; + + for(i = 1970; ; ++i) + { + time_t k = is_leap(i) ? 366 : 365; + if(work >= k) + { + work -= k; + } + else + { + break; + } + } + + r->tm_year = i - 1900; + r->tm_yday = work; + r->tm_mday = 1; + + if(is_leap(i) && work > 58) + { + if(work == 59) + { + r->tm_mday = 2; /* 29.2. */ + } + work -= 1; + } + + for(i = 11; i && daysPerMonth[i] > work; --i) { ; } + r->tm_mon = i; + r->tm_mday += work - daysPerMonth[i]; + return r; +} + +/* Drop-in replacement for ctime_r as some plattforms strip the function from their libc. */ +char *cs_ctime_r(const time_t *timep, char *buf) +{ + struct tm t; + localtime_r(timep, &t); + strftime(buf, 26, "%c\n", &t); + return buf; +} + +void cs_ftime(struct timeb *tp) +{ + struct timeval tv; + gettimeofday(&tv, NULL); +#if defined(CLOCKFIX) + if (tv.tv_sec > lasttime.tv_sec || (tv.tv_sec == lasttime.tv_sec && tv.tv_usec >= lasttime.tv_usec)) // check for time issues! + { + lasttime = tv; // register this valid time + } + else + { + tv = lasttime; + settimeofday(&tv, NULL); // set time back to last known valid time + //fprintf(stderr, "*** WARNING: BAD TIME AFFECTING WHOLE OSCAM ECM HANDLING, SYSTEMTIME SET TO LAST KNOWN VALID TIME **** \n"); + } +#endif + tp->time = tv.tv_sec; + tp->millitm = tv.tv_usec / 1000; +} + +void cs_ftimeus(struct timeb *tp) +{ + struct timeval tv; + gettimeofday(&tv, NULL); +#if defined(CLOCKFIX) + if (tv.tv_sec > lasttime.tv_sec || (tv.tv_sec == lasttime.tv_sec && tv.tv_usec >= lasttime.tv_usec)) // check for time issues! + { + lasttime = tv; // register this valid time + } + else + { + tv = lasttime; + settimeofday(&tv, NULL); // set time back to last known valid time + //fprintf(stderr, "*** WARNING: BAD TIME AFFECTING WHOLE OSCAM ECM HANDLING, SYSTEMTIME SET TO LAST KNOWN VALID TIME **** \n"); + } +#endif + tp->time = tv.tv_sec; + tp->millitm = tv.tv_usec; +} + +void cs_sleepms(uint32_t msec) +{ + // does not interfere with signals like sleep and usleep do + struct timespec req_ts, rem_ts; + req_ts.tv_sec = msec / 1000; + req_ts.tv_nsec = (msec % 1000) * 1000000L; + int32_t olderrno = errno; // Some OS (especially MacOSX) seem to set errno to ETIMEDOUT when sleeping + while (nanosleep(&req_ts, &rem_ts) == -1 && errno == EINTR) + { + req_ts = rem_ts; + } + errno = olderrno; +} + +void cs_sleepus(uint32_t usec) +{ + // does not interfere with signals like sleep and usleep do + struct timespec req_ts, rem_ts; + req_ts.tv_sec = usec / 1000000; + req_ts.tv_nsec = (usec % 1000000) * 1000L; + int32_t olderrno = errno; // Some OS (especially MacOSX) seem to set errno to ETIMEDOUT when sleeping + while (nanosleep(&req_ts, &rem_ts) == -1 && errno == EINTR) + { + req_ts = rem_ts; + } + errno = olderrno; +} + +void add_ms_to_timespec(struct timespec *timeout, int32_t msec) +{ + struct timespec now; + int64_t nanosecs, secs; + const int64_t NANOSEC_PER_MS = 1000000; + const int64_t NANOSEC_PER_SEC = 1000000000; + cs_gettime(&now); + nanosecs = (int64_t) (msec * NANOSEC_PER_MS + now.tv_nsec); + if (nanosecs >= NANOSEC_PER_SEC) + { + secs = now.tv_sec + (nanosecs / NANOSEC_PER_SEC); + nanosecs %= NANOSEC_PER_SEC; + } + else + { + secs = now.tv_sec; + } + timeout->tv_sec = (long)secs; + timeout->tv_nsec = (long)nanosecs; +} + +void add_ms_to_timeb(struct timeb *tb, int32_t ms) +{ + if (ms >= 1000){ + tb->time += ms / 1000; + tb->millitm += (ms % 1000); + } + else{ + tb->millitm += ms; + } + if(tb->millitm >= 1000) + { + tb->millitm %= 1000; + tb->time++; + } +} + +int64_t add_ms_to_timeb_diff(struct timeb *tb, int32_t ms) +{ + struct timeb tb_now; + add_ms_to_timeb(tb, ms); + cs_ftime(&tb_now); + return comp_timeb(tb, &tb_now); +} + +void __cs_pthread_cond_init(const char *n, pthread_cond_t *cond) +{ + pthread_condattr_t attr; + SAFE_CONDATTR_INIT_R(&attr, n); // init condattr with defaults + SAFE_COND_INIT_R(cond, &attr, n); // init thread with right clock assigned + pthread_condattr_destroy(&attr); +} + +void __cs_pthread_cond_init_nolog(const char *n, pthread_cond_t *cond) +{ + pthread_condattr_t attr; + SAFE_CONDATTR_INIT_NOLOG_R(&attr, n); // init condattr with defaults + SAFE_COND_INIT_NOLOG_R(cond, &attr, n); // init thread with right clock assigned + pthread_condattr_destroy(&attr); +} + +void sleepms_on_cond(const char *n, pthread_mutex_t *mutex, pthread_cond_t *cond, uint32_t msec) +{ + struct timespec ts; + add_ms_to_timespec(&ts, msec); + SAFE_MUTEX_LOCK_R(mutex, n); + SAFE_COND_TIMEDWAIT_R(cond, mutex, &ts, n); // sleep on sleep_cond + SAFE_MUTEX_UNLOCK_R(mutex, n); +} + +void cs_pthread_cond_init(const char *n, pthread_mutex_t *mutex, pthread_cond_t *cond) +{ + SAFE_MUTEX_INIT_R(mutex, NULL, n); + __cs_pthread_cond_init(n, cond); +} + +void cs_pthread_cond_init_nolog(const char *n, pthread_mutex_t *mutex, pthread_cond_t *cond) +{ + SAFE_MUTEX_INIT_NOLOG_R(mutex, NULL, n); + __cs_pthread_cond_init(n, cond); +} + +/* Return real time clock value calculated based on cs_gettime(). Use this instead of time() */ +time_t cs_time(void) +{ + struct timeb tb; + cs_ftime(&tb); + return tb.time; +} + +#ifdef __MACH__ +#include +#include +#endif + +void cs_gettime(struct timespec *ts) +{ + struct timeval tv; + gettimeofday(&tv, NULL); +#if defined(CLOCKFIX) + if (tv.tv_sec > lasttime.tv_sec || (tv.tv_sec == lasttime.tv_sec && tv.tv_usec >= lasttime.tv_usec)) // check for time issues! + { + lasttime = tv; // register this valid time + } + else + { + tv = lasttime; + settimeofday(&tv, NULL); // set time back to last known valid time + //fprintf(stderr, "*** WARNING: BAD TIME AFFECTING WHOLE OSCAM ECM HANDLING, SYSTEMTIME SET TO LAST KNOWN VALID TIME **** \n"); + } +#endif + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; + return; +} diff --git a/oscam-time.h b/oscam-time.h new file mode 100644 index 0000000..50fa5c2 --- /dev/null +++ b/oscam-time.h @@ -0,0 +1,32 @@ +#ifndef OSCAM_TIME_H_ +#define OSCAM_TIME_H_ + +int64_t comp_timeb(struct timeb *tpa, struct timeb *tpb); +int64_t comp_timebus(struct timeb *tpa, struct timeb *tpb); +time_t cs_timegm(struct tm *tm); +struct tm *cs_gmtime_r(const time_t *timep, struct tm *r); +char *cs_ctime_r(const time_t *timep, char *buf); +void cs_ftime(struct timeb *tp); +void cs_ftimeus(struct timeb *tp); +void cs_sleepms(uint32_t msec); +void cs_sleepus(uint32_t usec); + +void add_ms_to_timespec(struct timespec *timeout, int32_t msec); +void add_ms_to_timeb(struct timeb *tb, int32_t ms); +int64_t add_ms_to_timeb_diff(struct timeb *tb, int32_t ms); + +time_t cs_time(void); + +static inline bool cs_valid_time(struct timeb *tp) { return tp->time != 0; } + +void cs_gettime(struct timespec *ts); + +void __cs_pthread_cond_init(const char *n, pthread_cond_t *cond); +void cs_pthread_cond_init(const char *n, pthread_mutex_t *mutex, pthread_cond_t *cond); + +void __cs_pthread_cond_init_nolog(const char *n, pthread_cond_t *cond); +void cs_pthread_cond_init_nolog(const char *n, pthread_mutex_t *mutex, pthread_cond_t *cond); + +void sleepms_on_cond(const char *n, pthread_mutex_t *mutex, pthread_cond_t *cond, uint32_t msec); + +#endif diff --git a/oscam-work.c b/oscam-work.c new file mode 100644 index 0000000..4df6ae8 --- /dev/null +++ b/oscam-work.c @@ -0,0 +1,632 @@ +#define MODULE_LOG_PREFIX "work" + +#include "globals.h" +#include "module-cacheex.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-emm.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-work.h" +#include "reader-common.h" +#include "module-cccam.h" +#include "module-cccam-data.h" +#include "module-cccshare.h" +#include "oscam-time.h" + +extern CS_MUTEX_LOCK system_lock; +extern int32_t thread_pipe[2]; + +struct job_data +{ + enum actions action; + struct s_reader *rdr; + struct s_client *cl; + void *ptr; + struct timeb time; + uint16_t len; +}; + +static void free_job_data(struct job_data *data) +{ + if(!data) + { return; } + if(data->len && data->ptr) + { + // special free checks + if(data->action==ACTION_ECM_ANSWER_CACHE) + { + NULLFREE(((struct s_write_from_cache *)data->ptr)->er_cache); + } + + NULLFREE(data->ptr); + } + NULLFREE(data); +} + +void free_joblist(struct s_client *cl) +{ + int32_t lock_status = pthread_mutex_trylock(&cl->thread_lock); + LL_ITER it = ll_iter_create(cl->joblist); + + struct job_data *data; + while((data = ll_iter_next(&it))) + { + free_job_data(data); + } + + ll_destroy(&cl->joblist); + cl->account = NULL; + + if(cl->work_job_data) // Free job_data that was not freed by work_thread + { free_job_data(cl->work_job_data); } + + cl->work_job_data = NULL; + + if(lock_status == 0) + { SAFE_MUTEX_UNLOCK(&cl->thread_lock); } + + pthread_mutex_destroy(&cl->thread_lock); +} + +/* + Work threads are named like this: + w[r|c]XX-[rdr->label|client->username] + + w - work thread prefix + [r|c] - depending whether the the action is related to reader or client + XX - two digit action code from enum actions + label - reader label or client username (see username() function) +*/ +static void set_work_thread_name(struct job_data *data) +{ + char thread_name[16 + 1]; + snprintf(thread_name, sizeof(thread_name), "w%c%02d-%s", + data->action < ACTION_CLIENT_FIRST ? 'r' : 'c', + data->action, + username(data->cl) + ); + set_thread_name(thread_name); +} + +#define __free_job_data(client, job_data) \ + do { \ + client->work_job_data = NULL; \ + if(job_data && job_data != &tmp_data) { \ + free_job_data(job_data); \ + } \ + job_data = NULL; \ + } while(0) + +void *work_thread(void *ptr) +{ + struct job_data *data = (struct job_data *)ptr; + struct s_client *cl = data->cl; + struct s_reader *reader = cl->reader; + struct timeb start, end; // start time poll, end time poll + + struct job_data tmp_data; + struct pollfd pfd[1]; + + SAFE_SETSPECIFIC(getclient, cl); + cl->thread = pthread_self(); + cl->thread_active = 1; + + set_work_thread_name(data); + + struct s_module *module = get_module(cl); + uint16_t bufsize = module->bufsize; // CCCam needs more than 1024bytes! + if(!bufsize) + { bufsize = DEFAULT_MODULE_BUFsize; } + + uint8_t *mbuf; + if(!cs_malloc(&mbuf, bufsize)) + { return NULL; } + + cl->work_mbuf = mbuf; // Track locally allocated data, because some callback may call cs_exit/cs_disconect_client/pthread_exit and then mbuf would be leaked + int32_t n = 0, rc = 0, i, idx, s, dblvl; + (void)dblvl; + uint8_t dcw[16]; + int8_t restart_reader = 0; + + while(cl->thread_active) + { + cs_ftime(&start); // register start time + + while(cl->thread_active) + { + if(!cl || cl->kill || !is_valid_client(cl)) + { + SAFE_MUTEX_LOCK(&cl->thread_lock); + cl->thread_active = 0; + SAFE_MUTEX_UNLOCK(&cl->thread_lock); + cs_log_dbg(D_TRACE, "ending thread (kill)"); + __free_job_data(cl, data); + cl->work_mbuf = NULL; // Prevent free_client from freeing mbuf (->work_mbuf) + free_client(cl); + if(restart_reader) + { restart_cardreader(reader, 0); } + NULLFREE(mbuf); + pthread_exit(NULL); + return NULL; + } + + if(data && data->action != ACTION_READER_CHECK_HEALTH) + { cs_log_dbg(D_TRACE, "data from add_job action=%d client %c %s", data->action, cl->typ, username(cl)); } + + if(!data) + { + if(!cl->kill && cl->typ != 'r') + { client_check_status(cl); } // do not call for physical readers as this might cause an endless job loop + + SAFE_MUTEX_LOCK(&cl->thread_lock); + if(cl->joblist && ll_count(cl->joblist) > 0) + { + LL_ITER itr = ll_iter_create(cl->joblist); + data = ll_iter_next_remove(&itr); + if(data) + { set_work_thread_name(data); } + //cs_log_dbg(D_TRACE, "start next job from list action=%d", data->action); + } + SAFE_MUTEX_UNLOCK(&cl->thread_lock); + } + + if(!data) + { + /* for serial client cl->pfd is file descriptor for serial port not socket + for example: pfd=open("/dev/ttyUSB0"); */ + if(!cl->pfd || module->listenertype == LIS_SERIAL) + { break; } + + pfd[0].fd = cl->pfd; + pfd[0].events = POLLIN | POLLPRI; + + SAFE_MUTEX_LOCK(&cl->thread_lock); + cl->thread_active = 2; + SAFE_MUTEX_UNLOCK(&cl->thread_lock); + + rc = poll(pfd, 1, 3000); + + SAFE_MUTEX_LOCK(&cl->thread_lock); + cl->thread_active = 1; + SAFE_MUTEX_UNLOCK(&cl->thread_lock); + + if(rc > 0) + { + cs_ftime(&end); // register end time + cs_log_dbg(D_TRACE, "[OSCAM-WORK] new event %d occurred on fd %d after %"PRId64" ms inactivity", pfd[0].revents, + pfd[0].fd, comp_timeb(&end, &start)); + data = &tmp_data; + data->ptr = NULL; + cs_ftime(&start); // register start time for new poll next run + + if(reader) + { data->action = ACTION_READER_REMOTE; } + else + { + if(cl->is_udp) + { + data->action = ACTION_CLIENT_UDP; + data->ptr = mbuf; + data->len = bufsize; + } + else + { data->action = ACTION_CLIENT_TCP; } + + if(pfd[0].revents & (POLLHUP | POLLNVAL | POLLERR)) + { cl->kill = 1; } + } + } + } + + if(!data) + { continue; } + + if(!reader && data->action < ACTION_CLIENT_FIRST) + { + __free_job_data(cl, data); + break; + } + + if(!data->action) + { break; } + + struct timeb actualtime; + cs_ftime(&actualtime); + int64_t gone = comp_timeb(&actualtime, &data->time); + if(data != &tmp_data && gone > (int) cfg.ctimeout+1000) + { + cs_log_dbg(D_TRACE, "dropping client data for %s time %"PRId64" ms", username(cl), gone); + __free_job_data(cl, data); + continue; + } + + if(data != &tmp_data) + { cl->work_job_data = data; } // Track the current job_data + + switch(data->action) + { + case ACTION_READER_IDLE: + reader_do_idle(reader); + break; + + case ACTION_READER_REMOTE: + s = check_fd_for_data(cl->pfd); + if(s == 0) // no data, another thread already read from fd? + { break; } + if(s < 0) + { + if(cl->reader->ph.type == MOD_CONN_TCP) + { network_tcp_connection_close(reader, "disconnect"); } + break; + } + rc = cl->reader->ph.recv(cl, mbuf, bufsize); + if(rc < 0) + { + if(cl->reader->ph.type == MOD_CONN_TCP) + { + network_tcp_connection_close(reader, "disconnect on receive"); +#ifdef CS_CACHEEX_AIO + cl->cacheex_aio_checked = 0; +#endif + } + break; + } + cl->last = time(NULL); // *********************************** TO BE REPLACE BY CS_FTIME() LATER **************** + idx = cl->reader->ph.c_recv_chk(cl, dcw, &rc, mbuf, rc); + if(idx < 0) { break; } // no dcw received + if(!idx) { idx = cl->last_idx; } + cl->reader->last_g = time(NULL); // *********************************** TO BE REPLACE BY CS_FTIME() LATER **************** // for reconnect timeout + for(i = 0, n = 0; i < cfg.max_pending && n == 0; i++) + { + if(cl->ecmtask[i].idx == idx) + { + cl->pending--; + casc_check_dcw(reader, i, rc, dcw); + n++; + } + } + break; + + case ACTION_READER_RESET: + cardreader_do_reset(reader); + break; + + case ACTION_READER_ECM_REQUEST: + reader_get_ecm(reader, data->ptr); + break; + + case ACTION_READER_EMM: + reader_do_emm(reader, data->ptr); + break; + + case ACTION_READER_SENDCMD: +#ifdef READER_VIDEOGUARD + if (!reader) + { break; } + dblvl = cs_dblevel; + cs_dblevel = dblvl | D_READER; + rc = cardreader_do_rawcmd(reader, data->ptr); + cs_log_dbg(D_TRACE, "sendcmd rc: %i, csystem: %s", rc, reader->csystem->desc); + if(rc == -9) + { + CMD_PACKET *cp = data->ptr; + uint8_t response[MAX_CMD_SIZE]; + memset(response, 0, sizeof(response)); + uint16_t response_length[1] = { 0 }; + rc = reader_cmd2icc(reader, cp->cmd, cp->cmdlen, response, response_length); + cs_log_dbg(D_TRACE, "sendcmd rc: %i, len: %i", rc, *response_length); + if (*response_length) + { + cs_log_dump_dbg(D_TRACE, response, *response_length, "sendcmd response:"); + } + } + cs_dblevel = dblvl; +#endif + break; + + case ACTION_READER_CARDINFO: + reader_do_card_info(reader); + break; + + case ACTION_READER_POLL_STATUS: +#ifdef READER_VIDEOGUARD + cardreader_poll_status(reader); +#endif + break; + + case ACTION_READER_INIT: + if(!cl->init_done) + { reader_init(reader); } + break; + + case ACTION_READER_RESTART: + cl->kill = 1; + restart_reader = 1; + break; + + case ACTION_READER_RESET_FAST: + cl->reader->card_status = CARD_NEED_INIT; + cardreader_do_reset(reader); + break; + + case ACTION_READER_CHECK_HEALTH: + cardreader_do_checkhealth(reader); + break; + + case ACTION_READER_CAPMT_NOTIFY: + if(cl->reader->ph.c_capmt) { cl->reader->ph.c_capmt(cl, data->ptr); } + break; + + case ACTION_CLIENT_UDP: + n = module->recv(cl, data->ptr, data->len); + if(n < 0) { break; } + module->s_handler(cl, data->ptr, n); + break; + + case ACTION_CLIENT_TCP: + s = check_fd_for_data(cl->pfd); + if(s == 0) // no data, another thread already read from fd? + { break; } + if(s < 0) // system error or fd wants to be closed + { + cl->kill = 1; // kill client on next run + continue; + } + n = module->recv(cl, mbuf, bufsize); + if(n < 0) + { + cl->kill = 1; // kill client on next run + continue; + } + module->s_handler(cl, mbuf, n); + break; + + case ACTION_CACHEEX1_DELAY: + cacheex_mode1_delay(data->ptr); + break; + + case ACTION_CACHEEX_TIMEOUT: + cacheex_timeout(data->ptr); + break; + + case ACTION_FALLBACK_TIMEOUT: + fallback_timeout(data->ptr); + break; + + case ACTION_CLIENT_TIMEOUT: + ecm_timeout(data->ptr); + break; + + case ACTION_ECM_ANSWER_READER: + chk_dcw(data->ptr); + break; + + case ACTION_ECM_ANSWER_CACHE: + write_ecm_answer_fromcache(data->ptr); + break; + + case ACTION_CLIENT_INIT: + if(module->s_init) + { module->s_init(cl); } + cl->is_udp = module->type == MOD_CONN_UDP; + cl->init_done = 1; + break; + + case ACTION_CLIENT_IDLE: + if(module->s_idle) + { module->s_idle(cl); } + else + { + cs_log("user %s reached %d sec idle limit.", username(cl), cfg.cmaxidle); + cl->kill = 1; + } + break; + + case ACTION_CACHE_PUSH_OUT: + cacheex_push_out(cl, data->ptr); + break; + + case ACTION_CLIENT_KILL: + cl->kill = 1; + break; + + case ACTION_CLIENT_SEND_MSG: + { + if (config_enabled(MODULE_CCCAM)) + { + struct s_clientmsg *clientmsg = (struct s_clientmsg *)data->ptr; + cc_cmd_send(cl, clientmsg->msg, clientmsg->len, clientmsg->cmd); + } + break; + } + + case ACTION_PEER_IDLE: + if(module->s_peer_idle) + { module->s_peer_idle(cl); } + break; + + case ACTION_CLIENT_HIDECARDS: + { +#ifdef CS_ANTICASC + if(config_enabled(MODULE_CCCSHARE)) + { + int32_t hidetime = (cl->account->acosc_penalty_duration == -1 ? cfg.acosc_penalty_duration : cl->account->acosc_penalty_duration); + if(hidetime) + { + int32_t hide_count; + int32_t cardsize; + int32_t ii, uu=0; + LLIST **sharelist = get_and_lock_sharelist(); + LLIST *sharelist2 = ll_create("hidecards-sharelist"); + + for(ii = 0; ii < CAID_KEY; ii++) + { + if(sharelist[ii]) + { + ll_putall(sharelist2, sharelist[ii]); + } + } + + unlock_sharelist(); + + struct cc_card **cardarray = get_sorted_card_copy(sharelist2, 0, &cardsize); + ll_destroy(&sharelist2); + + for(ii = 0; ii < cardsize; ii++) + { + if(hidecards_card_valid_for_client(cl, cardarray[ii])) + { + if (cardarray[ii]->id) + { + hide_count = hide_card_to_client(cardarray[ii], cl); + if(hide_count) + { + cs_log_dbg(D_TRACE, "Hiding card_%d caid=%04x remoteid=%08x from %s for %d %s", + uu, cardarray[ii]->caid, cardarray[ii]->remote_id, username(cl), hidetime, hidetime>1 ? "secconds" : "seccond"); + uu += 1; + } + } + } + } + + cs_sleepms(hidetime * 1000); + uu = 0; + + for(ii = 0; ii < cardsize; ii++) + { + if(hidecards_card_valid_for_client(cl, cardarray[ii])) + { + if (cardarray[ii]->id) + { + hide_count = unhide_card_to_client(cardarray[ii], cl); + if(hide_count) + { + cs_log_dbg(D_TRACE, "Unhiding card_%d caid=%04x remoteid=%08x for %s", + uu, cardarray[ii]->caid, cardarray[ii]->remote_id, username(cl)); + uu += 1; + } + } + } + } + + NULLFREE(cardarray); + } + } +#endif + break; + } // case ACTION_CLIENT_HIDECARDS + + } // switch + + __free_job_data(cl, data); + } + + if(thread_pipe[1] && (mbuf[0] != 0x00)) + { + cs_log_dump_dbg(D_TRACE, mbuf, 1, "[OSCAM-WORK] Write to pipe:"); + if(write(thread_pipe[1], mbuf, 1) == -1) // wakeup client check + { + cs_log_dbg(D_TRACE, "[OSCAM-WORK] Writing to pipe failed (errno=%d %s)", errno, strerror(errno)); + } + } + + // Check for some race condition where while we ended, another thread added a job + SAFE_MUTEX_LOCK(&cl->thread_lock); + if(cl->joblist && ll_count(cl->joblist) > 0) + { + SAFE_MUTEX_UNLOCK(&cl->thread_lock); + continue; + } + else + { + cl->thread_active = 0; + SAFE_MUTEX_UNLOCK(&cl->thread_lock); + break; + } + } + cl->thread_active = 0; + cl->work_mbuf = NULL; // Prevent free_client from freeing mbuf (->work_mbuf) + NULLFREE(mbuf); + pthread_exit(NULL); + return NULL; +} + +/** + * adds a job to the job queue + * if ptr should be free() after use, set len to the size + * else set size to 0 +**/ +int32_t add_job(struct s_client *cl, enum actions action, void *ptr, int32_t len) +{ + if(!cl || cl->kill) + { + if(!cl) + { cs_log("WARNING: add_job failed. Client killed!"); } // Ignore jobs for killed clients + if(len && ptr) + { NULLFREE(ptr); } + return 0; + } + + if(action == ACTION_CACHE_PUSH_OUT && cacheex_check_queue_length(cl)) + { + if(len && ptr) + { NULLFREE(ptr); } + return 0; + } + + struct job_data *data; + if(!cs_malloc(&data, sizeof(struct job_data))) + { + if(len && ptr) + { NULLFREE(ptr); } + return 0; + } + + data->action = action; + data->ptr = ptr; + data->cl = cl; + data->len = len; + cs_ftime(&data->time); + + SAFE_MUTEX_LOCK(&cl->thread_lock); + if(cl && !cl->kill && cl->thread_active) + { + if(!cl->joblist) + { cl->joblist = ll_create("joblist"); } + ll_append(cl->joblist, data); + if(cl->thread_active == 2) + { pthread_kill(cl->thread, OSCAM_SIGNAL_WAKEUP); } + SAFE_MUTEX_UNLOCK(&cl->thread_lock); + cs_log_dbg(D_TRACE, "add %s job action %d queue length %d %s", + action > ACTION_CLIENT_FIRST ? "client" : "reader", action, + ll_count(cl->joblist), username(cl)); + return 1; + } + + /* pcsc doesn't like this; segfaults on x86, x86_64 */ + int8_t modify_stacksize = 0; + struct s_reader *rdr = cl->reader; + if(cl->typ != 'r' || !rdr || rdr->typ != R_PCSC) + { modify_stacksize = 1; } + + if(action != ACTION_READER_CHECK_HEALTH) + { + cs_log_dbg(D_TRACE, "start %s thread action %d", + action > ACTION_CLIENT_FIRST ? "client" : "reader", action); + } + + int32_t ret = start_thread("client work", work_thread, (void *)data, &cl->thread, 1, modify_stacksize); + if(ret) + { + cs_log("ERROR: can't create thread for %s (errno=%d %s)", + action > ACTION_CLIENT_FIRST ? "client" : "reader", ret, strerror(ret)); + free_job_data(data); + } + + cl->thread_active = 1; + SAFE_MUTEX_UNLOCK(&cl->thread_lock); + return 1; +} diff --git a/oscam-work.h b/oscam-work.h new file mode 100644 index 0000000..ac83689 --- /dev/null +++ b/oscam-work.h @@ -0,0 +1,43 @@ +#ifndef OSCAM_WORK_H_ +#define OSCAM_WORK_H_ + +enum actions +{ + // Reader action + ACTION_READER_IDLE = 1, // wr01 + ACTION_READER_REMOTE = 2, // wr02 + ACTION_READER_RESET = 4, // wr04 + ACTION_READER_ECM_REQUEST = 5, // wr05 + ACTION_READER_EMM = 6, // wr06 + ACTION_READER_CARDINFO = 7, // wr07 + ACTION_READER_INIT = 8, // wr08 + ACTION_READER_RESTART = 9, // wr09 + ACTION_READER_RESET_FAST = 10, // wr10 + ACTION_READER_CHECK_HEALTH = 11, // wr11 + ACTION_READER_CAPMT_NOTIFY = 12, // wr12 + ACTION_READER_POLL_STATUS = 13, // wr13 + ACTION_READER_SENDCMD = 14, // wr14 + // Client actions + ACTION_CLIENT_UDP = 22, // wc22 + ACTION_CLIENT_TCP = 23, // wc23 + ACTION_CLIENT_KILL = 24, // wc24 + ACTION_CLIENT_INIT = 25, // wc25 + ACTION_CLIENT_IDLE = 26, // wc26 + ACTION_CACHE_PUSH_OUT = 27, // wc27 + ACTION_CLIENT_SEND_MSG = 28, // wc28 + ACTION_CACHEEX_TIMEOUT = 29, // wc29 + ACTION_FALLBACK_TIMEOUT = 30, // wc30 + ACTION_CLIENT_TIMEOUT = 31, // wc31 + ACTION_ECM_ANSWER_READER = 32, // wc32 + ACTION_ECM_ANSWER_CACHE = 33, // wc33 + ACTION_CACHEEX1_DELAY = 34, // wc34 + ACTION_PEER_IDLE = 35, // wc35 + ACTION_CLIENT_HIDECARDS = 36 // wc36 +}; + +#define ACTION_CLIENT_FIRST 20 // This just marks where client actions start + +int32_t add_job(struct s_client *cl, enum actions action, void *ptr, int32_t len); +void free_joblist(struct s_client *cl); + +#endif diff --git a/oscam.c b/oscam.c new file mode 100644 index 0000000..eeea384 --- /dev/null +++ b/oscam.c @@ -0,0 +1,2048 @@ +#define MODULE_LOG_PREFIX "main" + +#include "globals.h" +#include + +#include "csctapi/cardreaders.h" +#include "modules.h" +#include "readers.h" + +#include "extapi/coolapi.h" +#include "module-anticasc.h" +#include "module-cacheex.h" +#include "module-cccam.h" +#include "module-dvbapi.h" +#include "module-dvbapi-azbox.h" +#include "module-dvbapi-mca.h" +#include "module-dvbapi-chancache.h" +#include "module-gbox-sms.h" +#include "module-lcd.h" +#include "module-led.h" +#include "module-stat.h" +#include "module-webif.h" +#include "module-webif-tpl.h" +#ifdef WEBIF_WIKI +#include "webif/pages_wiki.h" +#endif +#include "module-cw-cycle-check.h" +#include "module-streamrelay.h" +#include "oscam-chk.h" +#include "oscam-cache.h" +#include "oscam-client.h" +#include "oscam-config.h" +#include "oscam-ecm.h" +#include "oscam-emm.h" +#include "oscam-emm-cache.h" +#include "oscam-files.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" +#include "reader-common.h" +#include "module-gbox.h" + +#ifdef WITH_EMU + void add_emu_reader(void); +#endif + +#ifdef WITH_SSL +#include +#include +#include + +static void ssl_init(void) +{ + SSL_load_error_strings(); + ERR_load_BIO_strings(); + ERR_load_SSL_strings(); + SSL_library_init(); +} + +static void ssl_done(void) +{ +#if OPENSSL_VERSION_NUMBER < 0x1010005fL + ERR_remove_state(0); +#endif + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +} + +#else +static void ssl_init(void) { } +static void ssl_done(void) { } +#endif + +#ifdef WITH_SIGNING +#include "oscam-signing.h" +#endif + +extern char *config_ssl; +extern char *config_mak; + +/***************************************************************************** + Globals +*****************************************************************************/ +const char *syslog_ident = "oscam"; +static char *oscam_pidfile; +static char default_pidfile[64]; + +int32_t exit_oscam = 0; +static struct s_module modules[CS_MAX_MOD]; + +struct s_client *first_client = NULL; // Pointer to clients list, first client is master +struct s_reader *first_active_reader = NULL; // list of active readers (enable=1 deleted = 0) +LLIST *configured_readers = NULL; // list of all (configured) readers + +char cs_confdir[128]; +uint16_t cs_dblevel = 0; // Debug Level +int32_t thread_pipe[2] = {0, 0}; +static int8_t cs_restart_mode = 1; // Restartmode: 0=off, no restart fork, 1=(default)restart fork, restart by webif, 2=like=1, but also restart on segfaults +static int8_t cs_capture_SEGV; +static int8_t cs_dump_stack; +static uint16_t cs_waittime = 60; +char cs_tmpdir[200]; +CS_MUTEX_LOCK system_lock; +CS_MUTEX_LOCK config_lock; +CS_MUTEX_LOCK gethostbyname_lock; +CS_MUTEX_LOCK clientlist_lock; +CS_MUTEX_LOCK readerlist_lock; +CS_MUTEX_LOCK fakeuser_lock; +CS_MUTEX_LOCK cwcycle_lock; +pthread_key_t getclient; +static int32_t bg; +static int32_t gbdb; +static int32_t max_pending = 32; + +// ecms list +CS_MUTEX_LOCK ecmcache_lock; +struct ecm_request_t *ecmcwcache = NULL; +uint32_t ecmcwcache_size = 0; + +// pushout deleted list +CS_MUTEX_LOCK ecm_pushed_deleted_lock; +struct ecm_request_t *ecm_pushed_deleted = NULL; + +struct s_config cfg; + +int log_remove_sensitive = 1; + +static char *prog_name; +static char *stb_boxtype; +static char *stb_boxname; + +static int32_t oscam_stacksize = 0; + +/***************************************************************************** + Statics +*****************************************************************************/ +/* Prints usage information and information about the built-in modules. */ +static void show_usage(void) +{ + printf("%s", + " ___ ____ ___\n" + " / _ \\/ ___| / __|__ _ _ __ ___\n" + "| | | \\___ \\| | / _` | '_ ` _ \\\n" + "| |_| |___) | |_| (_| | | | | | |\n" + " \\___/|____/ \\___\\__,_|_| |_| |_|\n\n"); + printf("OSCam Cardserver v%s@%s (%s)\n", CS_VERSION, CS_GIT_COMMIT, CS_TARGET); + printf("Copyright (C) 2009-2026 OSCam developers.\n"); + printf("This program is distributed under GPLv3.\n"); + printf("OSCam is based on Streamboard mp-cardserver v0.9d written by dukat\n"); + printf("Visit %s for more details.\n\n", BOARD_URL); + + printf(" ConfigDir : %s\n", CS_CONFDIR); + printf("\n"); + printf(" Usage: oscam [parameters]\n"); + printf("\n Directories:\n"); + printf(" -c, --config-dir | Read configuration files from .\n"); + printf(" . Default: %s\n", CS_CONFDIR); + printf(" -t, --temp-dir | Set temporary directory to .\n"); +#if defined(__CYGWIN__) + printf(" . Default: (OS-TMP)\n"); +#else + printf(" . Default: /tmp/.oscam\n"); +#endif + printf("\n Startup:\n"); +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + printf(" -f, --foreground | Start in the foreground mode.\n"); +#else + printf(" -b, --daemon | Start in the background as daemon.\n"); +#endif + printf(" -B, --pidfile | Create pidfile when starting.\n"); + if(config_enabled(WEBIF)) + { + printf(" -r, --restart | Set restart level:\n"); + printf(" . 0 - Restart disabled (exit on restart request).\n"); + printf(" . 1 - WebIf restart is active (default).\n"); + printf(" . 2 - Like 1, but also restart on segfaults.\n"); + } + printf(" -w, --wait | Set how much seconds to wait at startup for the\n"); + printf(" . system clock to be set correctly. Default: 60\n"); + printf("\n Logging:\n"); + printf(" -I, --syslog-ident | Set syslog ident. Default: oscam\n"); + printf(" -S, --show-sensitive | Do not filter sensitive info (card serials, boxids)\n"); + printf(" . from the logs.\n"); + printf(" -d, --debug | Set debug level mask used for logging:\n"); + printf(" . 0 - No extra debugging (default).\n"); + printf(" . 1 - Detailed error messages.\n"); + printf(" . 2 - ATR parsing info, ECM, EMM and CW dumps.\n"); + printf(" . 4 - Traffic from/to the reader.\n"); + printf(" . 8 - Traffic from/to the clients.\n"); + printf(" . 16 - Traffic to the reader-device on IFD layer.\n"); + printf(" . 32 - Traffic to the reader-device on I/O layer.\n"); + printf(" . 64 - EMM logging.\n"); + printf(" . 128 - DVBAPI logging.\n"); + printf(" . 256 - Loadbalancer logging.\n"); + printf(" . 512 - CACHEEX logging.\n"); + printf(" . 1024 - Client ECM logging.\n"); + printf(" . 2048 - CSP logging.\n"); + printf(" . 4096 - CWC logging.\n"); +#ifdef CS_CACHEEX_AIO + printf(" . 8192 - CW Cache logging.\n"); +#endif + printf(" . 65535 - Debug all.\n"); + printf("\n Settings:\n"); + printf(" -p, --pending-ecm | Set the maximum number of pending ECM packets.\n"); + printf(" . Default: 32 Max: 4096\n"); + printf("\n Debug parameters:\n"); + printf(" -a, --crash-dump | Write oscam.crash file on segfault. This option\n"); + printf(" . needs GDB to be installed and OSCam executable to\n"); + printf(" . contain the debug information (run oscam-XXXX.debug)\n"); + printf(" -s, --capture-segfaults | Capture segmentation faults.\n"); + printf(" -g, --gcollect | Garbage collector debug mode:\n"); + printf(" . 1 - Immediate free.\n"); + printf(" . 2 - Check for double frees.\n"); + printf("\n Information:\n"); + printf(" -h, --help | Show command line help text.\n"); + printf(" -V, --build-info | Show OSCam binary configuration and version.\n"); +} + +/* Keep the options sorted */ +#if defined(WITH_STAPI) || defined(WITH_STAPI5) +static const char short_options[] = "aB:fc:d:g:hI:p:r:Sst:uVw:"; +#else +static const char short_options[] = "aB:bc:d:g:hI:p:r:Sst:uVw:"; +#endif + +/* Keep the options sorted by short option */ +static const struct option long_options[] = +{ + { "crash-dump", no_argument, NULL, 'a' }, + { "pidfile", required_argument, NULL, 'B' }, +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + { "foreground", no_argument, NULL, 'f' }, +#else + { "daemon", no_argument, NULL, 'b' }, +#endif + { "config-dir", required_argument, NULL, 'c' }, + { "debug", required_argument, NULL, 'd' }, + { "gcollect", required_argument, NULL, 'g' }, + { "help", no_argument, NULL, 'h' }, + { "syslog-ident", required_argument, NULL, 'I' }, + { "pending-ecm", required_argument, NULL, 'p' }, + { "restart", required_argument, NULL, 'r' }, + { "show-sensitive", no_argument, NULL, 'S' }, + { "capture-segfaults", no_argument, NULL, 's' }, + { "temp-dir", required_argument, NULL, 't' }, + { "build-info", no_argument, NULL, 'V' }, + { "wait", required_argument, NULL, 'w' }, + { 0, 0, 0, 0 } +}; + +static void set_default_dirs_first(void) +{ + snprintf(cs_confdir, sizeof(cs_confdir), "%s", CS_CONFDIR); + memset(cs_tmpdir, 0, sizeof(cs_tmpdir)); // will get further procesed trought oscam_files.c !! +} + +static void write_versionfile(bool use_stdout); + +static void parse_cmdline_params(int argc, char **argv) +{ +#if defined(WITH_STAPI) || defined(WITH_STAPI5) + bg = 1; +#endif + + int i; + while((i = getopt_long(argc, argv, short_options, long_options, NULL)) != EOF) + { + if(i == '?') + { fprintf(stderr, "ERROR: Unknown command line parameter: %s\n", argv[optind - 1]); } + switch(i) + { + case 'a': // --crash-dump + cs_dump_stack = 1; + break; + case 'B': // --pidfile + oscam_pidfile = optarg; + break; + case 'f': // --foreground + bg = 0; + break; + case 'b': // --daemon + bg = 1; + break; + case 'c': // --config-dir + cs_strncpy(cs_confdir, optarg, sizeof(cs_confdir)); + break; + case 'd': // --debug + cs_dblevel = atoi(optarg); + break; + case 'g': // --gcollect + gbdb = atoi(optarg); + break; + case 'h': // --help + show_usage(); + exit(EXIT_SUCCESS); + break; + case 'I': // --syslog-ident + syslog_ident = optarg; + break; + case 'p': // --pending-ecm + max_pending = atoi(optarg) <= 0 ? 32 : MIN(atoi(optarg), 4096); + break; + case 'r': // --restart + if(config_enabled(WEBIF)) + { + cs_restart_mode = atoi(optarg); + } + break; + case 'S': // --show-sensitive + log_remove_sensitive = !log_remove_sensitive; + break; + case 's': // --capture-segfaults + cs_capture_SEGV = 1; + break; + case 't': // --temp-dir + { + mkdir(optarg, S_IRWXU); + int j = open(optarg, O_RDONLY); + if(j >= 0) + { + close(j); + cs_strncpy(cs_tmpdir, optarg, sizeof(cs_tmpdir)); + } + else + { + printf("WARNING: Temp dir does not exist. Using default value.\n"); + } + break; + } + case 'V': // --build-info + write_versionfile(true); + exit(EXIT_SUCCESS); + break; + case 'w': // --wait + cs_waittime = strtoul(optarg, NULL, 10); + break; + } + } +} + +#define write_conf(CONFIG_VAR, text) \ + fprintf(fp, "%-40s %s\n", text ":", config_enabled(CONFIG_VAR) ? "yes" : "no") + +#define write_readerconf(CONFIG_VAR, text) \ + fprintf(fp, "%-40s %s\n", text ":", config_enabled(CONFIG_VAR) ? "yes" : "no - no EMM support!") + +#define write_cardreaderconf(CONFIG_VAR, text) \ + fprintf(fp, "%s%-29s %s\n", "cardreader_", text ":", config_enabled(CONFIG_VAR) ? "yes" : "no") + +static void write_versionfile(bool use_stdout) +{ +#ifdef WITH_SIGNING + if(!init_signing_info(prog_name)) cs_exit_oscam(); +#endif + FILE *fp = stdout; + if(!use_stdout) + { + char targetfile[256]; + fp = fopen(get_tmp_dir_filename(targetfile, sizeof(targetfile), "oscam.version"), "w"); + if(!fp) + { + cs_log("Cannot open %s (errno=%d %s)", targetfile, errno, strerror(errno)); + return; + } + struct tm st; + time_t walltime = cs_time(); + localtime_r(&walltime, &st); + fprintf(fp, "Unix Starttime: %" PRId64 "\n", (int64_t)walltime); + fprintf(fp, "Starttime: %02d.%02d.%04d %02d:%02d:%02d\n", + st.tm_mday, st.tm_mon + 1, st.tm_year + 1900, + st.tm_hour, st.tm_min, st.tm_sec); + } + + fprintf(fp, "Build Date: %s\n", "UNKNOWN"); // Placeholder if not defined by build system + fprintf(fp, "Version: %s@%s\n", "UNKNOWN", "UNKNOWN"); // Placeholder if not defined by build system + fprintf(fp, "Compiler: %s\n", "UNKNOWN"); // Placeholder if not defined by build system +#ifdef USE_COMPRESS + fprintf(fp, "Compression: %s, level %s\n", COMP_VERSION, COMP_LEVEL); +#endif + fprintf(fp, "Box Type: %s (%s)\n", boxtype_get(), boxname_get()); + fprintf(fp, "PID: %d\n", getppid()); + fprintf(fp, "TempDir: %s\n", cs_tmpdir); +#ifdef MODULE_GBOX + if(cfg.gbox_tmp_dir == NULL) + { + fprintf(fp, "GBox tmp_dir: not defined using: %s\n", cs_tmpdir); + } + else + { + fprintf(fp, "GBox tmp_dir: %s\n", cfg.gbox_tmp_dir); + } +#endif + + fprintf(fp, "ConfigDir: %s\n", cs_confdir); + +#ifdef WEBIF + fprintf(fp, "WebifPort: %d\n", cfg.http_port); +#endif + +#ifdef WITH_SIGNING + fprintf(fp, "\n"); + fprintf(fp, "Signature: %s\n", (osi.is_verified ? "Valid - successfully verified using built-in Public Key" : "Invalid - wrong signature or internal error occured!")); + fprintf(fp, " Binary: %s%s\n", osi.resolved_binfile, osi.binfile_exists ? "" : " is inaccessible!"); + fprintf(fp, " Signer: %s\n", config_ssl); + fprintf(fp, " SHA256: %s\n", osi.hash_sha256 ? osi.hash_sha256 : "n/a"); + fprintf(fp, "Certificate: %s %s Certificate\n", ((osi.cert_is_valid_self || osi.cert_is_valid_system) ? "Trusted" : "Untrusted"), (osi.cert_is_cacert ? "CA" : "self signed")); + fprintf(fp, " Subject: %s\n", osi.cert_subject); + fprintf(fp, " Issuer: %s\n", osi.cert_issuer); + fprintf(fp, " Version: %d\n", osi.cert_version); + fprintf(fp, " Serial: %s\n", osi.cert_serial); + fprintf(fp, " Fingerprint: %s\n", osi.cert_fingerprint); + fprintf(fp, " Valid from: %s\n", osi.cert_valid_from); + fprintf(fp, " Valid to: %s\n", osi.cert_valid_to); + fprintf(fp, " Status: %s\n", (osi.cert_is_expired ? "Expired" : "Valid")); + fprintf(fp, " Type: %s\n", osi.pkey_type); + if (osi.system_ca_file) fprintf(fp, "System CA: %s\n", osi.system_ca_file); +#endif + + fprintf(fp, "\n"); + write_conf(WEBIF, "Web interface support"); + write_conf(WEBIF_LIVELOG, "LiveLog support"); + write_conf(WEBIF_JQUERY, "jQuery support intern"); + write_conf(WEBIF_WIKI, "Embedded wiki help system"); + write_conf(WITH_COMPRESS_WEBIF, "Compressed pages"); + write_conf(WITH_SSL, "SSL support"); + write_conf(HAVE_DVBAPI, "DVB API support"); + if(config_enabled(HAVE_DVBAPI)) + { + write_conf(WITH_EXTENDED_CW, "DVB API with extended CW API support"); + write_conf(MODULE_STREAMRELAY, "DVB API with Stream Relay support"); + write_conf(WITH_AZBOX, "DVB API with AZBOX support"); + write_conf(WITH_MCA, "DVB API with MCA support"); + write_conf(WITH_COOLAPI, "DVB API with COOLAPI support"); + write_conf(WITH_COOLAPI2, "DVB API with COOLAPI2 support"); + write_conf(WITH_STAPI, "DVB API with STAPI support"); + write_conf(WITH_STAPI5, "DVB API with STAPI5 support"); + write_conf(WITH_NEUTRINO, "DVB API with NEUTRINO support"); + write_conf(READ_SDT_CHARSETS, "DVB API read-sdt charsets"); + } + write_conf(CS_ANTICASC, "Anti-cascading support"); + write_conf(WITH_DEBUG, "Debug mode"); + write_conf(MODULE_MONITOR, "Monitor"); + write_conf(WITH_LB, "Loadbalancing support"); + write_conf(CS_CACHEEX, "Cache exchange support"); +#ifdef CS_CACHEEX_AIO + write_conf(CS_CACHEEX_AIO, "Cache exchange AIO support"); +#endif + write_conf(CW_CYCLE_CHECK, "CW Cycle Check support"); + write_conf(LCDSUPPORT, "LCD support"); + write_conf(LEDSUPPORT, "LED support"); + write_conf(CLOCKFIX, "Clockfix with realtime clock"); + write_conf(IPV6SUPPORT, "IPv6 support"); +#if defined(__arm__) || defined(__aarch64__) + write_conf(WITH_ARM_NEON, "ARM NEON (SIMD/MPE) support"); +#endif +#ifdef WITH_SIGNING + write_conf(WITH_SIGNING, "Binary signing support"); +#endif + write_conf(WITH_EMU, "Emulator support"); + write_conf(WITH_SOFTCAM, "Built-in SoftCam.Key"); + + fprintf(fp, "\n"); + write_conf(MODULE_CAMD33, "camd 3.3x"); + write_conf(MODULE_CAMD35, "camd 3.5 UDP"); + write_conf(MODULE_CAMD35_TCP, "camd 3.5 TCP"); + write_conf(MODULE_NEWCAMD, "newcamd"); + write_conf(MODULE_CCCAM, "CCcam"); + write_conf(MODULE_CCCSHARE, "CCcam share"); + write_conf(MODULE_GBOX, "gbox"); + write_conf(MODULE_RADEGAST, "radegast"); + write_conf(MODULE_SCAM, "scam"); + write_conf(MODULE_SERIAL, "serial"); + write_conf(MODULE_CONSTCW, "constant CW"); + write_conf(MODULE_PANDORA, "Pandora"); + write_conf(MODULE_GHTTP, "ghttp"); + write_conf(MODULE_STREAMRELAY, "Streamrelay"); + + fprintf(fp, "\n"); + write_conf(WITH_CARDREADER, "Reader support"); + if(config_enabled(WITH_CARDREADER)) + { + fprintf(fp, "\n"); + write_readerconf(READER_NAGRA, "Nagra"); + write_readerconf(READER_NAGRA_MERLIN, "Nagra Merlin"); + write_readerconf(READER_IRDETO, "Irdeto"); + write_readerconf(READER_CONAX, "Conax"); + write_readerconf(READER_CRYPTOWORKS, "Cryptoworks"); + write_readerconf(READER_SECA, "Seca"); + write_readerconf(READER_VIACCESS, "Viaccess"); + write_readerconf(READER_VIDEOGUARD, "NDS Videoguard"); + write_readerconf(READER_DRE, "DRE Crypt"); + write_readerconf(READER_TONGFANG, "TONGFANG"); + write_readerconf(READER_BULCRYPT, "Bulcrypt"); + write_readerconf(READER_GRIFFIN, "Griffin"); + write_readerconf(READER_DGCRYPT, "DGCrypt"); + fprintf(fp, "\n"); + write_cardreaderconf(CARDREADER_PHOENIX, "phoenix"); + write_cardreaderconf(CARDREADER_DRECAS, "drecas"); + write_cardreaderconf(CARDREADER_INTERNAL_AZBOX, "internal_azbox"); + write_cardreaderconf(CARDREADER_INTERNAL_COOLAPI, "internal_coolapi"); + write_cardreaderconf(CARDREADER_INTERNAL_COOLAPI2, "internal_coolapi2"); + write_cardreaderconf(CARDREADER_INTERNAL_AMSMC, "internal_amsmc"); + write_cardreaderconf(CARDREADER_INTERNAL_SCI, "internal_sci"); + write_cardreaderconf(CARDREADER_SC8IN1, "sc8in1"); + write_cardreaderconf(CARDREADER_MP35, "mp35"); + write_cardreaderconf(CARDREADER_SMARGO, "smargo"); + write_cardreaderconf(CARDREADER_PCSC, "pcsc"); + write_cardreaderconf(CARDREADER_SMART, "smartreader"); + write_cardreaderconf(CARDREADER_DB2COM, "db2com"); + write_cardreaderconf(CARDREADER_STAPI, "stapi"); + write_cardreaderconf(CARDREADER_STAPI5, "stapi5"); + write_cardreaderconf(CARDREADER_STINGER, "stinger"); + } + else + { + write_readerconf(WITH_CARDREADER, "Reader Support"); + } + if(!use_stdout) + { fclose(fp); } +} +#undef write_conf +#undef write_readerconf +#undef write_cardreaderconf + +static void remove_versionfile(void) +{ + char targetfile[256]; + unlink(get_tmp_dir_filename(targetfile, sizeof(targetfile), "oscam.version")); +} + +#define report_emm_support(CONFIG_VAR, text) \ + do { \ + if (!config_enabled(CONFIG_VAR)) \ + cs_log_dbg(D_TRACE, "Binary without %s module - no EMM processing for %s possible!", text, text); \ + } while(0) + +static void do_report_emm_support(void) +{ + if(!config_enabled(WITH_CARDREADER)) + { + cs_log("Binary without Cardreader Support! No EMM processing possible!"); + } + else + { + report_emm_support(READER_NAGRA, "Nagra"); + report_emm_support(READER_NAGRA_MERLIN, "Nagra Merlin"); + report_emm_support(READER_IRDETO, "Irdeto"); + report_emm_support(READER_CONAX, "Conax"); + report_emm_support(READER_CRYPTOWORKS, "Cryptoworks"); + report_emm_support(READER_SECA, "Seca"); + report_emm_support(READER_VIACCESS, "Viaccess"); + report_emm_support(READER_VIDEOGUARD, "NDS Videoguard"); + report_emm_support(READER_DRE, "DRE Crypt"); + report_emm_support(READER_TONGFANG, "TONGFANG"); + report_emm_support(READER_BULCRYPT, "Bulcrypt"); + report_emm_support(READER_GRIFFIN, "Griffin"); + report_emm_support(READER_DGCRYPT, "DGCrypt"); + } +} +#undef report_emm_support + +#ifdef NEED_DAEMON +// The compat function is not called daemon() because this may cause problems. +static int32_t do_daemon(int32_t nochdir, int32_t noclose) +{ + int32_t fd; + + switch(fork()) + { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if(setsid() == (-1)) + { return (-1); } + + if(!nochdir) + { (void)chdir("/"); } + + if(!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) + { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if(fd > 2) + { (void)close(fd); } + } + return (0); +} +#else +#define do_daemon daemon +#endif + +/* + * flags: 1 = restart, 2 = don't modify if SIG_IGN, may be combined + */ +static void set_signal_handler(int32_t sig, int32_t flags, void (*sighandler)) +{ + struct sigaction sa; + sigaction(sig, (struct sigaction *) 0, &sa); + if(!((flags & 2) && (sa.sa_handler == SIG_IGN))) + { + sigemptyset(&sa.sa_mask); + sa.sa_flags = (flags & 1) ? SA_RESTART : 0; + sa.sa_handler = sighandler; + sigaction(sig, &sa, (struct sigaction *) 0); + } +} + +static void cs_master_alarm(void) +{ + cs_log("PANIC: master deadlock!"); + fprintf(stderr, "PANIC: master deadlock!"); + fflush(stderr); +} + +static void cs_sigpipe(void) +{ + if(cs_dblevel & D_ALL_DUMP) + { cs_log("Got sigpipe signal -> captured"); } +} + +static void cs_dummy(void) +{ + return; +} + +/* Switch debuglevel forward one step (called when receiving SIGUSR1). */ +static void cs_debug_level(void) +{ + switch(cs_dblevel) + { + case 0: + cs_dblevel = 1; + break; + case 128: + cs_dblevel = 255; + break; + case 255: + cs_dblevel = 0; + break; + default: + cs_dblevel <<= 1; + } + + cs_log("debug_level=%d", cs_dblevel); +} + +/** + * write stacktrace to oscam.crash. file is always appended + * Usage: + * 1. compile oscam with debug parameters (Makefile: DS_OPTS="-ggdb") + * 2. you need gdb installed and working on the local machine + * 3. start oscam with parameter: -a + */ +static void cs_dumpstack(int32_t sig) +{ + FILE *fp = fopen("oscam.crash", "a+"); + + time_t timep; + char buf[200]; + + time(&timep); + cs_ctime_r(&timep, buf); + + fprintf(stderr, "crashed with signal %d on %swriting oscam.crash\n", sig, buf); + + if (fp) + { + fprintf(fp, "%sOSCam cardserver v%s@%s (%s)\n", buf, CS_VERSION, CS_GIT_COMMIT, CS_TARGET); + fprintf(fp, "FATAL: Signal %d: %s Fault. Logged StackTrace:\n\n", sig, (sig == SIGSEGV) ? "Segmentation" : ((sig == SIGBUS) ? "Bus" : "Unknown")); + fclose(fp); + } + + FILE *cmd = fopen("/tmp/gdbcmd", "w"); + if (cmd) + { + fputs("bt\n", cmd); + fputs("thread apply all bt\n", cmd); + fclose(cmd); + } + + snprintf(buf, sizeof(buf) - 1, "gdb %s %d -batch -x /tmp/gdbcmd >> oscam.crash", prog_name, getpid()); + if(system(buf) == -1) + { fprintf(stderr, "Fatal error on trying to start gdb process."); } + + exit(-1); +} + + +/** + * called by signal SIGHUP + * + * reloads configs: + * - useraccounts (oscam.user) + * - readers (oscam.server) + * - services ids (oscam.srvid) + * - tier ids (oscam.tiers) + * Also clears anticascading stats. + **/ +static void cs_reload_config(void) +{ + static pthread_mutex_t mutex; + static int8_t mutex_init = 0; + + if(!mutex_init) + { + SAFE_MUTEX_INIT(&mutex, NULL); + mutex_init = 1; + } + + if(pthread_mutex_trylock(&mutex)) + { + return; + } + + if(cfg.reload_useraccounts) + { + cs_accounts_chk(); + } + + if(cfg.reload_readers) + { + reload_readerdb(); + } + + if(cfg.reload_provid) + { + init_provid(); + } + + if(cfg.reload_services_ids) + { + init_srvid(); + } + + if(cfg.reload_tier_ids) + { + init_tierid(); + } + + if(cfg.reload_fakecws) + { + init_fakecws(); + } + + if(cfg.reload_ac_stat) + { + ac_init_stat(); + } + + if(cfg.reload_log) + { + cs_reopen_log(); // FIXME: aclog.log, emm logs, cw logs (?) + } + + SAFE_MUTEX_UNLOCK(&mutex); +} + +/* Sets signal handlers to ignore for early startup of OSCam because for example log + could cause SIGPIPE errors and the normal signal handlers can't be used at this point. */ +static void init_signal_pre(void) +{ + set_signal_handler(SIGPIPE , 1, SIG_IGN); + set_signal_handler(SIGWINCH, 1, SIG_IGN); + set_signal_handler(SIGALRM , 1, SIG_IGN); + set_signal_handler(SIGHUP , 1, SIG_IGN); +} + +/* Sets the signal handlers.*/ +static void init_signal(void) +{ + set_signal_handler(SIGINT, 3, cs_exit); +#if defined(__APPLE__) + set_signal_handler(SIGEMT, 3, cs_exit); +#endif + set_signal_handler(SIGTERM, 3, cs_exit); + + set_signal_handler(SIGWINCH, 1, SIG_IGN); + set_signal_handler(SIGPIPE, 0, cs_sigpipe); + set_signal_handler(SIGALRM, 0, cs_master_alarm); + set_signal_handler(SIGHUP, 1, cs_reload_config); + set_signal_handler(SIGUSR1, 1, cs_debug_level); + set_signal_handler(SIGUSR2, 1, cs_card_info); + set_signal_handler(OSCAM_SIGNAL_WAKEUP, 0, cs_dummy); + + if(cs_capture_SEGV) + { + set_signal_handler(SIGSEGV, 1, cs_exit); + set_signal_handler(SIGBUS, 1, cs_exit); + } + else if(cs_dump_stack) + { + set_signal_handler(SIGSEGV, 1, cs_dumpstack); + set_signal_handler(SIGBUS, 1, cs_dumpstack); + } + + cs_log("signal handling initialized"); + return; +} + +void cs_exit(int32_t sig) +{ + if(cs_dump_stack && (sig == SIGSEGV || sig == SIGBUS || sig == SIGQUIT)) + { cs_dumpstack(sig); } + + set_signal_handler(SIGHUP , 1, SIG_IGN); + set_signal_handler(SIGPIPE, 1, SIG_IGN); + + struct s_client *cl = cur_client(); + if(!cl) + { return; } + + // this is very important - do not remove + if(cl->typ != 's') + { + cs_log_dbg(D_TRACE, "thread %8lX ended!", (unsigned long)pthread_self()); + + free_client(cl); + + // Restore signals before exiting thread + set_signal_handler(SIGPIPE, 0, cs_sigpipe); + set_signal_handler(SIGHUP, 1, cs_reload_config); + + pthread_exit(NULL); + return; + } + + if(!exit_oscam) + { exit_oscam = sig ? sig : 1; } +} + +static char *read_line_from_file(char *fname, char *buf, int bufsz) +{ + memset(buf, 0, bufsz); + FILE *f = fopen(fname, "r"); + if (!f) + return NULL; + while (fgets(buf, bufsz, f)) + { + if (strstr(buf,"\n")) // we need only the first line + { + buf[cs_strlen(buf)-1] = '\0'; + break; + } + } + fclose(f); + if (buf[0]) + return buf; + return NULL; +} + +static void init_machine_info(void) +{ + struct utsname buffer; + if (uname(&buffer) == 0) + { + cs_log("System name = %s", buffer.sysname); + cs_log("Host name = %s", buffer.nodename); + cs_log("Release = %s", buffer.release); + cs_log("Version = %s", buffer.version); + cs_log("Machine = %s", buffer.machine); + } else { + cs_log("ERROR: uname call failed: %s", strerror(errno)); + } + +#if !defined(__linux__) + return; +#endif + + // Linux only functionality + char boxtype[128]; + boxtype[0] = 0; + char model[64]; + model[0] = 0; + char vumodel[64]; + vumodel[0] = 0; + int8_t azmodel = 0; + FILE *f; + + if ((f = fopen("/proc/stb/info/azmodel", "r"))){ azmodel = 1; fclose(f);} + read_line_from_file("/proc/stb/info/model", model, sizeof(model)); + read_line_from_file("/proc/stb/info/boxtype", boxtype, sizeof(boxtype)); + read_line_from_file("/proc/stb/info/vumodel", vumodel, sizeof(vumodel)); + if (vumodel[0] && !boxtype[0] && !azmodel) + { + snprintf(boxtype, sizeof(boxtype), "vu%s", vumodel); + } + if (!boxtype[0] && azmodel) + snprintf(boxtype, sizeof(boxtype), "Azbox-%s", model); + + // Detect dreambox type + if (strcasecmp(buffer.machine, "ppc") == 0 && !model[0] && !boxtype[0]) + { + char line[128], *p; + int have_dreambox = 0; + if ((f = fopen("/proc/cpuinfo", "r"))) + { + while (fgets(line, sizeof(line), f)) + { + if (strstr(line, "STBx25xx")) have_dreambox++; + if (strstr(line, "pvr" )) have_dreambox++; + if (strstr(line, "Dreambox")) have_dreambox++; + if (strstr(line, "9.80" )) have_dreambox++; + if (strstr(line, "63MHz" )) have_dreambox++; + } + fclose(f); + have_dreambox = have_dreambox == 5 ? 1 : 0; // Need to find all 5 strings + } + if (have_dreambox) + { + if (read_line_from_file("/proc/meminfo", line, sizeof(line)) && (p = strchr(line, ' '))) + { + unsigned long memtotal = strtoul(p, NULL, 10); + if (memtotal > 40000) + snprintf(boxtype, sizeof(boxtype), "%s", "dm600pvr"); + else + snprintf(boxtype, sizeof(boxtype), "%s", "dm500"); + } + } + } + + if (!boxtype[0] && !strcasecmp(model, "dm800") && !strcasecmp(buffer.machine, "armv7l")) + snprintf(boxtype, sizeof(boxtype), "%s", "su980"); + + if (!boxtype[0]) + { + uint8_t *pos; + pos = (uint8_t *)memchr(buffer.release, 'd', sizeof(buffer.release)); + if(pos) + { + if((!memcmp(pos, "dbox2", sizeof("dbox2"))) && !strcasecmp(buffer.machine, "ppc")) + { + snprintf(boxtype, sizeof(boxtype), "%s", "dbox2"); + } + } + } + + if (model[0]) + cs_log("Stb model = %s", model); + + if (vumodel[0]) + cs_log("Stb vumodel = vu%s", vumodel); + + if (boxtype[0]) + { + char boxname[128]; + if(!strcasecmp(boxtype,"ini-8000am")){snprintf(boxname, sizeof(boxname), "%s", "Atemio Nemesis");} + else if(!strcasecmp(boxtype,"ini-9000ru")){snprintf(boxname, sizeof(boxname), "%s", "Sezam Marvel");} + else if(!strcasecmp(boxtype,"ini-8000sv")){snprintf(boxname, sizeof(boxname), "%s", "Miraclebox Ultra");} + else if(!strcasecmp(boxtype,"ini-9000de")){snprintf(boxname, sizeof(boxname), "%s", "Xpeed LX3");} + else boxname[0] = 0; + if(boxname[0]){cs_log("Stb boxname = %s", boxname); stb_boxname = cs_strdup(boxname);} + cs_log("Stb boxtype = %s", boxtype); + } + + if (boxtype[0]) + stb_boxtype = cs_strdup(boxtype); + else if (model[0]) + stb_boxtype = cs_strdup(model); +} + +const char *boxtype_get(void) +{ + return stb_boxtype ? stb_boxtype : "generic"; +} + +const char *boxname_get(void) +{ + return stb_boxname ? stb_boxname : "generic"; +} + +bool boxtype_is(const char *boxtype) +{ + return strcasecmp(boxtype_get(), boxtype) == 0; +} + +bool boxname_is(const char *boxname) +{ + return strcasecmp(boxname_get(), boxname) == 0; +} + +/* Checks if the date of the system is correct and waits if necessary. */ +static void init_check(void) +{ + char *ptr = __DATE__; + int32_t month, year = atoi(ptr + cs_strlen(ptr) - 4), day = atoi(ptr + 4); + if(day > 0 && day < 32 && year > 2010 && year < 9999) + { + struct tm timeinfo; + char months[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + for(month = 0; month < 12; ++month) + { + if(!strncmp(ptr, months[month], 3)) { break; } + } + if(month > 11) { month = 0; } + memset(&timeinfo, 0, sizeof(timeinfo)); + timeinfo.tm_mday = day; + timeinfo.tm_mon = month; + timeinfo.tm_year = year - 1900; + time_t builddate = mktime(&timeinfo) - 86400; + int32_t i = 0; + while(time((time_t *)0) < builddate) + { + if(i == 0) { cs_log("The current system time is smaller than the build date (%s). Waiting up to %d seconds for time to correct", ptr, cs_waittime); } + cs_sleepms(1000); + ++i; + if(i > cs_waittime) + { + cs_log("Waiting was not successful. OSCam will be started but is UNSUPPORTED this way. Do not report any errors with this version."); + break; + } + } + // adjust login time of first client + if(i > 0) { first_client->login = time((time_t *)0); } + } +} + +#ifdef __linux__ +#include +// PR_SET_NAME is introduced in 2.6.9 (which is ancient, released 18 Oct 2004) +// but apparantly we can't count on having at least that version :( +#ifndef PR_SET_NAME +#define PR_SET_NAME 15 +#endif +// Set the thread name (comm) under linux (the limit is 16 chars) +void set_thread_name(const char *thread_name) +{ + prctl(PR_SET_NAME, thread_name, NULL, NULL, NULL); +} +#else +void set_thread_name(const char *UNUSED(thread_name)) { } +#endif + + +static void fix_stacksize(void) +{ +// Changing the default stack size is generally a bad idea. +// We are doing it anyway at the moment, because we are using several threads, +// and are running on machnies with little RAM. +// HOWEVER, as we do not know which minimal stack size is needed to run +// oscam without SEQFAULT (stack overflow), this is risky business. +// If after a code change SEQFAULTs related to stack overflow appear, +// increase OSCAM_STACK_MIN or remove the calls to SAFE_ATTR_SETSTACKSIZE. + +#ifndef PTHREAD_STACK_MIN +#define PTHREAD_STACK_MIN 64000 +#endif +#define OSCAM_STACK_MIN PTHREAD_STACK_MIN + 32768 + + if(oscam_stacksize < OSCAM_STACK_MIN) + { + long pagesize = sysconf(_SC_PAGESIZE); + if(pagesize < 1) + { + oscam_stacksize = OSCAM_STACK_MIN; + return; + } + + oscam_stacksize = ((OSCAM_STACK_MIN) / pagesize + 1) * pagesize; + } +} + +/* Starts a thread named nameroutine with the start function startroutine. */ +int32_t start_thread(char *nameroutine, void *startroutine, void *arg, pthread_t *pthread, int8_t detach, int8_t modify_stacksize) +{ + pthread_t temp; + pthread_attr_t attr; + + cs_log_dbg(D_TRACE, "starting thread %s", nameroutine); + + SAFE_ATTR_INIT(&attr); + + if(modify_stacksize) + { SAFE_ATTR_SETSTACKSIZE(&attr, oscam_stacksize); } + + int32_t ret = pthread_create(pthread == NULL ? &temp : pthread, &attr, startroutine, arg); + if(ret) + { cs_log("ERROR: can't create %s thread (errno=%d %s)", nameroutine, ret, strerror(ret)); } + else + { + cs_log_dbg(D_TRACE, "%s thread started", nameroutine); + + if(detach) + { pthread_detach(pthread == NULL ? temp : *pthread); } + } + + pthread_attr_destroy(&attr); + + return ret; +} + +int32_t start_thread_nolog(char *nameroutine, void *startroutine, void *arg, pthread_t *pthread, int8_t detach, int8_t modify_stacksize) +{ + pthread_t temp; + pthread_attr_t attr; + + SAFE_ATTR_INIT(&attr); + + if(modify_stacksize) + { SAFE_ATTR_SETSTACKSIZE(&attr, oscam_stacksize); } + + int32_t ret = pthread_create(pthread == NULL ? &temp : pthread, &attr, startroutine, arg); + if(ret) + { fprintf(stderr, "ERROR: can't create %s thread (errno=%d %s)", nameroutine, ret, strerror(ret)); } + else + { + if(detach) + { pthread_detach(pthread == NULL ? temp : *pthread); } + } + + pthread_attr_destroy(&attr); + + return ret; +} + +/* Allows to kill another thread specified through the client cl with locking. + If the own thread has to be cancelled, cs_exit or cs_disconnect_client has to be used. */ +void kill_thread(struct s_client *cl) +{ + if(!cl || cl->kill) { return; } + if(cl == cur_client()) + { + cs_log("Trying to kill myself, exiting."); + cs_exit(0); + } + add_job(cl, ACTION_CLIENT_KILL, NULL, 0); //add kill job, ... + cl->kill = 1; //then set kill flag! +} + +struct s_module *get_module(struct s_client *cl) +{ + return &modules[cl->module_idx]; +} + +void module_reader_set(struct s_reader *rdr) +{ + int i; + if(!is_cascading_reader(rdr)) + { return; } + for(i = 0; i < CS_MAX_MOD; i++) + { + struct s_module *module = &modules[i]; + if(module->num && module->num == rdr->typ) + rdr->ph = *module; + } +} + +static void cs_waitforcardinit(void) +{ + if(cfg.waitforcards) + { + cs_log("waiting for local card init"); + int32_t card_init_done; + do + { + card_init_done = 1; + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->enable && !is_cascading_reader(rdr) && (rdr->card_status == CARD_NEED_INIT || rdr->card_status == UNKNOWN)) + { + card_init_done = 0; + break; + } + } + + if(!card_init_done) + { cs_sleepms(300); } // wait a little bit + //alarm(cfg.cmaxidle + cfg.ctimeout / 1000 + 1); + } + while(!card_init_done && !exit_oscam); + + if(cfg.waitforcards_extra_delay > 0 && !exit_oscam) + { cs_sleepms(cfg.waitforcards_extra_delay); } + cs_log("init for all local cards done"); + } +} + +static uint32_t resize_pfd_cllist(struct pollfd **pfd, struct s_client ***cl_list, uint32_t old_size, uint32_t new_size) +{ + if(old_size != new_size) + { + struct pollfd *pfd_new; + if(!cs_malloc(&pfd_new, new_size * sizeof(struct pollfd))) + { + return old_size; + } + struct s_client **cl_list_new; + if(!cs_malloc(&cl_list_new, new_size * sizeof(cl_list))) + { + NULLFREE(pfd_new); + return old_size; + } + if(old_size > 0) + { + memcpy(pfd_new, *pfd, old_size * sizeof(struct pollfd)); + memcpy(cl_list_new, *cl_list, old_size * sizeof(cl_list)); + NULLFREE(*pfd); + NULLFREE(*cl_list); + } + *pfd = pfd_new; + *cl_list = cl_list_new; + } + return new_size; +} + +static uint32_t chk_resize_cllist(struct pollfd **pfd, struct s_client ***cl_list, uint32_t cur_size, uint32_t chk_size) +{ + chk_size++; + if(chk_size > cur_size) + { + uint32_t new_size = ((chk_size % 100) + 1) * 100; //increase 100 step + cur_size = resize_pfd_cllist(pfd, cl_list, cur_size, new_size); + } + return cur_size; +} + +static void process_clients(void) +{ + int32_t i, k, j, rc, pfdcount = 0; + struct s_client *cl; + struct s_reader *rdr; + struct pollfd *pfd; + struct s_client **cl_list; + struct timeb start, end; // start time poll, end time poll + uint32_t cl_size = 0; + + uint8_t buf[10]; + + if(pipe(thread_pipe) == -1) + { + printf("cannot create pipe, errno=%d\n", errno); + exit(1); + } + + cl_size = chk_resize_cllist(&pfd, &cl_list, 0, 100); + + pfd[pfdcount].fd = thread_pipe[0]; + pfd[pfdcount].events = POLLIN | POLLPRI; + cl_list[pfdcount] = NULL; + + while(!exit_oscam) + { + pfdcount = 1; + + // connected tcp clients + for(cl = first_client->next; cl; cl = cl->next) + { + if(cl->init_done && !cl->kill && cl->pfd && cl->typ == 'c' && !cl->is_udp) + { + if(cl->pfd && !cl->thread_active) + { + cl_size = chk_resize_cllist(&pfd, &cl_list, cl_size, pfdcount); + cl_list[pfdcount] = cl; + pfd[pfdcount].fd = cl->pfd; + pfd[pfdcount++].events = POLLIN | POLLPRI; + } + } + //reader: + //TCP: + // - TCP socket must be connected + // - no active init thread + //UDP: + // - connection status ignored + // - no active init thread + rdr = cl->reader; + if(rdr && cl->typ == 'p' && cl->init_done) + { + if(cl->pfd && !cl->thread_active && ((rdr->tcp_connected && rdr->ph.type == MOD_CONN_TCP) || (rdr->ph.type == MOD_CONN_UDP))) + { + cl_size = chk_resize_cllist(&pfd, &cl_list, cl_size, pfdcount); + cl_list[pfdcount] = cl; + pfd[pfdcount].fd = cl->pfd; + pfd[pfdcount++].events = (POLLIN | POLLPRI); + } + } + } + + //server (new tcp connections or udp messages) + for(k = 0; k < CS_MAX_MOD; k++) + { + struct s_module *module = &modules[k]; + if((module->type & MOD_CONN_NET)) + { + for(j = 0; j < module->ptab.nports; j++) + { + if(module->ptab.ports[j].fd) + { + cl_size = chk_resize_cllist(&pfd, &cl_list, cl_size, pfdcount); + cl_list[pfdcount] = NULL; + pfd[pfdcount].fd = module->ptab.ports[j].fd; + pfd[pfdcount++].events = (POLLIN | POLLPRI); + } + } + } + } + + if(pfdcount >= 1024) + { cs_log("WARNING: too many users!"); } + cs_ftime(&start); // register start time + rc = poll(pfd, pfdcount, 5000); + if(rc < 1) { continue; } + cs_ftime(&end); // register end time + + for(i = 0; i < pfdcount && rc > 0; i++) + { + if(pfd[i].revents == 0) { continue; } // skip sockets with no changes + rc--; //event handled! + cs_log_dbg(D_TRACE, "[OSCAM] new event %d occurred on fd %d after %"PRId64" ms inactivity", pfd[i].revents, + pfd[i].fd, comp_timeb(&end, &start)); + //clients + cl = cl_list[i]; + if(cl && !is_valid_client(cl)) + { continue; } + + if(pfd[i].fd == thread_pipe[0] && (pfd[i].revents & (POLLIN | POLLPRI))) + { + // a thread ended and cl->pfd should be added to pollfd list again (thread_active==0) + int32_t len = read(thread_pipe[0], buf, sizeof(buf)); + if(len == -1) + { + cs_log_dbg(D_TRACE, "[OSCAM] Reading from pipe failed (errno=%d %s)", errno, strerror(errno)); + } + cs_log_dump_dbg(D_TRACE, buf, len, "[OSCAM] Readed:"); + continue; + } + + //clients + // message on an open tcp connection + if(cl && cl->init_done && cl->pfd && (cl->typ == 'c' || cl->typ == 'm')) + { + if(pfd[i].fd == cl->pfd && (pfd[i].revents & (POLLHUP | POLLNVAL | POLLERR))) + { + //client disconnects + kill_thread(cl); + continue; + } + if(pfd[i].fd == cl->pfd && (pfd[i].revents & (POLLIN | POLLPRI))) + { + add_job(cl, ACTION_CLIENT_TCP, NULL, 0); + } + } + + //reader + // either an ecm answer, a keepalive or connection closed from a proxy + // physical reader ('r') should never send data without request + rdr = NULL; + struct s_client *cl2 = NULL; + if(cl && cl->typ == 'p') + { + rdr = cl->reader; + if(rdr) + { cl2 = rdr->client; } + } + + if(rdr && cl2 && cl2->init_done) + { + if(cl2->pfd && pfd[i].fd == cl2->pfd && (pfd[i].revents & (POLLHUP | POLLNVAL | POLLERR))) + { + //connection to remote proxy was closed + //oscam should check for rdr->tcp_connected and reconnect on next ecm request sent to the proxy + network_tcp_connection_close(rdr, "closed"); + rdr_log_dbg(rdr, D_READER, "connection closed"); + } + if(cl2->pfd && pfd[i].fd == cl2->pfd && (pfd[i].revents & (POLLIN | POLLPRI))) + { + add_job(cl2, ACTION_READER_REMOTE, NULL, 0); + } + } + + //server sockets + // new connection on a tcp listen socket or new message on udp listen socket + if(!cl && (pfd[i].revents & (POLLIN | POLLPRI))) + { + for(k = 0; k < CS_MAX_MOD; k++) + { + struct s_module *module = &modules[k]; + if((module->type & MOD_CONN_NET)) + { + for(j = 0; j < module->ptab.nports; j++) + { + if(module->ptab.ports[j].fd && module->ptab.ports[j].fd == pfd[i].fd) + { + accept_connection(module, k, j); + } + } + } + } + } + } + cs_ftime(&start); // register start time for new poll next run + first_client->last = time((time_t *)0); + } + NULLFREE(pfd); + NULLFREE(cl_list); + return; +} + +static pthread_cond_t reader_check_sleep_cond; +static pthread_mutex_t reader_check_sleep_cond_mutex; + +static void *reader_check(void) +{ + struct s_client *cl; + struct s_reader *rdr; + set_thread_name(__func__); + cs_pthread_cond_init(__func__, &reader_check_sleep_cond_mutex, &reader_check_sleep_cond); + while(!exit_oscam) + { + for(cl = first_client->next; cl ; cl = cl->next) + { + if(!cl->thread_active) + { client_check_status(cl); } + } + cs_readlock(__func__, &readerlist_lock); + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + if(rdr->enable) + { + cl = rdr->client; + if(!cl || cl->kill) + { restart_cardreader(rdr, 0); } + else if(!cl->thread_active) + { client_check_status(cl); } + } + } + cs_readunlock(__func__, &readerlist_lock); + sleepms_on_cond(__func__, &reader_check_sleep_cond_mutex, &reader_check_sleep_cond, 1000); + } + return NULL; +} + +static pthread_cond_t card_poll_sleep_cond; + +static void * card_poll(void) { + struct s_client *cl; + struct s_reader *rdr; + pthread_mutex_t card_poll_sleep_cond_mutex; + SAFE_MUTEX_INIT(&card_poll_sleep_cond_mutex, NULL); + SAFE_COND_INIT(&card_poll_sleep_cond, NULL); + set_thread_name(__func__); + while (!exit_oscam) { + cs_readlock(__func__, &readerlist_lock); + for (rdr=first_active_reader; rdr; rdr=rdr->next) { + if (rdr->enable && rdr->card_status == CARD_INSERTED) { + cl = rdr->client; + if (cl && !cl->kill) + { add_job(cl, ACTION_READER_POLL_STATUS, 0, 0); } + } + } + cs_readunlock(__func__, &readerlist_lock); + struct timespec ts; + struct timeval tv; + gettimeofday(&tv, NULL); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + ts.tv_sec += 1; + SAFE_MUTEX_LOCK(&card_poll_sleep_cond_mutex); + SAFE_COND_TIMEDWAIT(&card_poll_sleep_cond, &card_poll_sleep_cond_mutex, &ts); // sleep on card_poll_sleep_cond + SAFE_MUTEX_UNLOCK(&card_poll_sleep_cond_mutex); + } + return NULL; +} + +#ifdef WEBIF +static pid_t pid; + +static void fwd_sig(int32_t sig) +{ + kill(pid, sig); +} + +static void restart_daemon(void) +{ + while(1) + { + // start client process: + pid = fork(); + if(!pid) + { return; } // client process=oscam process + if(pid < 0) + { exit(1); } + + // set signal handler for the restart daemon: + set_signal_handler(SIGINT, 3, fwd_sig); +#if defined(__APPLE__) + set_signal_handler(SIGEMT, 3, fwd_sig); +#endif + set_signal_handler(SIGTERM, 3, fwd_sig); + set_signal_handler(SIGQUIT, 0, fwd_sig); + set_signal_handler(SIGHUP , 0, fwd_sig); + set_signal_handler(SIGUSR1, 0, fwd_sig); + set_signal_handler(SIGUSR2, 0, fwd_sig); + set_signal_handler(SIGALRM , 0, fwd_sig); + set_signal_handler(SIGWINCH, 1, SIG_IGN); + set_signal_handler(SIGPIPE , 0, SIG_IGN); + set_signal_handler(OSCAM_SIGNAL_WAKEUP, 0, SIG_IGN); + + // restart control process: + int32_t res = 0; + int32_t status = 0; + do + { + res = waitpid(pid, &status, 0); + if(res == -1) + { + if(errno != EINTR) + { exit(1); } + } + } + while(res != pid); + + if(cs_restart_mode == 2 && WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) + { status = 99; } // restart on segfault! + else + { status = WEXITSTATUS(status); } + + // status=99 restart oscam, all other->terminate + if(status != 99) + { + exit(status); + } + } +} + +void cs_restart_oscam(void) +{ + exit_oscam = 99; + cs_log("restart oscam requested"); +} + +int32_t cs_get_restartmode(void) +{ + return cs_restart_mode; +} +#endif + +void cs_exit_oscam(void) +{ + exit_oscam = 1; + cs_log("exit oscam requested"); +} + +static void pidfile_create(char *pidfile) +{ + FILE *f = fopen(pidfile, "w"); + if(f) + { + pid_t my_pid = getpid(); + cs_log("creating pidfile %s with pid %d", pidfile, my_pid); + fprintf(f, "%d\n", my_pid); + fclose(f); + } +} + +static bool running_under_valgrind; + +static void detect_valgrind(void) +{ +#ifdef __linux__ + char fname[32]; + snprintf(fname, sizeof(fname), "/proc/%d/maps", getpid()); + FILE *f = fopen(fname, "r"); + if (f) { + char line[256]; + while (fgets(line, sizeof(line), f)) { + if (strstr(line, "/valgrind/")) { + running_under_valgrind = true; + break; + } + } + fclose(f); + } +#endif +} + +#ifdef BUILD_TESTS +extern void run_all_tests(void); +__attribute__ ((noreturn)) static void run_tests(void) +{ + run_all_tests(); + exit(0); +} +#else +static void run_tests(void) { } +#endif + +const struct s_cardsystem *cardsystems[] = +{ +#ifdef READER_NAGRA + &reader_nagra, +#endif +#ifdef READER_NAGRA_MERLIN + &reader_nagracak7, +#endif +#ifdef READER_IRDETO + &reader_irdeto, +#endif +#ifdef READER_CONAX + &reader_conax, +#endif +#ifdef READER_CRYPTOWORKS + &reader_cryptoworks, +#endif +#ifdef READER_SECA + &reader_seca, +#endif +#ifdef READER_VIACCESS + &reader_viaccess, +#endif +#ifdef READER_VIDEOGUARD + &reader_videoguard1, + &reader_videoguard2, + &reader_videoguard12, +#endif +#ifdef READER_DRE + &reader_dre, +#endif +#ifdef READER_DRECAS + &reader_drecas, +#endif +#ifdef READER_TONGFANG + &reader_tongfang, +#endif +#ifdef READER_BULCRYPT + &reader_bulcrypt, +#endif +#ifdef READER_GRIFFIN + &reader_griffin, +#endif +#ifdef READER_DGCRYPT + &reader_dgcrypt, +#endif + NULL +}; + +const struct s_cardreader *cardreaders[] = +{ +#ifdef CARDREADER_DB2COM + &cardreader_db2com, +#endif +#if defined(CARDREADER_INTERNAL_AZBOX) + &cardreader_internal_azbox, +#elif defined(CARDREADER_INTERNAL_COOLAPI) + &cardreader_internal_cool, +#elif defined(CARDREADER_INTERNAL_COOLAPI2) + &cardreader_internal_cool, +#elif defined(CARDREADER_INTERNAL_AMSMC) + &cardreader_internal_amsmc, +#elif defined(CARDREADER_INTERNAL_SCI) + &cardreader_internal_sci, +#endif +#ifdef CARDREADER_PHOENIX + &cardreader_mouse, +#endif +#ifdef CARDREADER_DRECAS + &cardreader_drecas, +#endif +#ifdef CARDREADER_MP35 + &cardreader_mp35, +#endif +#ifdef CARDREADER_PCSC + &cardreader_pcsc, +#endif +#ifdef CARDREADER_SC8IN1 + &cardreader_sc8in1, +#endif +#ifdef CARDREADER_SMARGO + &cardreader_smargo, +#endif +#ifdef CARDREADER_SMART + &cardreader_smartreader, +#endif +#if defined(CARDREADER_STAPI) || defined(CARDREADER_STAPI5) + &cardreader_stapi, +#endif +#ifdef CARDREADER_STINGER + &cardreader_stinger, +#endif +#ifdef WITH_EMU + &cardreader_emu, +#endif + + NULL +}; + +static void find_conf_dir(void) +{ + static const char* confdirs[] = + { + "/etc/tuxbox/config/", + "/etc/tuxbox/config/oscam/", + "/var/tuxbox/config/", + "/usr/keys/", + "/var/keys/", + "/var/etc/oscam/", + "/var/etc/", + "/var/oscam/", + "/config/oscam/", + NULL + }; + + char conf_file[128+16]; + int32_t i; + + if(cs_confdir[cs_strlen(cs_confdir) - 1] != '/') + { cs_strncat(cs_confdir, "/", sizeof(cs_confdir)); } + + if(snprintf(conf_file, sizeof(conf_file), "%soscam.conf", cs_confdir) < 0) + { return; } + + if(!access(conf_file, F_OK)) + { return; } + + for(i=0; confdirs[i] != NULL; i++) + { + if(snprintf(conf_file, sizeof(conf_file), "%soscam.conf", confdirs[i]) < 0) + { return; } + + if (!access(conf_file, F_OK)) + { + cs_strncpy(cs_confdir, confdirs[i], sizeof(cs_confdir)); + return; + } + } +} + +int32_t main(int32_t argc, char *argv[]) +{ + fix_stacksize(); + + run_tests(); + int32_t i, j; + prog_name = argv[0]; + + if(pthread_key_create(&getclient, NULL)) + { + fprintf(stderr, "Could not create getclient, exiting..."); + exit(1); + } + + void (*mod_def[])(struct s_module *) = + { +#ifdef MODULE_MONITOR + module_monitor, +#endif +#ifdef MODULE_CAMD33 + module_camd33, +#endif +#ifdef MODULE_CAMD35 + module_camd35, +#endif +#ifdef MODULE_CAMD35_TCP + module_camd35_tcp, +#endif +#ifdef MODULE_NEWCAMD + module_newcamd, +#endif +#ifdef MODULE_CCCAM + module_cccam, +#endif +#ifdef MODULE_PANDORA + module_pandora, +#endif +#ifdef MODULE_GHTTP + module_ghttp, +#endif +#ifdef CS_CACHEEX + module_csp, +#endif +#ifdef MODULE_GBOX + module_gbox, +#endif +#ifdef MODULE_CONSTCW + module_constcw, +#endif +#ifdef MODULE_RADEGAST + module_radegast, +#endif +#ifdef MODULE_SCAM + module_scam, +#endif +#ifdef MODULE_SERIAL + module_serial, +#endif +#ifdef HAVE_DVBAPI + module_dvbapi, +#endif +#ifdef MODULE_STREAMRELAY + module_streamrelay, +#endif + 0 + }; + + set_default_dirs_first(); + + find_conf_dir(); + + parse_cmdline_params(argc, argv); + + cs_log("DEBUG: main - cs_confdir after parsing command line and finding config dir: '%s'", cs_confdir); + + if(bg && do_daemon(1, 0)) + { + printf("Error starting in background (errno=%d: %s)", errno, strerror(errno)); + cs_exit(1); + } + + get_random_bytes_init(); + +#ifdef WEBIF + if(cs_restart_mode) + { restart_daemon(); } +#endif + + memset(&cfg, 0, sizeof(struct s_config)); + cfg.max_pending = max_pending; + + if(cs_confdir[cs_strlen(cs_confdir) - 1] != '/') { cs_strncat(cs_confdir, "/", sizeof(cs_confdir)); } + init_signal_pre(); // because log could cause SIGPIPE errors, init a signal handler first + init_first_client(); + cs_lock_create(__func__, &system_lock, "system_lock", 5000); + cs_lock_create(__func__, &config_lock, "config_lock", 10000); + cs_lock_create(__func__, &gethostbyname_lock, "gethostbyname_lock", 10000); + cs_lock_create(__func__, &clientlist_lock, "clientlist_lock", 5000); + cs_lock_create(__func__, &readerlist_lock, "readerlist_lock", 5000); + cs_lock_create(__func__, &fakeuser_lock, "fakeuser_lock", 5000); + cs_lock_create(__func__, &ecmcache_lock, "ecmcache_lock", 5000); + cs_lock_create(__func__, &ecm_pushed_deleted_lock, "ecm_pushed_deleted_lock", 5000); + cs_lock_create(__func__, &cwcycle_lock, "cwcycle_lock", 5000); + init_cache(); + cacheex_init_hitcache(); + init_config(); +#ifdef CS_CACHEEX_AIO + init_cw_cache(); + init_ecm_cache(); +#endif + cs_init_log(); + init_machine_info(); + init_check(); + if(!oscam_pidfile && cfg.pidfile) + { oscam_pidfile = cfg.pidfile; } + if(!oscam_pidfile) + { + oscam_pidfile = get_tmp_dir_filename(default_pidfile, sizeof(default_pidfile), "oscam.pid"); + } + if(oscam_pidfile) + { pidfile_create(oscam_pidfile); } + cs_init_statistics(); + coolapi_open_all(); + init_stat(); + ssl_init(); + + // These initializations *MUST* be called after init_config() + // because modules depend on config values. + for(i = 0; mod_def[i]; i++) + { + struct s_module *module = &modules[i]; + mod_def[i](module); + } + + init_sidtab(); + init_readerdb(); +#ifdef WITH_EMU + add_emu_reader(); +#endif + cfg.account = init_userdb(); + init_signal(); + init_provid(); + init_srvid(); + init_tierid(); + init_fakecws(); + + start_garbage_collector(gbdb); + + cacheex_init(); + + write_versionfile(false); + + led_init(); + led_status_default(); + + azbox_init(); + + mca_init(); + + global_whitelist_read(); + ratelimit_read(); + +#ifdef MODULE_SERIAL + twin_read(); +#endif + + for(i = 0; i < CS_MAX_MOD; i++) + { + struct s_module *module = &modules[i]; + if((module->type & MOD_CONN_NET)) + { + for(j = 0; j < module->ptab.nports; j++) + { + start_listener(module, &module->ptab.ports[j]); + } + } + } + + // set time for server to now to avoid 0 in monitor/webif + first_client->last = time((time_t *)0); + + webif_init(); + + start_thread("reader check", (void *) &reader_check, NULL, NULL, 1, 1); + cw_process_thread_start(); + checkcache_process_thread_start(); + + lcd_thread_start(); + + do_report_emm_support(); + + init_cardreader(); + + cs_waitforcardinit(); + + emm_load_cache(); + load_emmstat_from_file(); + + led_status_starting(); + + ac_init(); + + gbox_send_init_hello(); + + start_thread("card poll", (void *) &card_poll, NULL, NULL, 1, 1); + + for(i = 0; i < CS_MAX_MOD; i++) + { + struct s_module *module = &modules[i]; + if((module->type & MOD_CONN_SERIAL) && module->s_handler) + { module->s_handler(NULL, NULL, i); } + } + + // main loop function + process_clients(); + + SAFE_COND_SIGNAL(&card_poll_sleep_cond); // Stop card_poll thread + cw_process_thread_wakeup(); // Stop cw_process thread + SAFE_COND_SIGNAL(&reader_check_sleep_cond); // Stop reader_check thread + + // Cleanup +#ifdef MODULE_GBOX + stop_gbx_ticker(); +#endif +#ifdef MODULE_STREAMRELAY + stop_stream_server(); +#endif + webif_close(); + azbox_close(); + coolapi_close_all(); + mca_close(); + + led_status_stopping(); + led_stop(); + lcd_thread_stop(); + + remove_versionfile(); + + stat_finish(); + dvbapi_stop_all_descrambling(0); + dvbapi_save_channel_cache(); + emm_save_cache(); + save_emmstat_to_file(); + + cccam_done_share(); + gbox_send_good_night(); + + kill_all_clients(); + kill_all_readers(); + for(i = 0; i < CS_MAX_MOD; i++) + { + struct s_module *module = &modules[i]; + if((module->type & MOD_CONN_NET)) + { + for(j = 0; j < module->ptab.nports; j++) + { + struct s_port *port = &module->ptab.ports[j]; + if(port->fd) + { + shutdown(port->fd, SHUT_RDWR); + close(port->fd); + port->fd = 0; + } + } + } + } + + if(oscam_pidfile) + { unlink(oscam_pidfile); } + + // sleep a bit, so hopefully all threads are stopped when we continue + cs_sleepms(200); + + free_cache(); +#ifdef CS_CACHEEX_AIO + free_ecm_cache(); +#endif + cacheex_free_hitcache(); + webif_tpls_free(); +#if defined(WEBIF) && defined(WEBIF_WIKI) + webif_wiki_free(); +#endif + init_free_userdb(cfg.account); + cfg.account = NULL; + init_free_sidtab(); + free_readerdb(); + config_free(); + ssl_done(); + + detect_valgrind(); + if (!running_under_valgrind) + cs_log("cardserver down"); + else + cs_log("running under valgrind, waiting 5 seconds before stopping cardserver"); + log_free(); + + if (running_under_valgrind) sleep(5); // HACK: Wait a bit for things to settle + + stop_garbage_collector(); + + NULLFREE(first_client->account); + NULLFREE(first_client); + free(stb_boxtype); + free(stb_boxname); + + // This prevents the compiler from removing config_mak from the final binary + syslog_ident = config_mak; + + return exit_oscam; +} diff --git a/reader-bulcrypt.c b/reader-bulcrypt.c new file mode 100644 index 0000000..2f8dad5 --- /dev/null +++ b/reader-bulcrypt.c @@ -0,0 +1,780 @@ +/* + * Bulcrypt card reader for OSCAM + * Copyright (C) 2012 Unix Solutions Ltd. + * + * Authors: Anton Tinchev (atl@unixsol.org) + * Georgi Chorbadzhiyski (gf@unixsol.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * ========================================================================= + * + * For more information read the code and the comments. We have tried to + * write clear code with lots of comments so it is easy for others to + * understand what is going on. There are some things marked *FIXME*, + * that are mostly unknown or not fully understand. + * + * WHAT WAS TESTED AND WAS WORKING: + * - Cards with bulcrypt v1 ("cherga"/carpet) are working (we have cards + * that report CardType: 0x4c and 0x75. + * - Cards return valid code words for subscribed channels. + * - Tested with channels encrypted with CAID 0x5581 and 0x4aee on + * Hellas 39E. Both MPEG2 (SD) and H.264 (SD and HD) channels were + * decrypted. + * - Brand new cards were inited without ever being put into providers STBs. + * as long the protocol you are using is sending EMMs to the card. + * - AU was working (subscription dates and packages were updated) + * as long the protocol you are using is sending EMMs to the card. + * + * WHAT WE DON'T KNOW (YET!): + * - How to deobfuscate v2 codewords. + * + * PERSONAL MESSAGES: + * - Many thanks to ilian_71 @ satfriends forum for the protocol info. + * - Shouts to yuriks for oscam-ymod, pity it is violating the GPL. + * + */ + +#include "globals.h" +#ifdef READER_BULCRYPT +#include "oscam-work.h" +#include "reader-common.h" + +static const uint8_t atr_carpet[] = { 0x3b, 0x20, 0x00 }; + +// *FIXME* We do not know how every 4th byte of the sess_key is calculated. +// Currently they are correct thou and code words checksums are correct are +// the deobfuscation. +static const uint8_t sess_key[] = { 0xF2, 0x21, 0xC5, 0x69, 0x28, 0x86, 0xFB, 0x9E, + 0xC0, 0x20, 0x28, 0x06, 0xD2, 0x23, 0x72, 0x31 }; + +static const uint8_t cmd_set_key[] = { 0xDE, 0x1C, 0x00, 0x00, 0x0A, 0x12, 0x08, 0x56, + 0x47, 0x38, 0x29, 0x10, 0xAF, 0xBE, 0xCD }; + +static const uint8_t cmd_set_key_v2[] = { 0xDE, 0x1C, 0x00, 0x00, 0x0A, 0x12, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +// Response: 90 00 + +// V2 +static const uint8_t cmd_card_v2_key1[] = { 0xDE, 0x12, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t cmd_card_v2_key2[] = { 0xDE, 0x1E, 0x00, 0x00, 0x12, 0x00 }; + +static const uint8_t cmd_cardtype1[] = { 0xDE, 0x16, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t cmd_cardtype2[] = { 0xDE, 0x1E, 0x00, 0x00, 0x03, 0x00 }; +// Response1: 90 03 +// Response2: 01 01 4C 90 00 or 01 01 xx 90 00 +// xx - 4C or 75 (Card type) + +static const uint8_t cmd_unkn_0a1[] = { 0xDE, 0x0A, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t cmd_unkn_0a2[] = { 0xDE, 0x1E, 0x00, 0x00, 0x03, 0x00 }; +// Response1: 90 03 +// Response2: 08 01 00 90 00 + +static const uint8_t cmd_cardsn1[] = { 0xDE, 0x18, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t cmd_cardsn2[] = { 0xDE, 0x1E, 0x00, 0x00, 0x06, 0x00 }; +// Response1: 90 06 +// Response2: 02 04 xx xx xx xy 90 00 +// xx - Card HEX serial +// y - Unknown *FIXME* + +static const uint8_t cmd_ascsn1[] = { 0xDE, 0x1A, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t cmd_ascsn2[] = { 0xDE, 0x1E, 0x00, 0x00, 0x0F, 0x00 }; +// Response1: 90 0F +// Response2: 05 0D xx xx 20 xx xx xx xx xx xx 20 xx xx xx 90 00 +// xx - Card ASCII serial + +static const uint8_t cmd_ecm_empty[] = { 0xDE, 0x20, 0x00, 0x00, 0x00, 0x00 }; +// Response: 90 00 + +static const uint8_t cmd_ecm[] = { 0xDE, 0x20, 0x00, 0x00, 0x4c }; +// The last byte is ECM length + +static const uint8_t cmd_ecm_get_cw[] = { 0xDE, 0x1E, 0x00, 0x00, 0x13, 0x00 }; +// Response: 0A 11 80 xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx 90 00 +// 80 - Returned codeword type? *FIXME* +// xx - Obfuscated CW + +static const uint8_t cmd_emm1[] = { 0xDE, 0x02, 0x82, 0x00, 0xb0 }; +// Response: 90 00 (EMM written OK) or +// Response: 90 0A (Subscription data was updated) +// The last byte is EMM length (0xb0) + +static const uint8_t cmd_emm2[] = { 0xDE, 0x04, 0x00, 0x00, 0xb0 }; +// Response: 90 00 (EMM written OK) +// cmd_emm[2] = emm_cmd1 +// cmd_emm[3] = emm_cmd2 +// The last byte is EMM length (0xb0) + +static const uint8_t cmd_sub_info1[] = { 0xDE, 0x06, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t cmd_sub_info2[] = { 0xDE, 0x1E, 0x00, 0x00, 0x2B, 0x00 }; +// See bulcrypt_card_info() for reponse description + +struct bulcrypt_data +{ + uint8_t bulcrypt_version; +}; + +static int32_t bulcrypt_card_init(struct s_reader *reader, ATR *newatr) +{ + int i; + char tmp[1024]; + char card_serial[16]; + const uint8_t *set_key_command; + uint8_t card_type; + + get_atr + def_resp + + if(memcmp(atr, atr_carpet, MIN(sizeof(atr_carpet), atr_size)) != 0) + { + if(atr_size == 3) + { + rdr_log(reader, "ATR_len=3 but ATR is unknown: %s", + cs_hexdump(1, atr, atr_size, tmp, sizeof(tmp))); + } + return ERROR; + } + + if(!cs_malloc(&reader->csystem_data, sizeof(struct bulcrypt_data))) + { return ERROR; } + + struct bulcrypt_data *csystem_data = reader->csystem_data; + + reader->nprov = 1; + memset(reader->prid, 0, sizeof(reader->prid)); + memset(reader->hexserial, 0, sizeof(reader->hexserial)); + memset(card_serial, 0, sizeof(card_serial)); + + rdr_log(reader, "Bulcrypt card detected, checking card version."); + + // Do we have Bulcrypt V2 card? + write_cmd(cmd_card_v2_key1, NULL); + write_cmd(cmd_card_v2_key2, NULL); + if(cta_lr < 18 || (cta_res[0] != 0x11 && cta_res[1] != 0x10)) + { + // The card is v1 + csystem_data->bulcrypt_version = 1; + set_key_command = cmd_set_key; + } + else + { + // The card is v2 + csystem_data->bulcrypt_version = 2; + set_key_command = cmd_set_key_v2; + } + + // Set CW obfuscation key + write_cmd(set_key_command, set_key_command + 5); + if(cta_lr < 2 || (cta_res[0] != 0x90 && cta_res[1] != 0x00)) + { + rdr_log(reader, "(cmd_set_key) Unexpected card answer: %s", + cs_hexdump(1, cta_res, cta_lr, tmp, sizeof(tmp))); + return ERROR; + } + + rdr_log(reader, "Bulcrypt v%d card detected.%s", csystem_data->bulcrypt_version, + csystem_data->bulcrypt_version != 1 ? " *UNSUPPORTED CARD VERSION*" : ""); + + // Read card type + write_cmd(cmd_cardtype1, NULL); + write_cmd(cmd_cardtype2, NULL); + if(cta_lr < 5 || (cta_res[0] != 0x01 && cta_res[1] != 0x01)) + { + rdr_log(reader, "(cmd_cardtype) Unexpected card answer: %s", + cs_hexdump(1, cta_res, cta_lr, tmp, sizeof(tmp))); + return ERROR; + } + card_type = cta_res[2]; // We have seen 0x4c and 0x75 + + // *FIXME* Unknown command + write_cmd(cmd_unkn_0a1, NULL); + write_cmd(cmd_unkn_0a2, NULL); + + // Read card HEX serial + write_cmd(cmd_cardsn1, NULL); + write_cmd(cmd_cardsn2, NULL); + if(cta_lr < 6 || (cta_res[0] != 0x02 && cta_res[1] != 0x04)) + { + rdr_log(reader, "(card_sn) Unexpected card answer: %s", + cs_hexdump(1, cta_res, cta_lr, tmp, sizeof(tmp))); + return ERROR; + } + memcpy(reader->hexserial, cta_res + 2, 4); + // Skip bottom four bits (they are 0x0b on our cards) + reader->hexserial[3] = reader->hexserial[3] & 0xF0; + + // Read card ASCII serial + write_cmd(cmd_ascsn1, NULL); + write_cmd(cmd_ascsn2, NULL); + + if(cta_lr < 15 || (cta_res[0] != 0x05 && cta_res[1] != 0x0d)) + { + rdr_log(reader, "(asc_sn) Unexpected card answer: %s", + cs_hexdump(1, cta_res, cta_lr, tmp, sizeof(tmp))); + return ERROR; + } + memcpy(card_serial, cta_res + 2, 13); + cta_lr = cs_strlen(card_serial); + + for(i = 0; i < cta_lr; i++) + { + if(card_serial[i] == ' ') + { continue; } + + // Sanity check + if(!isdigit((uint8_t)card_serial[i])) + { card_serial[i] = '*'; } + } + + // Write empty ECM, *FIXME* why are we doing this? To prepare the card somehow? + write_cmd(cmd_ecm_empty, NULL); + + // The HEX serial have nothing to do with Serial (they do not match) + rdr_log_sensitive(reader, "CAID: 0x4AEE|0x5581, CardType: 0x%02x, Serial: {%s}, HexSerial: {%02X %02X %02X %02X}", + card_type, + card_serial, + reader->hexserial[0], reader->hexserial[1], reader->hexserial[2], reader->hexserial[3]); + + rdr_log(reader, "Ready for requests."); + + return OK; +} + +static int cw_is_valid(struct s_reader *reader, uint8_t *cw) +{ + unsigned int i = 0, cnt = 0; + do + { + if(cw[i++] == 0) + { cnt++; } + } + while(i < 8); + + if(cnt == 8) + { + rdr_log(reader, "Invalid CW (all zeroes)"); + return ERROR; + } + + uint8_t cksum1 = cw[0] + cw[1] + cw[2]; + uint8_t cksum2 = cw[4] + cw[5] + cw[6]; + if(cksum1 != cw[3] || cksum2 != cw[7]) + { + if(cksum1 != cw[3]) + { rdr_log(reader, "Invalid CW (cksum1 mismatch expected 0x%02x got 0x%02x)", cksum1, cw[3]); } + if(cksum2 != cw[7]) + { rdr_log(reader, "Invalid CW (cksum2 mismatch expected 0x%02x got 0x%02x)", cksum2, cw[7]); } + return ERROR; + } + + return OK; +} + +/* +Bulcrypt ECM structure: + + 80 70 - ECM header (80 | 81) + 4c - ECM length after this field (0x4c == 76 bytes) + 4f 8d 87 0b - unixts == 1334675211 == Tue Apr 17 18:06:51 EEST 2012 + 00 66 - *FIXME* Program number? + 00 7d - *FIXME* + ce 70 - ECM counter + 0b 88 - ECM type + xx yy zz .. - Encrypted ECM payload (64 bytes) + +*/ +static int32_t bulcrypt_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + char tmp[512]; + uint8_t ecm_cmd[256]; + struct bulcrypt_data *csystem_data = reader->csystem_data; + + def_resp + + int32_t ecm_len = check_sct_len(er->ecm, 3); + if(ecm_len < 64 || ecm_len > 188) + { + rdr_log(reader, "Wrong ECM length: %d", ecm_len); + return ERROR; + } + + // CMD: DE 20 00 00 4C + memcpy(ecm_cmd, cmd_ecm, sizeof(cmd_ecm)); + ecm_cmd[4] = er->ecm[2]; // Set ECM length + memcpy(ecm_cmd + sizeof(cmd_ecm), er->ecm + 3, ecm_cmd[4]); + + // Send ECM + write_cmd(ecm_cmd, ecm_cmd + 5); + if(cta_lr != 2) + { + rdr_log(reader, "(ecm_cmd) Unexpected card answer: %s", + cs_hexdump(1, cta_res, cta_lr, tmp, sizeof(tmp))); + return ERROR; + } + + if(cta_res[0] == 0x90 && cta_res[1] == 0x03) + { + rdr_log(reader, "No active subscription."); + return ERROR; + } + + if(!(cta_res[0] == 0x90 && cta_res[1] == 0x13)) + { + rdr_log(reader, "(ecm_cmd) Unexpected card answer: %s", + cs_hexdump(1, cta_res, cta_lr, tmp, sizeof(tmp))); + return ERROR; + } + + // Call get_cw + write_cmd(cmd_ecm_get_cw, NULL); + + // rdr_log(reader, "CW_LOG: %s", cs_hexdump(1, cta_res, cta_lr, tmp, sizeof(tmp))); + if(cta_lr < 20 || (cta_res[0] != 0x0a && cta_res[1] != 0x11)) + { + rdr_log(reader, "(get_cw) Unexpected card answer: %s", + cs_hexdump(1, cta_res, cta_lr, tmp, sizeof(tmp))); + return ERROR; + } + + // *FIXME* is the bellow info true? + // 0x80 (ver 1) is supported + // 0xc0 (ver 2) is *NOT* supported currently + if(cta_res[2] == 0xc0) + { + rdr_log(reader, "Possibly unsupported codeword (bulcrypt v2): %s", + cs_hexdump(1, cta_res, cta_lr, tmp, sizeof(tmp))); + // *FIXME* commented for testing, this really should be an error + //return ERROR; + } + + // Remove code word obfuscation + uint8_t *cw = cta_res + 3; + if(csystem_data->bulcrypt_version == 1) + { + int i; + for(i = 0 ; i < 16; i++) + { + cw[i] = cw[i] ^ sess_key[i]; + } + } + + if(er->ecm[0] == 0x81) + { + // Even/Odd CWs should be exchanged + memcpy(ea->cw, cw + 8, 8); + memcpy(ea->cw + 8, cw, 8); + } + else + { + memcpy(ea->cw, cw, 8); + memcpy(ea->cw + 8, cw + 8, 8); + } + + // Check if DCW is valid + if(!cw_is_valid(reader, ea->cw) || !cw_is_valid(reader, ea->cw + 8)) + { return ERROR; } + + return OK; +} + +/* +Bulcrypt EMMs structure + +All EMMs are with section length 183 (0xb7) + 3 bytes section header + 7 bytes EMM header + 173 bytes payload + + 82 70 - UNUQUE_EMM_82|8a + b4 - Payload length (0xb4 == 180) + xx xx xx xy - Card HEX SN (the last 4 bits (y) must be masked) + payload + + 85 70 - GLOBAL_EMM_85|8b + b4 - Payload length (0xb4 == 180) + xx xx yy yy - Card HEX SN (the last 16 bits (y) must be masked) + payload + + 84 70 - SHARED_EMM_84 + b4 - Payload length (0xb4 == 180) + xx xx - Card HEX SN Prefix + yy - + zz - + payload + + Padding EMM: + 8f 70 b4 ff ff ff ff ff ff ff ff ff .. .. (ff to the end) + +Stats for EMMs collected for a period of 1 hours and 24 minutes + + 2279742 - 82 70 b4 - unique_82 + 19051 - 8a 70 b4 - unique_8a (polaris equivallent of 0x82) + 199949 - 84 70 b4 - shared_84 + 595309 - 85 70 b4 - global_85 + 6417 - 8b 70 b4 - global_8b (polaris equivallent of 0x85) + 74850 - 8f 70 b4 - filler + +Total EMMs for the period: 3175317 +*/ + +#define BULCRYPT_EMM_UNIQUE_82 0x82 // Addressed at single card (updates subscription info) +#define BULCRYPT_EMM_UNIQUE_8a 0x8a // Addressed at single card (like 0x82) used for Polaris +#define BULCRYPT_EMM_SHARED_84 0x84 // Addressed to 4096 cards (updates keys) +#define BULCRYPT_EMM_GLOBAL_85 0x85 // Addressed at 4096 cards (updates packages) +#define BULCRYPT_EMM_GLOBAL_8b 0x8b // Addressed at 4096 cards (like 0x85) used for Polaris +#define BULCRYPT_EMM_FILLER 0x8f // Filler to pad the EMM stream + +static int32_t bulcrypt_get_emm_type(EMM_PACKET *ep, struct s_reader *reader) +{ + char dump_emm_sn[64]; + int32_t emm_len = check_sct_len(ep->emm, 3); + + memset(ep->hexserial, 0, 8); + + if(emm_len < 176) + { + rdr_log_dbg(reader, D_TRACE | D_EMM, "emm_len < 176 (%u): %s", + emm_len, cs_hexdump(1, ep->emm, 12, dump_emm_sn, sizeof(dump_emm_sn))); + ep->type = UNKNOWN; + return 0; + } + + ep->type = UNKNOWN; + switch(ep->emm[0]) + { + case BULCRYPT_EMM_UNIQUE_82: + ep->type = UNIQUE; + break; // Bulsatcom + + case BULCRYPT_EMM_UNIQUE_8a: + ep->type = UNIQUE; + break; // Polaris + + case BULCRYPT_EMM_SHARED_84: + ep->type = SHARED; + break; + + case BULCRYPT_EMM_GLOBAL_85: + ep->type = GLOBAL; + break; // Bulsatcom + + case BULCRYPT_EMM_GLOBAL_8b: + ep->type = GLOBAL; + break; // Polaris + } + + bool ret = false; + if(ep->type == UNIQUE) + { + // The serial numbers looks like this: + // aa bb cc dd + memcpy(ep->hexserial, ep->emm + 3, 4); + ret = reader->hexserial[0] == ep->hexserial[0] && + reader->hexserial[1] == ep->hexserial[1] && + reader->hexserial[2] == ep->hexserial[2] && + ((reader->hexserial[3] & 0xF0) == (ep->hexserial[3] & 0xF0)); + } + else + { + // To match EMM_84, EMM_85, EMM_8b + // aa bb -- -- + memcpy(ep->hexserial, ep->emm + 3, 2); + ret = reader->hexserial[0] == ep->hexserial[0] && + reader->hexserial[1] == ep->hexserial[1]; + } + + if(ret) + { + char dump_card_sn[64]; + cs_hexdump(1, reader->hexserial, 4, dump_card_sn, sizeof(dump_card_sn)); + cs_hexdump(1, ep->hexserial, 4, dump_emm_sn, sizeof(dump_emm_sn)); + rdr_log_sensitive(reader, "EMM_%s-%02x, emm_sn = {%s}, card_sn = {%s}", + ep->type == UNIQUE ? "UNIQUE" : + ep->type == SHARED ? "SHARED" : + ep->type == GLOBAL ? "GLOBAL" : "??????", + ep->emm[0], + dump_emm_sn, + dump_card_sn); + } + + return ret; +} + +static int32_t bulcrypt_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 5; + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { return ERROR; } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].filter[1] = rdr->hexserial[0]; + filters[idx].filter[2] = rdr->hexserial[1]; + filters[idx].filter[3] = rdr->hexserial[2]; + filters[idx].filter[4] = rdr->hexserial[3]; + filters[idx].mask[0] = 0xFF; + filters[idx].mask[1] = 0xFF; + filters[idx].mask[2] = 0xFF; + filters[idx].mask[3] = 0xFF; + filters[idx].mask[4] = 0xF0; + idx++; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x8a; + filters[idx].filter[1] = rdr->hexserial[0]; + filters[idx].filter[2] = rdr->hexserial[1]; + filters[idx].filter[3] = rdr->hexserial[2]; + filters[idx].filter[4] = rdr->hexserial[3]; + filters[idx].mask[0] = 0xFF; + filters[idx].mask[1] = 0xFF; + filters[idx].mask[2] = 0xFF; + filters[idx].mask[3] = 0xFF; + filters[idx].mask[4] = 0xF0; + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x84; + filters[idx].filter[1] = rdr->hexserial[0]; + filters[idx].filter[2] = rdr->hexserial[1]; + filters[idx].mask[0] = 0xFF; + filters[idx].mask[1] = 0xFF; + filters[idx].mask[2] = 0xFF; + idx++; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x85; + filters[idx].filter[1] = rdr->hexserial[0]; + filters[idx].filter[2] = rdr->hexserial[1]; + filters[idx].mask[0] = 0xFF; + filters[idx].mask[1] = 0xFF; + filters[idx].mask[2] = 0xFF; + idx++; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x8b; + filters[idx].filter[1] = rdr->hexserial[0]; + filters[idx].filter[2] = rdr->hexserial[1]; + filters[idx].mask[0] = 0xFF; + filters[idx].mask[1] = 0xFF; + filters[idx].mask[2] = 0xFF; + idx++; + + *filter_count = idx; + } + + return OK; +} + +static int32_t bulcrypt_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + char tmp[512]; + uint8_t emm_cmd[1024]; + + def_resp + + // DE 04 xx yy B0 + // xx == EMM type (emm[0]) + // yy == EMM type2 (emm[5]) + // B0 == EMM len (176) + memcpy(emm_cmd, cmd_emm1, sizeof(cmd_emm1)); + memcpy(emm_cmd + sizeof(cmd_emm1), ep->emm + 7, 176); + + switch(ep->emm[0]) + { + case BULCRYPT_EMM_UNIQUE_82: + emm_cmd[2] = ep->emm[0]; // 0x82 + break; + + case BULCRYPT_EMM_UNIQUE_8a: // Polaris equivallent of 0x82 + emm_cmd[2] = 0x82; + emm_cmd[3] = 0x0b; + break; + + case BULCRYPT_EMM_SHARED_84: + emm_cmd[2] = ep->emm[0]; // 0x84 + emm_cmd[3] = ep->emm[5]; // 0x0b + break; + + case BULCRYPT_EMM_GLOBAL_85: + case BULCRYPT_EMM_GLOBAL_8b: // Polaris 0x85 equivallent of 0x85 + memcpy(emm_cmd, cmd_emm2, sizeof(cmd_emm2)); + emm_cmd[2] = ep->emm[5]; // 0xXX (Last bytes of the serial) + emm_cmd[3] = ep->emm[6]; // 0x0b + break; + } + + // Write emm + write_cmd(emm_cmd, emm_cmd + 5); + if(cta_lr != 2 || cta_res[0] != 0x90 || (cta_res[1] != 0x00 && cta_res[1] != 0x0a && cta_res[1] != 0x12)) + { + rdr_log(reader, "(emm_cmd) Unexpected card answer: %s", + cs_hexdump(1, cta_res, cta_lr, tmp, sizeof(tmp))); + return ERROR; + } + + // V2 answers of 82 EMM + if(cta_res[0] == 0x90 && cta_res[1] == 0x12) + { + write_cmd(cmd_card_v2_key2, NULL); + if(cta_res[18] == 0x90 && cta_res[19] == 0x12) + { + write_cmd(cmd_card_v2_key2, NULL); + } + } + + if(ep->emm[0] == BULCRYPT_EMM_UNIQUE_82 && cta_res[0] == 0x90 && (cta_res[1] == 0x0a || cta_res[1] == 0x00)) + { + rdr_log(reader, "Your subscription data was updated."); + add_job(reader->client, ACTION_READER_CARDINFO, NULL, 0); + } + + return OK; +} + +static char *dec2bin_str(unsigned int d, char *s) +{ + unsigned int i, r = 8; + memset(s, 0, 9); + for(i = 1; i < 256; i <<= 1) + { s[--r] = (d & i) == i ? '+' : '-'; } + return s; +} + +static int32_t bulcrypt_card_info(struct s_reader *reader) +{ + char tmp[512]; + time_t last_upd_ts, subs_end_ts; + struct tm tm; + def_resp + + rdr_log(reader, "Reading subscription info."); + + cs_clear_entitlement(reader); + + write_cmd(cmd_sub_info1, NULL); + write_cmd(cmd_sub_info2, NULL); + + if(cta_lr < 45) + { + rdr_log(reader, "(info_cmd) Unexpected card answer: %s", + cs_hexdump(1, cta_res, cta_lr, tmp, sizeof(tmp))); + return ERROR; + } + + // Response contains: + // 13 29 0B + // 4F 8F 00 E9 - Unix ts set by UNIQUE_EMM_82 + // 3C 65 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BF + // 3C 84 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BF + // 90 2B + + last_upd_ts = b2i(4, cta_res + 3); + subs_end_ts = last_upd_ts + (31 * 86400); // *FIXME* this is just a guess + + reader->card_valid_to = subs_end_ts; + + gmtime_r(&last_upd_ts, &tm); + memset(tmp, 0, sizeof(tmp)); + strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S %Z", &tm); + rdr_log(reader, "Subscription data last update : %s", tmp); + + gmtime_r(&subs_end_ts, &tm); + memset(tmp, 0, sizeof(tmp)); + strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S %Z", &tm); + rdr_log(reader, "Subscription should be active to : %s", tmp); + + unsigned int subs1 = b2i(2, cta_res + 3 + 4 + 16); + unsigned int subs2 = b2i(2, cta_res + 3 + 4 + 16 + 18); + + if(subs1 == 0xffff) + { + rdr_log(reader, "No active subscriptions (0x%04x, 0x%04x)", subs1, subs2); + } + else + { + unsigned int i; + rdr_log(reader, "Subscription data 1 (0x%04x): %s", + subs1, dec2bin_str(subs1, tmp)); + rdr_log(reader, "Subscription data 2 (0x%04x): %s", + subs2, dec2bin_str(subs2, tmp)); + + // Configure your tiers to get subscription packets name resolution + // # Example oscam.tiers file + // 5581:0001|Economic + // 5581:0002|Standard + // 5581:0004|Premium + // 5581:0008|HBO + // 5581:0010|Cinemax + // 5581:0020|Unknown Package 20 + // 5581:0040|Film Plus - Sport Plus HD & Hobby TV HD + // 5581:0080|Unknown Package 80 + for(i = 1; i < 256; i <<= 1) + { + if((subs1 & i) == i) + { + cs_add_entitlement(reader, 0x4AEE, + 0, /* provid */ + i, /* id */ + 0, /* class */ + last_upd_ts, /* start_ts */ + subs_end_ts, /* end_ts */ + 4, /* type: Tier */ + 1 /* add */ + ); + cs_add_entitlement(reader, 0x5581, + 0, /* provid */ + i, /* id */ + 0, /* class */ + last_upd_ts, /* start_ts */ + subs_end_ts, /* end_ts */ + 4, /* type: Tier */ + 1 /* add */ + ); + get_tiername(i, 0x4aee, tmp); + if(tmp[0] == 0x00) + { get_tiername(i, 0x5581, tmp); } + rdr_log(reader, "Package %02x is active: %s", i, tmp); + } + } + } + + rdr_log(reader, "End subscription info."); + return OK; +} + +const struct s_cardsystem reader_bulcrypt = +{ + .desc = "bulcrypt", + .caids = (uint16_t[]){ 0x5581, 0x4AEE, 0 }, + .do_emm = bulcrypt_do_emm, + .do_ecm = bulcrypt_do_ecm, + .card_info = bulcrypt_card_info, + .card_init = bulcrypt_card_init, + .get_emm_type = bulcrypt_get_emm_type, + .get_emm_filter = bulcrypt_get_emm_filter, +}; + +#endif diff --git a/reader-common.c b/reader-common.c new file mode 100644 index 0000000..8bc3283 --- /dev/null +++ b/reader-common.c @@ -0,0 +1,619 @@ +#include "globals.h" + +#ifdef WITH_CARDREADER + +#include "module-gbox.h" +#include "module-led.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-emm.h" +#include "oscam-net.h" +#include "oscam-time.h" +#include "oscam-work.h" +#include "oscam-reader.h" +#include "reader-common.h" +//#include "csctapi/atr.h" +#include "csctapi/icc_async.h" +#include "readers.h" // required by the EMU reader + +extern const struct s_cardsystem *cardsystems[]; +extern char *RDR_CD_TXT[]; + +int32_t check_sct_len(const uint8_t *data, int32_t off) +{ + int32_t len = SCT_LEN(data); + if(len + off > MAX_LEN) + { + cs_log_dbg(D_TRACE | D_READER, "check_sct_len(): smartcard section too long %d > %d", len, MAX_LEN - off); + len = -1; + } + return len; +} + +static void reader_nullcard(struct s_reader *reader) +{ + reader->csystem_active = false; + reader->csystem = NULL; + memset(reader->hexserial, 0, sizeof(reader->hexserial)); + memset(reader->prid, 0xFF, sizeof(reader->prid)); + memset(reader->sa, 0, sizeof(reader->sa)); +#if defined(READER_NAGRA) || defined(READER_NAGRA_MERLIN) + memset(reader->emm82u, 0, sizeof(reader->emm82u)); + memset(reader->emm84, 0, sizeof(reader->emm84)); + memset(reader->emm84s, 0, sizeof(reader->emm84s)); + memset(reader->emm83s, 0, sizeof(reader->emm83s)); + memset(reader->emm83u, 0, sizeof(reader->emm83u)); + memset(reader->emm87, 0, sizeof(reader->emm87)); +#endif + reader->caid = 0; + reader->nprov = 0; + cs_clear_entitlement(reader); +} + +int32_t reader_cmd2icc(struct s_reader *reader, const uint8_t *buf, const int32_t l, uint8_t *cta_res, uint16_t *p_cta_lr) +{ + int32_t rc; + *p_cta_lr = CTA_RES_LEN - 1; // FIXME not sure whether this one is necessary + rdr_log_dump_dbg(reader, D_READER, buf, l, "write to cardreader"); + rc = ICC_Async_CardWrite(reader, (uint8_t *)buf, (uint16_t)l, cta_res, p_cta_lr); + return rc; +} + +#define CMD_LEN 5 + +int32_t card_write(struct s_reader *reader, const uint8_t *cmd, const uint8_t *data, uint8_t *response, uint16_t *response_length) +{ + int32_t datalen = MAX_ECM_SIZE; // default datalen is max ecm size defined + uint8_t buf[MAX_ECM_SIZE + CMD_LEN]; + // always copy to be able to be able to use const buffer without changing all code + memcpy(buf, cmd, CMD_LEN); // copy command + + if(data) + { + if(cmd[4]) + { + datalen = cmd[4]; + } + memcpy(buf + CMD_LEN, data, datalen); + return (reader_cmd2icc(reader, buf, CMD_LEN + datalen, response, response_length)); + } + else + { return (reader_cmd2icc(reader, buf, CMD_LEN, response, response_length)); } +} + +static inline int reader_use_gpio(struct s_reader *reader) +{ + return reader->use_gpio && reader->detect > 4; +} + +static int32_t reader_card_inserted(struct s_reader *reader) +{ + if(!reader_use_gpio(reader) && (reader->detect & 0x7f) > 3) + { return 1; } + + int32_t card; + if(ICC_Async_GetStatus(reader, &card)) + { + rdr_log(reader, "Error getting card status."); + return 0; // corresponds with no card inside!! + } + return (card); +} + +static int32_t reader_activate_card(struct s_reader *reader, ATR *atr, uint16_t deprecated) +{ + int32_t i, ret; + + if(reader->card_status != CARD_NEED_INIT) + { return 0; } + + /* Activate card */ + for(i = 0; i < 3; i++) + { + ret = ICC_Async_Activate(reader, atr, deprecated); + if(!ret) + { break; } + rdr_log(reader, "Error activating card."); + led_status_card_activation_error(); + cs_sleepms(500); + } + if(ret) { return (0); } + + //rdr_log("ATR: %s", cs_hexdump(1, atr, atr_size, tmp, sizeof(tmp))); // FIXME + cs_sleepms(1000); + return (1); +} + +void cardreader_get_card_info(struct s_reader *reader) +{ + if((reader->card_status == CARD_NEED_INIT) || (reader->card_status == CARD_INSERTED)) + { + struct s_client *cl = reader->client; + if(cl) + { cl->last = time((time_t *)0); } + + if(reader->csystem_active && reader->csystem && reader->csystem->card_info) + { + reader->csystem->card_info(reader); + } + } +} + +void cardreader_poll_status(struct s_reader *reader) +{ + if (reader && reader->card_status == CARD_INSERTED) + { + if (reader->csystem_active && reader->csystem && reader->csystem->poll_status) + { reader->csystem->poll_status(reader); } + } +} + +static int32_t reader_get_cardsystem(struct s_reader *reader, ATR *atr) +{ + int32_t i; + +#ifdef WITH_EMU + if(reader->typ == R_EMU) + { + NULLFREE(reader->csystem_data); + rdr_log(reader, "found card system %s", reader_emu.desc); + reader->csystem = &reader_emu; + reader->csystem_active = true; + led_status_found_cardsystem(); + return (reader->csystem_active); + } +#endif + + for(i = 0; cardsystems[i]; i++) + { + NULLFREE(reader->csystem_data); + const struct s_cardsystem *csystem = cardsystems[i]; + if(csystem->card_init(reader, atr)) + { + rdr_log(reader, "found card system %s", csystem->desc); + reader->csystem = csystem; + reader->csystem_active = true; + led_status_found_cardsystem(); + break; + } + else + { + // On error free allocated card system data if any + if(csystem->card_done) + csystem->card_done(reader); + NULLFREE(reader->csystem_data); + } + } + + if(!reader->csystem_active) + { + rdr_log(reader, "card system not supported"); + led_status_unsupported_card_system(); + } + + return (reader->csystem_active); +} + +void cardreader_do_reset(struct s_reader *reader) +{ + reader_nullcard(reader); + ATR atr; + int32_t ret = 0; + int16_t i = 0; + int16_t j = 0; + + if (reader->typ == R_SMART && reader->smartdev_found >= 4) j = 1; else j = 1; // back to a single start + + for (i= 0; i < j; i++) + { + ret = ICC_Async_Reset(reader, &atr, reader_activate_card, reader_get_cardsystem); + + if(ret == -1) + { return; } + + if(ret == 0) + { + uint16_t y; + uint16_t deprecated; + + reader->resetalways = 0; + if (reader->typ == R_SMART && reader->smartdev_found >= 4) y = 2; else y = 2; + //rdr_log(reader, "the restart atempts in deprecated is %u", y); + + for(deprecated = reader->deprecated; deprecated < y; deprecated++) + { + if(!reader_activate_card(reader, &atr, deprecated)) { break; } + + ret = reader_get_cardsystem(reader, &atr); + if(ret) + { break; } + + if(!deprecated) + { rdr_log(reader, "Normal mode failed, reverting to Deprecated Mode"); } + } + //try reset reader before each command + if (!ret) + { + rdr_log(reader, "Try reset reader before each command"); + reader->resetalways = 1; + if(!reader_activate_card(reader, &atr, reader->deprecated)) { break; } + ret = reader_get_cardsystem(reader, &atr); + } + } + + if (ret) + { + rdr_log(reader,"THIS WAS A SUCCESSFUL START ATTEMPT No %u out of max allotted of %u", (i + 1), j); + break; + } + else + { + rdr_log(reader, "THIS WAS A FAILED START ATTEMPT No %u out of max allotted of %u", (i + 1), j); + } + } + + if(!ret) + { + reader->card_status = CARD_FAILURE; + rdr_log(reader, "card initializing error"); + ICC_Async_DisplayMsg(reader, "AER"); + led_status_card_activation_error(); + } + else + { + cardreader_get_card_info(reader); + reader->card_status = CARD_INSERTED; + do_emm_from_file(reader); + ICC_Async_DisplayMsg(reader, "AOK"); +#ifdef MODULE_GBOX + gbx_local_card_stat(LOCALCARDUP, reader->caid); // local card up +#endif + } + + return; +} + +static int32_t cardreader_device_init(struct s_reader *reader) +{ + int32_t rc = -1; // FIXME + if(ICC_Async_Device_Init(reader)) + { rdr_log(reader, "Cannot open device: %s", reader->device); } + else + { rc = OK; } + return ((rc != OK) ? 2 : 0); // exit code 2 means keep retrying, exit code 0 means all OK +} + +int32_t cardreader_do_checkhealth(struct s_reader *reader) +{ + struct s_client *cl = reader->client; + if(reader_card_inserted(reader)) + { + if(reader->card_status == NO_CARD || reader->card_status == UNKNOWN) + { + rdr_log(reader, "card detected"); + led_status_card_detected(); + reader->card_status = CARD_NEED_INIT; + add_job(cl, ACTION_READER_RESET, NULL, 0); + } + } + else + { + rdr_log_dbg(reader, D_READER, "%s: !reader_card_inserted", __func__); + if(reader->card_status == CARD_INSERTED || reader->card_status == CARD_NEED_INIT) + { + rdr_log(reader, "card ejected"); + reader_nullcard(reader); + if(reader->csystem && reader->csystem->card_done) + reader->csystem->card_done(reader); + NULLFREE(reader->csystem_data); + + if(cl) + { + cl->lastemm = 0; + cl->lastecm = 0; + } + led_status_card_ejected(); +#ifdef MODULE_GBOX + reader->card_status = NO_CARD; + gbx_local_card_stat(LOCALCARDEJECTED, reader->caid); +#endif + } + reader->card_status = NO_CARD; + } + rdr_log_dbg(reader, D_READER, "%s: reader->card_status = %d, ret = %d", __func__, + reader->card_status, reader->card_status == CARD_INSERTED); + + return reader->card_status == CARD_INSERTED; +} + +// Check for card inserted or card removed on pysical reader +void cardreader_checkhealth(struct s_client *cl, struct s_reader *rdr) +{ + if(!rdr || !rdr->enable || !rdr->active) + { return; } + add_job(cl, ACTION_READER_CHECK_HEALTH, NULL, 0); +} + +void cardreader_reset(struct s_client *cl) +{ + add_job(cl, ACTION_READER_RESET, NULL, 0); +} + +void cardreader_init_locks(void) +{ + ICC_Async_Init_Locks(); +} + +bool cardreader_init(struct s_reader *reader) +{ + struct s_client *client = reader->client; + client->typ = 'r'; + int8_t i = 0; + set_localhost_ip(&client->ip); + + while((cardreader_device_init(reader) == 2) && i < 10) + { + cs_sleepms(2000); + if(!ll_contains(configured_readers, reader) || !is_valid_client(client) || reader->enable != 1) + { return false; } + i++; + } + + if (i >= 10) + { + reader->card_status = READER_DEVICE_ERROR; + cardreader_close(reader); + reader->enable = 0; + return false; + } + else + { + if(reader->typ == R_INTERNAL) + { + if(boxtype_is("dm500") || boxtype_is("dm600pvr")) + {reader->cardmhz = 3150;} + + if(boxtype_is("dm7025")) + {reader->cardmhz = 8300;} + + if((!strncmp(boxtype_get(), "vu", 2 ))||(boxtype_is("ini-8000am"))) + {reader->cardmhz = 2700; reader->mhz = 450;} // only one speed for VU+ and Atemio Nemesis due to usage of TDA8024 + } + + if( + reader->typ == R_INTERNAL && ( + (strncmp(boxtype_get(), "dm500hdv2", 9) == 0) || + (strncmp(boxtype_get(), "dm800sev2", 9) == 0) || + (strncmp(boxtype_get(), "dm7020hd", 8) == 0) || + (strncmp(boxtype_get(), "dm500hd", 7) == 0) || + (strncmp(boxtype_get(), "dm800se", 7) == 0) || + (strncmp(boxtype_get(), "dm7080", 6) == 0) || + (strncmp(boxtype_get(), "dm8000", 6) == 0) || + (strncmp(boxtype_get(), "dm520", 5) == 0) || + (strncmp(boxtype_get(), "dm525", 5) == 0) || + (strncmp(boxtype_get(), "dm800", 5) == 0) || + (strncmp(boxtype_get(), "dm820", 5) == 0) || + (strncmp(boxtype_get(), "dm900", 5) == 0) || + (strncmp(boxtype_get(), "dm920", 5) == 0) || + (strncmp(boxtype_get(), "one", 3) == 0) || + (strncmp(boxtype_get(), "two", 3) == 0)) ) + { + rdr_log(reader, "Dreambox %s found! set Internal Card-MHz = 2700", boxtype_get() ); + reader->cardmhz = 2700; + return true; + } + + if((reader->cardmhz > 2000) && (reader->typ != R_SMART)) + { + rdr_log(reader, "Reader initialized (device=%s, detect=%s%s, pll max=%.2f MHz, wanted mhz=%.2f MHz)", + reader->device, + reader->detect & 0x80 ? "!" : "", + RDR_CD_TXT[reader->detect & 0x7f], + (float)reader->cardmhz / 100, + (float)reader->mhz / 100); + rdr_log(reader,"Reader sci internal, detected box type: %s", boxtype_get()); + } + else + { + if (reader->typ == R_SMART || is_smargo_reader(reader)) + { + rdr_log_dbg(reader, D_IFD, "clocking for smartreader with smartreader or smargo protocol"); + if (reader->cardmhz >= 2000) reader->cardmhz = 369; else + if (reader->cardmhz >= 1600) reader->cardmhz = 1600; else + if (reader->cardmhz >= 1200) reader->cardmhz = 1200; else + if (reader->cardmhz >= 961) reader->cardmhz = 961; else + if (reader->cardmhz >= 800) reader->cardmhz = 800; else + if (reader->cardmhz >= 686) reader->cardmhz = 686; else + if (reader->cardmhz >= 600) reader->cardmhz = 600; else + if (reader->cardmhz >= 534) reader->cardmhz = 534; else + if (reader->cardmhz >= 480) reader->cardmhz = 480; else + if (reader->cardmhz >= 436) reader->cardmhz = 436; else + if (reader->cardmhz >= 400) reader->cardmhz = 400; else + if (reader->cardmhz >= 369) reader->cardmhz = 369; else + if (reader->cardmhz == 357) reader->cardmhz = 369; else // 357 not a default smartreader setting + if (reader->cardmhz >= 343) reader->cardmhz = 343; else + reader->cardmhz = 320; + + if (reader->mhz >= 1600) reader->mhz = 1600; else + if (reader->mhz >= 1200) reader->mhz = 1200; else + if (reader->mhz >= 961) reader->mhz = 961; else + if (reader->mhz >= 900) reader->mhz = 900; else + if (reader->mhz >= 800) reader->mhz = 800; else + if (reader->mhz >= 686) reader->mhz = 686; else + if (reader->mhz >= 600) reader->mhz = 600; else + if (reader->mhz >= 534) reader->mhz = 534; else + if (reader->mhz >= 480) reader->mhz = 480; else + if (reader->mhz >= 436) reader->mhz = 436; else + if (reader->mhz >= 400) reader->mhz = 369; else + if (reader->mhz >= 369) reader->mhz = 369; else + if (reader->mhz == 357) reader->mhz = 369; else // 357 not a default smartreader setting + if (reader->mhz >= 343) reader->mhz = 343; else + reader->mhz = 320; + } + + if ((reader->typ == R_SMART || is_smargo_reader(reader)) && reader->autospeed == 1) + { + rdr_log(reader, "Reader initialized (device=%s, detect=%s%s, mhz= AUTO, cardmhz=%d)", + reader->device, + reader->detect & 0x80 ? "!" : "", + RDR_CD_TXT[reader->detect & 0x7f], + reader->cardmhz); + } + else + { + rdr_log(reader, "Reader initialized (device=%s, detect=%s%s, mhz=%d, cardmhz=%d)", + reader->device, + reader->detect & 0x80 ? "!" : "", + RDR_CD_TXT[reader->detect & 0x7f], + reader->mhz, + reader->cardmhz); + + if (reader->typ == R_INTERNAL && !(reader->cardmhz > 2000)) + rdr_log(reader,"Reader sci internal, detected box type: %s", boxtype_get()); + } + } + return true; + } +} + +void cardreader_close(struct s_reader *reader) +{ + ICC_Async_Close(reader); +} + +void reader_post_process(struct s_reader *reader) +{ + // some systems eg. nagra2/3 needs post process after receiving cw from card + // To save ECM/CW time we added this function after writing ecm answer + if(reader->csystem_active && reader->csystem && reader->csystem->post_process) + { + reader->csystem->post_process(reader); + } +} + +int32_t cardreader_do_ecm(struct s_reader *reader, ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + int32_t rc = -1; + if((rc = cardreader_do_checkhealth(reader))) + { + rdr_log_dbg(reader, D_READER, "%s: cardreader_do_checkhealth returned rc=%d", __func__, rc); + struct s_client *cl = reader->client; + if(cl) + { + cl->last_srvid = er->srvid; + cl->last_caid = er->caid; + cl->last_provid = er->prid; + cl->last = time((time_t *)0); + } + + if(reader->csystem_active && reader->csystem && reader->csystem->do_ecm) + { + rc = reader->csystem->do_ecm(reader, er, ea); + rdr_log_dbg(reader, D_READER, "%s: after csystem->do_ecm rc=%d", __func__, rc); + } + else + { rc = 0; } + } + rdr_log_dbg(reader, D_READER, "%s: ret rc=%d", __func__, rc); + return (rc); +} + +int32_t cardreader_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + int32_t rc; + + // check health does not work with new card status check but is actually not needed for emm. + if(reader->typ == R_SMART) + { + rc = 1; + } + else + { + rc = cardreader_do_checkhealth(reader); + } + + if(rc) + { + if((1 << (ep->emm[0] % 0x80)) & reader->b_nano) + { return 3; } + + if(reader->csystem_active && reader->csystem && reader->csystem->do_emm) + { rc = reader->csystem->do_emm(reader, ep); } + else + { rc = 0; } + } + + if(rc > 0) { cs_ftime(&reader->emm_last); } // last time emm written is now! + return (rc); +} + +int32_t cardreader_do_rawcmd(struct s_reader *reader, CMD_PACKET *cp) +{ + int32_t rc; + rc = -9; // means no dedicated support by csystem + if(reader->csystem_active && reader->csystem && reader->csystem->do_rawcmd) + { + rc = reader->csystem->do_rawcmd(reader, cp); + } + return (rc); +} + +void cardreader_process_ecm(struct s_reader *reader, struct s_client *cl, ECM_REQUEST *er) +{ + struct timeb tps, tpe; + struct s_ecm_answer ea; + memset(&ea, 0, sizeof(struct s_ecm_answer)); + +#ifdef WITH_EXTENDED_CW + // Correct CSA mode is CBC - default to that instead + ea.cw_ex.algo_mode = CW_ALGO_MODE_CBC; +#endif + + cs_ftime(&tps); + int32_t rc = cardreader_do_ecm(reader, er, &ea); + cs_ftime(&tpe); + + rdr_log_dbg(reader, D_READER, "%s: cardreader_do_ecm returned rc=%d (ERROR=%d)", __func__, rc, ERROR); + + ea.rc = E_FOUND; // default assume found + ea.rcEx = 0; // no special flag + + if(rc == ERROR) + { + char buf[CS_SERVICENAME_SIZE]; + rdr_log_dbg(reader, D_READER, "Error processing ecm for caid %04X, provid %06X, srvid %04X, servicename: %s", + er->caid, er->prid, er->srvid, get_servicename(cl, er->srvid, er->prid, er->caid, buf, sizeof(buf))); + ea.rc = E_NOTFOUND; + ea.rcEx = 0; + ICC_Async_DisplayMsg(reader, "Eer"); + } + + if(rc == E_CORRUPT) + { + char buf[CS_SERVICENAME_SIZE]; + rdr_log_dbg(reader, D_READER, "Error processing ecm for caid %04X, provid %06X, srvid %04X, servicename: %s", + er->caid, er->prid, er->srvid, get_servicename(cl, er->srvid, er->prid, er->caid, buf, sizeof(buf))); + ea.rc = E_NOTFOUND; + ea.rcEx = E2_WRONG_CHKSUM; // flag it as wrong checksum + memcpy(ea.msglog, "Invalid ecm type for card", 25); + } +#ifdef CS_CACHEEX_AIO + er->localgenerated = 1; +#endif + write_ecm_answer(reader, er, ea.rc, ea.rcEx, ea.cw, ea.msglog, ea.tier, &ea.cw_ex); + + cl->lastecm = time((time_t *)0); +#ifdef WITH_DEBUG + if(cs_dblevel & D_READER) + { + char ecmd5[17 * 3]; + cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); + + rdr_log_dbg(reader, D_READER, "ecm hash: %s real time: %"PRId64" ms", ecmd5, comp_timeb(&tpe, &tps)); + } +#endif + reader_post_process(reader); +} + +#endif diff --git a/reader-common.h b/reader-common.h new file mode 100644 index 0000000..69ea22b --- /dev/null +++ b/reader-common.h @@ -0,0 +1,83 @@ +#ifndef READER_COMMON_H_ +#define READER_COMMON_H_ + +//Warning: OK = 0 and ERROR = 1 in csctapi !!! +#define SKIPPED 2 +#define OK 1 +#define ERROR 0 + +#include "csctapi/atr.h" +#include "oscam-string.h" +#include "oscam-reader.h" + +int32_t reader_cmd2icc(struct s_reader *reader, const uint8_t *buf, const int32_t l, uint8_t *response, uint16_t *response_length); +int32_t card_write(struct s_reader *reader, const uint8_t *, const uint8_t *, uint8_t *, uint16_t *); + +#define write_cmd(cmd, data) \ + { \ + if (card_write(reader, cmd, data, cta_res, &cta_lr)) return ERROR; \ + } + +#define get_atr \ + uint8_t atr[ATR_MAX_SIZE]; \ + uint32_t atr_size; \ + memset(atr, 0, sizeof(atr)); \ + ATR_GetRaw(newatr, atr, &atr_size); + +#define get_atr2 \ + uint8_t atr2[ATR_MAX_SIZE]; \ + uint32_t atr_size2; \ + memset(atr2, 0, sizeof(atr2)); \ + ATR_GetRaw(newatr, atr2, &atr_size2); + +#define get_hist \ + uint8_t hist[ATR_MAX_HISTORICAL]; \ + uint32_t hist_size = 0; \ + ATR_GetHistoricalBytes(newatr, hist, &hist_size); + +#define def_resp \ + uint8_t cta_res[CTA_RES_LEN]; \ + memset(cta_res, 0, CTA_RES_LEN); \ + uint16_t cta_lr; + +#ifdef WITH_CARDREADER +void cardreader_init_locks(void); +bool cardreader_init(struct s_reader *reader); +void cardreader_close(struct s_reader *reader); +void cardreader_do_reset(struct s_reader *reader); +void cardreader_reset(struct s_client *cl); +int32_t cardreader_do_checkhealth(struct s_reader *reader); +void cardreader_checkhealth(struct s_client *cl, struct s_reader *rdr); +int32_t cardreader_do_emm(struct s_reader *reader, EMM_PACKET *ep); +int32_t cardreader_do_rawcmd(struct s_reader *reader, CMD_PACKET *cp); +void cardreader_process_ecm(struct s_reader *reader, struct s_client *cl, ECM_REQUEST *er); +void cardreader_get_card_info(struct s_reader *reader); +void cardreader_poll_status(struct s_reader *reader); +int32_t check_sct_len(const uint8_t *data, int32_t off); +#else +static inline void cardreader_init_locks(void) { } +static inline bool cardreader_init(struct s_reader *UNUSED(reader)) +{ + return true; +} +static inline void cardreader_close(struct s_reader *UNUSED(reader)) { } +static inline void cardreader_do_reset(struct s_reader *UNUSED(reader)) +{ + return; +} +static inline void cardreader_reset(struct s_client *UNUSED(cl)) { } +static inline int32_t cardreader_do_checkhealth(struct s_reader *UNUSED(reader)) +{ + return false; +} +static inline void cardreader_checkhealth(struct s_client *UNUSED(cl), struct s_reader *UNUSED(rdr)) { } +static inline int32_t cardreader_do_emm(struct s_reader *UNUSED(reader), EMM_PACKET *UNUSED(ep)) +{ + return 0; +} +static inline void cardreader_process_ecm(struct s_reader *UNUSED(reader), struct s_client *UNUSED(cl), ECM_REQUEST *UNUSED(er)) { } +static inline void cardreader_get_card_info(struct s_reader *UNUSED(reader)) { } +static inline void cardreader_poll_status(struct s_reader *UNUSED(reader)) { } +#endif + +#endif diff --git a/reader-conax.c b/reader-conax.c new file mode 100644 index 0000000..f0f2817 --- /dev/null +++ b/reader-conax.c @@ -0,0 +1,678 @@ +#include "globals.h" +#ifdef READER_CONAX +#include "cscrypt/bn.h" +#include "reader-common.h" +#include "cscrypt/des.h" + +static int32_t CWPK_CNX(struct s_reader *reader,uint8_t *msg) +{ + uint8_t CWp[16], CWs[16]; + + memcpy(CWp, msg + 7, 8); + memcpy(CWp + 8, msg + 22, 8); + + des_ecb3_decrypt(CWp, reader->cwpk_mod); + des_ecb3_decrypt(CWp + 8, reader->cwpk_mod); + + memcpy(CWs, CWp + 4, 4); + memcpy(CWs + 4, CWp, 4); + memcpy(CWs + 8, CWp + 12, 4); + memcpy(CWs + 12, CWp + 8, 4); + + int chkok = 1; + if(((CWs[0] + CWs[1] + CWs[2]) & 0xFF) != CWs[3]) + { + chkok = 0; + rdr_log(reader, "CW0 checksum error [0]"); + } + if(((CWs[4] + CWs[5] + CWs[6]) & 0xFF) != CWs[7]) + { + chkok = 0; + rdr_log(reader, "CW0 checksum error [1]"); + } + if(((CWs[8] + CWs[9] + CWs[10]) & 0xFF) != CWs[11]) + { + chkok = 0; + rdr_log(reader, "CW1 checksum error [0]"); + } + if(((CWs[12] + CWs[13] + CWs[14]) & 0xFF) != CWs[15]) + { + chkok = 0; + rdr_log(reader, "CW1 checksum error [1]"); + } + + if(!chkok) + return -8; + + memcpy(msg + 7, CWs, 8); + memcpy(msg + 22, CWs + 8, 8); + return 0; +} + +static int32_t RSA_CNX(struct s_reader *reader, uint8_t *msg, uint8_t *mod, uint8_t *exp, uint32_t cta_lr, uint32_t modbytes, uint32_t expbytes) +{ + int32_t ret = 0; + uint32_t n = 0, pre_size = 0, size = 0; + BN_CTX *ctx; + BIGNUM *bn_mod, *bn_exp, *bn_data, *bn_res; + uint8_t data[64]; + + /*prefix size*/ + pre_size = 2 + 4 + msg[5]; + + /*size of data to decryption*/ + if(msg[1] > (pre_size - 2)) + { size = msg[1] - pre_size + 2; } + + if(cta_lr > (pre_size + size) && size >= modbytes && size < 128) + { + ctx = BN_CTX_new(); + + if(ctx == NULL) + { + rdr_log_dbg(reader, D_READER, "RSA Error in RSA_CNX"); + return -1; + } + + BN_CTX_start(ctx); + bn_mod = BN_CTX_get(ctx); + bn_exp = BN_CTX_get(ctx); + bn_data = BN_CTX_get(ctx); + bn_res = BN_CTX_get(ctx); + + /*RSA first round*/ + BN_bin2bn(mod, modbytes, bn_mod); // rsa modulus + BN_bin2bn(exp, expbytes, bn_exp); // exponent + BN_bin2bn(msg + pre_size, modbytes, bn_data); + BN_mod_exp(bn_res, bn_data, bn_exp, bn_mod, ctx); + + n = BN_bn2bin(bn_res, data); + + size -= modbytes; // 3 + pre_size += modbytes; + + /*Check if second round is needed*/ + if(0 < size) + { + /*check if length of data from first RSA round will be enough to padding rest of data*/ + if((n + size) >= modbytes) + { + /*RSA second round*/ + /*move the remaining data at the beginning of the buffer*/ + memcpy(msg, msg + pre_size, size); + /*padding buffer with data from first round*/ + memcpy(msg + size, data + (n - (modbytes - size)), modbytes - size); + + BN_bin2bn(msg, modbytes, bn_data); + BN_mod_exp(bn_res, bn_data, bn_exp, bn_mod, ctx); + n = BN_bn2bin(bn_res, data); + if(0x25 != data[0]) + { ret = -1; } /*RSA key is probably wrong*/ + } + else + { ret = -3; } /*wrong size of data for second round*/ + } + + if(0 == ret) + { memcpy(msg, data, n); } + + BN_CTX_end(ctx); + BN_CTX_free(ctx); + } + else + { ret = -2; } /*wrong size of data*/ + + return ret; +} + +static time_t chid_date(const uint8_t *ptr, char *buf, int32_t l) +{ + time_t rc = 0; + struct tm timeinfo; + memset(&timeinfo, 0, sizeof(struct tm)); + + if(buf) + { + timeinfo.tm_year = 90 + (ptr[1] >> 4) + (((ptr[0] >> 5) & 7) * 10); + timeinfo.tm_mon = (ptr[1] & 0xf) - 1; + timeinfo.tm_mday = ptr[0] & 0x1f; + timeinfo.tm_isdst = -1; + rc = mktime(&timeinfo); + strftime(buf, l, "%Y/%m/%d", &timeinfo); + } + return (rc); +} + +static int32_t read_record(struct s_reader *reader, const uint8_t *cmd, const uint8_t *data, uint8_t *cta_res) +{ + uint16_t cta_lr; + uint8_t insCA[] = { 0xDD, 0xCA, 0x00, 0x00, 0x00 }; + + write_cmd(cmd, data); // select record + if(cta_res[0] != 0x98) + { return (-1); } + + insCA[4] = cta_res[1]; // get len + write_cmd(insCA, NULL); // read record + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1])) + { return (-1); } + + return (cta_lr - 2); +} + +static int32_t check_pairing(struct s_reader *reader, const uint8_t *cmd, const uint8_t *data, uint8_t *cta_res) +{ + uint16_t cta_lr; + + if(reader->cwpk_mod_length) + { + write_cmd(cmd, data); + rdr_log(reader, "CWPK Pairing is active"); + } + else if(reader->rsa_mod_length) + { + rdr_log(reader, "RSA Pairing is active"); + } + else + { + rdr_log(reader, "Pairing is not active"); + } + return OK; +} + +static uint8_t PairingECMRotation(struct s_reader *reader, const ECM_REQUEST *er, int32_t n) +{ + uint8_t cta_res[CTA_RES_LEN] = { 0x00 }; + uint8_t ins26[] = { 0xDD, 0x26, 0x00, 0x00, 0x03, 0x10, 0x01, 0x00 }; + uint8_t cnxcurrecm = 0; + + if(0x0 != reader->rsa_mod[0] && n > 3 && + 0x54 == er->ecm[n - 3] && + 0x02 == er->ecm[n - 2] && + 0x00 == er->ecm[n - 1]) + { + cnxcurrecm = 1; + } + + if((0 == reader->cnxlastecm) != (0 == cnxcurrecm)) + { + if(0 == cnxcurrecm) // not paired + { ins26[7] = 0x30; } + else + { ins26[7] = 0x40; } + + if(read_record(reader, ins26, ins26 + 5, cta_res) <= 0) + { rdr_log(reader, "PairingECMRotation - ERROR"); } + } + reader->cnxlastecm = cnxcurrecm; + return cnxcurrecm; +} + +static int32_t conax_card_init(struct s_reader *reader, ATR *newatr) +{ + uint8_t cta_res[CTA_RES_LEN]; + int32_t i, j, n; + static const uint8_t ins26[] = { 0xDD, 0x26, 0x00, 0x00, 0x03, 0x10, 0x01, 0x40 }; + static const uint8_t inscp[] = { 0xDD, 0x26, 0x00, 0x00, 0x04, 0x6C, 0x02, 0x10,0x00 }; + uint8_t ins82[] = { 0xDD, 0x82, 0x00, 0x00, 0x11, 0x11, 0x0f, 0x01, 0xb0, 0x0f, 0xff, + 0xff, 0xfb, 0x00, 0x00, 0x09, 0x04, 0x0b, 0x00, 0xe0, 0x30, 0x2b }; + + uint8_t cardver = 0; + + get_hist; + if((hist_size < 4) || (memcmp(hist, "0B00", 4))) + { return ERROR; } + + reader->caid = 0xB00; + + if((n = read_record(reader, ins26, ins26 + 5, cta_res)) <= 0) { return ERROR; } // read caid, card-version + + for(i = 0; i < n; i += cta_res[i + 1] + 2) + { + switch(cta_res[i]) + { + case 0x20: + cardver = cta_res[i + 2]; + break; + + case 0x28: + reader->caid = (cta_res[i + 2] << 8) | cta_res[i + 3]; + } + } + + // Ins82 command needs to use the correct CAID reported in nano 0x28 + ins82[17] = (reader->caid >> 8) & 0xFF; + ins82[18] = (reader->caid) & 0xFF; + + if((n = read_record(reader, ins82, ins82 + 5, cta_res)) <= 0) { return ERROR; } // read serial + + reader->nprov = 0; + + for(j = 0, i = 2; i < n; i += cta_res[i + 1] + 2) + { + switch(cta_res[i]) + { + case 0x23: + if(cta_res[i + 5] != 0x00) + { + memcpy(reader->hexserial, &cta_res[i + 3], 6); + } + else + { + memcpy(reader->sa[j], &cta_res[i + 5], 4); + j++; + reader->nprov++; + } + break; + } + } + + memset(reader->prid, 0x00, sizeof(reader->prid)); + + rdr_log_sensitive(reader, "type: Conax, caid: %04X, serial: {%llu}, hex serial: {%02x%02x%02x%02x}, card: v%d", + reader->caid, (unsigned long long) b2ll(6, reader->hexserial), reader->hexserial[2], + reader->hexserial[3], reader->hexserial[4], reader->hexserial[5], cardver); + + rdr_log(reader, "Providers: %d", reader->nprov); + + for(j = 0; j < reader->nprov; j++) + { + rdr_log(reader, "Provider: %d Provider-Id: %06X", j + 1, b2i(4, reader->prid[j])); + rdr_log_sensitive(reader, "Provider: %d SharedAddress: {%08X}", j + 1, b2i(4, reader->sa[j])); + } + check_pairing(reader, inscp, inscp + 5, cta_res); + + return OK; +} + +static int32_t conax_send_pin(struct s_reader *reader) +{ + def_resp; + uint8_t insPIN[] = { 0xDD, 0xC8, 0x00, 0x00, 0x07, 0x1D, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00 }; // Last four are the Pin-Code + memcpy(insPIN + 8, reader->pincode, 4); + + write_cmd(insPIN, insPIN + 5); + rdr_log_dbg(reader, D_READER, "Sent pincode to card."); + + return OK; +} + +static int32_t conax_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + def_resp; + int32_t i, j, n, num_dw = 0, rc = 0; + uint8_t insA2[] = { 0xDD, 0xA2, 0x00, 0x00, 0x00 }; + uint8_t insCA[] = { 0xDD, 0xCA, 0x00, 0x00, 0x00 }; + + uint8_t exp[] = { 0x01, 0x00, 0x01 }; + uint8_t buf[256]; + + char ppp = 0x00; + + if((n = check_sct_len(er->ecm, 3)) < 0) + { return ERROR; } + + buf[0] = 0x14; + buf[1] = n + 1; + + if(reader->cwpk_mod_length) + { + buf[2] = 4; + ppp = 0x01; + } + else if(0x0 != reader->rsa_mod[0]) + { + if(0x0 != PairingECMRotation(reader, er, n)) + { + buf[2] = 2; + ppp = 0x03; + } + else + { + buf[2] = 0; + ppp = 0x02; + } + } + else + { + buf[2] = 0; + ppp = 0x02; + } + + memcpy(buf + 3, er->ecm, n); + insA2[4] = n + 3; + + write_cmd(insA2, buf); // write Header + ECM + + while((cta_res[cta_lr - 2] == 0x98) && // Antwort + ((insCA[4] = cta_res[cta_lr - 1]) > 0) && (insCA[4] != 0xFF)) + { + write_cmd(insCA, NULL); // Codeword auslesen + + if((cta_res[cta_lr - 2] == 0x98) || ((cta_res[cta_lr - 2] == 0x90))) + { + /*checks if answer is encrypted with RSA algo and decrypts it if needed*/ + if(0x81 == cta_res[0] && 2 == cta_res[2] >> 5 && 0x03 == ppp) /*81 XX 5X*/ + { + if(0x00 == cta_res[cta_lr - 1]) + { rc = RSA_CNX(reader, cta_res, reader->rsa_mod, exp, cta_lr, 64u, 3u); } + else + { rc = -4; } /*card has no right to decode this channel*/ + } + else if(0x01 == ppp) + { + if(0x00 == cta_res[cta_lr - 1]) + { + /*trying to decode using CWPK*/ + rc = CWPK_CNX(reader, cta_res); /*enabled when no loging needed*/ + } + else + { + rc = -4; + } + } + + if(0 == rc) + { + for(i = 0; i < cta_lr - 2 && num_dw < 2; i += cta_res[i + 1] + 2) + { + switch(cta_res[i]) + { + case 0x25: + if((cta_res[i + 1] >= 0xD) && !((n = cta_res[i + 4]) & 0xFE)) + { + rc |= (1 << n); + memcpy(ea->cw + (n << 3), cta_res + i + 7, 8); + ++num_dw; + } + break; + + case 0x31: + if((cta_res[i + 1] == 0x02 && cta_res[i + 2] == 0x00 && cta_res[i + 3] == 0x00) || \ + (cta_res[i + 1] == 0x02 && cta_res[i + 2] == 0x40 && cta_res[i + 3] == 0x00)) + { break; } + else if(strcmp(reader->pincode, "none")) + { + conax_send_pin(reader); + write_cmd(insA2, buf); // write Header + ECM + + while((cta_res[cta_lr - 2] == 0x98) && // Antwort + ((insCA[4] = cta_res[cta_lr - 1]) > 0) && (insCA[4] != 0xFF)) + { + write_cmd(insCA, NULL); // Codeword auslesen + + if((cta_res[cta_lr - 2] == 0x98) || + ((cta_res[cta_lr - 2] == 0x90) && (!cta_res[cta_lr - 1]))) + { + for(j = 0; j < cta_lr - 2; j += cta_res[j + 1] + 2) + { + if((cta_res[j] == 0x25) && // access: is cw + (cta_res[j + 1] >= 0xD) && // 0xD: 5 header + 8 cw + !((n = cta_res[j + 4]) & 0xFE)) // cw idx must be 0 or 1 + { + rc |= (1 << n); + memcpy(ea->cw + (n << 3), cta_res + j + 7, 8); + ++num_dw; + } + } + } + } + } + break; + } + } + } + } + } + + switch(rc) + { + case -1: + rdr_log(reader, "conax decode ECM problem - RSA key is probably faulty"); + break; + + case -2: + rdr_log(reader, "conax RSA pairing - wrong size of data"); + break; + + case -3: + rdr_log(reader, "conax RSA pairing- wrong size of data for second round"); + /* fallthrough */ + + case -4: + rdr_log(reader, "card has no right to decode this channel"); + break; + + case -8: + rdr_log(reader, "CWPK is faulty"); + break; + } + + /* answer 9011 - conax smart card need reset */ + if(2 <= cta_lr && 0x90 == cta_res[cta_lr - 2] && 0x11 == cta_res[cta_lr - 1]) + { + rdr_log(reader, "conax card hangs - reset is required"); + reader->card_status = UNKNOWN; + } + + if(rc == 3) + { return OK; } + else + { return ERROR; } +} + +static int32_t conax_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + int32_t i, ok = 0; + char tmp_dbg[17]; + + rdr_log_dbg(rdr, D_EMM, "Entered conax_get_emm_type ep->emm[2]=%02x", ep->emm[2]); + + for(i = 0; i < rdr->nprov; i++) + { + ok = (!memcmp(&ep->emm[6], rdr->sa[i], 4)); + if(ok) { break; } + } + + if(ok) + { + ep->type = SHARED; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, &ep->emm[6], 4); + rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED, ep->hexserial = {%s}", + cs_hexdump(1, ep->hexserial, 8, tmp_dbg, sizeof(tmp_dbg))); + return 1; + } + else + { + if(!memcmp(&ep->emm[6], rdr->hexserial + 2, 4)) + { + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial + 2, &ep->emm[6], 4); + rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE, ep->hexserial = {%s}", + cs_hexdump(1, ep->hexserial, 8, tmp_dbg, sizeof(tmp_dbg))); + return 1; + } + else + { + ep->type = GLOBAL; + rdr_log_dbg(rdr, D_EMM, "GLOBAL"); + memset(ep->hexserial, 0, 8); + return 1; + } + } +} + +static int32_t conax_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 2 + rdr->nprov; + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { return ERROR; } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int idx = 0, prov; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 0; // FIXME: dont see any conax global EMM yet + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[8] = 0x70; + filters[idx].mask[8] = 0xFF; + idx++; + + for(prov = 0; prov < rdr->nprov; prov++) + { + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[4], rdr->sa[prov], 4); + memset(&filters[idx].mask[4], 0xFF, 4); + idx++; + } + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[4], rdr->hexserial + 2, 4); + memset(&filters[idx].mask[4], 0xFF, 4); + idx++; + + *filter_count = idx; + } + + return OK; +} + +static int32_t conax_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + def_resp; + uint8_t insCA[] = { 0xDD, 0xCA, 0x00, 0x00, 0x00 }; + uint8_t insEMM[] = { 0xDD, 0x84, 0x00, 0x00, 0x00 }; + uint8_t buf[255]; + int32_t rc = 0; + + const int32_t l = ep->emm[2]; + + insEMM[4] = l + 5; + buf[0] = 0x12; + buf[1] = l + 3; + memcpy(buf + 2, ep->emm, buf[1]); + write_cmd(insEMM, buf); + + if(cta_res[0] == 0x98) + { + insCA[4] = cta_res[1]; + write_cmd(insCA, NULL); + } + + rc = ((cta_res[0] == 0x90) && (cta_res[1] == 0x00)); + + if(rc) + { return OK; } + else + { return ERROR; } +} + +static int32_t conax_card_info(struct s_reader *reader) +{ + def_resp; + int32_t type, i, j, k = 0, n = 0, l; + uint16_t provid = 0; + char provname[32], pdate[32], chid[32]; + static const uint8_t insC6[] = { 0xDD, 0xC6, 0x00, 0x00, 0x03, 0x1C, 0x01, 0x00 }; + static const uint8_t ins26[] = { 0xDD, 0x26, 0x00, 0x00, 0x03, 0x1C, 0x01, 0x01 }; + uint8_t insCA[] = { 0xDD, 0xCA, 0x00, 0x00, 0x00 }; + char *txt[] = { "Package", "PPV-Event" }; + static const uint8_t *cmd[] = { insC6, ins26 }; + time_t start_t = 0, end_t = 0; + uint32_t cxclass = 0; + + cs_clear_entitlement(reader); // reset the entitlements + + for(type = 0; type < 2; type++) + { + n = 0; + write_cmd(cmd[type], cmd[type] + 5); + while(cta_res[cta_lr - 2] == 0x98) + { + insCA[4] = cta_res[cta_lr - 1]; // get len + write_cmd(insCA, NULL); // read + + if((cta_res[cta_lr - 2] == 0x90) || (cta_res[cta_lr - 2] == 0x98)) + { + for(j = 0; j < cta_lr - 2; j += cta_res[j + 1] + 2) + { + provid = (cta_res[j + 2 + type] << 8) | cta_res[j + 3 + type]; + chid[0] = '\0'; + + for(k = 0, i = j + 4 + type; (i < j + cta_res[j + 1]); i += cta_res[i + 1] + 2) + { + switch(cta_res[i]) + { + case 0x01: + l = (cta_res[i + 1] < (sizeof(provname) - 1)) ? cta_res[i + 1] : sizeof(provname) - 1; + memcpy(provname, cta_res + i + 2, l); + provname[l] = '\0'; + break; + + case 0x30: + if(k > 1) + { + rdr_log(reader, "%s: %d, id: %04X%s, date: %s - %s, name: %s", + txt[type], ++n, provid, chid, pdate, pdate + 16, trim(provname)); + + // add entitlements to list + cs_add_entitlement(reader, reader->caid, b2ll(4, reader->prid[0]), + provid, cxclass, start_t, end_t, type + 1, 1); + + k = 0; + chid[0] = '\0'; + } + if(k == 0) { start_t = chid_date(cta_res + i + 2, pdate, 15); } + else { end_t = chid_date(cta_res + i + 2, pdate + 16, 15) /* add 23:59:59 here: */ + 0x1517F; } + ++k; + break; + + case 0x20: // Provider classes + case 0x90: // (?) not sure what this is, saw it once in log + snprintf(chid, sizeof(chid), ", classes: %02X%02X%02X%02X", + cta_res[i + 2], cta_res[i + 3], cta_res[i + 4] , cta_res[i + 5]); + cxclass = b2ll(4, &cta_res[i + 2]); + break; + } + } + rdr_log(reader, "%s: %d, id: %04X%s, date: %s - %s, name: %s", + txt[type], ++n, provid, chid, pdate, pdate + 16, trim(provname)); + + // add entitlements to list + cs_add_entitlement(reader, reader->caid, b2ll(4, reader->prid[0]), + provid, cxclass, start_t, end_t, type + 1, 1); + } + } + } + } + rdr_log(reader, "ready for requests"); + return OK; +} + +const struct s_cardsystem reader_conax = +{ + .desc = "conax", + .caids = (uint16_t[]){ 0x0B, 0 }, + .do_emm = conax_do_emm, + .do_ecm = conax_do_ecm, + .card_info = conax_card_info, + .card_init = conax_card_init, + .get_emm_type = conax_get_emm_type, + .get_emm_filter = conax_get_emm_filter, +}; + +#endif diff --git a/reader-cryptoworks.c b/reader-cryptoworks.c new file mode 100644 index 0000000..456e14c --- /dev/null +++ b/reader-cryptoworks.c @@ -0,0 +1,915 @@ +#include "globals.h" +#ifdef READER_CRYPTOWORKS +#include "cscrypt/bn.h" +#include "oscam-config.h" +#include "oscam-emm.h" +#include "reader-common.h" + +struct bignum_st; +struct cryptoworks_data +{ + BIGNUM *exp; + BIGNUM *ucpk; + int32_t ucpk_valid; +}; + +#define CMD_LEN 5 + +static const char *cs_cert = "oscam.cert"; + +static int search_boxkey(struct s_reader *rdr, uint16_t caid, char *key) +{ + int i, rc = 0; + FILE *fp; + char c_caid[512]; + + fp = fopen(get_config_filename(c_caid, sizeof(c_caid), cs_cert), "r"); + if(fp) + { + for(; (!rc) && fgets(c_caid, sizeof(c_caid), fp);) + { + char *c_provid, *c_key; + + c_provid = strchr(c_caid, '#'); + if(c_provid) + { *c_provid = '\0'; } + if(!(c_provid = strchr(c_caid, ':'))) + { continue; } + *c_provid++ = '\0'; + if(!(c_key = strchr(c_provid, ':'))) + { continue; } + *c_key++ = '\0'; + if(word_atob(trim(c_caid)) != caid) + { continue; } + if((i = (cs_strlen(trim(c_key)) >> 1)) > 256) + { continue; } + if(cs_atob((uint8_t *)key, c_key, i) < 0) + { + rdr_log(rdr, "ERROR: wrong key in \"%s\"", cs_cert); + continue; + } + rc = 1; + } + fclose(fp); + } + return rc; +} + +static void RotateBytes1(uint8_t *out, uint8_t *in, int32_t n) +{ + // loop is executed atleast once, so it's not a good idea to + // call with n=0 !! + out += n; + do + { + *(--out) = *(in++); + } + while(--n); +} + +static void RotateBytes2(uint8_t *in, int32_t n) +{ + // loop is executed atleast once, so it's not a good idea to + // call with n=0 !! + uint8_t *e = in + n - 1; + do + { + uint8_t temp = *in; + *in++ = *e; + *e-- = temp; + } + while(in < e); +} + +static int32_t Input(BIGNUM *d, uint8_t *in, int32_t n, int32_t LE) +{ + if(LE) + { + uint8_t tmp[n]; + RotateBytes1(tmp, in, n); + return (BN_bin2bn(tmp, n, d) != 0); + } + else + { return (BN_bin2bn(in, n, d) != 0); } +} + +static int32_t Output(struct s_reader *reader, uint8_t *out, int32_t n, BIGNUM *r, int32_t LE) +{ + int32_t s = BN_num_bytes(r); + if(s > n) + { + uint8_t buff[s]; + rdr_log_dbg(reader, D_READER, "rsa: RSA len %d > %d, truncating", s, n); + BN_bn2bin(r, buff); + memcpy(out, buff + s - n, n); + } + else if(s < n) + { + int32_t l = n - s; + rdr_log_dbg(reader, D_READER, "rsa: RSA len %d < %d, padding", s, n); + memset(out, 0, l); + BN_bn2bin(r, out + l); + } + else + { BN_bn2bin(r, out); } + if(LE) + { RotateBytes2(out, n); } + return (s); +} + +static int32_t cw_RSA(struct s_reader *reader, uint8_t *out, uint8_t *in, int32_t n, BIGNUM *exp, BIGNUM *mod, int32_t LE) +{ + int32_t rc = 0; + BN_CTX *ctx; + BIGNUM *r, *d; + ctx = BN_CTX_new(); + r = BN_new(); + d = BN_new(); + if(Input(d, in, n, LE)) + { + if(BN_mod_exp(r, d, exp, mod, ctx)) + { rc = Output(reader, out, n, r, LE); } + else + { rdr_log(reader, "rsa: mod-exp failed"); } + } + BN_CTX_free(ctx); + BN_free(d); + BN_free(r); + return (rc); +} + +static time_t chid_date(uint8_t *ptr, char *buf, int32_t l) +{ + time_t rc = 0; + struct tm timeinfo; + memset(&timeinfo, 0, sizeof(struct tm)); + if(buf) + { + timeinfo.tm_year = 90 + (ptr[0] >> 1); + timeinfo.tm_mon = (((ptr[0] & 1) << 3) | (ptr[1] >> 5)) - 1; + timeinfo.tm_mday = ptr[1] & 0x1f; + rc = mktime(&timeinfo); + strftime(buf, l, "%Y/%m/%d", &timeinfo); + } + return (rc); +} + + +static int32_t select_file(struct s_reader *reader, uint8_t f1, uint8_t f2, uint8_t *cta_res, uint16_t *p_cta_lr) +{ + uint16_t cta_lr; + uint8_t insA4[] = { 0xA4, 0xA4, 0x00, 0x00, 0x02, 0x00, 0x00 }; + insA4[5] = f1; + insA4[6] = f2; + write_cmd(insA4, insA4 + 5); // select file + *p_cta_lr = cta_lr; + return ((cta_res[0] == 0x9f) && (cta_res[1] == 0x11)); +} + +static int32_t read_record(struct s_reader *reader, uint8_t rec, uint8_t *cta_res) +{ + uint16_t cta_lr; + uint8_t insA2[] = { 0xA4, 0xA2, 0x00, 0x00, 0x01, 0x00 }; + uint8_t insB2[] = { 0xA4, 0xB2, 0x00, 0x00, 0x00 }; + + insA2[5] = rec; + write_cmd(insA2, insA2 + 5); // select record + if(cta_res[0] != 0x9f) + { return (-1); } + insB2[4] = cta_res[1]; // get len + write_cmd(insB2, NULL); // read record + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1])) + { return (-1); } + return (cta_lr - 2); +} + +/* +int32_t cryptoworks_send_pin(struct s_reader * reader) +{ + uint8_t insPIN[] = { 0xA4, 0x20, 0x00, 0x00, 0x04, 0x00,0x00,0x00,0x00 }; //Verify PIN + + if(reader->pincode[0] && (reader->pincode[0]&0xF0)==0x30) + { + memcpy(insPIN+5,reader->pincode,4); + + write_cmd(insPIN, insPIN+5); + rdr_log_dbg(reader, D_READER, "Sent pincode to card."); + if((cta_res[0]==0x98)&&(cta_res[1]==0x04)) rdr_log(reader, "bad pincode"); + + return OK; + } + + return(0); +} +*/ + +static int32_t cryptoworks_disable_pin(struct s_reader *reader) +{ + def_resp; + uint8_t insPIN[] = { 0xA4, 0x26, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00 }; // disable PIN + + if(reader->pincode[0] && (reader->pincode[0] & 0xF0) == 0x30) + { + memcpy(insPIN + 5, reader->pincode, 4); + + write_cmd(insPIN, insPIN + 5); + rdr_log(reader, "disable pincode to card"); + if((cta_res[0] == 0x98) && (cta_res[1] == 0x04)) { rdr_log(reader, "bad pincode"); } + return ERROR; + } + return OK; +} + +static int32_t cryptoworks_card_init(struct s_reader *reader, ATR *newatr) +{ + get_atr; + def_resp; + int32_t i; + uint32_t mfid = 0x3F20; + static const uint8_t cwexp[] = { 1, 0 , 1 }; + uint8_t insA4C[] = { 0xA4, 0xC0, 0x00, 0x00, 0x11 }; + uint8_t insB8[] = { 0xA4, 0xB8, 0x00, 0x00, 0x0C }; + uint8_t issuerid = 0; + char issuer[20] = { 0 }, tmp[11]; + char *unknown = "unknown", *pin = unknown, ptxt[CS_MAXPROV << 2] = {0}; + + if((atr[6] != 0xC4) || (atr[9] != 0x8F) || (atr[10] != 0xF1)) { return ERROR; } + + if(!cs_malloc(&reader->csystem_data, sizeof(struct cryptoworks_data))) + { return ERROR; } + struct cryptoworks_data *csystem_data = reader->csystem_data; + + rdr_log(reader, "card detected"); + rdr_log(reader, "type: CryptoWorks"); + + reader->caid = 0xD00; + reader->nprov = 0; + memset(reader->prid, 0, sizeof(reader->prid)); + + write_cmd(insA4C, NULL); // read masterfile-ID + if((cta_res[0] == 0xDF) && (cta_res[1] >= 6)) + { mfid = (cta_res[6] << 8) | cta_res[7]; } + + select_file(reader, 0x3f, 0x20, cta_res, &cta_lr); + insB8[2] = insB8[3] = 0; // first + for(cta_res[0] = 0xdf; cta_res[0] == 0xdf;) + { + write_cmd(insB8, NULL); // read provider id's + if(cta_res[0] != 0xdf) { break; } + if(((cta_res[4] & 0x1f) == 0x1f) && (reader->nprov < CS_MAXPROV)) + { + snprintf(ptxt + cs_strlen(ptxt), sizeof(ptxt) - cs_strlen(ptxt), ",%02X", cta_res[5]); + reader->prid[reader->nprov++][3] = cta_res[5]; + } + insB8[2] = insB8[3] = 0xff; // next + } + for(i = reader->nprov; i < CS_MAXPROV; i++) + { memset(&reader->prid[i][0], 0xff, 4); } + + select_file(reader, 0x2f, 0x01, cta_res, &cta_lr); // read caid + if(read_record(reader, 0xD1, cta_res) >= 4) + { reader->caid = (cta_res[2] << 8) | cta_res[3]; } + + if(read_record(reader, 0x80, cta_res) >= 7) // read serial + { memcpy(reader->hexserial, cta_res + 2, 5); } + rdr_log_sensitive(reader, "type: CryptoWorks, caid: %04X, ascii serial: {%llu}, hex serial: {%s}", + reader->caid, (unsigned long long) b2ll(5, reader->hexserial), cs_hexdump(0, reader->hexserial, 5, tmp, sizeof(tmp))); + + if(read_record(reader, 0x9E, cta_res) >= 66) // read ISK + { + uint8_t keybuf[256]; + BIGNUM *ipk; + if(search_boxkey(reader, reader->caid, (char *)keybuf)) + { + ipk = BN_new(); + BN_bin2bn(cwexp, sizeof(cwexp), csystem_data->exp); + BN_bin2bn(keybuf, 64, ipk); + cw_RSA(reader, cta_res + 2, cta_res + 2, 0x40, csystem_data->exp, ipk, 0); + BN_free(ipk); + + csystem_data->ucpk_valid = (cta_res[2] == ((mfid & 0xFF) >> 1)); + if(csystem_data->ucpk_valid) + { + cta_res[2] |= 0x80; + BN_bin2bn(cta_res + 2, 0x40, csystem_data->ucpk); + rdr_log_dump_dbg(reader, D_READER, cta_res + 2, 0x40, "IPK available -> session-key:"); + } + else + { + csystem_data->ucpk_valid = (keybuf[0] == (((mfid & 0xFF) >> 1) | 0x80)); + if(csystem_data->ucpk_valid) + { + BN_bin2bn(keybuf, 0x40, csystem_data->ucpk); + rdr_log_dump_dbg(reader, D_READER, keybuf, 0x40, "session-key found:"); + } + else + { rdr_log(reader, "invalid IPK or session-key for CAID %04X !", reader->caid); } + } + } + } + if(read_record(reader, 0x9F, cta_res) >= 3) + { issuerid = cta_res[2]; } + if(read_record(reader, 0xC0, cta_res) >= 16) + { + cs_strncpy(issuer, (const char *)cta_res + 2, sizeof(issuer)); + trim(issuer); + } + else + { cs_strncpy(issuer, unknown, sizeof(issuer)); } + + select_file(reader, 0x3f, 0x20, cta_res, &cta_lr); + select_file(reader, 0x2f, 0x11, cta_res, &cta_lr); // read pin + + if(read_record(reader, atr[8], cta_res) >= 7) + { + cta_res[6] = 0; + pin = (char *)cta_res + 2; + } + rdr_log(reader, "issuer: %s, id: %02X, bios: v%d, pin: %s, mfid: %04X", issuer, issuerid, atr[7], pin, mfid); + rdr_log(reader, "providers: %d (%s)", reader->nprov, ptxt + 1); + + cryptoworks_disable_pin(reader); + + if (((reader->caid == 0x0D96) || (reader->caid == 0x0D98)) && reader->needsglobalfirst == 1) + { + if(!cs_malloc(&reader->last_g_emm, sizeof(EMM_PACKET))) + { + return ERROR; + } + reader->last_g_emm_valid = false; + rdr_log(reader, "Init for global EMM handling CAID %04X successful",reader->caid); + } + + return OK; +} + +static int32_t cryptoworks_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + def_resp; + int32_t r = 0; + uint8_t ins4C[] = { 0xA4, 0x4C, 0x00, 0x00, 0x00 }; + uint8_t insC0[] = { 0xA4, 0xC0, 0x00, 0x00, 0x1C }; + uint8_t nanoD4[10]; + struct cryptoworks_data *csystem_data = reader->csystem_data; + int32_t secLen = check_sct_len(er->ecm, -5 + (csystem_data->ucpk_valid ? sizeof(nanoD4) : 0)); + + if(secLen > 5) + { + int32_t i; + const uint8_t *ecm = er->ecm; + uint8_t buff[MAX_LEN]; + + if(csystem_data->ucpk_valid) + { + memcpy(buff, er->ecm, secLen); + nanoD4[0] = 0xD4; + nanoD4[1] = 0x08; + for(i = 2; i < (int)sizeof(nanoD4); i++) + { nanoD4[i] = rand(); } + memcpy(&buff[secLen], nanoD4, sizeof(nanoD4)); + ecm = buff; + secLen += sizeof(nanoD4); + } + + ins4C[3] = csystem_data->ucpk_valid ? 2 : 0; + ins4C[4] = secLen - 5; + write_cmd(ins4C, ecm + 5); + + if(cta_res[cta_lr - 2] == 0x9f) + { + insC0[4] = cta_res[cta_lr - 1]; + write_cmd(insC0, NULL); + + for(i = 0; i < secLen && r < 2;) + { + int32_t n = cta_res[i + 1]; + switch(cta_res[i]) + { + case 0x80: + rdr_log_dbg(reader, D_READER, "nano 80 (serial)"); + break; + + case 0xD4: + rdr_log_dbg(reader, D_READER, "nano D4 (rand)"); + if(n < 8 || memcmp(&cta_res[i], nanoD4, sizeof(nanoD4))) + { + rdr_log_dbg(reader, D_READER, "random data check failed after decrypt"); + } + break; + + case 0xDB: // CW + rdr_log_dbg(reader, D_READER, "nano DB (cw)"); + if(n == 0x10) + { + memcpy(ea->cw, &cta_res[i + 2], 16); + r |= 1; + } + break; + + case 0xDF: // signature + rdr_log_dbg(reader, D_READER, "nano DF %02x (sig)", n); + if(n == 0x08) + { + if((cta_res[i + 2] & 0x50) == 0x50 && !(cta_res[i + 3] & 0x01) && (cta_res[i + 5] & 0x80)) + { r |= 2; } + } + else if(n == 0x40) // camcrypt + { + if(csystem_data->ucpk_valid) + { + cw_RSA(reader, &cta_res[i + 2], &cta_res[i + 2], n, csystem_data->exp, csystem_data->ucpk, 0); + rdr_log_dbg(reader, D_READER, "after camcrypt"); + r = 0; + secLen = n - 4; + n = 4; + } + else + { + rdr_log(reader, "valid UCPK needed for camcrypt!"); + return ERROR; + } + } + break; + + default: + rdr_log_dbg(reader, D_READER, "nano %02x (unhandled)", cta_res[i]); + break; + } + i += n + 2; + } + } + + /* + #ifdef LALL + if ((cta_res[cta_lr-2]==0x9f)&&(cta_res[cta_lr-1]==0x1c)) + { + write_cmd(insC0, NULL); + if ((cta_lr>26)&&(cta_res[cta_lr-2]==0x90)&&(cta_res[cta_lr-1]==0)) + { + if (rc=(((cta_res[20]&0x50)==0x50) && + (!(cta_res[21]&0x01)) && + (cta_res[23]&0x80))) + memcpy(ea->cw, cta_res+2, 16); + } + } + #endif + */ + } + + //return(rc ? 1 : 0); + return ((r == 3) ? 1 : 0); +} + +static uint32_t cryptoworks_get_emm_provid(uint8_t *buffer, int32_t len) +{ + uint32_t provid = 0; + int32_t i = 0; + + for(i = 0; i < len;) + { + switch(buffer[i]) + { + case 0x83: + provid = buffer[i + 2] & 0xfc; + return provid; + break; + + default: + i += buffer[i + 1] + 2; + break; + } + + } + return provid; +} + +static int32_t cryptoworks_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + char dumprdrserial[16], dumpemmserial[16]; + + rdr_log_dbg(rdr, D_EMM, "Entered cryptoworks_get_emm_type ep->emm[0]=%02x", ep->emm[0]); + switch(ep->emm[0]) + { + case 0x82: + if(ep->emm[3] == 0xA9 && ep->emm[4] == 0xFF && ep->emm[13] == 0x80 && ep->emm[14] == 0x05) + { + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 5, 5); +#ifdef WITH_DEBUG + if(cs_dblevel & D_EMM) + { + cs_hexdump(1, rdr->hexserial, 5, dumprdrserial, sizeof(dumprdrserial)); + cs_hexdump(1, ep->hexserial, 5, dumpemmserial, sizeof(dumpemmserial)); + } +#endif + i2b_buf(4, cryptoworks_get_emm_provid(ep->emm + 12, ep->emmlen - 12), ep->provid); + rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE, ep = {%s} rdr = {%s}", dumpemmserial, dumprdrserial); + return (!memcmp(ep->emm + 5, rdr->hexserial, 5)); // check for serial + } + break; + + case 0x84: + if(ep->emm[3] == 0xA9 && ep->emm[4] == 0xFF && ep->emm[12] == 0x80 && ep->emm[13] == 0x04) + { + ep->type = SHARED; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 5, 4); +#ifdef WITH_DEBUG + if(cs_dblevel & D_EMM) + { + cs_hexdump(1, rdr->hexserial, 4, dumprdrserial, sizeof(dumprdrserial)); + cs_hexdump(1, ep->hexserial, 4, dumpemmserial, sizeof(dumpemmserial)); + } +#endif + i2b_buf(4, cryptoworks_get_emm_provid(ep->emm + 12, ep->emmlen - 12), ep->provid); + rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED, ep = {%s} rdr = {%s}", dumpemmserial, dumprdrserial); + return (!memcmp(ep->emm + 5, rdr->hexserial, 4)); // check for SA + } + break; + + case 0x86: + if(ep->emm[3] == 0xA9 && ep->emm[4] == 0xFF && ep->emm[5] == 0x83 + && ep->emm[6] == 0x01 && (ep->emm[8] == 0x85 || ep->emm[8] == 0x84 || ep->emm[8] == 0x8C)) + { + rdr_log_dbg(rdr, D_EMM, "SHARED (Header)"); + ep->type = SHARED; + i2b_buf(4, cryptoworks_get_emm_provid(ep->emm + 8, ep->emmlen - 8), ep->provid); + // We need those packets to pass otherwise we would never + // be able to complete EMM reassembly + return 1; + } + break; + + case 0x88: + case 0x89: + if(ep->emm[3] == 0xA9 && ep->emm[4] == 0xFF && ep->emm[8] == 0x83 && ep->emm[9] == 0x01) + { + rdr_log_dbg(rdr, D_EMM, "GLOBAL"); + ep->type = GLOBAL; + i2b_buf(4, cryptoworks_get_emm_provid(ep->emm + 8, ep->emmlen - 8), ep->provid); + return 1; + } + break; + + case 0x8F: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "0x8F via camd3"); + + switch(ep->emm[4]) + { + case 0x44: + i2b_buf(4, cryptoworks_get_emm_provid(ep->emm + 8, ep->emmlen - 8), ep->provid); + ep->type = GLOBAL; + break; + + case 0x48: + i2b_buf(4, cryptoworks_get_emm_provid(ep->emm + 12, ep->emmlen - 12), ep->provid); + ep->type = SHARED; + break; + + case 0x42: + i2b_buf(4, cryptoworks_get_emm_provid(ep->emm + 12, ep->emmlen - 12), ep->provid); + ep->type = UNIQUE; + break; + } + return 1; + + /* FIXME: Seems to be that all other EMM types are rejected by the card */ + default: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + return 0; // skip emm + } + + rdr_log_dbg(rdr, D_EMM, "invalid"); + return 0; +} + +static int32_t cryptoworks_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 4; + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { return ERROR; } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x88; + filters[idx].mask[0] = 0xFE; + filters[idx].filter[1] = 0xA9; + filters[idx].mask[1] = 0xFF; + filters[idx].filter[2] = 0xFF; + filters[idx].mask[2] = 0xFF; + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x86; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xA9; + filters[idx].mask[1] = 0xFF; + filters[idx].filter[2] = 0xFF; + filters[idx].mask[2] = 0xFF; + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x84; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xA9; + filters[idx].mask[1] = 0xFF; + filters[idx].filter[2] = 0xFF; + filters[idx].mask[2] = 0xFF; + memcpy(&filters[idx].filter[3], rdr->hexserial, 4); + memset(&filters[idx].mask[3], 0xFF, 4); + idx++; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xA9; + filters[idx].mask[1] = 0xFF; + filters[idx].filter[2] = 0xFF; + filters[idx].mask[2] = 0xFF; + memcpy(&filters[idx].filter[3], rdr->hexserial, 5); + memset(&filters[idx].mask[3], 0xFF, 5); + idx++; + + *filter_count = idx; + } + + return OK; +} + +static int32_t cryptoworks_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + def_resp; + uint8_t insEMM_GA[] = { 0xA4, 0x44, 0x00, 0x00, 0x00 }; + uint8_t insEMM_SA[] = { 0xA4, 0x48, 0x00, 0x00, 0x00 }; + uint8_t insEMM_UA[] = { 0xA4, 0x42, 0x00, 0x00, 0x00 }; + uint8_t *emm = ep->emm; + + if(emm[0] == 0x8f && emm[3] == 0xA4) + { + // camd3 emm + write_cmd(emm + 3, emm + 3 + CMD_LEN); + } + else + { + switch(ep->type) + { + case GLOBAL: // GA + insEMM_GA[4] = ep->emm[2] - 2; + if(emm[7] == insEMM_GA[4] - 3) + { + write_cmd(insEMM_GA, emm + 5); + } + break; + + case SHARED: // SA + insEMM_SA[4] = ep->emm[2] - 6; + if(emm[11] == insEMM_SA[4] - 3) + { + write_cmd(insEMM_SA, emm + 9); + } + break; + + case UNIQUE: // UA + insEMM_UA[4] = ep->emm[2] - 7; + if(emm[12] == insEMM_UA[4] - 3) + { + //cryptoworks_send_pin(); //?? may be + write_cmd(insEMM_UA, emm + 10); + } + break; + } + } + + if((cta_res[0] == 0x90) && (cta_res[1] == 0x00)) + { + return OK; + } + + // emm already written before, entitlement / key is already up to date -> skipped + if((cta_res[0] == 0x94) && (cta_res[1] == 0x04)) + { + return SKIPPED; + } + + rdr_log_dbg(reader, D_EMM, "%s(): type %d - response %02X %02X", + __func__, ep->type, cta_res[0], cta_res[1]); + + return ERROR; +} + +static int32_t cryptoworks_card_info(struct s_reader *reader) +{ + def_resp; + int32_t i; + uint8_t insA21[] = { 0xA4, 0xA2, 0x01, 0x00, 0x05, 0x8C, 0x00, 0x00, 0x00, 0x00 }; + uint8_t insB2[] = { 0xA4, 0xB2, 0x00, 0x00, 0x00 }; + char l_name[20 + 8] = ", name: "; + + cs_clear_entitlement(reader); // reset the entitlements + + for(i = 0; i < reader->nprov; i++) + { + l_name[8] = 0; + select_file(reader, 0x1f, reader->prid[i][3], cta_res, &cta_lr); // select provider + select_file(reader, 0x0e, 0x11, cta_res, &cta_lr); // read provider name + + if(read_record(reader, 0xD6, cta_res) >= 16) + { + cs_strncpy(l_name + 8, (const char *)cta_res + 2, sizeof(l_name) - 8); + l_name[sizeof(l_name) - 1] = 0; + trim(l_name + 8); + } + + l_name[0] = (l_name[8]) ? ',' : 0; + rdr_log(reader, "provider: %d, id: %02X%s", i + 1, reader->prid[i][3], l_name); + select_file(reader, 0x0f, 0x20, cta_res, &cta_lr); // select provider class + write_cmd(insA21, insA21 + 5); + + if(cta_res[0] == 0x9f) + { + insB2[4] = cta_res[1]; + for(insB2[3] = 0; (cta_res[0] != 0x94) || (cta_res[1] != 0x2); insB2[3] = 1) + { + write_cmd(insB2, NULL); // read chid + if(cta_res[0] != 0x94) + { + char ds[16], de[16]; + + // todo: add entitlements to list but produces a warning related to date variable + cs_add_entitlement(reader, reader->caid, reader->prid[i][3], b2i(2, cta_res + 6), 0, + chid_date(cta_res + 28, ds, sizeof(ds) - 1), + chid_date(cta_res + 30, de, sizeof(de) - 1), 3, 1); + + rdr_log(reader, "chid: %02X%02X, date: %s - %s, name: %s", + cta_res[6], cta_res[7], ds, de, trim((char *) cta_res + 10)); + } + } + } + + select_file(reader, 0x0f, 0x00, cta_res, &cta_lr); // select provider channel + write_cmd(insA21, insA21 + 5); + + if(cta_res[0] == 0x9f) + { + insB2[4] = cta_res[1]; + for(insB2[3] = 0; (cta_res[0] != 0x94) || (cta_res[1] != 0x2); insB2[3] = 1) + { + write_cmd(insB2, NULL); // read chid + if(cta_res[0] != 0x94) + { + char ds[16], de[16]; + + // todo: add entitlements to list but produces a warning related to date variable + cs_add_entitlement(reader, reader->caid, reader->prid[i][3], b2i(2, cta_res + 6), 0, + chid_date(cta_res + 28, ds, sizeof(ds) - 1), + chid_date(cta_res + 30, de, sizeof(de) - 1), 3, 1); + + cta_res[27] = 0; + rdr_log(reader, "chid: %02X%02X, date: %s - %s, name: %s", + cta_res[6], cta_res[7], ds, de, trim((char *)cta_res + 10)); + } + } + } + } + rdr_log(reader, "ready for requests"); + return OK; +} + +static int32_t cryptoworks_reassemble_emm(struct s_reader *rdr, struct s_client *client, EMM_PACKET *ep) +{ + uint8_t *buffer = ep->emm; + int16_t *len = &ep->emmlen; + int16_t emm_len = 0; + + // Cryptoworks + // Cryptoworks EMM-S have to be assembled by the client from an EMM-SH with table + // id 0x84 and a corresponding EMM-SB (body) with table id 0x86. A pseudo EMM-S + // with table id 0x84 has to be build containing all nano commands from both the + // original EMM-SH and EMM-SB in ascending order. + // + if(*len > 500) { return 0; } + + if (!client->cw_rass && !cs_malloc(&client->cw_rass, sizeof(*client->cw_rass))) + { + cs_log("[cryptoworks] ERROR: Can't allocate EMM reassembly buffer."); + return 0; + } + struct emm_rass *r_emm = client->cw_rass; + + switch(buffer[0]) + { + case 0x82 : // emm-u + rdr_log_dbg(rdr, D_EMM, "unique emm (EMM-U)"); + break; + + case 0x84: // emm-sh + rdr_log_dbg(rdr, D_EMM, "shared emm (EMM-SH)"); + if(!memcmp(r_emm->emm, buffer, *len)) + { return 0; } + + if(ep->emm[11] == ep->emm[2] - 9) + { + rdr_log_dbg(rdr, D_EMM, "received assembled EMM-S"); + return 1; + } + + memcpy(r_emm->emm, buffer, *len); + r_emm->emmlen = *len; + rdr_log_dbg(rdr, D_EMM, "EMM-SH only in memcpy"); + return 0; + + case 0x86: // emm-sb + rdr_log_dbg(rdr, D_EMM, "shared emm (EMM-SB)"); + if(!r_emm->emmlen) + { return 0; } + + // we keep the first 12 bytes of the 0x84 emm (EMM-SH) + // now we need to append the payload of the 0x86 emm (EMM-SB) + // starting after the header (&buffer[5]) + // then the rest of the payload from EMM-SH + // so we should have : + // EMM-SH[0:12] + EMM-SB[5:len_EMM-SB] + EMM-SH[12:EMM-SH_len] + // then sort the nano in ascending order + // update the emm len (emmBuf[1:2]) + // + + emm_len = *len - 5 + r_emm->emmlen - 12; + uint8_t *tmp, *assembled; + if(!cs_malloc(&tmp, emm_len)) + { return 0; } + if(!cs_malloc(&assembled, emm_len + 12)) + { + free(tmp); + return 0; + } + uint8_t *assembled_EMM; + if(!cs_malloc(&assembled_EMM, emm_len + 12)) + { + free(assembled); + free(tmp); + return 0; + } + memcpy(tmp, &buffer[5], *len - 5); + memcpy(tmp + *len - 5, &r_emm->emm[12], r_emm->emmlen - 12); + memcpy(assembled_EMM, r_emm->emm, 12); + emm_sort_nanos(assembled_EMM + 12, tmp, emm_len); + + assembled_EMM[1] = ((emm_len + 9) >> 8) | 0x70; + assembled_EMM[2] = (emm_len + 9) & 0xFF; + + if(assembled_EMM[11] != emm_len) // sanity check + { + // error in emm assembly + rdr_log_dbg(rdr, D_EMM, "Error assembling EMM-S"); + free(assembled_EMM); + return 0; + } + + //copy back the assembled emm in the working buffer + memcpy(buffer, assembled_EMM, emm_len + 12); + *len = emm_len + 12; + + free(tmp); + free(assembled); + + r_emm->emmlen = 0; + + rdr_log_dump_dbg(rdr, D_EMM, buffer, *len, "shared emm (assembled):"); + free(assembled_EMM); + break; + + case 0x88: // emm-g + case 0x89: // emm-g + rdr_log_dbg(rdr, D_EMM, "global emm (EMM-G)"); + break; + } + return 1; +} + +const struct s_cardsystem reader_cryptoworks = +{ + .desc = "cryptoworks", + .caids = (uint16_t[]){ 0x0D, 0 }, + .do_emm_reassembly = cryptoworks_reassemble_emm, + .do_emm = cryptoworks_do_emm, + .do_ecm = cryptoworks_do_ecm, + .card_info = cryptoworks_card_info, + .card_init = cryptoworks_card_init, + .get_emm_type = cryptoworks_get_emm_type, + .get_emm_filter = cryptoworks_get_emm_filter, +}; + +#endif diff --git a/reader-dgcrypt.c b/reader-dgcrypt.c new file mode 100644 index 0000000..502b667 --- /dev/null +++ b/reader-dgcrypt.c @@ -0,0 +1,275 @@ +#include "globals.h" +#ifdef READER_DGCRYPT +#include "reader-common.h" + +#define DEBUG 0 + +static const uint8_t dgcrypt_atr[8] = { 0x3B, 0xE9, 0x00, 0x00, 0x81, 0x31, 0xC3, 0x45 }; +static const uint8_t cmd_CWKEY[5] = { 0x81, 0xD0, 0x00, 0x01, 0x08 }; +static const uint8_t cmd_CAID[5] = { 0x81, 0xC0, 0x00, 0x01, 0x0A }; +static const uint8_t cmd_SERIAL[5] = { 0x81, 0xD1, 0x00, 0x01, 0x10 }; +static const uint8_t cmd_CARD_ID[5] = { 0x81, 0xD4, 0x00, 0x01, 0x05 }; +static const uint8_t cmd_LABEL[5] = { 0x81, 0xD2, 0x00, 0x01, 0x10 }; +//static const uint8_t cmd_SUBSYS[5] = { 0x81, 0xDD, 0x00, 0x10, 0x04 }; +static const uint8_t cmd_ECM[3] = { 0x80, 0xEA, 0x80 }; +static const uint8_t cmd_EMM[3] = { 0x80, 0xEB, 0x80 }; + +struct dgcrypt_data +{ + uint8_t session_key[16]; + uint8_t cardid[5]; +}; + +static int32_t dgcrypt_cmd(struct s_reader *rdr, const uint8_t *buf, const int32_t buflen, uint8_t *response, uint16_t *response_length, uint16_t min_response_len) +{ + rdr->ifsc = 195; + rdr->ns = 1; + + if(DEBUG) + { + char tmp[512]; + rdr_log(rdr, "SEND -> %s(%d)", cs_hexdump(1, buf, buflen, tmp, sizeof(tmp)), buflen); + } + int32_t ret = reader_cmd2icc(rdr, buf, buflen, response, response_length); + + if(DEBUG) + { + char tmp[512]; + rdr_log(rdr, "RECV <- %s(%d) ret=%d", cs_hexdump(1, response, *response_length, tmp, sizeof(tmp)), *response_length, ret); + } + // reader_cmd2icc retuns ERROR=1, OK=0 - the opposite of OK and ERROR defines in reader-common.h + + if(ret) + { + rdr_log(rdr, "ERROR: reader_cmd2icc() ret=%d", ret); + return ERROR; + } + + if(*response_length < 2 || *response_length < min_response_len) + { + if (response[0] == 0x6b && response[1] == 0x01) rdr_log(rdr, "ERROR: card has expired, please update your card"); + else rdr_log(rdr, "ERROR: response_length=%d < min_response_length=%d", *response_length, min_response_len); + return ERROR; // Response is two short + } + + if(response[*response_length - 2] != 0x90 || (response[*response_length - 1] != 0x00 && response[*response_length - 1] != 0x17)) + { + rdr_log(rdr, "ERROR: response[-2] != 0x90 its 0x%02X", response[*response_length - 2]); + rdr_log(rdr, "ERROR: response[-1] != 0x00 or 0x17 its 0x%02X", response[*response_length - 1]); + return ERROR; // The reader responded with "command not OK" + } + return OK; +} + +static int32_t dgcrypt_card_init(struct s_reader *rdr, ATR *newatr) +{ + def_resp + + get_atr + if(atr_size < sizeof(dgcrypt_atr)) + { return ERROR; } + + // Full ATR: 3B E9 00 00 81 31 C3 45 99 63 74 69 19 99 12 56 10 EC + if(memcmp(atr, dgcrypt_atr, sizeof(dgcrypt_atr)) != 0) + { return ERROR; } + + if(!cs_malloc(&rdr->csystem_data, sizeof(struct dgcrypt_data))) + { return ERROR; } + + struct dgcrypt_data *csystem_data = rdr->csystem_data; + + rdr_log(rdr, "[dgcrypt-reader] card detected."); + + memset(rdr->sa, 0, sizeof(rdr->sa)); + memset(rdr->prid, 0, sizeof(rdr->prid)); + memset(rdr->hexserial, 0, sizeof(rdr->hexserial)); + + rdr->nprov = 1; + // rdr->caid = 0x4ABF; + + // Get session key + // Send: 81 D0 00 01 08 + // Recv: 32 86 17 D5 2C 66 61 14 90 00 + if(!dgcrypt_cmd(rdr, cmd_CWKEY, sizeof(cmd_CWKEY), cta_res, &cta_lr, 8)) + { return ERROR; } + memcpy(csystem_data->session_key + 0, cta_res, 8); + memcpy(csystem_data->session_key + 8, cta_res, 8); + + // Get CAID + // Send: 81 C0 00 01 0A + // Recv: 4A BF 90 00 + if (!dgcrypt_cmd(rdr, cmd_CAID, sizeof(cmd_CAID), cta_res, &cta_lr, 2)) + { return ERROR; } + rdr->caid = (cta_res[0] << 8) | cta_res[1]; + + // Get serial number + // Send: 81 D1 00 01 10 + // Recv: 00 0D DB 08 71 0D D5 0C 30 30 30 30 30 30 30 30 90 00 + if(!dgcrypt_cmd(rdr, cmd_SERIAL, sizeof(cmd_SERIAL), cta_res, &cta_lr, 8)) + { return ERROR; } + memcpy(rdr->hexserial, cta_res + 1, 7); + + // Get card id + // Send: 81 D4 00 01 05 + // Recv: 00 00 00 76 AC 90 00 + if(!dgcrypt_cmd(rdr, cmd_CARD_ID, sizeof(cmd_CARD_ID), cta_res, &cta_lr, 5)) + { return ERROR; } + memcpy(csystem_data->cardid, cta_res, 5); + + // Get LABEL + // Send: 81 D2 00 01 10 + // Recv: 50 61 79 5F 54 56 5F 43 61 72 64 00 00 00 00 00 90 00 + // Txt: P a y _ T V _ C a r d + if(!dgcrypt_cmd(rdr, cmd_LABEL, sizeof(cmd_LABEL), cta_res, &cta_lr, 16)) + { return ERROR; } + char label[17]; + memset(label, 0, sizeof(label)); + memcpy(label, cta_res, 16); + + // Get subsystem - !FIXME! We are not using the answer of this command! + // Send: 81 DD 00 10 04 + // Recv: 00 55 00 55 90 00, also 00 8F 00 8F 90 00 + // if(!dgcrypt_cmd(rdr, cmd_LABEL, sizeof(cmd_LABEL), cta_res, &cta_lr, 4)) + // { return ERROR; } + + rdr_log_sensitive(rdr, "CAID: 0x%04X, Serial: {%"PRIu64"} HexSerial: {%02X %02X %02X %02X %02X %02X %02X} Card Id: {%02X %02X %02X %02X %02X} Label: {%s}", + rdr->caid, + b2ll(7, rdr->hexserial), + rdr->hexserial[0], rdr->hexserial[1], rdr->hexserial[2], + rdr->hexserial[3], rdr->hexserial[4], rdr->hexserial[5], rdr->hexserial[6], + csystem_data->cardid[0], csystem_data->cardid[1], csystem_data->cardid[2], csystem_data->cardid[3], csystem_data->cardid[4], + label); + + return OK; +} + +static int32_t dgcrypt_do_ecm(struct s_reader *rdr, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + def_resp + uint8_t cmd_buffer[256]; + struct dgcrypt_data *csystem_data = rdr->csystem_data; + + memcpy(cmd_buffer, er->ecm, er->ecm[2] + 3); + // Replace The first 3 bytes of the ECM with the command + memcpy(cmd_buffer, cmd_ECM, sizeof(cmd_ECM)); + + // Write ECM + // Send: 80 EA 80 00 55 00 00 3F 90 03 00 00 18 5D 82 4E 01 C4 2D 60 12 ED 34 37 ED 72 .. .. .. + // Recv: 72 25 8D A1 0D 0D D2 44 EE ED 51 2F 3B 5D 19 63 E6 90 00 + if(!dgcrypt_cmd(rdr, cmd_buffer, er->ecm[2] + 3, cta_res, &cta_lr, 17)) + { return ERROR; } + + if(cta_res[0] != 0x72) // CW response MUST start with 0x72 + { return ERROR; } + + int i; + for(i = 0; i < 16; i++) + { + ea->cw[i] = cta_res[1 + i] ^ csystem_data->session_key[i]; + } + return OK; +} + +static int32_t dgcrypt_do_emm(struct s_reader *rdr, EMM_PACKET *ep) +{ + def_resp + uint8_t cmd_buffer[256]; + int32_t emm_length = ep->emm[2] + 3 + 2; + + // add 2 bytes for header + memcpy(cmd_buffer + 2, ep->emm, emm_length); + // Replace The first 3 bytes of the EMM with the command + memcpy(cmd_buffer, cmd_EMM, sizeof(cmd_EMM)); + + // Write EMM + // Send: 80 EB 80 00 54 00 00 00 00 76 AC 00 8F 82 4A 90 03 00 00 .. .. .. + // Recv: 90 17 + if(!dgcrypt_cmd(rdr, cmd_buffer, emm_length, cta_res, &cta_lr, 2)) + { return ERROR; } + + return OK; +} + +static int32_t dgcrypt_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + rdr_log_dbg(rdr, D_EMM, "Entered dgcrypt_get_emm_type ep->emm[0]=%x", ep->emm[0]); + char tmp_dbg[10]; + struct dgcrypt_data *csystem_data = rdr->csystem_data; + + switch(ep->emm[0]) + { + case 0x82: + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, 5); + + rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE, ep->hexserial = {%s}", + cs_hexdump(1, ep->hexserial, 5, tmp_dbg, sizeof(tmp_dbg))); + + rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE, csystem_data->cardid = {%s}", + cs_hexdump(1, csystem_data->cardid, 5, tmp_dbg, sizeof(tmp_dbg))); + + return (!memcmp(csystem_data->cardid, ep->hexserial, 5)); + break; + // Unknown EMM types, but allready subbmited to dev's + // FIXME: Drop EMM's until there are implemented + default: + ep->type = UNKNOWN; + return 1; + } +} + +static int32_t dgcrypt_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ + struct dgcrypt_data *csystem_data = rdr->csystem_data; + if(*emm_filters == NULL) + { + // need more info + //--|-|len|--|card id 5 byte| const |len|const|-------------- + //82 00 54 00 00 00 00 xx xx 00 8f 82 4a 90 03 ... tested, works + //82 00 64 00 00 00 00 00 00 00 8f 82 5a ff ff ... ? filler + //82 00 34 00 00 00 00 xx xx 00 8f 82 2a 90 03 ... ? + //82 00 37 00 00 00 00 xx xx 00 8f 82 2d 90 03 ... ? + + const unsigned int max_filter_count = 1; // fixme + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + + int32_t idx = 0; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[2], csystem_data->cardid, 5); + memset(&filters[idx].mask[2], 0xFF, 5); + idx++; +/* + // I've never seen it + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].mask[0] = 0xFF; + idx++; +*/ + *filter_count = idx; + } + return OK; +} + +const struct s_cardsystem reader_dgcrypt = +{ + .desc = "dgcrypt", + .caids = (uint16_t[]){ 0x4AB0, 0x4ABF, 0 }, + .card_init = dgcrypt_card_init, + .do_emm = dgcrypt_do_emm, + .do_ecm = dgcrypt_do_ecm, + .get_emm_type = dgcrypt_get_emm_type, + .get_emm_filter = dgcrypt_get_emm_filter, +}; + +#endif diff --git a/reader-dre-cas.c b/reader-dre-cas.c new file mode 100644 index 0000000..6ac0341 --- /dev/null +++ b/reader-dre-cas.c @@ -0,0 +1,885 @@ +#include "globals.h" +#ifdef READER_DRECAS +#include "cscrypt/des.h" +#include "reader-common.h" +#include "reader-dre-common.h" +#include "csctapi/icc_async.h" + +struct dre_data +{ + uint8_t provider; +}; + +struct stm_keys +{ + uint8_t stmcmd34[64][0x30]; +} stm_keys_t; + +uint8_t stm_curkey[2] = {0,0}; +extern char cs_confdir[128]; + +#define MSP_CMD_BYTE 0x59 +#define STM_CMD_BYTE 0x74 +#define MOD_CMD_BYTE 0xDB + +#define READ 0 +#define WRITE 1 + +static void stm_key_operaion(struct s_reader *reader, int operation) +{ + FILE *file = NULL; + char stmkeyfile[256]; + int i; + + if(reader->stmkeys == NULL) + { + snprintf(stmkeyfile,256,"%sstmkeys.bin",cs_confdir); + } + else + { + if(strchr(reader->stmkeys, '/') == NULL) + { + snprintf(stmkeyfile,256,"%s%s",cs_confdir, reader->stmkeys); + } + else + { + snprintf(stmkeyfile,256,"%s",reader->stmkeys); + } + } + + if((file = fopen(stmkeyfile, operation == READ ? "rb" : "wb")) == NULL) + { + cs_log("Error: can't' open stm key file (%s)", stmkeyfile); + return; + } + + if(operation == WRITE) + { + i = fwrite(&stm_keys_t, sizeof(stm_keys_t), 1, file); + } + else + { + i = fread(&stm_keys_t, sizeof(stm_keys_t), 1, file); + } + + fclose(file); + + if(!i) cs_log("Error read/write stm key file (%s)", stmkeyfile); +} + +static uint8_t xor(const uint8_t *cmd, int32_t cmdlen) +{ + int32_t i; + uint8_t checksum = 0x00; + for(i = 0; i < cmdlen; i++) + { checksum ^= cmd[i]; } + return checksum; +} + +static int8_t isValidDCW(uint8_t *dw) +{ + if (((dw[0] + dw[1] + dw[2]) & 0xFF) != dw[3]) + { + return 0; + } + if (((dw[4] + dw[5] + dw[6]) & 0xFF) != dw[7]) + { + return 0; + } + if (((dw[8] + dw[9] + dw[10]) & 0xFF) != dw[11]) + { + return 0; + } + if (((dw[12] + dw[13] + dw[14]) & 0xFF) != dw[15]) + { + return 0; + } + return 1; +} + +static int32_t drecas_send_cmd(struct s_reader *reader, uint8_t *cmd, int32_t cmdlen, uint8_t *cta_res, uint16_t *p_cta_lr, uint8_t dest) +{ + // any command starts with this, + // last byte is nr of bytes of the command that will be sent + uint8_t startcmd[3] = { 0xDB, 0x00, 0x00 }; + + uint8_t command[260]; + uint8_t checksum; + char tmp[256]; + + startcmd[1] = cmdlen + 2; // command + length + len + checksum bytes + startcmd[2] = dest; + + memcpy(command, startcmd, 3); + memcpy(command + 3, cmd, cmdlen); + cmdlen += 3; + checksum = xor(command+2, cmdlen-2); + command[cmdlen++] = checksum; + + rdr_log_dbg(reader, D_READER, "write to module: %s", cs_hexdump(0, command, cmdlen, tmp, sizeof(tmp))); + + ICC_Async_Transmit(reader, (uint32_t) cmdlen, 0, command, 0, 200); + ICC_Async_Receive(reader, 2, cta_res, 50, 3000000); + + ICC_Async_Receive(reader, cta_res[1], cta_res+2, 50, 3000000); + *p_cta_lr = cta_res[1] + 2; + + rdr_log_dbg(reader, D_READER, "answer from module: %s", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + + checksum = xor(cta_res + 2, *p_cta_lr - 3); + + if(cta_res[*p_cta_lr - 1] != checksum) + { + rdr_log(reader, "checksum does not match, expected %02x received %02x:%s", checksum, + cta_res[*p_cta_lr - 1], cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + return ERROR; // error + } + + return OK; +} + +static int32_t drecas_MSP_command(struct s_reader *reader, const uint8_t *cmd, int32_t cmdlen, uint8_t *cta_res, uint16_t *p_cta_lr) +{ + // attention: inputcommand will be changed!!! + // answer will be in cta_res, length cta_lr ; returning 1 = no error, return ERROR = err + + uint8_t startcmd[] = { 0x80, 0xFF, 0x10, 0x01, 0x05 }; // any command starts with this, + uint8_t command[256]; + uint8_t checksum; + char tmp[256]; + + startcmd[4] = cmdlen + 3; // command + length + len + checksum bytes + memcpy(command, startcmd, 5); + command[5] = MSP_CMD_BYTE; // type + command[6] = cmdlen + 1; // len = command + 1 checksum byte + memcpy(command + 7, cmd, cmdlen); + + checksum = ~xor(cmd, cmdlen); + + cmdlen += 7; + command[cmdlen++] = checksum; + + if(drecas_send_cmd(reader, command, cmdlen, cta_res, p_cta_lr, 1) != OK) return ERROR; + + if(cta_res[4] != MSP_CMD_BYTE) return ERROR; + + if((cta_res[5] == 0x03) && (cta_res[6] == 0xe2)) + { + switch(cta_res[7]) + { + case 0xe1: + rdr_log(reader, "checksum error: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe2: + rdr_log(reader, "wrong cmd len: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe3: + rdr_log(reader, "illegal command: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe4: + rdr_log(reader, "wrong adress type: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe5: + rdr_log(reader, "wrong CMD param: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe6: + rdr_log(reader, "wrong UA: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe7: + rdr_log(reader, "wrong group: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe8: + rdr_log(reader, "wrong key num: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xeb: + rdr_log(reader, "No key or subscribe : %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xec: + rdr_log(reader, "wrong signature: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xed: + rdr_log(reader, "wrong provider: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xef: + rdr_log(reader, "wrong GEO code: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + default: + rdr_log_dbg(reader, D_READER, "unknown error: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + } + return ERROR; // error + } + + int32_t length_excl_leader = *p_cta_lr; + + checksum = ~xor(cta_res + 6, length_excl_leader - 8); + + if(cta_res[length_excl_leader - 2] != checksum) + { + rdr_log(reader, "checksum does not match, expected %02x received %02x:%s", checksum, + cta_res[length_excl_leader - 2], cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + return ERROR; // error + } + + return OK; +} + +#define drecas_MSP_script_nb(cmd, len) \ + drecas_MSP_command(reader, cmd, len, cta_res, &cta_lr); \ + +#define drecas_MSP_script(cmd, len) \ + { \ + drecas_MSP_script_nb(cmd, len) \ + } + +#define drecas_MSP_cmd_nb(cmd) \ + drecas_MSP_command(reader, cmd, sizeof(cmd), cta_res, &cta_lr); \ + +#define drecas_MSP_cmd(cmd) \ + { \ + drecas_MSP_cmd_nb(cmd) \ + } + +static int32_t drecas_STM_command(struct s_reader *reader, const uint8_t *cmd, int32_t cmdlen, uint8_t *cta_res, uint16_t *p_cta_lr) +{ + // attention: inputcommand will be changed!!!! + //answer will be in cta_res, length cta_lr ; returning 1 = no error, return ERROR = err + + uint8_t command[256]; + uint8_t checksum; + char tmp[256]; + + command[0] = 0xC2; + command[1] = STM_CMD_BYTE; // type + command[2] = cmdlen + 1; // len = command + 1 checksum byte + memcpy(command + 3, cmd, cmdlen); + + checksum = ~xor(cmd, cmdlen); + + cmdlen += 3; + command[cmdlen++] = checksum; + + if(drecas_send_cmd(reader, command, cmdlen, cta_res, p_cta_lr, 0) != OK) return ERROR; + + if(cta_res[4] != STM_CMD_BYTE) return ERROR; + + if((cta_res[5] == 0x03) && (cta_res[6] == 0xe2)) + { + switch(cta_res[7]) + { + case 0xe1: + rdr_log(reader, "checksum error: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe2: + rdr_log(reader, "wrong cmd len: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe3: + rdr_log(reader, "illegal command: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe4: + rdr_log(reader, "wrong adress type: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe5: + rdr_log(reader, "wrong CMD param: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe6: + rdr_log(reader, "wrong UA: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe7: + rdr_log(reader, "wrong group: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe8: + rdr_log(reader, "wrong key num: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xeb: + rdr_log(reader, "No key or subscribe : %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xec: + rdr_log(reader, "wrong signature: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xed: + rdr_log(reader, "wrong provider: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xef: + rdr_log(reader, "wrong GEO code: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + default: + rdr_log_dbg(reader, D_READER, "unknown error: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + } + return ERROR; // error + } + + int32_t length_excl_leader = *p_cta_lr; + + checksum = ~xor(cta_res + 6, length_excl_leader - 8); + + if(cta_res[length_excl_leader - 2] != checksum) + { + rdr_log(reader, "checksum does not match, expected %02x received %02x:%s", checksum, + cta_res[length_excl_leader - 2], cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + return ERROR; // error + } + + return OK; +} + +#define drecas_STM_script_nb(cmd, len) \ + drecas_STM_command(reader, cmd, len, cta_res, &cta_lr); \ + +#define drecas_STM_script(cmd, len) \ + { \ + drecas_STM_script_nb(cmd, len) \ + } + +#define drecas_STM_cmd_nb(cmd) \ + drecas_STM_command(reader, cmd, sizeof(cmd), cta_res, &cta_lr); \ + +#define drecas_STM_cmd(cmd) \ + { \ + drecas_STM_cmd_nb(cmd) \ + } + +static int32_t drecas_set_provider_info(struct s_reader *reader) +{ + def_resp; + int32_t i; + uint8_t subscr[] = { 0x59, 0x14 }; // subscriptions + uint8_t dates[] = { 0x5b, 0x00, 0x14 }; // validity dates + struct dre_data *csystem_data = reader->csystem_data; + subscr[1] = csystem_data->provider; + + cs_clear_entitlement(reader); + + if(({drecas_MSP_cmd_nb(subscr)})) // ask subscription packages, returns error on 0x11 card + { + uint8_t pbm[32]; + char tmp_dbg[65]; + memcpy(pbm, cta_res + 7, 32); + rdr_log_dbg(reader, D_READER, "pbm: %s", cs_hexdump(0, pbm, 32, tmp_dbg, sizeof(tmp_dbg))); + + for(i = 0; i < 32; i++) + { + if(pbm[i] != 0xff) + { + dates[1] = i; + dates[2] = csystem_data->provider; + drecas_MSP_cmd(dates); // ask for validity dates + + time_t start; + time_t end; + start = (cta_res[7] << 24) | (cta_res[8] << 16) | (cta_res[9] << 8) | cta_res[10]; + end = (cta_res[11] << 24) | (cta_res[12] << 16) | (cta_res[13] << 8) | cta_res[14]; + + struct tm temp; + + localtime_r(&start, &temp); + int32_t startyear = temp.tm_year + 1900; + int32_t startmonth = temp.tm_mon + 1; + int32_t startday = temp.tm_mday; + localtime_r(&end, &temp); + int32_t endyear = temp.tm_year + 1900; + int32_t endmonth = temp.tm_mon + 1; + int32_t endday = temp.tm_mday; + rdr_log(reader, "active package %i valid from %04i/%02i/%02i to %04i/%02i/%02i", + i, startyear, startmonth, startday, endyear, endmonth, endday); + cs_add_entitlement(reader, reader->caid, b2ll(4, reader->prid[0]), 0, i, start, end, 5, 1); + } + } + } + + return OK; +} + +static int32_t drecas_card_init(struct s_reader *reader, ATR *newatr) +{ + get_atr; + def_resp; + uint8_t ua[] = { 0x43, 0x15 }; // get serial number (UA) + uint8_t providers[] = { 0x49, 0x15 }; // get providers + int32_t i; + char *card; + char tmp[9]; + uint8_t module_atr[] = { 0xDB, 0x0B, 0x08, 0xA3, 0x3B, 0x15, 0x11, 0x12, 0x01, 0x01, 0x11, 0x07, 0x90 }; + + if(memcmp(atr, module_atr, sizeof(module_atr)) != 0) + { return ERROR; } + + if(!cs_malloc(&reader->csystem_data, sizeof(struct dre_data))) + { return ERROR; } + struct dre_data *csystem_data = reader->csystem_data; + + csystem_data->provider = atr[10]; + uint8_t checksum = xor(atr + 5, 6); + + if(checksum != atr[11]) + { rdr_log(reader, "warning: expected ATR checksum %02x, smartcard reports %02x", checksum, atr[7]); } + + switch(atr[10]) + { + case 0x11: + card = "Tricolor Centr DRE2"; + reader->caid = 0x4ae1; + break; // 59 type card = MSP (74 type = ATMEL) + + case 0x14: + card = "Tricolor Syberia DRE2"; + reader->caid = 0x4ae1; + break; // 59 type card + + default: + return ERROR; + } + + memset(reader->prid, 0x00, 8); + + reader->prid[0][3] = csystem_data->provider; + + uint8_t cmd54[] = { 0x54, 0x14 }; // geocode + cmd54[1] = csystem_data->provider; + uint8_t geocode = 0; + + if(({drecas_MSP_cmd_nb(cmd54)})) // error would not be fatal, like on 0x11 cards + { geocode = cta_res[7]; } + + providers[1] = csystem_data->provider; + + if(!({drecas_MSP_cmd_nb(providers)})) + { return ERROR; } // fatal error + + if((cta_res[2] != 0x09) || (cta_res[3] != 0xC0)) + { return ERROR; } + + uint8_t provname[128]; + + for(i = 0; ((i < cta_res[6] - 6) && (i < 128)); i++) + { + provname[i] = cta_res[10 + i]; + if(provname[i] == 0x00) + { break; } + } + + int32_t major_version = cta_res[7]; + int32_t minor_version = cta_res[8]; + + ua[1] = csystem_data->provider; + drecas_MSP_cmd(ua); // error would not be fatal + + // discard first and last byte, last byte is always checksum, first is answer code + int32_t hexlength = cta_res[5] - 2; + + if(reader->force_ua) + { + rdr_log(reader, "WARNING!!!! used UA from force_ua %08X", reader->force_ua); + memcpy(cta_res + 7, &reader->force_ua, 8); + } + + reader->hexserial[0] = 0; + reader->hexserial[1] = 0; + memcpy(reader->hexserial + 2, cta_res + 7, hexlength); + + int32_t low_dre_id, dre_chksum; + uint8_t buf[32]; + + low_dre_id = ((cta_res[8] << 16) | (cta_res[9] << 8) | cta_res[10]) - 48608; + dre_chksum = 0; + snprintf((char *)buf, sizeof(buf), "%i%i%08i", csystem_data->provider - 16, major_version + 1, low_dre_id); + + for(i = 0; i < 32; i++) + { + if(buf[i] == 0x00) + { break; } + dre_chksum += buf[i] - 48; + } + + rdr_log(reader, "type: DRE Crypt, caid: %04X, serial: {%s}, dre id: %i%i%i%08i, geocode %i, card: %s v%i.%i", + reader->caid, cs_hexdump(0, reader->hexserial + 2, 4, tmp, sizeof(tmp)), dre_chksum, csystem_data->provider - 16, + major_version + 1, low_dre_id, geocode, card, major_version, minor_version); + + rdr_log(reader, "Provider name:%s.", provname); + + + memset(reader->sa, 0, sizeof(reader->sa)); + // copy first byte of unique address also in shared address, because we don't know what it is... + memcpy(reader->sa[0], reader->hexserial + 2, 1); + + rdr_log_sensitive(reader, "SA = %02X%02X%02X%02X, UA = {%s}", reader->sa[0][0], reader->sa[0][1], reader->sa[0][2], + reader->sa[0][3], cs_hexdump(0, reader->hexserial + 2, 4, tmp, sizeof(tmp))); + + reader->nprov = 1; + + // exec user script, wicardd format + if(reader->userscript != NULL) + { + uint8_t *usercmd = NULL; + int cmd_len; + int n; + char *tempbuf = malloc(2048); + if (!tempbuf) + { + return ERROR; + } + trim2(reader->userscript); + FILE *pFile = fopen(reader->userscript, "rt"); + + if(pFile != NULL) + { + do + { + tempbuf[0] = '\0'; + if(usercmd != NULL) NULLFREE(usercmd); + + if(fgets(tempbuf, 2048, pFile) == NULL) continue; + + if(cs_strlen(tempbuf) < 10) continue; + + trim2(tempbuf); + + if((tempbuf[0] != '5' && tempbuf[1] != '9') && (tempbuf[0] != '7' && tempbuf[1] != '4')) continue; + + strtoupper(tempbuf); + + cmd_len = cs_strlen(tempbuf) / 2 - 3; + usercmd = malloc(cmd_len); + if (!usercmd) + { + free(tempbuf); + return ERROR; + } + + for(i = 0, n = 4; i < cmd_len; i++, n += 2) + { + usercmd[i] = ((tempbuf[n] - (tempbuf[n] > 0x39 ? 0x37 : 0x30)) << 4) + ((tempbuf[n + 1] - (tempbuf[n + 1] > 0x39 ? 0x37 : 0x30)) & 0xF); + } + + if(tempbuf[0] != '7' && tempbuf[1] != '4') + { + rdr_log(reader, "Script %s", ({drecas_MSP_script_nb(usercmd, cmd_len)}) ? "done" : "error"); + } + else + { + rdr_log(reader, "Script %s", ({drecas_STM_script_nb(usercmd, cmd_len)}) ? "done" : "error"); + } + } + while(!feof(pFile)); + } + else + { + rdr_log(reader, "Can't open script file (%s)", reader->userscript); + } + + if(usercmd != NULL) free(usercmd); + if(tempbuf != NULL) free(tempbuf); + } + + if(csystem_data->provider == 0x11) + { + memset(reader->prid[1], 0x00, 8); + reader->prid[1][3] = 0xFE; + reader->nprov = 2; + } + + if(!drecas_set_provider_info(reader)) + { return ERROR; } // fatal error + + stm_key_operaion(reader, READ); + + rdr_log(reader, "ready for requests"); + return OK; +} + +static void DREover(struct s_reader *reader, const uint8_t *ECMdata, uint8_t *DW) +{ + uint32_t key_schedule[32]; + + if(reader->des_key_length < 128) + { + rdr_log(reader, "error: deskey is missing or too short"); + return; + } + + if(ECMdata[2] >= (43 + 4) && ECMdata[40] == 0x3A && ECMdata[41] == 0x4B) + { + des_set_key(&reader->des_key[(ECMdata[42] & 0x0F) * 8], key_schedule); + + des(DW, key_schedule, 0); // even DW post-process + des(DW + 8, key_schedule, 0); // odd DW post-process + }; +}; + +static int32_t drecas_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + def_resp; + uint16_t overcryptId; + uint8_t tmp[16]; + char tmp_dbg[256]; + struct dre_data *csystem_data = reader->csystem_data; + + if(reader->caid == 0x4ae1) + { + if(csystem_data->provider == 0x11 || csystem_data->provider == 0x14) + { + uint8_t ecmcmd51[] = { 0x51, 0x02, 0x56, 0x05, 0x00, 0x4A, 0xE3, // fixed header? + 0x9C, 0xDA, // first three nibbles count up, fourth nibble counts down; all ECMs sent twice + 0xC1, 0x71, 0x21, 0x06, 0xF0, 0x14, 0xA7, 0x0E, // next key? + 0x89, 0xDA, 0xC9, 0xD7, 0xFD, 0xB9, 0x06, 0xFD, // current key? + 0xD5, 0x1E, 0x2A, 0xA3, 0xB5, 0xA0, 0x82, 0x11, // key or signature? + 0x14 }; // provider + + memcpy(ecmcmd51 + 1, er->ecm + 5, 0x21); + rdr_log_dbg(reader, D_READER, "unused ECM info front:%s", cs_hexdump(0, er->ecm, 5, tmp_dbg, sizeof(tmp_dbg))); + rdr_log_dbg(reader, D_READER, "unused ECM info back:%s", cs_hexdump(0, er->ecm + 37, 4, tmp_dbg, sizeof(tmp_dbg))); + + rdr_log_dbg(reader, D_READER, "ECM: %s",cs_hexdump(0, er->ecm, er->ecm[2] + 3, tmp_dbg, sizeof(tmp_dbg))); + + ecmcmd51[33] = csystem_data->provider; // no part of sig + + if(({drecas_MSP_cmd_nb(ecmcmd51)})) // ecm request + { + if((cta_res[2] != 0x09) || (cta_res[3] != 0xC0)) + { return ERROR; } // exit if response is not 90 00 + + if(er->ecm[3] == 0x01) + { + uint8_t ecmcmd33[18] = { 0x33, 0x1F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + int i; + for(i = 0; i < 16; i++) + { + ecmcmd33[i + 2] = cta_res[7 + (i ^ 3)]; + } + + if(er->ecm[5] != stm_curkey[0] || er->ecm[6] != stm_curkey[1]) + { + uint8_t blank[0x30]; + memset(blank, 0, 0x30); + + if(memcmp(blank, stm_keys_t.stmcmd34[er->ecm[5] + (er->ecm[6] == 0x3B ? 0 : 32)], 0x30) == 0) + { + rdr_log_dbg(reader, D_READER, "STM key not found"); + return ERROR; + } + + if(!({drecas_STM_cmd_nb(stm_keys_t.stmcmd34[er->ecm[5] + (er->ecm[6] == 0x3B ? 0 : 32)])})) + { + rdr_log_dbg(reader, D_READER, "Error STM set key: %s",cs_hexdump(0, cta_res, cta_lr, tmp_dbg, sizeof(tmp_dbg))); + return ERROR; + } + + if((cta_res[cta_lr - 4] != 0x02) || (cta_res[cta_lr - 3] != 0xA2)) + { + rdr_log_dbg(reader, D_READER, "Error STM set key: %s",cs_hexdump(0, cta_res, cta_lr, tmp_dbg, sizeof(tmp_dbg))); + return ERROR; + } + } + + stm_curkey[0] = er->ecm[5]; + stm_curkey[1] = er->ecm[6]; + + if(!({drecas_STM_cmd_nb(ecmcmd33)})) + { return ERROR; } + + if(cta_res[1] != 0x17 || cta_res[6] != 0xD2) + { return ERROR; } + + memcpy(tmp, &cta_res[7], 16); + + for(i = 0; i < 16; i++) + { + cta_res[i + 7] = tmp[i ^ 3]; + } + } + + if(er->ecm[2] >= 46 && er->ecm[43] == 1 && csystem_data->provider == 0x11) + { + memcpy(&tmp[0], &cta_res[15], 8); + memcpy(&tmp[8], &cta_res[7], 8); + + overcryptId = b2i(2, &er->ecm[44]); + + rdr_log_dbg(reader, D_READER, "ICG ID: %04X", overcryptId); + + Drecrypt2OverCW(overcryptId,tmp); + + if(isValidDCW(tmp)) + { + memcpy(ea->cw, tmp, 16); + return OK; + } + return ERROR; + } + + DREover(reader, er->ecm, cta_res + 7); + + if(isValidDCW(cta_res + 7)) + { + memcpy(ea->cw, cta_res + 15, 8); + memcpy(ea->cw + 8, cta_res + 7, 8); + return OK; + } + } + } + } + return ERROR; +} + +static int32_t drecas_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + def_resp; + struct dre_data *csystem_data = reader->csystem_data; + + if(reader->caid == 0x4ae1) + { + if(reader->caid != b2i(2, ep->caid)) return ERROR; + + if(ep->type == UNIQUE && ep->emm[39] == 0x3d) + { + /* For new package activation. */ + uint8_t emmcmd58[26]; + emmcmd58[0] = 0x58; + memcpy(&emmcmd58[1], &ep->emm[40], 24); + emmcmd58[25] = csystem_data->provider; + + if(({drecas_MSP_cmd_nb(emmcmd58)})) + if((cta_res[2] != 0x09) || (cta_res[3] != 0xC0)) + { return ERROR; } + } + else if(ep->emm[0] == 0x86 && ep->emm[4] == 0x02 /*&& csystem_data->provider != 0x11*/) + { + uint8_t emmcmd52[0x3a]; + emmcmd52[0] = 0x52; + int32_t i; + + for(i = 0; i < 2; i++) + { + memcpy(emmcmd52 + 1, ep->emm + 5 + 32 + i * 56, 56); + + // check for shared address + if(ep->emm[3] != reader->sa[0][0]) + { return OK; } // ignore, wrong address + + emmcmd52[0x39] = csystem_data->provider; + + if(({drecas_MSP_cmd_nb(emmcmd52)})) + if((cta_res[2] != 0x09) || (cta_res[3] != 0xC0)) + { return ERROR; } // exit if response is not 90 00 + } + } + else if(ep->emm[0] == 0x86 && ep->emm[4] == 0x4D && csystem_data->provider == 0x11) + { + uint8_t emmcmd52[0x3a]; + emmcmd52[0] = 0x52; + emmcmd52[1] = 0x01; + emmcmd52[2] = ep->emm[5]; + emmcmd52[3] = 0x01; + emmcmd52[4] = ep->emm[3]; + emmcmd52[5] = 0; + emmcmd52[6] = 0; + emmcmd52[7] = 0; + emmcmd52[9] = 0x01; + emmcmd52[10] = 0x01; + emmcmd52[11] = 0; + memcpy(emmcmd52 + 13, ep->emm + 0x5C, 4); + int32_t i; + + for(i = 0; i < 2; i++) + { + emmcmd52[8] = ep->emm[0x61 + i * 0x29]; + + if(i == 0) emmcmd52[12] = ep->emm[0x60] == 0x56 ? 0x56 : 0x3B; + else emmcmd52[12] = ep->emm[0x60] == 0x56 ? 0x3B : 0x56; + + memcpy(emmcmd52 + 0x11, ep->emm + 0x62 + i * 0x29, 40); + + // check for shared address + if(ep->emm[3] != reader->sa[0][0]) + { return OK; } // ignore, wrong address + + emmcmd52[0x39] = csystem_data->provider; + + if(({drecas_MSP_cmd_nb(emmcmd52)})) + if((cta_res[2] != 0x09) || (cta_res[3] != 0xC0)) + { return ERROR; } // exit if response is not 90 00 + } + + uint8_t emmcmd34[0x30] = { + 0x34, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + emmcmd34[1] = ep->emm[0x05]; + emmcmd34[2] = ep->emm[0x5A]; + emmcmd34[3] = ep->emm[0x03]; + uint8_t need_save = 0; + + for(i = 0; i < 2; i++) + { + memcpy(&emmcmd34[7], &ep->emm[(i * 0x29) + 8] , 41); + + if(memcmp(emmcmd34, stm_keys_t.stmcmd34[ep->emm[0x05] + (ep->emm[7] == 0x3B ? i * 32 : (i == 0 ? 32 : 0))], 0x30) != 0) + { + memcpy(stm_keys_t.stmcmd34[ep->emm[0x05] + (ep->emm[7] == 0x3B ? i * 32 : (i == 0 ? 32 : 0))], emmcmd34, 0x30); + need_save = 1; + } + } + + if(need_save == 1) stm_key_operaion(reader, WRITE); + } + else if(ep->type == GLOBAL && ep->emm[0] == 0x91) + { + Drecrypt2OverEMM(ep->emm); + return OK; + } + else return OK; + } + + return ERROR; +} + +static int32_t drecas_card_info(struct s_reader *UNUSED(rdr)) +{ + return OK; +} + +const struct s_cardsystem reader_drecas = +{ + .desc = "drecas", + .caids = (uint16_t[]){ 0x4AE1, 0 }, + .do_emm = drecas_do_emm, + .do_ecm = drecas_do_ecm, + .card_info = drecas_card_info, + .card_init = drecas_card_init, + .get_emm_type = dre_common_get_emm_type, + .get_emm_filter = dre_common_get_emm_filter, +}; + +#endif diff --git a/reader-dre-common.c b/reader-dre-common.c new file mode 100644 index 0000000..1f530f9 --- /dev/null +++ b/reader-dre-common.c @@ -0,0 +1,845 @@ +#include "globals.h" +#include "oscam-string.h" +#include "reader-common.h" +#include "reader-dre-st20.h" +#include "reader-dre-common.h" + +int32_t dre_common_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + switch(ep->emm[0]) + { + case 0x87: + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, &ep->emm[3], 4); + return (!memcmp(&rdr->sa[0][0], &ep->emm[3], 4)); + + case 0x83: + case 0x89: + ep->type = SHARED; + // FIXME: Seems to be that SA is only used with caid 0x4ae1 + if(rdr->caid == 0x4ae1) + { + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, &ep->emm[3], 4); + return (!memcmp(&rdr->sa[0][0], &ep->emm[3], 4)); + } + else + { return 1; } + + case 0x8B: + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, &ep->emm[3], 4); + switch(ep->emm[7]) + { + case 0x61: + return (!memcmp(&rdr->hexserial[3] ,&ep->hexserial[1] , 2)); + case 0x62: + case 0x63: + default: + return (!memcmp(&rdr->hexserial[2] ,ep->hexserial , 4)); + } + + case 0x80: + case 0x86: + case 0x8C: + ep->type = SHARED; + memset(ep->hexserial, 0, 8); + ep->hexserial[0] = ep->emm[3]; + return ep->hexserial[0] == rdr->sa[0][0]; + + case 0x82: + case 0x91: + ep->type = GLOBAL; + return 1; + + default: + ep->type = UNKNOWN; + return 1; + } +} + +int32_t dre_common_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 9; + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { return ERROR; } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + if(rdr->caid == 0x2710) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x8B; + memcpy(&filters[idx].filter[1], &rdr->hexserial[2] , 4); + filters[idx].mask[0] = 0xFF; + filters[idx].mask[1] = 0x00; + filters[idx].mask[2] = 0xFF; + filters[idx].mask[3] = 0xFF; + filters[idx].mask[4] = 0xF0; + idx++; + } + else + { + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x80; + filters[idx].filter[1] = rdr->sa[0][0]; + filters[idx].mask[0] = 0xF2; + filters[idx].mask[1] = 0xFF; + idx++; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].filter[1] = rdr->sa[0][0]; + filters[idx].mask[0] = 0xF3; + if(rdr->caid == 0x4ae1) + { + memcpy(&filters[idx].filter[1], &rdr->sa[0][0], 4); + memset(&filters[idx].mask[1], 0xFF, 4); + } + filters[idx].mask[1] = 0xFF; + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x86; + filters[idx].filter[1] = rdr->sa[0][0]; + filters[idx].mask[0] = 0xFF; + filters[idx].mask[1] = 0xFF; + idx++; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x87; + filters[idx].mask[0] = 0xFF; + + memcpy(&filters[idx].filter[1], &rdr->sa[0][0], 4); + memset(&filters[idx].mask[1], 0xFF, 4); + + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x89; + filters[idx].mask[0] = 0xFF; + // FIXME: Seems to be that SA is only used with caid 0x4ae1 + if(rdr->caid == 0x4ae1) + { + memcpy(&filters[idx].filter[1], &rdr->sa[0][0], 4); + memset(&filters[idx].mask[1], 0xFF, 4); + } + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x8C; + filters[idx].filter[1] = rdr->sa[0][0]; + filters[idx].mask[0] = 0xFF; + filters[idx].mask[1] = 0xFF; + idx++; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x91; + filters[idx].mask[0] = 0xFF; + idx++; + } + + *filter_count = idx; + } + + return OK; +} + +// overcrypt code + +extern uint8_t dre_initial_snippet[3694]; + +static uint16_t gId = 0xFFFF; +static uint32_t decrypt_addr; +static uint32_t gRawSec = 0; +static uint8_t gVersion = 0xFF; + +typedef struct data_block_t +{ + uint8_t *data; + uint32_t size; + uint32_t used; +} data_block; + +typedef struct memory_block_t +{ + uint8_t *pos; + uint8_t *end; +} memory_block; + +static data_block raw_buffer = { NULL, 0, 0 }; +static data_block code_buffer = { NULL, 0, 0 }; + +static int offtin(uint8_t *buf) +{ + int y; + y = buf[7] & 0x7F; + y = y * 256; y += buf[6]; + y = y * 256; y += buf[5]; + y = y * 256; y += buf[4]; + y = y * 256; y += buf[3]; + y = y * 256; y += buf[2]; + y = y * 256; y += buf[1]; + y = y * 256; y += buf[0]; + if(buf[7] & 0x80) y -= y; + return y; +} + +static int bspatch(uint8_t *dest, uint8_t *src, int src_len, uint8_t *patch) +{ + int i, newsize, bzctrllen, bzdatalen, oldpos, newpos, ctrl[3]; + uint8_t *cstream, *dstream, *estream; + + if(memcmp(patch, "BSDIFF40", 8)) return -1; + + bzctrllen = offtin(patch + 8); + bzdatalen = offtin(patch + 16); + newsize = offtin(patch + 24); + if((bzctrllen < 0) || (bzdatalen < 0) || (newsize < 0)) return -1; + + oldpos = 0; + newpos = 0; + cstream = patch + 32; + dstream = cstream + bzctrllen; + estream = dstream + bzdatalen; + + while(newpos < newsize) + { + /* Read control data */ + for(i = 0; i < 3; i++) + { + ctrl[i] = offtin(cstream); + cstream += 8; + } + + /* Sanity-check */ + if((newpos + ctrl[0]) > newsize) return -1; + + /* Read diff string */ + memcpy(dest + newpos, dstream, ctrl[0]); + dstream += ctrl[0]; + + /* Add old data to diff string */ + for(i = 0; i < ctrl[0]; i++) + { + if(((oldpos + i) >= 0) && ((oldpos + i) < src_len)) dest[newpos + i] += src[oldpos + i]; + } + + /* Adjust pointers */ + newpos += ctrl[0]; + oldpos += ctrl[0]; + + /* Sanity-check */ + if((newpos + ctrl[1]) > newsize) return -1; + + /* Read extra string */ + memcpy(dest + newpos, estream, ctrl[1]); + estream += ctrl[1]; + + /* Adjust pointers */ + newpos += ctrl[1]; + oldpos += ctrl[2]; + } + return newsize; +} + +static int dre_unpack(uint8_t *dest, uint8_t *src, int len) +{ + uint8_t dbuf[0x1000], mask; + int i, soffs, doffs, dbidx, boffs, n; + + dbidx = 4078; + memset(dbuf, 32, 4078); + + for(soffs = 0, doffs = 0; soffs < len; ) + { + mask = src[soffs++]; + for(i = 0; i < 8 && soffs < len; i++, mask >>= 1) + { + if(mask & 1) + { + dest[doffs++] = dbuf[dbidx] = src[soffs++]; + dbidx = (dbidx + 1) & 0xfff; + } + else + { + boffs = src[soffs++]; + n = src[soffs++]; + boffs |= (n & 0xf0) << 4; + n &= 0xf; + n += 3; + + while(n--) + { + boffs &= 0xfff; + dest[doffs++] = dbuf[dbidx] = dbuf[boffs++]; + dbidx = (dbidx + 1) & 0xfff; + } + } + } + } + return doffs; +} + +typedef struct +{ + int magic_number; // magic number - value + int codesize; // size of code in the rcu + int entrypointoffset; // entrypoint offset into the code + int datasize; // size of data region + int datalocationoffset; // offset to place data at + int bsssize; // size of bss region + int bsslocationoffset; // offset to place bss at + int staticlinkoffset; // offset to staticlink in data + int relsize; // size of relocation table + int conssize; // size of constructor table + int dessize; // size of destructor table + int stacksize; // size of stack this rcu needs + int heapsize; // size of heap this rcu needs + int dbgfilenamesize; // size of .dbg filename + int slot0; // 4 words space for user use + int slot1; + int slot2; + int slot3; +} rcu_header_t; + +static void rcu_load_offs(uint32_t *offs, uint8_t *buf, int size) +{ + uint32_t i; + for(i = 0; i < size / sizeof(int); i++) + { + offs[i] = (uint32_t)(buf[3 + i * 4] << 24) | (buf[2 + i * 4] << 16) | (buf[1 + i * 4] << 8) | buf[0 + i * 4]; + } +} + +static void rcu_load(uint8_t *rcu) +{ + int rcu_data_size; + uint32_t i; + uint32_t nexports, nimports; + rcu_header_t rcuh; + uint8_t *ptr, *rcu_code, *rcu_data; + + rcu_load_offs((uint32_t *)&rcuh, rcu, sizeof(rcu_header_t)); + ptr = rcu + sizeof(rcu_header_t); + rcu_code = ptr; + ptr += rcuh.codesize; + rcu_data_size = rcuh.datasize + rcuh.bsssize; + rcu_data = ptr; + ptr += rcuh.datasize; + + if(rcuh.relsize) + { + uint32_t relocations[rcuh.relsize / sizeof(int)]; + rcu_load_offs(relocations, ptr, rcuh.relsize); + ptr += rcuh.relsize; + } + + if(rcuh.conssize) + { + uint32_t constructors[rcuh.conssize / sizeof(int)]; + rcu_load_offs(constructors, ptr, rcuh.conssize); + ptr += rcuh.conssize; + } + + if(rcuh.dessize) + { + uint32_t destructors[rcuh.dessize / sizeof(int)]; + rcu_load_offs(destructors, ptr, rcuh.dessize); + } + + ptr = rcu_data + rcu_data_size - 16 + 4; + rcu_load_offs(&nexports, ptr, sizeof(int)); + if(nexports) + { + uint32_t exports[nexports * 3]; + rcu_load_offs(exports, ptr - nexports * sizeof(int) * 3, nexports * sizeof(int) * 3); + for(i = 0; i < nexports; i++) + { + char *symbol = (char *) (rcu_code + exports[0 + i * 3]); + uint32_t faddr = exports[2 + i * 3]; + if(strcmp(symbol, "snippet_decrypt") == 0) decrypt_addr = faddr; + } + } + + ptr -= nexports * 3 * sizeof(int) + 8; + rcu_load_offs(&nimports, ptr, sizeof(int)); + if(nimports) + { + uint32_t imports[nimports * 3]; + rcu_load_offs(imports, ptr - nimports * sizeof(int) * 3, nimports * sizeof(int) * 3); + } +} + +int load_sections(uint8_t *body) +{ + uint8_t section[4096]; + uint16_t sect_len, sect_no, last_sect_no, curr_sect_no = 0; + int body_len, total_body_len = 0; + uint32_t i=0; + + while((i + 3) < raw_buffer.used) + { + memcpy(section, raw_buffer.data + i, 3); + i += 3; + memcpy(§_len, section + 1, 2); + sect_len = ntohs(sect_len) & 0xfff; + + memcpy(section + 3, raw_buffer.data + i, sect_len); + i += sect_len; + + if(section[0] != 0x91 || sect_len < (4 + 5)) continue; + + uint32_t crc = crc32(-1, section, sect_len + 3 - 4); + uint32_t sect_crc = (uint32_t) section[sect_len + 2] << 24 | section[sect_len + 1] << 16 | section[sect_len] << 8 | section[sect_len - 1]; + + if(crc != sect_crc) + { + cs_log("[icg] Broken section crc %08x %08x\n",(uint32_t) crc,(uint32_t) sect_crc); + continue; + } + + sect_no = section[6]; + last_sect_no = section[7]; + + if(curr_sect_no == 0) + { + gId = (section[13] << 8) | section[14]; + gVersion = (section[5] & 0x3e) >> 1; + } + + if(curr_sect_no == sect_no) + { + body_len = sect_len - 5 - 4; + memcpy(body, section + 8, body_len); + total_body_len += body_len; + body += body_len; + curr_sect_no++; + } + + if(curr_sect_no > last_sect_no) break; + } + return total_body_len; +} + +static int32_t allocate_data_block(data_block *buff, uint32_t size) +{ + if (buff->data == NULL) + { + buff->data = malloc(size); + if (buff->data == NULL) { return -1; } + buff->size = size; + buff->used = 0; + } + + if (buff->size < size) + { + uint8_t *new_buff; + + new_buff = malloc(size); + if (new_buff == NULL) { return -1; } + memcpy(new_buff, buff->data, buff->used); + + free(buff->data); + + buff->data = new_buff; + buff->size = size; + } + return 0; +} + +uint8_t Drecrypt2OverCW(uint16_t overcryptId, uint8_t *cw) +{ + if(overcryptId == gId) + { + if(st20_run(code_buffer.data, code_buffer.used, decrypt_addr, cw, overcryptId)) return 0; + else return 1; + } + else + { + cs_log("[icg] ICG algo %04X not found", overcryptId); + } + return 2; +} + +void Drecrypt2OverEMM(uint8_t *emm) +{ + uint32_t dataLen; + + if(gVersion == (emm[5] & 0x3e) >> 1) + { + return; + } + + if(emm[6] == 0 && (gId == ((emm[13] << 8) | emm[14]))) + { + return; + } + + if (gRawSec == 0) + { + if(emm[6] != 0) + { + return; + } + } + + if (emm[6] != gRawSec) + { + gRawSec = 0; + return; + } + + dataLen = ((emm[1] & 0xF) << 8) | emm[2]; + dataLen += 3; + if(dataLen < 4) + { + return; + } + + if (gRawSec == 0) + { + raw_buffer.used = 0; + } + + if (allocate_data_block(&raw_buffer, raw_buffer.used + dataLen) < 0) + { + cs_log("[icg] No free memory"); + return; + } + + memcpy(raw_buffer.data + raw_buffer.used, emm, dataLen); + raw_buffer.used += dataLen; + + if (emm[6] != emm[7]) + { + gRawSec++; + return; + } + + gRawSec = 0; + + int patch_len, rcu_len, len, snip_len; + uint8_t *buf = malloc(0x1000), *snip = malloc(0x10000), *rcu = malloc(0x10000), *patch = malloc(0x10000); + + if(buf == NULL || snip == NULL || rcu == NULL || patch == NULL) + { + cs_log("[icg] No free memory"); + goto exit_werr; + } + + snip_len = (dre_initial_snippet[4] << 24) | (dre_initial_snippet[5] << 16) | (dre_initial_snippet[6] << 8) | dre_initial_snippet[7]; + + if(dre_unpack(snip, dre_initial_snippet + 8, sizeof(dre_initial_snippet) - 8) >= snip_len) + { + if((len = load_sections(buf)) > 0) + { + patch_len = (buf[14] << 24) | (buf[15] << 16) | (buf[16] << 8) | buf[17]; + if(dre_unpack(patch, buf + 18, len - 18) >= patch_len) + { + rcu_len = bspatch(rcu, snip, snip_len, patch); + if(rcu_len > 0) + { + rcu_load(rcu); + if(allocate_data_block(&code_buffer, rcu_len) < 0) + { + cs_log("[icg] No free memory"); + goto exit_werr; + } + + memcpy(code_buffer.data, rcu, rcu_len); + code_buffer.used = (uint32_t) rcu_len; + cs_log("[icg] snippet patch created. ICG algo %04X", gId); + } + } + } + } + +exit_werr: + if(buf != NULL) free(buf); + if(snip != NULL) free(snip); + if(rcu != NULL) free(rcu); + if(patch != NULL) free(patch); +} + +void ReasmEMM82(uint8_t *emm) +{ + uint16_t dataLen = (uint16_t) (((emm[1] & 0xF) << 8) | emm[2]) + 5; + uint8_t emmbuf[dataLen]; + uint32_t crc; + + emmbuf[0] = 0x91; + emmbuf[1] = (uint8_t)(((dataLen - 3) >> 8) | 0x80); + emmbuf[2] = (uint8_t)(dataLen - 3) & 0xFF; + emmbuf[3] = ((emm[7] + 1) & 0x0F); + emmbuf[4] = 0; + + memcpy(&emmbuf[5], &emm[7], dataLen); + + emmbuf[5] += 1; + + crc = crc32(-1, emmbuf, dataLen - 4); + + emmbuf[dataLen - 1] = (uint8_t) ((crc >> 24) & 0xFF); + emmbuf[dataLen - 2] = (uint8_t) ((crc >> 16) & 0xFF); + emmbuf[dataLen - 3] = (uint8_t) ((crc >> 8) & 0xFF); + emmbuf[dataLen - 4] = (uint8_t) (crc & 0xFF); + + Drecrypt2OverEMM(emmbuf); +} + +uint8_t dre_initial_snippet[3694] = +{ + 0x6F, 0xF0, 0x0E, 0x0D, 0x00, 0x00, 0x18, 0x75, 0xFF, 0x75, 0x63, 0x72, 0x2E, 0x2C, 0x17, 0x00, + 0x00, 0x3F, 0x3D, 0x15, 0x00, 0x00, 0x9C, 0x00, 0xFB, 0xF7, 0xFA, 0xF5, 0x85, 0x4C, 0xFB, 0xF0, + 0x04, 0xFB, 0xF8, 0xFB, 0xF1, 0xF7, 0xF0, 0x24, 0x0E, 0x42, 0xFF, 0x22, 0xF0, 0x41, 0x22, 0xF0, + 0x60, 0xBF, 0x73, 0xFF, 0x24, 0x4B, 0x72, 0x9D, 0xD0, 0x70, 0x42, 0xF4, 0xBF, 0xA4, 0x70, 0xB1, + 0x22, 0xF0, 0x40, 0x49, 0x00, 0x64, 0xFF, 0xB0, 0x25, 0x73, 0xD0, 0x24, 0x4C, 0x25, 0x72, 0xFF, + 0x25, 0xFF, 0x21, 0x22, 0x29, 0xA2, 0x25, 0x72, 0xFF, 0x42, 0x21, 0xFB, 0xFA, 0xF6, 0x22, 0x26, + 0x03, 0xFF, 0x80, 0x26, 0x2A, 0x0E, 0x80, 0x22, 0x2B, 0x07, 0xFF, 0x80, 0x22, 0x28, 0x05, 0x80, + 0x26, 0x2C, 0x07, 0xFF, 0x80, 0x2C, 0x24, 0x05, 0x80, 0x27, 0x20, 0x09, 0xBF, 0x80, 0x26, 0x2E, + 0x00, 0x80, 0x23, 0x73, 0x00, 0x22, 0xFF, 0x2C, 0x09, 0x80, 0x25, 0x22, 0x0B, 0x80, 0x25, 0xFF, + 0x2C, 0x04, 0x80, 0x21, 0x21, 0x2F, 0x03, 0x2A, 0xFF, 0x24, 0x08, 0x80, 0x2A, 0x27, 0x0F, 0x80, + 0x23, 0x7D, 0x2F, 0x8C, 0x00, 0x2D, 0x05, 0x80, 0x23, 0x2C, 0x84, 0x01, 0xFA, 0x90, 0x00, 0x2F, + 0x78, 0x00, 0x26, 0x08, 0x80, 0x2E, 0x0A, 0xFF, 0x80, 0x80, 0x27, 0x24, 0x09, 0x80, 0x27, 0x2C, + 0xCF, 0x06, 0x80, 0x27, 0x29, 0x78, 0x00, 0x6F, 0x00, 0x2A, 0x2F, 0xFF, 0x0D, 0x80, 0x28, 0x23, + 0x05, 0x80, 0x2B, 0x28, 0xF7, 0x08, 0x80, 0x28, 0xB7, 0x00, 0x26, 0x2F, 0x0E, 0x80, 0xFF, 0x27, + 0x25, 0x00, 0x80, 0x2B, 0x21, 0x06, 0x80, 0xFF, 0x2A, 0x25, 0x01, 0x80, 0x21, 0x28, 0x00, 0x80, + 0xCB, 0x22, 0x2E, 0xB0, 0x01, 0x0D, 0x6D, 0x00, 0x88, 0x00, 0x28, 0x03, 0xFE, 0x91, 0x00, 0x0C, + 0x80, 0x2C, 0x2D, 0x0E, 0x80, 0x2A, 0xB5, 0x04, 0xBC, 0x00, 0x2A, 0xD4, 0x00, 0x27, 0x0C, 0xE5, + 0x00, 0x07, 0xFF, 0x80, 0x23, 0x2D, 0x0D, 0x80, 0x25, 0x21, 0x00, 0xFF, 0x80, 0x24, 0x24, 0x0D, + 0x80, 0x24, 0x2B, 0x08, 0xFF, 0x80, 0x24, 0x2D, 0x0C, 0x80, 0x2D, 0x21, 0x0A, 0xFE, 0x0D, 0x10, + 0x0C, 0x80, 0x28, 0x28, 0x02, 0x80, 0x26, 0xDF, 0x27, 0x0A, 0x80, 0x2A, 0x21, 0x24, 0x10, 0x20, + 0x08, 0xEA, 0x85, 0x00, 0x03, 0x21, 0x10, 0x0A, 0x19, 0x10, 0x07, 0x80, 0x2F, 0xFE, 0xCF, 0x01, + 0x2D, 0x0B, 0x80, 0x2A, 0x24, 0x00, 0x80, 0xFF, 0x29, 0x20, 0x0E, 0x80, 0x26, 0x22, 0x0A, 0x80, + 0xFF, 0x21, 0x20, 0x25, 0x01, 0x2A, 0x21, 0x01, 0x80, 0xD3, 0x2A, 0x03, 0xBC, 0x00, 0x6F, 0x10, + 0x24, 0x6F, 0x10, 0x21, 0x22, 0xEF, 0x03, 0x80, 0x2C, 0x02, 0x78, 0x10, 0x20, 0x29, 0x0E, 0xFF, + 0x28, 0x27, 0x00, 0x80, 0x27, 0x2F, 0x03, 0x80, 0xFB, 0x29, 0x21, 0x8C, 0x00, 0x73, 0x45, 0x25, + 0x71, 0x61, 0xFB, 0x2B, 0x96, 0x43, 0x01, 0x2D, 0x20, 0xAE, 0x70, 0x25, 0xFF, 0xB0, 0x22, 0xF0, + 0x70, 0xF1, 0x41, 0x24, 0xF6, 0xF5, 0xC0, 0xA2, 0x12, 0x81, 0xA7, 0x17, 0x21, 0x1D, 0x42, 0x24, + 0x7F, 0xFA, 0x21, 0x7D, 0x4C, 0x27, 0xF8, 0xD1, 0xC1, 0x10, 0xEF, 0x24, 0xF0, 0x21, 0xDD, 0xC7, + 0x11, 0xF1, 0x21, 0xDD, 0xDF, 0x71, 0x21, 0x7D, 0x23, 0xF3, 0xCC, 0x10, 0x1D, 0x70, 0x6A, 0xBE, + 0x10, 0x42, 0xB7, 0x13, 0x1F, 0xBE, 0x11, 0x7F, 0x43, 0xC4, 0x11, 0xEB, 0x7F, 0x43, 0xCA, 0x10, + 0xDF, 0xF3, 0x11, 0xF1, 0x21, 0xDF, 0x77, 0x71, 0x21, 0x7F, 0xD8, 0x10, 0xDF, 0x21, 0x1F, 0xDE, + 0x18, 0x3B, 0x1E, 0x44, 0xBF, 0x10, 0x7E, 0x21, 0x45, 0xC4, 0x11, 0x1A, 0x20, 0xFA, 0xCA, 0x10, + 0xDE, 0x20, 0x22, 0xF1, 0x21, 0xDE, 0x71, 0x21, 0x3D, 0x7E, 0xD8, 0x10, 0xDE, 0x21, 0x1E, 0x70, + 0x16, 0x20, 0xE2, 0x13, 0x7B, 0x22, 0x14, 0x16, 0x20, 0x22, 0x74, 0x21, 0x47, 0xC4, 0x10, 0xDE, + 0x48, 0x21, 0x24, 0xF0, 0x22, 0xD4, 0x4F, 0x22, 0xF1, 0x22, 0xBF, 0xD4, 0x71, 0x22, 0x74, 0x23, + 0xF3, 0x55, 0x20, 0x14, 0x3A, 0x39, 0x28, 0x18, 0x45, 0x21, 0x78, 0x21, 0x4E, 0x4C, 0x21, 0x78, + 0x20, 0xFA, 0x53, 0x20, 0xD8, 0x7E, 0x22, 0xF1, 0x22, 0xD8, 0x71, 0x22, 0x5D, 0x78, 0x62, 0x20, + 0xD8, 0x22, 0x18, 0x39, 0x28, 0x1A, 0x45, 0x21, 0x5B, 0x7A, 0x49, 0x4C, 0x21, 0x7A, 0x49, 0x53, + 0x20, 0xDA, 0xAC, 0x21, 0xBF, 0xF1, 0x22, 0xDA, 0x71, 0x22, 0x7A, 0x62, 0x20, 0xDA, 0x7B, 0x22, + 0x1A, 0x39, 0x27, 0x81, 0x21, 0x1A, 0x43, 0xBF, 0x10, 0xD1, 0x7A, 0x4B, 0x21, 0xD3, 0x20, 0xCA, + 0x10, 0xDA, 0xD9, 0x21, 0xF1, 0x21, 0xE7, 0xDA, 0x71, 0x21, 0xBC, 0x20, 0xDE, 0x20, 0x1A, 0x70, + 0x81, 0xAC, 0xD0, 0x20, 0xC7, 0x24, 0x22, 0x19, 0x45, 0x21, 0x79, 0x7A, 0x22, 0x79, 0xFA, 0x81, + 0x21, 0xD9, 0x07, 0x31, 0xF1, 0x22, 0xD9, 0x71, 0x22, 0x7D, 0x79, 0x62, 0x20, 0xD9, 0x22, 0x19, + 0x70, 0x81, 0xC4, 0x27, 0xAB, 0x22, 0x1B, 0x45, 0x21, 0x7B, 0x4B, 0x22, 0x7B, 0x52, 0x21, 0xDB, + 0x7E, 0x35, 0x31, 0xF1, 0x22, 0xDB, 0x71, 0x22, 0x7B, 0x62, 0x20, 0x57, 0xDB, 0x22, 0x1B, 0x1E, + 0x3A, 0x17, 0x45, 0x21, 0x77, 0xA8, 0x22, 0xF5, 0x77, 0xAE, 0x21, 0xD7, 0x63, 0x31, 0xF1, 0x22, + 0xD7, 0x71, 0xBB, 0x22, 0x77, 0x62, 0x20, 0xD7, 0x22, 0x17, 0x1E, 0x38, 0x82, 0x1B, 0x22, 0x13, + 0xD0, 0x20, 0x22, 0x73, 0xEF, 0x11, 0x8B, 0x30, 0x53, 0x20, 0xFD, 0xD3, 0x91, 0x31, 0xF1, 0x22, + 0xD3, 0x71, 0x22, 0x73, 0xBE, 0x62, 0x20, 0xD3, 0x22, 0x13, 0x70, 0x82, 0xF2, 0x26, 0x82, 0xDB, + 0x22, 0x12, 0x88, 0x31, 0x72, 0x44, 0x4C, 0x21, 0x72, 0x44, 0xFA, 0x53, 0x20, 0xD2, 0xBF, 0x31, + 0xF1, 0x22, 0xD2, 0x71, 0x22, 0x5D, 0x72, 0x62, 0x20, 0xD2, 0x22, 0x12, 0xA8, 0x3A, 0x15, 0x88, + 0x31, 0x5B, 0x75, 0x46, 0x4C, 0x21, 0x75, 0x46, 0x53, 0x20, 0xD5, 0xED, 0x31, 0xBF, 0xF1, 0x22, + 0xD5, 0x71, 0x22, 0x75, 0x62, 0x20, 0xD5, 0x6B, 0x22, 0x15, 0xA8, 0x3A, 0x11, 0x45, 0x21, 0x71, + 0x4F, 0x4C, 0x21, 0xF3, 0x71, 0x4F, 0x53, 0x20, 0x1A, 0x42, 0xF1, 0x22, 0xD1, 0x71, 0xFB, 0x22, + 0x71, 0x62, 0x20, 0xD1, 0x22, 0x11, 0x70, 0x82, 0x16, 0x7C, 0x37, 0x21, 0x1C, 0x16, 0x21, 0x7C, + 0x1B, 0x23, 0x44, 0x40, 0xCA, 0x10, 0xFD, 0xDC, 0x4A, 0x42, 0xF1, 0x21, 0xDC, 0x71, 0x21, 0x7C, + 0x6E, 0xD8, 0x10, 0xDC, 0x21, 0x1C, 0x32, 0x49, 0x22, 0x10, 0x45, 0x21, 0x47, 0x70, 0x21, 0x4A, + 0x4C, 0x21, 0x75, 0x40, 0x53, 0x20, 0xD0, 0x7B, 0x42, 0xBF, 0xF1, 0x22, 0xD0, 0x71, 0x22, 0x70, + 0x62, 0x20, 0xD0, 0xB3, 0x22, 0x10, 0x32, 0x4A, 0x2B, 0x31, 0x21, 0x7B, 0xEF, 0x12, 0x7B, 0xFA, + 0xF5, 0x11, 0xDB, 0xAB, 0x41, 0xF1, 0x21, 0xDB, 0x71, 0x21, 0x74, 0x45, 0x30, 0xB0, 0x40, 0x1B, + 0x32, 0x48, 0x51, 0x21, 0x11, 0xBE, 0x11, 0xE5, 0x71, 0xC3, 0x12, 0x71, 0xC9, 0x11, 0xD8, 0x42, + 0xF1, 0x21, 0xD1, 0x73, 0x71, 0x21, 0x2B, 0x40, 0xDE, 0x40, 0x11, 0x70, 0x51, 0xDF, 0x16, 0x57, + 0x51, 0x21, 0x10, 0xBE, 0x11, 0x70, 0xD5, 0x22, 0x70, 0xDB, 0x21, 0x7D, 0xD0, 0x07, 0x51, 0xF1, + 0x21, 0xD0, 0x71, 0x21, 0x8D, 0x40, 0xAA, 0x0C, 0x50, 0x10, 0xF0, 0x49, 0x1E, 0xBE, 0x10, 0x7E, + 0xA8, 0x21, 0x7E, 0xBA, 0xAE, 0x20, 0xDE, 0x33, 0x50, 0xF1, 0xDE, 0x71, 0x32, 0x20, 0xDE, 0xD9, + 0x1E, 0xF0, 0x49, 0xE3, 0x31, 0x75, 0x4D, 0xC4, 0x10, 0x75, 0x4D, 0x77, 0x24, 0xF0, 0xD5, 0x58, + 0x50, 0xF1, 0xD5, 0x71, 0xFD, 0x30, 0xAF, 0xD5, 0x15, 0x70, 0x51, 0xF2, 0x26, 0x51, 0xB5, 0x31, + 0x72, 0xEA, 0xE9, 0x31, 0x72, 0xEF, 0x30, 0xD2, 0x7D, 0x50, 0xF1, 0xD2, 0x71, 0xA6, 0xCF, 0x30, + 0xD2, 0x12, 0x68, 0x59, 0x87, 0x31, 0x73, 0xA8, 0x21, 0x73, 0xBA, 0xAE, 0x20, 0xD3, 0xA2, 0x50, + 0xF1, 0xD3, 0x71, 0xA1, 0x30, 0xD3, 0xD9, 0x13, 0x68, 0x59, 0x44, 0x21, 0x74, 0x4B, 0xC4, 0x10, + 0x74, 0x4B, 0x77, 0x24, 0xF0, 0xD4, 0xC7, 0x50, 0xF1, 0xD4, 0x71, 0x61, 0x20, 0xCF, 0xD4, 0x14, + 0x70, 0x51, 0xC4, 0x47, 0x11, 0x41, 0x71, 0x21, 0x9A, 0xA8, 0x20, 0xD2, 0xE7, 0x50, 0x24, 0xF0, + 0xE6, 0x40, 0xB5, 0x20, 0xD1, 0xA5, 0x72, 0x2B, 0x40, 0xD1, 0xEF, 0x40, 0xC4, 0x47, 0x16, 0x16, + 0x20, 0x76, 0xB9, 0x21, 0xC3, 0x51, 0x0F, 0x60, 0x24, 0xF0, 0xD6, 0x15, 0x61, 0xF1, 0x7F, 0xD6, + 0x71, 0x76, 0x23, 0xF3, 0xD6, 0x16, 0xD7, 0x59, 0x72, 0x59, 0x31, 0x77, 0x79, 0x22, 0x37, 0x60, + 0x24, 0xF0, 0xD7, 0x3D, 0x61, 0xB7, 0xF1, 0xD7, 0x71, 0x73, 0x30, 0xD7, 0x17, 0xD7, 0x58, 0x85, + 0x55, 0x1F, 0xD0, 0x20, 0x7F, 0xBB, 0x31, 0x7F, 0xC1, 0x30, 0xDF, 0x64, 0x60, 0xF7, 0xF1, 0xDF, + 0x71, 0x03, 0x20, 0xDF, 0x1F, 0x70, 0x85, 0x6E, 0xF2, 0x26, 0x86, 0x22, 0x16, 0xBE, 0x10, 0x22, + 0x76, 0x77, 0x42, 0xF5, 0x76, 0x7E, 0x41, 0xD6, 0x8B, 0x61, 0xF1, 0x22, 0xD6, 0x71, 0xB9, 0x22, + 0x22, 0x60, 0x90, 0x60, 0x16, 0x70, 0x86, 0xDF, 0x16, 0x86, 0x55, 0x19, 0xBE, 0x10, 0x79, 0xEF, + 0x11, 0x79, 0xF5, 0x10, 0xD9, 0xB7, 0x60, 0xB7, 0xF1, 0xD9, 0x71, 0x17, 0x30, 0xD9, 0x19, 0xA2, + 0x69, 0x1A, 0xF6, 0xBE, 0x10, 0x7A, 0x48, 0xC4, 0x10, 0x7A, 0x48, 0x24, 0xF0, 0xDD, 0xDA, 0xDC, + 0x60, 0xF1, 0xDA, 0x71, 0xBC, 0x20, 0xDA, 0x1A, 0xAA, 0xA2, 0x69, 0x18, 0xBE, 0x10, 0x78, 0xA8, + 0x21, 0x78, 0xAE, 0x20, 0xD8, 0x6E, 0x01, 0x70, 0xF1, 0xD8, 0x71, 0x90, 0x20, 0xD8, 0x18, 0xA2, + 0x68, 0x9F, 0x87, 0x1B, 0x41, 0x24, 0xFA, 0xA6, 0x42, 0xAC, 0x41, 0xDB, 0xF6, 0xB3, 0x41, 0xDB, + 0x71, 0x45, 0x30, 0xDB, 0x1B, 0x70, 0x87, 0x54, 0x1E, 0x70, 0x16, 0x74, 0x1C, 0x1E, 0x70, 0x7C, + 0xBB, 0x31, 0x7C, 0xC1, 0x30, 0xDD, 0xDC, 0x4B, 0x70, 0xF1, 0xDC, 0x71, 0x5C, 0x40, 0xDC, 0x1C, + 0xAA, 0x36, 0x79, 0x1D, 0x1E, 0x70, 0x7D, 0x1C, 0x21, 0x7D, 0x23, 0x20, 0xDD, 0x6E, 0x70, 0x70, + 0xF1, 0xDD, 0x71, 0xD7, 0x10, 0xDD, 0x1D, 0x36, 0x77, 0xFE, 0xE2, 0x12, 0x40, 0xD1, 0x70, 0x71, + 0xF2, 0xF1, 0xD2, 0xBF, 0x70, 0x41, 0x71, 0xFC, 0xF2, 0xF1, 0x91, 0x70, 0x23, 0xEB, 0xFB, 0x72, + 0x97, 0x70, 0x70, 0x9E, 0x70, 0x71, 0x81, 0xD1, 0xBF, 0x41, 0x71, 0xF9, 0xA2, 0x62, 0x0F, 0x8A, + 0x7A, 0x42, 0x50, 0x98, 0x77, 0xBF, 0x70, 0xA5, 0x7F, 0x8F, 0x71, 0x85, 0x93, 0x71, 0x47, 0x98, + 0x73, 0xF1, 0x85, 0x9E, 0x71, 0xE8, 0x70, 0xCD, 0x77, 0xA3, 0x80, 0x62, 0x0C, 0x4A, 0x8A, 0x76, + 0x86, 0xE4, 0x78, 0x86, 0xF0, 0x7F, 0x02, 0x89, 0x81, 0x93, 0x71, 0x45, 0x44, 0x98, 0x73, 0x81, + 0x9E, 0x71, 0x3E, 0x80, 0xCD, 0x74, 0x42, 0xFF, 0x7C, 0x05, 0x83, 0xE4, 0x78, 0x83, 0x1B, 0x8B, + 0x54, 0x8D, 0x39, 0x82, 0xE8, 0x74, 0x45, 0x82, 0x2A, 0x1F, 0x87, 0x43, 0xFF, 0x7C, 0x82, 0xE4, + 0x78, 0x82, 0x9C, 0x8F, 0x02, 0x89, 0xD4, 0xE4, 0x78, 0x1B, 0x8B, 0x44, 0xFF, 0x72, 0x0E, 0xE2, + 0x12, 0x2A, 0x23, 0xFF, 0x4F, 0x21, 0xFB, 0x23, 0x1C, 0x21, 0x40, 0x24, 0xFF, 0xFA, 0x40, 0xD1, + 0x23, 0x1C, 0x71, 0x70, 0xF2, 0xE9, 0xF1, 0xC1, 0x30, 0x1B, 0x91, 0xF1, 0x19, 0x91, 0x4F, 0x24, + 0xF6, 0x3F, 0x23, 0x1C, 0xF2, 0xF1, 0x24, 0xFB, 0x19, 0x90, 0xCF, 0x72, 0xB5, 0x48, 0xAD, 0x71, + 0x07, 0x05, 0x93, 0x21, 0x44, 0x0D, 0x90, 0x18, 0x3A, 0x11, 0x94, 0x18, 0x19, 0x9F, 0xF6, 0x23, + 0x18, 0x2E, 0x9F, 0xE2, 0x12, 0x7F, 0x29, 0x2E, 0x49, 0x21, 0xFB, 0x24, 0x10, 0x11, 0x93, 0x3B, + 0x24, 0x10, 0x54, 0x9F, 0xF6, 0x24, 0x10, 0x69, 0x9F, 0x7B, 0x93, 0x33, 0x2B, 0x4E, 0x0D, 0x90, + 0x86, 0x94, 0x23, 0x10, 0x62, 0x82, 0x1D, 0x95, 0xA0, 0x62, 0x82, 0x29, 0x91, 0xA3, 0x92, 0x6E, + 0x83, 0xD1, 0x70, 0x45, 0xAD, 0x71, 0x04, 0x56, 0x7B, 0x93, 0x29, 0x40, 0x0D, 0x90, 0x14, 0x11, + 0x94, 0x14, 0x8F, 0x9F, 0x67, 0xF6, 0x23, 0x14, 0xA4, 0x9F, 0x7B, 0x93, 0x26, 0x45, 0x83, 0x90, + 0xC2, 0x4B, 0x94, 0x24, 0x53, 0x9F, 0xA0, 0x90, 0x68, 0x9F, 0x7A, 0x94, 0x23, 0x4A, 0xD7, 0x21, + 0xFB, 0x22, 0x10, 0x94, 0x22, 0x18, 0x9F, 0x24, 0xF6, 0xA9, 0x22, 0x2D, 0x9F, 0x7A, 0x94, 0x20, + 0x0C, 0x90, 0x24, 0xFF, 0x94, 0x24, 0xB0, 0x07, 0xAF, 0xA0, 0x90, 0x1C, 0xAF, 0x3F, 0x93, 0x28, + 0x2E, 0x47, 0x90, 0x24, 0x82, 0x10, 0x94, 0x24, 0x7D, 0xAF, 0xA0, 0x90, 0x92, 0xAA, 0xAC, 0x72, + 0x3F, 0x93, 0x4C, 0xDF, 0x21, 0xD4, 0x4D, 0x21, 0xD5, 0x52, 0x00, 0x42, 0x21, 0xFF, 0x14, 0x25, + 0x71, 0x28, 0x2B, 0x95, 0xD0, 0x70, 0xF9, 0xA5, 0xA1, 0x12, 0xE2, 0x12, 0x21, 0x4E, 0x21, 0xD8, + 0x22, 0x57, 0x40, 0x21, 0xD9, 0x26, 0xB2, 0x18, 0x2C, 0xB0, 0x29, 0x30, 0xBB, 0xFF, 0x23, 0x44, + 0x21, 0xD6, 0x23, 0x45, 0x21, 0xD7, 0xEA, 0x26, 0xB2, 0x16, 0x2C, 0xB0, 0x27, 0x30, 0xBB, 0x46, + 0x21, 0xD2, 0xAF, 0x24, 0x40, 0x21, 0xD3, 0x26, 0xB2, 0x12, 0x2C, 0xB0, 0x25, 0xDC, 0x99, 0x10, + 0x33, 0xB8, 0x25, 0x73, 0x21, 0x94, 0x10, 0x6B, 0x2A, 0xBF, 0x9A, 0xD0, 0x70, 0x41, 0xF4, 0xA7, + 0x32, 0xB4, 0x70, 0x5F, 0xC1, 0xC0, 0xA2, 0x21, 0x01, 0x9D, 0xB0, 0x47, 0xA1, 0xB0, 0x7B, 0x28, + 0x9F, 0x90, 0xBF, 0x71, 0x6B, 0x27, 0x99, 0xA6, 0xBF, 0xDA, 0x9D, 0xB0, 0x49, 0xA1, 0xB0, 0x25, + 0x9E, 0xC1, 0xBF, 0x71, 0x6B, 0x53, 0x24, 0x98, 0xD7, 0xBF, 0x9D, 0xB0, 0x4F, 0xA1, 0xB0, 0x22, + 0x42, 0x00, 0xF6, 0xF4, 0xBF, 0x21, 0x97, 0x08, 0xCF, 0x25, 0x73, 0x22, 0x4B, 0xDF, 0x25, 0x71, + 0x6C, 0x2F, 0x9C, 0x23, 0xCF, 0x71, 0x6C, 0x79, 0x2E, 0x99, 0x10, 0x3B, 0xCC, 0x00, 0x25, 0x73, + 0x46, 0x4F, 0xC0, 0x95, 0x2C, 0x53, 0xCD, 0x22, 0xEC, 0xB0, 0x6C, 0x98, 0x11, 0x3B, 0xCF, 0x22, + 0xAD, 0x4A, 0x4F, 0xC0, 0x29, 0x9B, 0x54, 0xCC, 0x40, 0x4F, 0xC0, 0x28, 0xDC, 0x99, 0x12, 0x33, + 0xB3, 0x25, 0x73, 0x41, 0x4F, 0xC0, 0x27, 0x94, 0x5A, 0xCA, 0xC9, 0x42, 0x4F, 0xC0, 0x26, 0x92, + 0xCA, 0xC9, 0x43, 0x4F, 0xC0, 0x2B, 0x25, 0x90, 0xCA, 0xC9, 0x44, 0x4F, 0xC0, 0x23, 0xF1, 0xB0, + 0xCC, 0xC5, 0x4C, 0x98, 0xB4, 0x1D, 0xC0, 0x6C, 0x22, 0x38, 0xC0, 0xCC, 0xC7, 0x21, 0xC4, 0xC1, + 0x35, 0x21, 0xDB, 0xCA, 0x21, 0xD6, 0xC1, 0x20, 0x91, 0x29, 0xDA, 0xE8, 0xC0, 0x63, 0x6D, 0x2E, + 0x11, 0xD8, 0x9D, 0xB0, 0xFA, 0xC0, 0x6D, 0x2D, 0xB4, 0xC0, 0x3A, 0x14, 0xDC, 0x21, 0x7E, 0xC0, + 0x6D, 0x2C, 0x93, 0x29, 0xDA, 0xBB, 0xB0, 0x53, 0x6D, 0x2B, 0xFF, 0xCA, 0x87, 0xD1, 0x29, 0x61, + 0xDB, 0x48, 0x5D, 0xD0, 0x49, 0x28, 0x74, 0xD8, 0x97, 0xD4, 0x27, 0x07, 0xC0, 0x14, 0xDC, 0x22, + 0x6F, 0xD1, 0x35, 0x26, 0xFF, 0xCA, 0x22, 0x0C, 0xD0, 0x6D, 0x24, 0x42, 0x02, 0xF4, 0xD6, 0x86, + 0x94, 0x10, 0x6D, 0x23, 0xA5, 0xB0, 0xF2, 0xD8, 0x87, 0xD1, 0x27, 0xDB, 0x22, 0x78, 0x9A, 0xD1, + 0x3A, 0xD9, 0x8C, 0xC5, 0x4D, 0x25, 0x71, 0x6E, 0x52, 0xC1, 0x8A, 0xF2, 0xD8, 0x4E, 0x4F, 0xE0, + 0x2E, 0xD6, 0xB0, 0xF2, 0xD8, 0x1D, 0xC0, 0x6E, 0x55, 0x2D, 0xC9, 0xCA, 0x23, 0xC4, 0xC0, 0x6E, + 0x8B, 0xDB, 0x23, 0xD6, 0xC0, 0x31, 0x6E, 0x9E, 0xD9, 0x98, 0xB4, 0x7E, 0xC0, 0x6E, 0x29, 0x66, + 0xEA, 0x61, 0xE1, 0x09, 0x28, 0x28, 0xDB, 0x0C, 0xD0, 0x6E, 0xDA, 0xCB, 0xB1, 0xE1, 0xEC, 0xCB, + 0xB1, 0xE1, 0x60, 0xFE, 0xCB, 0xB1, 0xE1, 0x10, 0xD9, 0x9D, 0xB0, 0xAF, 0xC0, 0x6E, 0x22, 0xC4, + 0xDB, 0xA6, 0x4E, 0xC0, 0x6E, 0x21, 0xD7, 0xD8, 0xAF, 0xE3, 0x20, 0xC9, 0xCA, 0x21, 0x9F, 0x4C, + 0x25, 0x71, 0x6F, 0x2F, 0x8C, 0xDB, 0x4E, 0xE0, 0x6F, 0x99, 0x2E, 0xEF, 0xDB, 0xD6, 0xC0, 0x6F, + 0x2C, 0x02, 0xEB, 0xE8, 0xC0, 0x6F, 0x99, 0x2B, 0x15, 0xEB, 0xBF, 0xD0, 0x6F, 0x2A, 0x28, 0xDA, + 0x7E, 0xC0, 0x6F, 0x68, 0x4F, 0xB1, 0xF2, 0xD8, 0x57, 0xF1, 0x28, 0xED, 0xED, 0x6F, 0x27, 0xFF, + 0xCA, 0xD5, 0x23, 0x90, 0xF1, 0x25, 0x02, 0xEA, 0x23, 0xFA, 0xC0, 0x6F, 0x24, 0x4A, 0x15, 0xEA, + 0x23, 0xB5, 0xF1, 0x23, 0x28, 0xDA, 0xFF, 0xF2, 0x22, 0xDB, 0xCA, 0xEA, 0xFF, 0xF2, 0x21, 0x4E, + 0xDA, 0x23, 0x4E, 0xC0, 0x61, 0x2F, 0x2F, 0xAC, 0xF2, 0xFB, 0x57, 0xF0, 0x61, 0x2F, 0x65, 0xEB, + 0x23, 0x4E, 0xE0, 0x61, 0x73, 0x2F, 0x2D, 0xBA, 0xFA, 0xFF, 0xF1, 0x61, 0x2F, 0x2C, 0x3E, 0x0B, + 0x6E, 0x61, 0xE0, 0x61, 0x2F, 0x2A, 0xF2, 0xFE, 0x61, 0x2F, 0xB5, 0xEB, 0x5D, 0x24, 0xD6, 0xC0, + 0x61, 0x2F, 0x28, 0xBA, 0xFA, 0x24, 0xBB, 0xB0, 0xD7, 0x61, 0x2F, 0x27, 0x4E, 0xDA, 0x24, 0xBF, + 0xD0, 0x61, 0x2F, 0x9C, 0xF1, 0xFB, 0x56, 0xF1, 0x61, 0x2F, 0x24, 0x66, 0xEB, 0xE8, 0xC0, 0x61, + 0x73, 0x2F, 0x23, 0xCA, 0x0B, 0xEC, 0xB0, 0x61, 0x2F, 0x22, 0xDE, 0x0B, 0x4E, 0xAF, 0xC0, 0x61, + 0x2F, 0x20, 0x02, 0xE8, 0x98, 0xB4, 0x23, 0xD8, 0x01, 0xB3, 0x2E, 0x2F, 0xDB, 0xCA, 0x73, 0xE1, + 0x61, 0x2E, 0x6E, 0xFB, 0x23, 0x62, 0xEC, 0x01, 0x2E, 0x82, 0xC1, 0x7C, 0xE8, 0x28, 0x11, 0x2E, + 0x2B, 0x37, 0xFA, 0x2D, 0x23, 0x3C, 0x11, 0x2E, 0x2A, 0x3B, 0xEF, 0x23, 0x69, 0x12, 0xC3, 0xDB, + 0x92, 0xBD, 0x13, 0x27, 0x18, 0x0B, 0x69, 0x12, 0x26, 0x8C, 0xEB, 0x69, 0x12, 0x24, 0xD4, 0xC0, + 0xB0, 0xC7, 0x1D, 0x23, 0x74, 0xDF, 0x24, 0x14, 0x11, 0x2E, 0x22, 0x5A, 0xED, 0xCA, 0x24, 0x88, + 0x01, 0x2E, 0x20, 0x61, 0xDA, 0x24, 0x0C, 0xD0, 0xD7, 0x61, 0x2D, 0x2F, 0x15, 0xEA, 0x24, 0xA0, + 0xB0, 0x61, 0x2D, 0x38, 0x68, 0xC1, 0xB9, 0x08, 0x7E, 0xC0, 0x61, 0x2D, 0x2D, 0xED, 0xC8, 0x98, + 0xB4, 0x92, 0xD8, 0x01, 0x2D, 0x94, 0xFB, 0xEC, 0x01, 0x2D, 0xA7, 0xFB, 0x28, 0x11, 0x2D, 0x99, + 0x29, 0xDB, 0xCA, 0x3C, 0x11, 0x2D, 0x28, 0x4E, 0xDA, 0x4C, 0x01, 0x2D, 0xF5, 0x26, 0x11, 0xDD, + 0x43, 0x24, 0x31, 0x60, 0xBD, 0x40, 0xD0, 0xFF, 0x75, 0x21, 0xA0, 0x41, 0x60, 0x8F, 0xD2, 0x75, + 0xFF, 0x74, 0x61, 0x2D, 0x23, 0x95, 0xD1, 0x71, 0xA9, 0x7F, 0x71, 0xD0, 0x06, 0x21, 0x20, 0x46, + 0xB3, 0xDF, 0x81, 0xFF, 0x72, 0xC0, 0xA2, 0x41, 0xD1, 0x71, 0xC0, 0xA4, 0xDF, 0x21, 0x20, 0x41, + 0xD0, 0x70, 0x10, 0x30, 0x41, 0x71, 0xFF, 0xE0, 0x71, 0x30, 0x60, 0x8F, 0x71, 0xE0, 0x40, 0xFE, + 0xF4, 0x20, 0xBF, 0x40, 0xD0, 0x72, 0x30, 0xC0, 0xA7, 0xDF, 0x72, 0x30, 0x81, 0x72, 0xE0, 0x1F, + 0x30, 0xA4, 0x40, 0xED, 0xB1, 0x3C, 0xB0, 0x20, 0x41, 0x43, 0x30, 0x20, 0x20, 0x00, 0xFF, 0x0D, + 0x0B, 0x0A, 0x08, 0x02, 0x09, 0x01, 0x05, 0xFF, 0x0C, 0x0E, 0x03, 0x07, 0x04, 0x0F, 0x06, 0x0B, + 0xFF, 0x0C, 0x08, 0x09, 0x0F, 0x04, 0x03, 0x00, 0x0D, 0x7F, 0x01, 0x0A, 0x0E, 0x06, 0x02, 0x07, + 0x05, 0x62, 0x30, 0xFF, 0x0B, 0x06, 0x09, 0x00, 0x01, 0x0A, 0x05, 0x0E, 0xFF, 0x0D, 0x0C, 0x02, + 0x08, 0x07, 0x0F, 0x05, 0x03, 0xFF, 0x0B, 0x04, 0x0A, 0x09, 0x01, 0x0C, 0x06, 0x0E, 0xFF, 0x08, + 0x00, 0x07, 0x02, 0x0D, 0x02, 0x04, 0x0A, 0xFF, 0x0F, 0x0C, 0x01, 0x0E, 0x0B, 0x00, 0x09, 0x03, + 0xFF, 0x08, 0x0D, 0x07, 0x06, 0x05, 0x04, 0x03, 0x05, 0xFB, 0x0A, 0x06, 0x95, 0x30, 0x0E, 0x02, + 0x0C, 0x0F, 0x0D, 0xFF, 0x07, 0x08, 0x01, 0x05, 0x0A, 0x03, 0x02, 0x07, 0xFF, 0x04, 0x0D, 0x0E, + 0x01, 0x08, 0x00, 0x0F, 0x0C, 0xFF, 0x09, 0x06, 0x0B, 0x05, 0x0C, 0x09, 0x03, 0x01, 0xFF, 0x0D, + 0x07, 0x04, 0x06, 0x08, 0x0B, 0x02, 0x0A, 0xFF, 0x00, 0x0F, 0x0E, 0x05, 0x0D, 0x09, 0x01, 0x08, + 0xFF, 0x06, 0x04, 0x03, 0x0A, 0x07, 0x02, 0x0E, 0x00, 0xFF, 0x0B, 0x0C, 0x0F, 0x71, 0x51, 0x25, + 0xFA, 0x31, 0x5F, 0xD1, 0x32, 0xF6, 0x71, 0x54, 0xE0, 0x34, 0x57, 0xE0, 0x34, 0xF5, 0x5A, 0xE0, + 0x34, 0x5D, 0xE0, 0x33, 0x70, 0x72, 0x69, 0x6E, 0xFF, 0x74, 0x66, 0x00, 0x69, 0x63, 0x67, 0x5F, + 0x72, 0xFF, 0x75, 0x6E, 0x5F, 0x69, 0x6E, 0x5F, 0x74, 0x68, 0xFF, 0x72, 0x65, 0x61, 0x64, 0x73, + 0x00, 0x6D, 0x61, 0xFF, 0x6C, 0x6C, 0x6F, 0x63, 0x00, 0x66, 0x72, 0x65, 0xFF, 0x65, 0x00, 0x65, + 0x78, 0x69, 0x74, 0x00, 0x73, 0xFF, 0x6E, 0x69, 0x70, 0x70, 0x65, 0x74, 0x5F, 0x64, 0xFF, 0x65, + 0x69, 0x6E, 0x69, 0x74, 0x69, 0x61, 0x6C, 0xC7, 0x69, 0x7A, 0x65, 0x30, 0x46, 0x3B, 0x4F, 0x17, + 0x44, 0x5F, 0x63, 0x57, 0x6F, 0x75, 0x6E, 0x37, 0x41, 0x63, 0x45, 0x47, 0x64, 0x6D, 0x4A, 0xFF, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x5F, 0xBF, 0x61, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x30, + 0x48, 0x63, 0x7F, 0x72, 0x79, 0x70, 0x74, 0x00, 0x20, 0x20, 0x82, 0x90, 0xFF, 0x71, 0x21, 0x29, + 0x9D, 0x20, 0x21, 0x28, 0x09, 0xFF, 0x20, 0x69, 0x6D, 0x70, 0x6F, 0x72, 0x74, 0x20, 0xB7, 0x66, + 0x75, 0x6E, 0x92, 0x42, 0x20, 0x63, 0x21, 0x40, 0x20, 0xFF, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x0A, + 0x00, 0x60, 0xFF, 0xBB, 0x40, 0xD4, 0x77, 0x30, 0x40, 0xF9, 0x24, 0xEF, 0xA6, 0x40, 0xD0, 0x78, + 0xD7, 0x40, 0x23, 0xA4, 0x74, 0xFF, 0x43, 0xF8, 0xD3, 0x70, 0x43, 0xF8, 0x78, 0x31, 0xFF, 0xFA, + 0xD1, 0x73, 0x77, 0x31, 0xFA, 0xD2, 0x71, 0xFF, 0x30, 0x72, 0x30, 0x76, 0x2E, 0x9B, 0xC0, 0x21, + 0x5F, 0xA0, 0x71, 0x32, 0x72, 0xE2, 0xE8, 0x43, 0x31, 0xEF, 0x41, 0xD7, 0xE1, 0x70, 0x81, 0xDD, + 0x40, 0x70, 0xAE, 0x70, 0x00, 0x74, 0xFD, 0x81, 0xD5, 0x40, 0x74, 0xF9, 0xA2, 0x64, 0x0A, 0xB5, + 0xEE, 0xF4, 0x20, 0xBB, 0x40, 0xD3, 0xD6, 0x42, 0xA1, 0x68, 0x47, 0xF7, 0x21, 0xFB, 0xD4, 0xDC, + 0x43, 0x22, 0xAA, 0x73, 0x43, 0xD7, 0xF8, 0xD2, 0x72, 0xF0, 0x40, 0xD1, 0xE8, 0x43, 0x30, 0x71, + 0xFF, 0x30, 0x76, 0x29, 0x95, 0xC0, 0xA9, 0x74, 0x71, 0x73, 0xE2, 0x76, 0x40, 0x51, 0x0D, 0x57, + 0x0A, 0x73, 0x81, 0x28, 0x50, 0xDF, 0x73, 0xF9, 0xA2, 0x63, 0x04, 0x22, 0x50, 0x71, 0x21, 0xFF, + 0x30, 0x72, 0xE0, 0x71, 0x21, 0x31, 0x72, 0xE1, 0xFF, 0x71, 0x22, 0x34, 0x73, 0xE0, 0x71, 0x22, + 0x35, 0x5B, 0x73, 0xE1, 0x71, 0x50, 0x22, 0x54, 0x71, 0x51, 0x50, 0xF4, 0x20, 0xD5, 0xBC, 0xF8, + 0x20, 0x22, 0xE7, 0x30, 0xD1, 0xD7, 0x40, 0x21, 0xA2, 0xD5, 0x75, 0xE8, 0x40, 0x71, 0x0B, 0x53, + 0x71, 0x12, 0x51, 0x61, 0x0E, 0xDE, 0xF8, 0x21, 0x50, 0x25, 0xFA, 0xD2, 0x37, 0x51, 0xA2, 0x61, + 0x7E, 0xE6, 0xA1, 0xD3, 0x40, 0xD1, 0x75, 0x71, 0x72, 0x0B, 0x50, 0xF5, 0x73, 0xCA, 0x51, 0xE2, + 0xAA, 0x51, 0x43, 0xF2, 0xD1, 0x72, 0xF6, 0xAE, 0x52, 0x06, 0xB4, 0xF4, 0x21, 0x75, 0x76, 0x24, + 0xFB, 0x7F, 0x43, 0x24, 0xF6, 0xC0, 0x22, 0xAE, 0x24, 0xE7, 0xA0, 0xFD, 0x30, 0x9A, 0x50, 0x47, + 0x24, 0xF1, 0xD2, 0x06, 0x75, 0xBF, 0x51, 0xD5, 0x76, 0x51, 0xD6, 0x75, 0xF6, 0x50, 0xD0, 0xF7, + 0x76, 0x30, 0xF4, 0xFB, 0x40, 0x70, 0x71, 0xF4, 0x70, 0x9F, 0x23, 0xF2, 0x24, 0xF6, 0x72, 0xED, + 0x50, 0x01, 0x80, 0x0F, 0xFB, 0x75, 0xF1, 0x07, 0x61, 0xF1, 0xF4, 0x25, 0xFA, 0xC0, 0xFF, 0xAA, + 0x70, 0xA9, 0x75, 0x81, 0xD5, 0x76, 0x81, 0xAF, 0xD6, 0x61, 0x0A, 0xF0, 0x10, 0x30, 0x01, 0x3A, + 0x60, 0x71, 0xFF, 0x28, 0x9D, 0xA6, 0x21, 0x23, 0x29, 0xFA, 0x22, 0xFB, 0xF0, 0x00, 0x4A, 0x31, + 0x20, 0x60, 0xB0, 0x4A, 0xD6, 0xFF, 0x21, 0x72, 0x21, 0x71, 0x28, 0x9A, 0x41, 0xF2, 0xFF, 0xD7, + 0x21, 0x72, 0xD8, 0x10, 0x21, 0x71, 0x23, 0xFF, 0x93, 0x7B, 0x22, 0xC2, 0xC0, 0x21, 0xAB, 0x7B, + 0xFF, 0x60, 0xCE, 0xC0, 0x21, 0xAA, 0x7B, 0x60, 0xCD, 0xFE, 0x6C, 0x60, 0x21, 0x71, 0x2E, 0x91, + 0x23, 0x2E, 0x4D, 0x9F, 0xF0, 0xE0, 0x60, 0x4F, 0x21, 0x8C, 0x71, 0x80, 0x61, 0x60, 0xF5, 0x4E, + 0x85, 0x62, 0x4D, 0x80, 0x61, 0x20, 0x71, 0x21, 0x9D, 0xDF, 0xAE, 0x24, 0xF2, 0x21, 0x32, 0xF6, + 0x51, 0xAB, 0x72, 0xDB, 0x42, 0x9E, 0x8D, 0x61, 0x72, 0xEB, 0xA6, 0x65, 0x75, 0xF6, 0xFB, 0x42, + 0xD2, 0x9A, 0x65, 0xA4, 0x40, 0x45, 0x95, 0xD2, 0xF3, 0x72, 0xC1, 0xB2, 0x61, 0x4B, 0x61, 0xBF, + 0x68, 0xFC, 0xD0, 0xF1, 0x70, 0x30, 0x50, 0x0B, 0x60, 0x49, 0x31, 0x11, 0x00, 0x20, 0x05, 0xBF, + 0x60, 0xBF, 0x73, 0x81, 0xD2, 0x73, 0xEC, 0x50, 0x21, 0xFF, 0xA4, 0x73, 0x25, 0xFA, 0x81, 0xD3, + 0xF1, 0xA7, 0x7E, 0xE7, 0x61, 0xA8, 0x60, 0x02, 0x73, 0x72, 0xFC, 0x43, 0x30, 0xFD, 0x24, 0x82, + 0x90, 0xD4, 0x03, 0x73, 0x51, 0xD3, 0x73, 0xB6, 0x06, 0x61, 0x74, 0x30, 0x12, 0x63, 0x74, 0x31, + 0x19, 0x62, 0x61, 0xF7, 0x07, 0x70, 0x2F, 0x29, 0x90, 0x21, 0xAE, 0x70, 0x2F, 0xF7, 0x2F, 0x20, + 0x40, 0xE9, 0x60, 0xA0, 0x70, 0x74, 0x32, 0xEF, 0x24, 0xF6, 0xA5, 0x80, 0x07, 0x70, 0x0D, 0x73, + 0x83, 0xFF, 0xD3, 0x09, 0x80, 0x73, 0x82, 0xD3, 0x04, 0x80, 0xE8, 0x66, 0x50, 0xFB, 0x63, 0x3A, + 0x61, 0x80, 0x52, 0x70, 0x00, 0x00, 0xFF, 0xAF, 0x00, 0x71, 0x22, 0x56, 0x49, 0x62, 0x00, 0x62, + 0x70, 0xD0, 0xA9, 0x13, 0x62, 0x71, 0x68, 0x73, 0xD7, 0x67, 0x78, 0xEA, 0x67, 0x78, 0xF1, 0xAA, + 0x67, 0x78, 0xF6, 0x67, 0x78, 0x05, 0x62, 0x70, 0x04, 0x62, 0x70, 0xFB, 0x7E, 0x67, 0x74, 0xFB, + 0x12, 0x00, 0x00, 0x10, 0x14, 0x68, 0x73, 0xA5, 0xEF, 0xB3, 0x70, 0x23, 0xB7, 0x74, 0x62, 0x71, + 0x3C, 0xB7, 0x74, 0x03, 0xAA, 0x62, 0x70, 0x4B, 0xB7, 0x74, 0x1A, 0x62, 0x70, 0x62, 0xB7, 0x74, + 0xC0, 0xAA, 0xB3, 0x70, 0x06, 0x62, 0x70, 0x48, 0x9B, 0x78, 0x11, 0x62, 0x70, 0x1D, 0xAA, 0x62, + 0x70, 0x29, 0x62, 0x70, 0x35, 0x62, 0x70, 0x44, 0x62, 0x70, 0x49, 0xAA, 0x62, 0x70, 0x51, 0x62, + 0x70, 0x55, 0x62, 0x70, 0x5D, 0x62, 0x70, 0x61, 0xAA, 0x62, 0x70, 0x69, 0x62, 0x70, 0x6D, 0x62, + 0x70, 0x75, 0x62, 0x70, 0x79, 0xAA, 0x62, 0x70, 0x81, 0x62, 0x70, 0x85, 0x62, 0x70, 0x8D, 0x62, + 0x70, 0x94, 0xFE, 0x1F, 0x81, 0x15, 0x00, 0x00, 0x2E, 0x2E, 0x2F, 0x67, 0xFF, 0x65, 0x6E, 0x65, + 0x72, 0x61, 0x74, 0x6F, 0x72, 0x7D, 0x2F, 0x6E, 0x40, 0x2E, 0x64, 0x62, 0x67, 0x00 +}; diff --git a/reader-dre-common.h b/reader-dre-common.h new file mode 100644 index 0000000..0c80165 --- /dev/null +++ b/reader-dre-common.h @@ -0,0 +1,11 @@ +#ifndef DRE_COMMON_H_ +#define DRE_COMMON_H_ + + int32_t dre_common_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr); + int32_t dre_common_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count); + + uint8_t Drecrypt2OverCW(uint16_t overcryptId, uint8_t *cw); + void Drecrypt2OverEMM(uint8_t *emm); + void ReasmEMM82(uint8_t *emm); + +#endif diff --git a/reader-dre-st20.c b/reader-dre-st20.c new file mode 100644 index 0000000..e017062 --- /dev/null +++ b/reader-dre-st20.c @@ -0,0 +1,467 @@ +#include "globals.h" +#include "reader-dre-st20.h" + +#define IPTR 0 +#define WPTR 1 +#define AREG 2 +#define BREG 3 +#define CREG 4 + +#define FLASHS 0x7FE00000 +#define FLASHE 0x7FFFFFFF +#define RAMS 0x40000000 +#define RAME 0x401FFFFF +#define IRAMS 0x80000000 +#define IRAME 0x800017FF + +#define ERR_ILL_OP -1 +#define ERR_CNT -2 +#define FLA_ERR -3 +#define RAM_ERR -4 + +// ---------------------------------------------------------------- + +#define STACKMAX 16 +#define STACKMASK (STACKMAX-1) + +typedef struct +{ + uint32_t Iptr, Wptr; + uint8_t *flash, *ram; + uint32_t flashSize, ramSize; + int sptr, stack[STACKMAX]; + uint8_t iram[0x1800]; + int invalid; +} st20_context_t; + +static bool st20_set_flash(st20_context_t *ctx, uint8_t *m, uint32_t len); +static bool st20_set_ram(st20_context_t *ctx, uint8_t *m, uint32_t len); +static void st20_init(st20_context_t *ctx, uint32_t IPtr, uint32_t WPtr); +static void st20_free(st20_context_t *ctx); + +static void st20_set_call_frame(st20_context_t *ctx, uint32_t raddr, int p1, int p2, int p3); + +static uint32_t st20_get_reg(st20_context_t *ctx, int reg); +static void st20_set_reg(st20_context_t *ctx, int reg, uint32_t val); +static uint8_t st20_rbyte(st20_context_t *ctx, uint32_t off); +static void st20_wbyte(st20_context_t *ctx, uint32_t off, uint8_t val); +static uint32_t st20_rword(st20_context_t *ctx, uint32_t off); +static void st20_wword(st20_context_t *ctx, uint32_t off, uint32_t val); + +#define INVALID_VALUE 0xCC +#define ERRORVAL 0xDEADBEEF + +#define MININT 0x7FFFFFFF +#define MOSTPOS 0x7FFFFFFF +#define MOSTNEG 0x80000000 + +#define POP() ctx->stack[(ctx->sptr++)&STACKMASK] +#define PUSH(v) do { int32_t __v=(v); ctx->stack[(--ctx->sptr)&STACKMASK]=__v; } while(0) +#define DROP(n) ctx->sptr+=n + +#define AAA ctx->stack[ctx->sptr&STACKMASK] +#define BBB ctx->stack[(ctx->sptr+1)&STACKMASK] +#define CCC ctx->stack[(ctx->sptr+2)&STACKMASK] + +#define GET_OP() operand|=op1&0x0F +#define CLEAR_OP() operand=0 +#define JUMP(x) ctx->Iptr+=(x) +#define POP64() ({ uint32_t __b=POP(); ((uint64_t)POP()<<32)|__b; }) +#define PUSHPOP(op,val) do { int32_t __a=val; AAA op##= (__a); } while(0) + +#define RB(off) st20_rbyte(ctx, off) +#define RW(off) st20_rword(ctx, off) +#define WW(off,val) st20_wword(ctx, off, val) + +static uint32_t st20_get_reg(st20_context_t *ctx, int32_t reg) +{ + switch(reg) + { + case IPTR: return ctx->Iptr; + case WPTR: return ctx->Wptr; + case AREG: return AAA; + case BREG: return BBB; + case CREG: return CCC; + } + return 0; +} + +static void st20_set_reg(st20_context_t *ctx, int32_t reg, uint32_t val) +{ + switch(reg) + { + case IPTR: ctx->Iptr = val; return; + case WPTR: ctx->Wptr = val; return; + case AREG: AAA=val; return; + case BREG: BBB=val; return; + case CREG: CCC=val; return; + } +} + +static uint8_t *st20_addr(st20_context_t *ctx, uint32_t off) +{ + if(off >= FLASHS && off <= FLASHE) + { + return &ctx->flash[off - FLASHS]; + } + else if(off >= RAMS && off <= RAME) + { + return &ctx->ram[off - RAMS]; + } + else if(off >= IRAMS && off <= IRAME) + { + return &ctx->iram[off - IRAMS]; + } + + ctx->invalid = ERRORVAL; + return (uint8_t *) &ctx->invalid; +} + +static uint32_t st20_rword(st20_context_t *ctx, uint32_t off) +{ + uint8_t *temp; + temp = st20_addr(ctx, off); + + return ((temp[3] << 24) | (temp[2] << 16) | (temp[1] << 8) | temp[0]); +} + +static uint16_t st20_rshort(st20_context_t *ctx, uint32_t off) +{ + uint8_t *temp; + temp = st20_addr(ctx, off); + + return ((temp[0] << 8) | temp[1]); +} + +static uint8_t st20_rbyte(st20_context_t *ctx, uint32_t off) +{ + return *st20_addr(ctx, off); +} + +static void st20_wword(st20_context_t *ctx, uint32_t off, uint32_t val) +{ + uint8_t *temp; + temp = st20_addr(ctx, off); + temp[3] = (val >> 24) & 0xFF; + temp[2] = (val >> 16) & 0xFF; + temp[1] = (val >> 8) & 0xFF; + temp[0] = val & 0xFF; +} + +static void st20_wbyte(st20_context_t *ctx, uint32_t off, uint8_t val) +{ + uint8_t *temp; + temp = st20_addr(ctx, off); + temp[0] = val; +} + +static int32_t st20_decode(st20_context_t *ctx, int32_t count) +{ + int32_t operand = 0; + CLEAR_OP(); + + while(ctx->Iptr != 0) + { + int32_t a, op1 = RB(ctx->Iptr++); + GET_OP(); + + switch(op1 >> 4) + { + case 0x0: // j / jump + JUMP(operand); + CLEAR_OP(); + break; + + case 0x1: // ldlp + PUSH(ctx->Wptr + (operand * 4)); + CLEAR_OP(); + break; + + case 0x2: // positive prefix + operand <<= 4; + break; + + case 0x3: // ldnl + AAA=RW(AAA + (operand * 4)); + CLEAR_OP(); + break; + + case 0x4: // ldc + PUSH(operand); + CLEAR_OP(); + break; + + case 0x5: // ldnlp + PUSHPOP(+, operand * 4); + CLEAR_OP(); + break; + + case 0x6: // negative prefix + operand = (~operand) << 4; + break; + + case 0x7: // ldl + PUSH(RW(ctx->Wptr + (operand * 4))); + CLEAR_OP(); + break; + + case 0x8: // adc + PUSHPOP(+, operand); + CLEAR_OP(); + break; + + case 0x9: // call + ctx->Wptr -= 16; + WW(ctx->Wptr, ctx->Iptr); WW(ctx->Wptr + 4, POP()); WW(ctx->Wptr + 8, POP()); WW(ctx->Wptr + 12, POP()); + PUSH(ctx->Iptr); + JUMP(operand); + CLEAR_OP(); + break; + + case 0xA: // cj / conditional jump + if(AAA) { DROP(1); } else { JUMP(operand); } + CLEAR_OP(); + break; + + case 0xB: // ajw / adjust workspace + ctx->Wptr += operand * 4; + CLEAR_OP(); + break; + + case 0xC: // eqc / equals constant + AAA = (operand == AAA ? 1 : 0); + CLEAR_OP(); + break; + + case 0xD: // stl + WW(ctx->Wptr + (operand * 4), POP()); + CLEAR_OP(); + break; + + case 0xE: // stnl + a = POP(); WW(a + (operand * 4), POP()); + CLEAR_OP(); + break; + + case 0xF: // opr (secondary ins) + switch(operand) + { + case 0x00: a = AAA; AAA = BBB; BBB = a; break; + case 0x01: AAA = RB(AAA); break; + case 0x02: PUSHPOP(+, POP()); break; + case 0x04: PUSHPOP(-, POP()); break; + case 0x05: PUSHPOP(+, POP()); break; + case 0x06: a = AAA; AAA = ctx->Iptr; ctx->Iptr = a; break; + case 0x08: PUSHPOP(*, POP()); break; + case 0x09: a=POP(); AAA = (AAA > a); break; + case 0x0A: a=POP(); AAA = a + (AAA * 4); break; + case 0x0C: PUSHPOP(-, POP()); break; + case 0x1A: { a = POP(); uint64_t ll = POP64(); PUSH(ll % (uint32_t)a); PUSH(ll / (uint32_t)a); } break; + case 0x1B: PUSHPOP(+, ctx->Iptr); break; + case 0x1D: CCC = BBB; BBB = (AAA >= 0 ? 0 : -1); break; + case 0x1F: PUSHPOP(%, POP()); break; + case 0x20: ctx->Iptr = RW(ctx->Wptr); ctx->Wptr = ctx->Wptr + 16; break; + case 0x2C: PUSHPOP(/, POP()); break; + case 0x30: break; + case 0x32: AAA =~ AAA; break; + case 0x33: PUSHPOP(^, POP()); break; + case 0x34: PUSHPOP(*, 4); break; + case 0x35: { a = POP(); uint64_t ll = POP64() >> a; PUSH((ll >> 32) & 0xFFFFFFFF); PUSH(ll & 0xFFFFFFFF); } break; + case 0x36: { a = POP(); uint64_t ll = POP64() << a; PUSH((ll >> 32) & 0xFFFFFFFF); PUSH(ll & 0xFFFFFFFF); } break; + case 0x3B: a = POP(); st20_wbyte(ctx, a, POP()); break; + case 0x3F: a = POP(); PUSH(a & 3); PUSH((uint32_t)a >> 2); break; + case 0x40: a = POP(); AAA = (uint32_t)AAA >> a; break; + case 0x41: a = POP(); AAA = (uint32_t)AAA << a; break; + case 0x42: PUSH(MOSTNEG); break; + case 0x46: PUSHPOP(&, POP()); break; + case 0x4A: { a = POP(); int32_t b = POP(); int32_t c = POP(); while(a--) st20_wbyte(ctx, b++, st20_rbyte(ctx, c++)); } break; + case 0x4B: PUSHPOP(|, POP()); break; + case 0x53: PUSHPOP(*, POP()); break; + case 0x5A: PUSH(AAA); break; + case 0x5F: a = POP(); AAA = ((uint32_t)AAA > (uint32_t)a); break; + case 0x78: { a = POP(); int32_t b = POP(); int32_t bb = 0; while(a--){bb <<= 1; bb |= b & 1; b >>= 1;} PUSH(bb);} break; + case 0xCA: AAA = st20_rshort(ctx, AAA); break; + default: + cs_log("[icg] unknown opcode %X", operand); + return ERR_ILL_OP; + } + CLEAR_OP(); + break; + } + + if(--count <= 0 && operand == 0) + { + return ERR_CNT; + } + } + return 0; +} + +static bool st20_set_flash(st20_context_t *ctx, uint8_t *m, uint32_t len) +{ + if (len) + { + ctx->flash = (uint8_t *)malloc(len); + ctx->flashSize = len; + } + else + { + cs_log("ERROR len!"); + ctx->flashSize = 0; + return false; + } + + if (ctx->flash == NULL) + { + cs_log("ERROR, malloc!"); + ctx->flashSize = 0; + return false; + } + else + { + if(m == NULL) + { + memset(ctx->flash, 0, len); + } + else + { + memcpy(ctx->flash, m, len); + } + } + + return true; +} + +static bool st20_set_ram(st20_context_t *ctx, uint8_t *m, uint32_t len) +{ + if (len) + { + ctx->ram = (uint8_t *)malloc(len); + ctx->ramSize = len; + } + else + { + cs_log("ERROR len!"); + ctx->ramSize = 0; + return false; + } + + if (ctx->ram == NULL) + { + cs_log("ERROR, malloc!"); + ctx->ramSize = 0; + return false; + } + else + { + if(m == NULL) + { + memset(ctx->ram, 0, len); + } + else + { + memcpy(ctx->ram, m, len); + } + } + + return true; +} + +static void st20_init(st20_context_t *ctx, uint32_t IPtr, uint32_t WPtr) +{ + ctx->Wptr = WPtr; + ctx->Iptr = IPtr; + memset(ctx->stack, INVALID_VALUE, sizeof(ctx->stack)); + ctx->sptr = STACKMAX - 3; + memset(ctx->iram, 0, sizeof(ctx->iram)); +} + +static void st20_free(st20_context_t *ctx) +{ + if(ctx->flashSize) + { + free(ctx->flash); + ctx->flashSize = 0; + } + ctx->flash = NULL; + + if(ctx->ramSize) + { + free(ctx->ram); + ctx->ramSize = 0; + } + ctx->ram = NULL; +} + +static void st20_set_call_frame(st20_context_t *ctx, uint32_t raddr, int32_t p1, int32_t p2, int32_t p3) +{ + ctx->Wptr -= 16; + st20_wword(ctx, ctx->Wptr, raddr); // RET + st20_wword(ctx, ctx->Wptr + 4, p1); // Areg + st20_wword(ctx, ctx->Wptr + 8, p1); // Breg + st20_wword(ctx, ctx->Wptr + 12, p1); // Creg + st20_wword(ctx, ctx->Wptr + 16, p2); + st20_wword(ctx, ctx->Wptr + 20, p3); + st20_set_reg(ctx, AREG, raddr); // RET +} + +int st20_run(uint8_t* snip, uint32_t snip_len, int addr, uint8_t *data, uint16_t overcryptId) +{ + int error = 0, i, n; + st20_context_t ctx; + + cs_log("[icg] decrypt address = 0x%X, id = %04X", addr, overcryptId); + + cs_log("[icg] CW: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X ", + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15]); + + for(n = 0; n < 2; n++) + { + memset(&ctx, 0, sizeof(st20_context_t)); + if (!st20_set_ram(&ctx, 0, 0x1000)) + { + error = RAM_ERR; + break; + } + if (!st20_set_flash(&ctx, snip + 0x48, (int) (snip_len - 0x48))) + { + error = FLA_ERR; + break; + } + st20_init(&ctx, FLASHS + addr, RAMS + 0x100); + st20_set_call_frame(&ctx, 0, RAMS, RAMS, RAMS); + + for(i = 0; i < 8; i++) + { + st20_wbyte(&ctx, RAMS + i, data[i + n * 8]); + } + + if((error = st20_decode(&ctx, 800000)) < 0) + { + break; + } + + cs_log("[icg] cw%d ret = %d, AREG = %X", n + 1, error, st20_get_reg(&ctx, AREG)); + + for(i = 0; i < 8; i++) + { + data[i + n * 8] = st20_rbyte(&ctx, RAMS + i); + } + st20_free(&ctx); + } + + if(error < 0) + { + //in error case ensure free ctx! + st20_free(&ctx); + + cs_log("[icg] st20 processing failed with error %d", error); + return 0; + } + + cs_log("[icg] DW: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X ", + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15]); + return 1; +} diff --git a/reader-dre-st20.h b/reader-dre-st20.h new file mode 100644 index 0000000..a48e977 --- /dev/null +++ b/reader-dre-st20.h @@ -0,0 +1,6 @@ +#ifndef ST20_H_ +#define ST20_H_ + +int st20_run(uint8_t* snip, uint32_t snip_len, int addr, uint8_t *data, uint16_t overcryptId); + +#endif diff --git a/reader-dre.c b/reader-dre.c new file mode 100644 index 0000000..2d1b074 --- /dev/null +++ b/reader-dre.c @@ -0,0 +1,1269 @@ +#include "globals.h" +#ifdef READER_DRE +#include "cscrypt/des.h" +#include "reader-common.h" +#include "reader-dre-common.h" + +struct dre_data +{ + uint8_t provider; +}; + +#define OK_RESPONSE 0x61 +#define CMD_BYTE 0x59 + +static uint8_t xor(const uint8_t *cmd, int32_t cmdlen) +{ + int32_t i; + uint8_t checksum = 0x00; + for(i = 0; i < cmdlen; i++) + { checksum ^= cmd[i]; } + return checksum; +} + +static int8_t isValidDCW(uint8_t *dw) +{ + if (((dw[0] + dw[1] + dw[2]) & 0xFF) != dw[3]) + { + return 0; + } + if (((dw[4] + dw[5] + dw[6]) & 0xFF) != dw[7]) + { + return 0; + } + if (((dw[8] + dw[9] + dw[10]) & 0xFF) != dw[11]) + { + return 0; + } + if (((dw[12] + dw[13] + dw[14]) & 0xFF) != dw[15]) + { + return 0; + } + return 1; +} + +static int32_t dre_command(struct s_reader *reader, const uint8_t *cmd, int32_t cmdlen, uint8_t *cta_res, + uint16_t *p_cta_lr, uint8_t crypted, uint8_t keynum, uint8_t dre_v, uint8_t cmd_type) +{ + // attention: inputcommand will be changed!!!! + //answer will be in cta_res, length cta_lr ; returning 1 = no error, return ERROR = err + + uint8_t startcmd[] = { 0x80, 0xFF, 0x10, 0x01, 0x05 }; // any command starts with this, + // last byte is nr of bytes of the command that will be sent + // after the startcmd + // response on startcmd+cmd: = { 0x61, 0x05 } // 0x61 = "OK", last byte is nr. of bytes card will send + uint8_t reqans[] = { 0x00, 0xC0, 0x00, 0x00, 0x08 }; // after command answer has to be requested, + // last byte must be nr. of bytes that card has reported to send + uint8_t command[256]; + uint8_t checksum; + char tmp[256]; + int32_t headerlen = sizeof(startcmd); + + if(dre_v > 0) + { + startcmd[1] = 0; + startcmd[2] = crypted; + startcmd[3] = keynum; + } + + startcmd[4] = cmdlen + 3 - cmd_type; // commandlength + type + len + checksum bytes + memcpy(command, startcmd, headerlen); + command[headerlen++] = cmd_type ? 0x86 : CMD_BYTE; // type + command[headerlen++] = cmdlen + (cmd_type == 1 ? 0 : 1); // len = command + 1 checksum byte + memcpy(command + headerlen, cmd, cmdlen); + + if(!cmd_type) + { + checksum = ~xor(cmd, cmdlen); + //rdr_log_dbg(reader, D_READER, "Checksum: %02x", checksum); + cmdlen += headerlen; + command[cmdlen++] = checksum; + } + else cmdlen += headerlen; + + reader_cmd2icc(reader, command, cmdlen, cta_res, p_cta_lr); + + if((*p_cta_lr != 2) || (cta_res[0] != OK_RESPONSE)) + { + rdr_log(reader, "command sent to card: %s", cs_hexdump(0, command, cmdlen, tmp, sizeof(tmp))); + rdr_log(reader, "unexpected answer from card: %s", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + return ERROR; // error + } + + rdr_log_dbg(reader, D_READER, "command sent to card: %s", cs_hexdump(0, command, cmdlen, tmp, sizeof(tmp))); + rdr_log_dbg(reader, D_READER, "answer from card: %s", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + + reqans[4] = cta_res[1]; // adapt length byte + reader_cmd2icc(reader, reqans, 5, cta_res, p_cta_lr); + + if(cta_res[0] != CMD_BYTE) + { + rdr_log(reader, "unknown response: cta_res[0] expected to be %02x, is %02x", CMD_BYTE, cta_res[0]); + return ERROR; + } + + if((cta_res[1] == 0x03) && (cta_res[2] == 0xe2)) + { + switch(cta_res[3 + dre_v]) + { + case 0xe1: + rdr_log(reader, "checksum error: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe2: + rdr_log(reader, "wrong cmd len: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe3: + rdr_log(reader, "illegal command: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe4: + rdr_log(reader, "wrong adress type: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe5: + rdr_log(reader, "wrong CMD param: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe6: + rdr_log(reader, "wrong UA: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe7: + rdr_log(reader, "wrong group: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xe8: + rdr_log(reader, "wrong key num: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xeb: + rdr_log(reader, "No key or subscribe: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xec: + rdr_log(reader, "wrong signature: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xed: + rdr_log(reader, "wrong provider: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + case 0xef: + rdr_log(reader, "wrong GEO code: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + + default: + rdr_log_dbg(reader, D_READER, "unknown error: %s.", cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + break; + } + return ERROR; // error + } + + int32_t length_excl_leader = *p_cta_lr; + + if((cta_res[*p_cta_lr - 2] == 0x90) && (cta_res[*p_cta_lr - 1] == 0x00)) + { length_excl_leader -= 2; } + + checksum = ~xor(cta_res + 2, length_excl_leader - 3); + + if(cta_res[length_excl_leader - 1] != checksum) + { + rdr_log(reader, "checksum does not match, expected %02x received %02x:%s", checksum, + cta_res[length_excl_leader - 1], cs_hexdump(0, cta_res, *p_cta_lr, tmp, sizeof(tmp))); + return ERROR; // error + } + return OK; +} + +#define dre_script_nb(cmd, len, cmd_type, crypted, keynum) \ + dre_command(reader, cmd, len, cta_res, &cta_lr, crypted, keynum, crypted, cmd_type); \ + +#define dre_script(cmd, len, cmd_type, crypted, keynum) \ + { \ + dre_script_nb(cmd, len, cmd_type, crypted, keynum) \ + } + +#define dre_cmd_nb(cmd) \ + dre_command(reader, cmd, sizeof(cmd), cta_res, &cta_lr, 0, 0, 0, 0); \ + +#define dre_cmd(cmd) \ + { \ + dre_cmd_nb(cmd) \ + } + +#define dre_cmd_c_nb(cmd,crypted,keynum) \ + dre_command(reader, cmd, sizeof(cmd),cta_res,&cta_lr, crypted, keynum, 1, 0); \ + +#define dre_cmd_c(cmd,crypted,keynum) \ + { \ + dre_cmd_c_nb(cmd,crypted,keynum) \ + } + +static int32_t dre_set_provider_info(struct s_reader *reader) +{ + def_resp; + int32_t i; + int subscr_cmd_len = 4; + uint8_t subscr[4]; // = { 0x59, 0x14 }; // subscriptions + uint8_t dates[] = { 0x5b, 0x00, 0x14 }; // validity dates + uint8_t subscr_len = 0, n = 0; + struct dre_data *csystem_data = reader->csystem_data; + + cs_clear_entitlement(reader); + + switch(csystem_data->provider) + { + case 0x02: + case 0x03: + subscr[0] = 0x84; + subscr[1] = 0; + subscr[2] = 0x5F; + subscr[3] = csystem_data->provider; + dates[0] = 0x85; + subscr_len = 0x5F; + break; + + case 0x18: + case 0x19: + case 0x1A: + subscr[0] = 0x94; + subscr[1] = 0; + subscr[2] = 0x5F; + subscr[3] = csystem_data->provider; + dates[0] = 0x95; + subscr_len = 0x5F; + break; + + default: + subscr[0] = 0x59; + subscr[1] = csystem_data->provider; + subscr_len = 0x20; + subscr_cmd_len = 2; + } + +chk_subscr: + + if(({dre_script_nb(subscr, subscr_cmd_len, 0, 0, 0)})) // ask subscription packages, returns error on 0x11 card + { + uint8_t pbm[subscr_len]; + char tmp_dbg[subscr_len*2+1]; + memcpy(pbm, cta_res + 3, cta_lr - 6); + rdr_log_dbg(reader, D_READER, "pbm: %s", cs_hexdump(0, pbm, subscr_len, tmp_dbg, sizeof(tmp_dbg))); + + for(i = 0; i < subscr_len; i++) + { + if(pbm[i] != 0xff) + { + dates[1] = i; + dates[2] = csystem_data->provider; + dre_cmd(dates); // ask for validity dates + + time_t start; + time_t end; + start = (cta_res[3] << 24) | (cta_res[4] << 16) | (cta_res[5] << 8) | cta_res[6]; + end = (cta_res[7] << 24) | (cta_res[8] << 16) | (cta_res[9] << 8) | cta_res[10]; + + struct tm temp; + + localtime_r(&start, &temp); + int32_t startyear = temp.tm_year + 1900; + int32_t startmonth = temp.tm_mon + 1; + int32_t startday = temp.tm_mday; + localtime_r(&end, &temp); + int32_t endyear = temp.tm_year + 1900; + int32_t endmonth = temp.tm_mon + 1; + int32_t endday = temp.tm_mday; + rdr_log(reader, "active package %i valid from %04i/%02i/%02i to %04i/%02i/%02i", + i + n, startyear, startmonth, startday, endyear, endmonth, endday); + cs_add_entitlement(reader, reader->caid, b2ll(4, reader->prid[0]), 0, i + n, start, end, 5, 1); + } + } + } + + if(subscr_len == 0x5F) // read second part subscription packages, for DRE3 and DRE4 + { + subscr[1] = 0x5F; + subscr[2] = 0x21; + subscr_len = 0x21; + n = 0x5F; + goto chk_subscr; + } + + return OK; +} + +static void dre_read_ee(struct s_reader *reader, const char *path, uint8_t provid) +{ + def_resp; + int i, n; + uint8_t *ee = malloc(2048); + if(ee == NULL) return; + + uint8_t drecmd43[] = { 0x80, 0x00, 0x00, 0x00, 0x05, 0x59, 0x03, 0x43, 0x11, 0xAD }; + uint8_t drecmd45[] = { 0x45, 0x11 }; + + drecmd43[8] = drecmd45[1] = provid; + drecmd43[9] = ~xor(&drecmd43[7], 2); + + for(i = 0; i < 8; i++) + { + for(n = 0; n < 8; n++) + { + reader_cmd2icc(reader, drecmd43, 10, cta_res, &cta_lr); + + dre_cmd_c(drecmd45, n, i * 32); + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { + free(ee); + rdr_log(reader, "ERROR read ee.bin from card"); + return; + } + + memcpy(&ee[((n * 8) + i) * 32] ,&cta_res[2] ,32); + + } + } + + FILE *pFile = fopen(path, "wb"); + + if(pFile == NULL) + { + free(ee); + return ; + } + + fwrite(ee, 2048, 1, pFile); + fclose(pFile); + free(ee); + rdr_log(reader, "ee.bin saved to %s", path); +} +/* +static void cmd_test(struct s_reader *reader) +{ + def_resp; + int i; + uint8_t drecmd[] = { 0x00, 0x02 }; + char tmp[64]; + + for(i = 0; i <= 0xFF; i++) + { + if(i == 0x45) continue; + drecmd[0] = i; + dre_cmd(drecmd); + if(cta_res[2] == 0xE2) + { + if(cta_res[3] != 0xE3) rdr_log(reader, "cmd %02X error %02X",i ,cta_res[3]); + } + else + { + rdr_log(reader, "cmd %02X answer %s",i ,cs_hexdump(0, cta_res, cta_res[1]+2, tmp, sizeof(tmp))); + } + } + + uint8_t drecmd[64]; + + //memset(drecmd, 0, 64); + //drecmd[0] = 0x71; + for(i = 2; i <= 64; i++) + { + memset(drecmd, 0, 64); + drecmd[i-1] = 0x02; + drecmd[0] = 0x71; + + dre_script(drecmd, i, 0, 0, 0); + + if(cta_res[2] == 0xE2) + { + if((cta_res[3] != 0xE2) & (cta_res[3] != 0xED)) rdr_log(reader, "Len %02X error %02X",i ,cta_res[3]); + if((cta_res[3] & 0xF0) != 0xE0) rdr_log(reader, "Len %02X answer %s",i ,cs_hexdump(0, cta_res, cta_res[1]+2, tmp, sizeof(tmp))); + } + else + { + rdr_log(reader, "Len %02X answer %s",i ,cs_hexdump(0, cta_res, cta_res[1]+2, tmp, sizeof(tmp))); + } + } +} +*/ +static int32_t dre_card_init(struct s_reader *reader, ATR *newatr) +{ + get_atr; + def_resp; + uint8_t ua[] = { 0x43, 0x15 }; // get serial number (UA) + uint8_t providers[] = { 0x49, 0x15 }; // get providers + uint8_t cmd56[] = { 0x56, 0x00 }; + int32_t i; + char *card; + char tmp[9]; + + if((atr[0] != 0x3b) || (atr[1] != 0x15) || (atr[2] != 0x11) || (atr[3] != 0x12) || ( + ((atr[4] != 0x01) || (atr[5] != 0x01)) && + ((atr[4] != 0xca) || (atr[5] != 0x07)) && + ((atr[4] != 0xcb) || (atr[5] != 0x07)) && + ((atr[4] != 0xcc) || (atr[5] != 0x07)) && + ((atr[4] != 0xcd) || (atr[5] != 0x07)) + )) + { return ERROR; } + + if(!cs_malloc(&reader->csystem_data, sizeof(struct dre_data))) + { return ERROR; } + struct dre_data *csystem_data = reader->csystem_data; + + csystem_data->provider = atr[6]; + uint8_t checksum = xor(atr + 1, 6); + + if(checksum != atr[7]) + { rdr_log(reader, "warning: expected ATR checksum %02x, smartcard reports %02x", checksum, atr[7]); } + + switch(atr[6]) + { + case 0: + if(!({dre_cmd_nb(cmd56)})) { return ERROR; } + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) { return ERROR; } + + switch(cta_res[4]) + { + case 0x02: + card = "Tricolor Centr DRE3"; + reader->caid = 0x4ae1; + break; + + case 0x03: + card = "Tricolor Syberia DRE3"; + reader->caid = 0x4ae1; + break; + + case 0x18: + case 0x19: + card = "Tricolor Centr DRE4"; + reader->caid = 0x2710; + break; + + case 0x1A: + card = "Tricolor Syberia DRE4"; + reader->caid = 0x2710; + break; + + default: + return ERROR; + } + csystem_data->provider = cta_res[4]; + providers[0] = 0x83; + break; + + case 0x11: + card = "Tricolor Centr DRE2"; + reader->caid = 0x4ae1; + break; // 59 type card = MSP (74 type = ATMEL) + + case 0x12: + card = "Cable TV"; + reader->caid = 0x4ae1; // TODO not sure about this one + break; + + case 0x14: + card = "Tricolor Syberia DRE2"; + reader->caid = 0x4ae1; + break; // 59 type card + + case 0x15: + card = "Platforma HD / DW old"; + reader->caid = 0x4ae1; + break; // 59 type card + + default: + return ERROR; + } + + memset(reader->prid, 0x00, 8); + + if(atr[6] > 0) + { + reader->prid[0][3] = atr[6]; + } + else + { + reader->prid[0][3] = csystem_data->provider; + } + + uint8_t cmd54[] = { 0x54, 0x14 }; // geocode + cmd54[1] = csystem_data->provider; + uint8_t geocode = 0; + + if(({dre_cmd_nb(cmd54)})) // error would not be fatal, like on 0x11 cards + { geocode = cta_res[3]; } + + providers[1] = csystem_data->provider; + if(!({dre_cmd_nb(providers)})) + { return ERROR; } // fatal error + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } + + uint8_t provname[128]; + + for(i = 0; ((i < cta_res[2] - 6) && (i < 128)); i++) + { + provname[i] = cta_res[6 + i]; + if(provname[i] == 0x00) + { break; } + } + + int32_t major_version = cta_res[3]; + int32_t minor_version = cta_res[4]; + + ua[1] = csystem_data->provider; + dre_cmd(ua); // error would not be fatal + + int32_t hexlength = cta_res[1] - 2; // discard first and last byte, last byte is always checksum, first is answer code + + if(reader->force_ua) + { + rdr_log(reader, "WARNING!!! used UA from force_ua %08X", reader->force_ua); + memcpy(cta_res + 3, &reader->force_ua, 4); + } + + reader->hexserial[0] = 0; + reader->hexserial[1] = 0; + memcpy(reader->hexserial + 2, cta_res + 3, hexlength); + + int32_t low_dre_id, dre_chksum; + uint8_t buf[32]; + + if(major_version < 0x3) + { + low_dre_id = ((cta_res[4] << 16) | (cta_res[5] << 8) | cta_res[6]) - 48608; + dre_chksum = 0; + snprintf((char *)buf, sizeof(buf), "%i%i%08i", csystem_data->provider - 16, major_version + 1, low_dre_id); + + for(i = 0; i < 32; i++) + { + if(buf[i] == 0x00) + { break; } + dre_chksum += buf[i] - 48; + } + + if(major_version < 2) + { + reader->caid = 0x4ae0; + card = csystem_data->provider == 0x11 ? "Tricolor Centr DRE1" : "Tricolor Syberia DRE1"; + } + + rdr_log(reader, "type: DRE Crypt, caid: %04X, serial: {%s}, dre id: %i%i%i%08i, geocode %i, card: %s v%i.%i", + reader->caid, cs_hexdump(0, reader->hexserial + 2, 4, tmp, sizeof(tmp)), dre_chksum, + csystem_data->provider - 16, major_version + 1, low_dre_id, geocode, card, major_version, minor_version); + } + else + { + low_dre_id = ((cta_res[4] << 16) | (cta_res[5] << 8) | cta_res[6]); + dre_chksum = 0; + snprintf((char *)buf, sizeof(buf), "%i%i%08i", csystem_data->provider, major_version, low_dre_id); + + for(i = 0; i < 32; i++) + { + if(buf[i] == 0x00) + { break; } + dre_chksum += buf[i] - 48; + } + rdr_log(reader, "type: DRE Crypt, caid: %04X, serial: {%s}, dre id: %i%03i%i%08i, geocode %i, card: %s v%i.%i", + reader->caid, cs_hexdump(0, reader->hexserial + 2, 4, tmp, sizeof(tmp)), dre_chksum, csystem_data->provider, + major_version, low_dre_id, geocode, card, major_version, minor_version); + } + + rdr_log(reader, "Provider name:%s.", provname); + + + memset(reader->sa, 0, sizeof(reader->sa)); + memcpy(reader->sa[0], reader->hexserial + 2, 1); // copy first byte of unique address also in shared address, because we dont know what it is... + + rdr_log_sensitive(reader, "SA = %02X%02X%02X%02X, UA = {%s}", reader->sa[0][0], reader->sa[0][1], reader->sa[0][2], + reader->sa[0][3], cs_hexdump(0, reader->hexserial + 2, 4, tmp, sizeof(tmp))); + + reader->nprov = 1; + + //cmd_test(reader); + + // exec user script, wicardd format + if(reader->userscript != NULL) + { + uint8_t *usercmd = NULL; + int cmd_len; + int n; + char *tempbuf = malloc(2048); + trim2(reader->userscript); + FILE *pFile = fopen(reader->userscript, "rt"); + + if(pFile != NULL) + { + uint8_t ignoreProvid = 0; + uint8_t crypted = 0; + uint8_t cryptkey = 0; + do + { + tempbuf[0] = '\0'; + if(usercmd != NULL) NULLFREE(usercmd); + + if(fgets(tempbuf, 2048, pFile) == NULL) continue; + + if(cs_strlen(tempbuf) < 10) continue; + + trim2(tempbuf); + + ignoreProvid = 0; + crypted = 0; + cryptkey = 0; + + if(tempbuf[0] == '8' && tempbuf[1] == '6' && csystem_data->provider == 0x11) ignoreProvid = 1; + else if(strncmp(tempbuf ,"REG2" ,4) == 0) + { + dre_read_ee(reader, &tempbuf[4] ,csystem_data->provider); + continue; + } + else if(strncmp(tempbuf ,"CR" ,2) == 0) + { + crypted = 1; + cryptkey = ((tempbuf[2] - (tempbuf[2] > 0x39 ? 0x37:0x30)) << 4) + ((tempbuf[3] - (tempbuf[3] > 0x39 ? 0x37:0x30)) & 0xF); + } + else if(tempbuf[0] != '5' && tempbuf[1] != '9') continue; + + strtoupper(tempbuf); + + cmd_len = cs_strlen(tempbuf) / 2 - 3 + ignoreProvid - (crypted * 2); + usercmd = malloc(cmd_len); + if (!usercmd) + { + free(tempbuf); + fclose(pFile); + return ERROR; + } + + for(i = 0, n = 4 + (crypted * 4); i < cmd_len; i++, n += 2) + { + usercmd[i] = ((tempbuf[n] - (tempbuf[n] > 0x39 ? 0x37 : 0x30)) << 4) + ((tempbuf[n + 1] - (tempbuf[n + 1] > 0x39 ? 0x37 : 0x30)) & 0xF); + } + + /*if(usercmd[cmd_len-1] != csystem_data->provider && !ignoreProvid) + { + rdr_log(reader, "Skip script: current provid %02X , script provid %02X", csystem_data->provider, usercmd[cmd_len-1]); + continue; + } + */ + rdr_log(reader, "User script: %s", tempbuf); + + /*ret =*/ + + rdr_log(reader, "Script %s", ({dre_script_nb(usercmd, cmd_len, ignoreProvid, crypted, cryptkey)}) ? "done" : "error"); + } + while(!feof(pFile)); + fclose(pFile); + } + else + { + rdr_log(reader, "Can't open script file (%s)", reader->userscript); + } + + if(usercmd != NULL) free(usercmd); + if(tempbuf != NULL) free(tempbuf); + } + + if(csystem_data->provider == 0x11) + { + memset(reader->prid[1], 0x00, 8); + reader->prid[1][3] = 0xFE; + reader->nprov = 2; + } + + if(!dre_set_provider_info(reader)) + { return ERROR; } // fatal error + + + rdr_log(reader, "ready for requests"); + return OK; +} + +static void DREover(struct s_reader *reader, const uint8_t *ECMdata, uint8_t *DW) +{ + uint32_t key_schedule[32]; + + if(reader->des_key_length < 128) + { + rdr_log(reader, "error: deskey is missing or too short"); + return; + } + + if(ECMdata[2] >= (43 + 4) && ECMdata[40] == 0x3A && ECMdata[41] == 0x4B) + { + des_set_key(&reader->des_key[(ECMdata[42] & 0x0F) * 8], key_schedule); + + des(DW, key_schedule, 0); // even DW post-process + des(DW + 8, key_schedule, 0); // odd DW post-process + }; +}; + +static int32_t dre_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + def_resp; + uint16_t overcryptId; + uint8_t tmp[16]; + char tmp_dbg[256]; + struct dre_data *csystem_data = reader->csystem_data; + if(reader->caid == 0x4ae0) + { + uint8_t ecmcmd41[] = { 0x41, + 0x58, 0x1f, 0x00, // fixed part, dont change + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // 0x01 - 0x08: next key + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // 0x11 - 0x18: current key + 0x3b, 0x59, 0x11 }; // 0x3b = keynumber, can be a value 56 ;; 0x59 number of package = 58+1 - Pay Package ;; 0x11 = provider + + ecmcmd41[22] = csystem_data->provider; + memcpy(ecmcmd41 + 4, er->ecm + 8, 16); + ecmcmd41[20] = er->ecm[6]; // keynumber + ecmcmd41[21] = 0x58 + er->ecm[25]; // package number + rdr_log_dbg(reader, D_READER, "unused ECM info front:%s", cs_hexdump(0, er->ecm, 8, tmp_dbg, sizeof(tmp_dbg))); + rdr_log_dbg(reader, D_READER, "unused ECM info back:%s", cs_hexdump(0, er->ecm + 24, er->ecm[2] + 2 - 24, tmp_dbg, sizeof(tmp_dbg))); + + if(({dre_cmd_nb(ecmcmd41)})) // ecm request + { + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } // exit if response is not 90 00 + memcpy(ea->cw, cta_res + 11, 8); + memcpy(ea->cw + 8, cta_res + 3, 8); + + return OK; + } + } + else if(reader->caid == 0x4ae1) + { + if(csystem_data->provider == 0x11 || csystem_data->provider == 0x14) + { + uint8_t ecmcmd51[] = { 0x51, 0x02, 0x56, 0x05, 0x00, 0x4A, 0xE3, // fixed header? + 0x9C, 0xDA, // first three nibbles count up, fourth nibble counts down; all ECMs sent twice + 0xC1, 0x71, 0x21, 0x06, 0xF0, 0x14, 0xA7, 0x0E, // next key? + 0x89, 0xDA, 0xC9, 0xD7, 0xFD, 0xB9, 0x06, 0xFD, // current key? + 0xD5, 0x1E, 0x2A, 0xA3, 0xB5, 0xA0, 0x82, 0x11, // key or signature? + 0x14 }; // provider + + memcpy(ecmcmd51 + 1, er->ecm + 5, 0x21); + rdr_log_dbg(reader, D_READER, "unused ECM info front:%s", cs_hexdump(0, er->ecm, 5, tmp_dbg, sizeof(tmp_dbg))); + rdr_log_dbg(reader, D_READER, "unused ECM info back:%s", cs_hexdump(0, er->ecm + 37, 4, tmp_dbg, sizeof(tmp_dbg))); + ecmcmd51[33] = csystem_data->provider; // no part of sig + + if(({dre_cmd_nb(ecmcmd51)})) // ecm request + { + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } // exit if response is not 90 00 + + if(er->ecm[2] >= 46 && er->ecm[43] == 1 && csystem_data->provider == 0x11) + { + memcpy(tmp, cta_res + 11, 8); + memcpy(tmp + 8, cta_res + 3, 8); + overcryptId = b2i(2, &er->ecm[44]); + rdr_log_dbg(reader, D_READER, "ICG ID: %04X", overcryptId); + + Drecrypt2OverCW(overcryptId,tmp); + + if(isValidDCW(tmp)) + { + memcpy(ea->cw, tmp, 16); + return OK; + } + return ERROR; + } + + DREover(reader, er->ecm, cta_res + 3); + + if(isValidDCW(cta_res + 3)) + { + memcpy(ea->cw, cta_res + 11, 8); + memcpy(ea->cw + 8, cta_res + 3, 8); + return OK; + } + } + } + else if((csystem_data->provider == 0x02 || csystem_data->provider == 0x03) && er->ecm[3] == 3) + { + // DRE 3 + + if (er->ecm[4] == 2) + { + memcpy(ea->cw, &er->ecm[42], 8); + memcpy(&ea->cw[8], &er->ecm[34], 8); + return OK; + } + + uint8_t cmdlen; + uint8_t crypted = er->ecm[8] & 1; + uint8_t cryptkey = (er->ecm[8] & 6) >> 1; + + if (crypted == 0) + { + cmdlen = 50; + } + else + { + cmdlen = 57; + } + + uint8_t ecmcmd[cmdlen]; + + memcpy(ecmcmd, &er->ecm[17], cmdlen - 1); + ecmcmd[cmdlen - 1] = csystem_data->provider; + + dre_cmd_c(ecmcmd, crypted, cryptkey); + + if(cta_res[2] == 0xD2 && isValidDCW(cta_res + 3)) + { + memcpy(ea->cw, cta_res + 11, 8); + memcpy(ea->cw + 8, cta_res + 3, 8); + return OK; + } + } + } + else if(reader->caid == 0x2710 && er->ecm[3] == 4) + { + // DRE 4 + + if (er->ecm[4] == 4) + { + memcpy(ea->cw, &er->ecm[22], 8); + memcpy(&ea->cw[8], &er->ecm[14], 8); + return OK; + } + + uint8_t cmdlen; + uint8_t crypted = er->ecm[8] & 1; + uint8_t cryptkey = (er->ecm[8] & 6) >> 1; + + if (crypted == 0) + { + cmdlen = 58; + } + else + { + cmdlen = 65; + } + + uint8_t ecmcmd[cmdlen]; + + memcpy(ecmcmd, &er->ecm[9], cmdlen - 1); + ecmcmd[cmdlen - 1] = csystem_data->provider; + + dre_cmd_c(ecmcmd, crypted, cryptkey); + + if(cta_res[2] == 0xD2 && isValidDCW(cta_res + 3)) + { + memcpy(ea->cw, cta_res + 11, 8); + memcpy(ea->cw + 8, cta_res + 3, 8); + return OK; + } + } + return ERROR; +} + +static int32_t dre_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + def_resp; + struct dre_data *csystem_data = reader->csystem_data; + + if(reader->caid == 0x4ae1) + { + if(reader->caid != b2i(2, ep->caid)) return ERROR; + + if(ep->type == UNIQUE && ep->emm[39] == 0x3d) + { + /* For new package activation. */ + uint8_t emmcmd58[26]; + emmcmd58[0] = 0x58; + memcpy(&emmcmd58[1], &ep->emm[40], 24); + emmcmd58[25] = csystem_data->provider; + + if(({dre_cmd_nb(emmcmd58)})) + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } + } + else if(ep->emm[0] == 0x86 && ep->emm[4] == 0x02 /*&& csystem_data->provider != 0x11*/) + { + uint8_t emmcmd52[0x3a]; + emmcmd52[0] = 0x52; + int32_t i; + for(i = 0; i < 2; i++) + { + memcpy(emmcmd52 + 1, ep->emm + 5 + 32 + i * 56, 56); + + // check for shared address + if(ep->emm[3] != reader->sa[0][0]) + { return OK; } // ignore, wrong address + + emmcmd52[0x39] = csystem_data->provider; + + if(({dre_cmd_nb(emmcmd52)})) + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } // exit if response is not 90 00 + } + } + else if(ep->emm[0] == 0x86 && ep->emm[4] == 0x4D && csystem_data->provider == 0x11) + { + uint8_t emmcmd52[0x3a]; + + emmcmd52[0] = 0x52; + emmcmd52[1] = 0x01; + emmcmd52[2] = ep->emm[5]; + emmcmd52[3] = 0x01; + emmcmd52[4] = ep->emm[3]; + emmcmd52[5] = 0; + emmcmd52[6] = 0; + emmcmd52[7] = 0; + emmcmd52[9] = 0x01; + emmcmd52[10] = 0x01; + emmcmd52[11] = 0; + memcpy(emmcmd52 + 13, ep->emm + 0x5C, 4); + int32_t i; + + for(i = 0; i < 2; i++) + { + emmcmd52[8] = ep->emm[0x61 + i * 0x29]; + if(i == 0) emmcmd52[12] = ep->emm[0x60] == 0x56 ? 0x56 : 0x3B; + else emmcmd52[12] = ep->emm[0x60] == 0x56 ? 0x3B : 0x56; + memcpy(emmcmd52 + 0x11, ep->emm + 0x62 + i * 0x29, 40); + + // check for shared address + if(ep->emm[3] != reader->sa[0][0]) + { return OK; } // ignore, wrong address + + emmcmd52[0x39] = csystem_data->provider; + + if(({dre_cmd_nb(emmcmd52)})) + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } // exit if response is not 90 00 + } + } + else if (ep->emm[0] == 0x8c && (csystem_data->provider == 0x02 || csystem_data->provider == 0x03)) // dre3 group emm + { + if(ep->emm[3] != reader->sa[0][0]) + { return OK; } // ignore, wrong address + + uint8_t crypted = ep->emm[10]; + + if ((crypted & 1) == 1) + { + uint8_t emmcmd[0x49]; + + memcpy(emmcmd, &ep->emm[0x13], 0x48); + + emmcmd[0x48] = csystem_data->provider; + + dre_cmd_c(emmcmd, crypted & 1, (crypted & 6) >> 1); + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } //exit if response is not 90 00 + + memcpy(emmcmd, &ep->emm[0x5B], 0x48); + + emmcmd[0x48] = csystem_data->provider; + + dre_cmd_c(emmcmd, crypted & 1, (crypted & 6) >> 1); + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } //exit if response is not 90 00 + + return OK; + } + else + { + uint8_t emmcmd[0x42]; + + memcpy(emmcmd, &ep->emm[0x13], 0x41); + + emmcmd[0x41] = csystem_data->provider; + + dre_cmd_c(emmcmd, crypted & 1, (crypted & 6) >> 1); + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } // exit if response is not 90 00 + + memcpy(emmcmd, &ep->emm[0x5B], 0x41); + + emmcmd[0x41] = csystem_data->provider; + + dre_cmd_c(emmcmd, crypted & 1, (crypted & 6) >> 1); + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } // exit if response is not 90 00 + + return OK; + } + } + else if(ep->type == GLOBAL && ep->emm[0] == 0x91) + { + Drecrypt2OverEMM(ep->emm); + return OK; + } + else return OK; + } + else if(reader->caid == 0x2710) + { + // DRE 4 + if(ep->type == UNIQUE) + { + uint16_t cmdlen; + uint8_t class, hlbUA, KEYindex; + int i, keycount; + uint8_t CMDtype = ep->emm[7]; + uint16_t EMMlen = ep->emm[2] | ((ep->emm[1] & 0xF) << 8); + uint8_t cryptflag = ep->emm[10]; + uint8_t crypted = cryptflag & 1; + uint8_t cryptkey = (cryptflag & 6) >> 1; + + if (CMDtype == 0x61) + { + uint8_t emmcmd91[19]; + + emmcmd91[0] = 0x91; + emmcmd91[1] = ep->emm[19]; + emmcmd91[2] = ep->emm[8]; + emmcmd91[3] = ep->emm[20]; + if(reader->force_ua) emmcmd91[3] += 2; + memcpy(&emmcmd91[4], &reader->hexserial[2], 4); + emmcmd91[8] = 0xF0; + emmcmd91[17] = ep->emm[22]; + emmcmd91[18] = csystem_data->provider; + + if ((EMMlen - 24) > 16) + { + hlbUA = reader->hexserial[5] & 0xF; + + uint16_t keypos = cryptflag == 2 ? 17 : 9; + keycount = (EMMlen - 24) / keypos; + + for(i = 0; i <= keycount ;i++) + { + if (i == keycount) return OK; + if (hlbUA == (ep->emm[23 + (keypos * i)] & 0xF) ) break; + } + + keypos = 24 + (keypos * i); + + memcpy(&emmcmd91[9], &ep->emm[keypos], 8); + + if(({dre_cmd_nb(emmcmd91)})) + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00) || (cta_res[2] != 0xA2)) + return ERROR; // exit if response is not 90 00 + + if (cryptflag == 2) + { + if (emmcmd91[17] == 0x56) KEYindex = 0x3B; + else KEYindex = 0x56; + + keypos += 8; + memcpy(&emmcmd91[9], &ep->emm[keypos], 8); + emmcmd91[17] = KEYindex; + + dre_cmd(emmcmd91); + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00) || (cta_res[2] != 0xA2)) + { return ERROR; } // exit if response is not 90 00 + + return OK; + } + } + return ERROR; + } + else if (CMDtype == 0x62) + { + if (!memcmp(&reader->hexserial[2], &ep->emm[3], 4)) + { + if (crypted) + { + cmdlen = 49; + } + else + { + cmdlen = 42; + } + + uint8_t emmcmd92[cmdlen]; + + memcpy(emmcmd92, &ep->emm[19], cmdlen - 1); + emmcmd92[cmdlen - 1] = csystem_data->provider; + + if (crypted) + { + dre_cmd_c(emmcmd92, crypted, cryptkey); + } + else + { + dre_cmd(emmcmd92); + } + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } // exit if response is not 90 00 + + class = ep->emm[8]; + + uint8_t emmcmd95[3]; + + emmcmd95[0] = 0x95; + emmcmd95[1] = class; + emmcmd95[2] = csystem_data->provider; + + dre_cmd(emmcmd95); + + uint8_t emmcmd91[19]; + + emmcmd91[0] = 0x91; + emmcmd91[1] = ep->emm[102]; + emmcmd91[2] = ep->emm[8]; + emmcmd91[3] = ep->emm[103]; + if(reader->force_ua) emmcmd91[3] += 2; + memcpy(&emmcmd91[4], &reader->hexserial[2], 4); + emmcmd91[8] = ep->emm[104]; + memcpy(&emmcmd91[9], &ep->emm[72], 8); + emmcmd91[17] = ep->emm[105]; + emmcmd91[18] = csystem_data->provider; + + dre_cmd(emmcmd91); + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00) || (cta_res[2] != 0xA2) ) + { return ERROR; } // exit if response is not 90 00 + + if (emmcmd91[17] == 0x56) KEYindex = 0x3B; + else KEYindex = 0x56; + + memcpy(&emmcmd91[9], &ep->emm[86], 8); + emmcmd91[17] = KEYindex; + + dre_cmd(emmcmd91); + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } // exit if response is not 90 00 + } + return OK; + } + else + { + if (memcmp(&reader->hexserial[2], &ep->emm[3], 4)) return OK; + + if (CMDtype == 0x63) + { + uint8_t emmcmdA5[7]; + + emmcmdA5[0] = 0xA5; + emmcmdA5[1] = 0; + memcpy(&emmcmdA5[2], &reader->hexserial[2], 4); + emmcmdA5[6] = csystem_data->provider; + + dre_cmd(emmcmdA5); + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } //exit if response is not 90 00 + } + + if (crypted) cmdlen = EMMlen - 19; + else cmdlen = ep->emm[11] + 1; + + uint8_t emmcmd[cmdlen]; + + memcpy(emmcmd, &ep->emm[19], cmdlen - 1); + emmcmd[cmdlen - 1] = csystem_data->provider; + + if(emmcmd[0] == 0x45) + { + cs_log("TRICOLOR Send KILL command for your card"); + return ERROR; + } + + dre_cmd_c(emmcmd, crypted, cryptkey); + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } //exit if response is not 90 00 + } + return OK; + } + return ERROR; + } + else if(reader->caid != 0x2710 && reader->caid != 0x4AE1) + { + uint8_t emmcmd42[] = + { + 0x42, 0x85, 0x58, 0x01, 0xC8, 0x00, 0x00, 0x00, 0x05, 0xB8, 0x0C, 0xBD, 0x7B, 0x07, 0x04, 0xC8, + 0x77, 0x31, 0x95, 0xF2, 0x30, 0xB7, 0xE9, 0xEE, 0x0F, 0x81, 0x39, 0x1C, 0x1F, 0xA9, 0x11, 0x3E, + 0xE5, 0x0E, 0x8E, 0x50, 0xA4, 0x31, 0xBB, 0x01, 0x00, 0xD6, 0xAF, 0x69, 0x60, 0x04, 0x70, 0x3A, + 0x91, + 0x56, 0x58, 0x11 + }; + int32_t i; + + switch(ep->type) + { + case UNIQUE: + for(i = 0; i < 2; i++) + { + memcpy(emmcmd42 + 1, ep->emm + 42 + i * 49, 48); + emmcmd42[49] = ep->emm[i * 49 + 41]; // keynr + emmcmd42[50] = 0x58 + ep->emm[40]; // package nr + emmcmd42[51] = csystem_data->provider; + if(({dre_cmd_nb(emmcmd42)})) + { + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } // exit if response is not 90 00 + } + } + break; + + case SHARED: + default: + memcpy(emmcmd42 + 1, ep->emm + 6, 48); + emmcmd42[51] = csystem_data->provider; + //emmcmd42[50] = ecmcmd42[2]; // TODO package nr could also be fixed 0x58 + emmcmd42[50] = 0x58; + emmcmd42[49] = ep->emm[5]; // keynr + + /* response: + 59 05 A2 02 05 01 5B + 90 00 */ + + if(({dre_cmd_nb(emmcmd42)})) // first emm request + { + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } // exit if response is not 90 00 + + memcpy(emmcmd42 + 1, ep->emm + 55, 7); // TODO OR next two lines? + //memcpy (emmcmd42 + 1, ep->emm + 55, 7); // FIXME either I cant count or my EMM log contains errors + //memcpy (emmcmd42 + 8, ep->emm + 67, 41); + emmcmd42[51] = csystem_data->provider; + //emmcmd42[50] = ecmcmd42[2]; // TODO package nr could also be fixed 0x58 + emmcmd42[50] = 0x58; + emmcmd42[49] = ep->emm[54]; // keynr + + if(({dre_cmd_nb(emmcmd42)})) // second emm request + { + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { return ERROR; } // exit if response is not 90 00 + } + } + } + return OK; + } + return ERROR; +} + +static int32_t dre_card_info(struct s_reader *UNUSED(rdr)) +{ + return OK; +} + +const struct s_cardsystem reader_dre = +{ + .desc = "dre", + .caids = (uint16_t[]){ 0x4AE0, 0x4AE1, 0x7BE0, 0x7BE1, 0x2710, 0 }, + .do_emm = dre_do_emm, + .do_ecm = dre_do_ecm, + .card_info = dre_card_info, + .card_init = dre_card_init, + .get_emm_type = dre_common_get_emm_type, + .get_emm_filter = dre_common_get_emm_filter, +}; + +#endif diff --git a/reader-griffin.c b/reader-griffin.c new file mode 100644 index 0000000..387294f --- /dev/null +++ b/reader-griffin.c @@ -0,0 +1,485 @@ +/* + * Griffin card reader for OSCAM + * Copyright (C) 2013 Unix Solutions Ltd. + * + * Author(s): Georgi Chorbadzhiyski (gf@unixsol.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * ========================================================================= + * Once upon a time Bulsatcom in Bulgaria used Griffin CAS. Their cards were + * known as "strawberry cards" because they had a strawberry picture on the + * front. These cards have CAID 0x5501. You can't get EMM and ECM stream + * for these cards but if you can, then the reader would probably work. + * + * This reader is tested with working card that have CAID 0x5504. This card + * is used by KaTe Nova Gorica provider in Slovenia. + * ========================================================================= + * + * Griffin reader card protocol: + * ATR from CAID 0x5501 + * 3B 08 01 01 xx xx xx xx 10 00 + * (01 - last two octets in caid (0x01 = CAID 0x5501) + * (xx - card hex serial number) + * (10 - command base) + * + * ATR from CAID 0x5504 + * 3B 08 04 01 xx xx xx xx 20 00 + * (04 - last two octets in caid (0x04 = CAID 0x5504) + * (xx - card hex serial number) + * (20 - command base) + * + * The basic card conversation looks like this: + * Send DC xx 00 00 yy zz zz zz (xx = command op, yy payload length, zz - payload if payload_len > 0) + * Recv 90 03 (90 = command ok, 03 = length of the response) + * Send DC 12 00 00 xx (Read response , xx = length of the response) + * Recv xx yy zz 90 00 (Response , xx = response code, yy - data length, zz - data, 90 00 at the end) + * + * Command ops (hex), these commands are for card with base == 20: + * 02 - read command response (for base cards with base 10) + * 12 - read command response (for base cards with base 20) + * + * The command number is (base + number, the numbers bellow are for base==20) + * 20 - card init + * 22 - get hex serial + * 24 - get ascii serial + * 26 - get caid + * 28 - get card addresses + * 30 - unknown command + * 34 - unknown command + * 36 - send_ecm + * 32 - send_emm + * 40 - get subscription info + * 42 - unknown command + * 4a - unknown command + * 50 - unknown command + * + * Perform card INIT + * DC 20 00 00 00 -- 90 03 (card init /get base/) + * DC 12 00 00 03 -- 11 01 20 90 00 (20 == cmd base) + * + * DC 22 00 00 00 -- 90 06 (get hex serial) + * DC 12 00 00 06 -- 12 04 xx xx xx xx 90 00 (xx - hex serial) + * + * DC 24 00 00 00 -- 90 0E (get ascii serial) + * DC 12 00 00 0E -- 13 0C 47 43 30 34 53 xx xx xx xx xx xx 00 90 00 (xx - ascii serial - GC04S......) + * + * DC 26 00 00 00 -- 90 04 (get caid) + * DC 12 00 00 04 -- 14 02 xx xx 90 00 (xx xx = 55 04 /the caid/) + * + * DC 28 00 00 00 -- 90 32 + * DC 12 00 00 32 -- 15 30 \ + * 80 00 00 00 00 00 00 00 F0 FF FF FF FF 00 00 00 \ + * 80 yy yy yy yy 00 00 00 F0 FF FF FF FF 00 00 00 \ (yy = shared card address) + * 80 xx xx xx xx 00 00 00 F0 FF FF FF FF 00 00 00 90 00 (xx = card hex serial) + * + * DC 42 00 00 00 -- 90 03 + * DC 12 00 00 03 -- 1C 01 00 90 00 + * + * DC 34 00 00 00 -- 90 00 + * DC 30 00 00 00 -- 90 00 + * + * Send ECMs + * DC 36 00 00 42 -- 81 70 3F C6 71 A3 97 A3 91 36 76 C9 69 EC A8 46 CA FB 0B 31 D2 4B 2A BD 43 FF 5E A4 C0 CD 06 A9 48 1B 2B 6C 3D 28 B2 92 3A C0 C2 1C 38 35 29 D0 9D B2 16 6D 26 E3 27 A3 20 6F 8E 72 5D 0B A3 00 65 EC 90 18 + * DC 36 00 00 42 -- 80 70 3F 09 D2 9B F3 03 E3 81 5A E4 44 F4 18 9E 84 18 D5 6E 81 D8 1F A2 E8 CB 1B B6 A9 3D 5D C2 CA FE 6A C2 69 1A CD 15 7F 2C A6 77 8B CF 0C 4E 4F 74 04 97 17 15 93 4F 2E 93 10 B8 6B B0 FF 1C 20 7D 90 18 + * + * DC 36 00 00 41 -- 81 70 3E 09 EC 39 F4 35 CF 45 80 AB 17 56 56 64 F0 BB 39 97 BE 7F 9E D3 F7 70 6A B2 8A 52 56 BD B4 B3 77 14 22 13 70 7F 9D 03 2A BB 88 85 3E 1D AB 9D E1 C7 A1 CB B9 99 34 F6 EB 2C 15 7F 52 E5 1C 90 18 + * DC 36 00 00 41 -- 80 70 3E F4 7E B1 C4 30 56 C2 61 AA 31 29 FB 09 1C 79 13 14 8E 64 43 5F 4B 97 71 A0 D3 BA A4 08 AC 8B E4 21 B7 C6 8A A5 9F 72 19 A5 51 75 9B F2 40 B2 C9 8F E8 63 98 2C 5D 84 21 88 8F F1 DA CF 32 90 18 + * + * DC 36 00 00 3E -- 81 70 3B 07 76 06 C8 8D 9F 57 C8 19 30 1C 3B 93 9B A1 E1 88 E7 82 C3 E5 7A 05 44 DF 7D 90 CB F9 E1 43 C7 6F 39 75 3A A5 15 73 AA 5F 8C 1D 5B B6 52 2B 0B C2 02 88 7C C2 E8 4F D6 6A 73 A6 90 18 + * DC 36 00 00 3E -- 80 70 3B CE B9 CC BC 95 D8 BB 4A C0 7B 7C 7E 9C 39 00 10 47 E1 67 A7 CD 34 9F E6 43 CB 50 2E 77 9A 54 87 54 25 49 FC 4F 6A A6 56 FD 51 74 08 37 C3 00 04 BD 72 04 CB DB D9 7C 37 76 71 A7 90 18 + * + * DC 36 00 00 3D -- 81 70 3A 22 06 2B 48 2A 99 4B 82 20 C4 80 B4 55 72 CD B3 C9 FD BA 84 89 66 F4 F8 51 7F CD AC 38 4E 0E 6A 91 11 E9 E1 A4 0E 8D E7 56 43 11 56 F5 DA 78 19 42 37 B3 CA BA 33 11 69 B9 96 90 18 + * DC 36 00 00 3D -- 80 70 3A FB 09 20 41 48 2D 12 4F E8 13 E3 23 AD B9 25 CE DA 95 F2 C8 ED D6 08 2E 23 6A 13 19 A8 A7 9F 9A 8B 12 F3 97 95 09 5B F6 F6 AA 64 EA 46 3C AD 62 93 DC B5 07 FB 16 81 F8 A6 D3 90 18 + * + * DC 36 00 00 3A -- 81 70 37 16 21 7A 01 9A A5 BB C8 9E 93 88 79 56 C1 41 B4 37 5F 1F 3A 69 1E 4A CA DB 56 77 98 3A 02 9E 2C 8A FE 24 51 DD 5E F9 23 79 AF 4D 63 27 34 A0 28 44 11 45 BA 72 F2 92 90 18 + * DC 36 00 00 3A -- 80 70 37 79 E6 26 6E 93 D8 8E F1 DC A1 70 7A 36 77 6D 68 AE 36 1B 85 E4 85 EE 35 E8 33 5A 4D 84 AC AA 87 5B 7B EF F3 DF 76 20 7B 0A 91 B3 B1 3D 97 FE 21 8C 52 E2 8F 01 5D 50 90 18 + * + * Read DCWs (after sending ECM) + * DC 12 00 00 18 -- 19 04 00 00 00 00 1A 10 F9 EE 8F 76 A9 85 DC 0A E3 92 51 C6 40 B4 B0 A4 90 00 + * DC 12 00 00 18 -- 19 04 00 00 00 00 1A 10 F9 EE 8F 76 A9 85 DC 0A 51 AB 96 92 7C A0 7F 9B 90 00 + * DC 12 00 00 18 -- 19 04 00 00 00 00 1A 10 F6 51 8F D6 E6 F0 5E 34 AA 41 86 71 CC C0 29 B5 90 00 + * DC 12 00 00 18 -- 19 04 00 00 00 00 1A 10 F6 51 8F D6 E6 F0 5E 34 51 AB 96 92 7C A0 7F 9B 90 00 + * + * Send EMMs + * DC 32 00 00 B2 -- 83 70 AF -- -- -- -- 3F 38 ED 59 0B 52 7D 8B D9 43 B5 51 6F C5 1D F2 36 35 C4 90 92 83 92 3E A2 99 47 76 3A CF 81 79 5C A1 4E B3 5D 09 D0 7E 86 3F DD C8 56 30 72 B4 E0 DE 0F 76 03 6F 16 F4 1F 4E 35 DC 6F 36 E8 DB E8 F3 75 BB CF 7B FE 46 91 8F F9 1C 7D 18 27 98 27 31 5A A4 39 44 E5 62 B0 DA 81 73 65 58 08 0B 44 20 57 37 DA 20 19 6B 35 F4 07 74 BA 42 75 AD 4A C5 86 C1 E3 03 C1 A2 05 C2 A3 C2 4C 57 B8 7E 3E DE 74 FB 5D 32 4A 7F 68 2B 74 E8 84 B6 33 52 6A B8 3D FD 3F 14 C4 39 39 39 28 80 B1 AC 77 39 A0 EF 8A 3C F5 4F F6 99 67 90 A4 90 0A + * DC 12 00 00 0A -- 1F 04 51 06 B5 B5 16 02 01 00 90 00 + * + * DC 32 00 00 AF -- 82 70 AC -- -- -- -- C6 94 A0 54 68 47 D0 3F FB 05 C6 A3 C5 FA 5F F0 A7 56 96 19 A5 F6 31 95 CD F1 8D 71 C3 FE 96 FD 75 2A DE 1F 12 08 8C 53 5D B6 4E FC 34 5D F0 BB 52 84 6C 71 C3 EA CE 4C 8A 08 45 22 E3 74 4A 37 48 39 75 37 0C 4A A9 8B 62 D8 F5 EE EC 28 E2 92 66 2D DA FF 8C 2B BD 97 C5 95 6B A0 6F 8B 82 79 09 79 E6 63 66 77 0A AB 8F EC 65 4F EC 05 75 2B FD DF 78 85 48 6C 2C A0 4D 4C 96 B6 08 21 A1 01 8D 74 CC F3 92 04 D2 15 49 F7 CE 74 6B 38 D9 22 66 2D 7E D6 78 BB 3D 0B 30 A7 64 A1 DC AE 0E 54 90 D0 83 BC 89 9F CA 50 90 00 + * + * Get subscription info (for base 20 cards - CAID 5504) + * DC 40 00 00 00 -- 90 3C + * DC 12 00 00 3C -- 1B 02 07 FF 1B 02 07 FF 1B 02 07 FF 1B 02 07 FF 1B 02 00 0F 1B 02 00 00 \ + * 1B 02 00 00 1B 02 00 00 1B 02 00 00 1B 02 00 00 1B 02 00 00 1B 02 00 00 \ + * 1B 02 00 00 1B 02 00 00 1B 02 00 00 90 00 + * + * Get subscription info (for base 10 cards - CAID 5501) + * DC 30 00 00 00 -- 90 2D + * DC 02 00 00 2D -- 0B 07 30 30 30 30 36 30 00 0B 07 30 30 30 30 36 30 00 \ + * 0B 07 30 30 30 30 36 30 00 0B 07 30 30 30 30 36 30 00 \ + * 0B 07 30 30 30 30 31 32 00 90 00 + * + * Unknown commands + * DC 4A 00 00 00 -- 90 06 + * DC 12 00 00 06 -- 1D 04 00 00 00 00 90 00 + * + * DC 50 00 00 00 -- 90 0E + * DC 12 00 00 0E -- 1E 0C 00 0F 42 40 00 3D 09 00 00 1F 01 74 90 00 + * + */ + +#include "globals.h" + +#ifdef READER_GRIFFIN +#include "reader-common.h" + +#define DEBUG 0 + +#define GRIFFIN_CMD_INIT 0x00 +#define GRIFFIN_CMD_GET_HEX_SERIAL 0x02 +#define GRIFFIN_CMD_GET_ASCII_SERIAL 0x04 +#define GRIFFIN_CMD_GET_CAID 0x06 +#define GRIFFIN_CMD_GET_CARD_ADDRESS 0x08 +#define GRIFFIN_CMD_SEND_EMM 0x12 +#define GRIFFIN_CMD_SEND_ECM 0x16 +#define GRIFFIN_CMD_SUBSCRIPTION_INFO 0x20 + +#define cmd_buf_len 512 + +struct griffin_data +{ + uint8_t cmd_base; // Command base, depends on the card +}; + +// Sets cmd_buf and returns buf_len +static uint32_t griffin_init_cmd(struct s_reader *rdr, uint8_t *cmd_buf, uint8_t cmd_op, const uint8_t *data, uint8_t data_len) +{ +#define cmd_len 5 + + memset(cmd_buf, 0, cmd_buf_len); + cmd_buf[0] = 0xDC; // Command start + cmd_buf[1] = cmd_op; + cmd_buf[2] = 0x00; + cmd_buf[3] = 0x00; + cmd_buf[4] = data_len; // Set payload length + + if(data && data_len) + { memcpy(cmd_buf + cmd_len, data, data_len); } + + uint32_t len = cmd_len + (data ? data_len : 0); + + if(DEBUG) + { + char tmp[1024]; + rdr_log(rdr, "SEND[-] -> %s", cs_hexdump(1, cmd_buf, len, tmp, sizeof(tmp))); + } + return len; +} + +static int32_t griffin_exec_cmd(struct s_reader *rdr, uint8_t cmd_op, const uint8_t *data, uint8_t data_len, uint8_t *response, uint16_t *response_length) +{ + struct griffin_data *csystem_data = rdr->csystem_data; + uint8_t buf[cmd_buf_len]; + + int32_t ret = reader_cmd2icc(rdr, buf, griffin_init_cmd(rdr, buf, csystem_data->cmd_base + cmd_op, + data, data_len), response, response_length); + + if(DEBUG) + { + char tmp[1024]; + rdr_log(rdr, "RECV[1] <- %s (ret=%d resp_len=%d)", cs_hexdump(1, response, *response_length, tmp, sizeof(tmp)), ret, *response_length); + } + + if(ret || *response_length < 2) { return ERROR; } // Response is two short + if(response[0] != 0x90) { return ERROR; } // Invalid response + if(response[1] == 0) { return OK; } // Nothing to retrieve, command OK + + // Retrieve response + uint8_t cmd_read_response = 0x02; + if(csystem_data->cmd_base > 0x10) + { cmd_read_response += csystem_data->cmd_base - 0x10; } + + ret = reader_cmd2icc(rdr, buf, griffin_init_cmd(rdr, buf, cmd_read_response, NULL, response[1]), response, response_length); + + if(DEBUG) + { + char tmp[1024]; + rdr_log(rdr, "RECV[2] <- %s (ret=%d resp_len=%d)", cs_hexdump(1, response, *response_length, tmp, sizeof(tmp)), ret, *response_length); + } + + if(ret || *response_length < 2) { return ERROR; } // Response is two short + if(response[*response_length - 2] != 0x90) { return ERROR; } // Invalid response + if(response[*response_length - 1] != 0x00) { return ERROR; } // We don't expect command_op 0x12 to return more data + return OK; +} + +#define griffin_cmd(_cmd_op, _data, _data_len, _min_resp_len) \ + do { \ + if (!griffin_exec_cmd(rdr, _cmd_op, _data, _data_len, cta_res, &cta_lr) || cta_lr < _min_resp_len) \ + return ERROR; \ + } while(0) + +static int32_t griffin_card_init(struct s_reader *rdr, ATR *newatr) +{ + int32_t i; + get_atr + def_resp + + if(atr_size < 10) + { + return ERROR; + } + + // 0 1 2 3 4 5 6 7 8 9 + // ATR: 3B 08 yy 01 xx xx xx xx 10 00 + if(atr[0] != 0x3b || atr[1] != 0x08 || atr[3] != 0x01 || atr[9] != 0x00) + { + return ERROR; + } + + if(!cs_malloc(&rdr->csystem_data, sizeof(struct griffin_data))) + { + return ERROR; + } + + struct griffin_data *csystem_data = rdr->csystem_data; + + rdr->nprov = 1; + memset(rdr->sa, 0, sizeof(rdr->sa)); + memset(rdr->prid, 0, sizeof(rdr->prid)); + memset(rdr->hexserial, 0, sizeof(rdr->hexserial)); + + rdr->caid = (0x55 << 8) | atr[2]; + memcpy(rdr->hexserial, atr + 4, 4); + csystem_data->cmd_base = atr[8]; + + rdr_log_sensitive(rdr, "[griffin-reader] card detected, cmd_base: %02X caid: %04X hexserial: {%02X %02X %02X %02X}", + csystem_data->cmd_base, rdr->caid, rdr->hexserial[0], rdr->hexserial[1], rdr->hexserial[2], rdr->hexserial[3]); + + griffin_cmd(GRIFFIN_CMD_INIT, NULL, 0, 2); + csystem_data->cmd_base = cta_res[2]; // already set from ATR + + griffin_cmd(GRIFFIN_CMD_GET_HEX_SERIAL, NULL, 0, 6); + memcpy(rdr->hexserial, cta_res + 2, 4); + + char serial[16]; + memset(serial, 0, sizeof(serial)); + griffin_cmd(GRIFFIN_CMD_GET_ASCII_SERIAL, NULL, 0, 14); + memcpy(serial, cta_res + 2, 12); + + griffin_cmd(GRIFFIN_CMD_GET_CAID, NULL, 0, 4); + rdr->caid = (cta_res[2] << 8) | cta_res[3]; + + griffin_cmd(GRIFFIN_CMD_GET_CARD_ADDRESS, NULL, 0, 48); + + for(i = 1 ; i < CS_MAXPROV; i++) + { + if(3 + (i * 16) + 4 > cta_lr) + { + break; + } + memcpy(rdr->sa[i - 1], cta_res + 3 + (i * 16), 4); + } + + // Unknown commands + griffin_cmd(0x22, NULL, 0, 2); + griffin_cmd(0x10, NULL, 0, 2); + griffin_cmd(0x14, NULL, 0, 2); + //griffin_cmd(0x2a, NULL, 0, 2); + //griffin_cmd(0x30, NULL, 0, 2); + + for(i = 0 ; i < CS_MAXPROV; i++) + { + if(array_has_nonzero_byte(rdr->sa[i], 4)) + { + rdr_log_sensitive(rdr, "CAID: 0x%04X, Serial: {%s}, HexSerial: {%02X %02X %02X %02X} Addr: {%02X %02X %02X %02X}", + rdr->caid, serial, rdr->hexserial[0], rdr->hexserial[1], rdr->hexserial[2], rdr->hexserial[3], + rdr->sa[i][0], rdr->sa[i][1], rdr->sa[i][2], rdr->sa[i][3]); + } + } + + rdr_log(rdr, "Ready for requests."); + return OK; +} + +static int32_t griffin_do_ecm(struct s_reader *rdr, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + def_resp + griffin_cmd(GRIFFIN_CMD_SEND_ECM, er->ecm, er->ecm[2] + 3, 24); + memcpy(ea->cw, cta_res + 8, 16); + return OK; +} + +static int32_t griffin_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + memcpy(ep->hexserial, ep->emm + 3, 4); + switch(ep->emm[0]) + { + case 0x82: + case 0x83: + if(memcmp(ep->hexserial, rdr->sa[0], 4) == 0) + { + if(DEBUG) + { + rdr_log_sensitive(rdr, "SHARED EMM TYPE:%02X SA:{%02X %02X %02X %02X}", + ep->emm[0], ep->emm[3], ep->emm[4], ep->emm[5], ep->emm[6]); + } + ep->type = SHARED; + } + if(memcmp(ep->hexserial, rdr->sa[1], 4) == 0) + { + if(DEBUG) + { + rdr_log_sensitive(rdr, "UNIQUE EMM TYPE:%02X SA:{%02X %02X %02X %02X}", + ep->emm[0], ep->emm[3], ep->emm[4], ep->emm[5], ep->emm[6]); + } + ep->type = UNIQUE; + } + break; + + default: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN EMM TYPE:%02X SA:%02X %02X %02X %02X", + ep->emm[0], ep->emm[3], ep->emm[4], ep->emm[5], ep->emm[6]); + } + return OK; +} + +static int32_t griffin_do_emm(struct s_reader *rdr, EMM_PACKET *ep) +{ + def_resp + griffin_cmd(GRIFFIN_CMD_SEND_EMM, ep->emm, SCT_LEN(ep->emm), 2); + return OK; +} + +static int32_t griffin_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 4; + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].filter[1] = rdr->sa[0][0]; + filters[idx].filter[2] = rdr->sa[0][1]; + filters[idx].filter[3] = rdr->sa[0][2]; + filters[idx].filter[4] = rdr->sa[0][3]; + filters[idx].mask[0] = 0xFF; + filters[idx].mask[1] = 0xFF; + filters[idx].mask[2] = 0xFF; + filters[idx].mask[3] = 0xFF; + filters[idx].mask[4] = 0xFF; + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].filter[1] = rdr->sa[1][0]; + filters[idx].filter[2] = rdr->sa[1][1]; + filters[idx].filter[3] = rdr->sa[1][2]; + filters[idx].filter[4] = rdr->sa[1][3]; + filters[idx].mask[0] = 0xFF; + filters[idx].mask[1] = 0xFF; + filters[idx].mask[2] = 0xFF; + filters[idx].mask[3] = 0xFF; + filters[idx].mask[4] = 0xFF; + idx++; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].filter[1] = rdr->sa[0][0]; + filters[idx].filter[2] = rdr->sa[0][1]; + filters[idx].filter[3] = rdr->sa[0][2]; + filters[idx].filter[4] = rdr->sa[0][3]; + filters[idx].mask[0] = 0xF0; + filters[idx].mask[1] = 0xFF; + filters[idx].mask[2] = 0xFF; + filters[idx].mask[3] = 0xFF; + filters[idx].mask[4] = 0xFF; + idx++; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].filter[1] = rdr->sa[1][0]; + filters[idx].filter[2] = rdr->sa[1][1]; + filters[idx].filter[3] = rdr->sa[1][2]; + filters[idx].filter[4] = rdr->sa[1][3]; + filters[idx].mask[0] = 0xF0; + filters[idx].mask[1] = 0xFF; + filters[idx].mask[2] = 0xFF; + filters[idx].mask[3] = 0xFF; + filters[idx].mask[4] = 0xFF; + idx++; + + *filter_count = idx; + } + + return OK; +} + +static int32_t griffin_card_info(struct s_reader *rdr) +{ + def_resp + int i, r = 0; + rdr_log(rdr, "Reading subscription info."); + + griffin_cmd(GRIFFIN_CMD_SUBSCRIPTION_INFO, NULL, 0, 16); + + if(cta_res[0] == 0x0b) // Old cards + { + for(i = 0; i < cta_lr - 8; i += 9) + { + rdr_log(rdr, " Subscription stream %d - %c%c%c%c%c%c", + r++, cta_res[i + 2], cta_res[i + 3], cta_res[i + 4], + cta_res[i + 5], cta_res[i + 6], cta_res[i + 7]); + } + } + else if(cta_res[0] == 0x1b) // Newer cards + { + for(i = 0; i < cta_lr; i += 4) + { + rdr_log(rdr, " Subscription stream #%02d - 0x%04x", + r++, b2i(2, cta_res + i + 2)); + } + } + + rdr_log(rdr, "End subscription info."); + return OK; +} + +const struct s_cardsystem reader_griffin = +{ + .desc = "griffin", + .caids = (uint16_t[]){ 0x5501, 0x5502, 0x5504, 0x5506, 0x5508, 0x5509, 0x550E, 0x5511, 0x551A, 0 }, + .do_emm = griffin_do_emm, + .do_ecm = griffin_do_ecm, + .card_info = griffin_card_info, + .card_init = griffin_card_init, + .get_emm_type = griffin_get_emm_type, + .get_emm_filter = griffin_get_emm_filter, +}; + +#endif diff --git a/reader-irdeto.c b/reader-irdeto.c new file mode 100644 index 0000000..e292121 --- /dev/null +++ b/reader-irdeto.c @@ -0,0 +1,1404 @@ +#include "globals.h" +#ifdef READER_IRDETO +#include "oscam-time.h" +#include "reader-common.h" +#include "reader-irdeto.h" + +/* some variables for T0 protocol card */ +#define T0EMM 0xD1 +#define T0ECM 0xD5 +#define T0GET 0xD2 +/* end define */ + +typedef struct chid_base_date +{ + uint16_t caid; + uint16_t acs; + char c_code[4]; + uint32_t base; +} CHID_BASE_DATE; + +struct irdeto_data +{ + int32_t t0; // A flag for T0 protocol card + uint16_t acs; + char country_code[3]; // irdeto country code. +}; + +static void XRotateLeft8Byte(uint8_t *buf) +{ + int32_t k; + uint8_t t1 = buf[7]; + uint8_t t2 = 0; + + for(k = 0; k <= 7; k++) + { + t2 = t1; + t1 = buf[k]; + buf[k] = (buf[k] << 1) | (t2 >> 7); + } +} + +static void ReverseSessionKeyCrypt(const uint8_t *camkey, uint8_t *key) +{ + const uint8_t CryptTable[256] = { + 0xDA, 0x26, 0xE8, 0x72, 0x11, 0x52, 0x3E, 0x46, + 0x32, 0xFF, 0x8C, 0x1E, 0xA7, 0xBE, 0x2C, 0x29, + 0x5F, 0x86, 0x7E, 0x75, 0x0A, 0x08, 0xA5, 0x21, + 0x61, 0xFB, 0x7A, 0x58, 0x60, 0xF7, 0x81, 0x4F, + 0xE4, 0xFC, 0xDF, 0xB1, 0xBB, 0x6A, 0x02, 0xB3, + 0x0B, 0x6E, 0x5D, 0x5C, 0xD5, 0xCF, 0xCA, 0x2A, + 0x14, 0xB7, 0x90, 0xF3, 0xD9, 0x37, 0x3A, 0x59, + 0x44, 0x69, 0xC9, 0x78, 0x30, 0x16, 0x39, 0x9A, + 0x0D, 0x05, 0x1F, 0x8B, 0x5E, 0xEE, 0x1B, 0xC4, + 0x76, 0x43, 0xBD, 0xEB, 0x42, 0xEF, 0xF9, 0xD0, + 0x4D, 0xE3, 0xF4, 0x57, 0x56, 0xA3, 0x0F, 0xA6, + 0x50, 0xFD, 0xDE, 0xD2, 0x80, 0x4C, 0xD3, 0xCB, + 0xF8, 0x49, 0x8F, 0x22, 0x71, 0x84, 0x33, 0xE0, + 0x47, 0xC2, 0x93, 0xBC, 0x7C, 0x3B, 0x9C, 0x7D, + 0xEC, 0xC3, 0xF1, 0x89, 0xCE, 0x98, 0xA2, 0xE1, + 0xC1, 0xF2, 0x27, 0x12, 0x01, 0xEA, 0xE5, 0x9B, + 0x25, 0x87, 0x96, 0x7B, 0x34, 0x45, 0xAD, 0xD1, + 0xB5, 0xDB, 0x83, 0x55, 0xB0, 0x9E, 0x19, 0xD7, + 0x17, 0xC6, 0x35, 0xD8, 0xF0, 0xAE, 0xD4, 0x2B, + 0x1D, 0xA0, 0x99, 0x8A, 0x15, 0x00, 0xAF, 0x2D, + 0x09, 0xA8, 0xF5, 0x6C, 0xA1, 0x63, 0x67, 0x51, + 0x3C, 0xB2, 0xC0, 0xED, 0x94, 0x03, 0x6F, 0xBA, + 0x3F, 0x4E, 0x62, 0x92, 0x85, 0xDD, 0xAB, 0xFE, + 0x10, 0x2E, 0x68, 0x65, 0xE7, 0x04, 0xF6, 0x0C, + 0x20, 0x1C, 0xA9, 0x53, 0x40, 0x77, 0x2F, 0xA4, + 0xFA, 0x6D, 0x73, 0x28, 0xE2, 0xCD, 0x79, 0xC8, + 0x97, 0x66, 0x8E, 0x82, 0x74, 0x06, 0xC7, 0x88, + 0x1A, 0x4A, 0x6B, 0xCC, 0x41, 0xE9, 0x9D, 0xB8, + 0x23, 0x9F, 0x3D, 0xBF, 0x8D, 0x95, 0xC5, 0x13, + 0xB9, 0x24, 0x5A, 0xDC, 0x64, 0x18, 0x38, 0x91, + 0x7F, 0x5B, 0x70, 0x54, 0x07, 0xB6, 0x4B, 0x0E, + 0x36, 0xAC, 0x31, 0xE6, 0xD6, 0x48, 0xAA, 0xB4 }; + uint8_t localkey[8], tmp1, tmp2; + int32_t idx1, idx2; + + memcpy(localkey, camkey, 8); + + for(idx1 = 0; idx1 < 8; idx1++) + { + for(idx2 = 0; idx2 < 8; idx2++) + { + tmp1 = CryptTable[key[7] ^ localkey[idx2] ^ idx1]; + tmp2 = key[0]; + key[0] = key[1]; + key[1] = key[2]; + key[2] = key[3]; + key[3] = key[4]; + key[4] = key[5]; + key[5] = key[6] ^ tmp1; + key[6] = key[7]; + key[7] = tmp1 ^ tmp2; + } + XRotateLeft8Byte(localkey); + } +} + +static time_t chid_date(struct s_reader *reader, uint32_t date, char *buf, int32_t l) +{ + // Irdeto date starts 01.08.1997 which is + // 870393600 seconds in unix calendar time + // + // The above might not be true for all Irdeto card + // we need to find a way to identify cards to set the base date + // like we did for NDS + // + // this is the known default value. + + uint32_t date_base; + + if((reader->caid >> 8) == 0x06) + { + date_base = 946598400L; // this is actually 31.12.1999, 00:00 default for irdeto card + } + else + { + date_base = 870393600L; // this is actually 01.08.1997, 00:00 default for betacrypt cards + } + + // CAID, ACS, Country, base date D. M. Y, h : m + CHID_BASE_DATE table[] = { + {0x0616, 0x0608, "ITA", 944110500L}, // 01.12.1999, 23.55 //nitegate + {0x0647, 0x0005, "ITA", 946598400L}, // 31.12.1999, 00:00 //Redlight irdeto + {0x0664, 0x0608, "TUR", 946598400L}, // 31.12.1999, 00:00 + {0x0624, 0x0006, "CZE", 946598400L}, // 30.12.1999, 16:00 //skyklink irdeto + {0x0624, 0x0006, "SVK", 946598400L}, // 30.12.1999, 16:00 //skyklink irdeto + {0x0666, 0x0006, "SVK", 946598400L}, // 30.12.1999, 16:00 //cslink irdeto + {0x0668, 0x0006, "SVK", 946598400L}, // 30.12.1999, 00:00 //Towercom Irdeto + {0x0666, 0x0006, "CZE", 946598400L}, // 30.12.1999, 16:00 //cslink irdeto + {0x0653, 0x0608, "HUN", 946598400L}, // 31.12.1999, 00:00 //upc ice irdeto + {0x0653, 0x0005, "HUN", 946598400L}, // 31.12.1999, 00:00 //upc ice irdeto + {0x0650, 0x0608, "AUT", 946598400L}, // 31.12.1999, 00:00 //orf P410 irdeto + {0x0650, 0x0005, "AUT", 946598400L}, // 31.12.1999, 00:00 //orf P410 irdeto + {0x0648, 0x0608, "AUT", 946598400L}, // 31.12.1999, 00:00 //orf ice irdeto + {0x0648, 0x0005, "AUT", 946598400L}, // 31.12.1999, 00:00 //orf ice irdeto + {0x0627, 0x0608, "EGY", 946598400L}, // 30.12.1999, 16:00 + {0x0602, 0x0606, "NLD", 946598400L}, // 31.12.1999, 08:00 //Ziggo irdeto caid: 0602, acs: 6.06 + {0x0602, 0x0505, "NLD", 946598400L}, // 31.12.1999, 00:00 //Ziggo irdeto caid: 0602, acs: 5.05 + {0x0606, 0x0005, "NLD", 946598400L}, // 31.12.1999, 00:00 //Caiway irdeto card caid: 0606, acs: 0.05 + {0x0606, 0x0605, "NLD", 946598400L}, // 31.12.1999, 00:00 //Caiway irdeto card caid: 0606, acs: 6.05 + {0x0606, 0x0606, "NLD", 946598400L}, // 31.12.1999, 00:00 //Caiway irdeto card caid: 0606, acs: 6.06 + {0x0606, 0x0006, "ZAF", 946598400L}, // 31.12.1999, 00:00 //dstv irdeto + {0x0604, 0x1541, "GRC", 977817600L}, // 26.12.2000, 00:00 + {0x0604, 0x1542, "GRC", 977817600L}, // 26.12.2000, 00:00 + {0x0604, 0x1543, "GRC", 977817600L}, // 26.12.2000, 00:00 + {0x0604, 0x1544, "GRC", 977817600L}, // 26.12.2000, 17:00 + {0x0604, 0x0608, "EGY", 999993600L}, // 08.09.2001, 17:00 + {0x0604, 0x0606, "EGY", 1003276800L}, // 16.10.2001, 17:00 + {0x0604, 0x0605, "GRC", 1011052800L}, // 15.01.2002, 00:00 //nova irdeto + {0x0604, 0x0606, "GRC", 1011052800L}, // 15.01.2002, 00:00 //nova irdeto + {0x0604, 0x0607, "GRC", 1011052800L}, // 15.01.2002, 00:00 //nova irdeto + {0x0604, 0x0608, "GRC", 1011052800L}, // 15.01.2002, 00:00 //nova irdeto + {0x0604, 0x0005, "GRC", 1011052800L}, // 15.01.2002, 00:00 //mova irdeto + {0x0604, 0x0606, "NLD", 1066089600L}, // 14.10.2003, 00:00 + {0x0610, 0x0608, "NLD", 1066089600L}, // 14.10.2003, 00:00 //Ziggo irdeto caid: 0610, acs: 6.08 + {0x0604, 0x0608, "NLD", 1066089600L}, // 14.10.2003, 00:00 //Ziggo irdeto caid: 0604, acs: 6.08 + {0x0604, 0x0605, "NLD", 1066089600L}, // 14.10.2003, 00:00 //Ziggo irdeto caid: 0604, acs: 6.05 + {0x0604, 0x0005, "NLD", 1066089600L}, // 14.10.2003, 00:00 //Ziggo irdeto caid: 0604, acs: 0.05 + {0x0628, 0x0606, "MCR", 1159574400L}, // 29.09.2006, 00:00 + {0x0652, 0x0005, "MCR", 1206662400L}, // 28.03.2008, 00:00 //Raduga caid:0652, acs: 0.05 + {0x0652, 0x0608, "MCR", 1206662400L}, // 28.03.2008, 00:00 //Raduga caid:0652, acs: 6.08 + {0x0, 0x0, "", 0L} + }; + + // now check for specific providers base date + int32_t i = 0; + struct irdeto_data *csystem_data = reader->csystem_data; + + while(table[i].caid) + { + if((reader->caid == table[i].caid) && (csystem_data->acs == table[i].acs) + && (!memcmp(csystem_data->country_code, table[i].c_code, 3))) + { + date_base = table[i].base; + break; + } + i++; + } + + time_t ut = date_base + date * (24 * 3600); + if(buf) + { + struct tm t; + cs_gmtime_r(&ut, &t); + l = 27; + snprintf(buf, l, "%04d/%02d/%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday); + } + return (ut); +} + +static int32_t irdeto_do_cmd(struct s_reader *reader, uint8_t *buf, uint16_t good, uint8_t *cta_res, uint16_t *p_cta_lr) +{ + int32_t rc; + if((rc = reader_cmd2icc(reader, buf, buf[4] + 5, cta_res, p_cta_lr))) + { + return (rc); // result may be 0 (success) or negative + } + + if(*p_cta_lr < 2) + { + return (0x7F7F); // this should never happen + } + + return (good != b2i(2, cta_res + *p_cta_lr - 2)); +} + +#define reader_chk_cmd(cmd, l) { if (reader_cmd2icc(reader, cmd, sizeof(cmd), cta_res, &cta_lr)) return ERROR; if (l && (cta_lr!=l)) return ERROR; } + +static int32_t irdeto_card_init_provider(struct s_reader *reader) +{ + def_resp; + int32_t i, p; + uint8_t buf[256] = {0}; + struct irdeto_data *csystem_data = reader->csystem_data; + + uint8_t sc_T14GetProvider[] = { 0x02, 0x03, 0x03, 0x00, 0x00 }; + uint8_t sc_T0Prov[] = { 0xD2, 0x06, 0x03, 0x00, 0x01, 0x3C }; + uint8_t sc_T0_Cmd[] = { T0GET, 0xFE, 0x00, 0x00, 0x00 }; + + /* + * Provider + */ + memset(reader->prid, 0xff, sizeof(reader->prid)); + + for(buf[0] = i = p = 0; i < reader->nprov; i++) + { + int32_t anspadd = 0; + if(csystem_data->t0 == 1) + { + anspadd = 8; + sc_T0Prov[3] = i; + irdeto_do_cmd(reader, sc_T0Prov, 0x9021, cta_res, &cta_lr); + int32_t anslength = cta_res[cta_lr - 1]; + sc_T0_Cmd[4] = anslength; + reader_chk_cmd(sc_T0_Cmd, anslength + 2); + sc_T0Prov[5]++; + sc_T0_Cmd[3]++; + } + else + { + sc_T14GetProvider[3] = i; + reader_chk_cmd(sc_T14GetProvider, 0); + } + + if(((cta_lr == 26) && ((!(i & 1)) || (cta_res[0] != 0xf))) || (csystem_data->t0 == 1)) + { + reader->prid[i][4] = p++; + + // maps the provider id for Betacrypt from FFFFFF to 000000, + // fixes problems with cascading CCcam and OSCam + if(caid_is_betacrypt(reader->caid)) + { + memset(&reader->prid[i][0], 0, 4); + } + else + { + memcpy(&reader->prid[i][0], cta_res + anspadd, 4); + } + + if(!memcmp(cta_res + anspadd + 1, &reader->hexserial, 3)) + { + reader->prid[i][3] = 0xFF; + } + + snprintf((char *) buf + cs_strlen((char *)buf), sizeof(buf) - cs_strlen((char *)buf), ",%06x", b2i(3, &reader->prid[i][1])); + } + else + { + reader->prid[i][0] = 0xf; + } + } + + if(p) + { + rdr_log_sensitive(reader, "active providers: %d {(%s)}", p, buf + 1); + } + + return OK; +} + +static int32_t irdeto_card_init(struct s_reader *reader, ATR *newatr) +{ + def_resp; + get_atr; + uint8_t buf[256] = { 0 }; + uint8_t sc_T14GetCamKey384DZ[] = { + 0x02, 0x09, 0x03, 0x00, 0x40, + 0x27, 0xF2, 0xD6, 0xCD, 0xE6, 0x88, 0x62, 0x46, + 0x81, 0xB0, 0xF5, 0x3E, 0x6F, 0x13, 0x4D, 0xCC, + 0xFE, 0xD0, 0x67, 0xB1, 0x93, 0xDD, 0xF4, 0xDE, + 0xEF, 0xF5, 0x3B, 0x04, 0x1D, 0xE5, 0xC3, 0xB2, + 0x54, 0x38, 0x57, 0x7E, 0xC8, 0x39, 0x07, 0x2E, + 0xD2, 0xF4, 0x05, 0xAA, 0x15, 0xB5, 0x55, 0x24, + 0x90, 0xBB, 0x9B, 0x00, 0x96, 0xF0, 0xCB, 0xF1, + 0x8A, 0x08, 0x7F, 0x0B, 0xB8, 0x79, 0xC3, 0x5D }; + uint8_t sc_T14GetCamKey383C[] = { + 0x02, 0x09, 0x03, 0x00, 0x40, + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + uint8_t sc_T14GetCountryCode[] = { 0x02, 0x02, 0x03, 0x00, 0x00 }; + uint8_t sc_T14GetASCIISerial[] = { 0x02, 0x00, 0x03, 0x00, 0x00 }; + uint8_t sc_T14GetHEXSerial[] = { 0x02, 0x01, 0x00, 0x00, 0x00 }; + uint8_t sc_T14GetSCDetails[] = { 0x02, 0x1E, 0x00, 0x00, 0x00 }; + uint8_t sc_T14GetCardFile[] = { 0x02, 0x0E, 0x02, 0x00, 0x00 }; + + uint8_t sc_T0CamKey[70] = { 0xD2, 0x12, 0x00, 0x00, 0x41 }; + uint8_t sc_T0Country[] = { 0xD2, 0x04, 0x00, 0x00, 0x01, 0x3E }; + uint8_t sc_T0Ascii[] = { 0xD2, 0x00, 0x03, 0x00, 0x01, 0x3F }; + uint8_t sc_T0Hex[] = { 0xD2, 0x02, 0x03, 0x00, 0x01, 0x3E }; + uint8_t sc_T0SCDetails[] = { 0xD2, 0x3C, 0x00, 0x00, 0x01, 0x22 }; + uint8_t sc_T0CFile[] = { 0xD2, 0x1C, 0x02, 0x00, 0x01, 0x30 }; + uint8_t sc_T0_Cmd[] = { T0GET, 0xFE, 0x00, 0x00, 0x00 }; + + int32_t anspadd = 0; + int32_t t0 = 0; + + if(!memcmp(atr + 4, "IRDETO", 6)) + { + t0 = 0; + } + else + { + if((!memcmp(atr + 5, "IRDETO", 6)) || (((atr[6] == 0xC4) && (atr[9] == 0x8F) && (atr[10] == 0xF1)) && reader->force_irdeto)) + { + t0 = 1; + anspadd = 8; + rdr_log(reader, "Hist. Bytes: %s", atr + 5); + } + else + { + return ERROR; + } + } + + if(!cs_malloc(&reader->csystem_data, sizeof(struct irdeto_data))) + { + return ERROR; + } + struct irdeto_data *csystem_data = reader->csystem_data; + csystem_data->t0 = t0; + + rdr_log(reader, "detect irdeto card"); + if((array_has_nonzero_byte(reader->rsa_mod, 64) > 0) && (!reader->force_irdeto || csystem_data->t0)) // we use rsa from config as camkey + { + char tmp_dbg[65]; + rdr_log_dbg(reader, D_READER, "using camkey data from config"); + rdr_log_dbg(reader, D_READER, " camkey: %s", cs_hexdump(0, reader->boxkey, sizeof(reader->boxkey), tmp_dbg, sizeof(tmp_dbg))); + if(csystem_data->t0 == 1) + { + memcpy(&sc_T0CamKey[5], reader->rsa_mod, 0x40); + rdr_log_dbg(reader, D_READER, "camkey-data: %s", cs_hexdump(0, &sc_T0CamKey[5], 32, tmp_dbg, sizeof(tmp_dbg))); + rdr_log_dbg(reader, D_READER, "camkey-data: %s", cs_hexdump(0, &sc_T0CamKey[37], 32, tmp_dbg, sizeof(tmp_dbg))); + } + else + { + memcpy(&sc_T14GetCamKey383C[5], reader->rsa_mod, 0x40); + rdr_log_dbg(reader, D_READER, "camkey-data: %s", cs_hexdump(0, &sc_T14GetCamKey383C[5], 32, tmp_dbg, sizeof(tmp_dbg))); + rdr_log_dbg(reader, D_READER, "camkey-data: %s", cs_hexdump(0, &sc_T14GetCamKey383C[37], 32, tmp_dbg, sizeof(tmp_dbg))); + } + } + else + { + if(csystem_data->t0 == 1) + { + rdr_log(reader, "WARNING: T0 protocol card can require the CamKey from config"); + } + else + { + memcpy(reader->boxkey, "\x11\x22\x33\x44\x55\x66\x77\x88", 8); + } + } + + /* + * Get Irdeto Smartcard Details - version - patch level etc + */ + if(csystem_data->t0 == 1) + { + irdeto_do_cmd(reader, sc_T0SCDetails, 0x9015, cta_res, &cta_lr); + int32_t anslength = cta_res[cta_lr - 1]; + sc_T0_Cmd[4] = anslength; + reader_chk_cmd(sc_T0_Cmd, anslength + 2); + rdr_log(reader, "Irdeto SC %0x version %0x revision %0x, patch level %0x", cta_res[0 + anspadd], cta_res[1 + anspadd], cta_res[2 + anspadd], cta_res[5 + anspadd]); + } + else + { + if(!irdeto_do_cmd(reader, sc_T14GetSCDetails, 0, cta_res, &cta_lr)) + { + rdr_log(reader, "Irdeto SC %0x version %0x revision %0x, patch level %0x", cta_res[0 + anspadd], cta_res[1 + anspadd], cta_res[2 + anspadd], cta_res[5 + anspadd]); + } + } + + /* + * CountryCode + */ + if(csystem_data->t0 == 1) + { + irdeto_do_cmd(reader, sc_T0Country, 0x9019, cta_res, &cta_lr); + int32_t anslength = cta_res[cta_lr - 1]; + sc_T0_Cmd[4] = anslength; + reader_chk_cmd(sc_T0_Cmd, anslength + 2); + } + else + { + reader_chk_cmd(sc_T14GetCountryCode, 18); + } + csystem_data->acs = (cta_res[0 + anspadd] << 8) | cta_res[1 + anspadd]; + reader->caid = (cta_res[5 + anspadd] << 8) | cta_res[6 + anspadd]; + memcpy(csystem_data->country_code, cta_res + 13 + anspadd, 3); + rdr_log(reader, "caid: %04X, acs: %x.%02x, country code: %c%c%c", + reader->caid, cta_res[0 + anspadd], cta_res[1 + anspadd], cta_res[13 + anspadd], cta_res[14 + anspadd], cta_res[15 + anspadd]); + + /* + * Ascii/Hex-Serial + */ + if(csystem_data->t0 == 1) + { + irdeto_do_cmd(reader, sc_T0Ascii, 0x901D, cta_res, &cta_lr); + int32_t anslength = cta_res[cta_lr - 1]; + sc_T0_Cmd[4] = anslength; + reader_chk_cmd(sc_T0_Cmd, anslength + 2); + } + else + { + reader_chk_cmd(sc_T14GetASCIISerial, 22); + } + memcpy(buf, cta_res + anspadd, 10); + buf[10] = 0; + if(csystem_data->t0 == 1) + { + irdeto_do_cmd(reader, sc_T0Hex, 0x903E, cta_res, &cta_lr); + int32_t anslength = cta_res[cta_lr - 1]; + sc_T0_Cmd[4] = anslength; + reader_chk_cmd(sc_T0_Cmd, anslength + 2); + } + else + { + reader_chk_cmd(sc_T14GetHEXSerial, 18); + } + reader->nprov = cta_res[10 + anspadd]; + memcpy(reader->hexserial, cta_res + 12 + anspadd, 4); + + rdr_log_sensitive(reader, "providers: %d, ascii serial: {%s}, hex serial: {%02X%02X%02X}, hex base: {%02X}", + reader->nprov, buf, reader->hexserial[0], reader->hexserial[1], reader->hexserial[2], reader->hexserial[3]); + + /* + * CardFile + */ + if(csystem_data->t0 == 1) + { + irdeto_do_cmd(reader, sc_T0CFile, 0x9049, cta_res, &cta_lr); + int32_t anslength = cta_res[cta_lr - 1]; + sc_T0_Cmd[4] = anslength; + reader_chk_cmd(sc_T0_Cmd, anslength + 2); + sc_T0CFile[2] = 0x03; + sc_T0CFile[5]++; + irdeto_do_cmd(reader, sc_T0CFile, 0x9049, cta_res, &cta_lr); + anslength = cta_res[cta_lr - 1]; + sc_T0_Cmd[4] = anslength; + sc_T0_Cmd[2] = 0x03; + reader_chk_cmd(sc_T0_Cmd, anslength + 2); + sc_T0_Cmd[2] = 0x00; + } + else + { + for(sc_T14GetCardFile[2] = 2; sc_T14GetCardFile[2] < 4; sc_T14GetCardFile[2]++) + { + reader_chk_cmd(sc_T14GetCardFile, 0); + } + } + + /* + * CamKey + */ + if(csystem_data->t0 == 1) + { + int32_t i, crc = 61; + crc ^= 0x01, crc ^= 0x02, crc ^= 0x09; + crc ^= sc_T0CamKey[2], crc ^= sc_T0CamKey[3], crc ^= (sc_T0CamKey[4] + 1); + + for(i = 5; i < (int)sizeof(sc_T0CamKey) - 1; i++) + { + crc ^= sc_T0CamKey[i]; + } + sc_T0CamKey[69] = crc; + + if(irdeto_do_cmd(reader, sc_T0CamKey, 0x9011, cta_res, &cta_lr)) + { + rdr_log(reader, "You have a bad Cam Key set"); + return ERROR; + } + int32_t anslength = cta_res[cta_lr - 1]; + sc_T0_Cmd[4] = anslength; + reader_chk_cmd(sc_T0_Cmd, anslength + 2); + } + // Dirty hack for Ziggo will be removed when optimum values are find on these T14 cards for v2 and triple + // There are also other readers suffering from simmilar issue for those cards. + else if(((reader->caid == 0x0604) || (reader->caid == 0x1722)) && (csystem_data->t0 == 0) && (reader->typ == R_SMART) && (reader->smart_type >= 2)) + { + // Quick and dirty containment for the SmargoV2, Triple and Ziggo irdeto caid: 0604 using smartreader protocol + // dirty hack ziggo nl card smartreader v2 and triple will be removed after findings optimum T14 values for v2 and triple + // For some reason only 4 to 5 bytes are received, while 8 bytes are expected. + int32_t rc; + if(reader->caid == 0x1722) + { + rc = reader_cmd2icc(reader, sc_T14GetCamKey384DZ, sizeof(sc_T14GetCamKey384DZ), cta_res, &cta_lr); + } + else + { + rc = reader_cmd2icc(reader, sc_T14GetCamKey383C, sizeof(sc_T14GetCamKey383C), cta_res, &cta_lr); + } + rdr_log_dbg(reader, D_READER, "SmargoV2 camkey exchange containment: Ignoring returncode (%d), should have been 0.", rc); + rdr_log_dbg(reader, D_READER, "In case cardinit NOK and/or no entitlements, retry by restarting oscam."); + } // end dirty hack + else + { + if(reader->caid == 0x1722) + { + reader_chk_cmd(sc_T14GetCamKey384DZ, 0); + } + else + { + reader_chk_cmd(sc_T14GetCamKey383C, 0); + } + } + + return irdeto_card_init_provider(reader); +} + +int32_t irdeto_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + def_resp; + cta_lr = 0; // suppress compiler error + static const uint8_t sc_T14EcmCmd[] = { 0x05, 0x00, 0x00, 0x02, 0x00 }; + uint8_t sc_T0Ecm[] = { 0xD5, 0x00, 0x00, 0x02, 0x00 }; + uint8_t sc_T0_Cmd[] = { T0ECM, 0xFE, 0x00, 0x00, 0x00 }; + uint8_t cta_cmd[MAX_ECM_SIZE]; + struct irdeto_data *csystem_data = reader->csystem_data; + + int32_t i = 0, anspadd = 0; + if(csystem_data->t0 == 1) + { + int32_t crc = 63; + anspadd = 8; + sc_T0Ecm[4] = er->ecm[2] - 2; + crc ^= 0x01; + crc ^= 0x05; + crc ^= sc_T0Ecm[2]; + crc ^= sc_T0Ecm[3]; + crc ^= (sc_T0Ecm[4] - 1); + + for(i = 6; i < er->ecm[2] + 4; i++) + { + crc ^= er->ecm[i]; + } + + memcpy(cta_cmd, sc_T0Ecm, sizeof(sc_T0Ecm)); + memcpy(cta_cmd + 5, er->ecm + 6, er->ecm[2] - 1); + cta_cmd[er->ecm[2] + 2] = crc; + + irdeto_do_cmd(reader, cta_cmd, 0, cta_res, &cta_lr); + int32_t anslength = cta_res[cta_lr - 1]; + + sc_T0_Cmd[4] = anslength; + + int32_t try = 1; + int32_t ret; + do + { + if(try > 1) + { + snprintf(ea->msglog, MSGLOGSIZE, "%.22s reader_chk_cmd try nr %i", reader->label, try); + } + + reader_chk_cmd(sc_T0_Cmd, anslength + 2); + if((cta_res[2] == 0x9D) && (cta_res[3] == 0x00)) + { + ret = 0; + } + else + { + ret = 1; + } + ret = ret || (cta_lr == 11); + if(ret) + { + switch(cta_res[2]) + { + case 0x26: // valid for V6 and V7 cards *26 rare case card gets locked if bad EMM being written + { + snprintf(ea->msglog, MSGLOGSIZE, "%.19s cardstatus: LOCKED", reader->label); + return ERROR; + } + + case 0x27: // valid for V6 and V7 cards Time sync EMMs + { + snprintf(ea->msglog, MSGLOGSIZE, "%.23s Time Sync Global EMM needed", reader->label); + return ERROR; + } + + case 0x33: // valid for all cards *33 comes in 2 cases Either Card Requires to be init with Dynamic RSA AKA cmd28/A0 or Pairing Enabled + { + snprintf(ea->msglog, MSGLOGSIZE, "%.26s dynamic RSA init or pairing enabled", reader->label); + return ERROR; + } + + case 0x35: // valid for V6 and V7 cards Time sync EMMs + { + snprintf(ea->msglog, MSGLOGSIZE, "%.23s Time Sync Global EMM needed", reader->label); + return ERROR; + } + + case 0x90: // valid for all cards + { + snprintf(ea->msglog, MSGLOGSIZE,"%.26s unsubscribed channel or chid missing", reader->label); + return ERROR; + } + + case 0x92: // valid for all cards + { + snprintf(ea->msglog, MSGLOGSIZE,"%.22s regional chid missing", reader->label); + return ERROR; + } + + case 0x9E: // valid for all cards *9E comes in 2 cases if card not fully updated OR if pairing Enabled + { + if(cta_res[3] == 0x65) + { + snprintf(ea->msglog, MSGLOGSIZE,"%.24s chipset pairing enabled", reader->label); + return ERROR; + } + else + { + snprintf(ea->msglog, MSGLOGSIZE,"%.11s needs EMMs", reader->label); + return ERROR; + } + } + + case 0xA0: // valid for all cards + { + snprintf(ea->msglog, MSGLOGSIZE,"%.17s surflock enabled", reader->label); + return ERROR; + } + + default: // all other error status + { + snprintf(ea->msglog, MSGLOGSIZE, "%.16s reader_chk_cmd [%d] %02x %02x", reader->label, cta_lr, cta_res[2], cta_res[3]); + break; + } + } + } + try++; + } + while((try < 3) && (ret)); + + if(ret) + { + return ERROR; + } + } + else + { + memcpy(cta_cmd, sc_T14EcmCmd, sizeof(sc_T14EcmCmd)); + cta_cmd[4] = (er->ecm[2]) - 3; + memcpy(cta_cmd + sizeof(sc_T14EcmCmd), &er->ecm[6], cta_cmd[4]); + + int32_t try = 1; + int32_t ret; + do + { + if(try > 1) + { + snprintf(ea->msglog, MSGLOGSIZE, "%.22s irdeto_do_cmd try nr %i", reader->label, try); + } + + ret = (irdeto_do_cmd(reader, cta_cmd, 0x9D00, cta_res, &cta_lr)); + ret = ret || (cta_lr == 2); + if(ret) + { + switch(cta_res[cta_lr - 2]) + { + case 0x26: // valid for V6 and V7 cards *26 rare case card gets locked if bad EMM being written + { + snprintf(ea->msglog, MSGLOGSIZE, "%.19s cardstatus: LOCKED", reader->label); + return ERROR; + } + + case 0x27: // valid for V6 and V7 cards Time sync EMMs + { + snprintf(ea->msglog, MSGLOGSIZE, "%.23s Time Sync Global EMM needed", reader->label); + return ERROR; + } + + case 0x33: // valid for all cards *33 comes in 2 cases Either Card Requires to be init with Dynamic RSA AKA cmd28/A0 or Pairing Enabled + { + snprintf(ea->msglog, MSGLOGSIZE, "%.26s dynamic RSA init or pairing enabled", reader->label); + return ERROR; + } + + case 0x35: // valid for V6 and V7 cards Time sync EMMs + { + snprintf(ea->msglog, MSGLOGSIZE, "%.23s Time Sync Global EMM needed", reader->label); + return ERROR; + } + + case 0x90: // valid for all cards + { + snprintf(ea->msglog, MSGLOGSIZE,"%.26s unsubscribed channel or chid missing", reader->label); + return ERROR; + } + + case 0x92: // valid for all cards + { + snprintf(ea->msglog, MSGLOGSIZE,"%.22s regional chid missing", reader->label); + return ERROR; + } + + case 0x9E: // valid for all cards *9E comes in 2 cases if card not fully updated OR if pairing Enabled + { + if(cta_res[cta_lr - 1] == 0x65) + { + snprintf(ea->msglog, MSGLOGSIZE,"%.24s chipset pairing enabled", reader->label); + return ERROR; + } + else + { + snprintf(ea->msglog, MSGLOGSIZE,"%.11s needs EMMs", reader->label); + return ERROR; + } + } + + case 0xA0: // valid for all cards + { + snprintf(ea->msglog, MSGLOGSIZE,"%.17s surflock enabled", reader->label); + return ERROR; + } + + default: // all other error status + { + snprintf(ea->msglog, MSGLOGSIZE, "%.16s irdeto_do_cmd [%d] %02x %02x", reader->label, cta_lr, cta_res[cta_lr - 2], cta_res[cta_lr - 1]); + break; + } + } + } + try++; + } + while((try < 3) && (ret)); + + if(ret) + { + return ERROR; + } + } + + if((cta_res[3 + anspadd] == 0x36) || (cta_res[3 + anspadd] == 0x37) || (cta_res[3 + anspadd] == 0x24) || (cta_res[3 + anspadd] == 0x25)) + { + snprintf(ea->msglog, MSGLOGSIZE, "cw needs tweaking"); + } + + ReverseSessionKeyCrypt(reader->boxkey, cta_res + 6 + anspadd); + ReverseSessionKeyCrypt(reader->boxkey, cta_res + 14 + anspadd); + memcpy(ea->cw, cta_res + 6 + anspadd, 16); + return OK; +} + +static int32_t irdeto_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + int32_t i, l = (ep->emm[3] & 0x07); + int32_t base = (ep->emm[3] >> 3); + char dumprdrserial[l * 3], dumpemmserial[l * 3]; + + rdr_log_dbg(rdr, D_EMM, "Entered irdeto_get_emm_type ep->emm[3]=%02x", ep->emm[3]); + + switch(l) + { + case 0: + // global emm, 0 bytes addressed + ep->type = GLOBAL; + rdr_log_dbg(rdr, D_EMM, "GLOBAL base = %02x", base); + + if(base & 0x10) // hex serial based? + { + if(base == rdr->hexserial[3]) // does base match? + { + return 1; + } + else + { + return 0; // base doesnt match! + } + } + else + { + return 1; + } // provider based, match all! + + case 2: + // shared emm, 2 bytes addressed + ep->type = SHARED; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, l); +#ifdef WITH_DEBUG + if(cs_dblevel & D_EMM) + { + cs_hexdump(1, rdr->hexserial, l, dumprdrserial, sizeof(dumprdrserial)); + cs_hexdump(1, ep->hexserial, l, dumpemmserial, sizeof(dumpemmserial)); + } +#endif + rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED l = %d ep = {%s} rdr = {%s} base = %02x", + l, dumpemmserial, dumprdrserial, base); + + if(base & 0x10) + { + // hex addressed + return ((base == rdr->hexserial[3]) && (!memcmp(ep->emm + 4, rdr->hexserial, l))); + } + else + { + if(!memcmp(ep->emm + 4, rdr->hexserial, l)) + { + return 1; + } + + // provider addressed + for(i = 0; i < rdr->nprov; i++) + { + if((base == rdr->prid[i][0]) && (!memcmp(ep->emm + 4, &rdr->prid[i][1], l))) + { + return 1; + } + } + } + rdr_log_dbg(rdr, D_EMM, "neither hex nor provider addressed or unknown provider id"); + return 0; + + case 3: + // unique emm, 3 bytes addressed + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, l); + +#ifdef WITH_DEBUG + if(cs_dblevel & D_EMM) + { + cs_hexdump(1, rdr->hexserial, l, dumprdrserial, sizeof(dumprdrserial)); + cs_hexdump(1, ep->hexserial, l, dumpemmserial, sizeof(dumpemmserial)); + rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE l = %d ep = {%s} rdr = {%s} base = %02x", + l, dumpemmserial, dumprdrserial, base); + } +#endif + if(base & 0x10) // unique hex addressed + { + return ((base == rdr->hexserial[3]) && (!memcmp(ep->emm + 4, rdr->hexserial, l))); + } + else + { + if(!memcmp(ep->emm + 4, rdr->hexserial, l)) + { + return 1; + } + + // unique provider addressed + for(i = 0; i < rdr->nprov; i++) + { + if((base == rdr->prid[i][0]) && (!memcmp(ep->emm + 4, &rdr->prid[i][1], l))) + { + return 1; + } + } + } + rdr_log_dbg(rdr, D_EMM, "neither hex nor provider addressed or unknown provider id"); + return 0; + + default: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + return 1; + } +} + +static int32_t irdeto_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 3 + (rdr->nprov * 2); + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + unsigned int idx = 0; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = rdr->hexserial[3] << 3; + filters[idx].mask[1] = 0xFF; + idx++; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFB; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], rdr->hexserial, 3); + memset(&filters[idx].mask[2], 0xFF, 3); + idx++; + + // Shared on Hex Serial only for Betacrypt + if(caid_is_betacrypt(rdr->caid)) + { + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFA; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], rdr->hexserial, 2); + memset(&filters[idx].mask[2], 0xFF, 2); + idx++; + } + + int32_t i; + for(i = 0; i < rdr->nprov; i++) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFB; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], &rdr->prid[i][1], 3); + memset(&filters[idx].mask[2], 0xFF, 3); + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0xFA; + filters[idx].mask[1] = 0x07; + memcpy(&filters[idx].filter[2], &rdr->prid[i][1], 2); + memset(&filters[idx].mask[2], 0xFF, 2); + idx++; + } + + *filter_count = idx; + } + + return OK; +} + +static int32_t irdeto_get_tunemm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 3; + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + unsigned int idx = 0; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].filter[1] = rdr->hexserial[1]; + filters[idx].filter[2] = rdr->hexserial[0]; + filters[idx].filter[3] = 0x10; + filters[idx].filter[4] = 0x00; + filters[idx].filter[5] = 0x10; + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].filter[1] = rdr->hexserial[1]; + filters[idx].filter[2] = rdr->hexserial[0]; + filters[idx].filter[3] = 0x10; + filters[idx].filter[4] = rdr->hexserial[2]; + filters[idx].filter[5] = 0x00; + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + + *filter_count = idx; + } + + return OK; +} + +void irdeto_add_emm_header(EMM_PACKET *ep) +{ + uint8_t bt_emm[MAX_EMM_SIZE]; + static const char *typtext[] = { "unknown", "unique", "shared", "global" }; + memset(bt_emm, 0, sizeof(bt_emm)); + + ep->type = UNKNOWN; + if((ep->emm[0] == 0x83) && (ep->emm[5] == 0x10)) + { + if(ep->emm[7] == 0x00) + { + ep->type = UNIQUE; + } + else + { + ep->type = SHARED; + } + } + else + { + if(ep->emm[0] == 0x82) + { + ep->type = GLOBAL; + } + } + + if((ep->type != UNKNOWN) && (ep->emmlen == 142)) + { + cs_log_dbg(D_EMM, "[TUN_EMM] Type: %s - rewriting header", typtext[ep->type]); + } + else + { + return; + } + + // BETACRYPT/IRDETO EMM HEADER: + static uint8_t headerD0[6] = { 0x82, 0x70, 0x89, 0xd0, 0x01, 0x00 }; // GLOBAL + static uint8_t headerD2[8] = { 0x82, 0x70, 0x8b, 0xd2, 0x00, 0x00, 0x01, 0x00 }; // SHARED + static uint8_t headerD3[9] = { 0x82, 0x70, 0x8c, 0xd3, 0x00, 0x00, 0x00, 0x01, 0x00 }; // UNIQUE + + switch(ep->type) + { + case UNIQUE: + memcpy(bt_emm, headerD3, sizeof(headerD3)); + memcpy(bt_emm + sizeof(headerD3), ep->emm + 8, ep->emmlen - 8); + bt_emm[4] = ep->emm[4]; + bt_emm[5] = ep->emm[3]; + bt_emm[6] = ep->emm[6]; + ep->emmlen = 143; + break; + + case SHARED: + memcpy(bt_emm, headerD2, sizeof(headerD2)); + memcpy(bt_emm + sizeof(headerD2), ep->emm + 8, ep->emmlen - 8); + bt_emm[4] = ep->emm[4]; + bt_emm[5] = ep->emm[3]; + ep->emmlen = 142; + break; + + case GLOBAL: + memcpy(bt_emm, headerD0, sizeof(headerD0)); + memcpy(bt_emm + sizeof(headerD0), ep->emm + 8, ep->emmlen - 8); + ep->emmlen = 140; + break; + } + memcpy(ep->emm, bt_emm, sizeof(bt_emm)); +} + +#define ADDRLEN 4 // Address length in EMM commands + +static int32_t irdeto_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + def_resp; + static const uint8_t sc_T14EmmCmd[] = { 0x01, 0x00, 0x00, 0x00, 0x00 }; + static uint8_t sc_T0Emm[] = { 0xD1, 0x00, 0x00, 0x00, 0x00 }; + uint8_t sc_T0_Cmd[] = { T0EMM, 0xFE, 0x00, 0x00, 0x00 }; + struct irdeto_data *csystem_data = reader->csystem_data; + uint8_t cta_cmd[272]; + + if(ep->emm[0] != 0x82) + { + rdr_log_dbg(reader, D_EMM, "Invalid EMM: Has to start with 0x82, but starts with %02x!", ep->emm[0]); + return ERROR; + } + + int32_t i, l = (ep->emm[3] & 0x07), ok = 0; + int32_t mode = (ep->emm[3] >> 3); + uint8_t *emm = ep->emm; + + if(mode & 0x10) + { + // hex addressed + ok = ((mode == reader->hexserial[3]) && ((!l) || (!memcmp(&emm[4], reader->hexserial, l)))); + } + else + { + ok = !memcmp(&emm[4], reader->hexserial, l); + + // provider addressed + for(i = 0; i < reader->nprov && !ok; i++) + { + ok = ((mode == reader->prid[i][0]) && ((!l) || (!memcmp(&emm[4], &reader->prid[i][1], l)))); + } + } + + if(ok) + { + l++; + if(l <= ADDRLEN) + { + if(csystem_data->t0 == 1) + { + int32_t dataLen = 0; + + if(ep->type == UNIQUE) + { + dataLen = ep->emm[2] - 1; + } + else + { + dataLen = ep->emm[2]; + } + + if((dataLen < 7) || (dataLen > ((int32_t)sizeof(ep->emm) - 6)) || (dataLen > ((int32_t)sizeof(cta_cmd) - 9))) + { + rdr_log_dbg(reader, D_EMM, "dataLen %d seems wrong, faulty EMM?", dataLen); + return ERROR; + } + + if(ep->type == GLOBAL) + { + dataLen += 2; + } + + int32_t crc = 63; + sc_T0Emm[4] = dataLen; + memcpy(&cta_cmd, sc_T0Emm, sizeof(sc_T0Emm)); + crc ^= 0x01; + crc ^= 0x01; + crc ^= 0x00; + crc ^= 0x00; + crc ^= 0x00; + crc ^= (dataLen - 1); + memcpy(&cta_cmd[5], &ep->emm[3], 10); + + if(ep->type == UNIQUE) + { + memcpy(&cta_cmd[9], &ep->emm[9], dataLen - 4); + } + else + { + if(ep->type == GLOBAL) + { + memcpy(&cta_cmd[9], &ep->emm[6], 1); + memcpy(&cta_cmd[10], &ep->emm[7], dataLen - 6); + // cta_cmd[9]=0x00; + } + else + { + memcpy(&cta_cmd[9], &ep->emm[8], dataLen - 4); + } + } + + for(i = 5; i < dataLen + 4; i++) + { + crc ^= cta_cmd[i]; + } + + cta_cmd[dataLen - 1 + 5] = crc; + irdeto_do_cmd(reader, cta_cmd, 0, cta_res, &cta_lr); + int32_t anslength = cta_res[cta_lr - 1]; + sc_T0_Cmd[4] = anslength; + reader_chk_cmd(sc_T0_Cmd, anslength + 2); + + rdr_log_dbg(reader, D_EMM,"response %02X %02X %02X %02X %02X (%s)", + cta_res[0], cta_res[1], cta_res[2], cta_res[3], cta_res[4], + (((cta_res[2] == 0) || (cta_res[2] == 0x7B) || (cta_res[2] == 0x7C)) ? "OK" : "ERROR")); + + if((cta_res[2] == 0x7B) || (cta_res[2] == 0x7C)) // chid already written or chid already up to date + { + return SKIPPED; + } + + if(cta_res[2] == 0x00) + { + return OK; + } + return ERROR; // all other + } + else // T14 protocol based cards + { + const int32_t dataLen = SCT_LEN(emm) - 5 - l; // sizeof of emm bytes (nanos) + + if((dataLen < 1) || (dataLen > ((int32_t)sizeof(ep->emm) - 5 - l)) + || (dataLen > ((int32_t)sizeof(cta_cmd) - (int32_t)sizeof(sc_T14EmmCmd) - ADDRLEN))) + { + rdr_log_dbg(reader, D_EMM, "dataLen %d seems wrong, faulty EMM?", dataLen); + return ERROR; + } + + uint8_t *ptr = cta_cmd; + memcpy(ptr, sc_T14EmmCmd, sizeof(sc_T14EmmCmd)); // copy card command + ptr[4] = dataLen + ADDRLEN; // set card command emm size + ptr += sizeof(sc_T14EmmCmd); + emm += 3; + memset(ptr, 0, ADDRLEN); // clear addr range + memcpy(ptr, emm, l); // copy addr bytes + ptr += ADDRLEN; + emm += l; + memcpy(ptr, &emm[2], dataLen); // copy emm bytes] + irdeto_do_cmd(reader, cta_cmd, 0, cta_res, &cta_lr); + + rdr_log_dbg(reader, D_EMM,"response %02X %02X %02X %02X %02X (%s)", + cta_res[0], cta_res[1], cta_res[2], cta_res[3], cta_res[4], + (((cta_res[cta_lr-2] == 0) || (cta_res[cta_lr-2] == 0x7B) || (cta_res[cta_lr-2] == 0x7C)) ? "OK" : "ERROR")); + + if((cta_res[cta_lr-2] == 0x7B) || (cta_res[cta_lr-2] == 0x7C)) // chid already written or chid already up to date + { + return SKIPPED; + } + + if(cta_res[cta_lr-2] == 0x00) + { + return OK; + } + + return ERROR; // all other + } + } + else + { + rdr_log_dbg(reader, D_EMM, "addrlen %d > %d", l, ADDRLEN); + return ERROR; + } + } + else + { + rdr_log_dbg(reader, D_EMM, "EMM skipped since its hexserial or base doesnt match with this card!"); + return SKIPPED; + } +} + +static int32_t irdeto_card_info(struct s_reader *reader) +{ + def_resp; + int32_t i, p; + struct irdeto_data *csystem_data = reader->csystem_data; + + cs_clear_entitlement(reader); // reset the entitlements + + uint8_t sc_T14GetChannelIds[] = { 0x02, 0x04, 0x00, 0x00, 0x01, 0x00 }; + uint8_t sc_T14GetCountryCode2[] = { 0x02, 0x0B, 0x00, 0x00, 0x00 }; + uint8_t sc_T0Code[] = { 0xD2, 0x16, 0x00, 0x00, 0x01 , 0x37 }; + uint8_t sc_T0Prid[] = { 0xD2, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00 }; + uint8_t sc_T0_Cmd[] = { T0GET, 0xFE, 0x00, 0x00, 0x00 }; + + /* + * ContryCode2 + */ + int32_t anspadd = 0; + if(csystem_data->t0 == 1) + { + anspadd = 8; + reader_chk_cmd(sc_T0Code, 0); + int32_t anslength = cta_res[cta_lr - 1]; + sc_T0_Cmd[4] = anslength; + reader_chk_cmd(sc_T0_Cmd, anslength + 2); + } + else + { + reader_chk_cmd(sc_T14GetCountryCode2, 0); + } + + if(((cta_lr > 9) && !(cta_res[cta_lr - 2] | cta_res[cta_lr - 1])) || (csystem_data->t0 == 1)) + { + rdr_log_dbg(reader, D_READER, "max chids: %d, %d, %d, %d", + cta_res[6 + anspadd], cta_res[7 + anspadd], cta_res[8 + anspadd], cta_res[9 + anspadd]); + + /* + * Provider 2 + */ + for(i = p = 0; i < reader->nprov; i++) + { + int32_t j, k, chid, first = 1; + char t[32]; + + if(reader->prid[i][4] != 0xff) + { + p++; + sc_T0Prid[3] = i; + sc_T14GetChannelIds[3] = i; // provider at index i + j = 0; + + // for (j=0; j<10; j++) => why 10 .. do we know for sure the there are only 10 chids !!! + // shouldn't it me the max chid value we read above ?! + + while(1) // will exit if cta_lr < 61 .. which is the correct break condition. + { + if(csystem_data->t0 == 1) + { + int32_t crc = 63; + sc_T0Prid[5] = j; + crc ^= 0x01; + crc ^= 0x02; + crc ^= 0x04; + crc ^= sc_T0Prid[2]; + crc ^= sc_T0Prid[3]; + crc ^= (sc_T0Prid[4] - 1); + crc ^= sc_T0Prid[5]; + sc_T0Prid[6] = crc; + irdeto_do_cmd(reader, sc_T0Prid, 0x903C, cta_res, &cta_lr); + int32_t anslength = cta_res[cta_lr - 1]; + + if(anslength == 0x09) + { + break; + } + + sc_T0_Cmd[4] = anslength; + reader_chk_cmd(sc_T0_Cmd, anslength + 2); + + if(cta_res[10] == 0xFF) + { + break; + } + + cta_res[cta_lr - 3] = 0xff; + cta_res[cta_lr - 2] = 0xff; + cta_res[cta_lr - 1] = 0xff; + anspadd = 8; + } + else + { + sc_T14GetChannelIds[5] = j; // chid at index j for provider at index i + reader_chk_cmd(sc_T14GetChannelIds, 0); + } + + // if (cta_lr<61) break; // why 61 (0 to 60 in steps of 6 .. is it 10*6 from the 10 in the for loop ? + // what happen if the card only send back.. 9 chids (or less)... we don't see them + // so we should check whether or not we have at least 6 bytes (1 chid). + if(cta_lr < 6) + { + break; + } + + for(k = 0 + anspadd; k < cta_lr; k += 6) + { + chid = b2i(2, cta_res + k); + if(chid && chid != 0xFFFF) + { + time_t date, start_t, end_t; + + start_t = chid_date(reader, date = b2i(2, cta_res + k + 2), t, 16); + end_t = chid_date(reader, date + cta_res[k + 4], t + 16, 16); + + // todo: add entitlements to list but produces a warning related to date variable + cs_add_entitlement(reader, reader->caid, b2i(3, &reader->prid[i][1]), chid, 0, start_t, end_t, 3, 1); + + if(first) + { + rdr_log_sensitive(reader, "entitlements for provider: %d, id: {%06X}", p, b2i(3, &reader->prid[i][1])); + first = 0; + } + rdr_log(reader, "chid: %04X, date: %s - %s", chid, t, t + 16); + } + } + j++; + } + } + } + } + rdr_log(reader, "ready for requests"); + return OK; +} + +const struct s_cardsystem reader_irdeto = +{ + .desc = "irdeto", + .caids = (uint16_t[]){ 0x06, 0x17, 0 }, + .do_emm = irdeto_do_emm, + .do_ecm = irdeto_do_ecm, + .card_info = irdeto_card_info, + .card_init = irdeto_card_init, + .get_emm_type = irdeto_get_emm_type, + .get_emm_filter = irdeto_get_emm_filter, + .get_tunemm_filter = irdeto_get_tunemm_filter, +}; + +#endif diff --git a/reader-irdeto.h b/reader-irdeto.h new file mode 100644 index 0000000..02b9903 --- /dev/null +++ b/reader-irdeto.h @@ -0,0 +1,10 @@ +#ifndef READER_IRDETO_H_ +#define READER_IRDETO_H_ + +#ifdef READER_IRDETO +void irdeto_add_emm_header(EMM_PACKET *ep); +#else +static inline void irdeto_add_emm_header(EMM_PACKET *UNUSED(ep)) { } +#endif + +#endif diff --git a/reader-nagra-common.c b/reader-nagra-common.c new file mode 100644 index 0000000..b0ff145 --- /dev/null +++ b/reader-nagra-common.c @@ -0,0 +1,526 @@ +#include "globals.h" +#include "reader-common.h" +#include "reader-nagra-common.h" + +int32_t get_prov_idx(struct s_reader *rdr, const uint8_t *provid) +{ + int prov; + for(prov = 0; prov < rdr->nprov; prov++) // search for provider index + { + if(!memcmp(provid, &rdr->prid[prov][2], 2)) + { + return (prov); + } + } + return (-1); +} + +int32_t nagra_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ +#ifdef READER_NAGRA_MERLIN + if(rdr->cak7type == 3) + { + int i; + switch(ep->emm[0]) + { + case 0x83: + ep->type = GLOBAL; + + if(rdr->emm83 == 1 && ep->emm[3] == 0x00 && ep->emm[4] == 0x00) + { + return 1; + } + + return 0; + + case 0x84: + ep->type = SHARED; + + for(i = 0; i < rdr->nemm84s; i++) + { + if(!memcmp(rdr->emm84s[i] + 1, ep->emm + 3, 0x05)) + { + return 1; + } + } + return 0; + + case 0x82: + if(ep->emm[3] == 0x00 && ep->emm[4] == 0x00) + { + ep->type = UNIQUE; + + for(i = 0; i < rdr->nemm82u; i++) + { + if(!memcmp(rdr->emm82u[i] + 3, ep->emm + 5, 0x04)) + { + return 1; + } + } + } + return 0; + + case 0x90: + ep->type = UNIQUE; + if(rdr->cwpkcaid_length && rdr->nuid_length) + { + memset(ep->hexserial, 0x00, 0x08); + ep->hexserial[0] = ep->emm[5]; + ep->hexserial[1] = ep->emm[4]; + ep->hexserial[2] = ep->emm[3]; + ep->hexserial[3] = ep->emm[6]; + return (!memcmp(rdr->nuid, ep->hexserial, 4)); + } + return 0; + + default: + ep->type = UNKNOWN; + return 0; + } + } + else if(rdr->cak7type == 1) + { + int i; + switch(ep->emm[0]) + { + case 0x82: + ep->type = GLOBAL; + if(rdr->emm82 == 1 && ep->emm[3] == 0x00 && ep->emm[4] == 0x00 && ep->emm[5] == 0x00) + { + return 1; + } + return 0; + + case 0x83: + if(ep->emm[7] == 0x10) + { + ep->type = SHARED; + + for(i = 0; i < rdr->nemm83s; i++) + { + if(!memcmp(rdr->emm83s[i] + 1, ep->emm + 3, 0x03)) + { + return 1; + } + } + } + else + { + ep->type = UNIQUE; + + for(i = 0; i < rdr->nemm83u; i++) + { + if(!memcmp(rdr->emm83u[i] + 1, ep->emm + 3, 0x04)) + { + return 1; + } + } + } + return 0; + + case 0x84: + ep->type = GLOBAL; + + for(i = 0; i < rdr->nemm84; i++) + { + if(!memcmp(rdr->emm84[i] + 1, ep->emm + 3, 0x02)) + { + return 1; + } + } + return 0; + + case 0x87: + ep->type = SHARED; + + for(i = 0; i < rdr->nemm87; i++) + { + if(!memcmp(rdr->emm87[i] + 1, ep->emm + 3, 0x04)) + { + return 1; + } + } + return 0; + + case 0x90: + ep->type = UNIQUE; + if(rdr->cwpkcaid_length && rdr->nuid_length) + { + memset(ep->hexserial, 0x00, 0x08); + ep->hexserial[0] = ep->emm[5]; + ep->hexserial[1] = ep->emm[4]; + ep->hexserial[2] = ep->emm[3]; + ep->hexserial[3] = ep->emm[6]; + return (!memcmp(rdr->nuid, ep->hexserial, 4)); + } + return 0; + + default: + ep->type = UNKNOWN; + return 0; + } + } + else +#endif + { + int i; + switch(ep->emm[0]) + { + case 0x82: + memset(ep->hexserial, 0x00, 0x08); + ep->hexserial[0] = ep->emm[5]; + ep->hexserial[1] = ep->emm[6]; + ep->hexserial[2] = ep->emm[7]; + ep->hexserial[3] = ep->emm[8]; + if (!memcmp(rdr->hexserial + 2, ep->hexserial, 0x04)) + { + ep->type = UNIQUE; + return 1; + } + else if ((ep->emm[3] == 0x00) && (ep->emm[4] == 0x00) && (ep->emm[5] == 0x00) && (ep->emm[6] == 0x00) && (ep->emm[7] == 0x00) && ((ep->emm[8] == 0x04) || (ep->emm[8] == 0xD3)) && ((ep->emm[9] == 0x84) || (ep->emm[9] == 0x8F) || (ep->emm[9] == 0x87))) + { + ep->type = GLOBAL; + return 1; + } + return 0; + + case 0x84: + memset(ep->hexserial, 0x00, 0x08); + memcpy(ep->hexserial, ep->emm + 5, 3); + i = get_prov_idx(rdr, ep->emm + 3); + if((ep->emm[3] == 0x00 || ep->emm[3] == 0x01) && (ep->emm[5] == 0x00) && (ep->emm[6] == 0x00) && (ep->emm[7] == 0x00) && (ep->emm[8] == 0x04) && (ep->emm[9] == 0x84)) + { + if(i != -1) + { + ep->type = GLOBAL; + return 1; + } + } + if(i != -1) + { + ep->type = SHARED; + return (!memcmp(rdr->sa[i], ep->hexserial, 3)); + } + return 0; + + case 0x83: + memset(ep->hexserial, 0x00, 0x08); + ep->hexserial[0] = ep->emm[5]; + ep->hexserial[1] = ep->emm[4]; + ep->hexserial[2] = ep->emm[3]; + ep->hexserial[3] = ep->emm[6]; + if(ep->emm[7] == 0x10) + { + ep->type = SHARED; + + for(i = 0; i < rdr->nprov; i++) + { + if(!memcmp(rdr->sa[i], "\x00\x00\x00", 3)) + { + continue; + } + + if(!memcmp(rdr->sa[i], ep->hexserial, 0x03)) + { + return 1; + } + } + } + else if (!memcmp(rdr->hexserial + 2, ep->hexserial, 0x04)) + { + ep->type = UNIQUE; + return 1; + } + else if ((ep->emm[5] == 0x04) && (ep->emm[6] == 0x70)) + { + ep->type = GLOBAL; + return 1; + } + return 0; + + case 0x87: + memset(ep->hexserial, 0x00, 0x08); + ep->hexserial[0] = ep->emm[5]; + ep->hexserial[1] = ep->emm[4]; + ep->hexserial[2] = ep->emm[3]; + ep->hexserial[3] = ep->emm[6]; + ep->type = SHARED; + + for(i = 0; i < rdr->nprov; i++) + { + if(!memcmp(rdr->sa[i], "\x00\x00\x00", 3)) + { + continue; + } + if(!memcmp(rdr->sa[i], ep->hexserial, 0x03)) + { + return 1; + } + } + return 0; + + default: + ep->type = UNKNOWN; + return 0; + } + } +} + +int32_t nagra_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ +#ifdef READER_NAGRA_MERLIN + if(rdr->cak7type == 3) + { + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 2 + (2 * rdr->nprov); + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + if(rdr->emm83 == 1) + { + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].mask[0] = 0xFF; + idx++; + } + + int32_t i; + for(i = 0; i < rdr->nemm82u; i++) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + memcpy(&filters[idx].filter[0], rdr->emm82u[i], 7); + memset(&filters[idx].mask[0], 0xFF, 7); + idx++; + } + + for(i = 0; i < rdr->nemm84s; i++) + { + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + memcpy(&filters[idx].filter[0], rdr->emm84s[i], 6); + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + } + + if(rdr->cwpkcaid_length && rdr->nuid_length) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x90; + filters[idx].filter[1] = rdr->nuid[2]; + filters[idx].filter[2] = rdr->nuid[1]; + filters[idx].filter[3] = rdr->nuid[0]; + filters[idx].filter[4] = rdr->nuid[3]; + memset(&filters[idx].mask[0], 0xFF, 5); + idx++; + } + + *filter_count = idx; + } + + return OK; + } + else if(rdr->cak7type == 1) + { + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 2 + (4 * rdr->nprov); + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + if(rdr->emm82 == 1) + { + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + idx++; + } + + int32_t i; + for(i = 0; i < rdr->nemm83u; i++) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + memcpy(&filters[idx].filter[0], rdr->emm83u[i], 6); + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + } + + for(i = 0; i < rdr->nemm83s; i++) + { + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + memcpy(&filters[idx].filter[0], rdr->emm83s[i], 6); + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + } + + for(i = 0; i < rdr->nemm84; i++) + { + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + memcpy(&filters[idx].filter[0], rdr->emm84[i], 3); + memset(&filters[idx].mask[0], 0xFF, 3); + idx++; + } + + for(i = 0; i < rdr->nemm87; i++) + { + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + memcpy(&filters[idx].filter[0], rdr->emm87[i], 6); + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + } + + if(rdr->cwpkcaid_length && rdr->nuid_length) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x90; + filters[idx].filter[1] = rdr->nuid[2]; + filters[idx].filter[2] = rdr->nuid[1]; + filters[idx].filter[3] = rdr->nuid[0]; + filters[idx].filter[4] = rdr->nuid[3]; + memset(&filters[idx].mask[0], 0xFF, 5); + idx++; + } + + *filter_count = idx; + } + + return OK; + } + else +#endif + { + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 4 + (4 * rdr->nprov); + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[1], rdr->hexserial, 6); + memset(&filters[idx].mask[1], 0xFF, 6); + idx++; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].filter[1] = rdr->hexserial[4]; + filters[idx].filter[2] = rdr->hexserial[3]; + filters[idx].filter[3] = rdr->hexserial[2]; + filters[idx].filter[4] = rdr->hexserial[5]; + filters[idx].filter[5] = 0x00; + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0x00; + filters[idx].filter[2] = 0x00; + filters[idx].filter[3] = 0x00; + filters[idx].filter[4] = 0x00; + filters[idx].filter[5] = 0x00; + memset(&filters[idx].mask[1], 0xFF, 5); + idx++; + + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0x00; + filters[idx].filter[2] = 0x00; + memset(&filters[idx].mask[1], 0xFF, 2); + idx++; + + int32_t prov; + for(prov = 0; prov < rdr->nprov; prov++) + { + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x84; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[1], &rdr->prid[prov][2], 2); + memset(&filters[idx].mask[1], 0xFF, 2); + filters[idx].filter[3] = 0x00; + filters[idx].filter[4] = 0x00; + filters[idx].filter[5] = 0x00; + memset(&filters[idx].mask[3], 0xFF, 3); + idx++; + + if(!memcmp(rdr->sa[prov], "\x00\x00\x00", 3)) + { + continue; + } + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x84; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[1], &rdr->prid[prov][2], 2); + memset(&filters[idx].mask[1], 0xFF, 2); + memcpy(&filters[idx].filter[3], &rdr->sa[prov], 3); + memset(&filters[idx].mask[3], 0xFF, 3); + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].filter[1] = rdr->sa[prov][2]; + filters[idx].filter[2] = rdr->sa[prov][1]; + filters[idx].filter[3] = rdr->sa[prov][0]; + filters[idx].filter[4] = 0x00; + filters[idx].filter[5] = 0x10; + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x87; + filters[idx].filter[1] = rdr->sa[prov][2]; + filters[idx].filter[2] = rdr->sa[prov][1]; + filters[idx].filter[3] = rdr->sa[prov][0]; + filters[idx].filter[4] = 0x00; + filters[idx].filter[5] = 0x00; + memset(&filters[idx].mask[0], 0xFF, 6); + idx++; + } + + *filter_count = idx; + } + + return OK; + } +} diff --git a/reader-nagra-common.h b/reader-nagra-common.h new file mode 100644 index 0000000..4077d1f --- /dev/null +++ b/reader-nagra-common.h @@ -0,0 +1,10 @@ +#ifndef READER_NAGRA_COMMON_H_ +#define READER_NAGRA_COMMON_H_ + +#define SYSTEM_NAGRA 0x1800 +#define SYSTEM_MASK 0xFF00 + +int32_t nagra_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr); +int32_t nagra_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count); + +#endif diff --git a/reader-nagra.c b/reader-nagra.c new file mode 100644 index 0000000..7919793 --- /dev/null +++ b/reader-nagra.c @@ -0,0 +1,1558 @@ +#include "globals.h" +#ifdef READER_NAGRA +#include "cscrypt/bn.h" +#include "cscrypt/idea.h" +#include "cscrypt/des.h" +#include "oscam-time.h" +#include "reader-common.h" +#include "reader-nagra-common.h" +#include "oscam-work.h" +#include "oscam-chk.h" + +int8_t ins7e11_state = 0; + +struct nagra_data +{ + IDEA_KEY_SCHEDULE ksSession; + int8_t is_pure_nagra; + int8_t is_tiger; + int8_t is_n3_na; + int8_t has_dt08; + int8_t swapCW; + uint8_t ExpiryDate[2]; + uint8_t ActivationDate[2]; + uint8_t plainDT08RSA[64]; + uint8_t IdeaCamKey[16]; + uint8_t sessi[16]; + uint8_t signature[8]; + uint8_t ird_info; + uint8_t cam_state[3]; +}; + +// Card Status checks +#define HAS_CW() ((csystem_data->cam_state[2]&6)==6) +#define RENEW_SESSIONKEY() ((csystem_data->cam_state[0]&128)==128 || (csystem_data->cam_state[0]&64)==64 || (csystem_data->cam_state[0]&32)==32 || (csystem_data->cam_state[2]&8)==8) +#define SENDDATETIME() (csystem_data->cam_state[0]&8) +// IRD Info +#define CW_NEEDS_3DES() ((csystem_data->ird_info&0x18)==0x18) +// Datatypes +#define DT01 0x01 +#define IRDINFO 0x00 +#define TIERS 0x05 +#define DT06 0x06 +#define CAMDATA 0x08 + +static time_t tier_date(uint32_t date, char *buf, int32_t l) +{ + time_t ut = 870393600L + date * (24 * 3600); + if(buf) + { + struct tm t; + t.tm_isdst = -1; + cs_gmtime_r(&ut, &t); + l = 27; + snprintf(buf, l, "%04d/%02d/%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday); + } + return ut; +} + +static char *nagra_datetime(struct s_reader *rdr, uint8_t *ndays, int32_t offset, char *result, time_t *t) +{ + struct nagra_data *csystem_data = rdr->csystem_data; + struct tm tms; + memset(&tms, 0, sizeof(tms)); + int32_t days = (ndays[0] << 8 | ndays[1]) + offset; + int32_t sec = 0; + + if(!csystem_data->is_tiger) + { + sec = (ndays[2] << 8 | ndays[3]); + } + + if(days > 0x41B4 && sizeof(time_t) < 8) // to overcome 32-bit systems limitations + { + days = 0x41A2; // 01-01-2038 + } + + tms.tm_year = 92; + tms.tm_mday = days + 1; + tms.tm_sec = sec; + time_t ut = mktime(&tms); + + if(t) + { + *t = ut; + } + + if(csystem_data->is_tiger) + { + snprintf(result, 27, "%02d/%02d/%04d", tms.tm_mday, tms.tm_mon + 1, tms.tm_year + 1900); + } + else + { + snprintf(result, 33, "%04d/%02d/%02d %02d:%02d", tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday, tms.tm_hour, tms.tm_min); + } + return result; +} + +static int32_t do_cmd(struct s_reader *reader, uint8_t cmd, int32_t ilen, uint8_t res, int32_t rlen, const uint8_t *data, uint8_t *cta_res, uint16_t *p_cta_lr) +{ + /* + here we build the command related to the protocol T1 for ROM142 or T14 for ROM181 + the only different that i know is the command length byte msg[4], this msg[4]+=1 by a ROM181 smartcard (_nighti_) + one example for the cmd$C0 + T14 protocol: 01 A0 CA 00 00 03 C0 00 06 91 + T1 protocol: 21 00 08 A0 CA 00 00 02 C0 00 06 87 + */ + int32_t msglen = ilen + 6; + uint8_t msg[msglen]; + static const char nagra_head[] = {0xA0, 0xCA, 0x00, 0x00}; + struct nagra_data *csystem_data = reader->csystem_data; + + memset(msg, 0, msglen); + memcpy(msg, nagra_head, 4); + msg[4] = ilen; + msg[5] = cmd; + int32_t dlen = ilen - 2; + msg[6] = dlen; + + if(data && dlen > 0) + { + memcpy(msg + 7, data, dlen); + } + + msg[dlen + 7] = rlen; + if(dlen < 0) + { + rdr_log_dbg(reader, D_READER, "invalid data length encountered"); + return ERROR; + } + + if(csystem_data->is_pure_nagra == 1) + { + msg[4] += 1; + } + + if(!reader_cmd2icc(reader, msg, msglen, cta_res, p_cta_lr)) + { + cs_sleepms(5); + + if(cta_res[0] != res) + { + rdr_log_dbg(reader, D_READER, "result not expected (%02x != %02x)", cta_res[0], res); + return ERROR; + } + + if((*p_cta_lr - 2) != rlen) + { + rdr_log_dbg(reader, D_READER, "result length expected (%d != %d)", (*p_cta_lr - 2), rlen); + return ERROR; + } + return *p_cta_lr; + } + return ERROR; +} + +static void ReverseMem(uint8_t *vIn, int32_t len) +{ + uint8_t temp; + int32_t i; + + for(i = 0; i < (len / 2); i++) + { + temp = vIn[i]; + vIn[i] = vIn[len - i - 1]; + vIn[len - i - 1] = temp; + } +} + +static void Signature(uint8_t *sig, const uint8_t *vkey, const uint8_t *msg, int32_t len) +{ + IDEA_KEY_SCHEDULE ks; + uint8_t v[8]; + uint8_t b200[16]; + uint8_t b0f0[8]; + memcpy(b200, vkey, sizeof(b200)); + int32_t i; + int32_t j; + + for(i = 0; i < len; i += 8) + { + idea_set_encrypt_key(b200, &ks); + memset(v, 0, sizeof(v)); + idea_cbc_encrypt(msg + i, b0f0, 8, &ks, v, IDEA_DECRYPT); + + for(j = 7; j >= 0; j--) + { + b0f0[j] ^= msg[i + j]; + } + memcpy(b200 + 0, b0f0, 8); + memcpy(b200 + 8, b0f0, 8); + } + memcpy(sig, b0f0, 8); + return; +} + +static int32_t CamStateRequest(struct s_reader *reader) +{ + def_resp; + struct nagra_data *csystem_data = reader->csystem_data; + char tmp_dbg[10]; + + if(do_cmd(reader, 0xC0, 0x02, 0xB0, 0x06, NULL, cta_res, &cta_lr)) + { + csystem_data->ird_info = cta_res[2]; + rdr_log_dbg(reader, D_READER, "Irdinfo: %02X", csystem_data->ird_info); + memcpy(csystem_data->cam_state, cta_res + 3, 3); + rdr_log_dbg(reader, D_READER, "Camstate: %s", cs_hexdump(1, csystem_data->cam_state, 3, tmp_dbg, sizeof(tmp_dbg))); + } + else + { + rdr_log_dbg(reader, D_READER, "CamStateRequest failed"); + return ERROR; + } + return OK; +} + +static void DateTimeCMD(struct s_reader *reader) +{ + def_resp; + if(!do_cmd(reader, 0xC8, 0x02, 0xB8, 0x06, NULL, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "DateTimeCMD failed!"); + } + +} + +static int32_t NegotiateSessionKey_Tiger(struct s_reader *reader) +{ + def_resp; + uint8_t exponent = 0x11; + uint8_t parte_fija[120]; + uint8_t parte_variable[88]; + uint8_t d1_rsa_modulo[88]; + uint8_t d2_data[88]; + uint8_t sign1[8]; + uint8_t sk[16]; + uint8_t tmp[104]; + uint8_t idea_key[16]; + uint8_t rnd[88]; + char tmp2[17]; + struct nagra_data *csystem_data = reader->csystem_data; + + if(!do_cmd(reader, 0xd1, 0x02, 0x51, 0xd2, NULL, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "CMD$D1 failed"); + return ERROR; + } + + BN_CTX *ctx = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx); +#endif + BIGNUM *bnN = BN_CTX_get(ctx); + BIGNUM *bnE = BN_CTX_get(ctx); + BIGNUM *bnCT = BN_CTX_get(ctx); + BIGNUM *bnPT = BN_CTX_get(ctx); + BN_bin2bn(reader->rsa_mod, 120, bnN); + BN_bin2bn(&exponent, 1, bnE); + BN_bin2bn(&cta_res[90], 120, bnCT); + BN_mod_exp(bnPT, bnCT, bnE, bnN, ctx); + memset(parte_fija, 0, 120); + BN_bn2bin(bnPT, parte_fija + (120 - BN_num_bytes(bnPT))); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + + rdr_log_dbg(reader, D_READER, "---------- SIG CHECK ---------------------"); + memset(tmp, 0, 104); + memcpy(tmp + 4, parte_fija + 11, 100); + memset(idea_key, 0x37, 16); + Signature(sign1, idea_key, tmp, 104); + rdr_log_dbg(reader, D_READER, "sign1: %s", cs_hexdump(0, sign1, 8, tmp2, sizeof(tmp2))); + rdr_log_dbg(reader, D_READER, "sign2: %s", cs_hexdump(0, parte_fija + 111, 8, tmp2, sizeof(tmp2))); + + if((!memcmp(parte_fija + 111, sign1, 8)) == 0) + { + rdr_log_dbg(reader, D_READER, "signature check nok"); + rdr_log_dbg(reader, D_READER, "------------------------------------------"); + return ERROR; + } + rdr_log_dbg(reader, D_READER, "signature check ok"); + rdr_log_dbg(reader, D_READER, "------------------------------------------"); + + memcpy(reader->hexserial + 2, parte_fija + 15, 4); + memcpy(reader->sa[0], parte_fija + 15, 3); + + memcpy(reader->irdId, parte_fija + 19, 4); + memcpy(d1_rsa_modulo, parte_fija + 23, 88); + + ReverseMem(cta_res + 2, 88); + BN_CTX *ctx1 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx1); +#endif + BIGNUM *bnN1 = BN_CTX_get(ctx1); + BIGNUM *bnE1 = BN_CTX_get(ctx1); + BIGNUM *bnCT1 = BN_CTX_get(ctx1); + BIGNUM *bnPT1 = BN_CTX_get(ctx1); + BN_bin2bn(d1_rsa_modulo, 88, bnN1); + BN_bin2bn(&exponent, 1, bnE1); + BN_bin2bn(cta_res + 2, 88, bnCT1); + BN_mod_exp(bnPT1, bnCT1, bnE1, bnN1, ctx1); + memset(parte_variable, 0, 88); + BN_bn2bin(bnPT1, parte_variable + (88 - BN_num_bytes(bnPT1))); + BN_CTX_end(ctx1); + BN_CTX_free(ctx1); + + csystem_data->ActivationDate[0] = parte_variable[65]; + csystem_data->ActivationDate[1] = parte_variable[66]; + csystem_data->ExpiryDate[0] = parte_variable[69]; + csystem_data->ExpiryDate[1] = parte_variable[70]; + + reader->prid[0][0] = 0x00; + reader->prid[0][1] = 0x00; + reader->prid[0][2] = parte_variable[73]; + reader->prid[0][3] = parte_variable[74]; + reader->caid = (SYSTEM_NAGRA | parte_variable[76]); + memcpy(sk, &parte_variable[79], 8); + memset(sk + 8, 0xBB, 8); + rdr_log_sensitive(reader, "type: NAGRA, caid: %04X, IRD ID: {%s}", reader->caid, cs_hexdump(1, reader->irdId, 4, tmp2, sizeof(tmp2))); + rdr_log(reader, "ProviderID: %s", cs_hexdump(1, reader->prid[0], 4, tmp2, sizeof(tmp2))); + + memcpy(rnd, sk, 8); + memset(&rnd[8], 0xBB, 79); + rnd[87] = 0x6B; + ReverseMem(rnd, 88); + + + BN_CTX *ctx3 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx3); +#endif + BIGNUM *bnN3 = BN_CTX_get(ctx3); + BIGNUM *bnE3 = BN_CTX_get(ctx3); + BIGNUM *bnCT3 = BN_CTX_get(ctx3); + BIGNUM *bnPT3 = BN_CTX_get(ctx3); + BN_bin2bn(d1_rsa_modulo, 88, bnN3); + BN_bin2bn(&exponent, 1, bnE3); + BN_bin2bn(rnd, 88, bnCT3); + BN_mod_exp(bnPT3, bnCT3, bnE3, bnN3, ctx3); + memset(d2_data, 0, 88); + BN_bn2bin(bnPT3, d2_data + (88 - BN_num_bytes(bnPT3))); + BN_CTX_end(ctx3); + BN_CTX_free(ctx3); + ReverseMem(d2_data, 88); + + if(!do_cmd(reader, 0xd2, 0x5a, 0x52, 0x03, d2_data, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "CMD$D2 failed"); + return ERROR; + } + + if(cta_res[2] == 0x00) + { + memcpy(csystem_data->sessi, sk, 16); + IDEA_KEY_SCHEDULE ks; + idea_set_encrypt_key(csystem_data->sessi, &ks); + idea_set_decrypt_key(&ks, &csystem_data->ksSession); + rdr_log_dbg(reader, D_READER, "Tiger session key negotiated"); + return OK; + } + + rdr_log(reader, "Negotiate sessionkey was not successful! Please check tivusat rsa key"); + return ERROR; +} + +static int32_t NegotiateSessionKey(struct s_reader *reader) +{ + def_resp; + uint8_t negot[64]; + uint8_t cmd2b[] = { + 0x21, 0x40, 0x4D, 0xA0, 0xCA, 0x00, 0x00, 0x47, 0x27, 0x45, + 0x1C, 0x54, 0xd1, 0x26, 0xe7, 0xe2, 0x40, 0x20, + 0xd1, 0x66, 0xf4, 0x18, 0x97, 0x9d, 0x5f, 0x16, + 0x8f, 0x7f, 0x7a, 0x55, 0x15, 0x82, 0x31, 0x14, + 0x06, 0x57, 0x1a, 0x3f, 0xf0, 0x75, 0x62, 0x41, + 0xc2, 0x84, 0xda, 0x4c, 0x2e, 0x84, 0xe9, 0x29, + 0x13, 0x81, 0xee, 0xd6, 0xa9, 0xf5, 0xe9, 0xdb, + 0xaf, 0x22, 0x51, 0x3d, 0x44, 0xb3, 0x20, 0x83, + 0xde, 0xcb, 0x5f, 0x35, 0x2b, 0xb0, 0xce, 0x70, + 0x01, 0x02, 0x03, 0x04, //IRD nr + 0x00 };//keynr + + uint8_t cmd2a[] = { + 0x00, + 0xA5, 0xFB, 0x02, 0x76, //NUID + 0x00, 0x08, //OTP-CSC + 0x00, 0x00, //OTA-CSC + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + 0x22, 0x11 }; //Provider ID + + uint8_t tmp[64]; + uint8_t idea1[16]; + uint8_t idea2[16]; + uint8_t sign1[8]; + uint8_t sign2[8]; + struct nagra_data *csystem_data = reader->csystem_data; + + if(csystem_data->is_tiger) + { + if(!NegotiateSessionKey_Tiger(reader)) + { + rdr_log_dbg(reader, D_READER, "NegotiateSessionKey_Tiger failed"); + return ERROR; + } + return OK; + } + + if(!csystem_data->has_dt08) // if we have no valid dt08 calc then we use rsa from config and hexserial for calc of sessionkey + { + rdr_log_dbg(reader, D_READER, "No valid DT08 calc using rsa from config and serial from card"); + memcpy(csystem_data->plainDT08RSA, reader->rsa_mod, 64); + memcpy(csystem_data->signature, reader->boxkey, 8); + } + + if((csystem_data->is_n3_na) && (!do_cmd(reader, 0x29, 0x02, 0xA9, 0x04, NULL, cta_res, &cta_lr))) + { + rdr_log_dbg(reader, D_READER, "Nagra3: CMD$29 failed"); + return ERROR; + } + + memcpy(tmp, reader->irdId, 4); + tmp[4] = 0; //keynr 0 + + if(!csystem_data->is_n3_na) + { + if (reader->cak63nuid_length == 4) //nuid is set + { + // inject provid + cmd2a[26] = reader->prid[0][2]; + cmd2a[27] = reader->prid[0][3]; + + memcpy(&cmd2a[1], reader->cak63nuid, 4); // inject NUID + + if (!do_cmd(reader, 0x2a,0x1E,0xAA,0x42, cmd2a, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "CMD$2A failed"); + return ERROR; + } + } + else + { + if(!do_cmd(reader, 0x2a, 0x02, 0xaa, 0x42, NULL, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "CMD$2A failed"); + return ERROR; + } + + } + } + else if(!do_cmd(reader, 0x26, 0x07, 0xa6, 0x42, tmp, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "CMD$26 failed"); + return ERROR; + } + + // RSA decrypt of cmd$2a data, result is stored in "negot" + ReverseMem(cta_res + 2, 64); + uint8_t vFixed[] = {0, 1, 2, 3}; + BN_CTX *ctx = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx); +#endif + BIGNUM *bnN = BN_CTX_get(ctx); + BIGNUM *bnE = BN_CTX_get(ctx); + BIGNUM *bnCT = BN_CTX_get(ctx); + BIGNUM *bnPT = BN_CTX_get(ctx); + BN_bin2bn(csystem_data->plainDT08RSA, 64, bnN); + BN_bin2bn(vFixed + 3, 1, bnE); + BN_bin2bn(cta_res + 2, 64, bnCT); + BN_mod_exp(bnPT, bnCT, bnE, bnN, ctx); + memset(negot, 0, 64); + BN_bn2bin(bnPT, negot + (64 - BN_num_bytes(bnPT))); + + memcpy(tmp, negot, 64); + ReverseMem(tmp, 64); + + // build sessionkey + // first halve is IDEA Hashed in chuncs of 8 bytes using the Signature1 from dt08 calc, CamID-Inv.CamID(16 bytes key) the results are the First 8 bytes of the Session key + memcpy(idea1, csystem_data->signature, 8); + memcpy(idea1 + 8, reader->hexserial + 2, 4); + idea1[12] = ~reader->hexserial[2]; + idea1[13] = ~reader->hexserial[3]; + idea1[14] = ~reader->hexserial[4]; + idea1[15] = ~reader->hexserial[5]; + + Signature(sign1, idea1, tmp, 32); + memcpy(idea2, sign1, 8); + memcpy(idea2 + 8, sign1, 8); + Signature(sign2, idea2, tmp, 32); + memcpy(csystem_data->sessi, sign1, 8); + memcpy(csystem_data->sessi + 8, sign2, 8); + + // prepare cmd$2b data + BN_bin2bn(negot, 64, bnCT); + BN_mod_exp(bnPT, bnCT, bnE, bnN, ctx); + memset(cmd2b + 10, 0, 64); + BN_bn2bin(bnPT, cmd2b + 10 + (64 - BN_num_bytes(bnPT))); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + ReverseMem(cmd2b + 10, 64); + + IDEA_KEY_SCHEDULE ks; + idea_set_encrypt_key(csystem_data->sessi, &ks); + idea_set_decrypt_key(&ks, &csystem_data->ksSession); + + memcpy(cmd2b + 74, reader->irdId, 4); + cmd2b[78] = 0; //keynr + + if(!csystem_data->is_n3_na) + { + if(!do_cmd(reader, 0x2b, 0x42, 0xab, 0x02, cmd2b + 10, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "CMD$2B failed"); + return ERROR; + } + } + else if(!do_cmd(reader, 0x27, 0x47, 0xa7, 0x02, cmd2b + 10, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "CMD$27 failed"); + return ERROR; + } + + rdr_log_dbg(reader, D_READER, "session key negotiated"); + + DateTimeCMD(reader); + + if(!CamStateRequest(reader)) + { + rdr_log_dbg(reader, D_READER, "CamStateRequest failed"); + return ERROR; + } + + if RENEW_SESSIONKEY() + { + rdr_log(reader, "Negotiate sessionkey was not successful! Please check rsa key and boxkey"); + return ERROR; + } + + return OK; +} + +static void decryptDT08(struct s_reader *reader, uint8_t *cta_res) +{ + uint8_t vFixed[] = {0, 1, 2, 3}; + uint8_t v[72]; + uint8_t buf[72]; + uint8_t sign2[8]; + uint8_t static_dt08[73]; + uint8_t camid[4]; + char tmp_dbg[13]; + int32_t i, n; + BN_CTX *ctx; + BIGNUM *bn_mod, *bn_exp, *bn_data, *bn_res; + struct nagra_data *csystem_data = reader->csystem_data; + + memcpy(static_dt08, &cta_res[12], 73); + // decrypt RSA Part of dt08 + bn_mod = BN_new(); + bn_exp = BN_new(); + bn_data = BN_new(); + bn_res = BN_new(); + ctx = BN_CTX_new(); + + if(ctx == NULL) + { + rdr_log_dbg(reader, D_READER, "RSA Error in dt08 decrypt"); + } + ReverseMem(static_dt08 + 1, 64); + BN_bin2bn(reader->rsa_mod, 64, bn_mod); // rsa modulus + BN_bin2bn(vFixed + 3, 1, bn_exp); // exponent + BN_bin2bn(static_dt08 + 1, 64, bn_data); + BN_mod_exp(bn_res, bn_data, bn_exp, bn_mod, ctx); + memset(static_dt08 + 1, 0, 64); + n = BN_bn2bin(bn_res, static_dt08 + 1); + BN_CTX_free(ctx); + ReverseMem(static_dt08 + 1, n); + + // RSA data can never be bigger than the modulo + static_dt08[64] |= static_dt08[0] & 0x80; + + // IdeaCamKey + memcpy(&csystem_data->IdeaCamKey[0], reader->boxkey, 8); + memcpy(&csystem_data->IdeaCamKey[8], reader->irdId, 4); + + for(i = 0; i < 4; i++) + { + csystem_data->IdeaCamKey[12 + i] = ~reader->irdId[i]; + } + + // now IDEA decrypt + IDEA_KEY_SCHEDULE ks; + idea_set_encrypt_key(csystem_data->IdeaCamKey, &ks); + idea_set_decrypt_key(&ks, &csystem_data->ksSession); + memcpy(&buf[0], static_dt08 + 1, 64); + memcpy(&buf[64], static_dt08 + 65, 8); + memset(v, 0, sizeof(v)); + memset(static_dt08, 0, sizeof(static_dt08)); + idea_cbc_encrypt(buf, static_dt08, 72, &csystem_data->ksSession, v, IDEA_DECRYPT); + + if(csystem_data->swapCW == 1) + { + memset(camid, 0xff, 4); + } + else + { + memcpy(camid, reader->hexserial + 2, 4); + } + rdr_log_dbg(reader, D_READER, "using camid %s for dt08 calc", cs_hexdump(1, camid, 4, tmp_dbg, sizeof(tmp_dbg))); + + // Calculate csystem_data->signature + memcpy(csystem_data->signature, static_dt08, 8); + memset(static_dt08 + 0, 0, 4); + memcpy(static_dt08 + 4, camid, 4); + Signature(sign2, csystem_data->IdeaCamKey, static_dt08, 72); + + if(memcmp(csystem_data->signature, sign2, 8) == 0) + { + csystem_data->has_dt08 = 1; + memcpy(csystem_data->plainDT08RSA, static_dt08 + 8, 64); + rdr_log_dbg(reader, D_READER, "DT08 signature check ok"); + } + else + { + csystem_data->has_dt08 = 0; + rdr_log_dbg(reader, D_READER, "DT08 signature check nok"); + } + + BN_free(bn_mod); + BN_free(bn_exp); + BN_free(bn_data); + BN_free(bn_res); +} + +static void addProvider(struct s_reader *reader, uint8_t *cta_res) +{ + int32_t i; + int32_t toadd = 1; + for(i = 0; i < reader->nprov; i++) + { + if((cta_res[7] == reader->prid[i][2]) && (cta_res[8] == reader->prid[i][3])) + { + toadd = 0; + } + } + + if(toadd) + { + reader->prid[reader->nprov][0] = 0; + reader->prid[reader->nprov][1] = 0; + reader->prid[reader->nprov][2] = cta_res[7]; + reader->prid[reader->nprov][3] = cta_res[8]; + memcpy(reader->sa[reader->nprov], reader->sa[0], 4); + reader->nprov += 1; + } +} + +static int32_t ParseDataType(struct s_reader *reader, uint8_t dt, uint8_t *cta_res, uint16_t cta_lr) +{ + struct nagra_data *csystem_data = reader->csystem_data; + char ds[20], de[16]; + uint16_t chid; + + switch(dt) + { + case IRDINFO: + { + reader->prid[0][0] = 0; + reader->prid[0][1] = 0; + reader->prid[0][2] = cta_res[7]; + reader->prid[0][3] = cta_res[8]; + + // provider 3411, 0401 needs cw swap + if(((cta_res[7] == 0x34) && (cta_res[8] == 0x11)) || ((cta_res[7] == 0x04) && (cta_res[8] == 0x01))) + { + rdr_log_dbg(reader, D_READER, "detect provider with swap cw!"); + csystem_data->swapCW = 1; + } + + reader->prid[1][0] = 0x00; + reader->prid[1][1] = 0x00; + reader->prid[1][2] = 0x00; + reader->prid[1][3] = 0x00; + memcpy(reader->sa[1], reader->sa[0], 4); + reader->nprov += 1; + + reader->caid = (SYSTEM_NAGRA | cta_res[11]); + memcpy(reader->irdId, cta_res + 14, 4); + + // do not output on init but only afterwards in card_info + if(reader->csystem_active) + { + rdr_log_sensitive(reader, "IRD ID: {%s}", cs_hexdump(1, reader->irdId, 4, ds, sizeof(ds))); + nagra_datetime(reader, cta_res + 24, 0, ds, &reader->card_valid_to); + rdr_log(reader, "active to: %s", ds); + } + return OK; + } + + case TIERS: + if((cta_lr > 33) && (chid = b2i(2, cta_res + 11))) + { + int32_t id = (cta_res[7] * 256) | cta_res[8]; + int32_t expire_date1 = b2i(2, cta_res + 13); + int32_t expire_date2 = b2i(2, cta_res + 24); + int32_t sooner_expire_date = expire_date1 <= expire_date2 ? expire_date1 : expire_date2; + // todo: add entitlements to list + cs_add_entitlement(reader, reader->caid, id, chid, 0, tier_date(b2i(2, cta_res + 20) - 0x7f7, ds, 15), tier_date(sooner_expire_date - 0x7f7, de, 15), 4, 1); + rdr_log(reader, "|%04X|%04X |%s |%s |", id, chid, ds, de); + addProvider(reader, cta_res); + } + return OK; + + case 0x08: + case 0x88: + if(cta_res[11] == 0x49) + { + decryptDT08(reader, cta_res); + } + return OK; + + default: + return OK; + } + return ERROR; +} + +static int32_t GetDataType(struct s_reader *reader, uint8_t dt, int32_t len) +{ + def_resp; + int32_t result = OK; + + while(result == OK) + { + if(!do_cmd(reader, 0x22, 0x03, 0xA2, len, &dt, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "failed to get datatype %02X", dt); + result = ERROR; + break; + } + + if((cta_res[2] == 0) && ((dt&0x80) == 0x80)) + { + result = OK; + break; + } + + if(!ParseDataType(reader, dt & 0x0F, cta_res, cta_lr)) + { + result = ERROR; + break; + } + + if(((dt&0x0F) != TIERS) && (cta_res[11] == 0x49) && ((dt&0x80) == 0x80)) + { + result = OK; + break; + } + dt |= 0x80; // get next item + } + return result; +} + +static int32_t nagra2_card_init(struct s_reader *reader, ATR *newatr) +{ + get_atr; + def_resp; + memset(reader->rom, 0, 15); + static const uint8_t ins80[] = { 0x80, 0xCA, 0x00, 0x00, 0x11 }; // switch to nagra layer + static const uint8_t handshake[] = { 0xEE, 0x51, 0xDC, 0xB8, 0x4A, 0x1C, 0x15, 0x05, 0xB5, 0xA6, 0x9B, 0x91, 0xBA, 0x33, 0x19, 0xC4, 0x10 }; // nagra handshake + + int8_t is_pure_nagra = 0; + int8_t is_tiger = 0; + int8_t is_n3_na = 0; + memset(reader->irdId, 0xff, 4); + memset(reader->hexserial, 0, 8); + + cs_clear_entitlement(reader); // reset the entitlements + + if(memcmp(atr + 11, "DNASP240", 8) == 0 || memcmp(atr + 11, "DNASP241", 8) == 0) + { + rdr_log(reader, "detect nagra 3 NA card"); + memcpy(reader->rom, atr + 11, 15); + is_n3_na = 1; + } + else if((memcmp(atr + 11, "DNASP", 5) == 0) && (memcmp(atr + 11, "DNASP4", 6) != 0)) + { + rdr_log(reader, "detect native nagra card"); + memcpy(reader->rom, atr + 11, 15); + } + else if(memcmp(atr + 11, "TIGER", 5) == 0 || (memcmp(atr + 11, "NCMED", 5) == 0)) + { + rdr_log(reader, "detect nagra tiger card"); + memcpy(reader->rom, atr + 11, 15); + is_tiger = 1; + } + else if((!memcmp(atr + 4, "IRDETO", 6)) && ((atr[14] == 0x03) && (atr[15] == 0x84) && (atr[16] == 0x55))) + { + rdr_log(reader, "detect irdeto tunneled nagra card"); + if(!array_has_nonzero_byte(reader->rsa_mod, 64)) + { + rdr_log(reader, "no rsa key configured -> using irdeto mode"); + return ERROR; + } + +#ifdef READER_IRDETO + if(reader->force_irdeto) + { + rdr_log(reader, "rsa key configured but irdeto mode forced -> using irdeto mode"); + return ERROR; + } +#endif + + rdr_log(reader, "rsa key configured -> using nagra mode"); + is_pure_nagra = 1; + + if(!cs_malloc(&reader->csystem_data, sizeof(struct nagra_data))) + { + return ERROR; + } + + struct nagra_data *csystem_data = reader->csystem_data; + csystem_data->is_pure_nagra = is_pure_nagra; + + if(!do_cmd(reader, 0x10, 0x02, 0x90, 0x11, 0, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "get rom version failed"); + return ERROR; + } + memcpy(reader->rom, cta_res + 2, 15); + } + else if( +#ifdef READER_NAGRA_MERLIN + !reader->cak7_mode && +#endif + reader->detect_seca_nagra_tunneled_card && memcmp(atr + 7, "pp", 2) == 0 && ((atr[9]&0x0F) >= 10)) + { + rdr_log(reader, "detect seca/nagra tunneled card"); + + if(!cs_malloc(&reader->csystem_data, sizeof(struct nagra_data))) + { + rdr_log(reader,"mem alloc error"); return ERROR; + } + + if(!card_write(reader, ins80, handshake, cta_res, &cta_lr)) // try to init nagra layer + { + if(cta_res[0] == 0x61 && cta_res[1] == 0x10) + { + reader->seca_nagra_card = 1; + if ((reader->typ == R_SMART || reader->typ == R_INTERNAL || is_smargo_reader(reader)) && !reader->ins7e11_fast_reset) + { + ins7e11_state = 1; + reader->ins7e11_fast_reset = 1; + } + + reader->card_atr_length = 23; + const struct s_cardreader *crdr_ops = reader->crdr; + + if (!crdr_ops) + { + return ERROR; + } + + call(crdr_ops->activate(reader, newatr)); // read nagra atr + get_atr2; + memcpy(reader->rom, atr2 + 8, 15); // get historical bytes containing romrev from nagra atr + rdr_log(reader,"Nagra layer found"); + rdr_log(reader,"Rom revision: %.15s", reader->rom); + reader->card_atr_length = 14; + reader->seca_nagra_card = 2; + call(crdr_ops->activate(reader, newatr)); // read seca atr to switch back + + if ((reader->typ == R_SMART || reader->typ == R_INTERNAL || is_smargo_reader(reader)) && ins7e11_state == 1) + { + ins7e11_state = 0; + reader->ins7e11_fast_reset = 0; + } + } + else + { + rdr_log(reader," Nagra atr not ok"); + return ERROR; + } + } + return ERROR; // quitting csystem still not having needed commands to run on nagra layer + } + else + { + return ERROR; + } + + // Private data may be already allocated, see above (the irdeto check). + if(!reader->csystem_data) + { + if(!cs_malloc(&reader->csystem_data, sizeof(struct nagra_data))) + { + return ERROR; + } + } + + struct nagra_data *csystem_data = reader->csystem_data; + csystem_data->is_pure_nagra = is_pure_nagra; + csystem_data->is_tiger = is_tiger; + csystem_data->is_n3_na = is_n3_na; + + reader->nprov = 1; + + if(!csystem_data->is_tiger) + { + CamStateRequest(reader); + if(!do_cmd(reader, 0x12, 0x02, 0x92, 0x06, 0, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "get serial failed"); + return ERROR; + } + memcpy(reader->hexserial + 2, cta_res + 2, 4); + memcpy(reader->sa[0], cta_res + 2, 3); + + if(!GetDataType(reader, DT01, 0x0E)) + { + return ERROR; + } + rdr_log_dbg(reader, D_READER, "DT01 DONE"); + CamStateRequest(reader); + if(!GetDataType(reader, IRDINFO, 0x39)) + { + return ERROR; + } + rdr_log_dbg(reader, D_READER, "IRDINFO DONE"); + CamStateRequest(reader); + if(!GetDataType(reader, CAMDATA, 0x55)) + { + return ERROR; + } + rdr_log_dbg(reader, D_READER, "CAMDATA Done"); + if(!GetDataType(reader, 0x04, 0x44)) + { + return ERROR; + } + rdr_log_dbg(reader, D_READER, "DT04 DONE"); + CamStateRequest(reader); + if(!GetDataType(reader, DT06, 0x16)) + { + return ERROR; + } + rdr_log_dbg(reader, D_READER, "DT06 DONE"); + CamStateRequest(reader); + } + + if(!NegotiateSessionKey(reader)) + { + rdr_log_dbg(reader, D_READER, "NegotiateSessionKey failed"); + return ERROR; + } + rdr_log(reader, "ready for requests"); + return OK; +} + +typedef struct +{ + char date1[11]; + char date2[11]; + uint8_t type; + uint16_t value; + uint16_t price; +} ncmed_rec; + +static time_t tiger_date2time(const char *date) +{ + struct tm timeinfo; + int32_t y, m, d; + + sscanf(date, "%02d/%02d/%04d", &d, &m, &y); + memset(&timeinfo, 0, sizeof(struct tm)); + timeinfo.tm_year = y - 1900; + timeinfo.tm_mon = m - 1; + timeinfo.tm_mday = d; + + return mktime(&timeinfo); +} + +static int32_t reccmp(const void *r1, const void *r2) +{ + int32_t v1, v2, y, m, d; + sscanf(((ncmed_rec *)r1)->date1, "%02d/%02d/%04d", &d, &m, &y); + v1 = y * 372 + 1 + m * 31 + d; + sscanf(((ncmed_rec *)r2)->date1, "%02d/%02d/%04d", &d, &m, &y); + v2 = y * 372 + 1 + m * 31 + d; + return (v1 == v2) ? 0 : (v1 < v2) ? -1 : 1; +} + +static int32_t reccmp2(const void *r1, const void *r2) +{ + char rec1[13], rec2[13]; + snprintf(rec1, sizeof(rec1), "%04X", ((ncmed_rec *)r1)->value); + memcpy(rec1 + 4, ((ncmed_rec *)r1)->date2 + 6, 4); + memcpy(rec1 + 8, ((ncmed_rec *)r1)->date2 + 3, 2); + memcpy(rec1 + 10, ((ncmed_rec *)r1)->date2, 2); + snprintf(rec2, sizeof(rec2), "%04X", ((ncmed_rec *)r2)->value); + memcpy(rec2 + 4, ((ncmed_rec *)r2)->date2 + 6, 4); + memcpy(rec2 + 8, ((ncmed_rec *)r2)->date2 + 3, 2); + memcpy(rec2 + 10, ((ncmed_rec *)r2)->date2, 2); + rec1[12] = rec2[12] = 0; + return strcmp(rec2, rec1); +} + +static int32_t nagra2_card_info(struct s_reader *reader) +{ + int32_t i; + char currdate[27], tmp[64]; + struct nagra_data *csystem_data = reader->csystem_data; + rdr_log(reader, "ROM: %c %c %c %c %c %c %c %c", reader->rom[0], reader->rom[1], reader->rom[2], reader->rom[3], reader->rom[4], reader->rom[5], reader->rom[6], reader->rom[7]); + rdr_log(reader, "REV: %c %c %c %c %c %c", reader->rom[9], reader->rom[10], reader->rom[11], reader->rom[12], reader->rom[13], reader->rom[14]); + rdr_log_sensitive(reader, "SER: {%s}", cs_hexdump(1, reader->hexserial + 2, 4, tmp, sizeof(tmp))); + rdr_log(reader, "CAID: %04X", reader->caid); + rdr_log(reader, "Prv.ID: %s(sysid)", cs_hexdump(1, reader->prid[0], 4, tmp, sizeof(tmp))); + + for(i = 1; i < reader->nprov; i++) + { + rdr_log(reader, "Prv.ID: %s", cs_hexdump(1, reader->prid[i], 4, tmp, sizeof(tmp))); + } + cs_clear_entitlement(reader); // reset the entitlements + + if(csystem_data->is_tiger) + { + rdr_log(reader, "Activation Date : %s", nagra_datetime(reader, csystem_data->ActivationDate, 0, currdate, 0)); + rdr_log(reader, "Expiry Date : %s", nagra_datetime(reader, csystem_data->ExpiryDate, 0, currdate, &reader->card_valid_to)); + } + + if(reader->nagra_read && csystem_data->is_tiger && (memcmp(reader->rom, "NCMED", 5) == 0 || memcmp(reader->rom, "TIGER", 5) == 0)) + { + ncmed_rec records[255]; + int32_t num_records = 0; + uint8_t tier_cmd1[] = { 0x00, 0x00 }; + uint8_t tier_cmd2[] = { 0x01, 0x00 }; + def_resp; + int32_t j; + do_cmd(reader, 0xD0, 0x04, 0x50, 0x0A, tier_cmd1, cta_res, &cta_lr); + + if(cta_lr == 0x0C) + { + int32_t prepaid = 0; + int32_t credit = 0; + int32_t balance = 0; + + uint16_t credit_in = cta_res[8] << 8 | cta_res[9]; + uint16_t credit_out = cta_res[5] << 8 | cta_res[6]; + balance = (credit_in - credit_out) / 100; + + for(i = 0; i < 13; ++i) + { + tier_cmd2[1] = i; + do_cmd(reader, 0xD0, 0x04, 0x50, 0xAA, tier_cmd2, cta_res, &cta_lr); + if(cta_lr == 0xAC) + { + //rdr_log_dump(reader, cta_res, cta_lr, "NCMED Card Record %d", i+1); + for(j = 2; j < cta_res[1] - 14; ++j) + { + if(cta_res[j] == 0x80 && cta_res[j + 6] != 0x00) + { + int32_t val_offs = 0; + nagra_datetime(reader, &cta_res[j + 6], 0, records[num_records].date2, 0); + + switch(cta_res[j + 1]) + { + case 0x00: + case 0x01: + case 0x20: + case 0x21: + case 0x29: + nagra_datetime(reader, &cta_res[j + 8], 0, records[num_records].date1, 0); + val_offs = 1; + break; + + case 0x80: + nagra_datetime(reader, &cta_res[j + 6], 0, records[num_records].date1, 0); + val_offs = 1; + break; + + default: + rdr_log(reader, "Unknown record : %s", cs_hexdump(1, &cta_res[j], 17, tmp, sizeof(tmp))); + } + + if(val_offs > 0) + { + records[num_records].type = cta_res[j + 1]; + records[num_records].value = cta_res[j + 4] << 8 | cta_res[j + 5]; + records[num_records++].price = cta_res[j + 11] << 8 | cta_res[j + 12]; + } + j += 16; + } + } + } + } + + if(reader->nagra_read == 1) + { + qsort(records, num_records, sizeof(ncmed_rec), reccmp); + } + else + { + qsort(records, num_records, sizeof(ncmed_rec), reccmp2); + } + + int32_t euro = 0; + char tiername[83]; + time_t rawtime; + struct tm timeinfo; + time(&rawtime); + localtime_r(&rawtime, &timeinfo); + snprintf(currdate, sizeof(currdate), "%02d/%02d/%04d", timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900); + + for(i = 0; i < num_records; ++i) + { + switch(records[i].type) + { + case 0x00: + case 0x01: + if(reccmp(records[i].date2, currdate) >= 0) + { + if(reader->nagra_read == 2) + { + rdr_log(reader, "Tier : %04X, expiry date: %s %s", + records[i].value, records[i].date2, get_tiername(records[i].value, reader->caid, tiername)); + } + else if(reader->nagra_read == 1) + { + euro = (records[i].price / 100); + rdr_log(reader, "Activation : ( %04X ) from %s to %s (%3d euro) %s", + records[i].value, records[i].date1, records[i].date2, euro, get_tiername(records[i].value, reader->caid, tiername)); + } + cs_add_entitlement( + reader, + reader->caid, + b2ll(4, reader->prid[0]), + records[i].value, + 0, + tiger_date2time(records[i].date1), + tiger_date2time(records[i].date2)+ 0x1517F, + 4, + 1); + } break; + + case 0x20: + case 0x21: + if(reccmp(records[i].date2, currdate) >= 0) + { + if(reader->nagra_read == 2) + { + rdr_log(reader, "Tier : %04X, expiry date: %s %s", records[i].value, records[i].date2, get_tiername(records[i].value, reader->caid, tiername)); + } + cs_add_entitlement( + reader, + reader->caid, + b2ll(4, reader->prid[0]), + records[i].value, + 0, + tiger_date2time(records[i].date1), + tiger_date2time(records[i].date2)+ 0x1517F, + 4, + 1); + } break; + } + + if(reader->nagra_read == 2) + { + while(i < num_records - 1 && records[i].value == records[i + 1].value) + { + ++i; + } + } + } + + for(i = 0; i < num_records; ++i) + { + switch(records[i].type) + { + case 0x80: + if(reader->nagra_read == 1) + { + euro = (records[i].price / 100) - prepaid; + credit += euro; + prepaid += euro; + if(euro) + { + rdr_log(reader, "Recharge : %s (%3d euro)", records[i].date2, euro); + } + } break; + + case 0x20: + case 0x21: + if(reader->nagra_read == 1) + { + euro = records[i].price / 100; + credit -= euro; + rdr_log(reader, "Subscription : ( %04X ) from %s to %s (%3d euro) %s", + records[i].value, records[i].date1, records[i].date2, euro, get_tiername(records[i].value, reader->caid, tiername)); + } break; + + case 0x29: + euro = records[i].price / 100; + if(reader->nagra_read == 1) { credit -= euro; } + rdr_log(reader, "Event purchase : ( %04X ) from %s to %s (%3d euro)", records[i].value, records[i].date1, records[i].date2, euro); + break; + } + } + + if(reader->nagra_read == 1) + { + rdr_log(reader, "Credit : %3d euro", credit); + } + else + { + rdr_log(reader, "Credit : %3d euro", balance); + } + } + } + else + { + def_resp; + char tmp_dbg[13]; + CamStateRequest(reader); + + if(!do_cmd(reader, 0x12, 0x02, 0x92, 0x06, 0, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "get serial failed"); + return ERROR; + } + + memcpy(reader->hexserial + 2, cta_res + 2, 4); + rdr_log_dbg_sensitive(reader, D_READER, "SER: {%s}", cs_hexdump(1, reader->hexserial + 2, 4, tmp_dbg, sizeof(tmp_dbg))); + memcpy(reader->sa[0], cta_res + 2, 3); + reader->nprov = 1; + + if(!GetDataType(reader, IRDINFO, 0x39)) + { + return ERROR; + } + + rdr_log_dbg(reader, D_READER, "IRDINFO DONE"); + CamStateRequest(reader); + + if((!memcmp(reader->rom + 5, "181", 3)) == 0) // dt05 is not supported by rom181 + { + rdr_log(reader, "-----------------------------------------"); + rdr_log(reader, "|id |tier |valid from |valid to |"); + rdr_log(reader, "+----+--------+------------+------------+"); + + if(!GetDataType(reader, TIERS, 0x57)) + { + return ERROR; + } + + rdr_log(reader, "-----------------------------------------"); + CamStateRequest(reader); + } + } + return OK; +} + +void nagra2_post_process(struct s_reader *reader) +{ + struct nagra_data *csystem_data = reader->csystem_data; + if(!csystem_data->is_tiger) + { + CamStateRequest(reader); + if RENEW_SESSIONKEY() + { + NegotiateSessionKey(reader); + } + + if SENDDATETIME() + { + DateTimeCMD(reader); + } + } +} + +static int32_t nagra2_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + def_resp; + struct nagra_data *csystem_data = reader->csystem_data; + if(!csystem_data->is_tiger) + { + int32_t retry = 0; + if(csystem_data->is_n3_na) + { + uint8_t ecm_pkt[256 + 16]; + memset(ecm_pkt, 0, sizeof(ecm_pkt)); + memcpy(ecm_pkt, er->ecm + 3 + 2, er->ecm[4]); + + while(!do_cmd(reader, er->ecm[3] + 1, er->ecm[4] + 5 + 2, 0x88, 0x04, ecm_pkt, cta_res, &cta_lr)) + { + if(retry == 0) + { + rdr_log_dbg(reader, D_READER, "nagra2_do_ecm (N3_NA) failed, retry"); + } + else + { + rdr_log_dbg(reader, D_READER, "nagra2_do_ecm (N3_NA) failed, retry failed!"); + return ERROR; + } + retry++; + cs_sleepms(10); + } + } + else + { + if(reader->ecmcommand < 5) // cache ecm commands until ecmcommand cache is full + { + reader->ecmcommandcache[reader->ecmcommand] = er->ecm[3]; + reader->ecmcommand++; + + if(reader->ecmcommand == 5) // cache is full, comparing! + { + int32_t t = 0; + int32_t matchfound = 0; + reader->ecmcommand++; // No more caching of ecm commands, next ecms will be compared! + + while(t < 5) + { + if(reader->ecmcommandcache[t] == er->ecm[3]) + { + matchfound++; + } + t++; + } + + if(matchfound != 5) + { + reader->ecmcommand = 0; // reset ecm filter, start a new auto filter attempt + rdr_log_dbg(reader, D_READER, "Auto ecm command filter caid %04X failed!", reader->caid); + } + else + { + reader->ecmcommandcache[0] = er->ecm[3]; // Passed the filter, store the normal ecm command for this reader! + rdr_log_dbg(reader, D_READER, "Auto ecm command filter caid %04X set to command %02X", reader->caid, er->ecm[3]); + } + } + } + else if(reader->ecmcommandcache[0] != er->ecm[3]) + { + rdr_log_dbg(reader, D_READER, "Warning: received an abnominal ecm command %02X for caid: %04X, ignoring!", er->ecm[3], reader->caid); + memset(ea, 0, sizeof(struct s_ecm_answer)); // give it back 00000000 to not disturb the loadbalancer for valid ecm requests on this channel. + return OK; + } + + while(!do_cmd(reader, er->ecm[3], er->ecm[4] + 2, 0x87, 0x02, er->ecm + 3 + 2, cta_res, &cta_lr)) + { + if(retry == 0) + { + rdr_log_dbg(reader, D_READER, "nagra2_do_ecm failed, retry"); + } + else + { + rdr_log_dbg(reader, D_READER, "nagra2_do_ecm failed, retry failed!"); + return ERROR; + } + retry++; + cs_sleepms(10); + } + } + cs_sleepms(10); + + retry = 0; + while(!CamStateRequest(reader) && retry < 3) + { + rdr_log_dbg(reader, D_READER, "CamStateRequest failed, try: %d", retry); + retry++; + cs_sleepms(10); + } + + if(HAS_CW() && (do_cmd(reader, 0x1C, 0x02, 0x9C, 0x36, NULL, cta_res, &cta_lr))) + { + uint8_t v[8]; + memset(v, 0, sizeof(v)); + uint8_t _cwe0[8]; + uint8_t _cwe1[8]; + char tmp_dbg[25]; + + if(csystem_data->swapCW == 1) + { + rdr_log_dbg(reader, D_READER, "swap cws"); + idea_cbc_encrypt(&cta_res[30], &_cwe1[0], 8, &csystem_data->ksSession, v, IDEA_DECRYPT); + memset(v, 0, sizeof(v)); + idea_cbc_encrypt(&cta_res[4], &_cwe0[0], 8, &csystem_data->ksSession, v, IDEA_DECRYPT); + } + else + { + idea_cbc_encrypt(&cta_res[30], &_cwe0[0], 8, &csystem_data->ksSession, v, IDEA_DECRYPT); + memset(v, 0, sizeof(v)); + idea_cbc_encrypt(&cta_res[4], &_cwe1[0], 8, &csystem_data->ksSession, v, IDEA_DECRYPT); + } + rdr_log_dbg(reader, D_READER, "CW0 after IDEA decrypt: %s", cs_hexdump(1, _cwe0, 8, tmp_dbg, sizeof(tmp_dbg))); + rdr_log_dbg(reader, D_READER, "CW1 after IDEA decrypt: %s", cs_hexdump(1, _cwe1, 8, tmp_dbg, sizeof(tmp_dbg))); + + if(CW_NEEDS_3DES()) + { + rdr_log_dbg(reader, D_READER, "3DES encryption of CWs detected. Using CWPK index:%02X", (csystem_data->ird_info & 7)); + + if(reader->cak63cwekey_length != 16) + { + rdr_log_dbg(reader, D_READER, "ERROR: Invalid CWPK, can not decrypt CW"); + return ERROR; + } + + des_ecb3_decrypt(_cwe0, reader->cak63cwekey); + des_ecb3_decrypt(_cwe1, reader->cak63cwekey); + rdr_log_dbg(reader, D_READER, "CW0 after 3DES decrypt: %s", cs_hexdump(1, _cwe0, 8, tmp_dbg, sizeof(tmp_dbg))); + rdr_log_dbg(reader, D_READER, "CW1 after 3DES decrypt: %s", cs_hexdump(1, _cwe1, 8, tmp_dbg, sizeof(tmp_dbg))); + + if (!cfg.disablecrccws && !reader->disablecrccws && !chk_if_ignore_checksum((ECM_REQUEST*) er, &cfg.disablecrccws_only_for) && !chk_if_ignore_checksum((ECM_REQUEST*) er, &reader->disablecrccws_only_for)) + { + int chkok = 1; + if(((_cwe0[0] + _cwe0[1] + _cwe0[2]) & 0xFF) != _cwe0[3]) + { + chkok = 0; + rdr_log_dbg(reader, D_READER, "CW0 checksum error [0]"); + } + + if(((_cwe0[4] + _cwe0[5] + _cwe0[6]) & 0xFF) != _cwe0[7]) + { + chkok = 0; + rdr_log_dbg(reader, D_READER, "CW0 checksum error [1]"); + } + + if(((_cwe1[0] + _cwe1[1] + _cwe1[2]) & 0xFF) != _cwe1[3]) + { + chkok = 0; + rdr_log_dbg(reader, D_READER, "CW1 checksum error [0]"); + } + + if(((_cwe1[4] + _cwe1[5] + _cwe1[6]) & 0xFF) != _cwe1[7]) + { + chkok = 0; + rdr_log_dbg(reader, D_READER, "CW1 checksum error [1]"); + } + + if(chkok == 0) + { + rdr_log_dbg(reader, D_READER, "CW Decrypt failed"); + return ERROR; + } + } + else + { + rdr_log_dbg(reader, D_READER, "checksum test skipped"); + } + } + + memcpy(ea->cw, _cwe0, 0x08); + memcpy(ea->cw + 8, _cwe1, 0x08); + + return OK; + } + } + else + { + // check ECM prov id + if(memcmp(&reader->prid[0][2], er->ecm + 5, 2)) + { + return ERROR; + } + + // ecm_data: 80 30 89 D3 87 54 11 10 DA A6 0F 4B 92 05 34 00 + // serial_data: A0 CA 00 00 8C D3 8A 00 00 00 00 00 10 DA A6 0F + uint8_t ecm_trim[150]; + memset(ecm_trim, 0, 150); + memcpy(&ecm_trim[5], er->ecm + 3 + 2 + 2, er->ecm[4] + 2); + + if(do_cmd(reader, er->ecm[3], er->ecm[4] + 5, 0x53, 0x16, ecm_trim, cta_res, &cta_lr)) + { + if(cta_res[2] == 0x01) + { + uint8_t v[8]; + memset(v, 0, sizeof(v)); + idea_cbc_encrypt(&cta_res[14], ea->cw, 8, &csystem_data->ksSession, v, IDEA_DECRYPT); + memset(v, 0, sizeof(v)); + idea_cbc_encrypt(&cta_res[6], ea->cw + 8, 8, &csystem_data->ksSession, v, IDEA_DECRYPT); + return OK; + } + rdr_log_dbg(reader, D_READER, "can't decode ecm"); + return ERROR; + } + } + return ERROR; +} + +static int32_t nagra2_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + def_resp; + struct nagra_data *csystem_data = reader->csystem_data; + if(!csystem_data->is_tiger) + { + if(!do_cmd(reader, ep->emm[8], ep->emm[9] + 2, 0x84, 0x02, ep->emm + 8 + 2, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "nagra2_do_emm failed"); + return ERROR; + } + + // for slow t14 nagra cards, we must do additional timeout + if(csystem_data->is_pure_nagra == 1) + { + cs_sleepms(300); + } + cs_sleepms(250); + nagra2_post_process(reader); + } + else + { + //check EMM prov id + if(memcmp(&reader->prid[0][2], ep->emm + 10, 2)) + { + rdr_log_dbg(reader, D_READER, "EMM skipped since provider doesnt match!"); + return SKIPPED; + } + + // emm_data: 82 70 8E 00 00 00 00 00 D3 87 8D 11 C0 F4 B1 27 2C 3D 25 94 ... + //serial_data: A0 CA 00 00 8C D3 8A 01 00 00 00 00 C0 F4 B1 27 2C 3D 25 94 ... + uint8_t emm_trim[150] = { 0x01, 0x00, 0x00, 0x00, 0x00 }; + memcpy(&emm_trim[5], ep->emm + 3 + 5 + 2 + 2, ep->emm[9] + 2); + + if(!do_cmd(reader, ep->emm[8], ep->emm[9] + 5, 0x53, 0x16, emm_trim, cta_res, &cta_lr)) + { + rdr_log_dbg(reader, D_READER, "nagra2_do_emm failed"); + return ERROR; + } + cs_sleepms(300); + } + + if(ep->type != GLOBAL) + { + struct timeb now; + cs_ftime(&now); + int64_t gone = comp_timeb(&now, &reader->emm_last); + if(gone > 3600*1000) + { + add_job(reader->client, ACTION_READER_CARDINFO, NULL, 0); // refresh entitlement since it might have been changed! + } + } + return OK; +} + +const struct s_cardsystem reader_nagra = +{ + .desc = "nagra", + .caids = (uint16_t[]){ 0x18, 0 }, + .do_emm = nagra2_do_emm, + .do_ecm = nagra2_do_ecm, + .post_process = nagra2_post_process, + .card_info = nagra2_card_info, + .card_init = nagra2_card_init, + .get_emm_type = nagra_get_emm_type, + .get_emm_filter = nagra_get_emm_filter, +}; + +#endif diff --git a/reader-nagracak7.c b/reader-nagracak7.c new file mode 100644 index 0000000..f195149 --- /dev/null +++ b/reader-nagracak7.c @@ -0,0 +1,1831 @@ +#include "globals.h" +#ifdef READER_NAGRA_MERLIN +#include "cscrypt/bn.h" +#include "cscrypt/idea.h" +#include "csctapi/icc_async.h" +#include "oscam-time.h" +#include "reader-common.h" +#include "reader-nagra-common.h" +#include "oscam-work.h" +#include "cscrypt/des.h" +#include "cscrypt/mdc2.h" + +static uint8_t public_exponent[] = { 0x01, 0x00, 0x01 }; +static const uint8_t d00ff[] = { 0x00, 0xFF, 0xFF, 0xFF }; + +// Datatypes +#define SYSID_CAID 0x02 +#define IRDINFO 0x03 +#define DT05 0x05 +#define TIERS 0x0C + +static time_t tier_date(uint64_t date, char *buf, int32_t l) +{ + time_t ut = +694224000L + (date >> 1); + if(buf) + { + struct tm t; + cs_gmtime_r(&ut, &t); + l = 27; + snprintf(buf, l, "%04d/%02d/%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday); + } + return ut; +} + +static void rsa_decrypt(uint8_t *edata50, int len, uint8_t *out, uint8_t *key, int keylen, uint8_t *expo, uint8_t expolen) +{ + BN_CTX *ctx0 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx0); +#endif + BIGNUM *bnN0 = BN_CTX_get(ctx0); + BIGNUM *bnE0 = BN_CTX_get(ctx0); + BIGNUM *bnCT0 = BN_CTX_get(ctx0); + BIGNUM *bnPT0 = BN_CTX_get(ctx0); + BN_bin2bn(&key[0], keylen, bnN0); + BN_bin2bn(&expo[0], expolen, bnE0); + BN_bin2bn(&edata50[0], len, bnCT0); + BN_mod_exp(bnPT0, bnCT0, bnE0, bnN0, ctx0); + memset(out, 0x00, len); + BN_bn2bin(bnPT0, out + (len - BN_num_bytes(bnPT0))); + BN_CTX_end(ctx0); + BN_CTX_free(ctx0); +} + +static void addProvider(struct s_reader *reader, uint8_t *cta_res) +{ + int i; + bool toadd = true; + for(i = 0; i < reader->nprov; i++) + { + if((cta_res[0] == reader->prid[i][2]) && (cta_res[1] == reader->prid[i][3])) + { + toadd = false; + } + } + if(toadd) + { + reader->prid[reader->nprov][0] = 0; + reader->prid[reader->nprov][1] = 0; + reader->prid[reader->nprov][2] = cta_res[0]; + reader->prid[reader->nprov][3] = cta_res[1]; + reader->nprov += 1; + } +} + +static int32_t get_prov_index(struct s_reader *reader, const uint8_t *provid) +{ + int prov; + for(prov = 0; prov < reader->nprov; prov++) + { + if(!memcmp(provid, &reader->prid[prov][2], 2)) + { + return (prov); + } + } + return (-1); +} + +static void addSA(struct s_reader *reader, uint8_t *cta_res) +{ + if((cta_res[0] == 0x83 && cta_res[5] == 0x10) || cta_res[0] == 0x87) + { + int i; + bool toadd = true; + if(reader->evensa) + { + if(cta_res[0] == 0x87) + { + cta_res[4] = 0x00; + } + unsigned long sax = (cta_res[3] << 16) + (cta_res[2] << 8) + (cta_res[1]); + if(sax % 2 != 0) + { + sax--; + cta_res[3] = (sax >> 16) & 0xFF; + cta_res[2] = (sax >> 8) & 0xFF; + cta_res[1] = (sax ) & 0xFF; + } + } + for(i = 0; i < reader->nsa; i++) + { + if((cta_res[1] == reader->sa[i][2]) && (cta_res[2] == reader->sa[i][1]) && (cta_res[3] == reader->sa[i][0]) && (cta_res[4] == reader->sa[i][3])) + { + toadd = false; + } + } + if(toadd && (memcmp(cta_res + 1, "\x00\x00\x00", 3))) + { + reader->sa[reader->nsa][0] = cta_res[3]; + reader->sa[reader->nsa][1] = cta_res[2]; + reader->sa[reader->nsa][2] = cta_res[1]; + reader->sa[reader->nsa][3] = cta_res[4]; + reader->nsa += 1; + } + } +} + +static void addSAseca(struct s_reader *reader, uint8_t *cta_res) +{ + if(cta_res[0] == 0x84) + { + addProvider(reader, cta_res + 1); + if(reader->evensa) + { + unsigned long sax = (cta_res[3] << 16) + (cta_res[4] << 8) + (cta_res[5]); + if(sax % 2 != 0) + { + sax--; + cta_res[3] = (sax >> 16) & 0xFF; + cta_res[4] = (sax >> 8) & 0xFF; + cta_res[5] = (sax ) & 0xFF; + } + } + if(memcmp(cta_res + 3, "\x00\x00\x00", 3)) + { + int i; + i = get_prov_index(reader, cta_res + 1); + memcpy(reader->sa[i], cta_res + 3, 3); + } + } +} + +static void addemmfilter(struct s_reader *reader, uint8_t *cta_res) +{ + if(cta_res[0] == 0x82) + { + reader->emm82 = 1; + } + else if(cta_res[0] == 0x84) + { + int i; + bool toadd = true; + for(i = 0; i < reader->nemm84; i++) + { + if(!memcmp(cta_res, reader->emm84[i], 3)) + { + toadd = false; + } + } + if(toadd && (memcmp(cta_res + 1, "\x00\x00", 2))) + { + reader->emm84[reader->nemm84][0] = cta_res[0]; + reader->emm84[reader->nemm84][1] = cta_res[1]; + reader->emm84[reader->nemm84][2] = cta_res[2]; + reader->nemm84 += 1; + } + } + else if(cta_res[0] == 0x83 && cta_res[5] == 0x00) + { + int i; + bool toadd = true; + for(i = 0; i < reader->nemm83u; i++) + { + if(!memcmp(cta_res, reader->emm83u[i], 6)) + { + toadd = false; + } + } + if(toadd && (memcmp(cta_res + 1, "\x00\x00\x00\x00", 4))) + { + memcpy(reader->emm83u[reader->nemm83u], cta_res, 6); + reader->nemm83u += 1; + } + } + else if(cta_res[0] == 0x83 && cta_res[5] == 0x10) + { + int i; + bool toadd = true; + if(reader->evensa) + { + unsigned long sax = (cta_res[3] << 16) + (cta_res[2] << 8) + (cta_res[1]); + if(sax % 2 != 0) + { + sax--; + cta_res[3] = (sax >> 16) & 0xFF; + cta_res[2] = (sax >> 8) & 0xFF; + cta_res[1] = (sax ) & 0xFF; + } + } + for(i = 0; i < reader->nemm83s; i++) + { + if(!memcmp(cta_res, reader->emm83s[i], 6)) + { + toadd = false; + } + } + if(toadd && (memcmp(cta_res + 1, "\x00\x00\x00", 3))) + { + memcpy(reader->emm83s[reader->nemm83s], cta_res, 6); + reader->nemm83s += 1; + } + } + else if(cta_res[0] == 0x87) + { + int i; + bool toadd = true; + if(reader->evensa) + { + cta_res[4] = 0x00; + unsigned long sax = (cta_res[3] << 16) + (cta_res[2] << 8) + (cta_res[1]); + if(sax % 2 != 0) + { + sax--; + cta_res[3] = (sax >> 16) & 0xFF; + cta_res[2] = (sax >> 8) & 0xFF; + cta_res[1] = (sax ) & 0xFF; + } + } + for(i = 0; i < reader->nemm87; i++) + { + if(!memcmp(cta_res, reader->emm87[i], 6)) + { + toadd = false; + } + } + if(toadd && (memcmp(cta_res + 1, "\x00\x00\x00", 3))) + { + memcpy(reader->emm87[reader->nemm87], cta_res, 6); + reader->nemm87 += 1; + } + } +} + +static void addemmfilterseca(struct s_reader *reader, uint8_t *cta_res) +{ + if(cta_res[0] == 0x83) + { + reader->emm83 = 1; + } + else if(cta_res[0] == 0x82 && cta_res[1] == 0x00 && cta_res[2] == 0x00) + { + int i; + bool toadd = true; + for(i = 0; i < reader->nemm82u; i++) + { + if(!memcmp(cta_res, reader->emm82u[i], 7)) + { + toadd = false; + } + } + if(toadd && (memcmp(cta_res + 3, "\x00\x00\x00\x00", 4))) + { + memcpy(reader->emm82u[reader->nemm82u], cta_res, 7); + reader->nemm82u += 1; + } + } + else if(cta_res[0] == 0x84) + { + int i; + bool toadd = true; + if(reader->evensa) + { + unsigned long sax = (cta_res[3] << 16) + (cta_res[4] << 8) + (cta_res[5]); + if(sax % 2 != 0) + { + sax--; + cta_res[3] = (sax >> 16) & 0xFF; + cta_res[4] = (sax >> 8) & 0xFF; + cta_res[5] = (sax ) & 0xFF; + } + } + for(i = 0; i < reader->nemm84s; i++) + { + if(!memcmp(cta_res, reader->emm84s[i], 6)) + { + toadd = false; + } + } + if(toadd && (memcmp(cta_res + 3, "\x00\x00\x00", 3))) + { + memcpy(reader->emm84s[reader->nemm84s], cta_res, 6); + reader->nemm84s += 1; + } + } +} + +static int32_t ParseDataType(struct s_reader *reader, uint8_t dt, uint8_t *cta_res, uint16_t cta_lr) +{ + char ds[27], de[27]; + + switch(dt) + { + case SYSID_CAID: + { + reader->prid[0][0] = 0x00; + reader->prid[0][1] = 0x00; + reader->prid[0][2] = cta_res[19]; + reader->prid[0][3] = cta_res[20]; + reader->prid[1][0] = 0x00; + reader->prid[1][1] = 0x00; + reader->prid[1][2] = 0x00; + reader->prid[1][3] = 0x00; + reader->nprov += 1; + reader->caid = (SYSTEM_NAGRA | cta_res[25]); + reader->cak7_emm_caid = (SYSTEM_NAGRA | cta_res[25]); + rdr_log_dbg(reader, D_READER, "EMM CAID : %04X", reader->cak7_emm_caid); + return OK; + } + + case IRDINFO: // case 0x03 + { + uint16_t chid = 0; + uint32_t id = b2i(0x02, cta_res + 19); + uint32_t start_date = 1; + uint32_t expire_date = 0; + + if(cta_res[21] == 0x9C) + { + if((reader->caid == 0x186D) || (reader->caid == 0x187D)) + { + const uint8_t timestamp2038[4] = {0xAD, 0x0E, 0x26, 0x01}; + expire_date = b2i(0x04, timestamp2038); + } + else + { + expire_date = b2i(0x04, cta_res + 22); + } + } + + if((cta_res[21] == 0x01) && (reader->protocol_type == ATR_PROTOCOL_TYPE_T0 || + ((reader->caid == 0x1856 || reader->caid == 0x187D) && cta_res[8] != 0x18))) + { + expire_date = b2i(0x04, cta_res + 22); + } + + if (expire_date) + { + reader->card_valid_to = tier_date(expire_date, de, 11); + cs_add_entitlement(reader, reader->caid, id, chid, 0, tier_date(start_date, ds, 11), tier_date(expire_date, de, 11), 4, 1); + rdr_log(reader, "|%04X|%04X |%s |%s |", id, chid, ds, de); + addProvider(reader, cta_res + 19); + } + + return OK; + } + + case 0x04: + { + if(cta_res[18] != 0x80) + { + addProvider(reader, cta_res + 19); + uint8_t check[] = {0x00, 0x01}; + uint8_t checkecmcaid[] = {0xFF, 0x07}; + uint8_t emm84g_provid[2]; + check[0] = reader->cak7_emm_caid & 0xFF; + + int p; + for(p = 23; p < (cta_lr - 6); p++) + { + if(!memcmp(cta_res + p, check, 2)) + { + addProvider(reader, cta_res + p + 2); + if(reader->cak7type == 3) + { + addSAseca(reader, cta_res + p + 5); + addemmfilterseca(reader, cta_res + p + 5); + } + else + { + if ((reader->cak7_emm_caid == 0x186A) && ((cta_res + p + 5)[0] == 0x84) && ((cta_res + p + 5)[1] == 0x00)) + { + (cta_res + p + 5)[2] = 0xAC; + } + if ((cta_res + p + 5)[0] == 0x84) + { + emm84g_provid[0] = (cta_res + p + 5)[1] & (cta_res + p + 5)[4]; + emm84g_provid[1] = (cta_res + p + 5)[2] & (cta_res + p + 5)[5]; + addProvider(reader, emm84g_provid); + } + addSA(reader, cta_res + p + 5); + addemmfilter(reader, cta_res + p + 5); + } + } + if((!memcmp(cta_res + p, checkecmcaid, 2)) && ((cta_res + p + 2)[1] == 0x02)) + { + reader->caid = (SYSTEM_NAGRA | (cta_res + p + 2)[0]); + rdr_log_dbg(reader, D_READER, "ECM CAID : %04X", reader->caid); + } + } + } + return OK; + } + + case 0x09: + { + if((cta_res[19] == cta_res[23]) && (cta_res[20] == cta_res[24])) + { + addProvider(reader, cta_res + 19); + } + return OK; + } + + case DT05: // case 0x05 + { + memcpy(reader->edata,cta_res + 26, 0x70); + reader->dt5num = cta_res[20]; + char tmp[8]; + rdr_log(reader, "Card has DT05_%s", cs_hexdump(1, &reader->dt5num, 1, tmp, sizeof(tmp))); + IDEA_KEY_SCHEDULE ks; + if(reader->dt5num == 0x00) + { + rsa_decrypt(reader->edata, 0x70, reader->out, reader->mod1, reader->mod1_length, public_exponent, 3); + memcpy(reader->kdt05_00, &reader->out[18], 0x5C + 2); + memcpy(&reader->kdt05_00[0x5C + 2], cta_res + 26 + 0x70, 6); + memcpy(reader->ideakey1, reader->out, 16); + rdr_log_dump_dbg(reader, D_READER, reader->ideakey1, 16, "IDEAKEY1: "); + memcpy(reader->block3, cta_res + 26 + 0x70 + 6, 8); + idea_set_encrypt_key(reader->ideakey1, &ks); + memset(reader->v, 0, sizeof(reader->v)); + idea_cbc_encrypt(reader->block3, reader->iout, 8, &ks, reader->v, IDEA_DECRYPT); + memcpy(&reader->kdt05_00[0x5C + 2 + 6], reader->iout, 8); + + uint8_t mdc_hash1[MDC2_DIGEST_LENGTH]; + uint8_t check1[0x7E]; + memset(check1, 0x00, 0x7E); + memcpy(check1 + 18, reader->kdt05_00, 0x6C); + MDC2_CTX c1; + MDC2_Init(&c1); + MDC2_Update(&c1, check1, 0x7E); + MDC2_Final(&(mdc_hash1[0]), &c1); + rdr_log_dump_dbg(reader, D_READER, mdc_hash1, 16, "MDC_HASH: "); + + if(memcmp(mdc_hash1 + 1, reader->ideakey1 + 1, 14) == 0) + { + rdr_log(reader, "DT05_00 is correct"); + } + else + { + rdr_log(reader, "DT05_00 error - check MOD1"); + } + + rdr_log_dump_dbg(reader, D_READER, reader->kdt05_00, sizeof(reader->kdt05_00), "DT05_00: "); + } + + if(reader->dt5num == 0x10) + { + rsa_decrypt(reader->edata, 0x70, reader->out, reader->mod1, reader->mod1_length, public_exponent, 3); + memcpy(reader->kdt05_10, &reader->out[16], 6 * 16); + memcpy(reader->ideakey1, reader->out, 16); + memcpy(reader->block3, cta_res + 26 + 0x70, 8); + idea_set_encrypt_key(reader->ideakey1, &ks); + memset(reader->v, 0, sizeof(reader->v)); + idea_cbc_encrypt(reader->block3, reader->iout, 8, &ks, reader->v, IDEA_DECRYPT); + memcpy(&reader->kdt05_10[6 * 16], reader->iout, 8); + rdr_log_dump_dbg(reader, D_READER, reader->kdt05_10, sizeof(reader->kdt05_10), "DT05_10: "); + } + + if(reader->dt5num == 0x20) + { + rsa_decrypt(reader->edata, 0x70, reader->out, reader->mod2, reader->mod2_length, public_exponent, 3); + memcpy(reader->tmprsa, reader->out, 0x70); + reader->hasunique = 1; + } + + return OK; + } + + case TIERS: // case 0x0C + { + if(cta_lr >= 0x30) + { + uint16_t chid = b2i(0x02, cta_res + 23); + uint32_t id = b2i(0x02, cta_res + 19); + uint32_t start_date = 0; + uint32_t expire_date1; + uint32_t expire_date2; + uint32_t expire_date = 0; + + switch(reader->caid) + { + case 0x1830: // Max TV + case 0x1843: // HD02 + case 0x1860: // HD03 + start_date = b2i(0x04, cta_res + 42); + expire_date1 = b2i(0x04, cta_res + 28); + expire_date2 = b2i(0x04, cta_res + 46); + expire_date = expire_date1 <= expire_date2 ? expire_date1 : expire_date2; + break; + + case 0x1861: // Polsat, Vodafone D08, Movistar Latin America + { + if(!memcmp(reader->rom, "\x44\x4E\x41\x53\x50\x34\x35\x30\x20\x52\x65\x76\x57\x36\x30", 15)) + { + start_date = b2i(0x04, cta_res + 53); + expire_date1 = b2i(0x04, cta_res + 39); + expire_date2 = b2i(0x04, cta_res + 57); + expire_date = expire_date1 <= expire_date2 ? expire_date1 : expire_date2; + } + else if(!memcmp(reader->rom, "\x44\x4E\x41\x53\x50\x34\x31\x30\x20\x52\x65\x76\x51\x32\x31", 15)) + { + start_date = b2i(0x04, cta_res + 42); + expire_date = b2i(0x04, cta_res + 28); + } + else if(!memcmp(reader->rom, "\x44\x4E\x41\x53\x50\x34\x31\x30\x20\x52\x65\x76\x51\x32\x53", 15)) + { + start_date = 1; + expire_date = b2i(0x04, cta_res + 28); + } + else + { + start_date = b2i(0x04, cta_res + 42); + expire_date1 = b2i(0x04, cta_res + 28); + expire_date2 = b2i(0x04, cta_res + 46); + expire_date = expire_date1 <= expire_date2 ? expire_date1 : expire_date2; + } + break; + } + + case 0x186A: // HD04, HD05 + start_date = b2i(0x04, cta_res + 53); + expire_date1 = b2i(0x04, cta_res + 39); + expire_date2 = b2i(0x04, cta_res + 57); + expire_date = expire_date1 <= expire_date2 ? expire_date1 : expire_date2; + break; + + case 0x186D: // HD04H + case 0x187D: // Canal+ reunion + case 0x1884: // nc+ + { + if(cta_res[8] == 0x45) + { + start_date = b2i(0x04, cta_res + 47); + expire_date1 = b2i(0x04, cta_res + 39); + expire_date2 = b2i(0x04, cta_res + 51); + expire_date = expire_date1 <= expire_date2 ? expire_date1 : expire_date2; + } + break; + } + + case 0x1812: + case 0x1814: // MEO + case 0x1817: // Canal Digitaal + case 0x1818: // TV Vlaanderen + case 0x1819: // Telesat + { + if(cta_res[8] > 0x28) + { + start_date = b2i(0x04, cta_res + 32); + expire_date = b2i(0x04, cta_res + 36); + } + if(cta_res[8] == 0x20) + { + start_date = b2i(0x04, cta_res + 28); + expire_date = b2i(0x04, cta_res + 32); + } + break; + } + + case 0x1824: + start_date = 1; + expire_date = b2i(0x04, cta_res + 28); + break; + + default: // unknown card + start_date = 1; + expire_date = 0xAD0E2601; + } + + cs_add_entitlement(reader, reader->caid, id, chid, 0, tier_date(start_date, ds, 11), tier_date(expire_date, de, 11), 4, 1); + rdr_log(reader, "|%04X|%04X |%s |%s |", id, chid, ds, de); + addProvider(reader, cta_res + 19); + } + return OK; + } + + default: + return OK; + } + + return ERROR; +} + +static int32_t CAK7do_cmd(struct s_reader *reader, uint8_t dt, uint8_t *res, uint16_t *rlen, int32_t sub, uint8_t retlen) +{ + uint8_t dtdata[0x10]; + memset(dtdata, 0xCC, 0x10); + dtdata[ 7] = 0x04; + dtdata[ 8] = 0x04; + dtdata[ 9] = (sub >> 16) & 0xFF; + dtdata[10] = (sub >> 8) & 0xFF; + dtdata[11] = (sub ) & 0xFF; + dtdata[12] = dt; + + do_cak7_cmd(reader, res, rlen, dtdata, sizeof(dtdata), retlen); + + return OK; +} + +static int32_t CAK7GetDataType(struct s_reader *reader, uint8_t dt) +{ + def_resp; + int32_t sub = 0x00; + uint8_t retlen = 0x10; + + while(1) + { + CAK7do_cmd(reader, dt, cta_res, &cta_lr, sub, retlen); + rdr_log_dump_dbg(reader, D_READER, cta_res, cta_lr, "Decrypted Answer:"); + + // hier eigentlich check auf 90 am ende usw... obs halt klarging ... + if(cta_lr == 0) + { + break; + } + + if(cta_res[cta_lr - 2] == 0x6F && cta_res[cta_lr - 1] == 0x01) + { + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + break; + } + + uint32_t newsub = (cta_res[9] << 16) + (cta_res[10] << 8) + (cta_res[11]); + if(newsub == 0xFFFFFF) + { + break; + } + + if(cta_res[12] == dt) + { + uint8_t oretlen = retlen; + retlen = cta_res[13] + 0x10 + 0x2; + + while(retlen % 0x10 != 0x00) + { + retlen++; + } + + if(retlen == oretlen) + { + sub = newsub + 1; + retlen = 0x10; + ParseDataType(reader, dt, cta_res, cta_lr); + } + } + else + { + break; + } + } + + return OK; +} + +static void sub_6AD78(uint32_t *dinit) // gbox function +{ + double res = *dinit * 16807.0; + *dinit = (uint32_t)(res - (uint32_t)(res / 2147483647.0) * 2147483647.0); +} + +static void calc_cak7_exponent(uint32_t *dinit, uint8_t *out, uint8_t len) +{ + memset(out, 0x00, len); + sub_6AD78(dinit); + int nR4 = 0; + int nR5 = 0; + + while(true) + { + uint32_t nR0 = (uint32_t)* dinit; + int nR3 = nR4 + 3; + nR5 += 4; + + if(nR3 > len) + { + break; + } + + out[nR5 - 1] = ((nR0 ) & 0xFF); + out[nR5 - 2] = ((nR0 >> 8) & 0xFF); + out[nR5 - 3] = ((nR0 >> 16) & 0xFF); + out[nR5 - 4] = ((nR0 >> 24) & 0xFF); + nR4 += 4; + sub_6AD78(dinit); + } + + uint32_t nR0 = (uint32_t)* dinit; + + while(nR4 < len) + { + out[nR4] = nR0 & 0xFF; + nR4++; + nR0 >>= 8; + } + + out[ 0] &= 0x03; + out[0x10] |= 0x01; +} + +static void IdeaDecrypt(unsigned char *data, int len, const unsigned char *key, unsigned char *iv) +{ + unsigned char v[8]; + + if(!iv) + { + memset(v, 0, sizeof(v)); + iv = v; + } + + IDEA_KEY_SCHEDULE ks; + idea_set_encrypt_key(key, &ks); + idea_cbc_encrypt(data, data, len&~7, &ks, iv, IDEA_DECRYPT); +} + +static inline void xor8(uint8_t *data, const uint8_t *v1, const uint8_t *v2) +{ + int i; + for(i = 0; i < 8; ++i) + { + data[i] = v1[i] ^ v2[i]; + } +} + +static void CreateRSAPair60(struct s_reader *reader, const unsigned char *key) +{ + unsigned char idata[96]; + + int i; + for(i = 11; i >= 0; i--) + { + unsigned char *d = &idata[i * 8]; + memcpy(d, &key[13], 8); + *d ^= i; + IdeaDecrypt(d, 8, key, 0); + xor8(d, d, &key[13]); + *d ^= i; + } + + BN_CTX *ctx5 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx5); +#endif + BIGNUM *p = BN_CTX_get(ctx5); + BIGNUM *q = BN_CTX_get(ctx5); + BIGNUM *m = BN_CTX_get(ctx5); + BIGNUM *e = BN_CTX_get(ctx5); + BIGNUM *a = BN_CTX_get(ctx5); + BIGNUM *r = BN_CTX_get(ctx5); + + // Calculate P + idata[ 0] |= 0x80; + idata[47] |= 1; + BN_bin2bn(idata, 48, p); + BN_add_word(p, (key[21] << 5) | ((key[22] & 0xF0) >> 3)); + + // Calculate Q + idata[48] |= 0x80; + idata[95] |= 1; + BN_bin2bn(idata + 48, 48, q); + BN_add_word(q, ((key[22] & 0xF) << 9) | (key[23] << 1)); + + // Calculate M = P * Q + BN_mul(m, p, q, ctx5); + memset(reader->key60, 0x00, 0x60); + BN_bn2bin(m, reader->key60 + (0x60 - BN_num_bytes(m))); + rdr_log_dump_dbg(reader, D_READER, reader->key60, sizeof(reader->key60), "key60: "); + + // Calculate D + BN_sub_word(p, 1); + BN_sub_word(q, 1); + BN_mul(e, p, q, ctx5); + BN_bin2bn(public_exponent, 3, a); + BN_mod_inverse(r, a, e, ctx5); + memset(reader->exp60, 0x00, 0x60); + BN_bn2bin(r, reader->exp60 + (0x60 - BN_num_bytes(r))); + rdr_log_dump_dbg(reader, D_READER, reader->exp60, sizeof(reader->exp60), "exp60: "); + BN_CTX_end(ctx5); + BN_CTX_free(ctx5); +} + +static void CreateRSAPair68(struct s_reader *reader, const unsigned char *key) +{ + unsigned char idata[104]; + + int i; + for(i = 12; i >= 0; i--) + { + unsigned char *d = &idata[i * 8]; + memcpy(d, &key[13], 8); + *d ^= i; + IdeaDecrypt(d, 8, key, 0); + xor8(d, d, &key[13]); + *d ^= i; + } + + BN_CTX *ctx6 = BN_CTX_new(); +#ifdef WITH_LIBCRYPTO + BN_CTX_start(ctx6); +#endif + BIGNUM *p = BN_CTX_get(ctx6); + BIGNUM *q = BN_CTX_get(ctx6); + BIGNUM *m = BN_CTX_get(ctx6); + BIGNUM *e = BN_CTX_get(ctx6); + BIGNUM *a = BN_CTX_get(ctx6); + BIGNUM *r = BN_CTX_get(ctx6); + + // Calculate P + idata[ 0] |= 0x80; + idata[51] |= 1; + BN_bin2bn(idata, 52, p); + BN_add_word(p, (key[21] << 5) | ((key[22] & 0xF0) >> 3)); + + // Calculate Q + idata[ 52] |= 0x80; + idata[103] |= 1; + BN_bin2bn(idata + 52, 52, q); + BN_add_word(q, ((key[22] & 0xF) << 9) | (key[23] << 1)); + + // Calculate M = P * Q + BN_mul(m, p, q, ctx6); + memset(reader->key68, 0x00, 0x68); + BN_bn2bin(m, reader->key68 + (0x68 - BN_num_bytes(m))); + rdr_log_dump_dbg(reader, D_READER, reader->key68, sizeof(reader->key68), "key68: "); + + // Calculate D + BN_sub_word(p, 1); + BN_sub_word(q, 1); + BN_mul(e, p, q, ctx6); + BN_bin2bn(public_exponent, 3, a); + BN_mod_inverse(r, a, e, ctx6); + memset(reader->exp68, 0x00, 0x68); + BN_bn2bin(r, reader->exp68 + (0x68 - BN_num_bytes(r))); + rdr_log_dump_dbg(reader, D_READER, reader->exp68, sizeof(reader->exp68), "exp68: "); + BN_CTX_end(ctx6); + BN_CTX_free(ctx6); +} + +static void dt05_20(struct s_reader *reader) +{ + uint8_t data_20_00[72]; + uint8_t sig_20_00[16]; + uint8_t data_20_id[72]; + uint8_t data_20_x[64]; + uint8_t data_20_fin[72]; + uint8_t data_20_flag58[16]; + + rdr_log_dump_dbg(reader, D_READER, reader->tmprsa, sizeof(reader->tmprsa), "DT05_20 after RSA: "); + + // copy signature + memcpy(sig_20_00, reader->tmprsa + 24, 16); + + // copy data + memcpy(data_20_00, reader->tmprsa + 40, 72); + + // IDEA encrypt 0x48 data + int i; + int offs = 0; + for(i = 0; i < 9; i++) + { + IDEA_KEY_SCHEDULE ks; + idea_set_encrypt_key(reader->key3310, &ks); + idea_ecb_encrypt(data_20_00 + offs, data_20_id + offs, &ks); + offs += 8; + } + + // xor + for(i = 0; i < 64; i++) + { + data_20_x[i] = data_20_00[i] ^ data_20_id[i + 8]; + } + rdr_log_dump_dbg(reader, D_READER, data_20_x, sizeof(data_20_x), "data_20_x: "); + + // create final data block + memcpy(data_20_fin, data_20_id, 8); + memcpy(data_20_fin + 8, data_20_x, 64); + rdr_log_dump_dbg(reader, D_READER, data_20_fin, sizeof(data_20_fin), "data_20_fin: "); + uint8_t mdc_hash4[MDC2_DIGEST_LENGTH]; + uint8_t check4[112]; + memset(check4, 0x00, 112); + memcpy(check4, reader->cardid, 4); + memcpy(check4 + 4, reader->idird, 4); + memcpy(check4 + 23, reader->tmprsa + 23, 1); + memcpy(check4 + 40, data_20_fin, 72); + MDC2_CTX c4; + MDC2_Init(&c4); + MDC2_Update(&c4, check4, 112); + MDC2_Final(&(mdc_hash4[0]), &c4); + if(memcmp(mdc_hash4, sig_20_00, 16) == 0) + { + rdr_log(reader, "DT05_20 is correct"); + } + else + { + rdr_log(reader, "DT05_20 error - check MOD2"); + } + + // Store 3des software key Flag58 CW overencrypt + memcpy(data_20_flag58, data_20_x + 16, 16); + memcpy(reader->key3des, data_20_flag58, 16); + rdr_log_dump_dbg(reader, D_READER, reader->key3des, sizeof(reader->key3des), "Flag58 3DES Key: "); + + // create rsa pair from final data + memcpy(reader->klucz68, data_20_fin, 0x18); + rdr_log_dump_dbg(reader, D_READER, reader->klucz68, sizeof(reader->klucz68), "klucz68: "); +} + +static int32_t CAK7_cmd03_global(struct s_reader *reader) +{ + def_resp; + + if(reader->cak7_seq <= 15) + { + unsigned char klucz[24]; + memset(klucz, 0x00, 24); + memcpy(klucz, reader->key3588, 24); + CreateRSAPair60(reader, klucz); + } + + rsa_decrypt(reader->step1, 0x60, reader->data, reader->key60, 0x60, reader->exp60, 0x60); + + memcpy(&reader->step2[0], d00ff, 4); + memcpy(&reader->step2[4], reader->cardid, 4); + memcpy(&reader->step2[8], reader->data, 0x60); + rdr_log_dump_dbg(reader, D_READER, reader->step2, sizeof(reader->step2), "STEP 2:"); + rsa_decrypt(reader->step2, 0x68, reader->data, reader->kdt05_10, 0x68, public_exponent, 3); + + memcpy(&reader->step3[0], d00ff, 4); + memcpy(&reader->step3[4], reader->data, 0x68); + rdr_log_dump_dbg(reader, D_READER, reader->step3, sizeof(reader->step3), "STEP 3:"); + rsa_decrypt(reader->step3, 0x6C, reader->data, reader->kdt05_00, 0x6C, public_exponent, 3); + + uint8_t cmd03[] = {0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x0A, 0x03, 0x6C, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC}; + memcpy(&cmd03[9], reader->data, 0x6C); + do_cak7_cmd(reader, cta_res, &cta_lr, cmd03, sizeof(cmd03), 0x90); + if(cta_lr == 0) + { + rdr_log(reader, "card is not responding to CMD03 - check your data"); + return ERROR; + } + rdr_log_dump_dbg(reader, D_READER, cta_res, 0x90, "CMD03 ANSWER:"); + memcpy(reader->encrypted, &cta_res[10], 0x68); + rsa_decrypt(reader->encrypted, 0x68, reader->result, reader->kdt05_10, 0x68, public_exponent, 3); + //uint8_t stillencrypted[0x50]; + memcpy(reader->stillencrypted, &reader->result[12], 0x50); + //uint8_t resultrsa[0x50]; + rsa_decrypt(reader->stillencrypted, 0x50, reader->resultrsa, reader->mod50, reader->mod50_length, reader->cak7expo, 0x11); + + uint8_t mdc_hash3[MDC2_DIGEST_LENGTH]; + MDC2_CTX c3; + MDC2_Init(&c3); + MDC2_Update(&c3, reader->resultrsa, sizeof(reader->resultrsa)); + MDC2_Final(&(mdc_hash3[0]), &c3); + memcpy(&reader->cak7_aes_key[16], mdc_hash3, 16); + memcpy(reader->cak7_aes_key, mdc_hash3, 16); + char tmp7[128]; + rdr_log(reader, "New AES: %s", cs_hexdump(1, reader->cak7_aes_key, 16, tmp7, sizeof(tmp7))); + + return OK; +} + +static int32_t CAK7_cmd03_unique(struct s_reader *reader) +{ + def_resp; + + rsa_decrypt(reader->step1, 0x60, reader->data, reader->key3460, reader->key3460_length, public_exponent, 3); + + memcpy(&reader->step2[0], d00ff, 4); + memcpy(&reader->step2[4], reader->cardid, 4); + memcpy(&reader->step2[8], reader->data, 0x60); + rdr_log_dump_dbg(reader, D_READER, reader->step2, sizeof(reader->step2), "STEP 2:"); + + if(reader->cak7_seq <= 15) + { + dt05_20(reader); + CreateRSAPair68(reader, reader->klucz68); + } + + rsa_decrypt(reader->step2, 0x68, reader->data, reader->key68, 0x68, reader->exp68, 0x68); + + memcpy(&reader->step3[0], d00ff, 4); + memcpy(&reader->step3[4], reader->data, 0x68); + rdr_log_dump_dbg(reader, D_READER, reader->step3, sizeof(reader->step3), "STEP 3:"); + rsa_decrypt(reader->step3, 0x6C, reader->data, reader->kdt05_00, 0x6C, public_exponent, 3); + + uint8_t cmd03[] = {0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x0A, 0x03, 0x6C, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC}; + memcpy(&cmd03[9], reader->data, 0x6C); + + do_cak7_cmd(reader, cta_res, &cta_lr, cmd03, sizeof(cmd03), 0x90); + if(cta_lr == 0) + { + rdr_log(reader, "card is not responding to CMD03 - check your data"); + return ERROR; + } + rdr_log_dump_dbg(reader, D_READER, cta_res, 0x90, "CMD03 ANSWER:"); + memcpy(reader->encrypted, &cta_res[18], 0x60); + rsa_decrypt(reader->encrypted, 0x60, reader->result, reader->key3460, reader->key3460_length, public_exponent, 3); + rdr_log_dump_dbg(reader, D_READER, reader->result, 0x60, "after RSA_3460: "); + //uint8_t stillencrypted[0x50]; + memcpy(reader->stillencrypted, &reader->result[4], 0x50); + //uint8_t resultrsa[0x50]; + rsa_decrypt(reader->stillencrypted, 0x50, reader->resultrsa, reader->mod50, reader->mod50_length, reader->cak7expo, 0x11); + + uint8_t mdc_hash5[MDC2_DIGEST_LENGTH]; + MDC2_CTX c5; + MDC2_Init(&c5); + MDC2_Update(&c5, reader->resultrsa, sizeof(reader->resultrsa)); + MDC2_Final(&(mdc_hash5[0]), &c5); + memcpy(&reader->cak7_aes_key[16], mdc_hash5, 16); + memcpy(reader->cak7_aes_key, mdc_hash5, 16); + char tmp7[128]; + rdr_log(reader, "New AES: %s", cs_hexdump(1, reader->cak7_aes_key, 16, tmp7, sizeof(tmp7))); + + return OK; +} + +static int32_t CAK7_GetCamKey(struct s_reader *reader) +{ + def_resp; + uint8_t cmd0e[] = {0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x0E, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC}; + + if(!reader->nuid_length) + { + uint8_t cmd02[] = {0x02, 0x7B}; + memcpy(cmd0e + 7, cmd02, 2); + rdr_log(reader, "using CMD02"); + } + else + { + int cwekeycount = 0, i; + memcpy(cmd0e + 132, reader->nuid, reader->nuid_length); // inject NUID + + for (i = 0; i < 17; i++) + { + cwekeycount = reader->cwekey_length[i] ? i + 1 : cwekeycount; + } + + if(cwekeycount == 0) + { + if(reader->otpcsc_length) + { + memcpy(cmd0e + 136, reader->otpcsc, reader->otpcsc_length); + } + else + { + cmd0e[136] = 0x00; + cmd0e[137] = !reader->cwpkota ? 0xFF : 0x00; + } + if(reader->otacsc_length) + { + memcpy(cmd0e + 138, reader->otacsc, reader->otacsc_length); + } + else + { + cmd0e[138] = 0x00; + cmd0e[139] = reader->cwpkota ? 0xFF : 0x00; + } + } + else + { + if(reader->otpcsc_length) + { + memcpy(cmd0e + 136, reader->otpcsc, reader->otpcsc_length); + } + else + { + cmd0e[136] = 0x00; + cmd0e[137] = !reader->cwpkota ? cwekeycount : 0x00; + } + if(reader->otacsc_length) + { + memcpy(cmd0e + 138, reader->otacsc, reader->otacsc_length); + } + else + { + cmd0e[138] = 0x00; + cmd0e[139] = reader->cwpkota ? cwekeycount : 0x00; + } + } + char tmp[16]; + rdr_log(reader, "OTP CSC No. of keys: %s", cs_hexdump(1, cmd0e + 136, 2, tmp, sizeof(tmp))); + rdr_log(reader, "OTA CSC No. of keys: %s", cs_hexdump(1, cmd0e + 138, 2, tmp, sizeof(tmp))); + } + + if(reader->forcepair_length) + { + rdr_log(reader, "Forcing Pairing Type"); + memcpy(cmd0e + 13, reader->forcepair, 1); + } + else + { + if(reader->hasunique == 1) + { + cmd0e[13] = 0x40; + } + } + + memcpy(cmd0e + 14, reader->idird, 4); + memcpy(reader->irdId, reader->idird, 4); + + if(reader->cmd0eprov_length) + { + memcpy(cmd0e + 18, reader->cmd0eprov, 2); + } + else + { + memcpy(cmd0e + 18, reader->prid[0] + 2, 2); + } + + memcpy(cmd0e + 20, reader->key3588 + 24, 0x70); + + if(reader->cak7_seq <= 15) + { + srand(time(NULL)); + } + + uint32_t data1r = rand() % 4294967294u; + reader->timestmp1[0] = (data1r >> 24) & 0xFF; + reader->timestmp1[1] = (data1r >> 16) & 0xFF; + reader->timestmp1[2] = (data1r >> 8) & 0xFF; + reader->timestmp1[3] = (data1r ) & 0xFF; + memcpy(cmd0e + 9, reader->timestmp1, 0x04); + rdr_log_dump_dbg(reader, D_READER, reader->timestmp1, 4, "DATA1 CMD0E:"); + rdr_log_dump_dbg(reader, D_READER, reader->prid[0], 4, "SysID:"); + + do_cak7_cmd(reader,cta_res, &cta_lr, cmd0e, sizeof(cmd0e), 0x20); + if(cta_lr == 0) + { + rdr_log(reader, "card is not responding to CMD02/E - check your data"); + return ERROR; + } + rdr_log_dump_dbg(reader, D_READER, cta_res, 0x20, "Decrypted answer to CMD02/0E:"); + + reader->needrestart = (cta_res[22] << 16); + reader->needrestart += (cta_res[23] << 8); + reader->needrestart += (cta_res[24] ); + reader->needrestart--; + if(reader->cak7_seq <= 15) + { + rdr_log(reader, "card needs FASTreinit after %d CMDs", reader->needrestart); + } + else + { + uint32_t cmdleft = reader->needrestart - reader->cak7_seq; + rdr_log(reader, "%d CMDs left to FASTreinit", cmdleft); + } + + reader->dword_83DBC = (cta_res[18] << 24); + reader->dword_83DBC += (cta_res[19] << 16); + reader->dword_83DBC += (cta_res[20] << 8); + reader->dword_83DBC += (cta_res[21] ); + calc_cak7_exponent(&reader->dword_83DBC, reader->cak7expo, 0x11); + rdr_log_dump_dbg(reader, D_READER, reader->cak7expo, 0x11, "CAK7 Exponent:"); + memcpy(reader->cardid, cta_res + 14, 4); + rdr_log_dump_dbg(reader, D_READER, reader->cardid, 0x04, "CardSerial: "); + memcpy(reader->hexserial + 2, reader->cardid, 4); + + unsigned long datal = (cta_res[9] << 24) + (cta_res[10] << 16) + (cta_res[11] << 8) + (cta_res[12]); + + datal++; + reader->data2[0] = (datal >> 24) & 0xFF; + reader->data2[1] = (datal >> 16) & 0xFF; + reader->data2[2] = (datal >> 8) & 0xFF; + reader->data2[3] = (datal ) & 0xFF; + + data1r++; + reader->timestmp2[0] = (data1r >> 24) & 0xFF; + reader->timestmp2[1] = (data1r >> 16) & 0xFF; + reader->timestmp2[2] = (data1r >> 8) & 0xFF; + reader->timestmp2[3] = (data1r ) & 0xFF; + + memcpy(reader->ecmheader, cta_res + 18, 4); + + if(reader->cak7_seq <= 15) + { + uint8_t mdc_hash2[MDC2_DIGEST_LENGTH]; + uint8_t check2[0x78]; + memset(check2, 0x00, 0x78); + memcpy(check2, reader->cardid, 4); + memcpy(check2 + 16, reader->kdt05_10, 0x68); + + MDC2_CTX c2; + MDC2_Init(&c2); + MDC2_Update(&c2, check2, 0x78); + MDC2_Final(&(mdc_hash2[0]), &c2); + rdr_log_dump_dbg(reader, D_READER, reader->ideakey1, 16, "IDEAKEY1: "); + rdr_log_dump_dbg(reader, D_READER, mdc_hash2, 16, "MDC_HASH: "); + + if(memcmp(mdc_hash2 + 1, reader->ideakey1 + 1, 14) == 0) + { + rdr_log(reader, "DT05_10 is correct"); + } + else + { + rdr_log(reader, "DT05_10 error - check MOD1"); + } + } + + rsa_decrypt(reader->data50, reader->data50_length, reader->data, reader->mod50, reader->mod50_length, reader->cak7expo, 0x11); + rdr_log_dump_dbg(reader, D_READER, reader->timestmp2, 4, "DATA1 CMD03:"); + memcpy(&reader->step1[0], d00ff, 4); + memcpy(&reader->step1[4], reader->data, 0x50); + memcpy(&reader->step1[4 + 0x50], reader->idird, 0x04); + memcpy(&reader->step1[4 + 4 + 0x50], reader->timestmp2, 0x04); + memcpy(&reader->step1[4 + 4 + 4 + 0x50], reader->data2, 0x04); + rdr_log_dump_dbg(reader, D_READER, reader->step1, sizeof(reader->step1), "STEP 1:"); + + reader->pairtype = cta_res[13]; + if((reader->pairtype > 0x00) && (reader->pairtype < 0xC0)) + { + rdr_log(reader,"Card is starting in GLOBAL mode"); + if(!CAK7_cmd03_global(reader)) + { + return ERROR; + } + } + else if(reader->pairtype == 0xC0) + { + rdr_log(reader,"Card is starting in UNIQUE mode"); + if(!reader->mod2_length) + { + rdr_log(reader, "no mod2 defined"); + return ERROR; + } + + if(!reader->key3460_length) + { + rdr_log(reader, "no key3460 defined"); + return ERROR; + } + + if(!reader->key3310_length) + { + rdr_log(reader, "no key3310 defined"); + return ERROR; + } + + if(!CAK7_cmd03_unique(reader)) + { + return ERROR; + } + } + else + { + rdr_log(reader, "Unknown Pairing Type"); + return ERROR; + } + + return OK; +} + +static int32_t nagra3_card_init(struct s_reader *reader, ATR *newatr) +{ + get_atr; + memset(reader->hexserial, 0, 8); + reader->cak7_seq = 0; + reader->hasunique = 0; + memset(reader->ecmheader, 0, 4); + cs_clear_entitlement(reader); + + if(memcmp(atr + 8, "DNASP4", 6) == 0) + { + if((memcmp(atr + 8, "DNASP400", 8) == 0) && !reader->cak7_mode) + { + return ERROR; + } + else + { + memcpy(reader->rom, atr + 8, 15); + rdr_log(reader, "Rom revision: %.15s", reader->rom); + } + } + else if(memcmp(atr + 11, "DNASP4", 6) == 0) + { + memcpy(reader->rom, atr + 11, 15); + rdr_log(reader, "Rom revision: %.15s", reader->rom); + } + else + { + return ERROR; + } + + reader->nprov = 1; + /*reader->nsa = 0; + reader->nemm82u = 0; + reader->nemm84 = 0; + reader->nemm84s = 0; + reader->nemm83u = 0; + reader->nemm83s = 0; + reader->nemm87 = 0;*/ + + if(!reader->mod1_length) + { + rdr_log(reader, "no MOD1 defined"); + return ERROR; + } + + if(!reader->key3588_length) + { + rdr_log(reader, "no key3588 defined"); + return ERROR; + } + + if(!reader->data50_length) + { + rdr_log(reader, "no data50 defined"); + return ERROR; + } + + if(!reader->mod50_length) + { + rdr_log(reader, "no mod50 defined"); + return ERROR; + } + + if(!reader->idird_length) + { + rdr_log(reader, "no idird defined"); + return ERROR; + } + + CAK7GetDataType(reader, SYSID_CAID); + CAK7GetDataType(reader, DT05); + if(!CAK7_GetCamKey(reader)) + { + return ERROR; + } + CAK7GetDataType(reader, 0x09); + + char tmp[4 * 3 + 1]; + reader->nsa = 0; + reader->nemm82u = 0; + reader->nemm84 = 0; + reader->nemm84s = 0; + reader->nemm83u = 0; + reader->nemm83s = 0; + reader->nemm87 = 0; + CAK7GetDataType(reader, 0x04); + + if(reader->forceemmg) + { + reader->emm82 = 1; + } + + int i; + for(i = 1; i < reader->nprov; i++) + { + rdr_log(reader, "Prv.ID: %s", cs_hexdump(1, reader->prid[i], 4, tmp, sizeof(tmp))); + } + + if(reader->cak7type != 3) + { + rdr_log(reader, "-----------------------------------------"); + rdr_log(reader, "| EMM Filters (PRIVATE!!) |"); + rdr_log(reader, "+---------------------------------------+"); + if(reader->emm82 == 1) + { + rdr_log(reader, "|emm82 |"); + } + char tmp7[48]; + for(i = 0; i < reader->nemm84; i++) + { + rdr_log(reader, "|emm84 : %s |", cs_hexdump(1, reader->emm84[i], 3, tmp7, sizeof(tmp7))); + } + for(i = 0; i < reader->nemm83u; i++) + { + rdr_log(reader, "|emm83U: %s |", cs_hexdump(1, reader->emm83u[i], 6, tmp7, sizeof(tmp7))); + } + for(i = 0; i < reader->nemm83s; i++) + { + rdr_log(reader, "|emm83S: %s |", cs_hexdump(1, reader->emm83s[i], 6, tmp7, sizeof(tmp7))); + } + for(i = 0; i < reader->nemm87; i++) + { + rdr_log(reader, "|emm87 : %s |", cs_hexdump(1, reader->emm87[i], 6, tmp7, sizeof(tmp7))); + } + rdr_log(reader, "-----------------------------------------"); + } + + if(reader->cak7type != 1) + { + rdr_log(reader, "-----------------------------------------"); + rdr_log(reader, "| EMM Filters (PRIVATE!!) |"); + rdr_log(reader, "+---------------------------------------+"); + if(reader->emm83 == 1) + { + rdr_log(reader, "|emm83 |"); + } + char tmp7[48]; + for(i = 0; i < reader->nemm82u; i++) + { + rdr_log(reader, "|emm82U: %s |", cs_hexdump(1, reader->emm82u[i], 7, tmp7, sizeof(tmp7))); + } + for(i = 0; i < reader->nemm84s; i++) + { + rdr_log(reader, "|emm84S: %s |", cs_hexdump(1, reader->emm84s[i], 6, tmp7, sizeof(tmp7))); + } + rdr_log(reader, "-----------------------------------------"); + } + + rdr_log(reader, "ready for requests"); + + return OK; +} + +static int32_t nagra3_card_info(struct s_reader *reader) +{ + char tmp[4 * 3 + 1]; + + rdr_log(reader, "ROM: %c %c %c %c %c %c %c %c", reader->rom[0], reader->rom[1], reader->rom[2], reader->rom[3], reader->rom[4], reader->rom[5], reader->rom[6], reader->rom[7]); + rdr_log(reader, "REV: %c %c %c %c %c %c", reader->rom[9], reader->rom[10], reader->rom[11], reader->rom[12], reader->rom[13], reader->rom[14]); + rdr_log_sensitive(reader, "SER: {%s}", cs_hexdump(1, reader->hexserial + 2, 4, tmp, sizeof(tmp))); + rdr_log(reader, "ECM CAID: %04X", reader->caid); + rdr_log(reader, "EMM CAID: %04X", reader->cak7_emm_caid); + rdr_log(reader, "Prv.ID: %s(sysid)", cs_hexdump(1, reader->prid[0], 4, tmp, sizeof(tmp))); + cs_clear_entitlement(reader); // reset the entitlements + rdr_log(reader, "-----------------------------------------"); + rdr_log(reader, "|id |tier |valid from |valid to |"); + rdr_log(reader, "+----+--------+------------+------------+"); + CAK7GetDataType(reader, IRDINFO); + CAK7GetDataType(reader, TIERS); + rdr_log(reader, "-----------------------------------------"); + struct timeb now; + cs_ftime(&now); + reader->last_refresh = now; + + return OK; +} + +static int32_t fastreinit(struct s_reader *reader) +{ + ATR newatr[ATR_MAX_SIZE]; + memset(newatr, 0, 1); + + if(ICC_Async_Activate(reader, newatr, 0)) + { + return ERROR; + } + + reader->cak7_seq = 0; + + if(!CAK7_GetCamKey(reader)) + { + return ERROR; + } + + return OK; +} + +static void nagra3_post_process(struct s_reader *reader) +{ + if(reader->cak7_seq >= reader->needrestart) + { + rdr_log(reader, "card needs FASTreinit to prevent crash"); + if(!fastreinit(reader)) + { + rdr_log(reader, "FASTreinit failed - need to restart reader"); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + } + else if((reader->cak7_camstate & 64) == 64) + { + rdr_log(reader, "negotiating new Session Key"); + if(!CAK7_GetCamKey(reader)) + { + rdr_log(reader, "negotiations failed - trying FASTreinit"); + if(!fastreinit(reader)) + { + rdr_log(reader, "FASTreinit failed - need to restart reader"); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + } + } +} + +static int32_t nagra3_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + def_resp; + + if(reader->cak7type == 3) + { + if(er->ecm[2] > 0x61 && er->ecm[7] == 0x5C && er->ecm[100] == 0x0B) + { + if(er->ecm[101] == 0x03 || er->ecm[101] == 0x04) + { + if(er->ecm[104] > reader->pairtype) + { + rdr_log(reader, "reinit card in Unique Pairing Mode"); + return ERROR; + } + if(er->ecm[104] == 0x80 && reader->pairtype == 0x80) + { + rdr_log(reader, "reinit card in Unique Pairing Mode"); + return ERROR; + } + } + if(er->ecm[101] == 0x04 && !reader->nuid_length) + { + rdr_log(reader, "reinit card with NUID"); + return ERROR; + } + } + } + else + { + if(er->ecm[2] > 0x86 && er->ecm[4] == 0x84 && er->ecm[137] == 0x0B) + { + if(er->ecm[138] == 0x03 || er->ecm[138] == 0x04) + { + if(er->ecm[141] > reader->pairtype) + { + rdr_log(reader, "reinit card in Unique Pairing Mode"); + return ERROR; + } + if(er->ecm[141] == 0x80 && reader->pairtype == 0x80) + { + rdr_log(reader, "reinit card in Unique Pairing Mode"); + return ERROR; + } + } + if(er->ecm[138] == 0x04 && !reader->nuid_length) + { + rdr_log(reader, "reinit card with NUID"); + return ERROR; + } + } + } + + uint8_t ecmreq[0xC0]; + memset(ecmreq, 0xCC, 0xC0); + ecmreq[7] = 0x05; + if(reader->headermode == 0) + { + ecmreq[ 9] = 0x00; + ecmreq[10] = 0x00; + ecmreq[11] = 0x00; + ecmreq[12] = 0x00; + ecmreq[13] = 0x00; + } + else if(reader->headermode == 1) + { + ecmreq[ 9] = 0x04; + ecmreq[10] = reader->ecmheader[0]; + ecmreq[11] = reader->ecmheader[1]; + ecmreq[12] = reader->ecmheader[2]; + ecmreq[13] = reader->ecmheader[3]; + } + + if(reader->cak7type == 3) + { + ecmreq[8] = er->ecm[7] + 6; + memcpy(&ecmreq[14], er->ecm + 7, er->ecm[7] + 1); + } + else + { + ecmreq[8] = er->ecm[4] + 6; + memcpy(&ecmreq[14], er->ecm + 4, er->ecm[4] + 1); + } + + if((er->ecm[2] == 0xAC) && (er->ecm[3] == 0x05)) + { + ecmreq[15] = 0x0A; + } + + do_cak7_cmd(reader, cta_res, &cta_lr, ecmreq, sizeof(ecmreq), 0xB0); + rdr_log_dump_dbg(reader, D_READER, cta_res, 0xB0, "Decrypted ECM Answer:"); + + if((cta_res[cta_lr - 2] != 0x90 && cta_res[cta_lr - 1] != 0x00) || cta_lr == 0) + { + rdr_log(reader, "(ECM) Reader will be restart now cause: %02X %02X card answer!!!", cta_res[cta_lr - 2], cta_res[cta_lr - 1]); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + else if(cta_res[27] != 0x00 && cta_res[27] != 0xCC) + { + memcpy(reader->ecmheader, cta_res + 9, 4); + reader->cak7_camstate = cta_res[4]; + uint8_t _cwe0[8]; + uint8_t _cwe1[8]; + + if(cta_res[78] == 0x01 || reader->forcecwswap) + { + memcpy(_cwe0, &cta_res[52], 0x08); + memcpy(_cwe1, &cta_res[28], 0x08); + } + else + { + memcpy(_cwe0, &cta_res[28], 0x08); + memcpy(_cwe1, &cta_res[52], 0x08); + } + + if(cta_res[27] == 0x5C) + { + uint8_t cta_res144 = cta_res[144]; + if(cta_res144 < 0x11) + { + if(!reader->cwekey_length[cta_res144]) + { + rdr_log(reader, "ERROR: CWPK%d is not set, can not decrypt CW", cta_res[144]); + return ERROR; + } + des_ecb3_decrypt(_cwe0, reader->cwekey[cta_res144]); + des_ecb3_decrypt(_cwe1, reader->cwekey[cta_res144]); + rdr_log_dbg(reader, D_READER, "CW Decrypt ok"); + } + } + else if(cta_res[27] == 0x58) + { + des_ecb3_decrypt(_cwe0, reader->key3des); + des_ecb3_decrypt(_cwe1, reader->key3des); + rdr_log_dbg(reader, D_READER, "CW Decrypt ok"); + } + + memcpy(ea->cw, _cwe0, 0x08); + memcpy(ea->cw + 8, _cwe1, 0x08); + return OK; + } + else if(cta_res[23] == 0x00) + { + memcpy(reader->ecmheader, cta_res + 9, 4); + reader->cak7_camstate = cta_res[4]; + if(reader->hasunique && reader->pairtype < 0xC0) + { + rdr_log(reader, "reinit card in Unique Pairing Mode"); + } + else + { + rdr_log(reader, "card has no right to decode this channel"); + } + } + else if(cta_res[23] == 0x04) + { + if(!reader->nuid_length) + { + rdr_log(reader, "reinit card with NUID"); + } + else + { + rdr_log(reader, "wrong OTP/OTA CSC values"); + } + } + else + { + rdr_log(reader, "card got wrong ECM"); + } + + return ERROR; +} + +static int32_t nagra3_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + def_resp; + + if(ep->emm[0] == 0x90) + { + rdr_log(reader, "OSCam got your BoxEMM"); + char tmp[128]; + rdr_log(reader, "NUID: %s", cs_hexdump(1, reader->nuid, 4, tmp, sizeof(tmp))); + rdr_log(reader, "Index: %s", cs_hexdump(1, ep->emm + 10, 1, tmp, sizeof(tmp))); + rdr_log(reader, "eCWPK: %s", cs_hexdump(1, ep->emm + 11, 16, tmp, sizeof(tmp))); + } + else + { + uint8_t emmreq[0xC0]; + memset(emmreq, 0xCC, 0xC0); + emmreq[7] = 0x05; + if(reader->headermode == 0) + { + emmreq[ 9] = 0x00; + emmreq[10] = 0x00; + emmreq[11] = 0x00; + emmreq[12] = 0x00; + emmreq[13] = 0x00; + } + else if(reader->headermode == 1) + { + emmreq[ 9] = 0x04; + emmreq[10] = reader->ecmheader[0]; + emmreq[11] = reader->ecmheader[1]; + emmreq[12] = reader->ecmheader[2]; + emmreq[13] = reader->ecmheader[3]; + } + + if(reader->cak7type == 3) + { + int32_t i; + uint8_t *prov_id_ptr; + + switch(ep->type) + { + case SHARED: + emmreq[8] = ep->emm[9] + 6; + prov_id_ptr = ep->emm + 3; + memcpy(&emmreq[14], ep->emm + 9, ep->emm[9] + 1); + break; + + case UNIQUE: + emmreq[8] = ep->emm[12] + 6; + prov_id_ptr = ep->emm + 9; + memcpy(&emmreq[14], ep->emm + 12, ep->emm[12] + 1); + break; + + case GLOBAL: + emmreq[8] = ep->emm[6] + 6; + prov_id_ptr = ep->emm + 3; + memcpy(&emmreq[14], ep->emm + 6, ep->emm[6] + 1); + break; + + default: + rdr_log(reader, "EMM: Congratulations, you have discovered a new EMM on Merlin."); + rdr_log(reader, "This has not been decoded yet."); + return ERROR; + } + + i = get_prov_index(reader, prov_id_ptr); + if(i == -1) + { + rdr_log(reader, "EMM: skipped since provider id doesnt match"); + return SKIPPED; + } + } + else + { + emmreq[8] = ep->emm[9] + 6; + memcpy(&emmreq[14], ep->emm + 9, ep->emm[9] + 1); + } + + do_cak7_cmd(reader, cta_res, &cta_lr, emmreq, sizeof(emmreq), 0xB0); + rdr_log_dump_dbg(reader, D_READER, cta_res, 0xB0, "Decrypted EMM Answer:"); + + if((cta_res[cta_lr - 2] != 0x90 && cta_res[cta_lr - 1] != 0x00) || cta_lr == 0) + { + rdr_log(reader, "(EMM) Reader will be restart now cause: %02X %02X card answer!!!", cta_res[cta_lr - 2], cta_res[cta_lr - 1]); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + else + { + memcpy(reader->ecmheader, cta_res + 9, 4); + if(reader->cak7_seq >= reader->needrestart) + { + rdr_log(reader, "card needs FASTreinit to prevent crash"); + if(!fastreinit(reader)) + { + rdr_log(reader, "FASTreinit failed - need to restart reader"); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + } + else if(cta_res[4] == 0x80) + { + rdr_log_dbg(reader, D_READER, "EMM forced card to reinit"); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + else if(cta_res[13] == 0x02) + { + rdr_log_dbg(reader, D_READER, "Revision update - card reinit necessary"); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + else if((cta_res[4] & 64) == 64) + { + rdr_log(reader, "negotiating new Session Key"); + if(!CAK7_GetCamKey(reader)) + { + rdr_log(reader, "negotiations failed - trying FASTreinit"); + if(!fastreinit(reader)) + { + rdr_log(reader, "FASTreinit failed - need to restart reader"); + reader->card_status = CARD_NEED_INIT; + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + } + } + else if(cta_res[8] == 0x0E) + { + rdr_log_dbg(reader, D_READER, "card got wrong EMM"); + return OK; + } + + struct timeb now; + cs_ftime(&now); + int64_t gone_now = comp_timeb(&now, &reader->emm_last); + int64_t gone_refresh = comp_timeb(&reader->emm_last, &reader->last_refresh); + if(((gone_now > (int64_t)3600*1000) && (gone_now < (int64_t)365*24*3600*1000)) || ((gone_refresh > (int64_t)12*3600*1000) && (gone_refresh < (int64_t)365*24*3600*1000))) + { + reader->last_refresh = now; + add_job(reader->client, ACTION_READER_CARDINFO, NULL, 0); // refresh entitlement since it might have been changed! + } + } + } + + return OK; +} + +const struct s_cardsystem reader_nagracak7 = +{ + .desc = "nagra merlin", + .caids = (uint16_t[]){ 0x18, 0 }, + .do_emm = nagra3_do_emm, + .do_ecm = nagra3_do_ecm, + .post_process = nagra3_post_process, + .card_info = nagra3_card_info, + .card_init = nagra3_card_init, + .get_emm_type = nagra_get_emm_type, + .get_emm_filter = nagra_get_emm_filter, +}; + +#endif diff --git a/reader-seca.c b/reader-seca.c new file mode 100644 index 0000000..33d4512 --- /dev/null +++ b/reader-seca.c @@ -0,0 +1,773 @@ +#include "globals.h" +#ifdef READER_SECA +#include "reader-common.h" +#include "csctapi/icc_async.h" +#include "cscrypt/idea.h" + +struct seca_data +{ + bool valid_provider[CS_MAXPROV]; + IDEA_KEY_SCHEDULE ks; + IDEA_KEY_SCHEDULE ksSession; +}; + + +static uint64_t get_pbm(struct s_reader *reader, uint8_t idx, bool fedc) +{ + def_resp; + uint8_t ins34[] = { 0xc1, 0x34, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00 }; // set request options + uint8_t ins32[] = { 0xc1, 0x32, 0x00, 0x00, 0x0A }; // get PBM + uint64_t pbm = 0; + + ins32[2] = idx; + if (!idx) // change request options for first (=managment) provider only + { + ins32[4] = 0x0D; + ins34[5] = 0x04; + } + + if(!fedc) + { + write_cmd(ins34, ins34 + 5); // set request options + write_cmd(ins32, NULL); // pbm request + + switch(cta_res[0]) + { + case 0x04: + rdr_log(reader, "no PBM for provider %u", idx + 1); + break; + + case 0x83: + pbm = b2ll(8, cta_res + 1); + rdr_log(reader, "PBM for provider %u: %08llx", idx + 1, (unsigned long long) pbm); + break; + + case 0xb2: + pbm = b2ll(8, cta_res + 1); + rdr_log(reader, "PBM for provider %u: %08llx", idx + 1, (unsigned long long) pbm); + break; + + default: + rdr_log(reader, "ERROR: PBM returns unknown byte %02x", cta_res[0]); + } + } + return pbm; +} + +static int32_t set_provider_info(struct s_reader *reader, int32_t i) +{ + def_resp; + uint8_t ins12[] = { 0xc1, 0x12, 0x00, 0x00, 0x19 }; // get provider info + int32_t year, month, day; + struct tm lt; + time_t t; + bool valid = false; + bool fedc = false; + char l_name[16 + 8 + 1] = ", name: "; + char tmp[9]; + + uint32_t provid; + + ins12[2] = i; // select provider + rdr_log(reader, "Request provider %i", i + 1); + write_cmd(ins12, NULL); // show provider properties + + if((cta_res[25] != 0x90) || (cta_res[26] != 0x00)) + { + return ERROR; + } + + reader->prid[i][0] = 0; + reader->prid[i][1] = 0; // blanken high byte provider code + + if (cta_res[0] == 0xFE) + { + fedc = true; + rdr_log(reader, "FEDC provider %i", i + 1); + cta_res[0] = 0x00; + + switch(i + 1) + { + case 0x01: + cta_res[1] = 0x00; + break; + + case 0x02: + cta_res[1] = 0x68; + break; + + case 0x03: + cta_res[1] = 0x65; + break; + + default: + cta_res[1] = 0x68; + } + } + memcpy(&reader->prid[i][2], cta_res, 2); + + provid = b2ll(4, reader->prid[i]); + year = (cta_res[22] >> 1) + 1990; + month = ((cta_res[22] & 0x1) << 3) | (cta_res[23] >> 5); + day = (cta_res[23] & 0x1f); + t = time(NULL); + localtime_r(&t, <); + + if(lt.tm_year + 1900 != year) + { + valid = (lt.tm_year + 1900 < year); + } + else if(lt.tm_mon + 1 != month) + { + valid = (lt.tm_mon + 1 < month); + } + else if(lt.tm_mday != day) + { + valid = (lt.tm_mday < day); + } + + memcpy(l_name + 8, cta_res + 2, 16); + l_name[sizeof(l_name) - 1] = 0; + trim(l_name + 8); + l_name[0] = (l_name[8]) ? ',' : 0; + + if(l_name[8]) + { + add_provider(0x0100, provid, l_name + 8, "", ""); + } + + struct seca_data *csystem_data = reader->csystem_data; + csystem_data->valid_provider[i] = valid; + rdr_log(reader, "provider %d: %04X, valid: %i%s, expiry date: %4d/%02d/%02d", i + 1, provid, valid, l_name, year, month, day); + memcpy(&reader->sa[i][0], cta_res + 18, 4); + + if(valid) // if not expired + { + rdr_log_sensitive(reader, "SA: {%s}", cs_hexdump(0, cta_res + 18, 4, tmp, sizeof(tmp))); + } + + // add entitlement to list + memset(<, 0, sizeof(struct tm)); + lt.tm_year = year - 1900; + lt.tm_mon = month - 1; + lt.tm_mday = day; + + // Check if entitlement entry exists + LL_ITER it = ll_iter_create(reader->ll_entitlements); + S_ENTITLEMENT *entry = NULL; + + do + { + entry = ll_iter_next(&it); + if((entry) && (entry->provid == provid)) + { + break; + } + } + while(entry); + + if(entry) + { + // update entitlement info if found + entry->end = mktime(<); + entry->id = get_pbm(reader, i, fedc); + entry->type = (i) ? 6 : 7; + } + else // add entitlement info + { + cs_add_entitlement(reader, reader->caid, provid, get_pbm(reader, i, fedc), 0, 0, mktime(<), (i) ? 6 : 7, 1); + } + + return OK; +} + +static int32_t get_maturity(struct s_reader *reader) +{ + // Get maturity on card + static const uint8_t ins16[] = { 0xC1, 0x16, 0x00, 0x00, 0x06 }; + + def_resp; + + write_cmd(ins16, NULL); + if((cta_res[cta_lr - 2] == 0x90) && cta_res[cta_lr - 1] == 0x00) + { + reader->maturity=cta_res[cta_lr - 4] & 0xF ; + //rdr_log(reader, "Maturity rating on the card 0x%X!", reader->maturity); + + if (reader->maturity<0xF) + { + rdr_log(reader, "Maturity level [%X]= older than %i years", reader->maturity, reader->maturity); + } + else + { + rdr_log(reader, "Maturity level [%X]=no age limit", reader->maturity); + } + } + + rdr_log_dbg(reader, D_READER, "ins30_answer: %02x%02x", cta_res[0], cta_res[1]); + return 0; +} + +static int32_t unlock_parental(struct s_reader *reader) +{ + // Unlock parental control + // c1 30 00 01 09 + // 00 00 00 00 00 00 00 00 ff + static const uint8_t ins30[] = { 0xc1, 0x30, 0x00, 0x01, 0x09 }; + static uint8_t ins30data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }; + + def_resp; + + if(strcmp(reader->pincode, "none")) + { + rdr_log(reader, "Using PIN %s", reader->pincode); + // the pin need to be coded in bcd, so we need to convert from ascii to bcd, so '1234' -> 0x12 0x34 + ins30data[6] = ((reader->pincode[0] - 0x30) << 4) | ((reader->pincode[1] - 0x30) & 0x0f); + ins30data[7] = ((reader->pincode[2] - 0x30) << 4) | ((reader->pincode[3] - 0x30) & 0x0f); + } + else + { + rdr_log(reader, "Using PIN 0000!"); + } + + write_cmd(ins30, ins30data); + rdr_log_dbg(reader, D_READER, "ins30_answer: %02x%02x", cta_res[0], cta_res[1]); + + if(!(cta_res[cta_lr - 2] == 0x90 && cta_res[cta_lr - 1] == 0)) + { + if(strcmp(reader->pincode, "none")) + { + rdr_log(reader, "Can't disable parental lock. Wrong PIN? OSCam used %s!", reader->pincode); + } + else + { + rdr_log(reader, "Can't disable parental lock. Wrong PIN? OSCam used 0000!"); + } + } + else + { + rdr_log(reader, "Parental lock disabled"); + get_maturity(reader); + } + + return 0; +} + +static int32_t seca_card_init(struct s_reader *reader, ATR *newatr) +{ + get_atr; + def_resp; + char *card; + uint64_t serial ; + static const uint8_t ins0e[] = { 0xc1, 0x0e, 0x00, 0x00, 0x08 }; // get serial number (UA) + + cs_clear_entitlement(reader); + + if((atr[10] != 0x0e) || (atr[11] != 0x6c) || (atr[12] != 0xb6) || (atr[13] != 0xd6)) + { + return ERROR; + } + + if(!cs_malloc(&reader->csystem_data, sizeof(struct seca_data))) + { + return ERROR; + } + + switch(atr[7] << 8 | atr[8]) + { + case 0x5084: + card = "Generic"; + break; + + case 0x5384: + card = "Philips"; + break; + + case 0x5130: + case 0x5430: + case 0x5760: + card = "Thompson"; + break; + + case 0x5284: + case 0x5842: + case 0x6060: + card = "Siemens"; + break; + + case 0x7070: + card = "Mediaguard"; + break; + + default: + card = "Unknown"; + break; + } + + reader->caid = 0x0100; + memset(reader->prid, 0xff, sizeof(reader->prid)); + write_cmd(ins0e, NULL); // read unique id + memcpy(reader->hexserial, cta_res + 2, 6); + serial = b2ll(5, cta_res + 3); + + rdr_log_sensitive(reader, "type: SECA, caid: %04X, serial: {%llu}, card: %s v%d.%d", + reader->caid, (unsigned long long) serial, card, atr[9] & 0x0F, atr[9] >> 4); + + int seca_version = atr[9] & 0X0F; // Get seca cardversion from cardatr + + if(seca_version == 10) // check for nagra smartcard (seca3) + { + reader->secatype = 3; + rdr_log_dbg(reader, D_IFD, "Detected seca/nagra (seca3) card"); + } + + if(seca_version == 7) // check for seca smartcard (seca2) + { + reader->secatype = 2; + rdr_log(reader, "Detected seca2 card"); + } + + get_maturity(reader); + + // Unlock parental control + if(cfg.ulparent != 0) + { + unlock_parental(reader); + get_maturity(reader); + } + else + { + rdr_log_dbg(reader, D_IFD, "parental locked"); + } + + struct seca_data *csystem_data = reader->csystem_data; + //init ideakeys + uint8_t IdeaKey[16]; + memcpy(IdeaKey, reader->boxkey, 16); + idea_set_encrypt_key(IdeaKey, &csystem_data->ks); + idea_set_decrypt_key(&csystem_data->ks, &csystem_data->ksSession); + + return OK; +} + +// returns provider id or -1 if not found +static int32_t get_prov_index(struct s_reader *rdr, const uint8_t *provid) +{ + int32_t prov; + for(prov = 0; prov < rdr->nprov; prov++) // search for provider index + { + if(!memcmp(provid, &rdr->prid[prov][2], 2)) + { + return (prov); + } + } + return (-1); +} + +// CDS seca2/3 solution +static int32_t seca_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + // provid006A=CDS NL uses seca2 and nagra/mediaguard3 crypt on same caid/provid only ecmpid is different + if(er->ecm[3] == 0x00 && er->ecm[4] == 0x6a) + { + // default assume ecmtype same as cardtype in reader + int32_t ecm_type = reader->secatype; + + if(er->ecm[8] == 0x00) // this is a mediaguard3 ecm request + { + ecm_type = 3; // flag it! + } + + if((er->ecm[8] == 0x10) && (er->ecm[9] == 0x01)) // this is a seca2 ecm request + { + ecm_type = 2; // flag it! + } + + if(ecm_type != reader->secatype) // only accept ecmrequest for right card! + { + return ERROR; + } + } + + def_resp; + uint8_t ins3c[] = { 0xc1, 0x3c, 0x00, 0x00, 0x00 }; // coding cw + uint8_t ins3a[] = { 0xc1, 0x3a, 0x00, 0x00, 0x10 }; // decoding cw + int32_t i; + + if((i = get_prov_index(reader, er->ecm + 3)) == -1) // if provider not found + { + snprintf(ea->msglog, MSGLOGSIZE, "provider not found"); + return ERROR; + } + + struct seca_data *csystem_data = reader->csystem_data; + if((er->ecm[7] & 0x0F) != 0x0E && !csystem_data->valid_provider[i]) // if expired and not using OP Key 0E + { + snprintf(ea->msglog, MSGLOGSIZE, "provider expired"); + return ERROR; + } + + ins3c[2] = i; + ins3c[3] = er->ecm[7]; // key nr + ins3c[4] = (((er->ecm[1] & 0x0f) << 8) | er->ecm[2]) - 0x05; + int32_t try = 1; + int32_t ret; + + do + { + if(try > 1) + { + snprintf(ea->msglog, MSGLOGSIZE, "ins3c try nr %i", try); + } + + write_cmd(ins3c, er->ecm + 8); // ecm request + uint8_t ins30[] = { 0xC1, 0x30, 0x00, 0x02, 0x09 }; + uint8_t ins30data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }; + + /* We need to use a token */ + if(cta_res[0] == 0x90 && cta_res[1] == 0x1a) + { + write_cmd(ins30, ins30data); + write_cmd(ins3c, er->ecm + 8); // ecm request + } + + ret = (((cta_res[0] != 0x90) && (cta_res[0] != 0x93) && (cta_res[0] != 0x96)) || ((cta_res[1] != 0x00) && (cta_res[1] != 0x02))); + + // Handle all not initial 90 00 ecm of with a get decoding cw + // does avoid the need off card reset after a lot off them + // the try ++ has been removed as it triggers the anti share mode + // off seca cards due to not recorded extra ecm's by rate limiter + + if((cta_res[0] == 0x93) && (cta_res[1] == 0x02)) + { + write_cmd(ins3a, NULL); // get cw + if(er->ecm[2] > 0x61 && er->ecm[7] == 0x5C && er->ecm[100] == 0x0B) + { + rdr_log(reader, "reinit card in CAK7 mode"); + } + else + { + snprintf(ea->msglog, MSGLOGSIZE, "unsubscribed 93 02"); + } + return ERROR; + } // exit if unsubscribed + + if((cta_res[0] == 0x96) && (cta_res[1] == 0x00)) + { + write_cmd(ins3a, NULL); // get cw + if(er->ecm[2] > 0x61 && er->ecm[7] == 0x5C && er->ecm[100] == 0x0B) { + rdr_log(reader, "reinit card in CAK7 mode"); + } else { + snprintf(ea->msglog, MSGLOGSIZE, "fake 96 00 ecm"); + } + return E_CORRUPT; + } // exit if fake 96 00 ecm + + if(ret) + { + snprintf(ea->msglog, MSGLOGSIZE, "%.16s ins3c card res: %02x %02x", reader->label, cta_res[0] , cta_res[1]); + write_cmd(ins3a, NULL); // get cw + return ERROR; + } // exit on other's then 96 00 or 93 02 + } + + while((try < 2) && (ret)); + + if(ret) + { + return ERROR; + } + + write_cmd(ins3a, NULL); // get cw's + if((cta_res[16] != 0x90) || (cta_res[17] != 0x00)) + { + snprintf(ea->msglog, MSGLOGSIZE, "ins3a card response: %02x %02x", cta_res[16] , cta_res[17]); + return ERROR; + } // exit if response not 90 00 + + // TODO: if response is 9027 ppv mode is possible! + + if(er->ecm[5] == 0x01 && ((reader->card_atr[9] & 0X0F) == 10)) // seca3: nano 01 in effect? + { + if(reader->boxkey_length == 16) + { + uint8_t v[8]; + memset(v, 0, sizeof(v)); + idea_cbc_encrypt(cta_res, ea->cw, 8, &csystem_data->ksSession, v, IDEA_DECRYPT); + memset(v, 0, sizeof(v)); + idea_cbc_encrypt(cta_res + 8, ea->cw + 8, 8, &csystem_data->ksSession, v, IDEA_DECRYPT); + uint8_t c; + + for(i = 0; i < 16; i += 4) + { + c = ((ea->cw[i] + ea->cw[i + 1] + ea->cw[i + 2]) & 0xff); + + if(ea->cw[i + 3] != c) + { + break; + } + } + + if(i == 16) + { + return OK; + } + } + memset(ea->cw, 0, 16); + snprintf(ea->msglog, MSGLOGSIZE, "need sessionkey"); + return ERROR; + } + memcpy(ea->cw, cta_res, 16); + return OK; +} + +// returns 1 if shared emm matches SA, unique emm matches serial, or global or unknown +static int32_t seca_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + rdr_log_dbg(rdr, D_EMM, "Entered seca_get_emm_type ep->emm[0]=%i", ep->emm[0]); + int32_t i; + char tmp_dbg[25]; + + switch(ep->emm[0]) + { + case 0x82: + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 3, 6); + + rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE, ep->hexserial = {%s}", + cs_hexdump(1, ep->hexserial, 6, tmp_dbg, sizeof(tmp_dbg))); + + rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE, rdr->hexserial = {%s}", + cs_hexdump(1, rdr->hexserial, 6, tmp_dbg, sizeof(tmp_dbg))); + + return (!memcmp(rdr->hexserial, ep->hexserial, 6)); + break; + + case 0x84: + ep->type = SHARED; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 5, 3); // don't include custom byte; this way the network also knows SA + i = get_prov_index(rdr, ep->emm + 3); + + rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED, ep->hexserial = {%s}", + cs_hexdump(1, ep->hexserial, 3, tmp_dbg, sizeof(tmp_dbg))); + + if(i == -1) // provider not found on this card + { return 0; } //do not pass this EMM + + rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED, rdr->sa[%i] = {%s}", i, + cs_hexdump(1, rdr->sa[i], 3, tmp_dbg, sizeof(tmp_dbg))); + + return (!memcmp(rdr->sa[i], ep->hexserial, 3)); + break; + + // Unknown EMM types, but allready subbmited to dev's + // FIXME: Drop EMM's until there are implemented + case 0x83: + ep->type = GLOBAL; + rdr_log_dbg(rdr, D_EMM, "GLOBAL, PROVID: %04X", (ep->emm[3] << 8) | ep->emm[4]); + return 1; + /* EMM-G manadge ppv by provid + 83 00 74 33 41 04 70 00 BF 20 A1 15 48 1B 88 FF + CF F5 50 CB 6F E1 26 A2 70 02 8F D0 07 6A 13 F9 + 50 F9 61 88 FB E4 B8 03 EF 68 C9 54 EB C0 51 2E + 9D F9 E1 4A D9 A6 3F 5D 7A 1E B0 6E 3D 9B 93 E7 + 5A E8 D4 AE 29 B9 37 07 5A 43 C8 F2 DE BD F8 BA + 69 DC A4 87 C2 FA 25 87 87 42 47 67 AE B7 1A 54 + CA F6 B7 EC 15 0A 67 1C 59 F8 B9 B8 6F 7D 58 94 + 24 63 17 15 58 1E 59 + */ + + case 0x88: + case 0x89: + // EMM-G ? + ep->type = UNKNOWN; + return 0; + + default: + ep->type = UNKNOWN; + return 1; + } +} + +static int32_t seca_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 1 + (2 * rdr->nprov); + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[1], rdr->hexserial, 6); + memset(&filters[idx].mask[1], 0xFF, 6); + idx++; + + int32_t prov; + for(prov = 0; prov < rdr->nprov; prov++) + { + // if sa == null skip update by shared & global (provid inactive) + if(!memcmp(rdr->sa[prov], "\x00\x00\x00", 3)) + { + continue; + } + + filters[idx].type = EMM_GLOBAL; // global by provider + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x83; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[1], &rdr->prid[prov][2], 2); + memset(&filters[idx].mask[1], 0xFF, 2); + idx++; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x84; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[1], &rdr->prid[prov][2], 2); + memset(&filters[idx].mask[1], 0xFF, 2); + memcpy(&filters[idx].filter[3], &rdr->sa[prov], 3); + memset(&filters[idx].mask[3], 0xFF, 3); + idx++; + } + + *filter_count = idx; + } + + return OK; +} + +static int32_t seca_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + def_resp; + uint8_t ins40[] = { 0xc1, 0x40, 0x00, 0x00, 0x00 }; + int32_t i, ins40data_offset; + int32_t emm_length = ((ep->emm[1] & 0x0f) << 8) + ep->emm[2]; + uint8_t *prov_id_ptr; + + switch(ep->type) + { + case SHARED: + ins40[3] = ep->emm[9]; + ins40[4] = emm_length - 0x07; + ins40data_offset = 10; + prov_id_ptr = ep->emm + 3; + break; + + case UNIQUE: + ins40[3] = ep->emm[12]; + ins40[4] = emm_length - 0x0A; + ins40data_offset = 13; + prov_id_ptr = ep->emm + 9; + break; + + case GLOBAL: + ins40[3] = ep->emm[6]; + ins40[4] = emm_length - 0x04; + ins40data_offset = 7; + prov_id_ptr = ep->emm + 3; + break; + + default: + rdr_log(reader, "EMM: Congratulations, you have discovered a new EMM on SECA."); + rdr_log(reader, "This has not been decoded yet, so send this output to authors:"); + rdr_log_dump(reader, ep->emm, emm_length + 3, "EMM:"); + return ERROR; + } + + i = get_prov_index(reader, prov_id_ptr); + if(i == -1) + { + rdr_log(reader, "EMM: skipped since provider id doesnt match"); + return SKIPPED; + } + + ins40[2] = (ep->emm[ins40data_offset - 2] & 0xF0) | (i & 0x0F); + write_cmd(ins40, ep->emm + ins40data_offset); // emm request + + if(cta_res[0] == 0x97) + { + if(!(cta_res[1] & 4)) // date updated + { + set_provider_info(reader, i); + } + else + { + rdr_log(reader, "EMM: Update not necessary."); + } + return OK; // Update not necessary + } + + if((cta_res[0] == 0x90) && ((cta_res[1] == 0x00) || (cta_res[1] == 0x19))) + { + if(ep->type == GLOBAL) // do not print new provider info after global emm + { + return OK; + } + + if(set_provider_info(reader, i) == OK) // after successful EMM, print32_t new provider info + { + return OK; + } + } + return ERROR; +} + +static int32_t seca_card_info(struct s_reader *reader) +{ + def_resp; + static const uint8_t ins16[] = { 0xc1, 0x16, 0x00, 0x00, 0x06 }; // get nr. of providers + int32_t prov = 0; + uint16_t pmap = 0; // provider-maptable + + int16_t tries = 0; + int16_t i = 0; + + while(reader->nprov == 0 && tries < 254) + { + write_cmd(ins16, NULL); // read nr of providers + pmap = cta_res[2] << 8 | cta_res[3]; + + for(reader->nprov = 0, i = pmap; i; i >>= 1) + { + reader->nprov += i & 1; + } + + if(reader->nprov == 0) + { + tries++; + continue; + } + } + + for(prov = 0; prov < reader->nprov; prov++) + { + set_provider_info(reader, prov); + } + return OK; +} + +const struct s_cardsystem reader_seca = +{ + .desc = "seca", + .caids = (uint16_t[]){ 0x01, 0 }, + .do_emm = seca_do_emm, + .do_ecm = seca_do_ecm, + .card_info = seca_card_info, + .card_init = seca_card_init, + .get_emm_type = seca_get_emm_type, + .get_emm_filter = seca_get_emm_filter, +}; + +#endif diff --git a/reader-tongfang.c b/reader-tongfang.c new file mode 100644 index 0000000..10635a8 --- /dev/null +++ b/reader-tongfang.c @@ -0,0 +1,644 @@ +#include "globals.h" +#ifdef READER_TONGFANG +#include "reader-common.h" +#include "cscrypt/des.h" +#include + +// returns 1 if cw_is_valid, returns 0 if cw is all zeros +static int32_t cw_is_valid(uint8_t *cw) +{ + int32_t i; + + for(i = 0; i < 8; i++) + { + if(cw[i] != 0) // test if cw = 00 + { + return OK; + } + } + return ERROR; +} + +static int32_t tongfang_read_data(struct s_reader *reader, uint8_t size, uint8_t *cta_res, uint16_t *status) +{ + uint8_t read_data_cmd[] = {0x00, 0xc0, 0x00, 0x00, 0xff}; + uint16_t cta_lr; + + read_data_cmd[4] = size; + write_cmd(read_data_cmd, NULL); + + *status = (cta_res[cta_lr - 2] << 8) | cta_res[cta_lr - 1]; + + return (cta_lr - 2); +} + +static int32_t tongfang_card_init(struct s_reader *reader, ATR *newatr) +{ + const uint8_t get_ppua_cmdv1[] = {0x00, 0xa4, 0x04, 0x00, 0x05, 0xf9, 0x5a, 0x54, 0x00, 0x06}; + const uint8_t get_ppua_cmdv3[] = {0x80, 0x46, 0x00, 0x00, 0x04, 0x07, 0x00, 0x00, 0x08}; + uint8_t get_serial_cmdv1[] = {0x80, 0x32, 0x00, 0x00, 0x58}; + uint8_t get_serial_cmdv2[] = {0x80, 0x46, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x04}; + uint8_t get_serial_cmdv3[] = {0x80, 0x46, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x14}; + uint8_t get_commkey_cmd[17] = {0x80, 0x56, 0x00, 0x00, 0x0c}; + uint8_t confirm_commkey_cmd[21] = {0x80, 0x4c, 0x00, 0x00, 0x10}; + uint8_t pairing_cmd[9] = {0x80, 0x4c, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0xFF}; + + uint8_t data[257]; + uint8_t card_id[20]; + uint16_t status = 0; + uint8_t boxID[] = {0xFF, 0xFF, 0xFF, 0xFF}; + uint8_t zero[8] = {0}; + uint8_t deskey[8]; + int32_t i; + uint32_t calibsn = 0; + int8_t readsize = 0; + + get_atr; + def_resp; + + if(atr_size == 8 && atr[0] == 0x3B && atr[1] == 0x64) + { + reader->tongfang_version = 1; + } + else if(atr_size > 9 && atr[0] == 0x3B && (atr[1] & 0xF0) == 0x60 && 0 == memcmp(atr + 4, "NTIC", 4)) + { + reader->tongfang_version = atr[8] - 0x30 + 1; + } + else if((atr_size == ((uint32_t)(atr[1] & 0x0F) + 4)) && (atr[0] == 0x3B) && ((atr[1] & 0xF0) == 0x60) && (atr[2] == 0x00) && ((atr[3] & 0xF0) == 0x00)) + { + reader->tongfang_version = 2; + } + else + { + return ERROR; //not yxsb/yxtf + } + + uint32_t cas_version = reader->tongfang_version & 0x00FFFFL; + + reader->caid = 0x4A02; + reader->nprov = 1; + memset(reader->prid, 0x00, sizeof(reader->prid)); + memset(card_id, 0, sizeof(card_id)); + memset(reader->hexserial, 0, 8); + + if(reader->tongfang_boxid > 0) + { + for(i = 0; (size_t)i < sizeof(boxID); i++) + { + boxID[i] = (reader->tongfang_boxid >> (8 * (3 - i))) % 0x100; + } + } + + if(cas_version <= 2) + { //tongfang 1-2 + write_cmd(get_ppua_cmdv1, get_ppua_cmdv1 + 5); + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { + return ERROR; + } + rdr_log(reader, "Tongfang %d card detected", cas_version); + + //get card serial + if(atr[8] == 0x31) + { //degrade card from version 3 + write_cmd(get_serial_cmdv2, get_serial_cmdv2 + 5); + if((cta_res[cta_lr - 2] & 0xf0) != 0x60) + { + rdr_log(reader, "error: get card serial failed."); + return ERROR; + } + readsize = cta_res[cta_lr - 1]; + if(readsize != tongfang_read_data(reader, readsize, data, &status) || status != 0x9000) + { + rdr_log(reader, "error: card get serial data failed."); + return ERROR; + } + memcpy(reader->hexserial + 2, data, 4); + } + else + { + write_cmd(get_serial_cmdv1, get_serial_cmdv1 + 5); + if((cta_res[cta_lr - 2] & 0xf0) != 0x60) + { + rdr_log(reader, "error: get card serial failed."); + return ERROR; + } + readsize = cta_res[cta_lr - 1]; + if(readsize != tongfang_read_data(reader, readsize, data, &status) || status != 0x9000) + { + rdr_log(reader, "error: card get serial data failed."); + return ERROR; + } + memcpy(reader->hexserial + 2, cta_res, 4); + } + + // check pairing + write_cmd(pairing_cmd, pairing_cmd + 5); + if((cta_res[cta_lr - 2] == 0x94) && (cta_res[cta_lr - 1] == 0xB1)) + { + rdr_log_dbg(reader, D_READER, "the card needlessly pairing with any box."); + } + else if((cta_res[cta_lr - 2] == 0x94) && (cta_res[cta_lr - 1] == 0xB2)) + { + if(reader->tongfang_boxid > 0) + { + memcpy(pairing_cmd + 5, boxID, 4); + write_cmd(pairing_cmd, pairing_cmd + 5); + + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { + rdr_log(reader, "error: this card pairing failed with the box, please check your boxid setting."); + //return ERROR; + } + } + else + { + rdr_log(reader, "warning: the card pairing with some box."); + //return ERROR; + } + } + else + { + rdr_log(reader, "error: this card pairing failed with the box(return code:0x%02X%02X).", cta_res[cta_lr - 2], cta_res[cta_lr - 1]); + } + + } + else if(cas_version == 3) + { //tongfang 3 + write_cmd(get_ppua_cmdv3, get_ppua_cmdv3 + 5); + if((cta_res[cta_lr - 2] & 0xf0) != 0x60) + { + return ERROR; + } + readsize = cta_res[cta_lr - 1]; + if(readsize != tongfang_read_data(reader, readsize, data, &status)) + { + rdr_log(reader, "error: get ppua v3 failed."); + return ERROR; + } + + rdr_log(reader, "Tongfang3 card detected"); + + // get commkey + if(!reader->tongfang3_deskey_length) + { + rdr_log(reader, "error: tongfang3_deskey must be configured."); + return ERROR; + } + else + { + memcpy(deskey, reader->tongfang3_deskey, sizeof(reader->tongfang3_deskey)); + } + memcpy(data, zero, sizeof(zero)); + des_ecb_encrypt(data, deskey, 8); + memcpy(get_commkey_cmd + 5, data, 8); + if(reader->tongfang3_calibsn > 0) + { + calibsn = reader->tongfang3_calibsn; + } + else + { + rdr_log(reader, "error: tongfang3_calibsn must be configured."); + return ERROR; + } + get_commkey_cmd[5 + 8] = (calibsn >> 24) & 0xFF; + get_commkey_cmd[5 + 8 + 1] = (calibsn >> 16) & 0xFF; + get_commkey_cmd[5 + 8 + 2] = (calibsn >> 8) & 0xFF; + get_commkey_cmd[5 + 8 + 3] = (calibsn) & 0xFF; + write_cmd(get_commkey_cmd, get_commkey_cmd + 5); + if((cta_res[cta_lr - 2] & 0xf0) != 0x60) + { + rdr_log(reader,"error: get card commkey failed."); + return ERROR; + } + readsize = cta_res[cta_lr - 1]; + if(readsize != tongfang_read_data(reader, readsize, data, &status)) + { + rdr_log(reader, "error: get card seed failed."); + return ERROR; + } + //rdr_log(reader, "card seed got."); + memcpy(reader->tongfang3_commkey, data, 8); + des_ecb_encrypt(reader->tongfang3_commkey, deskey, 8); + + rdr_log_dbg(reader, D_READER, "card commkey got(%llX)",(unsigned long long)b2ll(8,reader->tongfang3_commkey)); + + //get card serial + write_cmd(get_serial_cmdv3, get_serial_cmdv3 + 5); + if((cta_res[cta_lr - 2] & 0xf0) != 0x60) + { + rdr_log(reader, "error: get card serial failed."); + return ERROR; + } + readsize = cta_res[cta_lr - 1]; + if(readsize != tongfang_read_data(reader, readsize, data, &status) || status != 0x9000) + { + rdr_log(reader, "error: card get serial failed."); + return ERROR; + } + //rdr_log(reader, "card serial got."); + + memset(reader->hexserial, 0, 8); + memcpy(reader->hexserial + 2, data, 4); // might be incorrect offset + + memcpy(card_id, data + 4, (readsize - 4) > ((int32_t)sizeof(card_id) - 1) ? (int32_t)sizeof(card_id) - 1 : readsize - 5); + card_id[sizeof(card_id) - 1] = '\0'; + + //confirm commkey and pairing + memcpy(data, reader->stbid, sizeof(reader->stbid)); + des_ecb_encrypt(data, reader->tongfang3_commkey, 8); + + if(reader->tongfang_boxid > 0) + { + memcpy(zero + 2, boxID, 4); + } + memcpy(data + 8, zero, 8); + des_ecb_encrypt(data + 8, reader->tongfang3_commkey, 8); + + memcpy(confirm_commkey_cmd + 5, data, 16); + write_cmd(confirm_commkey_cmd, confirm_commkey_cmd + 5); + + if((cta_res[cta_lr - 2] & 0xf0) == 0x60) + { + readsize = cta_res[cta_lr - 1]; + if(readsize != tongfang_read_data(reader, readsize, data, &status)) + { + rdr_log(reader, "error: confirm commkey failed(read response data failed)."); + } + + if(data[0] == 0x90 && data[1] == 0x00) + { + rdr_log_dbg(reader, D_READER, "the card pairing with any box succeed."); + } + else if(data[0] == 0x94 && data[1] == 0xB1) + { + rdr_log_dbg(reader, D_READER, "the card needlessly pairing with any box"); + } + else if (data[0] == 0x94 && data[1] == 0xB2) + { + rdr_log(reader, "error: this card pairing failed with the box, please check your boxid setting."); + } + } + else + { + if(cta_res[cta_lr - 2] == 0x90 && cta_res[cta_lr - 1] == 0x00) + { + rdr_log_dbg(reader, D_READER, "the card pairing with any box succeed."); + } + else if(cta_res[cta_lr - 2] == 0x94 && cta_res[cta_lr - 1] == 0xB1) + { + rdr_log_dbg(reader, D_READER, "the card needlessly pairing with any box"); + } + else if(cta_res[cta_lr - 2] == 0x94 && cta_res[cta_lr - 1] == 0xB2) + { + rdr_log(reader, "error: this card pairing failed with the box, please check your boxid setting."); + } + else if(cta_res[cta_lr - 2] == 0x94 && cta_res[cta_lr - 1] == 0xB4) + { + rdr_log(reader, "error: this card initializing failed, please check your deskey setting, calibsn setting, and verify that stbid starts with 0000 or 0100."); + } + else + { + rdr_log(reader, "error: confirm commkey failed(return code:0x%02X%02X).", cta_res[cta_lr - 2], cta_res[cta_lr - 1]); + } + } + } + else + { + rdr_log(reader, "error: NTIC%c card not support yet!", atr[8]); + return ERROR; + } + + rdr_log_sensitive(reader, "type: Tongfang, caid: %04X, serial: {%llu}, hex serial: {%llX}, Card ID: {%s}, BoxID: {%08X}", reader->caid, (unsigned long long) b2ll(6, reader->hexserial), (unsigned long long) b2ll(4, reader->hexserial + 2), card_id, b2i(4, boxID)); + + return OK; +} + +/* +Example ecm: +03 85 80 70 61 8E 2A 16 4F 00 12 0F 21 5A E5 6A +8F 4D C1 57 4E 24 2A 38 3C 26 8A 4C C2 74 A1 23 +9F 12 43 80 3A 16 4F 3E 8E 2A C0 40 0F 22 94 E4 +6A 89 F1 09 38 8F DF 3D 08 A6 29 1A 61 98 31 82 +7F 34 55 74 0E A3 54 38 01 09 00 01 00 01 D9 31 +A5 1B 8B CA A8 95 E0 D1 24 7D 36 8C F6 89 4A F7 +B2 3A 74 3D D1 D4 + +Example ecm: +81 70 76 22 91 14 96 01 0C 17 C4 00 12 09 5A 00 +98 80 B0 D8 65 32 1B 26 03 F0 21 3B 8C 07 15 12 +58 80 3A 14 96 53 22 91 C0 04 17 C5 61 C0 FF 3A +D9 3C EE 51 CD 6E 70 A2 EC 71 FF 0F D6 E8 52 D6 +69 C2 7F 07 0F 83 02 09 00 01 00 01 B5 AC C0 8D +7A B0 65 +*/ +static int32_t tongfang_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + uint8_t ecm_buf[512]; //{0x80,0x3a,0x00,0x01,0x53}; + uint8_t *ecm_cmd = ecm_buf; + int32_t ecm_len = 0; + uint8_t data[256] = {0}; + char *tmp; + int32_t i = 0; + size_t write_len = 0; + size_t read_size = 0; + size_t data_len = 0; + uint16_t status = 0; + + uint32_t cas_version = reader->tongfang_version & 0x00FFFFL; + + def_resp; + + if(cs_malloc(&tmp, er->ecmlen * 3 + 1)) + { + rdr_log_dbg(reader, D_IFD, "ECM: %s", cs_hexdump(1, er->ecm, er->ecmlen, tmp, er->ecmlen * 3 + 1)); + NULLFREE(tmp); + } + + if((ecm_len = check_sct_len(er->ecm, 3)) < 0) + { + rdr_log(reader, "error: check_sct_len failed, smartcard section too long %d > %zd", SCT_LEN(er->ecm), sizeof(er->ecm) - 3); + return ERROR; + } + + for(i = 0; i < ecm_len; i++) + { + if ((i < (ecm_len - 1)) && (er->ecm[i] == 0x80) && (er->ecm[i + 1] == 0x3a) && (er->ecm[i + 2] == er->ecm[5]) && (er->ecm[i + 3] == er->ecm[6])) + { + break; + } + } + if(i == ecm_len) + { + rdr_log(reader, "error: invalid ecm data..."); + return ERROR; + } + + write_len = er->ecm[i + 4] + 5; + if(write_len > (sizeof(ecm_buf))) + { + if(write_len > MAX_ECM_SIZE || !cs_malloc(&ecm_cmd,write_len)) + { + rdr_log(reader, "error: ecm data too long,longer than sizeof ecm_buf(%zd > %zd).", write_len, sizeof(ecm_cmd)); + return ERROR; + } + } + + memcpy(ecm_cmd, er->ecm + i, write_len); + write_cmd(ecm_cmd, ecm_cmd + 5); + if((cta_lr - 2) >= 2) + { + read_size = cta_res[1]; + } + else + { + if((cta_res[cta_lr - 2] & 0xf0) == 0x60) + { + read_size = cta_res[cta_lr - 1]; + } + else + { + char ecm_cmd_string[150]; + rdr_log(reader, "error: card send parsing ecm command failed!(%s)", cs_hexdump(1, ecm_cmd, write_len, ecm_cmd_string, sizeof(ecm_cmd_string))); + if(ecm_cmd != ecm_buf) + { + NULLFREE(ecm_cmd); + } + return ERROR; + } + } + + if(ecm_cmd != ecm_buf) + { + NULLFREE(ecm_cmd); + } + + if(read_size > sizeof(data)) + { + rdr_log(reader, "error: read_size is bigger than sizeof data.(%zd>%zd)", read_size, sizeof(data)); + return ERROR; + } + + data_len = tongfang_read_data(reader, read_size, data, &status); + if(data_len < 23) + { + char ecm_string[256 * 3 + 1]; + rdr_log(reader, "error: card return cw data failed, return data len=%zd(ECM:%s).", data_len, cs_hexdump(1, er->ecm, er->ecmlen, ecm_string, sizeof(ecm_string))); + return ERROR; + } + + if(!(er->ecm[0] & 0x01)) + { + memcpy(ea->cw, data + 8, 16); + } + else + { + memcpy(ea->cw, data + 16, 8); + memcpy(ea->cw + 8, data + 8, 8); + } + + // All zeroes is no valid CW, can be a result of wrong boxid + if(!cw_is_valid(ea->cw) || !cw_is_valid(ea->cw + 8)) + { + rdr_log(reader,"error: cw is invalid."); + return ERROR; + } + + if(cas_version == 3) + { + des_ecb_encrypt(ea->cw, reader->tongfang3_commkey, 8); + des_ecb_encrypt(ea->cw + 8, reader->tongfang3_commkey, 8); + } + + return OK; +} + +static int32_t tongfang_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + uint8_t emm_cmd[200]; + def_resp; + int32_t write_len; + + if(SCT_LEN(ep->emm) < 8) + { + return ERROR; + } + + write_len = ep->emm[15] + 5; + memcpy(emm_cmd, ep->emm + 11, write_len); + + write_cmd(emm_cmd, emm_cmd + 5); + + return OK; +} + +static int32_t tongfang_card_info(struct s_reader *reader) +{ + static const uint8_t get_provider_cmd[] = {0x80, 0x44, 0x00, 0x00, 0x08}; + uint8_t get_subscription_cmd[] = {0x80, 0x48, 0x00, 0x01, 0x04, 0x01, 0x00, 0x00, 0x13}; + static const uint8_t get_agegrade_cmd[] = {0x80, 0x46, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x09}; + def_resp; + int32_t i; + uint8_t data[256]; + uint16_t status = 0; + + write_cmd(get_provider_cmd, NULL); + if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1] != 0x00)) + { + return ERROR; + } + + reader->nprov = 0; + memset(reader->prid, 0x00, sizeof(reader->prid)); + + for(i = 0; i < 4; i++) + { + if(((cta_res[i * 2] != 0xFF) || (cta_res[i * 2 + 1] != 0xFF)) && ((cta_res[i * 2] != 0x00) || (cta_res[i * 2 + 1] != 0x00))) + { + int j; + int found = 0; + for(j = 0; j < reader->nprov; j++) + { + if(reader->nprov > 0 && reader->prid[j][2] == cta_res[i * 2] && reader->prid[j][3] == cta_res[i * 2 + 1]) + { + found = 1; + break; + } + } + + if(found == 1) + { + continue; + } + + memcpy(&reader->prid[reader->nprov][2], cta_res + i * 2, 2); + rdr_log(reader, "Provider:%06X", b2i(2, cta_res + i * 2)); + reader->nprov ++; + } + } + + cs_clear_entitlement(reader); + + for(i = 0; i < reader->nprov; i++) + { + get_subscription_cmd[2] = reader->prid[i][2]; + get_subscription_cmd[3] = reader->prid[i][3]; + write_cmd(get_subscription_cmd, get_subscription_cmd + 5); + if((cta_res[cta_lr - 2] & 0xF0) != 0x60) + { + continue; + } + if((3 > tongfang_read_data(reader, cta_res[cta_lr - 1], data, &status)) || (status != 0x9000)) + { + continue; + } + + uint16_t count = data[2]; + int j; + for(j = 0; j < count; j++) + { + if(!data[j * 13 + 4]) + { + continue; + } + + time_t start_t, end_t; + //946656000L = 2000-01-01 00:00:00 + start_t = 946656000L + b2i(2, data + j * 13 + 8) * 24 * 3600L; + end_t = 946656000L + b2i(2, data + j * 13 + 12) * 24 * 3600L; + uint64_t product_id = b2i(2, data + j * 13 + 5); + + struct tm tm_start, tm_end; + char start_day[11], end_day[11]; + + localtime_r(&start_t, &tm_start); + localtime_r(&end_t, &tm_end); + strftime(start_day, sizeof(start_day), "%Y/%m/%d", &tm_start); + strftime(end_day, sizeof(end_day), "%Y/%m/%d", &tm_end); + + if(!j) + { + rdr_log(reader, "entitlements for provider: %d (%04X:%06X)", i, reader->caid, b2i(2, &reader->prid[i][2])); + } + + rdr_log(reader, " chid: %04"PRIX64" date: %s - %s", product_id, start_day, end_day); + + cs_add_entitlement(reader, reader->caid, b2i(2, &reader->prid[i][2]), product_id, 0, start_t, end_t, 4, 1); + } + } + + write_cmd(get_agegrade_cmd, get_agegrade_cmd + 5); + if((cta_res[cta_lr - 2] & 0xF0) != 0x60) + { + return OK; + } + + tongfang_read_data(reader, cta_res[cta_lr - 1], data, &status); + if(status != 0x9000) + { + return OK; + } + + rdr_log(reader, "AgeGrade:%d", (data[0] + 3)); + + return OK; +} + +static int32_t tongfang_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + switch(ep->emm[0]) + { + case 0x82: + ep->type = SHARED; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 5, 3); + return (!memcmp(rdr->hexserial + 2, ep->hexserial, 3)); + + default: + ep->type = UNKNOWN; + return 1; + } +} + +static int32_t tongfang_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 1; + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + + int32_t idx = 0; + + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + memcpy(&filters[idx].filter[3], rdr->hexserial + 2, 3); + memset(&filters[idx].mask[3], 0xFF, 3); + idx++; + + *filter_count = idx; + } + + return OK; +} + +const struct s_cardsystem reader_tongfang = +{ + .desc = "tongfang", + .caids = (uint16_t[]){ 0x4A02, 0 }, + .do_emm = tongfang_do_emm, + .do_ecm = tongfang_do_ecm, + .card_info = tongfang_card_info, + .card_init = tongfang_card_init, + .get_emm_type = tongfang_get_emm_type, + .get_emm_filter = tongfang_get_emm_filter, +}; + +#endif diff --git a/reader-viaccess.c b/reader-viaccess.c new file mode 100644 index 0000000..9fa4871 --- /dev/null +++ b/reader-viaccess.c @@ -0,0 +1,2554 @@ +#include "globals.h" + +#ifdef READER_VIACCESS + +#include "oscam-aes.h" +#include "oscam-time.h" +#include "oscam-emm.h" +#include "reader-common.h" +#include "cscrypt/des.h" +#include "oscam-work.h" + +typedef unsigned int uint; +typedef unsigned char byte; + +struct geo_cache +{ + uint32_t provid; + uint8_t geo[256]; + uint8_t geo_len; + int32_t number_ecm; +}; + +struct viaccess_data +{ + struct geo_cache last_geo; + uint8_t availkeys[CS_MAXPROV][16]; +}; + +struct via_date +{ + uint16_t day_s : 5; + uint16_t month_s : 4; + uint16_t year_s : 7; + uint16_t day_e : 5; + uint16_t month_e : 4; + uint16_t year_e : 7; +}; + +uint8_t N98Init = 0; +uint KeyS[132]; +char SBoxInverse[] = +{ + 13, 3, 11, 0, 10, 6, 5, 12, 1, 14, 4, 7, 15, 9, 8, 2, + 5, 8, 2, 14, 15, 6, 12, 3, 11, 4, 7, 9, 1, 13, 10, 0, + 12, 9, 15, 4, 11, 14, 1, 2, 0, 3, 6, 13, 5, 8, 10, 7, + 0, 9, 10, 7, 11, 14, 6, 13, 3, 5, 12, 2, 4, 8, 15, 1, + 5, 0, 8, 3, 10, 9, 7, 14, 2, 12, 11, 6, 4, 15, 13, 1, + 8, 15, 2, 9, 4, 1, 13, 14, 11, 6, 5, 3, 7, 12, 10, 0, + 15, 10, 1, 13, 5, 3, 6, 0, 4, 9, 14, 7, 2, 12, 8, 11, + 3, 0, 6, 13, 9, 14, 15, 8, 5, 12, 11, 7, 10, 1, 4, 2 +}; + +static void parse_via_date(const uint8_t *buf, struct via_date *vd, int32_t fend) +{ + uint16_t date; + date = (buf[0] << 8) | buf[1]; + vd->day_s = date & 0x1f; + vd->month_s = (date >> 5) & 0x0f; + vd->year_s = (date >> 9) & 0x7f; + + if(fend) + { + date = (buf[2] << 8) | buf[3]; + vd->day_e = date & 0x1f; + vd->month_e = (date >> 5) & 0x0f; + vd->year_e = (date >> 9) & 0x7f; + } +} + +struct emm_rass *find_rabuf(struct s_client *client, int32_t provid, uint8_t nano, int8_t add) +{ + struct emm_rass *e; + LL_ITER it; + + if(!client->ra_buf) + { + client->ra_buf = ll_create("client->ra_buf"); + } + + it = ll_iter_create(client->ra_buf); + + while((e = ll_iter_next(&it)) != NULL) + { + if(!add && e->provid == provid && e->emmlen != 0) + { + return e; + } + if(add && e->provid == provid && e->emm[0] == nano) + { + return e; + } + } + + if(!add) + { + return NULL; + } + + if(!cs_malloc(&e, sizeof(struct emm_rass))) + { + return NULL; + } + + e->provid = provid; + ll_append(client->ra_buf, e); + + return e; +} + +static void show_class(struct s_reader *reader, const char *p, uint32_t provid, const uint8_t *b, int32_t l) +{ + int32_t i, j; + // b -> via date (4 uint8_ts) + b += 4; + l -= 4; + j = l - 1; + + for(; j >= 0; j--) + { + for(i = 0; i < 8; i++) + { + if(b[j] & (1 << (i & 7))) + { + uint8_t cls; + struct via_date vd; + parse_via_date(b - 4, &vd, 1); + cls = (l - (j + 1)) * 8 + i; + + if(p) // just show class info, dont add entitlement! + { + rdr_log(reader, "%sclass: %02X, expiry date: %04d/%02d/%02d - %04d/%02d/%02d", p, cls, + vd.year_s + 1980, vd.month_s, vd.day_s, + vd.year_e + 1980, vd.month_e, vd.day_e); + } + else + { + rdr_log(reader, "class: %02X, expiry date: %04d/%02d/%02d - %04d/%02d/%02d", cls, + vd.year_s + 1980, vd.month_s, vd.day_s, + vd.year_e + 1980, vd.month_e, vd.day_e); + //convert time: + time_t start_t, end_t; + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_year = vd.year_s + 80; //via year starts in 1980, tm_year starts in 1900 + tm.tm_mon = vd.month_s - 1; // january is 0 in tm_mon + tm.tm_mday = vd.day_s; + start_t = cs_timegm(&tm); + tm.tm_year = vd.year_e + 80; //via year starts in 1980, tm_year starts in 1900 + tm.tm_mon = vd.month_e - 1; // january is 0 in tm_mon + tm.tm_mday = vd.day_e; + end_t = cs_timegm(&tm); + cs_add_entitlement(reader, reader->caid, provid, cls, cls, start_t, end_t, 5, 1); + } + } + } + } +} + +static int8_t add_find_class(struct s_reader *reader, uint32_t provid, const uint8_t *b, int32_t l, int8_t add) +{ + int32_t i, j, freshdate = 0; + // b -> via date (4 uint8_ts) + b += 4; + l -= 4; + j = l - 1; + + for(; j >= 0; j--) + { + for(i = 0; i < 8; i++) + { + if(b[j] & (1 << (i & 7))) + { + uint8_t cls; + cls = (l - (j + 1)) * 8 + i; + if(cs_add_entitlement(reader, reader->caid, provid, cls, cls, 0, 0, 5, 0) == NULL && !add) + { + rdr_log(reader, "provid %06X class %02X not found", provid, cls); + freshdate = 1; + } + else + { + if(!add) + { + rdr_log(reader, "provid %06X has matching class %02X", provid, cls); + } + struct via_date vd; + parse_via_date(b - 4, &vd, 1); + time_t start_t, end_t; + struct tm tm; + //convert time: + memset(&tm, 0, sizeof(tm)); + tm.tm_year = vd.year_s + 80; // via year starts in 1980, tm_year starts in 1900 + tm.tm_mon = vd.month_s - 1; // january is 0 in tm_mon + tm.tm_mday = vd.day_s; + start_t = cs_timegm(&tm); + tm.tm_year = vd.year_e + 80; //via year starts in 1980, tm_year starts in 1900 + tm.tm_mon = vd.month_e - 1; // january is 0 in tm_mon + tm.tm_mday = vd.day_e; + end_t = cs_timegm(&tm); + + if(cs_add_entitlement(reader, reader->caid, provid, cls, cls, start_t, end_t, 5, add) != NULL) + { + if(!add) + { + rdr_log(reader, "class %02X provid %06X has already this daterange or newer entitled", cls, provid); + } + } + else + { + freshdate = 1; + } + } + } + } + } + + if(freshdate == 0) + { + return -2; + } + + return 1; // emmdate is fresh! +} + +static void show_subs(struct s_reader *reader, const uint8_t *emm) +{ + switch(emm[0]) + { + case 0xA9: + { + show_class(reader, "nano A9: ", 1, emm + 2, emm[1]); + break; + } + + case 0xA6: + { + char szGeo[256]; + memset(szGeo, 0, 256); + cs_strncpy(szGeo, (char *)emm + 2, emm[1]); + rdr_log(reader, "nano A6: geo %s", szGeo); + break; + } + + case 0xB6: + { + uint8_t m; // modexp + struct via_date vd; + m = emm[emm[1] + 1]; + parse_via_date(emm + 2, &vd, 0); + rdr_log(reader, "nano B6: modexp %d%d%d%d%d%d: %02d/%02d/%04d", + (m & 0x20) ? 1 : 0, + (m & 0x10) ? 1 : 0, + (m & 0x08) ? 1 : 0, + (m & 0x04) ? 1 : 0, + (m & 0x02) ? 1 : 0, + (m & 0x01) ? 1 : 0, + vd.day_s, vd.month_s, vd.year_s + 1980); + break; + } + } +} + +static int32_t chk_prov(struct s_reader *reader, uint8_t *id, uint8_t keynr) +{ + struct viaccess_data *csystem_data = reader->csystem_data; + + int32_t i, j, rc; + for(rc = i = 0; (!rc) && (i < reader->nprov); i++) + { + if(!memcmp(&reader->prid[i][1], id, 3)) + { + for(j = 0; (!rc) && (j < 16); j++) + { + if(csystem_data->availkeys[i][j] == keynr) + { + rc = 1; + } + } + } + } + + return (rc); +} + +static int32_t get_maturity(struct s_reader *reader) +{ + /* retrieve maturity rating on the card */ + def_resp; + uint8_t insac[] = { 0xca, 0xac, 0x00, 0x00, 0x00 }; // select data + uint8_t insb8[] = { 0xca, 0xb8, 0x00, 0x00, 0x00 }; // read selected data + insac[2] = 0x06; + write_cmd(insac, NULL); // request maturity rating + insb8[4] = 0x02; + write_cmd(insb8, NULL); // read maturity rating nano + len + insb8[4] = cta_res[1]; + write_cmd(insb8, NULL); // read maturity rating + reader->maturity = cta_res[cta_lr - 3] & 0x0F; + + if (reader->maturity < 0xF) + { + rdr_log(reader, "Maturity level [%X]= older than %i years", reader->maturity, reader->maturity); + } + else + { + rdr_log(reader, "Maturity level [%X]=no age limit", reader->maturity); + } + + return 0; +} + +static int32_t unlock_parental(struct s_reader *reader) +{ + /* disabling parental lock. assuming pin "0000" if no pin code is provided in the config */ + static const uint8_t inDPL[] = { 0xca, 0x24, 0x02, 0x00, 0x09 }; + uint8_t cmDPL[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F }; + def_resp; + + if(strcmp(reader->pincode, "none")) + { + rdr_log(reader, "Using PIN %s", reader->pincode); + // the pin need to be coded in bcd, so we need to convert from ascii to bcd, so '1234' -> 0x12 0x34 + cmDPL[6] = ((reader->pincode[0] - 0x30) << 4) | ((reader->pincode[1] - 0x30) & 0x0f); + cmDPL[7] = ((reader->pincode[2] - 0x30) << 4) | ((reader->pincode[3] - 0x30) & 0x0f); + } + else + { + rdr_log(reader, "Using PIN 0000!"); + } + + write_cmd(inDPL, cmDPL); + + if(!(cta_res[cta_lr - 2] == 0x90 && cta_res[cta_lr - 1] == 0)) + { + if(strcmp(reader->pincode, "none")) + { + rdr_log(reader, "Can't disable parental lock. Wrong PIN? OSCam used %s!", reader->pincode); + } + else + { + rdr_log(reader, "Can't disable parental lock. Wrong PIN? OSCam used 0000!"); + } + } + else + { + rdr_log(reader, "Parental lock disabled"); + get_maturity(reader); + } + + return 0; +} + +int32_t hdSurEncBasicCrypt_D2_0F_11(int32_t Value, int32_t XorVal) +{ + int32_t i = (Value << 13) - Value + 0x1B59; + i = (i * Value) + 0x07CF; + return (i ^ XorVal); +} + +int32_t hdSurEncCryptLookup_D2_0F_11(uint8_t Value, uint8_t AddrInd) +{ + static const uint8_t lookup[] = + { + 0x94, 0xB2, 0xA9, 0x79, 0xC4, 0xC7, 0x0D, 0x36, 0x6F, 0x24, 0x11, 0xD1, 0xDB, 0x59, 0xD2, 0xA5, + 0xE1, 0x00, 0xD4, 0x97, 0xA3, 0x2B, 0x11, 0xFA, 0x5F, 0xF1, 0xC1, 0x44, 0xBF, 0x9B, 0x5A, 0xC8, + 0xF1, 0xE1, 0x99, 0x82, 0x0E, 0xB2, 0x01, 0x09, 0x0C, 0xC8, 0xB3, 0x3B, 0xD1, 0x80, 0x50, 0xE8, + 0xF5, 0x52, 0x4C, 0xE6, 0x82, 0xAC, 0x58, 0x40, 0xD4, 0x71, 0x87, 0x52, 0x06, 0xEA, 0xA6, 0x27, + 0xB7, 0xFE, 0x6C, 0x49, 0x47, 0x3B, 0x70, 0x6C, 0xEB, 0xCD, 0xC5, 0x0B, 0x8C, 0x31, 0x29, 0x42, + 0x4E, 0x10, 0x2B, 0x2D, 0x46, 0xEC, 0x39, 0xA3, 0x90, 0x4B, 0x25, 0x60, 0x9C, 0x62, 0xD4, 0x20, + 0xF6, 0x16, 0xA8, 0x9C, 0xE4, 0x20, 0xED, 0xC7, 0xBA, 0x5E, 0xB6, 0x4E, 0x03, 0x15, 0xA6, 0xF6, + 0x23, 0x98, 0x32, 0xC0, 0xAE, 0xA3, 0xFD, 0xD3, 0x7F, 0xF8, 0xED, 0xF0, 0x29, 0x29, 0x12, 0xB3, + 0xB7, 0x58, 0xAD, 0xA2, 0x58, 0x2C, 0x70, 0x1B, 0xA4, 0x25, 0xE8, 0xA5, 0x43, 0xF1, 0xB9, 0x8F, + 0x1E, 0x3B, 0x10, 0xDF, 0x52, 0xFE, 0x58, 0x29, 0xAD, 0x3F, 0x99, 0x4D, 0xDF, 0xD2, 0x08, 0x06, + 0xA1, 0x1C, 0x66, 0x29, 0x26, 0x80, 0x52, 0x8A, 0x5A, 0x73, 0xE7, 0xDF, 0xC1, 0xC4, 0x47, 0x82, + 0xAB, 0x5C, 0x32, 0xAE, 0x96, 0x04, 0x2B, 0xC3, 0x2D, 0x5A, 0xD2, 0xB0, 0x64, 0x88, 0x97, 0xBF, + 0x7E, 0x99, 0x60, 0xCC, 0x63, 0x76, 0x66, 0xE9, 0x9A, 0x3D, 0xBB, 0xF7, 0x7F, 0xE4, 0x7C, 0x3F, + 0xB8, 0x4D, 0x10, 0x8D, 0x2A, 0xEA, 0x3C, 0xD3, 0x03, 0x74, 0xE6, 0x46, 0xC0, 0x29, 0xAE, 0xB0, + 0x79, 0xBE, 0xCB, 0x18, 0x34, 0xBE, 0x5A, 0xE9, 0x19, 0x8F, 0xA3, 0x8F, 0xD6, 0x6A, 0x6C, 0x88, + 0x1E, 0x21, 0x08, 0x15, 0xC4, 0xE7, 0xE6, 0xBA, 0x97, 0x9C, 0x4F, 0x89, 0x9F, 0x1A, 0x67, 0x4F, + 0xC0, 0xD5, 0x72, 0x51, 0x16, 0xB4, 0xD3, 0x8A, 0x1F, 0xE3, 0x92, 0x02, 0x7F, 0x59, 0x56, 0x8F, + 0x07, 0x8D, 0xC1, 0xC2, 0x42, 0x69, 0x3C, 0xA6, 0xBF, 0x3D, 0xDF, 0x0D, 0xAA, 0x4F, 0x7E, 0x80, + 0x07, 0x11, 0xE2, 0x94, 0x19, 0x9B, 0x16, 0x26, 0x1A, 0x46, 0x09, 0x0D, 0xB5, 0xB8, 0x8E, 0x01, + 0x9C, 0xFE, 0x09, 0xB3, 0x60, 0xC2, 0xAE, 0x50, 0x3C, 0x68, 0x75, 0x4A, 0x57, 0xD8, 0x4F, 0xD7, + 0xA2, 0x76, 0x2C, 0xC1, 0xA2, 0x23, 0xBC, 0x54, 0x2A, 0xDD, 0xF3, 0xDD, 0xA7, 0x34, 0xF7, 0x5C, + 0xF4, 0x86, 0x23, 0x48, 0x7C, 0x3F, 0x05, 0x40, 0x0E, 0xB0, 0xE5, 0xEB, 0x3E, 0xDF, 0x6A, 0x83, + 0x65, 0xA0, 0xB2, 0x06, 0xD1, 0x40, 0x79, 0x0D, 0xDE, 0x95, 0x84, 0x96, 0x87, 0x6F, 0xCE, 0x48, + 0x24, 0x13, 0x0B, 0xF5, 0xC7, 0xF5, 0xA8, 0x7F, 0x2E, 0xC7, 0xE1, 0xBA, 0xAE, 0x2B, 0xF7, 0xF0, + 0x8E, 0xF7, 0x54, 0x0B, 0xF0, 0xD2, 0x41, 0x81, 0x68, 0x3B, 0x1E, 0x35, 0xAB, 0xD9, 0x2B, 0x46, + 0x57, 0xE8, 0x53, 0xDF, 0xDE, 0x10, 0xEF, 0xCB, 0x4C, 0xE0, 0x52, 0x18, 0x2C, 0x4E, 0xB9, 0x20, + 0xE9, 0x7E, 0x85, 0xDF, 0x75, 0x32, 0xE6, 0x10, 0xE9, 0x9C, 0x7B, 0x2E, 0x4C, 0xDA, 0x46, 0xE6, + 0xCC, 0x77, 0x36, 0x1D, 0x4A, 0x15, 0xF5, 0x32, 0x18, 0x6B, 0x7E, 0xAA, 0xCC, 0x97, 0xCC, 0xD1, + 0x2F, 0xE5, 0x58, 0x03, 0x35, 0x35, 0x3D, 0xA0, 0x2B, 0x13, 0x3A, 0x65, 0xFF, 0x24, 0x72, 0xCF, + 0xA7, 0x6D, 0x52, 0x55, 0xF6, 0xC2, 0x30, 0x23, 0x7D, 0x9B, 0x9E, 0xB0, 0x94, 0x02, 0xAD, 0x60, + 0x8A, 0x9F, 0xBC, 0xC8, 0xE4, 0x2B, 0x92, 0x96, 0xF5, 0xAE, 0x04, 0xA4, 0x33, 0x0C, 0x90, 0x67, + 0xF0, 0xB9, 0x1E, 0x7E, 0xBE, 0x02, 0x18, 0xB2, 0x03, 0xB6, 0x40, 0xBF, 0x05, 0xE3, 0x76, 0x98, + 0x21, 0x38, 0xC9, 0x5F, 0xD3, 0x51, 0x8B, 0x43, 0x0B, 0x1A, 0x0B, 0xF9, 0x3C, 0x21, 0x6C, 0x3D, + 0xB8, 0xA0, 0x57, 0xCA, 0x68, 0xCD, 0x1E, 0xD2, 0x2C, 0x50, 0xEE, 0xC0, 0xDF, 0x25, 0x88, 0x52, + 0x37, 0xE1, 0x44, 0xC6, 0x76, 0x3B, 0x91, 0x95, 0x86, 0x76, 0x87, 0x49, 0x21, 0x93, 0x44, 0x0A, + 0x52, 0xB9, 0x2D, 0x2B, 0xE3, 0x1D, 0xB0, 0xE4, 0x98, 0xC6, 0xEE, 0x3D, 0x96, 0x53, 0x4B, 0xFF, + 0x39, 0x00, 0xD5, 0x42, 0x7E, 0xE1, 0x4C, 0x6F, 0xD5, 0xB7, 0xE6, 0x99, 0x2A, 0x5B, 0x67, 0xEE, + 0x3E, 0xBA, 0xF7, 0xEC, 0x43, 0x2A, 0x1C, 0xB6, 0xB5, 0x04, 0x26, 0x59, 0xB1, 0x4C, 0x17, 0xCC, + 0x83, 0xB9, 0x00, 0x3E, 0x36, 0x91, 0x90, 0xF7, 0x5E, 0x38, 0xDC, 0xE4, 0x15, 0xC7, 0x67, 0xF0, + 0xCA, 0xC8, 0xD2, 0x91, 0x5D, 0x74, 0xAC, 0x97, 0x56, 0x36, 0x1A, 0x82, 0x0A, 0xAA, 0xB4, 0x4E, + 0xBF, 0x29, 0x5C, 0xBF, 0x58, 0xB3, 0x97, 0xF9, 0xEB, 0x7C, 0x85, 0xB4, 0xA5, 0x13, 0x2F, 0xD1, + 0xDE, 0x1C, 0xEC, 0x97, 0xDD, 0xE2, 0x39, 0xE4, 0xFB, 0x0A, 0x02, 0xE0, 0xC3, 0xBA, 0x39, 0x79, + 0xAA, 0x1C, 0x37, 0x75, 0x25, 0x54, 0xBE, 0x85, 0x74, 0x2C, 0xFA, 0x0C, 0xFA, 0x50, 0xF6, 0xBE, + 0x9F, 0x2A, 0x53, 0x7C, 0x27, 0x46, 0x68, 0x2D, 0x74, 0x2B, 0x46, 0xDA, 0xF5, 0x07, 0x95, 0x09, + 0x6A, 0x91, 0xB7, 0xB1, 0x34, 0x07, 0x5F, 0xEA, 0xBE, 0x0F, 0x87, 0x28, 0x68, 0x97, 0x43, 0x77, + 0xD5, 0x38, 0x2B, 0x11, 0x11, 0x4F, 0xD9, 0x75, 0x5E, 0xE1, 0x06, 0xA0, 0x3B, 0xAC, 0x32, 0xFE, + 0xBF, 0x73, 0x59, 0x5B, 0xA2, 0xA8, 0x7E, 0x10, 0x4C, 0x6E, 0x78, 0xF0, 0x4A, 0x4E, 0x95, 0xD6, + 0xDD, 0x05, 0x7A, 0xBB, 0xF1, 0xEB, 0xA8, 0xA4, 0x5D, 0x91, 0xF0, 0xED, 0xDB, 0xB8, 0x01, 0x41, + 0xF8, 0x97, 0x7F, 0xC3, 0x91, 0x53, 0xBF, 0xE9, 0xEA, 0x33, 0x1F, 0xDC, 0xA6, 0xE6, 0x8D, 0xCB, + 0x75, 0xD0, 0x69, 0xD0, 0xA4, 0x59, 0xA5, 0x02, 0xFC, 0x60, 0x0D, 0x6A, 0xA0, 0x05, 0x1A, 0x54, + 0x8A, 0xA7, 0x57, 0xA3, 0xF0, 0x90, 0x8A, 0xD5, 0x6F, 0x1E, 0x2E, 0x10, 0x9A, 0x93, 0x2B, 0x51, + 0x2C, 0xFD, 0x99, 0xE5, 0x9B, 0x5D, 0xB2, 0xA7, 0x37, 0x99, 0x26, 0x35, 0xCA, 0xDD, 0x22, 0x19, + 0x59, 0x2A, 0xB0, 0x99, 0x23, 0xDF, 0xA7, 0xA9, 0x85, 0x12, 0xCF, 0xBF, 0xFC, 0x74, 0x80, 0x87, + 0xE1, 0x97, 0xD0, 0xF9, 0xEF, 0x5F, 0x1B, 0x45, 0xF7, 0x76, 0xDB, 0x66, 0x39, 0x05, 0x43, 0x06, + 0xA9, 0x9F, 0x2E, 0x14, 0x9F, 0x1C, 0x0C, 0x1F, 0xD5, 0xD9, 0xA4, 0x8D, 0x18, 0x6F, 0x08, 0x53, + 0x0B, 0x92, 0x9A, 0x0C, 0xEA, 0x4C, 0xE4, 0x1D, 0x9E, 0x9A, 0x51, 0xB8, 0x7E, 0x2D, 0xE7, 0x3C, + 0xFF, 0x84, 0x5C, 0xBF, 0x8F, 0x8C, 0x89, 0x09, 0x1B, 0x7E, 0x4B, 0xE7, 0x85, 0xEC, 0x04, 0xB5, + 0x20, 0x18, 0x1E, 0x55, 0xD5, 0x5B, 0xAC, 0xC6, 0x25, 0x5A, 0xA1, 0x81, 0xC1, 0x31, 0x9C, 0xF5, + 0xB5, 0x54, 0x07, 0x65, 0x0A, 0x5B, 0x90, 0x06, 0x4F, 0x84, 0xB2, 0x7F, 0xD1, 0xAD, 0x16, 0x81, + 0x25, 0xAF, 0xAF, 0xE2, 0x03, 0xA9, 0x1F, 0x13, 0x02, 0x5D, 0x54, 0x89, 0xCD, 0x44, 0x51, 0xEB, + 0xA4, 0x2B, 0xBD, 0x47, 0xB0, 0xB6, 0x27, 0x1D, 0x9B, 0x14, 0x6F, 0xBF, 0xCD, 0x59, 0xBC, 0x0A, + 0x37, 0xA8, 0x74, 0x7D, 0x16, 0x90, 0x28, 0xD5, 0x94, 0xC3, 0xE4, 0x23, 0xC4, 0x98, 0x91, 0xCE, + 0x55, 0xBD, 0x21, 0x3B, 0x84, 0xBD, 0x44, 0x3C, 0xF9, 0xCD, 0x37, 0x43, 0x4A, 0xC6, 0x8C, 0x23, + 0x04, 0x28, 0x63, 0x7A, 0x03, 0x85, 0xD2, 0x46, 0x93, 0xCA, 0xFE, 0xC3, 0x83, 0x0B, 0x13, 0xCC, + 0x5D, 0xCB, 0xBA, 0xCA, 0x68, 0xAB, 0x05, 0xF7, 0xEC, 0x4A, 0x9C, 0x0F, 0xD5, 0xC4, 0x5A, 0xA5, + 0xA0, 0x04, 0x41, 0x6A, 0xF6, 0xEF, 0x16, 0x9B, 0x69, 0x38, 0xF6, 0x2D, 0xAA, 0xEB, 0x2D, 0xE2, + 0x82, 0xA2, 0x9F, 0x6F, 0xBD, 0x2A, 0xE3, 0x66, 0x6B, 0x21, 0xDA, 0x56, 0xAD, 0x82, 0x2B, 0x93, + 0xF3, 0x25, 0xEA, 0xFC, 0xFD, 0xFD, 0x1B, 0xA9, 0xFC, 0xB8, 0xC6, 0x98, 0x45, 0xF2, 0x70, 0x03, + 0x4A, 0x9C, 0x60, 0x82, 0x65, 0xB6, 0x68, 0x4C, 0xE7, 0x41, 0x10, 0x9D, 0x59, 0x40, 0x03, 0x02, + 0x07, 0x12, 0x33, 0xAF, 0x79, 0xE1, 0xC4, 0xEB, 0xB8, 0xCE, 0x6A, 0x90, 0x72, 0x61, 0x5D, 0x56, + 0xC7, 0x59, 0x31, 0xCB, 0x45, 0x2D, 0x42, 0x9F, 0x10, 0x1D, 0x09, 0x63, 0x59, 0x8C, 0x6C, 0xDB, + 0x11, 0xCF, 0xA1, 0xDF, 0x5F, 0x4D, 0xDF, 0xB4, 0xC3, 0x82, 0xEE, 0x58, 0x16, 0xB4, 0x74, 0xFA, + 0xBE, 0x11, 0x9C, 0x1E, 0x98, 0x29, 0xDE, 0xE3, 0xE5, 0x9E, 0xCF, 0xD7, 0x91, 0x0A, 0xA3, 0xA4, + 0x42, 0xA1, 0x95, 0x09, 0x9E, 0x16, 0xD5, 0xA8, 0x24, 0x56, 0x5B, 0x23, 0xC8, 0x56, 0x4C, 0xCB, + 0x89, 0x18, 0x69, 0xEB, 0x0C, 0x1F, 0xC0, 0x41, 0x5C, 0x63, 0x04, 0x68, 0xB2, 0x0F, 0x3F, 0x88, + 0x36, 0xDD, 0x23, 0x4D, 0x4C, 0xC0, 0x81, 0xE3, 0xE9, 0xAD, 0xE0, 0x27, 0xD5, 0xE5, 0x46, 0xEB, + 0xFF, 0x32, 0xA2, 0xB7, 0x14, 0x64, 0x0B, 0x6D, 0x1B, 0xE5, 0xD8, 0xAE, 0x9D, 0xE8, 0x55, 0xB9, + 0x52, 0x70, 0x59, 0xB8, 0x72, 0x92, 0x69, 0x37, 0x95, 0x61, 0x0A, 0xE5, 0xF6, 0x55, 0x97, 0x1D, + 0xBF, 0xF7, 0x29, 0x77, 0x0F, 0x72, 0x80, 0xB2, 0x7E, 0x56, 0xBF, 0xFD, 0xE9, 0xF5, 0x9B, 0x62, + 0xE9, 0xBD, 0x0B, 0xC2, 0x07, 0x55, 0x31, 0x4C, 0x57, 0x3A, 0x05, 0xB9, 0x27, 0x41, 0x4A, 0xC3, + 0xEC, 0x72, 0x20, 0xB3, 0x0C, 0xF9, 0xD9, 0x3A, 0x14, 0x6A, 0x03, 0x44, 0x6A, 0xF1, 0x41, 0x55, + 0x7F, 0x81, 0xC2, 0x04, 0xA8, 0x05, 0xB9, 0x49, 0x2E, 0x43, 0xC4, 0x00, 0x87, 0x86, 0x04, 0xAC, + 0xAF, 0x73, 0x78, 0x0E, 0xA4, 0x43, 0x5B, 0x36, 0xA2, 0x8F, 0x9C, 0xF7, 0x66, 0x4A, 0x5A, 0x09, + 0x6B, 0xAA, 0x69, 0x6F, 0xB1, 0x20, 0x0D, 0x56, 0x85, 0x0A, 0x5E, 0x06, 0xBF, 0xE2, 0x32, 0xB4, + 0x5C, 0x46, 0x33, 0x0D, 0x27, 0xA3, 0x6B, 0xE1, 0xB2, 0x6A, 0x7D, 0x4A, 0xA7, 0x81, 0x0F, 0x2B, + 0x16, 0x7C, 0x51, 0xD6, 0xC0, 0x3D, 0xB9, 0xFE, 0xB4, 0x66, 0xC4, 0xB6, 0x54, 0x53, 0x67, 0xDA, + 0x70, 0x96, 0x9A, 0x0A, 0x07, 0x1A, 0x26, 0xBA, 0x85, 0x50, 0xF5, 0x27, 0x53, 0x9C, 0x3A, 0x94, + 0x0A, 0x7D, 0xDB, 0xE1, 0xC3, 0xE3, 0x6A, 0x3E, 0x9E, 0xD5, 0x13, 0x0A, 0xA3, 0xD2, 0x21, 0x75, + 0x79, 0x17, 0x26, 0xAC, 0x48, 0x5F, 0x3D, 0xE1, 0x7D, 0xA4, 0xB1, 0x56, 0x0F, 0x92, 0x2C, 0x60, + 0xE6, 0xCB, 0x87, 0x35, 0xB8, 0x75, 0xC3, 0xA2, 0x03, 0x50, 0x4B, 0xA2, 0x6E, 0x01, 0xE1, 0xDD, + 0x87, 0xA5, 0x33, 0xC6, 0x2F, 0xA2, 0x41, 0xFC, 0x72, 0x98, 0xA2, 0x69, 0x4C, 0x3F, 0xF0, 0x53, + 0xF5, 0x41, 0x2B, 0x23, 0x24, 0x3B, 0xCE, 0x9D, 0x39, 0x31, 0x17, 0x08, 0xE1, 0x3F, 0x5F, 0xFB, + 0x00, 0xFA, 0xF1, 0xE3, 0xE1, 0x7B, 0x0C, 0xDF, 0x8D, 0xA2, 0xC4, 0xCD, 0x62, 0x3D, 0xAE, 0xC7, + 0x48, 0x09, 0x1C, 0x66, 0xCB, 0x0E, 0x23, 0xE8, 0x1B, 0x9F, 0x1B, 0xCB, 0xF8, 0x14, 0xC3, 0x34, + 0x91, 0x32, 0x2B, 0x39, 0x1C, 0xBA, 0x1C, 0xA0, 0x19, 0xF2, 0x57, 0x9D, 0x78, 0x00, 0x55, 0x1F, + 0x15, 0x12, 0x9A, 0xA2, 0xF2, 0xC2, 0xB7, 0x4E, 0xEA, 0x46, 0x01, 0xC2, 0xE9, 0x76, 0xBF, 0xDE, + 0xCF, 0x8B, 0xC7, 0x50, 0x80, 0xEE, 0x46, 0x91, 0x93, 0x1E, 0x5C, 0x48, 0x5D, 0xC8, 0xC8, 0x63, + 0xD1, 0x89, 0x02, 0x29, 0xE9, 0x90, 0x9F, 0x0B, 0x0A, 0x1A, 0x44, 0x17, 0xE7, 0x4E, 0xAD, 0x58, + 0x55, 0xF8, 0x38, 0xF6, 0x4F, 0xD8, 0x1C, 0x7E, 0x25, 0x9B, 0x59, 0x16, 0xBC, 0x65, 0x24, 0xC5, + 0xA7, 0x56, 0xE5, 0x20, 0x3F, 0xD9, 0x27, 0xE0, 0x32, 0x24, 0xE1, 0x7B, 0xE1, 0x32, 0xEA, 0xF4, + 0xFE, 0xD9, 0xA5, 0xFF, 0x35, 0xAE, 0xA9, 0x1B, 0x38, 0x28, 0x6A, 0xC0, 0x1A, 0x42, 0xD9, 0x5E, + 0x14, 0x2C, 0xC2, 0x2D, 0x9B, 0x94, 0x5B, 0xCF, 0x83, 0x30, 0xB9, 0x06, 0xAF, 0x4B, 0xD7, 0xF6, + 0x38, 0x7C, 0xFF, 0xB4, 0xA5, 0x1A, 0xA0, 0xE9, 0xF3, 0x01, 0xE3, 0x97, 0xC4, 0xA9, 0x57, 0xF5, + 0xB9, 0x96, 0xA7, 0xA3, 0xB8, 0x10, 0x0E, 0xFB, 0x1D, 0x39, 0x44, 0x16, 0x97, 0x94, 0x3E, 0x5F, + 0xAF, 0x0F, 0xE3, 0x99, 0xDC, 0xA0, 0xE9, 0x8D, 0x26, 0x2B, 0xD9, 0xAE, 0xEC, 0x4C, 0x4F, 0x09, + 0x86, 0x7E, 0x7B, 0xC3, 0xE3, 0xC6, 0x17, 0xAE, 0x30, 0x9C, 0x31, 0xD1, 0x84, 0x47, 0xAF, 0xCB, + 0xEA, 0x69, 0x2A, 0x08, 0x3E, 0x13, 0x00, 0xDE, 0xF6, 0x4A, 0x42, 0xD3, 0xBE, 0x33, 0xD9, 0x50, + 0x6B, 0x8D, 0x59, 0x12, 0x1A, 0xD3, 0xA7, 0x7C, 0x0A, 0xE7, 0x87, 0x47, 0xCA, 0xAA, 0x33, 0xFD, + 0xC1, 0xF6, 0x28, 0xC1, 0x62, 0xA2, 0x4C, 0x79, 0x83, 0x48, 0x86, 0x0E, 0xA4, 0x67, 0x34, 0x95, + 0xAE, 0x7D, 0xD6, 0xEE, 0x91, 0x05, 0x35, 0x91, 0xE8, 0x34, 0x39, 0xA3, 0xE5, 0xE6, 0x80, 0x53, + 0x76, 0x1F, 0x94, 0xA0, 0xF6, 0xA5, 0x41, 0x79, 0x82, 0xD3, 0xB0, 0x1F, 0xCE, 0xE1, 0x86, 0x64, + 0x65, 0x0C, 0x8D, 0xD6, 0xFA, 0xC1, 0x10, 0x6C, 0x07, 0xD5, 0xF0, 0x77, 0x65, 0xB9, 0x0C, 0xBD, + 0xAE, 0x2D, 0x62, 0x6C, 0x42, 0x7E, 0x2A, 0xBE, 0x5F, 0xC1, 0x17, 0x3B, 0x07, 0xFF, 0x5E, 0xD7, + 0x31, 0x52, 0x26, 0x2F, 0x9F, 0x12, 0xD8, 0x2E, 0xA3, 0xF5, 0xB5, 0xD2, 0xFC, 0x6E, 0x08, 0x1F, + 0xC8, 0x93, 0xA1, 0xEB, 0xF9, 0x13, 0x1D, 0x1F, 0x98, 0x5E, 0xB0, 0x0C, 0x65, 0x6C, 0xAE, 0x07, + 0x78, 0xF8, 0x12, 0xD2, 0xD1, 0x1E, 0x77, 0x5C, 0x24, 0x62, 0xE5, 0x94, 0xD6, 0x6A, 0x8E, 0xD0, + 0x72, 0x59, 0xDA, 0x48, 0x38, 0x2F, 0x31, 0x75, 0x0C, 0x52, 0xF0, 0x0C, 0x8F, 0x5C, 0xE9, 0x5E, + 0x5A, 0x94, 0xE8, 0xD2, 0x80, 0xF8, 0x4F, 0xE7, 0xAA, 0x6C, 0xBE, 0x47, 0xFB, 0xDD, 0x57, 0x0A, + 0xD8, 0x5E, 0xCC, 0x0D, 0x8F, 0x42, 0x5E, 0xDC, 0x5D, 0x95, 0x95, 0x60, 0x9B, 0x6F, 0x05, 0x5E, + 0x08, 0x45, 0x91, 0xE4, 0xB8, 0x06, 0xB1, 0xF2, 0xC0, 0xD7, 0xE3, 0x47, 0xB7, 0x38, 0x08, 0xA8, + 0x58, 0xE4, 0x55, 0xFC, 0xE2, 0x37, 0x1F, 0x38, 0xA2, 0x18, 0x9E, 0xC2, 0x0F, 0x90, 0x14, 0x20, + 0x50, 0xD1, 0xD0, 0xAB, 0x36, 0x7F, 0xAA, 0x03, 0x1C, 0xE6, 0x0A, 0xF9, 0x8E, 0x41, 0xDB, 0x32, + 0x1C, 0x68, 0xA0, 0xA0, 0xED, 0x4A, 0xF4, 0x4B, 0x09, 0xD0, 0xF0, 0x01, 0x8B, 0x17, 0x44, 0xE1, + 0xEA, 0xC5, 0x9D, 0x3B, 0x37, 0x7A, 0x68, 0xF1, 0x78, 0x46, 0xCF, 0xB6, 0x57, 0xDB, 0x4B, 0x5C, + 0x03, 0xE1, 0x9D, 0xC0, 0x37, 0x55, 0x8D, 0x03, 0xFB, 0x6A, 0x00, 0x82, 0x19, 0xD1, 0xC0, 0x76, + 0x97, 0xEE, 0xC9, 0xAD, 0x0D, 0x72, 0x0B, 0xE9, 0xA8, 0x09, 0x92, 0x03, 0xA4, 0xAA, 0x2C, 0xCF, + 0xFD, 0xDE, 0x86, 0xD0, 0x06, 0x4A, 0xAE, 0x7E, 0xC1, 0xB8, 0x2A, 0x4E, 0x9F, 0xA3, 0x5E, 0x8C, + 0x12, 0x40, 0x74, 0x38, 0xE7, 0xEA, 0xB0, 0x51, 0xC2, 0xB9, 0x6D, 0x4A, 0x50, 0xBF, 0x59, 0x9C, + 0x05, 0xB2, 0x42, 0xE2, 0x0F, 0x71, 0x44, 0xDB, 0x97, 0x0B, 0xD0, 0xDB, 0x44, 0x1F, 0x9A, 0x3B, + 0x18, 0x2A, 0x7B, 0xD9, 0x03, 0x83, 0x0B, 0xCF, 0x27, 0x20, 0x43, 0xA6, 0x42, 0xED, 0x89, 0x63, + 0xDB, 0x2D, 0x27, 0xC2, 0x3B, 0xE6, 0x0D, 0x3E, 0xB6, 0x96, 0x33, 0x70, 0xA6, 0xF3, 0xF5, 0x56, + 0xEA, 0xEB, 0xF1, 0xE7, 0xD8, 0xCB, 0x04, 0x48, 0x99, 0x4C, 0x00, 0xA4, 0x2A, 0xA5, 0x8A, 0xF1, + 0x58, 0xD5, 0x17, 0x4C, 0xC5, 0x88, 0x06, 0x8F, 0xA6, 0x67, 0xA6, 0x14, 0xC7, 0xB9, 0xE0, 0x86, + 0xAC, 0x67, 0xFD, 0xB3, 0x5B, 0x3E, 0xDF, 0x03, 0xFD, 0xC8, 0xC4, 0x4A, 0x32, 0x78, 0x6B, 0xD1, + 0xC1, 0xE2, 0x36, 0x9D, 0x0B, 0xF2, 0x54, 0x25, 0xB8, 0xB7, 0xB2, 0x10, 0x7A, 0xA6, 0x79, 0x52, + 0xC2, 0xEE, 0x98, 0xA5, 0x3D, 0xF0, 0x07, 0x8D, 0x25, 0xC3, 0xAC, 0xFD, 0xCF, 0x83, 0x98, 0x80, + 0x56, 0x95, 0xC4, 0x14, 0xA2, 0xA5, 0x93, 0xFE, 0x24, 0x59, 0x44, 0x73, 0xDF, 0xD6, 0x47, 0xDA, + 0x22, 0x3A, 0x82, 0xC5, 0xD1, 0x59, 0x40, 0x9D, 0x0C, 0x1A, 0xB7, 0x79, 0x45, 0x9A, 0xF8, 0x6D, + 0x5A, 0x5C, 0xC2, 0x80, 0xFC, 0xAA, 0x8A, 0xA4, 0xFE, 0x68, 0x61, 0x7D, 0xFE, 0x2C, 0x36, 0xE3, + 0xE0, 0x59, 0x28, 0x40, 0x79, 0xAD, 0x2D, 0x28, 0x12, 0x30, 0xFC, 0x56, 0x2E, 0x1D, 0xEC, 0x48, + 0x3A, 0xF0, 0xC5, 0x6C, 0x31, 0xE0, 0x2E, 0xB3, 0x91, 0x70, 0xB9, 0x9E, 0xBD, 0xE7, 0x96, 0x58, + 0xCB, 0xBC, 0x1C, 0xE4, 0xC7, 0x78, 0xC7, 0x1E, 0x39, 0xDB, 0xB8, 0x77, 0x50, 0xB7, 0x65, 0x20, + 0x04, 0x16, 0x8B, 0xFC, 0x66, 0xC4, 0x6D, 0x05, 0x8C, 0x3C, 0xB6, 0x32, 0x2F, 0xDE, 0xC3, 0x6F, + 0xFC, 0x82, 0x06, 0x02, 0x87, 0x47, 0xFD, 0xD8, 0xDA, 0x75, 0xE0, 0x4E, 0x8C, 0x40, 0x00, 0xB2, + 0x9B, 0x35, 0x78, 0xA4, 0x61, 0x64, 0x96, 0x62, 0x37, 0xF6, 0x3E, 0x39, 0xFA, 0x14, 0x5B, 0xC4, + 0x70, 0x17, 0xDC, 0x0C, 0x9E, 0x31, 0x82, 0x2C, 0x63, 0xCC, 0x8A, 0x43, 0x7C, 0x69, 0x12, 0x05, + 0x18, 0xA3, 0x62, 0xCC, 0xA2, 0x13, 0x96, 0x25, 0xA6, 0x1B, 0xF2, 0x10, 0xC8, 0x73, 0x4F, 0xCB, + 0x80, 0xCA, 0xAF, 0x73, 0xC9, 0x78, 0xB1, 0xAE, 0x87, 0xB8, 0xDF, 0x50, 0xD3, 0x55, 0x1E, 0x3A, + 0x81, 0xF6, 0x84, 0xD6, 0x57, 0x36, 0xCF, 0x38, 0xB7, 0xBC, 0xBC, 0x1E, 0x48, 0x62, 0x9F, 0x0F, + 0x0C, 0xE5, 0xF0, 0x63, 0x33, 0xE6, 0x59, 0x6B, 0x1E, 0xE6, 0x1C, 0x8A, 0xF9, 0xDD, 0x6B, 0xA3, + 0xDC, 0x02, 0x4A, 0x2F, 0x8C, 0x6A, 0x8D, 0x16, 0x7E, 0x2F, 0xF1, 0x75, 0xD5, 0x15, 0x93, 0x07, + 0x27, 0xD9, 0x6F, 0x1A, 0x5D, 0x43, 0xF3, 0x47, 0xC4, 0xED, 0xAD, 0x05, 0x9F, 0xEC, 0x8F, 0xD0, + 0xBE, 0xB5, 0x58, 0xF4, 0xF6, 0xBE, 0x08, 0x73, 0x96, 0x19, 0x05, 0x25, 0xEC, 0x3D, 0x26, 0xF4, + 0x93, 0xDB, 0x9F, 0x56, 0x48, 0x4C, 0xBC, 0xD0, 0x02, 0x59, 0xD1, 0x40, 0x4C, 0xA6, 0x06, 0x41, + 0xE8, 0x7D, 0x47, 0xAE, 0x3A, 0x9E, 0x1A, 0x71, 0x52, 0xD4, 0x67, 0xC1, 0x14, 0x7E, 0x40, 0x6F, + 0x1C, 0x75, 0x30, 0x7B, 0x70, 0x3A, 0xE0, 0x37, 0xB7, 0x41, 0x7F, 0xCB, 0x4A, 0xBA, 0xA7, 0xCE, + 0x56, 0x54, 0xC5, 0x46, 0x65, 0x6F, 0xB4, 0xB6, 0xF0, 0x57, 0xCE, 0x2E, 0x4F, 0xA9, 0xF0, 0x14, + 0x50, 0xC3, 0x30, 0xC5, 0xBA, 0xE1, 0x5E, 0xD6, 0xDC, 0xC5, 0x78, 0x55, 0x32, 0xAA, 0xCB, 0x29, + 0x35, 0x81, 0x46, 0x5E, 0x92, 0xE7, 0xDE, 0xCC, 0x92, 0x29, 0x86, 0xE0, 0x8F, 0x91, 0x3C, 0x74, + 0x97, 0x79, 0x63, 0x97, 0x4A, 0xCC, 0x88, 0xB5, 0xA3, 0x7A, 0xF0, 0xF0, 0x33, 0x87, 0xCD, 0xBD + }; + uint8_t b = Value ^ hdSurEncBasicCrypt_D2_0F_11(Value, lookup[(((AddrInd * 2) + 0) * 256) + Value]); + + return (Value ^ hdSurEncBasicCrypt_D2_0F_11(b, lookup[(((AddrInd * 2) + 1) * 256) + b])); +} + +void hdSurEncPhase1_D2_0F_11(uint8_t *CWs) +{ + static const uint8_t lookup1[] = + { + 0x16, 0x71, 0xCA, 0x14, 0xC4, 0xF4, 0xA3, 0x5A, 0x9D, 0x5F, 0x85, 0x8B, 0xA6, 0x77, 0xFD, 0x3C, + 0x5F, 0x13, 0x2A, 0x5F, 0x61, 0x36, 0xE4, 0xDC, 0x0D, 0x82, 0x92, 0xC5, 0x25, 0xE1, 0x7A, 0x1C, + 0x29, 0x19, 0x94, 0x2F, 0xC5, 0xD2, 0xDC, 0xBA, 0x86, 0x60, 0x64, 0x60, 0x86, 0x92, 0xA3, 0x4E, + 0x3D, 0x9B, 0xCC, 0x16, 0xBB, 0xBA, 0xD2, 0xF0, 0x6A, 0xD3, 0x2F, 0x07, 0x75, 0xBD, 0x28, 0xDB + }; + static const int8_t lookup2[] = { 1, -1, -1, 1, -1, 2, 1, -2, -1, 1, 2, -2, 1, -2, -2, 4 }; + static const int8_t CAddrIndex[] = { 0, 1, 3, 4 }; + + int32_t i, j, i1, i2, i3; + for(i = 3; i >= 0; --i) + { + for(j = 0; j <= 15; ++j) + { + CWs[j] = CWs[j] ^ hdSurEncBasicCrypt_D2_0F_11(j , lookup1 [(16 * i) + j]); + } + + uint8_t Buffer[16]; + uint32_t k; + for(i1 = 0; i1 <= 3; ++i1) + { + for(i2 = 0; i2 <= 3; ++i2) + { + k = 0; + for(i3 = 0; i3 <= 3; ++i3) + { + k = k + (CWs[(i2 * 4) + i3] * lookup2[(i3 * 4) + i1]); + Buffer[(i2 * 4) + i1] = (uint8_t)k; + } + } + } + + memcpy(CWs, Buffer, 16); + // CW positions are mixed around here + uint8_t a4[4]; + + for(i1 = 1; i1 <= 3; ++i1) + { + for(i2 = 0; i2 <= 3; ++i2) + { + a4[i2] = i1 + (i2 * 4); + } + + for(i2 = 0; i2 <= i1 - 1; ++i2) // the given code in Func1_3 seems to be wrong here(3 instead of i1-1)! + { + uint8_t tmp = CWs[a4[0]]; + for(i3 = 1; i3 <= 3; ++i3) + { + CWs[a4[i3 - 1]] = CWs[a4[i3]]; + } + CWs[a4[3]] = tmp; + } + + } + + for(i1 = 0; i1 <= 15; ++i1) + { + CWs[i1] = hdSurEncCryptLookup_D2_0F_11(CWs[i1], CAddrIndex[i1 & 3]); + } + } +} + +void hdSurEncPhase2_D2_0F_11_sub(uint8_t *CWa, uint8_t *CWb, uint8_t AddrInd) +{ + uint8_t Buffer[8]; + uint8_t tmp, i; + for(i = 0; i <= 7; ++i) + { + Buffer[i] = hdSurEncCryptLookup_D2_0F_11(CWb[i], AddrInd); + } + + // some bitshifting + tmp = Buffer[7]; + + for(i = 7; i >= 1; --i) + { + Buffer[i] = ((Buffer[1] >> 4) & 0xFF) | ((Buffer[i - 1] << 4) & 0xFF); + } + + Buffer[0] = ((Buffer[0] >> 4) & 0xFF) | ((tmp << 4) & 0xFF); + + // saving the result + for(i = 0; i <= 7; ++i) + { + CWa[i] = CWa[i] ^ Buffer[i]; + } +} + +void hdSurEncPhase2_D2_0F_11(uint8_t *CWs) +{ + hdSurEncPhase2_D2_0F_11_sub(CWs, CWs + 8, 0); + hdSurEncPhase2_D2_0F_11_sub(CWs + 8, CWs, 1); + hdSurEncPhase2_D2_0F_11_sub(CWs, CWs + 8, 2); + hdSurEncPhase2_D2_0F_11_sub(CWs + 8, CWs, 3); + hdSurEncPhase2_D2_0F_11_sub(CWs, CWs + 8, 4); +} + +void CommonMain_1_D2_13_15(const uint8_t *datain, uint8_t *dataout) +{ + const uint8_t Tab3[88] = + { + 0x1B, 0x12, 0x12, 0x0C, 0x12, 0x0C, 0x0C, 0x08, 0x09, 0x09, 0x06, 0x06, 0x06, 0x06, 0x04, 0x04, + 0x08, 0x04, 0x04, 0x02, 0x04, 0x02, 0x02, 0x01, 0x09, 0x06, 0x09, 0x06, 0x06, 0x04, 0x06, 0x04, + 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x09, 0x06, 0x06, 0x04, 0x09, 0x06, 0x06, 0x04, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, + 0x04, 0x02, 0x04, 0x02, 0x02, 0x01, 0x02, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 + }; + + int i1,i2; + unsigned long bb; + for (i1 = 0; i1 < 11; i1++) + { + bb = 0; + for (i2 = 0; i2 < 8; i2++) + { + bb += (Tab3[(i1 * 8) + i2] * datain[i2]); + } + + dataout[i1] = (bb & 0xFF); + } +} + +unsigned short CommonMain_2_D2_13_15(const uint8_t *data, const unsigned long num) +{ + unsigned long bb1, bb2; + bb1 = num >> 3; + bb2 = (data[bb1] << 24) + (data[bb1 + 1] << 16) + (data[bb1 + 2] << 8); + return ((bb2 >> (21 - (num & 7))) & 0x7FF); +} + +void CommonMain_3_D2_13_15(uint8_t *data0, uint8_t *data1, int nbrloop) +{ + int i; + unsigned long bb1, bb2; + bb1 = 0; + bb2 = 0; + for (i = nbrloop - 1; i >= 0; i--) + { + bb1 += (data0[i] * 2) + data1[i]; + bb2 += data0[i] + data1[i]; + data0[i] = (bb1 & 0xFF); + data1[i] = (bb2 & 0xFF); + bb1 >>= 8; + bb2 >>= 8; + } +} + +void CommonMain_D2_13_15(const uint8_t *datain, uint8_t *dataout, int loopval) +{ + const uint8_t Tab0_Comp[0x800] = + { + 0x54, 0x75, 0x01, 0x0C, 0x7C, 0xE2, 0xC3, 0xC2, 0x5E, 0x13, 0x26, 0xCA, 0xB2, 0xCD, 0xB8, 0x3D, + 0x02, 0x2C, 0xE4, 0x19, 0x41, 0x3D, 0xE4, 0x0F, 0xEC, 0xF1, 0x45, 0x83, 0xE2, 0xE2, 0x72, 0xF9, + 0xCD, 0x75, 0x1E, 0x41, 0xCC, 0x0C, 0x1F, 0x39, 0x87, 0x9B, 0x46, 0xFF, 0x68, 0x1F, 0x00, 0xD8, + 0x41, 0x82, 0xCA, 0xC6, 0xEF, 0x87, 0x90, 0xA2, 0x7E, 0xD9, 0xDE, 0xC8, 0x25, 0xEA, 0xC9, 0x75, + 0x6E, 0x18, 0x81, 0xD8, 0x5A, 0xA6, 0x74, 0x05, 0xAF, 0xAE, 0xE0, 0x4F, 0x85, 0xAD, 0x94, 0xF6, + 0x45, 0xF4, 0xF5, 0x55, 0xA8, 0xEB, 0xEC, 0xDB, 0x6C, 0xFF, 0x2F, 0xC2, 0xC3, 0x7D, 0x93, 0xE6, + 0xF5, 0x31, 0x96, 0xB7, 0x9A, 0xDB, 0xE5, 0x76, 0x66, 0xFB, 0xDD, 0xBC, 0x19, 0x18, 0x42, 0xC6, + 0x36, 0xCD, 0x46, 0x33, 0xEA, 0xF1, 0x4C, 0xC0, 0x72, 0x07, 0xCD, 0x61, 0xCE, 0x0E, 0x08, 0x01, + 0xA3, 0xFA, 0x84, 0x21, 0xF2, 0x43, 0x37, 0x1C, 0xDE, 0x25, 0x8A, 0x1A, 0xF4, 0xBB, 0x40, 0xF3, + 0x53, 0xFE, 0x17, 0x60, 0x91, 0x6D, 0x7B, 0x6D, 0x5F, 0x1C, 0x15, 0x73, 0xCC, 0x6E, 0x73, 0x46, + 0x27, 0x73, 0xA3, 0x10, 0x16, 0x32, 0xB3, 0x39, 0x45, 0xA6, 0x55, 0xE7, 0x91, 0x32, 0x24, 0xC8, + 0xAE, 0xAF, 0x1B, 0x28, 0x69, 0x22, 0x2F, 0xE9, 0x77, 0x72, 0xBF, 0x4B, 0x8B, 0x07, 0x82, 0x31, + 0xB0, 0x95, 0x10, 0x78, 0x9F, 0xC5, 0xF3, 0x73, 0xE1, 0xF8, 0x36, 0x84, 0xFE, 0x1B, 0x92, 0xB2, + 0xE6, 0xA5, 0xCE, 0xDA, 0x56, 0x48, 0x52, 0x77, 0x9D, 0x9D, 0x8E, 0x37, 0x4B, 0xC8, 0x35, 0x7E, + 0xB9, 0x5D, 0xA4, 0xAE, 0x3F, 0xD0, 0xAA, 0x60, 0xA8, 0x4C, 0x85, 0x49, 0xF6, 0x0C, 0x27, 0xE8, + 0x94, 0x84, 0xA0, 0xAA, 0x06, 0x4D, 0xAC, 0x58, 0x8B, 0x61, 0x29, 0x3D, 0x68, 0x25, 0xD3, 0xD3, + 0x8E, 0xA1, 0xE0, 0x71, 0xBF, 0x21, 0x0C, 0xC7, 0x18, 0x19, 0xF1, 0x25, 0x98, 0x5F, 0x79, 0x5E, + 0x51, 0xBA, 0x8C, 0x2F, 0x52, 0x43, 0xF3, 0x5A, 0xE3, 0x58, 0x97, 0x64, 0x23, 0xE0, 0x44, 0x4F, + 0x30, 0x2A, 0xE0, 0x16, 0x8D, 0x4D, 0xD1, 0x1F, 0x7B, 0xC9, 0xC5, 0x74, 0x11, 0x23, 0x5D, 0x95, + 0xAC, 0x7F, 0x2E, 0x30, 0xBE, 0x2D, 0xE3, 0xB5, 0xC6, 0xA7, 0x69, 0x99, 0x1F, 0x18, 0x3C, 0x96, + 0x30, 0x45, 0x99, 0x71, 0x28, 0x08, 0x3C, 0xF7, 0x37, 0x4F, 0x6A, 0xD6, 0xAE, 0x9B, 0x57, 0xC1, + 0xCC, 0x2C, 0xE2, 0x0F, 0x7D, 0x66, 0xF4, 0x36, 0x0C, 0x3B, 0x35, 0xF6, 0x28, 0x03, 0xA3, 0x7A, + 0x83, 0x15, 0xF5, 0x61, 0x5E, 0xE8, 0xB7, 0xD8, 0x54, 0x33, 0x93, 0x63, 0x80, 0x40, 0x43, 0xD0, + 0x9C, 0xAA, 0x3A, 0x98, 0x50, 0xD2, 0xB8, 0x80, 0x5D, 0x16, 0xDF, 0x1C, 0x03, 0xAA, 0x87, 0xC7, + 0x63, 0xA5, 0x8D, 0xA9, 0x2E, 0xFB, 0x4F, 0x7C, 0x2B, 0xF5, 0xF9, 0x57, 0xB5, 0x90, 0xD8, 0x75, + 0xAB, 0x81, 0x4C, 0x1B, 0xAF, 0x6C, 0x0E, 0xCB, 0xB1, 0x4F, 0xD3, 0xE3, 0x69, 0x18, 0x8C, 0x7A, + 0x3C, 0xE1, 0x11, 0x86, 0x47, 0x78, 0x11, 0xA0, 0xD4, 0x28, 0xC3, 0x0D, 0xAC, 0xC6, 0x17, 0xA1, + 0x32, 0x9F, 0x8F, 0x42, 0xD9, 0x3F, 0x66, 0xD7, 0x2D, 0x87, 0x7B, 0x65, 0xD3, 0xD6, 0x90, 0x83, + 0xA2, 0x75, 0xE8, 0x98, 0x90, 0x9D, 0xDE, 0x81, 0x9E, 0x3D, 0xE4, 0xA9, 0xE4, 0x0B, 0xBC, 0xBA, + 0x96, 0xDD, 0x05, 0xCA, 0xAE, 0x78, 0x69, 0x24, 0xDB, 0xA7, 0x3E, 0x7A, 0x3B, 0xB4, 0xC4, 0x59, + 0x61, 0xD2, 0xF2, 0xE3, 0x99, 0x8F, 0x8F, 0x8A, 0x82, 0x33, 0xB8, 0x17, 0x5E, 0x7A, 0x32, 0x41, + 0x10, 0x8D, 0xC2, 0xEF, 0xAA, 0xF8, 0x5A, 0xF7, 0xD2, 0x1D, 0xC0, 0xCB, 0x5E, 0xB7, 0x8A, 0x78, + 0x49, 0x42, 0xEB, 0x19, 0x1B, 0x61, 0xA0, 0x77, 0x5A, 0xF4, 0x6D, 0x55, 0xDA, 0xEB, 0xCE, 0x4E, + 0xB8, 0xE6, 0x32, 0xD7, 0x51, 0x3F, 0x73, 0x14, 0x34, 0x6E, 0x38, 0xD6, 0xA7, 0x28, 0x87, 0x4A, + 0x59, 0xCA, 0x1C, 0x80, 0xB5, 0x8C, 0x9D, 0x94, 0xCB, 0xFE, 0x29, 0x41, 0xE5, 0x69, 0xCF, 0xFD, + 0x0B, 0xE1, 0x7C, 0xA1, 0x70, 0x12, 0x76, 0x43, 0xDA, 0xB9, 0xD4, 0xC3, 0x31, 0xBC, 0x94, 0x77, + 0x04, 0xB4, 0x1C, 0xAA, 0xEC, 0x6F, 0xA5, 0x12, 0x9D, 0x6F, 0x34, 0x65, 0x77, 0xA0, 0xD2, 0x6F, + 0x60, 0xC6, 0x47, 0xC2, 0xDF, 0x6A, 0x10, 0x53, 0xD4, 0xBA, 0xF3, 0xB7, 0x38, 0x79, 0x63, 0xC9, + 0xD4, 0x77, 0xBC, 0x54, 0xE9, 0x79, 0x42, 0xD5, 0xE0, 0x71, 0xE7, 0x9E, 0x5A, 0x62, 0x0C, 0xAD, + 0x01, 0x09, 0xA8, 0x9F, 0x8E, 0x67, 0x4A, 0x30, 0xA4, 0xB1, 0x08, 0xFC, 0x0A, 0xEA, 0x7A, 0x1D, + 0x4C, 0x4A, 0x21, 0xDE, 0x00, 0xD7, 0x41, 0x98, 0x6B, 0x38, 0x50, 0x3E, 0x1F, 0x25, 0x06, 0xE3, + 0x6C, 0xA3, 0x84, 0x5B, 0xC1, 0xED, 0x47, 0xDD, 0xB3, 0x83, 0x46, 0x72, 0x69, 0xCE, 0x72, 0x04, + 0x43, 0x67, 0x3A, 0x19, 0xD9, 0x0A, 0xF7, 0x43, 0x88, 0xCA, 0xC7, 0x31, 0x34, 0x21, 0x4E, 0x4C, + 0xE8, 0xD1, 0x70, 0x00, 0xBD, 0xB1, 0xB6, 0x76, 0x6F, 0x5B, 0xF9, 0xF5, 0xF4, 0x19, 0x20, 0x21, + 0xC1, 0xF0, 0x11, 0x36, 0x66, 0xAB, 0x15, 0xBD, 0x69, 0x92, 0xC6, 0x46, 0xDE, 0xDC, 0xE9, 0x9A, + 0xF8, 0x6C, 0x15, 0x29, 0x15, 0xA6, 0x35, 0x3E, 0x08, 0xE5, 0x90, 0x62, 0x9F, 0x86, 0x56, 0x83, + 0x5D, 0x60, 0x0D, 0x22, 0x77, 0xA7, 0x60, 0x9B, 0x26, 0x80, 0x16, 0x67, 0xB4, 0x46, 0xBF, 0x74, + 0x55, 0x92, 0x5B, 0x34, 0xFF, 0xC8, 0x28, 0x37, 0xFF, 0x14, 0x62, 0xFA, 0xBD, 0x03, 0x78, 0x04, + 0x1B, 0x65, 0x7F, 0x99, 0x05, 0x27, 0x14, 0xC0, 0x06, 0x4D, 0x4B, 0x0E, 0x98, 0x34, 0x6A, 0xB3, + 0xA1, 0xFE, 0xBC, 0x45, 0x7D, 0x52, 0x50, 0x0E, 0x2C, 0xFB, 0x91, 0xF5, 0xFB, 0x2A, 0xB7, 0xD9, + 0xB8, 0xB8, 0x54, 0x31, 0x81, 0x03, 0x93, 0x2C, 0xE1, 0x5C, 0xB9, 0x2C, 0xE8, 0x38, 0xC0, 0xA7, + 0x58, 0x18, 0x92, 0xC5, 0x8B, 0xEF, 0x1E, 0x33, 0xA4, 0xBA, 0x86, 0x2B, 0xE9, 0xEE, 0xB1, 0xDF, + 0xAB, 0xB8, 0x48, 0xDA, 0x84, 0xF1, 0x68, 0x05, 0x4E, 0xDE, 0xB5, 0x9E, 0x88, 0x12, 0xC9, 0x60, + 0x50, 0x58, 0x56, 0x9D, 0x26, 0x84, 0xB6, 0x1A, 0xE6, 0x4B, 0x40, 0x94, 0x6D, 0xE9, 0x1D, 0x0D, + 0x8A, 0xF9, 0x2A, 0xB5, 0xBC, 0xDB, 0x06, 0x8F, 0x13, 0x7E, 0x1D, 0x1C, 0xC7, 0xFD, 0x8F, 0x78, + 0x55, 0x3F, 0x16, 0x84, 0x48, 0xDA, 0x1A, 0xD1, 0x93, 0x95, 0x20, 0x58, 0x92, 0x39, 0xF6, 0x73, + 0x4E, 0x9E, 0x7B, 0x70, 0xFC, 0x1E, 0x5B, 0x20, 0x48, 0x96, 0xB3, 0x7C, 0x50, 0x09, 0x5B, 0x61, + 0x57, 0x97, 0x36, 0x04, 0x29, 0x2C, 0x32, 0x8E, 0x93, 0x4A, 0x45, 0xFA, 0xD5, 0x24, 0x14, 0x1A, + 0x28, 0x9C, 0x1A, 0x71, 0xAE, 0x85, 0x4B, 0x26, 0x79, 0x99, 0x65, 0xD0, 0x07, 0x98, 0xED, 0xC9, + 0x1B, 0x39, 0x57, 0x5B, 0xDB, 0x3D, 0x87, 0x69, 0x66, 0x9B, 0x03, 0x23, 0x54, 0x6B, 0x4B, 0xAC, + 0x6E, 0x7A, 0x25, 0x1E, 0xB6, 0x97, 0xCF, 0x1D, 0x07, 0xCB, 0x2A, 0x3E, 0x85, 0x02, 0x93, 0x31, + 0x12, 0x27, 0xF0, 0xA6, 0x6D, 0x0F, 0x9A, 0xB6, 0xFC, 0x22, 0x79, 0x6C, 0x77, 0xFD, 0x3F, 0xDC, + 0x19, 0xD0, 0xDF, 0xBD, 0x9E, 0xE0, 0xBE, 0x20, 0x13, 0xA3, 0x0A, 0x0B, 0x22, 0xF2, 0xC8, 0x6B, + 0xA1, 0xDD, 0x6C, 0x67, 0xB3, 0xFD, 0x71, 0xC2, 0x7B, 0x08, 0x3B, 0xF1, 0x37, 0xB5, 0x0F, 0x86, + 0xFA, 0xA9, 0xE9, 0x42, 0xD1, 0xE8, 0xCD, 0x05, 0xEF, 0xD3, 0xCC, 0x0B, 0x70, 0x51, 0x5B, 0x97, + 0x06, 0xC4, 0x9D, 0x88, 0x11, 0x3E, 0x99, 0x9F, 0xBE, 0x76, 0x8C, 0x8D, 0xE6, 0xBA, 0xDA, 0x48, + 0xD0, 0x04, 0x86, 0x4F, 0xA9, 0xC6, 0xB0, 0xED, 0xA4, 0x94, 0x46, 0x96, 0x27, 0xEE, 0x9F, 0xBD, + 0xDA, 0x9B, 0x3D, 0x11, 0x80, 0xD3, 0x7B, 0x5A, 0x48, 0x94, 0xE5, 0xCC, 0x48, 0xEA, 0xE4, 0x18, + 0xDF, 0x51, 0xB3, 0x02, 0x57, 0x20, 0x4B, 0x0F, 0x07, 0xFF, 0x41, 0x33, 0x0F, 0x6B, 0x2E, 0xAA, + 0xDE, 0xB2, 0x56, 0xF7, 0xFB, 0xA2, 0x48, 0x3C, 0x97, 0x1A, 0x64, 0x2C, 0xD1, 0x74, 0x40, 0xCF, + 0x65, 0x7F, 0x14, 0x08, 0x59, 0xC4, 0x35, 0xD3, 0x8A, 0x0F, 0xFD, 0x71, 0x7A, 0x71, 0xAC, 0x2D, + 0xF3, 0xFD, 0x7B, 0x12, 0x5F, 0xC0, 0xBC, 0x4E, 0x96, 0x12, 0xF2, 0x8E, 0x41, 0x84, 0x01, 0x0F, + 0xED, 0x7B, 0xC1, 0xB9, 0x39, 0x03, 0x35, 0x40, 0x49, 0x53, 0xB8, 0xB4, 0x6B, 0xA6, 0xE7, 0x0A, + 0x14, 0xBB, 0x29, 0x16, 0xEC, 0x2A, 0x3A, 0xD6, 0x09, 0xBB, 0x5C, 0x20, 0xF8, 0x09, 0xFD, 0x86, + 0xC4, 0x25, 0x09, 0x85, 0x0B, 0xD5, 0xD8, 0x51, 0xB1, 0xA2, 0xCB, 0xDC, 0xC4, 0xDD, 0x34, 0xDF, + 0xE2, 0x85, 0xA9, 0xCC, 0x4E, 0x66, 0x51, 0xFA, 0x9C, 0x4D, 0xB7, 0x1E, 0x3E, 0x49, 0x34, 0x9C, + 0x21, 0x66, 0x07, 0x44, 0xB2, 0xEC, 0x73, 0xC5, 0xBB, 0x27, 0x9A, 0xA5, 0x91, 0x5A, 0xB9, 0x9F, + 0xBE, 0xC8, 0xA2, 0x27, 0x89, 0x21, 0xA7, 0xEE, 0x50, 0x4D, 0x43, 0x50, 0x67, 0xC2, 0x3B, 0x7C, + 0x20, 0x0B, 0x95, 0x40, 0xBE, 0xEA, 0xB5, 0xD9, 0x82, 0xD7, 0x9C, 0xB5, 0x21, 0xAD, 0xA6, 0xF9, + 0x70, 0xEA, 0xCD, 0x04, 0xDD, 0x58, 0x91, 0x89, 0xB2, 0xA9, 0xF9, 0xB4, 0x12, 0xA2, 0x63, 0x89, + 0x40, 0x8E, 0xEA, 0x62, 0xEE, 0x0B, 0x01, 0x82, 0x6F, 0xB3, 0x5E, 0x5C, 0x36, 0xBE, 0xF4, 0x97, + 0x2C, 0xCF, 0x96, 0x7C, 0x0D, 0xAD, 0x62, 0xCE, 0xD4, 0x38, 0xC5, 0x32, 0x02, 0x24, 0x57, 0x27, + 0xE0, 0xCF, 0x56, 0xA5, 0x72, 0x6D, 0x90, 0x89, 0x2D, 0x4C, 0x34, 0xF6, 0x1D, 0xDD, 0x88, 0x5E, + 0x7A, 0x23, 0xE3, 0x6F, 0x42, 0xA3, 0xD9, 0x58, 0x7E, 0xE3, 0x52, 0x74, 0x57, 0x63, 0xB7, 0xB2, + 0xC1, 0xA3, 0x30, 0x92, 0x2E, 0xB0, 0x91, 0x01, 0x13, 0x36, 0x9A, 0x6A, 0xA7, 0x5B, 0x3C, 0x07, + 0xFB, 0xD8, 0x1E, 0x7E, 0xCF, 0x49, 0xAB, 0x3F, 0xCA, 0xCE, 0x74, 0x40, 0x54, 0x8D, 0x83, 0x61, + 0xCA, 0xC3, 0x76, 0x59, 0x5C, 0x9F, 0x49, 0x8A, 0x7D, 0xD1, 0x17, 0x9C, 0xA4, 0xDB, 0xB9, 0x16, + 0x4D, 0x64, 0xF7, 0xC7, 0xF0, 0x24, 0xE7, 0x00, 0xB6, 0x98, 0xD5, 0x8B, 0x54, 0xCB, 0x1E, 0x8B, + 0xA2, 0x2B, 0x7D, 0x50, 0x51, 0x8A, 0xF0, 0xEF, 0x47, 0xAE, 0xD0, 0xD6, 0xA0, 0x42, 0x8A, 0xD8, + 0x22, 0xAF, 0x02, 0x99, 0x4A, 0xE0, 0x8D, 0x8D, 0xBF, 0x11, 0x05, 0xA4, 0xC4, 0x9D, 0xB3, 0x89, + 0xB4, 0x4C, 0xC9, 0xF7, 0x4D, 0xC5, 0x2A, 0x35, 0x95, 0x30, 0xF3, 0x0E, 0x2F, 0xEC, 0x6E, 0x3A, + 0x8B, 0x05, 0x76, 0xED, 0x1A, 0x7C, 0xC0, 0xE7, 0x22, 0xCB, 0x59, 0xFF, 0xE6, 0x37, 0x78, 0x44, + 0xD4, 0xEE, 0xAD, 0xD7, 0xBD, 0x2E, 0xB7, 0x6A, 0xA4, 0x4E, 0x0E, 0xFB, 0xB0, 0xF5, 0xCB, 0x87, + 0xCF, 0xC3, 0x18, 0x64, 0x6F, 0x26, 0x5C, 0xD7, 0x16, 0xC8, 0x7F, 0xAB, 0x29, 0xC4, 0xBA, 0xFF, + 0xCD, 0x1C, 0xE4, 0x3A, 0xF2, 0xEB, 0x6A, 0x38, 0xE4, 0x65, 0xC2, 0x33, 0x03, 0x26, 0x7D, 0x9B, + 0x7E, 0x1D, 0x83, 0x00, 0x04, 0x2D, 0x2B, 0x5F, 0xFE, 0x39, 0x7E, 0xF1, 0x3C, 0xA2, 0x8C, 0x52, + 0x95, 0xBF, 0x46, 0x81, 0x24, 0x44, 0xF8, 0x10, 0xC3, 0x87, 0x8E, 0x64, 0x80, 0x17, 0x44, 0xE2, + 0x8B, 0xD1, 0x3C, 0x4A, 0xE2, 0x1F, 0xA9, 0xDE, 0x75, 0x13, 0xFC, 0x2E, 0x86, 0x0A, 0x5C, 0x5F, + 0x92, 0x2B, 0x92, 0x2D, 0x2A, 0xEC, 0xD2, 0x5C, 0x82, 0x6B, 0x76, 0x1E, 0xED, 0xE6, 0x56, 0xF7, + 0xD2, 0xDB, 0x96, 0x68, 0x02, 0x68, 0x99, 0x49, 0xEE, 0x88, 0x66, 0xCE, 0x5D, 0x08, 0x88, 0xA8, + 0xB9, 0x24, 0xB0, 0xB4, 0xDC, 0xA6, 0xC9, 0xD8, 0x68, 0x80, 0xBF, 0x6B, 0x32, 0x57, 0x7F, 0x91, + 0x0E, 0x37, 0x59, 0xF6, 0x76, 0xD2, 0xC5, 0x0B, 0xF3, 0x23, 0xBF, 0x38, 0x52, 0x0D, 0x97, 0x81, + 0x17, 0xBB, 0x9A, 0xC2, 0x55, 0x44, 0x72, 0xCE, 0xEE, 0xFA, 0xBB, 0xDA, 0xAB, 0xB0, 0x09, 0xEA, + 0xDB, 0xBF, 0x45, 0x95, 0x07, 0x88, 0xD4, 0xD2, 0x0D, 0x2E, 0x15, 0x31, 0xBE, 0x6A, 0xF4, 0xEF, + 0xA3, 0x7D, 0x22, 0x81, 0x3B, 0xA8, 0x83, 0xF9, 0x42, 0xE5, 0x9B, 0x79, 0x01, 0xF5, 0xDC, 0x19, + 0x64, 0xEB, 0x47, 0x67, 0xAF, 0xA4, 0xB2, 0xAE, 0xF8, 0xF9, 0x4D, 0x63, 0xAD, 0x54, 0xE1, 0x02, + 0x56, 0x89, 0x4E, 0x0A, 0xE8, 0x3E, 0x03, 0xFA, 0x33, 0x61, 0x58, 0x80, 0x64, 0x55, 0x3C, 0x8C, + 0x2A, 0x3D, 0x70, 0x3E, 0xE5, 0xC1, 0xA7, 0x75, 0xFC, 0x91, 0x75, 0x05, 0x8C, 0x6E, 0x3A, 0x74, + 0x10, 0xF1, 0x30, 0xE6, 0xF6, 0xF7, 0xAB, 0x6C, 0xB1, 0x2B, 0xF0, 0x2F, 0x13, 0x6E, 0xD4, 0x0A, + 0x64, 0x29, 0xF8, 0xBB, 0xA1, 0xAA, 0x55, 0x09, 0x93, 0x47, 0x2F, 0x8C, 0x7D, 0xF1, 0x2D, 0x81, + 0xFE, 0x78, 0xFC, 0xEE, 0x3F, 0xDD, 0x49, 0xDC, 0x0D, 0x52, 0x5C, 0x3B, 0x8F, 0x08, 0xB0, 0xDF, + 0xDC, 0xFC, 0xBE, 0x5F, 0x3B, 0x53, 0x82, 0xE2, 0xBD, 0x6D, 0x5D, 0xF2, 0x8D, 0xFB, 0x5A, 0x1D, + 0x15, 0x1B, 0xE4, 0xB1, 0x56, 0x06, 0x1A, 0xF8, 0x9C, 0xB9, 0x44, 0xF2, 0xD9, 0xF4, 0xB2, 0x00, + 0x9A, 0x94, 0x62, 0x33, 0x7E, 0x0A, 0xB0, 0x0C, 0xD5, 0xEF, 0x8E, 0xA8, 0xEB, 0x47, 0xE9, 0x20, + 0xA8, 0x68, 0xEF, 0x53, 0xA0, 0x59, 0x1B, 0xA0, 0x2B, 0xC5, 0x2B, 0x30, 0xB6, 0x5D, 0xAB, 0xB4, + 0x5F, 0x86, 0x71, 0x95, 0x89, 0xFC, 0xC7, 0x9A, 0xC3, 0xED, 0x82, 0xA0, 0x3D, 0x73, 0xC1, 0x36, + 0x01, 0x5F, 0x9E, 0xD7, 0xE3, 0xC0, 0x62, 0x74, 0xED, 0x13, 0xB6, 0xD6, 0xD5, 0x37, 0x17, 0xE1, + 0x39, 0xC7, 0x6D, 0x31, 0xBA, 0x02, 0xAF, 0xD5, 0xCC, 0x51, 0xA8, 0x09, 0x3F, 0x00, 0x4A, 0x8F, + 0xA6, 0x23, 0x13, 0x88, 0xCD, 0x1F, 0x38, 0x60, 0xE7, 0xE7, 0x53, 0xDC, 0x65, 0xE8, 0x53, 0x26, + 0xBB, 0xE1, 0x1F, 0x65, 0xF0, 0xAD, 0x53, 0x3B, 0xBD, 0xAD, 0x97, 0xAC, 0xD1, 0xA5, 0xD0, 0xE9, + 0xEB, 0xD6, 0x11, 0xD5, 0x00, 0xDF, 0x72, 0x9C, 0xCC, 0x7F, 0xD3, 0x67, 0xA1, 0x3A, 0x79, 0xE1, + 0x85, 0x70, 0xE5, 0x43, 0xC9, 0x28, 0xA5, 0x2F, 0x9E, 0xE7, 0xFE, 0xEB, 0x14, 0x10, 0x23, 0xC7, + 0xAF, 0xB1, 0x24, 0xC8, 0xE5, 0x44, 0x6F, 0x4C, 0x04, 0xEC, 0xC1, 0xF0, 0x23, 0x1C, 0xF6, 0xAC, + 0xAF, 0xC4, 0x0E, 0x2D, 0x59, 0x39, 0x47, 0xA9, 0x9E, 0xD9, 0x2E, 0x79, 0xBA, 0xFE, 0x4F, 0x12, + 0x7F, 0x63, 0x7F, 0x62, 0x67, 0x7C, 0x52, 0x2F, 0xCA, 0x8B, 0x6B, 0x4F, 0x10, 0x8F, 0x14, 0xC6, + 0xA1, 0x9B, 0x45, 0x15, 0x90, 0x63, 0x22, 0x5D, 0x68, 0x4B, 0xCF, 0xFA, 0x6A, 0x06, 0xF0, 0x26, + 0xAC, 0x6C, 0x3A, 0x89, 0x25, 0xF3, 0x5E, 0x90, 0x06, 0x93, 0xB6, 0x35, 0x0D, 0x85, 0x60, 0x98, + 0xBC, 0x6E, 0xF2, 0xA5, 0x17, 0x29, 0x70, 0xD6, 0xFF, 0x0C, 0xD0, 0xC0, 0x35, 0xD7, 0x4A, 0xFD + }; + const uint8_t Tab1_Comp[11 * 8] = + { + 0x70, 0x49, 0xD7, 0xE3, 0xDF, 0x3C, 0x96, 0x03, 0x2A, 0x70, 0x82, 0xA6, 0x5F, 0xDE, 0xCC, 0x0C, + 0x2A, 0x62, 0x2A, 0x3E, 0xA4, 0x0C, 0x0A, 0xAB, 0x4F, 0x06, 0x5D, 0xD4, 0x14, 0xAA, 0xE1, 0xC3, + 0x96, 0xDA, 0x16, 0x36, 0x45, 0x3C, 0x63, 0xC2, 0x97, 0x71, 0x87, 0xAB, 0xFA, 0xB2, 0xFC, 0xD6, + 0x8F, 0x85, 0xC9, 0x04, 0x56, 0xBA, 0xEB, 0x3F, 0x42, 0x9F, 0xCB, 0x66, 0x55, 0x45, 0x1C, 0x96, + 0xFF, 0x4D, 0x35, 0xDF, 0x88, 0x0E, 0xDC, 0xC8, 0x4E, 0x3F, 0x81, 0x74, 0xD8, 0x77, 0x4C, 0x8E, + 0x00, 0xC0, 0x64, 0x83, 0x4E, 0xBB, 0xF0, 0xB1 + }; + unsigned short buff8[8]; + uint8_t buff11[11 + 1]; // +1 to avoid func2 bug + int i1, i2; + buff11[11] = 0; + CommonMain_1_D2_13_15(datain, buff11); + + for (i1 = 0; i1 < 11; i1++) + { + buff11[i1] ^= Tab1_Comp[(loopval * 11) + i1]; + } + + for (i1 = 0; i1 < 8; i1++) + { + buff8[i1] = CommonMain_2_D2_13_15(buff11, i1 * 11); + } + + for (i1 = 0; i1 < 8; i1++) + { + dataout[i1] = Tab0_Comp[buff8[i1]]; + } + + i1 = 1; + while (i1 < 8) + { + i2 = 0 ; + + while (i2 < 8) + { + CommonMain_3_D2_13_15(&dataout[i2], &dataout[i1 + i2], i1); + i2 += (i1 * 2); + } + + i1 *= 2; + } +} + +void Common_D2_13_15(uint8_t *cw0, const uint8_t *cw1, int loopval) +{ + int i; + uint8_t buff8[8]; + CommonMain_D2_13_15(cw1, buff8, loopval); + + for (i = 0; i < 8; i++) + { + cw0[i] ^= buff8[i]; + } +} + +void ExchangeCWs(uint8_t *cw0, uint8_t *cw1) +{ + int i; + uint8_t b; + for (i = 0; i < 8; i++) + { + b = cw1[i]; + cw1[i] = cw0[i]; + cw0[i] = b; + } +} + +void hdSurEncPhase1_D2_13_15(uint8_t *cws) +{ + int i; + for (i = 0; i <= 7; i++) + { + // Possible code + if ((i & 1) == 0) + { + Common_D2_13_15((uint8_t *) &cws[0], (uint8_t *) &cws[8], i); + } + else + { + Common_D2_13_15((uint8_t *) &cws[8], (uint8_t *) &cws[0], i); + } + } + ExchangeCWs((uint8_t *) &cws[0], (uint8_t *) &cws[8]); +} + +void hdSurEncPhase2_D2_13_15(uint8_t *cws) +{ + int i; + for (i = 7; i >= 0; i--) + { + // Possible code + if ((i & 1) == 0) + { + Common_D2_13_15((uint8_t *) &cws[8], (uint8_t *) &cws[0], i); + } + else + { + Common_D2_13_15((uint8_t *) &cws[0], (uint8_t *) &cws[8], i); + } + } + ExchangeCWs((uint8_t *) &cws[8], (uint8_t *) &cws[0]); +} + +uint getBit(uint number, int bt) +{ + return (number >> bt ) & 1; +} + +void SBitslice(uint* Iarray, int offsetin, uint* Oarray, int offsetout, int row, char* box) +{ + int bi, input, l, output; + uint calcArray[4] = {0, 0, 0, 0}; + + for (bi = 0; bi < 32; bi++) + { + input = 0; + + for (l = 0; l < 4; l++) + { + input += getBit(Iarray[l + offsetin], bi) << l; + } + + output = box[input + 16 * row]; + + for (l = 0; l < 4; l++) + { + calcArray[l] += getBit(output, l) << bi; + } + } + + for (l = 0; l < 4; l++) + { + Oarray[l + offsetout] = calcArray[l]; + } +} + +void xorBlock(uint* data, int offset) +{ + int i; + for (i = 0; i < 4; i++) + { + data[i] ^= KeyS[i + 4*offset]; + } +} + +uint rotr(uint val, int nbits) +{ + return ((val >> nbits) + (val << (32 - nbits))); +} + +void LTBitsliceInverse(uint* data) +{ + uint C0 = data[0]; + uint C1 = data[1]; + uint C2 = data[2]; + uint C3 = data[3]; + C2 = rotr(C2, 22); + C0 = rotr(C0, 5); + C2 ^= C3 ^ (C1 << 7); + C0 ^= C1 ^ C3; + C3 = rotr(C3, 7); + C3 ^= C2 ^ (C0 << 3); + C1 = rotr(C1, 1); + C1 ^= C0 ^ C2; + C2 = rotr(C2, 3); + C0 = rotr(C0, 13); + data[0] = C0; + data[1] = C1; + data[2] = C2; + data[3] = C3; +} + +void N98_decrypt(byte* data) +{ + int r; // round + int i, j; + uint N98[4]; // make 32 bits words + + for(i = 0; i < 4; i++) + { + N98[i] = data[4 * i] + (data[4 * i + 1] << 8) + (data[4 * i + 2] << 16) + (data[4 * i + 3] << 24); + } + + for(r = 31; r >= 0; r--) // decrypt, inverse order + { + if (r == 31) + { + xorBlock(N98, 32); + } + else + { + LTBitsliceInverse(N98); // inverse Linear Transform + } + SBitslice(N98, 0, N98, 0, (r & 7), SBoxInverse); // SBox inverse, bitslice mode + xorBlock(N98, r); + } + + for(i = 0; i < 4; i++) // set bytes to caller's array + { + for(j = 0; j < 4; j++) + { + data[4 * i + j] = (N98[i] >> 8 * j) & 0xFF; + } + } +} + +void MakeSubKeys(uint* KeySch, char key98Idx) +{ + int i, k; + char j; + uint w[140] = {0}; + char Sbox[128]; + // uint KeyS[132]; + + // calc SBox , inverse of SBoxInverse + for(i = 0; i < 8; i++) //row + { + for(j = 0; j < 16; j++) // search for column + { + k = 0; + while (SBoxInverse[k+16*i] != j) + { + k = k +1; + } + Sbox[j + 16 * i] = k; + } + } + + switch (key98Idx) + { + case 0x21: + { + w[0] = 0x6341F22E; + w[1] = 0x002E2D10; + w[2] = 0x181D7704; + w[3] = 0x1D93A0F3; + w[4] = 1; + w[5] = 0; + w[6] = 0; + w[7] = 0; + break; + } + } + + for(i = 0; i < 132; i++) + { + w[i + 8] = rotr((w[i + 0] ^ w[i + 3] ^ w[i + 5] ^ w[i + 7] ^ 0x9E3779B9 ^ i), 21); + } + for(i = 0; i < 33; i++) + { + SBitslice(w, (4 * i + 8), KeySch, (4 * i), ((35 - i) & 7), Sbox); // SBox , bitslice mode + } +} + +static int32_t viaccess_card_init(struct s_reader *reader, ATR *newatr) +{ + get_atr; + def_resp; + int32_t i; + uint8_t buf[256]; + uint8_t insac[] = { 0xca, 0xac, 0x00, 0x00, 0x00 }; // select data + uint8_t insb8[] = { 0xca, 0xb8, 0x00, 0x00, 0x00 }; // read selected data + uint8_t insa4[] = { 0xca, 0xa4, 0x00, 0x00, 0x00 }; // select issuer + uint8_t insc0[] = { 0xca, 0xc0, 0x00, 0x00, 0x00 }; // read data item + static const uint8_t insFAC[] = { 0x87, 0x02, 0x00, 0x00, 0x03 }; // init FAC + static const uint8_t FacDat[] = { 0x00, 0x00, 0x28 }; + static uint8_t ins8702_data[] = { 0x00, 0x00, 0x11}; + static uint8_t ins8704[] = { 0x87, 0x04, 0x00, 0x00, 0x07 }; + static uint8_t ins8706[] = { 0x87, 0x06, 0x00, 0x00, 0x04 }; + + if((atr[1] != 0x77) || ((atr[2] != 0x18) && (atr[2] != 0x11) && (atr[2] != 0x19)) || ((atr[9] != 0x68) && (atr[9] != 0x6C) && (atr[9] != 0x64))) + { + return ERROR; + } + + write_cmd(insFAC, FacDat); + if(!(cta_res[cta_lr - 2] == 0x90 && cta_res[cta_lr - 1] == 0)) + { + return ERROR; + } + + if(!cs_malloc(&reader->csystem_data, sizeof(struct viaccess_data))) + { + return ERROR; + } + + struct viaccess_data *csystem_data = reader->csystem_data; + write_cmd(insFAC, ins8702_data); + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00)) + { + write_cmd(ins8704, NULL); + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00)) + { + write_cmd(ins8706, NULL); + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00)) + { + csystem_data->last_geo.number_ecm = (cta_res[2] << 8) | (cta_res[3]); + rdr_log(reader, "using ecm #%x for long viaccess ecm", csystem_data->last_geo.number_ecm); + } + } + } + + reader->caid = 0x500; + memset(reader->prid, 0xff, sizeof(reader->prid)); + + insac[2] = 0xa4; + write_cmd(insac, NULL); // request unique id + + insb8[4] = 0x07; + write_cmd(insb8, NULL); // read unique id + memcpy(reader->hexserial, cta_res + 2, 5); + // rdr_log(reader, "[viaccess-reader] type: Viaccess, ver: %s serial: %llu", ver, b2ll(5, cta_res+2)); + rdr_log_sensitive(reader, "type: Viaccess (%sstandard atr), caid: %04X, serial: {%llu}", + atr[9] == 0x68 ? "" : "non-", reader->caid, (unsigned long long) b2ll(5, cta_res + 2)); + + i = 0; + insa4[2] = 0x00; + write_cmd(insa4, NULL); // select issuer 0 + buf[0] = 0; + while((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0)) + { + insc0[4] = 0x1a; + write_cmd(insc0, NULL); // show provider properties + cta_res[2] &= 0xF0; + reader->prid[i][0] = 0; + memcpy(&reader->prid[i][1], cta_res, 3); + memcpy(&csystem_data->availkeys[i][0], cta_res + 10, 16); + + snprintf((char *)buf + cs_strlen((char *)buf), sizeof(buf) - cs_strlen((char *)buf), ",%06X", b2i(3, &reader->prid[i][1])); + // rdr_log(reader, "[viaccess-reader] buf: %s", buf); + + insac[2] = 0xa5; + write_cmd(insac, NULL); // request sa + + insb8[4] = 0x06; + write_cmd(insb8, NULL); // read sa + memcpy(&reader->sa[i][0], cta_res + 2, 4); + + insa4[2] = 0x02; + write_cmd(insa4, NULL); // select next issuer + + i++; + } + + reader->nprov = i; + rdr_log(reader, "providers: %d (%s)", reader->nprov, buf + 1); + + get_maturity(reader); + + if(cfg.ulparent) + { + unlock_parental(reader); + } + + rdr_log(reader, "ready for requests"); + + return OK; +} + +bool dcw_crc(uint8_t *dw) +{ + int8_t i; + for(i = 0; i < 16; i += 4) + { + if(dw[i + 3] != ((dw[i] + dw[i + 1] + dw[i + 2]) & 0xFF)) + { + return 0; + } + } + + return 1; +} + +static int32_t viaccess_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + def_resp; + static const uint8_t insa4[] = { 0xca, 0xa4, 0x04, 0x00, 0x03 }; // set provider id + uint8_t ins88[] = { 0xca, 0x88, 0x00, 0x00, 0x00 }; // set ecm + uint8_t insf8[] = { 0xca, 0xf8, 0x00, 0x00, 0x00 }; // set geographic info + static const uint8_t insc0[] = { 0xca, 0xc0, 0x00, 0x00, 0x12 }; // read dcw + + struct viaccess_data *csystem_data = reader->csystem_data; + + // //XXX what is the 4th uint8_t for ?? + int32_t ecm88Len = MIN(MAX_ECM_SIZE - 4, SCT_LEN(er->ecm) - 4); + if(ecm88Len < 1) + { + rdr_log(reader, "ECM: Size of ECM couldn't be correctly calculated."); + return ERROR; + } + + uint8_t ecmData[ecm88Len]; + memset(ecmData, 0, ecm88Len); + memcpy(ecmData, er->ecm + 4, ecm88Len); + uint8_t *ecm88Data = &ecmData[0]; + uint32_t provid = 0; + int32_t rc = 0; + int32_t hasD2 = 0; + uint8_t hasE0 = 0; + int32_t has98 = 0; + int32_t has9635 = 0; + int32_t has9632 = 0; + int32_t curEcm88len = 0; + int32_t nanoLen = 0; + uint8_t *nextEcm; + uint8_t DE04[MAX_ECM_SIZE]; + uint8_t nano9635ecm88Data[MAX_ECM_SIZE]; + uint8_t nano9632ecm88Data[MAX_ECM_SIZE]; + int32_t D2KeyID = 0; + int32_t curnumber_ecm = 0; + uint8_t SubECM = 0; + char key98Idx = 0; + // nanoD2 d2 02 0d 02 -> D2 nano, len 2 + // 0b, 0f, 13 -> pre AES decrypt CW + // 0d, 11, 15 -> post AES decrypt CW + int32_t nanoD2 = 0; // knowns D2 nanos: 0x0b ,0x0d ,0x0f ,0x11, 0x13, 0x15 + memset(DE04, 0, sizeof(DE04)); //fix dorcel de04 bug + + nextEcm = ecm88Data; + while(ecm88Len > 0 && !rc) + { + if(ecm88Data[0] == 0x00 && ecm88Data[1] == 0x00) + { + // nano 0x00 and len 0x00 aren't valid... something is obviously wrong with this ecm. + rdr_log(reader, "ECM: Invalid ECM structure. Rejecting"); + return ERROR; + } + + // 80 33 nano 80 (ecm) + len (33) + if(ecm88Data[0] == 0x80) // nano 80, give ecm len + { + curEcm88len = ecm88Data[1]; + nextEcm = ecm88Data + curEcm88len + 2; + ecm88Data += 2; + ecm88Len -= 2; + } + + if(!curEcm88len) // there was no nano 80 -> simple ecm + { + curEcm88len = ecm88Len; + } + + // d2 02 0d 02 -> D2 nano, len 2, select the AES key to be used + if(ecm88Data[0] == 0xd2) + { + // test if it needs AES decrypt + if(ecm88Data[2] == 0x0b) + { + nanoD2 = 0x0b; + rdr_log_dbg(reader, D_READER, "ECM: nano D2 0x0b"); + } + + if(ecm88Data[2] == 0x0d) + { + nanoD2 = 0x0d; + rdr_log_dbg(reader, D_READER, "ECM: nano D2 0x0d"); + } + + if(ecm88Data[2] == 0x0f) + { + nanoD2 = 0x0f; + rdr_log_dbg(reader, D_READER, "ECM: nano D2 0x0f"); + } + + if(ecm88Data[2] == 0x11) + { + nanoD2 = 0x11; + rdr_log_dbg(reader, D_READER, "ECM: nano D2 0x11"); + } + + if(ecm88Data[2] == 0x13) + { + nanoD2 = 0x13; + rdr_log_dbg(reader, D_READER, "ECM: nano D2 0x13"); + } + + if(ecm88Data[2] == 0x15) + { + nanoD2 = 0x15; + rdr_log_dbg(reader, D_READER, "ECM: nano D2 0x15"); + } + + // use the d2 arguments to get the key # to be used + int32_t len = ecm88Data[1] + 2; + D2KeyID = ecm88Data[3]; + ecm88Data += len; + ecm88Len -= len; + curEcm88len -= len; + hasD2 = 1; + } + else + { + hasD2 = 0; + } + + // 40 07 03 0b 00 -> nano 40, len =7 ident 030B00 (tntsat), key #0 <== we're pointing here + // 09 -> use key #9 + // 05 67 00 + if((ecm88Data[0] == 0x90 || ecm88Data[0] == 0x40) && (ecm88Data[1] == 0x03 || ecm88Data[1] == 0x07)) + { + uint8_t ident[3], keynr; + uint8_t *ecmf8Data = 0; + int32_t ecmf8Len = 0; + nanoLen = ecm88Data[1] + 2; + keynr = ecm88Data[4] & 0x0F; + // 40 07 03 0b 00 -> nano 40, len =7 ident 030B00 (tntsat), key #0 <== we're pointing here + // 09 -> use key #9 + + if(nanoLen > 5) + { + curnumber_ecm = (ecm88Data[6] << 8) | (ecm88Data[7]); + rdr_log_dbg(reader, D_READER, "checking if the ecm number (%x) match the card one (%x)", curnumber_ecm, csystem_data->last_geo.number_ecm); + + // if we have an ecm number we check it. + // we can't assume that if the nano len is 5 or more we have an ecm number + // as some card don't support this + if(csystem_data->last_geo.number_ecm > 0) + { + if(csystem_data->last_geo.number_ecm == curnumber_ecm && !(ecm88Data[nanoLen - 1] == 0x01)) + { + keynr = ecm88Data[5]; + rdr_log_dbg(reader, D_READER, "keyToUse = %02x, ECM ending with %02x", ecm88Data[5], ecm88Data[nanoLen - 1]); + } + else + { + if(ecm88Data[nanoLen - 1] == 0x01) + { + rdr_log_dbg(reader, D_READER, "Skip ECM ending with = %02x for ecm number (%x) for provider %02x%02x%02x", ecm88Data[nanoLen - 1], curnumber_ecm, ecm88Data[2], ecm88Data[3], ecm88Data[4]); + } + + rdr_log_dbg(reader, D_READER, "Skip ECM ending with = %02x for ecm number (%x)", ecm88Data[nanoLen - 1], curnumber_ecm); + ecm88Data = nextEcm; + ecm88Len -= curEcm88len; + + continue; // loop to next ecm + } + } + else // long ecm but we don't have an ecm number so we have to try them all. + { + keynr = ecm88Data[5]; + rdr_log_dbg(reader, D_READER, "keyToUse = %02x", ecm88Data[5]); + } + } + + memcpy(ident, &ecm88Data[2], sizeof(ident)); + provid = b2i(3, ident); + ident[2] &= 0xF0; + + if(hasD2 && reader->aes_list) + { + // check that we have the AES key to decode the CW + // if not there is no need to send the ecm to the card + if(!aes_present(reader->aes_list, 0x500, (uint32_t)(provid & 0xFFFFF0) , D2KeyID)) + { + return ERROR; + } + } + + if(!chk_prov(reader, ident, keynr)) + { + rdr_log_dbg(reader, D_READER, "ECM: provider or key not found on card"); + snprintf(ea->msglog, MSGLOGSIZE, "provider(%02x%02x%02x) or key(%d) not found on card", ident[0], ident[1], ident[2], keynr); + return ERROR; + } + + SubECM = ecm88Data[nanoLen - 1] ; // 01 permut 4 , FF no permut + ecm88Data += nanoLen; + ecm88Len -= nanoLen; + curEcm88len -= nanoLen; + + if(ecm88Data[0] == 0x96 && ecm88Data[1] == 0x35) + { + rdr_log_dbg(reader, D_READER, "[viaccess-reader] nano 96/35 ECM detected!"); + ecm88Data += 55; + has9635 = 1; + } + + if(ecm88Data[0] == 0x96 && ecm88Data[1] == 0x32) + { + rdr_log_dbg(reader, D_READER, "[viaccess-reader] nano 96/32 ECM detected!"); + ecm88Data += 52; + has9632 = 1; + } + + if(ecm88Data[0] == 0xDE && ecm88Data[1] == 0x04) + { + rdr_log_dbg(reader, D_READER, "[viaccess-reader] nano DE/04 ECM detected!"); + memcpy(DE04, &ecm88Data[0], 6); + ecm88Data += 6; + } + + if((ecm88Data[0] == 0xD9) && (ecm88Data[1] == 0x0A)) + { + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano D9/0A ECM detected!"); + + if((ecm88Data[12] == 0xE0) && (ecm88Data[13] == 0x02) && (ecm88Data[15] == 0x02)) // accept parental data 0F for ecm88Data[2] + { + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano E0/02 ECM detected!"); + hasE0 = 1; + } + + if((ecm88Data[16] == 0x98) && (ecm88Data[17] == 0x08)) + { + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano 98/08 ECM detected!"); + has98 = 1; + key98Idx = ecm88Data[25]; + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano 98 Index : %02X", key98Idx); + } + + if((ecm88Data[19] == 0x98) && (ecm88Data[20] == 0x08)) + { + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano 98/08 ECM detected!"); + has98 = 1; + key98Idx = ecm88Data[28]; + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano 98 Index : %02X", key98Idx); + } + } + + if((ecm88Data[0] == 0xE0) && (ecm88Data[1] == 0x02) && (ecm88Data[3] == 0x02)) // accept parental data 0F for ecm88Data[2] + { + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano E0/02 ECM detected!"); + hasE0 = 1; + + if((ecm88Data[4] == 0x98) && (ecm88Data[5] == 0x08)) + { + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano 98/08 ECM detected!"); + has98 = 1; + key98Idx = ecm88Data[13]; + rdr_log_dbg(reader, D_READER,"[viaccess-reader] nano 98 Index : %02X", key98Idx); + } + } + + if(csystem_data->last_geo.provid != provid) + { + csystem_data->last_geo.provid = provid; + csystem_data->last_geo.geo_len = 0; + csystem_data->last_geo.geo[0] = 0; + write_cmd(insa4, ident); // set provider + } + + // Nano D2 0x0b, 0x0f, 0x13 -> pre AES decrypt CW + if(hasD2 && (nanoD2 == 0x0b|| nanoD2 == 0x0f|| nanoD2 == 0x13)) + { + uint8_t *ecm88DataCW = ecm88Data; + int32_t cwStart = 0; + //int32_t cwStartRes = 0; + int32_t must_exit = 0; + + // find CW start + while(cwStart < curEcm88len - 1 && !must_exit) + { + if(ecm88Data[cwStart] == 0xEA && ecm88Data[cwStart + 1] == 0x10) + { + ecm88DataCW = ecm88DataCW + cwStart + 2; + must_exit = 1; + } + + cwStart = cwStart + ecm88Data[cwStart + 1] + 2; // parse via nanos + //cwStart++; // error if EA 10 in nanos datas + } + + if(nanoD2 == 0x0f) + { + hdSurEncPhase1_D2_0F_11(ecm88DataCW); + hdSurEncPhase2_D2_0F_11(ecm88DataCW); + } + + if(nanoD2 == 0x13) + { + hdSurEncPhase1_D2_13_15(ecm88DataCW); + } + + // use AES from list to decrypt CW + rdr_log_dbg(reader, D_READER, "Decoding CW : using AES key id %d for provider %06x", D2KeyID, (provid & 0xFFFFF0)); + + if(aes_decrypt_from_list(reader->aes_list, 0x500, (uint32_t)(provid & 0xFFFFF0), D2KeyID, &ecm88DataCW[0], 16) == 0) + { + snprintf(ea->msglog, MSGLOGSIZE, "Missing AES key(%d)[aka E%X]",D2KeyID, D2KeyID); + } + + if(nanoD2 == 0x0f) + { + hdSurEncPhase1_D2_0F_11(ecm88DataCW); + } + + if(nanoD2 == 0x13) + { + hdSurEncPhase2_D2_13_15(ecm88DataCW); + } + } + + while(ecm88Len > 1 && ecm88Data[0] < 0xA0) + { + nanoLen = ecm88Data[1] + 2; + + if(!ecmf8Data) + { + ecmf8Data = (uint8_t *)ecm88Data; + } + + ecmf8Len += nanoLen; + ecm88Len -= nanoLen; + curEcm88len -= nanoLen; + ecm88Data += nanoLen; + } + + if(ecmf8Len) + { + if(csystem_data->last_geo.geo_len != ecmf8Len || memcmp(csystem_data->last_geo.geo, ecmf8Data, csystem_data->last_geo.geo_len)) + { + memcpy(csystem_data->last_geo.geo, ecmf8Data, ecmf8Len); + csystem_data->last_geo.geo_len = ecmf8Len; + insf8[3] = keynr; + insf8[4] = ecmf8Len; + write_cmd(insf8, ecmf8Data); + } + } + + ins88[2] = ecmf8Len ? 1 : 0; + ins88[3] = keynr; + ins88[4] = (curEcm88len > 0xFF) ? 0x00 : curEcm88len; + + // + // we should check the nano to make sure the ecm is valid + // we should look for at least 1 E3 nano, 1 EA nano and the F0 signature nano + // + // DE04 + if(DE04[0] == 0xDE) + { + uint32_t l = curEcm88len - 6; + if(l > MAX_ECM_SIZE || curEcm88len <= 6) //don't known if this is ok... + { + rdr_log(reader, "ecm invalid/too long! len=%d", curEcm88len); + return ERROR; + } + + memcpy(DE04 + 6, (uint8_t *)ecm88Data, l); + write_cmd(ins88, DE04); // request dcw + } + else if(has9635) + { + ins88[4] -= 0x37; + memcpy(nano9635ecm88Data, (uint8_t *)ecm88Data, ins88[4]); + write_cmd(ins88, nano9635ecm88Data); + } + else if(has9632) + { + ins88[4] -= 0x34; + memcpy(nano9632ecm88Data, (uint8_t *)ecm88Data, ins88[4]); + write_cmd(ins88, nano9632ecm88Data); + } + else + { + write_cmd(ins88, (uint8_t *)ecm88Data); // request dcw + } + + write_cmd(insc0, NULL); // read dcw + + switch(cta_res[0]) + { + case 0xe8: // even + if(cta_res[1] == 8) + { + memcpy(ea->cw, cta_res + 2, 8); + rc = 1; + } break; + + case 0xe9: // odd + if(cta_res[1] == 8) + { + memcpy(ea->cw + 8, cta_res + 2, 8); + rc = 1; + } break; + + case 0xea: // complete + if(cta_res[1] == 16) + { + memcpy(ea->cw, cta_res + 2, 16); + rc = 1; + } break; + + default : + ecm88Data = nextEcm; + ecm88Len -= curEcm88len; + rdr_log_dbg(reader, D_READER, "Error: card respondend %02X %02X, trying next ECM", cta_res[0], cta_res[1]); + snprintf(ea->msglog, MSGLOGSIZE, "key to use is not the current one, trying next ECM"); + } + } + else + { + //ecm88Data = nextEcm; + //ecm88Len -= curEcm88len; + rdr_log_dbg(reader, D_READER, "ECM: Unknown ECM type"); + snprintf(ea->msglog, MSGLOGSIZE, "Unknown ECM type"); + + return ERROR; /*Lets interupt the loop and exit, because we don't know this ECM type.*/ + } + } + + // Nano D2 0d, 11, 15 -> post AES decrypt CW + if(hasD2 && !dcw_crc(ea->cw) && (nanoD2 == 0x0d || nanoD2 == 0x11 || nanoD2 == 0x15)) + { + if(nanoD2 == 0x11) + { + hdSurEncPhase1_D2_0F_11(ea->cw); + hdSurEncPhase2_D2_0F_11(ea->cw); + } + + if(nanoD2 == 0x15) + { + hdSurEncPhase1_D2_13_15(ea->cw); + } + + rdr_log_dbg(reader, D_READER, "Decoding CW : using AES key id %d for provider %06x", D2KeyID, (provid & 0xFFFFF0)); + + rc = aes_decrypt_from_list(reader->aes_list, 0x500, (uint32_t)(provid & 0xFFFFF0), D2KeyID, ea->cw, 16); + if(rc == 0) + { + snprintf(ea->msglog, MSGLOGSIZE, "Missing AES key(%d)[aka E%X]",D2KeyID, D2KeyID); + } + + if(nanoD2 == 0x11) + { + hdSurEncPhase1_D2_0F_11(ea->cw); + } + + if(nanoD2 == 0x15) + { + hdSurEncPhase2_D2_13_15(ea->cw); + } + } + + if (has98) + { + if (N98Init == 0) + { + MakeSubKeys(KeyS, key98Idx); + N98Init = 1; + } + + uint8_t inp[16]; + memcpy(inp, ea->cw, 16); + N98_decrypt(inp); + memcpy(ea->cw, inp, 16); + } + + if (hasE0) + { + if (reader->initCA28) + { + uint8_t returnedcw[16]; + memcpy(returnedcw, ea->cw, 16); + // Processing 3DES + // Processing even cw + des(returnedcw, reader->key_schedule1, 0); // decrypt + des(returnedcw, reader->key_schedule2, 1); // crypt + des(returnedcw, reader->key_schedule1, 0); // decrypt + // Processing odd cw + des(returnedcw + 8, reader->key_schedule1, 0); // decrypt + des(returnedcw + 8, reader->key_schedule2, 1); // crypt + des(returnedcw + 8, reader->key_schedule1, 0); // decrypt + // returning value + memcpy(ea->cw, returnedcw, 16); + } + else + { + snprintf(ea->msglog, MSGLOGSIZE, "Nano E0 detected, no valid boxkey + deskey defined: no decoding"); + } + } + + if (SubECM == 1) + { + uint8_t rw[16]; + memcpy(rw, ea->cw, 16); + memcpy(ea->cw, rw + 4, 4); + memcpy(ea->cw + 4, rw, 4); + memcpy(ea->cw + 8, rw + 12, 4); + memcpy(ea->cw + 12, rw + 8, 4); + } + + return (rc ? OK : ERROR); +} + +static int32_t viaccess_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + uint32_t provid = 0; + rdr_log_dbg(rdr, D_EMM, "Entered viaccess_get_emm_type ep->emm[0]=%02x", ep->emm[0]); + + if(ep->emm[3] == 0x90 && ep->emm[4] == 0x03) + { + provid = b2i(3, ep->emm + 5); + provid &= 0xFFFFF0; + i2b_buf(4, provid, ep->provid); + } + + switch(ep->emm[0]) + { + case 0x88: + ep->type = UNIQUE; + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 4, 4); + rdr_log_dbg(rdr, D_EMM, "UNIQUE"); + if(!is_network_reader(rdr)) + { + return (!memcmp(rdr->hexserial + 1, ep->hexserial, 4)); // local reader + } + else + { + return 1; // let server decide! + } + + case 0x8A: + case 0x8B: + ep->type = GLOBAL; + rdr_log_dbg(rdr, D_EMM, "GLOBAL"); + return 1; + + case 0x8C: + case 0x8D: + ep->type = SHARED; + rdr_log_dbg(rdr, D_EMM, "SHARED (part)"); + // We need those packets to pass otherwise we would never + // be able to complete EMM reassembly + return 1; + + case 0x8E: + ep->type = SHARED; + rdr_log_dbg(rdr, D_EMM, "SHARED"); + memset(ep->hexserial, 0, 8); + memcpy(ep->hexserial, ep->emm + 3, 3); + // local reader + int8_t i; + for(i = 0; i < rdr->nprov; i++) + { + if(!memcmp(&rdr->prid[i][2], ep->hexserial+1, 2)) + { + return 1; + } + return (!memcmp(&rdr->sa[0][0], ep->hexserial, 3)); + } /* fallthrough */ + + default: + ep->type = UNKNOWN; + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + return 1; + } +} + +static int32_t viaccess_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ + if(*emm_filters == NULL) + { + bool network = is_network_reader(rdr); + int8_t device_emm = ((rdr->deviceemm > 0) ? 1 : 0); // set to 1 if device specific emms should be catched too + const unsigned int max_filter_count = 4 + ((device_emm != 0 && rdr->nprov > 0) ? 1:0) + (3 * ((rdr->nprov > 0) ? (rdr->nprov - 1) : 0)); + + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + int32_t idx = 0; + int32_t prov; + + if(rdr->nprov > 0 && device_emm == 1) + { + filters[idx].type = EMM_GLOBAL; // 8A or 8B no reassembly needed! + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x8A; + filters[idx].mask[0] = 0xFE; + filters[idx].filter[3] = 0x80; // device specific emms + filters[idx].mask[3] = 0x80; + idx++; + } + + // shared are most important put them on top, define first since viaccess produces a lot of filters! + for(prov = 0; (prov < rdr->nprov); prov++) + { + if((rdr->prid[prov][2] &0xF0) == 0xF0) // skip this not useful provider + { + continue; + } + + filters[idx].type = EMM_SHARED; // 8C or 8D always first part of shared, second part delivered by 8E! + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x8C; + filters[idx].mask[0] = 0xFE; + + if(rdr->nprov > 0) + { + memcpy(&filters[idx].filter[4], &rdr->prid[prov][2], 2); + filters[idx].mask[4] = 0xFF; + filters[idx].mask[5] = 0xF0; // ignore last digit since this is key on card indicator! + } + + idx++; + filters[idx].type = EMM_SHARED; // 8E second part reassembly with 8c/8d needed! + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x8E; + filters[idx].mask[0] = 0xFF; + + if(rdr->nprov > 0) + { + memcpy(&filters[idx].filter[1], &rdr->sa[prov][0], 3); + memset(&filters[idx].mask[1], 0xFF, 3); + } + + idx++; + } + + // globals are less important, define last since viaccess produces a lot of filters! + for(prov = 0; (prov < rdr->nprov); prov++) + { + if((rdr->prid[prov][2] &0xF0) == 0xF0) // skip this not useful provider + { + continue; + } + + filters[idx].type = EMM_GLOBAL; // 8A or 8B no reassembly needed! + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x8A; + filters[idx].mask[0] = 0xFE; + + if(rdr->nprov > 0) + { + memcpy(&filters[idx].filter[4], &rdr->prid[prov][2], 2); + filters[idx].mask[4] = 0xFF; + filters[idx].mask[5] = 0xF0; // ignore last digit since this is key on card indicator! + } + else if (device_emm == 0) + { + filters[idx].filter[3] = 0x00; // additional filter to cancel device specific emms + filters[idx].mask[3] = 0x80; + } + + idx++; + } + + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x88; + filters[idx].mask[0] = 0xFF; + + if(!network) // network has only 3 digits out of 4 + { + memcpy(&filters[idx].filter[1], rdr->hexserial + 1, 4); + memset(&filters[idx].mask[1], 0xFF, 4); + } + else + { + memcpy(&filters[idx].filter[1], rdr->hexserial + 1, 3); + memset(&filters[idx].mask[1], 0xFF, 3); + } + + idx++; + *filter_count = idx; + } + return OK; +} + +static int32_t viaccess_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + def_resp; + static const uint8_t insa4[] = { 0xca, 0xa4, 0x04, 0x00, 0x03 }; // set provider id + uint8_t insf0[] = { 0xca, 0xf0, 0x00, 0x01, 0x22 }; // set adf + uint8_t insf4[] = { 0xca, 0xf4, 0x00, 0x01, 0x00 }; // set adf, encrypted + uint8_t ins18[] = { 0xca, 0x18, 0x01, 0x01, 0x00 }; // set subscription + uint8_t ins1c[] = { 0xca, 0x1c, 0x01, 0x01, 0x00 }; // set subscription, encrypted + struct viaccess_data *csystem_data = reader->csystem_data; + int32_t emmdatastart = 7; + + if (ep->emm[1] == 0x01) // emm from cccam + { + emmdatastart = 12; + ep->emm[1] = 0x70; // (& 0x0f) of this byte is length, so 0x01 would increase the length by 256 + ep->emm[2] -= 1; + + if (ep->type == SHARED || ep->type == GLOBAL) // build missing 0x90 nano from provider at serial position + { + memcpy(ep->emm + 7, ep->emm + 3, 3); + ep->emm[5] = 0x90; + ep->emm[6] = 0x03; + ep->emm[9] |= 0x01; + ep->emm[10] = 0x9E; + ep->emm[11] = 0x20; + emmdatastart = 5; + } + } + + if(ep->type == UNIQUE) + { + emmdatastart++; + } + + if(ep->type == GLOBAL && emmdatastart == 7) + { + emmdatastart -= 4; + } + + int32_t emmLen = SCT_LEN(ep->emm) - emmdatastart; + int32_t rc = 0; + rdr_log_dump(reader, ep->emm, emmLen + emmdatastart, "RECEIVED EMM VIACCESS"); + int32_t emmUpToEnd; + uint8_t *emmParsed = ep->emm + emmdatastart; + int32_t provider_ok = 0; + uint32_t emm_provid = 0; + uint8_t keynr = 0; + int32_t ins18Len = 0; + uint8_t ins18Data[512]; + uint8_t insData[512]; + uint8_t *nano81Data = 0; + uint8_t *nano91Data = 0; + uint8_t *nano92Data = 0; + uint8_t *nano9EData = 0; + uint8_t *nanoF0Data = 0; + uint8_t *nanoA9Data = 0; + + for(emmUpToEnd = emmLen; (emmParsed[1] != 0) && (emmUpToEnd > 0); emmUpToEnd -= (2 + emmParsed[1]), emmParsed += (2 + emmParsed[1])) + { + rdr_log_dump(reader, emmParsed, emmParsed[1] + 2, "NANO"); + + if(emmParsed[0] == 0x90 && emmParsed[1] == 0x03) + { + /* identification of the service operator */ + uint8_t soid[3], ident[3], i; + for(i = 0; i < 3; i++) + { + soid[i] = ident[i] = emmParsed[2 + i]; + } + + ident[2] &= 0xF0; + emm_provid = b2i(3, ident); + keynr = soid[2] & 0x0F; + + if(chk_prov(reader, ident, keynr)) + { + provider_ok = 1; + } + else + { + rdr_log(reader, "EMM: ignored since provider or key not present on card (%x, %x)", emm_provid, keynr); + return SKIPPED; + } + + // check if the provider changes. If yes, set the new one. If not, don't... card will return an error if we do. + if(csystem_data->last_geo.provid != emm_provid) + { + write_cmd(insa4, ident); + if(cta_res[cta_lr - 2] != 0x90 || cta_res[cta_lr - 1] != 0x00) + { + rdr_log_dump(reader, insa4, 5, "set provider cmd:"); + rdr_log_dump(reader, soid, 3, "set provider data:"); + rdr_log(reader, "update error: %02X %02X", cta_res[cta_lr - 2], cta_res[cta_lr - 1]); + return ERROR; + } + } + + // as we are maybe changing the used provider, clear the cache, so the next ecm will re-select the correct one + csystem_data->last_geo.provid = 0; + csystem_data->last_geo.geo_len = 0; + csystem_data->last_geo.geo[0] = 0; + } + else if(emmParsed[0] == 0x9e && emmParsed[1] == 0x20) + { + /* adf */ + if(!nano91Data) + { + /* adf is not crypted, so test it */ + uint8_t custwp; + uint8_t *afd; + custwp = reader->sa[0][3]; + afd = (uint8_t *)emmParsed + 2; + + if(afd[31 - custwp / 8] & (1 << (custwp & 7))) + { + rdr_log_dbg(reader, D_READER, "emm for our card %08X", b2i(4, &reader->sa[0][0])); + } + else + { + rdr_log_dbg(reader, D_READER, "emm not suitable for our card %08X", b2i(4, &reader->sa[0][0])); + return SKIPPED; + } + } + + // memorize + nano9EData = emmParsed; + } + else if(emmParsed[0] == 0x81) + { + nano81Data = emmParsed; + } + else if(emmParsed[0] == 0x91 && emmParsed[1] == 0x08) + { + nano91Data = emmParsed; + } + else if(emmParsed[0] == 0x92 && emmParsed[1] == 0x08) + { + nano92Data = emmParsed; + } + else if(emmParsed[0] == 0xF0 && emmParsed[1] == 0x08) + { + nanoF0Data = emmParsed; + } + else if(emmParsed[0] == 0xF0 && emmParsed[1] == 0x10 && (((emm_provid >> 8) == 0x0419) || ((emm_provid >> 8) == 0x0702))) + { + nanoF0Data = emmParsed; + } + else if(emmParsed[0] == 0xD8 && emmParsed[2] == 0x45) + { + uint8_t pos = 4 + emmParsed[3]; + char *tmpbuf; + if(emmParsed[pos] == 0x46 && ((emmParsed[pos+1] - emmParsed[1]) == pos)) + { + if(cs_malloc(&tmpbuf, emmParsed[pos + 1])) + { + cs_strncpy(tmpbuf, (char *)emmParsed + pos + 2, emmParsed[pos + 1]); + rdr_log(reader, "Viaccess EMM-text: %s", tmpbuf); + NULLFREE(tmpbuf); + } + } + } + else + { + /* other nanos */ + show_subs(reader, emmParsed); + if(emmParsed[0] == 0xA9 && ep->type == SHARED) // check on shared (reassembled) emm if all classes are present and up to date on card: error 90 40 + { + if(!emm_provid) + { + rdr_log(reader, "no provid in shared emm -> skipped!"); + return SKIPPED; + } + int8_t match = add_find_class(reader, emm_provid, emmParsed + 2, emmParsed[1], 0); + + if(match == -2) + { + rdr_log(reader, "shared emm provid %06X all classes have entitlementdate already same or newer -> skipped!", emm_provid); + return SKIPPED; + } + + nanoA9Data = emmParsed; + } + + memcpy(ins18Data + ins18Len, emmParsed, emmParsed[1] + 2); + ins18Len += emmParsed [1] + 2; + } + } + + if(!provider_ok) + { + rdr_log_dbg(reader, D_READER, "provider not found in emm, continue anyway"); + // force key to 1... + keynr = 1; + // return ERROR; + } + + if(!nanoF0Data) + { + rdr_log_dump(reader, ep->emm, ep->emmlen, "can't find 0xf0 in emm..."); + return ERROR; // error + } + + if(nano9EData) + { + if(!nano91Data) + { + // set adf + insf0[3] = keynr; // key + insf0[4] = nano9EData[1] + 2; + write_cmd(insf0, nano9EData); + + if(cta_res[cta_lr - 2] != 0x90 || cta_res[cta_lr - 1] != 0x00) + { + rdr_log_dump(reader, insf0, 5, "set adf cmd:"); + rdr_log_dump(reader, nano9EData, insf0[4] , "set adf data:"); + rdr_log(reader, "update error: %02X %02X", cta_res[cta_lr - 2], cta_res[cta_lr - 1]); + return ERROR; + } + } + else + { + // set adf crypte + insf4[3] = keynr; // key + insf4[4] = nano91Data[1] + 2 + nano9EData[1] + 2; + memcpy(insData, nano91Data, nano91Data[1] + 2); + memcpy(insData + nano91Data[1] + 2, nano9EData, nano9EData[1] + 2); + write_cmd(insf4, insData); + + if((cta_res[cta_lr - 2] != 0x90 && cta_res[cta_lr - 2] != 0x91) || cta_res[cta_lr - 1] != 0x00) + { + rdr_log_dump(reader, insf4, 5, "set adf encrypted cmd:"); + rdr_log_dump(reader, insData, insf4[4], "set adf encrypted data:"); + rdr_log(reader, "update error: %02X %02X", cta_res[cta_lr - 2], cta_res[cta_lr - 1]); + return ERROR; + } + } + } + + if(!nano92Data) + { + // send subscription + ins18[2] = nano9EData ? 0x01 : 0x00; // found 9E nano ? + ins18[3] = keynr; // key + ins18[4] = ins18Len + nanoF0Data[1] + 2; + memcpy(insData, ins18Data, ins18Len); + memcpy(insData + ins18Len, nanoF0Data, nanoF0Data[1] + 2); + write_cmd(ins18, insData); + + if((cta_res[cta_lr - 2] == 0x90 || cta_res[cta_lr - 2] == 0x91) && cta_res[cta_lr - 1] == 0x00) + { + if(nanoA9Data) + { + add_find_class(reader, emm_provid, nanoA9Data + 2, nanoA9Data[1], 1); + rdr_log(reader, "Your subscription data was updated."); + } + rc = 1; // written + } + else + { + rdr_log_dump(reader, ins18, 5, "set subscription cmd:"); + rdr_log_dump(reader, insData, ins18[4], "set subscription data:"); + + if(!(cta_res[cta_lr -2] == 0x90 && cta_res[cta_lr - 1] == 0x40)) // dont throw softerror 9040 in log! + { + rdr_log(reader, "update error: %02X %02X", cta_res[cta_lr - 2], cta_res[cta_lr - 1]); + } + else + { + rc = 2; // skipped + } + } + } + else + { + // send subscription encrypted + if(!nano81Data) + { + rdr_log_dump(reader, ep->emm, ep->emmlen, "0x92 found, but can't find 0x81 in emm..."); + return ERROR; // error + } + + ins1c[2] = nano9EData ? 0x01 : 0x00; // found 9E nano ? + + if(ep->type == UNIQUE) + { + ins1c[2] = 0x02; + } + + ins1c[3] = keynr; // key + ins1c[4] = nano92Data[1] + 2 + nano81Data[1] + 2 + nanoF0Data[1] + 2; + memcpy(insData, nano92Data, nano92Data[1] + 2); + memcpy(insData + nano92Data[1] + 2, nano81Data, nano81Data[1] + 2); + memcpy(insData + nano92Data[1] + 2 + nano81Data[1] + 2, nanoF0Data, nanoF0Data[1] + 2); + write_cmd(ins1c, insData); + + if((cta_res[cta_lr - 2] == 0x90 || cta_res[cta_lr - 2] == 0x91) && (cta_res[cta_lr - 1] == 0x00 || cta_res[cta_lr - 1] == 0x08)) + { + rdr_log(reader, "update successfully written"); + } + + if(cta_res[cta_lr - 2] == 0x98 && cta_res[cta_lr - 1] == 0x00 ) + { + static const uint8_t insFAC[] = { 0x87, 0x02, 0x00, 0x00, 0x03 }; // init FAC + static uint8_t ins8702_data[] = { 0x00, 0x00, 0x11}; + static uint8_t ins8704[] = { 0x87, 0x04, 0x00, 0x00, 0x07 }; + static uint8_t ins8706[] = { 0x87, 0x06, 0x00, 0x00, 0x04 }; + write_cmd(insFAC, ins8702_data); + + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00)) + { + write_cmd(ins8704, NULL); + + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00)) + { + write_cmd(ins8706, NULL); + + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00)) + { + csystem_data->last_geo.number_ecm = (cta_res[2] << 8) | (cta_res[3]); + rdr_log(reader, "using ecm #%x for long viaccess ecm", csystem_data->last_geo.number_ecm); + } + } + } + } + + rc = 1; + } + + return rc; +} +static int32_t viaccess_card_info(struct s_reader *reader) +{ + def_resp; + int32_t i, l; + time_t now; + + struct tm timeinfo; + + uint16_t tmpdate; + uint8_t insac[] = { 0xca, 0xac, 0x00, 0x00, 0x00 }; // select data + uint8_t insb8[] = { 0xca, 0xb8, 0x00, 0x00, 0x00 }; // read selected data + uint8_t insa4[] = { 0xca, 0xa4, 0x00, 0x00, 0x00 }; // select issuer + uint8_t insc0[] = { 0xca, 0xc0, 0x00, 0x00, 0x00 }; // read data item + static const uint8_t ins24[] = { 0xca, 0x24, 0x00, 0x00, 0x09 }; // set pin + uint8_t cls[] = { 0x00, 0x21, 0xff, 0x9f}; + uint8_t prebook[] = {0xFC, 0x00, 0x00, 0xFE, 0xFF, 0xFF}; + static const uint8_t pin[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04}; + + struct viaccess_data *csystem_data = reader->csystem_data; + + csystem_data->last_geo.provid = 0; + csystem_data->last_geo.geo_len = 0; + csystem_data->last_geo.geo[0] = 0; + rdr_log(reader, "card detected"); + cs_clear_entitlement(reader); //reset the entitlements + + // set pin + write_cmd(ins24, pin); + + insac[2] = 0xa4; + write_cmd(insac, NULL); // request unique id + + insb8[4] = 0x07; + write_cmd(insb8, NULL); // read unique id + rdr_log_sensitive(reader, "serial: {%llu}", (unsigned long long) b2ll(5, cta_res + 2)); + + insa4[2] = 0x00; + write_cmd(insa4, NULL); // select issuer 0 + for(i = 1; (cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0); i++) + { + bool added = false; + uint32_t l_provid, l_sa; + uint8_t l_name[64]; + + insc0[4] = 0x1a; + write_cmd(insc0, NULL); // show provider properties + cta_res[2] &= 0xF0; + l_provid = b2i(3, cta_res); + + insac[2] = 0xa5; + write_cmd(insac, NULL); // request sa + + insb8[4] = 0x06; + write_cmd(insb8, NULL); // read sa + l_sa = b2i(4, cta_res + 2); + + insac[2] = 0xa7; + write_cmd(insac, NULL); // request name + + insb8[4] = 0x02; + write_cmd(insb8, NULL); // read name nano + len + l = cta_res[1]; + + insb8[4] = l; + write_cmd(insb8, NULL); // read name + cta_res[l] = 0; + trim((char *)cta_res); + if(cta_res[0]) + { + snprintf((char *)l_name, sizeof(l_name), ", name: %.55s", cta_res); + } + else + { + l_name[0] = 0; + } + + // read GEO + insac[2] = 0xa6; + write_cmd(insac, NULL); // request GEO + + insb8[4] = 0x02; + write_cmd(insb8, NULL); // read GEO nano + len + l = cta_res[1]; + char tmp[l * 3 + 1]; + insb8[4] = l; + write_cmd(insb8, NULL); // read geo + rdr_log_sensitive(reader, "provider: %d, id: {%06X%s}, sa: {%08X}, geo: %s", i, l_provid, l_name, l_sa, (l < 4) ? "empty" : cs_hexdump(1, cta_res, l, tmp, sizeof(tmp))); + + // read classes subscription + insac[2] = 0xa9; + insac[4] = 4; + if(!reader->read_old_classes) + { + now = time(NULL) - (24 * 60 * 60); + cs_gmtime_r(&now, &timeinfo); + tmpdate = timeinfo.tm_mday | ((timeinfo.tm_mon + 1) << 5) | ((timeinfo.tm_year - 80) << 9); + cls[0] = tmpdate >> 8; + cls[1] = tmpdate & 0xff; + } + write_cmd(insac, cls); // request class subs + while((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0)) + { + insb8[4] = 0x02; + write_cmd(insb8, NULL); // read class subs nano + len + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0)) + { + l = cta_res[1]; + insb8[4] = l; + write_cmd(insb8, NULL); // read class subs + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00 || cta_res[cta_lr - 1] == 0x08)) + { + show_class(reader, NULL, l_provid, cta_res, cta_lr - 2); + added = true; + } + } + } + if(!added) + { + // add entitlement info for provid without class + cs_add_entitlement(reader, reader->caid, l_provid, 0, 0, 0, 0, 5, 1); + } + + // Read List of «pre-booked pay-per-view per programme» entitlements within the range [INUMB, FNUMB] + insac[2] = 0xaa; + insac[4] = 6; + write_cmd(insac, prebook); // request class subs + while((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00)) + { + insb8[4] = 0x02; + write_cmd(insb8, NULL); // read PPV + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0)) + { + l = cta_res[1]; + insb8[4] = l; + write_cmd(insb8, NULL); // read PPV + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0x00 || cta_res[cta_lr - 1] == 0x08)) + { + time_t start_t, end_t; + struct tm tm; + memset(&tm, 0, sizeof(tm)); + + tm.tm_year = (cta_res[2] >> 4) + 116; + tm.tm_mon = (cta_res[2] & 0xF) - 1; + tm.tm_mday = 1; + start_t = cs_timegm(&tm); + + tm.tm_year = (cta_res[5] >> 4) + 116; + tm.tm_mon = (cta_res[5] & 0xF) - 1; + if ((tm.tm_mon == 0) || (tm.tm_mon == 2) || (tm.tm_mon == 4) || (tm.tm_mon == 6) || (tm.tm_mon == 7) || (tm.tm_mon == 9) || (tm.tm_mon == 11)) + { + tm.tm_mday = 31; + } + else if ((tm.tm_mon == 3) || (tm.tm_mon == 5) || (tm.tm_mon == 8) || (tm.tm_mon == 10)) + { + tm.tm_mday = 30; + } + else if ((tm.tm_mon == 1) && (((cta_res[5] >> 4) % 4) == 0)) + { + tm.tm_mday = 29; + } + else + { + tm.tm_mday = 28; + } + end_t = cs_timegm(&tm); + + cs_add_entitlement(reader, reader->caid, l_provid, cta_res[1], 0, start_t, end_t, 2, 1); + } + } + } + insac[4] = 0; + insa4[2] = 0x02; + write_cmd(insa4, NULL); // select next provider + } + // return ERROR; + + // Start process init CA 28 + reader->initCA28=0; + int32_t lenboxkey = reader->boxkey_length; + int32_t lendeskey = reader->des_key_length; + + if ((lenboxkey >= 4) && (lendeskey > 0)) + { + uint8_t ins28[] = { 0xCA, 0x28, 0x00, 0x00, 0x04 }; //Init for nanoE0 ca28 + ins28[4] = (uint8_t) lenboxkey; + uint8_t ins28_data[4]; + memcpy(ins28_data, reader->boxkey, 4); + write_cmd(ins28, ins28_data); // unlock card to reply on E002xxyy + if((cta_res[cta_lr - 2] == 0x90) && (cta_res[cta_lr - 1] == 0)) + { + rdr_log(reader, "CA 28 initialisation successful!"); + // init 3DES key + des_set_key(reader->des_key, reader->key_schedule1); + des_set_key(reader->des_key+8, reader->key_schedule2); + reader->initCA28=1; + } + else + { + rdr_log(reader, "CA 28 initialisation failed! CA 28 refused"); + } + } + //end process init CA 28 + + return OK; +} + +static int32_t viaccess_reassemble_emm(struct s_reader *rdr, struct s_client *client, EMM_PACKET *ep) +{ + uint8_t *buffer = ep->emm; + int16_t *len = &ep->emmlen; + int32_t pos = 0, i; + int16_t k; + int32_t prov, provid = 0; + + struct emm_rass *r_emm = NULL; + + // Viaccess + if(*len > 500) + { + return 0; + } + + switch(buffer[0]) + { + case 0x8c: + case 0x8d: + // emm-s part 1 + provid = b2i(3, ep->emm + 5); // extract provid from emm + provid &= 0xFFFFF0; // last digit is dont care + r_emm = find_rabuf(client, provid, (uint8_t) buffer[0], 1); + if(!r_emm) + { + cs_log("[viaccess] ERROR: Can't allocate EMM reassembly buffer."); + return 0; + } + if(!memcmp(&r_emm->emm, &buffer[0], *len)) // skip same shared emm, this make sure emmlen isnt replaced. emmlen = 0 means this shared emm has been used for reassembly + { + return 0; + } + memset(&r_emm->emm[0], 0, sizeof(r_emm->emm)); // zero it! + memcpy(&r_emm->emm[0], &buffer[0], *len); // put the fresh new shared emm + r_emm->emmlen = *len; // put the emmlen indicating that this shared emm isnt being reassembled + rdr_log_dump_dbg(rdr, D_EMM, r_emm->emm, r_emm->emmlen, "%s: received fresh emm-gh for provid %06X", __func__, provid); + return 0; + + case 0x8e: + // emm-s part 2 + for(prov = 0; prov < rdr->nprov ; prov++) + { + if(!memcmp(&buffer[3], &rdr->sa[prov][0], 3)) + { + //matching sa found! + if(is_network_reader(rdr)) + { + provid = b2i(4, ep->provid); // use provid from emm since we have nothing better! + provid &= 0xFFFFF0; // last digit is dont care + } + else + { + provid = b2i(4, rdr->prid[prov]); // get corresponding provid from reader since there is no provid in emm payload! + provid &= 0xFFFFF0; // last digit is dont care + } + r_emm = find_rabuf(client, provid, 0, 0); // nano = don't care, the shared 8c or 8d not been written gets returned! + if(!r_emm || !r_emm->emmlen) + { + continue; // match but no emm-gh found for this provider + } + else + { + break; // stop searching-> emm-gh found! + } + } + } + if(!r_emm || !r_emm->emmlen) + { + return 0; // stop -> no emm-gh found! + } + //extract nanos from emm-gh and emm-s + uint8_t emmbuf[512]; + rdr_log_dbg(rdr, D_EMM, "%s: start extracting nanos", __func__); + //extract from emm-gh + for(i = 3; i < r_emm->emmlen; i += r_emm->emm[i + 1] + 2) + { + //copy nano (length determined by i+1) + memcpy(emmbuf + pos, r_emm->emm + i, r_emm->emm[i + 1] + 2); + pos += r_emm->emm[i + 1] + 2; + } + if(buffer[2] == 0x2c) + { + //add 9E 20 nano + first 32 uint8_ts of emm content + memcpy(emmbuf + pos, "\x9E\x20", 2); + memcpy(emmbuf + pos + 2, buffer + 7, 32); + pos += 34; + //add F0 08 nano + 8 subsequent uint8_ts of emm content + memcpy(emmbuf + pos, "\xF0\x08", 2); + memcpy(emmbuf + pos + 2, buffer + 39, 8); + pos += 10; + } + else if(buffer[2] == 0x34 && (((provid >> 8) == 0x0419) || ((provid >> 8) == 0x0702))) + { + //add 9E 20 nano + first 32 uint8_ts of emm content + memcpy(emmbuf + pos, "\x9E\x20", 2); + memcpy(emmbuf + pos + 2, buffer + 7, 32); + pos += 34; + //add F0 10 nano + 16 subsequent uint8_ts of emm content + memcpy(emmbuf + pos, "\xF0\x10", 2); + memcpy(emmbuf + pos + 2, buffer + 39, 16); + pos += 18; + } + else + { + //extract from variable emm-s + for(k = 7; k < (*len); k += buffer[k + 1] + 2) + { + //copy nano (length determined by k+1) + memcpy(emmbuf + pos, buffer + k, buffer[k + 1] + 2); + pos += buffer[k + 1] + 2; + } + } + rdr_log_dump_dbg(rdr, D_EMM, buffer, *len, "%s: %s emm-s", __func__, (buffer[2] == 0x2c) ? "fixed" : "variable"); + emm_sort_nanos(buffer + 7, emmbuf, pos); + pos += 7; + //calculate emm length and set it on position 2 + buffer[2] = pos - 3; + rdr_log_dump_dbg(rdr, D_EMM, r_emm->emm, r_emm->emmlen, "%s: emm-gh provid %06X", __func__, provid); + rdr_log_dump_dbg(rdr, D_EMM, buffer, pos, "%s: assembled emm", __func__); + *len = pos; + r_emm->emmlen = 0; // mark this shared 8c or 8d as being used for reassembly and send to reader! + break; + } + + return 1; +} + +const struct s_cardsystem reader_viaccess = +{ + .desc = "viaccess", + .caids = (uint16_t[]){ 0x05, 0 }, + .do_emm_reassembly = viaccess_reassemble_emm, + .do_emm = viaccess_do_emm, + .do_ecm = viaccess_do_ecm, + .card_info = viaccess_card_info, + .card_init = viaccess_card_init, + .get_emm_type = viaccess_get_emm_type, + .get_emm_filter = viaccess_get_emm_filter, +}; + +#endif diff --git a/reader-videoguard-common.c b/reader-videoguard-common.c new file mode 100644 index 0000000..fd0078d --- /dev/null +++ b/reader-videoguard-common.c @@ -0,0 +1,1149 @@ +// +// Common videoguard functions. +// + +#include "globals.h" +#ifdef READER_VIDEOGUARD +#include "reader-common.h" +#include "reader-videoguard-common.h" + +#define VG_EMMTYPE_MASK 0xC0 +#define VG_EMMTYPE_G 0 +#define VG_EMMTYPE_U 1 +#define VG_EMMTYPE_S 2 + +typedef struct mailmsg_s +{ + uint16_t caid; + uint32_t serial; + uint16_t date; + uint16_t id; + uint8_t nsubs; + uint16_t len; + uint8_t mask; + uint8_t written; + char *message; + char *subject; +} MAILMSG; + +static LLIST *vg_msgs; + +static void cCamCryptVG_LongMult(uint16_t *pData, uint16_t *pLen, uint32_t mult, uint32_t carry); +static void cCamCryptVG_PartialMod(uint16_t val, uint32_t count, uint16_t *outkey, const uint16_t *inkey); +static void cCamCryptVG_RotateRightAndHash(uint8_t *p); +static void cCamCryptVG_Reorder16A(uint8_t *dest, const uint8_t *src); +static void cCamCryptVG_ReorderAndEncrypt(struct s_reader *reader, uint8_t *p); +static void cCamCryptVG_Process_D0(struct s_reader *reader, const uint8_t *ins, uint8_t *data); +static void cCamCryptVG_Process_D1(struct s_reader *reader, const uint8_t *ins, uint8_t *data, const uint8_t *status); +static void cCamCryptVG_Decrypt_D3(struct s_reader *reader, uint8_t *ins, uint8_t *data, const uint8_t *status); +static void cCamCryptVG_PostProcess_Decrypt(struct s_reader *reader, uint8_t *rxbuff); +static int32_t cAES_Encrypt(struct s_reader *reader, const uint8_t *data, int32_t len, uint8_t *crypted); +static void swap_lb(uint8_t *buff, int32_t len); + +// returns 1 if cw_is_valid, returns 0 if cw is all zeros +int32_t cw_is_valid(uint8_t *cw) +{ + int32_t i; + + for(i = 0; i < 8; i++) + { + if(cw[i] != 0) // test if cw = 00 + { + return OK; + } + } + return ERROR; +} + +void cAES_SetKey(struct s_reader *reader, const uint8_t *key) +{ + struct videoguard_data *csystem_data = reader->csystem_data; + AES_set_encrypt_key(key, 128, &(csystem_data->ekey)); +} + +int32_t cAES_Encrypt(struct s_reader *reader, const uint8_t *data, int32_t len, uint8_t *crypted) +{ + struct videoguard_data *csystem_data = reader->csystem_data; + len = (len + 15) & (~15); // pad up to a multiple of 16 + int32_t i; + for(i = 0; i < len; i += 16) { AES_encrypt(data + i, crypted + i, &(csystem_data->ekey)); } + return len; +} + +static void swap_lb(uint8_t *buff, int32_t len) +{ +#if __BYTE_ORDER != __BIG_ENDIAN + return; +#endif + int32_t i; + uint8_t tmp; + + for(i = 0; i < len / 2; i++) + { + tmp = buff[i * 2]; + buff[i * 2] = buff[(i * 2) + 1]; + buff[(i * 2) + 1] = tmp; + } +} + +void __xxor(uint8_t *data, int32_t len, const uint8_t *v1, const uint8_t *v2) +{ + uint32_t i; + switch(len) + { + case 16: + for(i = 0; i < 16; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + + case 8: + for(i = 0; i < 8; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + + case 4: + for(i = 0; i < 4; ++i) + { + data[i] = v1[i] ^ v2[i]; + } + break; + + default: + while(len--) { *data++ = *v1++ ^ *v2++; } + } +} + +void cCamCryptVG_SetSeed(struct s_reader *reader) +{ +#if __BYTE_ORDER != __BIG_ENDIAN + static const uint8_t key1[] = + { + 0xb9, 0xd5, 0xef, 0xd5, 0xf5, 0xd5, 0xfb, 0xd5, 0x31, 0xd6, 0x43, 0xd6, 0x55, 0xd6, 0x61, 0xd6, + 0x85, 0xd6, 0x9d, 0xd6, 0xaf, 0xd6, 0xc7, 0xd6, 0xd9, 0xd6, 0x09, 0xd7, 0x15, 0xd7, 0x21, 0xd7, + 0x27, 0xd7, 0x3f, 0xd7, 0x45, 0xd7, 0xb1, 0xd7, 0xbd, 0xd7, 0xdb, 0xd7, 0x11, 0xd8, 0x23, 0xd8, + 0x29, 0xd8, 0x2f, 0xd8, 0x4d, 0xd8, 0x8f, 0xd8, 0xa1, 0xd8, 0xad, 0xd8, 0xbf, 0xd8, 0xd7, 0xd8 + }; + static const uint8_t key2[] = + { + 0x01, 0x00, 0xcf, 0x13, 0xe0, 0x60, 0x54, 0xac, 0xab, 0x99, 0xe6, 0x0c, 0x9f, 0x5b, 0x91, 0xb9, + 0x72, 0x72, 0x4d, 0x5b, 0x5f, 0xd3, 0xb7, 0x5b, 0x01, 0x4d, 0xef, 0x9e, 0x6b, 0x8a, 0xb9, 0xd1, + 0xc9, 0x9f, 0xa1, 0x2a, 0x8d, 0x86, 0xb6, 0xd6, 0x39, 0xb4, 0x64, 0x65, 0x13, 0x77, 0xa1, 0x0a, + 0x0c, 0xcf, 0xb4, 0x2b, 0x3a, 0x2f, 0xd2, 0x09, 0x92, 0x15, 0x40, 0x47, 0x66, 0x5c, 0xda, 0xc9 + }; +#else + static const uint8_t key1[] = + { + 0xd5, 0xb9, 0xd5, 0xef, 0xd5, 0xf5, 0xd5, 0xfb, 0xd6, 0x31, 0xd6, 0x43, 0xd6, 0x55, 0xd6, 0x61, + 0xd6, 0x85, 0xd6, 0x9d, 0xd6, 0xaf, 0xd6, 0xc7, 0xd6, 0xd9, 0xd7, 0x09, 0xd7, 0x15, 0xd7, 0x21, + 0xd7, 0x27, 0xd7, 0x3f, 0xd7, 0x45, 0xd7, 0xb1, 0xd7, 0xbd, 0xd7, 0xdb, 0xd8, 0x11, 0xd8, 0x23, + 0xd8, 0x29, 0xd8, 0x2f, 0xd8, 0x4d, 0xd8, 0x8f, 0xd8, 0xa1, 0xd8, 0xad, 0xd8, 0xbf, 0xd8, 0xd7 + }; + static const uint8_t key2[] = + { + 0x00, 0x01, 0x13, 0xcf, 0x60, 0xe0, 0xac, 0x54, 0x99, 0xab, 0x0c, 0xe6, 0x5b, 0x9f, 0xb9, 0x91, + 0x72, 0x72, 0x5b, 0x4d, 0xd3, 0x5f, 0x5b, 0xb7, 0x4d, 0x01, 0x9e, 0xef, 0x8a, 0x6b, 0xd1, 0xb9, + 0x9f, 0xc9, 0x2a, 0xa1, 0x86, 0x8d, 0xd6, 0xb6, 0xb4, 0x39, 0x65, 0x64, 0x77, 0x13, 0x0a, 0xa1, + 0xcf, 0x0c, 0x2b, 0xb4, 0x2f, 0x3a, 0x09, 0xd2, 0x15, 0x92, 0x47, 0x40, 0x5c, 0x66, 0xc9, 0xda + }; +#endif + struct videoguard_data *csystem_data = reader->csystem_data; + memcpy(csystem_data->cardkeys[1], key1, sizeof(csystem_data->cardkeys[1])); + memcpy(csystem_data->cardkeys[2], key2, sizeof(csystem_data->cardkeys[2])); +} + +void cCamCryptVG_GetCamKey(struct s_reader *reader, uint16_t *tb2) +{ + struct videoguard_data *csystem_data = reader->csystem_data; + uint16_t c = 1; + memset(tb2, 0, 64); + tb2[0] = 1; + int32_t i; + + for(i = 0; i < 32; i++) + { + cCamCryptVG_LongMult(tb2, &c, csystem_data->cardkeys[1][i], 0); + } + swap_lb((uint8_t *)tb2, 64); +} + +static void cCamCryptVG_PostProcess_Decrypt(struct s_reader *reader, uint8_t *rxbuff) +{ + switch(rxbuff[0]) + { + case 0xD0: + cCamCryptVG_Process_D0(reader, rxbuff, rxbuff + 5); + break; + + case 0xD1: + cCamCryptVG_Process_D1(reader, rxbuff, rxbuff + 5, rxbuff + rxbuff[4] + 5); + break; + + case 0xD3: + cCamCryptVG_Decrypt_D3(reader, rxbuff, rxbuff + 5, rxbuff + rxbuff[4] + 5); + break; + } +} + +static void cCamCryptVG_Process_D0(struct s_reader *reader, const uint8_t *ins, uint8_t *data) +{ + struct videoguard_data *csystem_data = reader->csystem_data; + + switch(ins[1]) + { + case 0xb4: + swap_lb(data, 64); + memcpy(csystem_data->cardkeys[0], data, sizeof(csystem_data->cardkeys[0])); + break; + + case 0xbc: + { + swap_lb(data, 64); + const uint16_t *key1 = (const uint16_t *)csystem_data->cardkeys[1]; + uint16_t key2[32]; + memcpy(key2, csystem_data->cardkeys[2], sizeof(key2)); + int32_t count2; + uint16_t iidata[32]; + memcpy((uint8_t *)&iidata, data, 64); + + for(count2 = 0; count2 < 32; count2++) + { + uint32_t rem = 0, divisor = key1[count2]; + int8_t i; + + for(i = 31; i >= 0; i--) + { + uint32_t x = iidata[i] | (rem << 16); + rem = (x % divisor) & 0xffff; + } + + uint32_t carry = 1, t = val_by2on3(divisor) | 1; + + while(t) + { + if(t & 1) { carry = ((carry * rem) % divisor) & 0xffff; } + rem = ((rem * rem) % divisor) & 0xffff; + t >>= 1; + } + cCamCryptVG_PartialMod(carry, count2, key2, key1); + } + + uint16_t idatacount = 0; + int32_t i; + + for(i = 31; i >= 0; i--) + { + cCamCryptVG_LongMult(iidata, &idatacount, key1[i], key2[i]); + } + + memcpy(data, iidata, 64); + swap_lb(data, 64); + uint8_t stateD1[16]; + cCamCryptVG_Reorder16A(stateD1, data); + cAES_SetKey(reader, stateD1); + break; + } + } +} + +static void cCamCryptVG_Process_D1(struct s_reader *reader, const uint8_t *ins, uint8_t *data, const uint8_t *status) +{ + struct videoguard_data *csystem_data = reader->csystem_data; + uint8_t iter[16], tmp[16]; + + memset(iter, 0, sizeof(iter)); + memcpy(iter, ins, 5); + xor16(iter, csystem_data->stateD3A, iter); + memcpy(csystem_data->stateD3A, iter, sizeof(iter)); + + int32_t datalen = status - data; + int32_t datalen1 = datalen; + + if(datalen < 0) + { + datalen1 += 15; + } + + int32_t blocklen = datalen1 >> 4; + int32_t i; + int32_t iblock; + + for(i = 0, iblock = 0; i < blocklen + 2; i++, iblock += 16) + { + uint8_t in[16]; + int32_t docalc = 1; + + if(blocklen == i && (docalc = datalen & 0xf)) + { + memset(in, 0, sizeof(in)); + memcpy(in, &data[iblock], datalen - (datalen1 & ~0xf)); + } + else if(blocklen + 1 == i) + { + memset(in, 0, sizeof(in)); + memcpy(&in[5], status, 2); + } + else + { + memcpy(in, &data[iblock], sizeof(in)); + } + + if(docalc) + { + xor16(iter, in, tmp); + cCamCryptVG_ReorderAndEncrypt(reader, tmp); + xor16(tmp, csystem_data->stateD3A, iter); + } + } + memcpy(csystem_data->stateD3A, tmp, 16); +} + +static void cCamCryptVG_Decrypt_D3(struct s_reader *reader, uint8_t *ins, uint8_t *data, const uint8_t *status) +{ + struct videoguard_data *csystem_data = reader->csystem_data; + + if(ins[4] > 16) + { + ins[4] -= 16; + } + + if(ins[1] == 0xbe) + { + memset(csystem_data->stateD3A, 0, sizeof(csystem_data->stateD3A)); + } + + uint8_t tmp[16]; + memset(tmp, 0, sizeof(tmp)); + memcpy(tmp, ins, 5); + xor16(tmp, csystem_data->stateD3A, csystem_data->stateD3A); + + int32_t len1 = ins[4]; + int32_t blocklen = len1 >> 4; + + if(ins[1] != 0xbe) + { + blocklen++; + } + + uint8_t iter[16], states[16][16]; + memset(iter, 0, sizeof(iter)); + + int32_t blockindex; + for(blockindex = 0; blockindex < blocklen; blockindex++) + { + iter[0] += blockindex; + xor16(iter, csystem_data->stateD3A, iter); + cCamCryptVG_ReorderAndEncrypt(reader, iter); + xor16(iter, &data[blockindex * 16], states[blockindex]); + + if(blockindex == (len1 >> 4)) + { + int32_t c = len1 - (blockindex * 16); + if(c < 16) + { + memset(&states[blockindex][c], 0, 16 - c); + } + } + xor16(states[blockindex], csystem_data->stateD3A, csystem_data->stateD3A); + cCamCryptVG_RotateRightAndHash(csystem_data->stateD3A); + } + + memset(tmp, 0, sizeof(tmp)); + memcpy(tmp + 5, status, 2); + xor16(tmp, csystem_data->stateD3A, csystem_data->stateD3A); + cCamCryptVG_ReorderAndEncrypt(reader, csystem_data->stateD3A); + + memcpy(csystem_data->stateD3A, status - 16, sizeof(csystem_data->stateD3A)); + cCamCryptVG_ReorderAndEncrypt(reader, csystem_data->stateD3A); + + memcpy(data, states[0], len1); + + if(ins[1] == 0xbe) + { + cCamCryptVG_Reorder16A(tmp, states[0]); + cAES_SetKey(reader, tmp); + } +} + +static void cCamCryptVG_ReorderAndEncrypt(struct s_reader *reader, uint8_t *p) +{ + uint8_t tmp[16]; + cCamCryptVG_Reorder16A(tmp, p); + cAES_Encrypt(reader, tmp, 16, tmp); + cCamCryptVG_Reorder16A(p, tmp); +} + +// reorder AAAABBBBCCCCDDDD to ABCDABCDABCDABCD +static void cCamCryptVG_Reorder16A(uint8_t *dest, const uint8_t *src) +{ + int32_t i; + int32_t j; + int32_t k; + for(i = 0, k = 0; i < 4; i++) + { + for(j = i; j < 16; j += 4, k++) + { + dest[k] = src[j]; + } + } +} + +static void cCamCryptVG_LongMult(uint16_t *pData, uint16_t *pLen, uint32_t mult, uint32_t carry) +{ + int32_t i; + for(i = 0; i < *pLen; i++) + { + carry += pData[i] * mult; + pData[i] = (uint16_t)carry; + carry >>= 16; + } + + if(carry) { pData[(*pLen)++] = carry; } +} + +static void cCamCryptVG_PartialMod(uint16_t val, uint32_t count, uint16_t *outkey, const uint16_t *inkey) +{ + if(count) + { + uint32_t mod = inkey[count]; + uint16_t mult = (inkey[count] - outkey[count - 1]) & 0xffff; + uint32_t i; + uint32_t ib1; + + for(i = 0, ib1 = count - 2; i < count - 1; i++, ib1--) + { + uint32_t t = (inkey[ib1] * mult) % mod; + mult = t - outkey[ib1]; + + if(mult > t) + { + mult += mod; + } + } + mult += val; + + if((val > mult) || (mod < mult)) + { + mult -= mod; + } + outkey[count] = (outkey[count] * mult) % mod; + } + else + { + outkey[0] = val; + } +} + +static void cCamCryptVG_RotateRightAndHash(uint8_t *p) +{ + static const uint8_t table1[256] = + { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, + }; + + uint8_t t1 = p[15]; + int32_t i; + + for(i = 0; i < 16; i++) + { + uint8_t t2 = t1; + t1 = p[i]; + p[i] = table1[(t1 >> 1) | ((t2 & 1) << 7)]; + } +} + +int32_t status_ok(const uint8_t *status) +{ + //rdr_log(reader, "check status %02x%02x", status[0],status[1]); + return (status[0] == 0x90 || status[0] == 0x91) && + (status[1] == 0x00 || status[1] == 0x01 || status[1] == 0x20 || status[1] == 0x21 || + status[1] == 0x80 || status[1] == 0x81 || status[1] == 0xa0 || status[1] == 0xa1); +} + +/*checksum for precam datas*/ +int32_t checksum_ok(const uint8_t *ird_payload) +{ + int32_t b, check = 0; + + for (b = 0; b <= ird_payload[1]; b++) + { + check = (check + ird_payload[b]) & 0xFF; + } + + if (ird_payload[ird_payload[1] + 1] == check) + { + return 1; + } + else + { + return 0; // Checksum error + } +} + +void memorize_cmd_table(struct s_reader *reader, const uint8_t *mem, int32_t size) +{ + struct videoguard_data *csystem_data = reader->csystem_data; + NULLFREE(csystem_data->cmd_table); + if(cs_malloc(&csystem_data->cmd_table, size)) + { + memcpy(csystem_data->cmd_table, mem, size); + } +} + +int32_t cmd_table_get_info(struct s_reader *reader, const uint8_t *cmd, uint8_t *rlen, uint8_t *rmode) +{ + struct videoguard_data *csystem_data = reader->csystem_data; + struct s_CmdTabEntry *pcte = csystem_data->cmd_table->e; + int32_t i; + + for(i = 0; i < csystem_data->cmd_table->Nentries; i++, pcte++) + { + if(cmd[1] == pcte->cmd) + { + *rlen = pcte->len; + *rmode = pcte->mode; + return 1; + } + } + return 0; +} + +int32_t cmd_exists(struct s_reader *reader, const uint8_t *cmd) +{ + struct videoguard_data *csystem_data = reader->csystem_data; + struct s_CmdTabEntry *pcte = csystem_data->cmd_table->e; + int32_t i; + + for(i = 0; i < csystem_data->cmd_table->Nentries; i++, pcte++) + { + if(cmd[1] == pcte->cmd) + { + return 1; + } + } + return 0; +} + +int32_t read_cmd_len(struct s_reader *reader, const uint8_t *cmd) +{ + def_resp; + uint8_t cmd2[5]; + memcpy(cmd2, cmd, 5); + + if(cmd2[0] == 0xD3) // use classD1 for length request of classD3 + { + cmd2[0] = 0xD1; + } + cmd2[3] |= 0x80; + cmd2[4] = 1; + + uint8_t rxbuff[8]; + memcpy(rxbuff, cmd2, 5); + + // some card reply with L 91 00 (L being the command length). + if(!write_cmd_vg(cmd2, NULL) || !status_ok(cta_res + 1) || cta_res[0] == 0) + { + if(cta_res[0] == 0) // some cards reply len=0x00 for not supported ins + { + rdr_log_dbg(reader, D_READER, "failed to read %02x%02x cmd length (%02x %02x)", + cmd[1], cmd[2], cta_res[1], cta_res[2]); + } + else // others reply only status byte + { + rdr_log_dbg(reader, D_READER, "failed to read %02x%02x cmd length (%02x %02x)", + cmd[1], cmd[2], cta_res[0], cta_res[1]); + } + + memcpy(rxbuff + 5, cta_res, 3); + cCamCryptVG_PostProcess_Decrypt(reader, rxbuff); + + return -1; + } + + memcpy(rxbuff + 5, cta_res, 3); + cCamCryptVG_PostProcess_Decrypt(reader, rxbuff); + + return cta_res[0]; +} + +int32_t do_cmd(struct s_reader *reader, const uint8_t *ins, const uint8_t *txbuff, uint8_t *rxbuff, uint8_t *cta_res) +{ + uint16_t cta_lr; + uint8_t ins2[5]; + memcpy(ins2, ins, 5); + uint8_t len = 0, mode = 0; + + if(cmd_table_get_info(reader, ins2, &len, &mode)) + { + if(len == 0xFF && mode == 2) + { + if(ins2[4] == 0) + { + ins2[4] = len = read_cmd_len(reader, ins2); + if(len == 0xFF) + { + return -1; + } + } + } + else if((mode != 0) && ((ins2[3] != 0x7f) && (ins2[4] != 0x02))) + { + ins2[4] = len; + } + } + + if(ins2[0] == 0xd3) + { + if(ins2[4] == 0) + { + return 0; + } + ins2[4] += 16; + } + + len = ins2[4]; + uint8_t tmp[264]; + + if(rxbuff == NULL) + { + rxbuff = tmp; + } + + if(mode > 1) + { + if(!write_cmd_vg(ins2, NULL) || !status_ok(cta_res + len)) + { + return -1; + } + + memcpy(rxbuff, ins2, 5); + memcpy(rxbuff + 5, cta_res, len); + memcpy(rxbuff + 5 + len, cta_res + len, 2); + } + else + { + if(!write_cmd_vg(ins2, txbuff) || !status_ok(cta_res)) + { + return -2; + } + + memcpy(rxbuff, ins2, 5); + memcpy(rxbuff + 5, txbuff, len); + memcpy(rxbuff + 5 + len, cta_res, 2); + } + cCamCryptVG_PostProcess_Decrypt(reader, rxbuff); + + if(ins2[0] == 0xd3) + { + rdr_log_dump_dbg(reader, D_READER, rxbuff + 5, rxbuff[4], "Decrypted payload"); + } + + return len; +} + +int32_t videoguard_do_rawcmd(struct s_reader *reader, CMD_PACKET *cp) +{ + int32_t rc; + unsigned char cta_res[CTA_RES_LEN]; + memset(cta_res, 0, sizeof(cta_res)); + + reader->last_poll = time(0); // keep videoguard2_poll_status at bay, because it hurts consecutive commands + + rc = do_cmd(reader, cp->cmd, &cp->cmd[5], NULL, cta_res); + + return rc; +} + +void rev_startdate_calc_tm(const uint8_t *Date, struct tm *timeinfo, int32_t startdate_base_month, int32_t startdate_base_year) +{ + timeinfo->tm_year = Date[0] / 12 + startdate_base_year - 1900; // tm year starts at 1900 + timeinfo->tm_mon = (Date[0] % 12) + (startdate_base_month - 1); // tm month starts with 0 + timeinfo->tm_mday = Date[1] & 0x1f; + timeinfo->tm_hour = Date[2] / 8; + timeinfo->tm_min = (0x100 * (Date[2] - timeinfo->tm_hour * 8) + Date[3]) / 32; + timeinfo->tm_sec = (Date[3] - timeinfo->tm_min * 32) * 2; +} + +void rev_expiredate_calc_tm(const uint8_t *Date, struct tm *timeinfo, int32_t expiredate_base_month, int32_t expiredate_base_year) +{ + timeinfo->tm_year = Date[0] / 12 + expiredate_base_year - 1900; // tm year starts at 1900 + timeinfo->tm_mon = (Date[0] % 12) + (expiredate_base_month - 1); // tm month starts with 0 + timeinfo->tm_mday = Date[1] & 0x1f; + timeinfo->tm_hour = Date[2] / 8; + timeinfo->tm_min = (0x100 * (Date[2] - timeinfo->tm_hour * 8) + Date[3]) / 32; + timeinfo->tm_sec = (Date[3] - timeinfo->tm_min * 32) * 2; +} + +int32_t videoguard_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr) +{ + int32_t i; + int32_t serial_count = ((ep->emm[3] >> 4) & 3) + 1; + int32_t serial_len = (ep->emm[3] & 0x80) ? 3 : 4; + uint8_t emmtype = (ep->emm[3] & VG_EMMTYPE_MASK) >> 6; + + switch(emmtype) + { + case VG_EMMTYPE_G: + rdr_log_dbg(rdr, D_EMM, "GLOBAL"); + ep->type = GLOBAL; + return 1; + + case VG_EMMTYPE_U: + case VG_EMMTYPE_S: + rdr_log_dbg(rdr, D_EMM, "%s", (emmtype == VG_EMMTYPE_U) ? "UNIQUE" : "SHARED"); + ep->type = emmtype; + if(ep->emm[1] == 0) // detected UNIQUE EMM from cccam (there is no serial) + { + rdr_log_dbg(rdr, D_EMM, "CCCam unique EMM detected, no serial available, skipping filter check"); + ep->skip_filter_check = 1; + return 1; + } + for(i = 0; i < serial_count; i++) + { + if(!memcmp(&ep->emm[i * 4 + 4], rdr->hexserial + 2, serial_len)) + { + memcpy(ep->hexserial, &ep->emm[i * 4 + 4], serial_len); + return 1; + } + } + return 0; // if UNIQUE or SHARED but no serial match return FALSE + + default: + // remote emm without serial + rdr_log_dbg(rdr, D_EMM, "UNKNOWN"); + ep->type = UNKNOWN; + return 1; + } +} + +int32_t videoguard_do_emm(struct s_reader *reader, EMM_PACKET *ep, uint8_t CLA, void (*read_tiers)(struct s_reader *), + int32_t (*docmd)(struct s_reader *, const uint8_t *ins, const uint8_t *txbuff, uint8_t *rxbuff, uint8_t *cta_res)) +{ + uint8_t cta_res[CTA_RES_LEN]; + uint8_t ins42[5] = { CLA, 0x42, 0x00, 0x00, 0xFF }; + uint8_t *EmmIrdHeader; + int32_t rc = SKIPPED; + int32_t nsubs = ((ep->emm[3] & 0x30) >> 4) + 1; + int32_t offs = 4; + int32_t emmv2 = 0; + int32_t position, ua_position = -1; + int32_t serial_len = (ep->type == SHARED) ? 3 : 4; + int32_t vdrsc_fix = 0; + + if(ep->type == UNIQUE || ep->type == SHARED) + { + if(ep->emm[1] == 0x00) // cccam sends emm-u without UA + { + nsubs = 1; + ua_position = 0; + } + else + { + int32_t i; + for(i = 0; i < nsubs; ++i) + { + if(memcmp(&ep->emm[4 + i * 4], &reader->hexserial[2], serial_len) == 0) + { + ua_position = i; + break; + } + } + offs += nsubs * 4; + } + + if(ua_position == -1) + { + return ERROR; + } + } + + if(ep->emm[offs] == 0x00 && (ep->emm[offs + 1] == 0x00 || ep->emm[offs + 1] == 0x01)) // unmodified emm from dvbapi + { + emmv2 = ep->emm[offs + 1]; + offs += 2 + 1 + emmv2; // skip sub-emm len (2 bytes sub-emm len if 0x01); + } + + for(position = 0; position < nsubs && offs + 2 < ep->emmlen; ++position) + { + if(ep->emm[offs] > 0x07) // workaround for mgcamd and emmv2 + { + ++offs; + } + + if(ep->emm[offs] == 0x02 || ep->emm[offs] == 0x03 || ep->emm[offs] == 0x07) + { + if(ep->emm[offs+1] != 0) // Checksum test for sub-packets emm: + { + EmmIrdHeader = ep->emm + offs; // example: + int32_t chk; // 827097300000 + chk = checksum_ok(EmmIrdHeader); // D002 0602C317ABA02F 1690144004A6... chk=2F + if (chk != 1) // D607 0E03A3010325070102810002000778 1B90154004A9... chk=78 + { // D607 0E03A301032507010281000200097A 1B9015400441... chk=7A + return rc; + } + } + + if(ep->emm[offs] == 0x03) + { + if(position == ua_position || vdrsc_fix) + { + videoguard_mail_msg(reader, &ep->emm[offs + 2]); + return rc; + } + else + { + offs += ep->emm[offs + 1] + 2; + if(!(offs + 1 < ep->emmlen)) + { + return rc; + } + + if(ep->emm[offs] == 0x00 && (ep->emm[offs + 1] == 0x00 || ep->emm[offs + 1] == 0x01)) + { + offs += 2 + 1 + emmv2; + } + continue; + } + } + + offs += ep->emm[offs + 1] + 2; + if(!(offs + 1 < ep->emmlen)) + { + return rc; + } + + if(ep->emm[offs] != 0) + { + if(ep->type == GLOBAL || vdrsc_fix || position == ua_position) + { + ins42[4] = ep->emm[offs]; + int32_t l = (*docmd)(reader, ins42, &ep->emm[offs + 1], NULL, cta_res); + rc = (l > 0 && status_ok(cta_res)) ? OK : ERROR; + rdr_log_dbg(reader, D_EMM, "request return code : %02X%02X", cta_res[0], cta_res[1]); + + if(status_ok(cta_res) && (cta_res[1] & 0x01)) + { + (*read_tiers)(reader); + } + } + + offs += ep->emm[offs] + 1; + if(offs < ep->emmlen && ep->emm[offs] == 0x00) + { + ++offs; + } + } + + offs += 1 + emmv2; + if(vdrsc_fix) + { + --position; + } + } + else + { + return rc; + } + } + return rc; +} + +uint8_t videoguard_get_emm_filter_address_byte(uint8_t isUnique, uint32_t n) +{ + uint8_t ret; + switch(n) + { + default: + case 0: + // do not filter by sub-emm count + ret = 0; + break; + + case 1: + // unused + // here we would need two filters, + // one with sub-emm count 1x, and one with 01 + ret = 0x10; + break; + + case 2: + // filter sub-emm count with 1x + ret = 0x20; + break; + + case 3: + // filter sub-emm count with 11 + ret = 0x30; + break; + } + + if(isUnique) + { + ret |= 0x40; + } + else //shared + { + ret |= 0x80; + } + + return ret; +} + +uint8_t videoguard_get_emm_filter_address_mask(uint32_t n) +{ + uint8_t ret = 0xC0; + switch(n) + { + default: + case 0: + // at least 1 sub-emm is always present, so we do not care + break; + + case 1: + // must have 2 sub-emms or more (01, 10, 11, but not 00) + // we could create a 1x and 01 filter here, + // but atm we do not care, to keep the filter number low + break; + + case 2: + // must have 3 sub-emms or more (10, 11, but not 00, 01) + ret |= 0x20; + break; + + case 3: + // must have 4 sub-emms (11) + ret |= 0x30; + break; + } + + return ret; +} + +int32_t videoguard_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count) +{ + if(*emm_filters == NULL) + { + const unsigned int max_filter_count = 7; + if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter))) + { + return ERROR; + } + + struct s_csystem_emm_filter *filters = *emm_filters; + *filter_count = 0; + int32_t idx = 0; + uint32_t n; + + for(n = 0; n < 3; ++n) + { + filters[idx].type = EMM_UNIQUE; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = videoguard_get_emm_filter_address_byte(1, n); + filters[idx].mask[1] = videoguard_get_emm_filter_address_mask(n); + memcpy(&filters[idx].filter[2 + 4 * n], rdr->hexserial + 2, 4); + memset(&filters[idx].mask[2 + 4 * n], 0xFF, 4); + idx++; + } + + // fourth serial position does not fit within the 16bytes demux filter + for(n = 0; n < 3; ++n) + { + filters[idx].type = EMM_SHARED; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = videoguard_get_emm_filter_address_byte(0, n); + filters[idx].mask[1] = videoguard_get_emm_filter_address_mask(n); + memcpy(&filters[idx].filter[2 + 4 * n], rdr->hexserial + 2, 3); + memset(&filters[idx].mask[2 + 4 * n], 0xFF, 3); + idx++; + } + + // fourth serial position does not fit within the 16bytes demux filter + filters[idx].type = EMM_GLOBAL; + filters[idx].enabled = 1; + filters[idx].filter[0] = 0x82; + filters[idx].mask[0] = 0xFF; + filters[idx].filter[1] = 0x00; + filters[idx].mask[1] = 0xC0; + idx++; + *filter_count = idx; + } + return OK; +} + +static MAILMSG *find_msg(uint16_t caid, uint32_t serial, uint16_t date, uint16_t msg_id) +{ + MAILMSG *msg; + LL_ITER it = ll_iter_create(vg_msgs); + while((msg = (MAILMSG *)ll_iter_next(&it))) + { + if(msg->caid == caid && msg->serial == serial && msg->date == date && msg->id == msg_id) + { + return msg; + } + } + return 0; +} + +static void write_msg(struct s_reader *reader, MAILMSG *msg, uint32_t basemonth, uint32_t baseyear) +{ + FILE *fp = fopen(cfg.mailfile, "a"); + if(fp == 0) + { + rdr_log(reader, "Cannot open mailfile %s", cfg.mailfile); + return; + } + + uint16_t i; + for(i = 0; i < msg->len - 1; ++i) + { + if(msg->message[i] == 0x00 && msg->message[i + 1] == 0x32) + { + msg->subject = &msg->message[i + 3]; + break; + } + } + + int32_t year = (msg->date >> 8) / 12 + baseyear; + int32_t mon = ((msg->date >> 8) % 12) + (basemonth - 1) + 1; + int32_t day = msg->date & 0x1f; + + fprintf(fp, "%04X:%08X:%02d/%02d/%04d:%04X:\"%s\":\"%s\"\n", + msg->caid, + msg->serial, + day, + mon, + year, + msg->id, + msg->subject, + msg->message); + + fclose(fp); + NULLFREE(msg->message); + msg->message = msg->subject = 0; + msg->written = 1; +} + +static void msgs_init(uint32_t basemonth, uint32_t baseyear) +{ + vg_msgs = ll_create("vg_msgs"); + FILE *fp = fopen(cfg.mailfile, "r"); + if(fp == 0) + { + return; + } + + int32_t year, mon, day; + char buffer[2048]; + + while(fgets(buffer, sizeof(buffer), fp)) + { + MAILMSG *msg; + if(!cs_malloc(&msg, sizeof(MAILMSG))) + { + fclose(fp); + return; + } + + sscanf(buffer, "%04hX:%08X:%02d/%02d/%04d:%04hX", &msg->caid, &msg->serial, &day, &mon, &year, &msg->id); + year -= baseyear; + mon += basemonth - 1; + msg->date = ((year * 12) + mon - 1) << 8 | day; + msg->message = msg->subject = 0; + msg->written = 1; + ll_append(vg_msgs, msg); + } + fclose(fp); +} + +void videoguard_mail_msg(struct s_reader *rdr, uint8_t *data) +{ + if(cfg.disablemail) + { + return; + } + + if(vg_msgs == 0) + { + msgs_init(rdr->card_expiredate_basemonth, rdr->card_expiredate_baseyear); + } + + if(data[0] != 0xFF || data[1] != 0xFF) + { + return; + } + + uint16_t msg_id = (data[2] << 8) | data[3]; + uint8_t idx = data[4] & 0x0F; + int32_t msg_size = data[5] * 10 + 2; + uint16_t date = (data[9] << 8) | data[10]; + int32_t submsg_len = data[12] - 2; + uint16_t submsg_idx = (data[13] << 8) | data[14]; + uint32_t serial = (rdr->hexserial[2] << 24) | (rdr->hexserial[3] << 16) | (rdr->hexserial[4] << 8) | rdr->hexserial[5]; + MAILMSG *msg = find_msg(rdr->caid, serial, date, msg_id); + + if(msg == 0) + { + if(!cs_malloc(&msg, sizeof(MAILMSG))) + { + return; + } + msg->caid = rdr->caid; + msg->serial = serial; + msg->date = date; + msg->id = msg_id; + msg->nsubs = (data[4] & 0xF0) >> 4; + msg->mask = 1 << idx; + msg->written = 0; + msg->len = submsg_len; + + if(!cs_malloc(&msg->message, msg_size)) + { + NULLFREE(msg); + return; + } + + memset(msg->message, 0, msg_size); + memcpy(&msg->message[submsg_idx], &data[15], submsg_len); + msg->subject = 0; + ll_append(vg_msgs, msg); + } + else + { + if(msg->written == 1 || msg->mask & (1 << idx)) + { + return; + } + + msg->mask |= 1 << idx; + msg->len += submsg_len; + memcpy(&msg->message[submsg_idx], &data[15], submsg_len); + } + + if(msg->mask == (1 << msg->nsubs) - 1) + { + write_msg(rdr, msg, rdr->card_expiredate_basemonth, rdr->card_expiredate_baseyear); + } +} +#endif diff --git a/reader-videoguard-common.h b/reader-videoguard-common.h new file mode 100644 index 0000000..b48ff49 --- /dev/null +++ b/reader-videoguard-common.h @@ -0,0 +1,66 @@ +#ifndef __NDS_COMMON__ +#define __NDS_COMMON__ + +#define write_cmd_vg(cmd, data) (card_write(reader, cmd, data, cta_res, &cta_lr) == 0) + +#define NDSAUTO 0 +#define NDS1 1 +#define NDS12 12 +#define NDS2 2 + +struct s_CmdTabEntry +{ + uint8_t cla; + uint8_t cmd; + uint8_t len; + uint8_t mode; +}; + +struct s_CmdTab +{ + uint8_t index; + uint8_t size; + uint8_t Nentries; + uint8_t dummy; + struct s_CmdTabEntry e[1]; +}; + +struct videoguard_data +{ + time_t card_valid_to; + struct s_CmdTab *cmd_table; + uint16_t cardkeys[3][32]; + uint8_t stateD3A[16]; + AES_KEY ekey; + AES_KEY astrokey; +}; + +extern int32_t cw_is_valid(uint8_t *cw); +extern void cAES_SetKey(struct s_reader *reader, const uint8_t *key); + +extern void __xxor(uint8_t *data, int32_t len, const uint8_t *v1, const uint8_t *v2); +#define xor16(v1,v2,d) __xxor((d),16,(v1),(v2)) +#define val_by2on3(x) ((0xaaab*(x))>>16) // fixed point *2/3 + +extern void cCamCryptVG_SetSeed(struct s_reader *reader); +extern void cCamCryptVG_GetCamKey(struct s_reader *reader, uint16_t *tb2); +extern int32_t status_ok(const uint8_t *status); +extern int32_t checksum_ok(const uint8_t *ird_payload); +extern void memorize_cmd_table(struct s_reader *reader, const uint8_t *mem, int32_t size); +extern int32_t cmd_table_get_info(struct s_reader *reader, const uint8_t *cmd, uint8_t *rlen, uint8_t *rmode); +extern int32_t cmd_exists(struct s_reader *reader, const uint8_t *cmd); +extern int32_t read_cmd_len(struct s_reader *reader, const uint8_t *cmd); +extern int32_t do_cmd(struct s_reader *reader, const uint8_t *ins, const uint8_t *txbuff, uint8_t *rxbuff, uint8_t *cta_res); +int32_t videoguard_do_rawcmd(struct s_reader *reader, CMD_PACKET *cp); +extern void rev_startdate_calc_tm(const uint8_t *Date, struct tm *timeinfo, int32_t startdate_base_month, int32_t startdate_base_year); +extern void rev_expiredate_calc_tm(const uint8_t *Date, struct tm *timeinfo, int32_t expiredate_base_month, int32_t expiredate_base_year); +extern void set_known_card_info(struct s_reader *reader, const uint8_t *atr, const uint32_t *atr_size); + +int32_t videoguard_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr); +int32_t videoguard_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **, unsigned int *); +int32_t videoguard_do_emm(struct s_reader *reader, EMM_PACKET *ep, uint8_t CLA, void (*read_tiers)(struct s_reader *), + int32_t (*docmd)(struct s_reader *, const uint8_t *ins, const uint8_t *txbuff, uint8_t *rxbuff, uint8_t *cta_res) + ); +void videoguard_mail_msg(struct s_reader *rdr, uint8_t *data); + +#endif // __NDS_COMMON__ diff --git a/reader-videoguard1.c b/reader-videoguard1.c new file mode 100644 index 0000000..54a38af --- /dev/null +++ b/reader-videoguard1.c @@ -0,0 +1,384 @@ +#include "globals.h" +#ifdef READER_VIDEOGUARD +#include "reader-common.h" +#include "reader-videoguard-common.h" + +static int32_t vg1_do_cmd(struct s_reader *reader, const uint8_t *ins, const uint8_t *txbuff, uint8_t *rxbuff, uint8_t *cta_res) +{ + uint16_t cta_lr; + uint8_t ins2[5]; + memcpy(ins2, ins, 5); + uint8_t len = 0; + len = ins2[4]; + + if(txbuff == NULL) + { + if(!write_cmd_vg(ins2, NULL) || !status_ok(cta_res + len)) + { + return -1; + } + + if(rxbuff != NULL) + { + memcpy(rxbuff, ins2, 5); + memcpy(rxbuff + 5, cta_res, len); + memcpy(rxbuff + 5 + len, cta_res + len, 2); + } + } + else + { + if(!write_cmd_vg(ins2, (uint8_t *)txbuff) || !status_ok(cta_res)) + { + return -2; + } + + if(rxbuff != NULL) + { + memcpy(rxbuff, ins2, 5); + memcpy(rxbuff + 5, txbuff, len); + memcpy(rxbuff + 5 + len, cta_res, 2); + } + } + + return len; +} + +static void read_tiers(struct s_reader *reader) +{ + def_resp; + //const uint8_t ins2a[5] = { 0x48, 0x2a, 0x00, 0x00, 0x00 }; + int32_t l; + + //return; // Not working at present so just do nothing + + //l = vg1_do_cmd(reader, ins2a, NULL, NULL, cta_res); + //if (l < 0 || !status_ok(cta_res + l)) + //{ + // return; + //} + + uint8_t ins76[5] = { 0x48, 0x76, 0x00, 0x00, 0x00 }; + ins76[3] = 0x7f; + ins76[4] = 2; + + if(!write_cmd_vg(ins76, NULL) || !status_ok(cta_res + 2)) + { + return; + } + + ins76[3] = 0; + ins76[4] = 0x0a; + int32_t num = cta_res[1]; + int32_t i; + + cs_clear_entitlement(reader); // reset the entitlements + + for(i = 0; i < num; i++) + { + ins76[2] = i; + l = vg1_do_cmd(reader, ins76, NULL, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res + l)) + { + return; + } + + if(cta_res[2] == 0 && cta_res[3] == 0) + { + break; + } + + uint16_t tier_id = (cta_res[2] << 8) | cta_res[3]; + // add entitlements to list + struct tm timeinfo; + memset(&timeinfo, 0, sizeof(struct tm)); + rev_expiredate_calc_tm(&cta_res[4], &timeinfo, reader->card_expiredate_basemonth, reader->card_expiredate_baseyear); + char tiername[83]; + cs_add_entitlement(reader, reader->caid, b2ll(4, reader->prid[0]), tier_id, 0, 0, mktime(&timeinfo), 4, 1); + rdr_log(reader, "tier: %04x, expiry date: %04d/%02d/%02d-%02d:%02d:%02d %s", + tier_id, timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, + timeinfo.tm_min, timeinfo.tm_sec, get_tiername(tier_id, reader->caid, tiername)); + } +} + +static int32_t videoguard1_card_init(struct s_reader *reader, ATR *newatr) +{ + get_hist; + /* 40 B0 09 4A 50 01 4E 5A */ + if((hist_size < 7) || (hist[1] != 0xB0) || (hist[3] != 0x4A) || (hist[4] != 0x50)) + { + return ERROR; + } + + get_atr; + def_resp; + + if(!cs_malloc(&reader->csystem_data, sizeof(struct videoguard_data))) + { + return ERROR; + } + + if((reader->ndsversion != NDS1) && (reader->ndsversion != NDSAUTO)) + { + /* known ATR and not NDS1 + or unknown ATR and not forced to NDS1 + or known NDS1 ATR and forced to another NDS version + ...probably not NDS1 */ + return ERROR; + } + + rdr_log(reader, "type: VideoGuard1 Card, base tiers expiration date: %i/%i", reader->card_expiredate_basemonth, reader->card_expiredate_baseyear); + if(reader->ndsversion == NDS1) + { + rdr_log(reader, "forced to NDS1+"); + } + + /* NDS1 Class 48 only cards only need a very basic initialisation + NDS1 Class 48 only cards do not respond to vg1_do_cmd(ins7416) + nor do they return list of valid command therefore do not even try + NDS1 Class 48 only cards need to be told the length as (48, ins, 00, 80, 01) + does not return the length */ + + int32_t l = 0; + uint8_t buff[256]; + memset(buff, 0, sizeof(buff)); + + /* Try to get the boxid from the card, even if BoxID specified in the config file + also used to check if it is an NDS1 card as the returned information will + not be encrypted if it is an NDS1 card */ + + static const uint8_t ins36[5] = { 0x48, 0x36, 0x00, 0x00, 0x90 }; + uint8_t boxID[4]; + int32_t boxidOK = 0; + l = vg1_do_cmd(reader, ins36, NULL, buff, cta_res); + + if(buff[7] > 0x0F) + { + rdr_log(reader, "class48 ins36: encrypted - therefore not an NDS1 card"); + return ERROR; + } + else + { + /* skipping the initial fixed fields: cmdecho (4) + length (1) + encr/rev++ (4) */ + int32_t i = 9; + int32_t gotUA = 0; + while(i < l) + { + if(!gotUA && buff[i] < 0xF0) /* then we guess that the next 4 bytes is the UA */ + { + gotUA = 1; + i += 4; + } + else + { + switch(buff[i]) /* object length vary depending on type */ + { + case 0x00: /* padding */ + { + i += 1; + break; + } + + case 0xEF: /* card status */ + { + i += 3; + break; + } + + case 0xD1: + { + i += 4; + break; + } + + case 0xDF: /* next server contact */ + { + i += 5; + break; + } + + case 0xF3: /* boxID */ + { + memcpy(&boxID, &buff[i + 1], sizeof(boxID)); + boxidOK = 1; + i += 5; + break; + } + + case 0xF6: + { + i += 6; + break; + } + + case 0xFC: /* No idea seems to with with NDS1 */ + { + i += 14; + break; + } + + case 0x01: /* date & time */ + { + i += 7; + break; + } + + case 0xFA: + { + i += 9; + break; + } + + case 0x5E: + case 0x67: /* signature */ + case 0xDE: + case 0xE2: + case 0xE9: /* tier dates */ + case 0xF8: /* Old PPV Event Record */ + case 0xFD: + { + i += buff[i + 1] + 2; /* skip length + 2 bytes (type and length) */ + break; + } + + default: /* default to assume a length byte */ + { + rdr_log(reader, "class48 ins36: returned unknown type=0x%02X - parsing may fail", buff[i]); + i += buff[i + 1] + 2; + } + } + } + } + } + + /* the boxid is specified in the config */ + if(reader->boxid > 0) + { + int32_t i; + for(i = 0; i < 4; i++) + { + boxID[i] = (reader->boxid >> (8 * (3 - i))) % 0x100; + } + } + else + { + if(!boxidOK) + { + rdr_log(reader, "no boxID available"); + return ERROR; + } + } + + // Send BoxID + static const uint8_t ins4C[5] = { 0x48, 0x4C, 0x00, 0x00, 0x09 }; + uint8_t payload4C[9] = { 0, 0, 0, 0, 3, 0, 0, 0, 4 }; + memcpy(payload4C, boxID, 4); + + if(!write_cmd_vg(ins4C, payload4C) || !status_ok(cta_res + l)) + { + rdr_log(reader, "class48 ins4C: sending boxid failed"); + return ERROR; + } + + static const uint8_t ins58[5] = { 0x48, 0x58, 0x00, 0x00, 0x17 }; + l = vg1_do_cmd(reader, ins58, NULL, buff, cta_res); + if(l < 0) + { + rdr_log(reader, "class48 ins58: failed"); + return ERROR; + } + + memset(reader->hexserial, 0, 8); + memcpy(reader->hexserial + 2, cta_res + 1, 4); + memcpy(reader->sa, cta_res + 1, 3); + //reader->caid = cta_res[24] * 0x100 + cta_res[25]; + /* Force caid until can figure out how to get it */ + reader->caid = 0x9 * 0x100 + 0x69; + + /* we have one provider, 0x0000 */ + reader->nprov = 1; + memset(reader->prid, 0x00, sizeof(reader->prid)); + + rdr_log_sensitive(reader, "type: VideoGuard, caid: %04X, serial: {%02X%02X%02X%02X}, BoxID: {%02X%02X%02X%02X}", + reader->caid, reader->hexserial[2], reader->hexserial[3], reader->hexserial[4], reader->hexserial[5], + boxID[0], boxID[1], boxID[2], boxID[3]); + rdr_log(reader, "ready for requests"); + + return OK; +} + +static int32_t videoguard1_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + uint8_t cta_res[CTA_RES_LEN]; + uint8_t ins40[5] = { 0x48, 0x40, 0x00, 0x80, 0xFF }; + static const uint8_t ins54[5] = { 0x48, 0x54, 0x00, 0x00, 0x0D }; + int32_t posECMpart2 = er->ecm[6] + 7; + int32_t lenECMpart2 = er->ecm[posECMpart2]; + uint8_t tbuff[264]; + uint8_t rbuff[264]; + memcpy(&tbuff[0], &(er->ecm[posECMpart2 + 1]), lenECMpart2); + ins40[4] = lenECMpart2; + int32_t l; + l = vg1_do_cmd(reader, ins40, tbuff, NULL, cta_res); + + if(l > 0 && status_ok(cta_res)) + { + l = vg1_do_cmd(reader, ins54, NULL, rbuff, cta_res); + if(l > 0 && status_ok(cta_res + l)) + { + if(!cw_is_valid(rbuff + 5)) // sky cards report 90 00 = ok but send cw = 00 when channel not subscribed + { + rdr_log(reader, "class48 ins54 status 90 00 but cw=00 -> channel not subscribed"); + return ERROR; + } + + if(er->ecm[0] & 1) + { + memset(ea->cw + 0, 0, 8); + memcpy(ea->cw + 8, rbuff + 5, 8); + } + else + { + memcpy(ea->cw + 0, rbuff + 5, 8); + memset(ea->cw + 8, 0, 8); + } + return OK; + } + } + rdr_log(reader, "class48 ins54 (%d) status not ok %02x %02x", l, cta_res[0], cta_res[1]); + return ERROR; +} + +static int32_t videoguard1_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + return videoguard_do_emm(reader, ep, 0x48, read_tiers, vg1_do_cmd); +} + +static int32_t videoguard1_do_rawcmd(struct s_reader *reader, CMD_PACKET *cp) +{ + return videoguard_do_rawcmd(reader, cp); +} + +static int32_t videoguard1_card_info(struct s_reader *reader) +{ + /* info is displayed in init, or when processing info */ + rdr_log(reader, "card detected"); + rdr_log(reader, "type: VideoGuard1 Card"); + read_tiers(reader); + return OK; +} + +const struct s_cardsystem reader_videoguard1 = +{ + .desc = "videoguard1", + .caids = (uint16_t[]){ 0x09, 0 }, + .do_emm = videoguard1_do_emm, + .do_rawcmd = videoguard1_do_rawcmd, + .do_ecm = videoguard1_do_ecm, + .card_info = videoguard1_card_info, + .card_init = videoguard1_card_init, + .get_emm_type = videoguard_get_emm_type, + .get_emm_filter = videoguard_get_emm_filter, +}; + +#endif diff --git a/reader-videoguard12.c b/reader-videoguard12.c new file mode 100644 index 0000000..96d1ab2 --- /dev/null +++ b/reader-videoguard12.c @@ -0,0 +1,428 @@ +#include "globals.h" +#ifdef READER_VIDEOGUARD +#include "reader-common.h" +#include "reader-videoguard-common.h" + +static int32_t vg12_do_cmd(struct s_reader *reader, const uint8_t *ins, const uint8_t *txbuff, uint8_t *rxbuff, uint8_t *cta_res) +{ + uint16_t cta_lr; + uint8_t ins2[5]; + memcpy(ins2, ins, 5); + uint8_t len = 0; + len = ins2[4]; + + if(txbuff == NULL) + { + if(!write_cmd_vg(ins2, NULL) || !status_ok(cta_res + len)) + { + return -1; + } + + if(rxbuff != NULL) + { + memcpy(rxbuff, ins2, 5); + memcpy(rxbuff + 5, cta_res, len); + memcpy(rxbuff + 5 + len, cta_res + len, 2); + } + } + else + { + if(!write_cmd_vg(ins2, (uint8_t *)txbuff) || !status_ok(cta_res)) + { + return -2; + } + + if(rxbuff != NULL) + { + memcpy(rxbuff, ins2, 5); + memcpy(rxbuff + 5, txbuff, len); + memcpy(rxbuff + 5 + len, cta_res, 2); + } + } + return len; +} + +static void read_tiers(struct s_reader *reader) +{ + def_resp; + + static const uint8_t ins2A[5] = { 0x48, 0x2A, 0x00, 0x00, 0x90 }; + int32_t l; + + if(!write_cmd_vg(ins2A, NULL) || !status_ok(cta_res + cta_lr - 2)) + { + rdr_log(reader, "class48 ins2A: failed"); + return; + } + + // return at present as not sure how to parse this + return; + + uint8_t ins76[5] = { 0x48, 0x76, 0x00, 0x00, 0x00 }; + ins76[3] = 0x7f; + ins76[4] = 2; + + if(!write_cmd_vg(ins76, NULL) || !status_ok(cta_res + 2)) + { + return; + } + + ins76[3] = 0; + ins76[4] = 0x0a; + + int32_t num = cta_res[1]; + int32_t i; + + cs_clear_entitlement(reader); // reset the entitlements + + for(i = 0; i < num; i++) + { + ins76[2] = i; + l = vg12_do_cmd(reader, ins76, NULL, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res + l)) + { + return; + } + + if(cta_res[2] == 0 && cta_res[3] == 0) + { + break; + } + + uint16_t tier_id = (cta_res[2] << 8) | cta_res[3]; + struct tm timeinfo; + memset(&timeinfo, 0, sizeof(struct tm)); + rev_expiredate_calc_tm(&cta_res[4], &timeinfo, reader->card_expiredate_basemonth, reader->card_expiredate_baseyear); + cs_add_entitlement(reader, reader->caid, b2ll(4, reader->prid[0]), tier_id, 0, 0, mktime(&timeinfo), 4, 1); + char tiername[83]; + rdr_log(reader, "tier: %04x, expiry date: %04d/%02d/%02d-%02d:%02d:%02d %s", + tier_id, timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, + timeinfo.tm_min, timeinfo.tm_sec, get_tiername(tier_id, reader->caid, tiername)); + } +} + +static int32_t videoguard12_card_init(struct s_reader *reader, ATR *newatr) +{ + get_hist; + + if((hist_size < 7) || (hist[1] != 0xB0) || (hist[4] != 0xFF) || (hist[5] != 0x4A) || (hist[6] != 0x50)) + { + return ERROR; + } + + get_atr; + def_resp; + + if(!cs_malloc(&reader->csystem_data, sizeof(struct videoguard_data))) + { + return ERROR; + } + + if((reader->ndsversion != NDS12) && (reader->ndsversion != NDSAUTO)) + { + /* known ATR and not NDS12 + or unknown ATR and not forced to NDS12 + or known NDS12 ATR and forced to another NDS version + ...probably not NDS12 */ + return ERROR; + } + + rdr_log(reader, "type: VideoGuard12 Card, base tiers expiration date: %i/%i", reader->card_expiredate_basemonth, reader->card_expiredate_baseyear); + if(reader->ndsversion == NDS12) + { + rdr_log(reader, "forced to NDS12"); + } + + /* NDS12 Class 48/49/4A/4B cards only need a very basic initialisation + NDS12 Class 48/49/4A/4B cards do not respond to ins7416 + nor do they return list of valid command therefore do not even try + NDS12 Class 48/49/4A/4B cards need to be told the length as (48, ins, 00, 80, 01) + does not return the length */ + + static const uint8_t ins4852[5] = { 0x48, 0x52, 0x00, 0x00, 0x14 }; + if(!write_cmd_vg(ins4852, NULL) || !status_ok(cta_res + cta_lr - 2)) + { + rdr_log(reader, "class48 ins52: failed"); + return ERROR; + } + + uint8_t boxID[4]; + + /* the boxid is specified in the config */ + if(reader->boxid > 0) + { + int32_t i; + for(i = 0; i < 4; i++) + { + boxID[i] = (reader->boxid >> (8 * (3 - i))) % 0x100; + } + } + else + { + int32_t boxidOK = 0; + + // Try to get the boxid from the card, even if BoxID specified in the config file + uint8_t ins36[5] = { 0x48, 0x36, 0x00, 0x00, 0x53 }; + + // get the length of ins36 + static const uint8_t ins38[5] = { 0x48, 0x38, 0x80, 0x00, 0x02 }; + if(!write_cmd_vg(ins38,NULL) || !status_ok(cta_res + cta_lr - 2)) + { + rdr_log(reader, "class48 ins38: failed"); + return ERROR; + } + else + { + ins36[3] = cta_res[0]; + ins36[4] = cta_res[1]; + } + + + if(!write_cmd_vg(ins36, NULL) || !status_ok(cta_res + cta_lr - 2)) + { + rdr_log(reader, "class48 ins36: failed"); + return ERROR; + } + + if(cta_res[2] > 0x0F) + { + rdr_log(reader, "class48 ins36: encrypted - therefore not an NDS12 card"); + } + else + { + // skipping the initial fixed fields: encr/rev++ (4) + int32_t i = 4; + int32_t gotUA = 0; + while(i < (cta_lr - 2)) + { + if (!gotUA && cta_res[i] < 0xF0) + { // then we guess that the next 4 bytes is the UA + gotUA = 1; + i += 4; + } + else + { + switch (cta_res[i]) + { // object length vary depending on type + case 0x00: // padding + { + i += 1; + break; + } + case 0xEF: // card status + { + i += 3; + break; + } + case 0xD1: + { + i += 4; + break; + } + case 0xDF: // next server contact + { + i += 5; + break; + } + case 0xF3: // boxID + { + memcpy(&boxID, &cta_res[i + 1], sizeof(boxID)); + boxidOK = 1; + i += 5; + break; + } + case 0xF6: + { + i += 6; + break; + } + case 0xFC: // No idea NDS1/NDS12 + { + i += 14; + break; + } + case 0x01: // date & time + { + i += 7; + break; + } + case 0xFA: + { + i += 9; + break; + } + case 0x5E: + case 0x67: // signature + case 0xDE: + case 0xE2: + case 0xE9: // tier dates + case 0xF8: // Old PPV Event Record + case 0xFD: + { + i += cta_res[i + 1] + 2; // skip length + 2 bytes (type and length) + break; + } + default: // default to assume a length byte + { + rdr_log(reader, "class48 ins36: returned unknown type=0x%02X - parsing may fail", cta_res[i]); + i += cta_res[i + 1] + 2; + } + } //switch + }//else + }//while + }//else + + if(!boxidOK) + { + rdr_log(reader, "no boxID available"); + return ERROR; + } + } + + // Send BoxID + static const uint8_t ins484C[5] = { 0x48, 0x4C, 0x00, 0x00, 0x09 }; + uint8_t payload4C[9] = { 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02 }; + memcpy(payload4C, boxID, 4); + if(!write_cmd_vg(ins484C, payload4C) || !status_ok(cta_res + cta_lr - 2)) + { + rdr_log(reader, "class48 ins4C: sending boxid failed"); + return ERROR; + } + + static const uint8_t ins4858[5] = { 0x48, 0x58, 0x00, 0x00, 0x35 }; + if(!write_cmd_vg(ins4858, NULL) || !status_ok(cta_res + cta_lr - 2)) + { + rdr_log(reader, "class48 ins58: failed"); + return ERROR; + } + + memset(reader->hexserial, 0, 8); + memcpy(reader->hexserial + 2, cta_res + 4, 4); + memcpy(reader->sa, cta_res + 4, 3); + reader->caid = cta_res[2] * 0x100 + cta_res[3]; + + /* we have one provider, 0x0000 */ + reader->nprov = 1; + memset(reader->prid, 0x00, sizeof(reader->prid)); + + static const uint8_t insBE[5] = { 0x4B, 0xBE, 0x00, 0x00, 0x12 }; + if(!write_cmd_vg(insBE, NULL) || !status_ok(cta_res + cta_lr - 2)) + { + rdr_log(reader, "class4B insBE: failed"); + return ERROR; + } + + static const uint8_t ins4952[5] = { 0x49, 0x52, 0x00, 0x00, 0x14 }; + if(!write_cmd_vg(ins4952, NULL) || !status_ok(cta_res + cta_lr - 2)) + { + rdr_log(reader, "class49 ins52: failed"); + return ERROR; + } + + static const uint8_t ins4958[5] = { 0x49, 0x58, 0x00, 0x00, 0x35 }; + if(!write_cmd_vg(ins4958, NULL) || !status_ok(cta_res + cta_lr - 2)) + { + rdr_log(reader, "class49 ins58: failed"); + return ERROR; + } + + // Send BoxID class 49 + static const uint8_t ins494C[5] = { 0x49, 0x4C, 0x00, 0x00, 0x09 }; + if(!write_cmd_vg(ins494C, payload4C) || !status_ok(cta_res + cta_lr - 2)) + { + rdr_log(reader, "class49 ins4C: sending boxid failed"); + return ERROR; + } + + static const uint8_t ins0C[5] = { 0x49, 0x0C, 0x00, 0x00, 0x0A }; + if(!write_cmd_vg(ins0C, NULL) || !status_ok(cta_res + cta_lr - 2)) + { + rdr_log(reader, "class49 ins0C: failed"); + return ERROR; + } + + rdr_log_sensitive(reader, "type: VideoGuard, caid: %04X, serial: {%02X%02X%02X%02X}, BoxID: {%02X%02X%02X%02X}", + reader->caid, reader->hexserial[2], reader->hexserial[3], reader->hexserial[4], + reader->hexserial[5], boxID[0], boxID[1], boxID[2], boxID[3]); + rdr_log(reader, "ready for requests"); + + return OK; +} + +static int32_t videoguard12_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + uint8_t cta_res[CTA_RES_LEN]; + uint8_t ins40[5] = { 0x49, 0x40, 0x40, 0x80, 0xFF }; + static const uint8_t ins54[5] = { 0x4B, 0x54, 0x00, 0x00, 0x17 }; + int32_t posECMpart2 = er->ecm[6] + 7; + int32_t lenECMpart2 = er->ecm[posECMpart2]; + uint8_t tbuff[264]; + uint8_t rbuff[264]; + + memcpy(&tbuff[0], &(er->ecm[posECMpart2 + 1]), lenECMpart2); + ins40[4] = lenECMpart2; + + int32_t l; + l = vg12_do_cmd(reader, ins40, tbuff, NULL, cta_res); + if(l > 0 && status_ok(cta_res)) + { + l = vg12_do_cmd(reader, ins54, NULL, rbuff, cta_res); + if(l > 0 && status_ok(cta_res + l)) + { + if(!cw_is_valid(rbuff + 5)) // sky cards report 90 00 = ok but send cw = 00 when channel not subscribed + { + rdr_log(reader, "class4B ins54 status 90 00 but cw=00 -> channel not subscribed"); + return ERROR; + } + + if(er->ecm[0] & 1) + { + memset(ea->cw + 0, 0, 8); + memcpy(ea->cw + 8, rbuff + 5, 8); + } + else + { + memcpy(ea->cw + 0, rbuff + 5, 8); + memset(ea->cw + 8, 0, 8); + } + return OK; + } + } + rdr_log(reader, "class4B ins54 (%d) status not ok %02x %02x", l, cta_res[0], cta_res[1]); + return ERROR; +} + +static int32_t videoguard12_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + return videoguard_do_emm(reader, ep, 0x49, read_tiers, vg12_do_cmd); +} + +static int32_t videoguard12_do_rawcmd(struct s_reader *reader, CMD_PACKET *cp) +{ + return videoguard_do_rawcmd(reader, cp); +} + +static int32_t videoguard12_card_info(struct s_reader *reader) +{ + /* info is displayed in init, or when processing info */ + rdr_log(reader, "card detected"); + rdr_log(reader, "type: VideoGuard12 Card"); + read_tiers(reader); + return OK; +} + +const struct s_cardsystem reader_videoguard12 = +{ + .desc = "videoguard12", + .caids = (uint16_t[]){ 0x09, 0 }, + .do_emm = videoguard12_do_emm, + .do_rawcmd = videoguard12_do_rawcmd, + .do_ecm = videoguard12_do_ecm, + .card_info = videoguard12_card_info, + .card_init = videoguard12_card_init, + .get_emm_type = videoguard_get_emm_type, + .get_emm_filter = videoguard_get_emm_filter, +}; + +#endif diff --git a/reader-videoguard2.c b/reader-videoguard2.c new file mode 100644 index 0000000..b0bb42b --- /dev/null +++ b/reader-videoguard2.c @@ -0,0 +1,1620 @@ +#include "globals.h" +#ifdef READER_VIDEOGUARD +#include "cscrypt/md5.h" +#include "cscrypt/des.h" +#include "oscam-work.h" +#include "reader-common.h" +#include "reader-videoguard-common.h" + + +static void do_post_dw_hash(struct s_reader *reader, uint8_t *cw, const uint8_t *ecm_header_data) +{ + int32_t i, ecmi, ecm_header_count; + uint8_t buffer[0x85]; // original 0x80 but with 0x7D mask applied +8 bytes cw it was still too small + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + + static const uint16_t Hash3[] = { 0x0123, 0x4567, 0x89AB, 0xCDEF, 0xF861, 0xCB52 }; + static const uint8_t Hash4[] = { 0x0B, 0x04, 0x07, 0x08, 0x05, 0x09, 0x0B, 0x0A, + 0x07, 0x02, 0x0A, 0x05, 0x04, 0x08, 0x0D, 0x0F }; + + static const uint16_t NdTabB001[0x15][0x20] = + { + { + 0xEAF1, 0x0237, 0x29D0, 0xBAD2, 0xE9D3, 0x8BAE, 0x2D6D, 0xCD1B, + 0x538D, 0xDE6B, 0xA634, 0xF81A, 0x18B5, 0x5087, 0x14EA, 0x672E, + 0xF0FC, 0x055E, 0x62E5, 0xB78F, 0x5D09, 0x0003, 0xE4E8, 0x2DCE, + 0x6BE0, 0xAC4E, 0xF485, 0x6967, 0xF28C, 0x97A0, 0x01EF, 0x0100 + }, + { + 0xC539, 0xF5B9, 0x9099, 0x013A, 0xD4B9, 0x6AB5, 0xEA67, 0x7EB4, + 0x6C30, 0x4BF0, 0xB810, 0xB0B5, 0xB76D, 0xA751, 0x1AE7, 0x14CA, + 0x4F4F, 0x1586, 0x2608, 0x10B1, 0xE7E1, 0x48BE, 0x7DDD, 0x5ECB, + 0xCFBF, 0x323B, 0x8B31, 0xB131, 0x0F1A, 0x664B, 0x0140, 0x0100 + }, + { + 0x3C7D, 0xBDC4, 0xFEC7, 0x26A6, 0xB0A0, 0x6E55, 0xF710, 0xF9BF, + 0x0023, 0xE81F, 0x41CA, 0xBE32, 0xB461, 0xE92D, 0xF1AF, 0x409F, + 0xFC85, 0xFE5B, 0x7FCE, 0x17F5, 0x01AB, 0x4A46, 0xEB05, 0xA251, + 0xDC6F, 0xF0C0, 0x10F0, 0x1D51, 0xEFAA, 0xE9BF, 0x0100, 0x0100 + }, + { + 0x1819, 0x0CAA, 0x9067, 0x607A, 0x7576, 0x1CBC, 0xE51D, 0xBF77, + 0x7EC6, 0x839E, 0xB695, 0xF096, 0xDC10, 0xCB69, 0x4654, 0x8E68, + 0xD62D, 0x4F1A, 0x4227, 0x92AC, 0x9064, 0x6BD1, 0x1E75, 0x2747, + 0x00DA, 0xA6A6, 0x6CF1, 0xD151, 0xBE56, 0x3E33, 0x0128, 0x0100 + }, + { + 0x4091, 0x09ED, 0xD494, 0x6054, 0x1869, 0x71D5, 0xB572, 0x7BF1, + 0xE925, 0xEE2D, 0xEEDE, 0xA13C, 0x6613, 0x9BAB, 0x122D, 0x7AE4, + 0x5268, 0xE6C9, 0x50CB, 0x79A1, 0xF212, 0xA062, 0x6B48, 0x70B3, + 0xF6B0, 0x06D5, 0xF8AB, 0xECF5, 0x6255, 0xEDD8, 0x79D2, 0x290A + }, + { + 0xD3CF, 0x014E, 0xACB3, 0x8F6B, 0x0F2C, 0xA5D8, 0xE8E0, 0x863D, + 0x80D5, 0x5705, 0x658A, 0x8BC2, 0xEE46, 0xD3AE, 0x0199, 0x0100, + 0x4A35, 0xABE4, 0xF976, 0x935A, 0xA8A5, 0xBAE9, 0x24D0, 0x71AA, + 0xB3FE, 0x095E, 0xAB06, 0x4CD5, 0x2F0D, 0x1ACB, 0x59F3, 0x4C50 + }, + { + 0xFD27, 0x0F8E, 0x191A, 0xEEE7, 0x2F49, 0x3A05, 0x3267, 0x4F88, + 0x38AE, 0xFCE9, 0x9476, 0x18C6, 0xF961, 0x4EF0, 0x39D0, 0x42E6, + 0xB747, 0xE625, 0xB68E, 0x5100, 0xF92A, 0x86FE, 0xE79B, 0xEE91, + 0x21D5, 0x4C3C, 0x683D, 0x5AD1, 0x1B49, 0xF407, 0x0194, 0x0100 + }, + { + 0x4BF9, 0xDC0D, 0x9478, 0x5174, 0xCB4A, 0x8A89, 0x4D6A, 0xFED8, + 0xF123, 0xA8CD, 0xEEE7, 0xA6D1, 0xB763, 0xF5E2, 0xE085, 0x01EF, + 0xE466, 0x9FA3, 0x2F68, 0x2190, 0x423F, 0x287F, 0x7F3F, 0x09F6, + 0x2111, 0xA963, 0xD0BB, 0x674A, 0xBA72, 0x45F9, 0xF186, 0xB8F5 + }, + { + 0x0010, 0xD1B9, 0xB164, 0x9E87, 0x1F49, 0x6950, 0x2DBF, 0x38D3, + 0x2EB0, 0x3E8E, 0x91E6, 0xF688, 0x7E41, 0x566E, 0x01B0, 0x0100, + 0x24A1, 0x73D8, 0xA0C3, 0xF71B, 0xA0A5, 0x2A06, 0xBA46, 0xFEC3, + 0xDD4C, 0x52CC, 0xF9BC, 0x3B7E, 0x3812, 0x0666, 0xB74B, 0x40F8 + }, + { + 0x28F2, 0x7C81, 0xFC92, 0x6FBD, 0x53D6, 0x72A3, 0xBBDF, 0xB6FC, + 0x9CE5, 0x2331, 0xD4F6, 0xC5BB, 0xE8BB, 0x6676, 0x02D9, 0x2F0E, + 0xD009, 0xD136, 0xCD09, 0x7551, 0x1826, 0x9D9B, 0x63EA, 0xFC63, + 0x68CD, 0x3672, 0xCB95, 0xD28E, 0xF1CD, 0x20CA, 0x014C, 0x0100 + }, + { + 0xE539, 0x55B7, 0x989D, 0x21C4, 0x463A, 0xE68F, 0xF8B5, 0xE5C5, + 0x662B, 0x35BF, 0x3C50, 0x0131, 0xF4BF, 0x38B2, 0x41BC, 0xB829, + 0x02B7, 0x6B8F, 0xA25C, 0xAFD2, 0xD84A, 0x2243, 0x53EB, 0xC6C9, + 0x2E14, 0x181F, 0x8F96, 0xDF0E, 0x0D4C, 0x30F6, 0xFFE1, 0x9DDA + }, + { + 0x30B6, 0x777E, 0xDA3D, 0xAF77, 0x205E, 0xC90B, 0x856B, 0xB451, + 0x3BCC, 0x76C2, 0x8ACF, 0xDCB1, 0xA5E5, 0xDD64, 0x0197, 0x0100, + 0xE751, 0xB661, 0x0404, 0xDB4A, 0xE9DD, 0xA400, 0xAF26, 0x3F5E, + 0x904B, 0xA924, 0x09E0, 0xE72B, 0x825B, 0x2C50, 0x6FD0, 0x0D52 + }, + { + 0x2730, 0xC2BA, 0x9E44, 0x5815, 0xFC47, 0xB21D, 0x67B8, 0xF8B9, + 0x047D, 0xB0AF, 0x9F14, 0x741B, 0x4668, 0xBE54, 0xDE16, 0xDB14, + 0x7CB7, 0xF2B8, 0x0683, 0x762C, 0x09A0, 0x9507, 0x7F92, 0x022C, + 0xBA6A, 0x7D52, 0x0AF4, 0x1BC3, 0xB46A, 0xC4FD, 0x01C2, 0x0100 + }, + { + 0x7611, 0x66F3, 0xEE87, 0xEDD3, 0xC559, 0xEFD4, 0xDC59, 0xF86B, + 0x6D1C, 0x1C85, 0x9BB1, 0x3373, 0x763F, 0x4EBE, 0x1BF3, 0x99B5, + 0xD721, 0x978F, 0xCF5C, 0xAC51, 0x0984, 0x7462, 0x8F0C, 0x2817, + 0x4AD9, 0xFD41, 0x6678, 0x7C85, 0xD330, 0xC9F8, 0x1D9A, 0xC622 + }, + { + 0x5AE4, 0xE16A, 0x60F6, 0xFD45, 0x668C, 0x29D6, 0x0285, 0x6B92, + 0x92C2, 0x21DE, 0x45E0, 0xEF3D, 0x8B0D, 0x02CD, 0x0198, 0x0100, + 0x9E6D, 0x4D38, 0xDEF9, 0xE6F2, 0xF72E, 0xB313, 0x14F2, 0x390A, + 0x2D67, 0xC71E, 0xCB69, 0x7F66, 0xD3CF, 0x7F8A, 0x81D9, 0x9DDE + }, + { + 0x85E3, 0x8F29, 0x36EB, 0xC968, 0x3696, 0x59F6, 0x7832, 0xA78B, + 0xA1D8, 0xF5CF, 0xAB64, 0x646D, 0x7A2A, 0xBAF8, 0xAA87, 0x41C7, + 0x5120, 0xDE78, 0x738D, 0xDC1A, 0x268D, 0x5DF8, 0xED69, 0x1C8A, + 0xBC85, 0x3DCD, 0xAE30, 0x0F8D, 0xEC89, 0x3ABD, 0x0166, 0x0100 + }, + { + 0xB8BD, 0x643B, 0x748E, 0xBD63, 0xEC6F, 0xE23A, 0x9493, 0xDD76, + 0x0A62, 0x774F, 0xCD68, 0xA67A, 0x9A23, 0xC8A8, 0xBDE5, 0x9D1B, + 0x2B86, 0x8B36, 0x5428, 0x1DFB, 0xCD1D, 0x0713, 0x29C2, 0x8E8E, + 0x5207, 0xA13F, 0x6005, 0x4F5E, 0x52E0, 0xE7C8, 0x6D1C, 0x3E34 + }, + { + 0x581D, 0x2BFA, 0x5E1D, 0xA891, 0x1069, 0x1DA4, 0x39A0, 0xBE45, + 0x5B9A, 0x7333, 0x6F3E, 0x8637, 0xA550, 0xC9E9, 0x5C6C, 0x42BA, + 0xA712, 0xC3EA, 0x3808, 0x0910, 0xAA4D, 0x5B25, 0xABCD, 0xE680, + 0x96AD, 0x2CEC, 0x8EBB, 0xA47D, 0x1690, 0xE8FB, 0x01C8, 0x0100 + }, + { + 0x73B9, 0x82BC, 0x9EBC, 0xB130, 0x0DA5, 0x8617, 0x9F7B, 0x9766, + 0x205D, 0x752D, 0xB05C, 0x2A17, 0xA75C, 0x18EF, 0x8339, 0xFD34, + 0x8DA2, 0x7970, 0xD0B4, 0x70F1, 0x3765, 0x7380, 0x7CAF, 0x570E, + 0x6440, 0xBC44, 0x0743, 0x2D02, 0x0419, 0xA240, 0x2113, 0x1AD4 + }, + { + 0x1EB5, 0xBBFF, 0x39B1, 0x3209, 0x705F, 0x15F4, 0xD7AD, 0x340B, + 0xC2A6, 0x25CA, 0xF412, 0x9570, 0x0F4F, 0xE4D5, 0x1614, 0xE464, + 0x911A, 0x0F0E, 0x07DA, 0xA929, 0x2379, 0xD988, 0x0AA6, 0x3B57, + 0xBF63, 0x71FB, 0x72D5, 0x26CE, 0xB0AF, 0xCF45, 0x011B, 0x0100 + }, + { + 0x9999, 0x98FE, 0xA108, 0x6588, 0xF90B, 0x4554, 0xFF38, 0x4642, + 0x8F5F, 0x6CC3, 0x4E8E, 0xFF7E, 0x64C2, 0x50CA, 0x0E7F, 0xAD7D, + 0x6AAB, 0x33C1, 0xE1F4, 0x6165, 0x7894, 0x83B9, 0x0A0C, 0x38AF, + 0x5803, 0x18C0, 0xFA36, 0x592C, 0x4548, 0xABB8, 0x1527, 0xAEE9 + } + }; + + //ecm_header_data = 01 03 b0 01 01 + if(!cw_is_valid(cw)) // if cw is all zero, keep it that way + { + return; + } + + ecm_header_count = ecm_header_data[0]; + for(i = 0, ecmi = 1; i < ecm_header_count; i++) + { + if(ecm_header_data[ecmi + 1] != 0xb0) + { + ecmi += ecm_header_data[ecmi] + 1; + } + else + { + switch(ecm_header_data[ecmi + 2]) + { + case 1: // b0 01 + { + uint16_t hk[8], r, j, m = 0; + for(r = 0; r < 6; r++) + { + hk[2 + r] = Hash3[r]; + } + + for(r = 0; r < 2; r++) + { + for(j = 0; j < 0x48; j += 2) + { + if(r) + { + hk[0] = ((hk[3] & hk[5]) | ((~hk[5]) & hk[4])); + } + else + { + hk[0] = ((hk[3] & hk[4]) | ((~hk[3]) & hk[5])); + } + + if(j < 8) + { + hk[0] = (hk[0] + ((cw[j + 1] << 8) | cw[j])); + } + + if(j == 8) + { + hk[0] = (hk[0] + 0x80); + } + + hk[0] = (hk[0] + hk[2] + (0xFF & NdTabB001[ecm_header_data[ecmi + 3]][m >> 1] >> ((m & 1) << 3))); + hk[1] = hk[2]; + hk[2] = hk[3]; + hk[3] = hk[4]; + hk[4] = hk[5]; + hk[5] = hk[6]; + hk[6] = hk[7]; + hk[7] = hk[2] + (((hk[0] << Hash4[m & 0xF]) | (hk[0] >> (0x10 - Hash4[m & 0xF])))); + m = (m + 1) & 0x3F; + } + } + + for(r = 0; r < 6; r++) + { + hk[2 + r] += Hash3[r]; + } + + for(r = 0; r < 7; r++) + { + cw[r] = hk[2 + (r >> 1)] >> ((r & 1) << 3); + } + + cw[3] = (cw[0] + cw[1] + cw[2]) & 0xFF; + cw[7] = (cw[4] + cw[5] + cw[6]) & 0xFF; + rdr_log_dump_dbg(reader, D_READER, cw, 8, "Postprocessed Case 1 DW:"); + break; + } + + case 3: + { + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, cw, 8); + memcpy(buffer + 8, &ecm_header_data[ecmi + 3], ecm_header_data[ecmi] & 0x7D); + MD5(buffer, 8 + (ecm_header_data[ecmi] & 0x7D), md5tmp); + memcpy(cw, md5tmp, 8); + rdr_log_dump_dbg(reader, D_READER, cw, 8, "Postprocessed Case 3 DW:"); + break; + } + + case 2: + { + // Method 2 left out + //memcpy(DW_OUTPUT, DW_INPUT, 8); + break; + } + } + } + } +} + +static void vg2_read_tiers(struct s_reader *reader) +{ + uint8_t cta_res[CTA_RES_LEN]; + + if(reader->readtiers == 1) + { + uint8_t ins707f[5] = { 0xD1, 0x70, 0x00, 0x7f, 0x02 }; + if(do_cmd(reader, ins707f, NULL, NULL, cta_res) < 0) + { + rdr_log(reader, "classD1 ins707f: failed to get number of classes supported"); + } + else + { + cs_clear_entitlement(reader); + rdr_log(reader, "------------------------------------------------------------------"); + rdr_log(reader, "|- class -|-- tier --|----- valid to ------|--- package name ----|"); + rdr_log(reader, "+---------+----------+---------------------+---------------------+"); + + if((reader->VgFuse & 5) == 0) + { + rdr_log(reader, "|------- This card is not active, so no package available! ------|"); + } + + uint32_t TierClass, ClassSupported; + ClassSupported = cta_res[1]; + uint8_t ins70[5] = { 0xD1, 0x70, 0x00, 0x00, 0x00 }; + + for(TierClass = 0; TierClass < ClassSupported; TierClass++) + { + ins70[2] = TierClass; + if(do_cmd(reader, ins70, NULL, NULL, cta_res) < 0) + { + rdr_log(reader, "classD1 ins70: failed to get tiers for class %02X", TierClass); + } + else + { + char tiername[83]; + uint32_t word; + uint32_t bitnum; + uint32_t tier_id; + struct tm timeinfo; + memset(&timeinfo, 0, sizeof(struct tm)); + time_t start_t = 0, end_t; + + if(cta_res[1] > 0x24) + { + rev_startdate_calc_tm(&cta_res[38], &timeinfo, reader->card_startdate_basemonth, reader->card_startdate_baseyear); + start_t = mktime(&timeinfo); + } + + rev_expiredate_calc_tm(&cta_res[34], &timeinfo, reader->card_expiredate_basemonth, reader->card_expiredate_baseyear); + end_t = mktime(&timeinfo); + + for(word = 0; word < 32; word += 2) + { + for (bitnum = 0; bitnum < 8; bitnum++) + { + if((cta_res[word + 2] >> bitnum) & 1) + { + tier_id = ((TierClass << 8) + (word << 3) + bitnum); + cs_add_entitlement(reader, reader->caid, b2ll(4, reader->prid[0]), tier_id, TierClass, start_t, end_t, 4, 1); + rdr_log(reader, "|-- %02x ---|-- %04x --| %04d/%02d/%02d-%02d:%02d:%02d | %s", + TierClass, + tier_id, + timeinfo.tm_year + 1900, + timeinfo.tm_mon + 1, + timeinfo.tm_mday, + timeinfo.tm_hour, + timeinfo.tm_min, + timeinfo.tm_sec, + get_tiername(tier_id, reader->caid, tiername)); + } + + if((cta_res[word + 1 + 2] >> bitnum) & 1) + { + tier_id = ((TierClass << 8) + (word << 3) + bitnum + 8); + cs_add_entitlement(reader, reader->caid, b2ll(4, reader->prid[0]), tier_id, TierClass, start_t, end_t, 4, 1); + rdr_log(reader, "|-- %02x ---|-- %04x --| %04d/%02d/%02d-%02d:%02d:%02d | %s", + TierClass, + tier_id, + timeinfo.tm_year + 1900, + timeinfo.tm_mon + 1, + timeinfo.tm_mday, + timeinfo.tm_hour, + timeinfo.tm_min, + timeinfo.tm_sec, + get_tiername(tier_id, reader->caid, tiername)); + } + } + } + } + } + } + rdr_log(reader, "------------------------------------------------------from-ins70--"); + } + else if(reader->readtiers == 2) + { + int32_t l; + // ins2a is not needed and causes an error on some cards eg Sky Italy 09CD + // check if ins2a is in command table before running it + + static const uint8_t ins2a[5] = { 0xD1, 0x2a, 0x00, 0x00, 0x00 }; + if(cmd_exists(reader, ins2a)) + { + l = do_cmd(reader, ins2a, NULL, NULL, cta_res); + if(l < 0 || !status_ok(cta_res + l)) + { + rdr_log(reader, "classD1 ins2a: failed"); + return; + } + } + + static const uint8_t ins76007f[5] = { 0xD1, 0x76, 0x00, 0x7f, 0x02 }; + l = do_cmd(reader, ins76007f, NULL, NULL, cta_res); + if(l < 0 || !status_ok(cta_res + 2)) + { + rdr_log(reader, "classD1 ins76007f: failed"); + return; + } + int32_t num = cta_res[1]; + + int32_t i; + uint8_t ins76[5] = { 0xD1, 0x76, 0x00, 0x00, 0x00 }; + + // some cards start real tiers info in middle of tier info + // and have blank tiers between old tiers and real tiers eg 09AC + int32_t starttier = 0; + if(reader->caid == 0x09AC || reader->caid == 0x0910 || reader->caid == 0x0913 || reader->caid == 0x09C1) + { + starttier = 50; + } + bool stopemptytier = 1; + if(!starttier) + { + stopemptytier = 0; + } + + // check to see if specified start tier is blank and if blank, start at 0 and ignore blank tiers + ins76[2] = starttier; + l = do_cmd(reader, ins76, NULL, NULL, cta_res); + if(l < 0 || !status_ok(cta_res + l)) + { + return; + } + + if(cta_res[2] == 0 && cta_res[3] == 0) + { + stopemptytier = 0; + starttier = 0; + } + + cs_clear_entitlement(reader); // reset the entitlements + rdr_log(reader, "------------------------------------------------------------------"); + rdr_log(reader, "|- class -|-- tier --|----- valid to ------|--- package name ----|"); + rdr_log(reader, "+---------+----------+---------------------+---------------------+"); + if((reader->VgFuse&5) == 0) + { + rdr_log(reader, "|------- This card is not active, so no package available! ------|"); + } + + for(i = starttier; i < num; i++) + { + ins76[2] = i; + l = do_cmd(reader, ins76, NULL, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res + l)) + { + return; + } + + if(cta_res[2] == 0 && cta_res[3] == 0 && stopemptytier) + { + return; + } + + if(cta_res[2] != 0 || cta_res[3] != 0) + { + char tiername[83]; + uint16_t tier_id = (cta_res[2] << 8) | cta_res[3]; + // add entitlements to list + struct tm timeinfo; + memset(&timeinfo, 0, sizeof(struct tm)); + rev_expiredate_calc_tm(&cta_res[4], &timeinfo, reader->card_expiredate_basemonth, reader->card_expiredate_baseyear); + cs_add_entitlement(reader, reader->caid, b2ll(4, reader->prid[0]), tier_id, 0, 0, mktime(&timeinfo), 4, 1); + + if(!stopemptytier) + { + rdr_log_dbg(reader, D_READER, "tier: %04x, tier-number: 0x%02x", tier_id, i); + } + rdr_log(reader, "|-- %02x ---|-- %04x --| %04d/%02d/%02d-%02d:%02d:%02d | %s", + cta_res[2], + tier_id, + timeinfo.tm_year + 1900, + timeinfo.tm_mon + 1, + timeinfo.tm_mday, + timeinfo.tm_hour, + timeinfo.tm_min, + timeinfo.tm_sec, + get_tiername(tier_id, reader->caid, tiername)); + } + } + rdr_log(reader, "------------------------------------------------------from-ins76--"); + } + else if(reader->readtiers == 0) + { + rdr_log(reader, "------------------------------------------------------------------"); + rdr_log(reader, "|--- The reading of the tiers is disabled by the configuration --|"); + rdr_log(reader, "------------------------------------------------------------------"); + } +} + +void videoguard2_poll_status(struct s_reader *reader) +{ + const time_t poll_interval = 12; // less is better + time_t now = time(0); + int32_t i; + + if(now >= reader->last_poll + poll_interval) + { + static const uint8_t ins5C[5] = { 0xD1, 0x5C, 0x00, 0x00, 0x04 }; + uint8_t cta_res[CTA_RES_LEN]; + int32_t l; + l = do_cmd(reader, ins5C, NULL, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res + l)) + { + rdr_log(reader, "classD1 ins5C: failed"); + } + else + { + switch (cta_res[1]) + { + case 0x14: // loc_43C250 + { + static const uint8_t ins4Ca[5] = { 0xD1, 0x4C, 0x00, 0x00, 0x00 }; + l = do_cmd(reader, ins4Ca, reader->payload4C, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res)) + { + rdr_log(reader, "classD1 ins4Ca: failed"); + } + + if(reader->ins7E[0x1A]) + { + static const uint8_t ins7E[5] = { 0xD1, 0x7E, 0x10, 0x00, 0x1A }; + l = do_cmd(reader, ins7E, reader->ins7E, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res)) + { + rdr_log(reader, "classD1 ins7E: failed"); + } + } + + if(reader->ins2e06[4]) + { + static const uint8_t ins2e06[5] = { 0xD1, 0x2E, 0x06, 0x00, 0x04 }; + l = do_cmd(reader, ins2e06, reader->ins2e06, NULL, cta_res); + if(l < 0 || !status_ok(cta_res)) + { + rdr_log(reader, "classD1 ins2E: failed"); + } + } + + static const uint8_t ins58a[5] = { 0xD1, 0x58, 0x00, 0x00, 0x00 }; + if((do_cmd(reader, ins58a, NULL, NULL, cta_res) < 0)) + { + rdr_log(reader, "classD1 ins58: failed"); + } + + reader->VgFuse = cta_res[2]; + static const uint8_t ins7403a[5] = { 0xD1, 0x74, 0x03, 0x00, 0x00 }; + + if((do_cmd(reader, ins7403a, NULL, NULL, cta_res) < 0)) + { + rdr_log(reader, "classD1 ins7403a: failed"); + } + else + { + if(((cta_res[2] >> 5) & 1)) + { + static const uint8_t ins7423[5] = { 0xD3, 0x74, 0x23, 0x00, 0x00 }; + if(do_cmd(reader, ins7423, NULL, NULL, cta_res) < 0) + { + rdr_log(reader, "classD1 ins7423: failed"); + } + } + } + break; + } + + case 0xB: // .text:000000000043C050 + { + uint8_t ins5E[5] = { 0xD1,0x5E,0x00,0x00,0x00 }; + ins5E[2] = cta_res[2]; + ins5E[3] = cta_res[1]; + ins5E[4] = cta_res[3]; + l = do_cmd(reader, ins5E, NULL, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res + l)) + { + rdr_log(reader, "Ins5E: failed"); + } + + uint8_t ins78[5] = { 0xD1, 0x78, 0x00, 0x00, 0x18 }; + ins78[2] = cta_res[0]; + l = do_cmd(reader, ins78, NULL, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res + l)) + { + rdr_log(reader, "classD1 ins78: failed"); + } + + uint8_t ins32[5] = { 0xD1, 0x32, 0x00, 0x00, 0x01 }; + const uint8_t payload32[1] = { 0x25 }; + l = do_cmd(reader, ins32, payload32, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res + l)) + { + rdr_log(reader, "classD1 ins32: failed"); + } + break; + } + + case 0x0C: // loc_43C13F + { + uint8_t ins5E[5] = { 0xD1,0x5E,0x00,0x00,0x00 }; + ins5E[2] = cta_res[2]; + ins5E[3] = cta_res[1]; + ins5E[4] = cta_res[3]; + l = do_cmd(reader, ins5E, NULL, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res + l)) + { + rdr_log(reader, "Ins5E: failed"); + } + else + { + uint8_t ins36[5] = { 0xD1, 0x36, 0x00, 0x00, 0x00 }; + ins36[4] = cta_res[1]; + + for (i = 0; i <= cta_res[0]; i++) + { + ins36[3] = i; + l = do_cmd(reader, ins36, NULL, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res + l)) + { + rdr_log(reader, "Ins36: failed"); + } + } + } + break; + } + + case 0x10: // loc_43C203 + { + uint8_t ins7411[5] = { 0xD3,0x74,0x11,0x00,0x00 }; + l = read_cmd_len(reader, ins7411); + ins7411[4] = l + 0x10; + l = do_cmd(reader, ins7411, NULL, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res)) + { + rdr_log(reader, "classD3 ins7411: failed"); + } + break; + } + + case 0x00: // normal state + { + break; + } + + default: + { + rdr_log(reader, "unknown ins5C state: %02X %02X %02X %02X", + cta_res[0], cta_res[1], cta_res[2], cta_res[3]); + break; + } + } + } + reader->last_poll = now; + } +} + +static int32_t videoguard2_card_init(struct s_reader *reader, ATR *newatr) +{ + get_hist; + if((hist_size < 7) || (hist[1] != 0xB0) || (hist[4] != 0xFF) || (hist[5] != 0x4A) || (hist[6] != 0x50)) + { + return ERROR; + } + + get_atr; + def_resp; + + if(!cs_malloc(&reader->csystem_data, sizeof(struct videoguard_data))) + { + return ERROR; + } + struct videoguard_data *csystem_data = reader->csystem_data; + + if((reader->ndsversion != NDS2) && (reader->ndsversion != NDSAUTO)) + { + /* known ATR and not NDS2 + or known NDS2 ATR and forced to another NDS version */ + return ERROR; + } + + rdr_log_dbg(reader, D_READER, "type: VideoGuard2 Card, startdate baseyear: %i, startdate basemonth: %i, expiredate basemonth: %i, expiredate baseyear: %i", reader->card_startdate_basemonth, reader->card_startdate_baseyear, reader->card_expiredate_basemonth, reader->card_expiredate_baseyear); + if(reader->ndsversion == NDS2) + { + rdr_log_dbg(reader, D_READER, "forced to NDS2"); + } + + // a non videoguard2/NDS2 card will fail on read_cmd_len(ins7401) + // this way unknown videoguard2/NDS2 cards will also pass this check + + uint8_t ins7401[5] = { 0xD0, 0x74, 0x01, 0x00, 0x00 }; + int32_t l; + if((l = read_cmd_len(reader, ins7401)) < 0) // not a videoguard2/NDS card or communication error + { + return ERROR; + } + + ins7401[4] = l; + if(!write_cmd_vg(ins7401, NULL) || !status_ok(cta_res + l)) + { + rdr_log(reader, "classD0 ins7401: failed - cmd list not read"); + return ERROR; + } + + memorize_cmd_table(reader, cta_res, l); + + uint8_t buff[256]; + static const uint8_t ins02[5] = { 0xD0, 0x02, 0x00, 0x00, 0x08 }; + + // D0 02 command is not always present in command table but should be supported + // on most cards so do not use do_cmd() + if(!write_cmd_vg(ins02, NULL) || !status_ok(cta_res + 8)) + { + rdr_log(reader, "Unable to get NDS ROM version."); + } + else + { + int i; + for(i = 0; i < 8; i++) + { + if(cta_res[i] <= 0x09) + { + cta_res[i] = cta_res[i] + 0x30; + } + else if(!isalnum(cta_res[i])) + { + cta_res[i] = '*'; + } + } + memset(reader->rom, 0, sizeof(reader->rom)); + memcpy(reader->rom, cta_res, 4); + reader->rom[4] = '-'; + memcpy(reader->rom + 5, cta_res + 4, 4); + + rdr_log(reader, "Card type: %c%c%c%c", reader->rom[0], reader->rom[1], reader->rom[2], reader->rom[3]); + rdr_log(reader, "Rom version: %c%c%c%c", reader->rom[5], reader->rom[6], reader->rom[7], reader->rom[8]); + } + + // get Vg credit on card + uint8_t ins7404[5] = { 0xD0, 0x74, 0x04, 0x00, 0x00 }; + if((l = read_cmd_len(reader, ins7404)) > 0) // get command len for ins7404 + { + ins7404[4] = l; + if(!write_cmd_vg(ins7404, NULL) || !status_ok(cta_res + l)) + { + rdr_log(reader, "classD0 ins7404: failed"); + } + else + { + if(cta_res[0] == 0x15) + { + reader->VgCredit = ((cta_res[8] << 8) + cta_res[9]) / 100; + rdr_log(reader, "Credit available on card: %i euro", reader->VgCredit); + } + } + } + else // case V13 + { + rdr_log(reader, "Unable to get smartcard credit"); + } + + static const uint8_t ins7416[5] = { 0xD0, 0x74, 0x16, 0x00, 0x00 }; + + if(do_cmd(reader, ins7416, NULL, NULL, cta_res) < 0) + { + rdr_log(reader, "classD0 ins7416: failed"); + return ERROR; + } + uint8_t boxID [4]; + + if(reader->boxid > 0) + { + // the boxid is specified in the config + int32_t i; + for(i = 0; i < 4; i++) + { + boxID[i] = (reader->boxid >> (8 * (3 - i))) % 0x100; + } + } + else + { + uint8_t ins36[5] = { 0xD0, 0x36, 0x00, 0x00, 0x00 }; + static const uint8_t ins5e[5] = { 0xD0, 0x5E, 0x00, 0x0C, 0x02 }; + + // we can try to get the boxid from the card + int32_t boxidOK = 0; + l = read_cmd_len(reader, ins36); + if(l > 0) + { + ins36[4] = l; + } + else if(cmd_exists(reader, ins5e)) + { + if(!write_cmd_vg(ins5e, NULL) || !status_ok(cta_res + 2)) + { + rdr_log(reader, "classD0 ins5e: failed"); + } + else + { + ins36[3] = cta_res[0]; + ins36[4] = cta_res[1]; + } + } + + l = ins36[4]; + if(!write_cmd_vg(ins36, NULL) || !status_ok(cta_res + l)) + { + rdr_log(reader, "classD0 ins36: failed"); + return ERROR; + } + + memcpy(buff, ins36, 5); + memcpy(buff + 5, cta_res, l); + memcpy(buff + 5 + l, cta_res + l, 2); + + if(l < 13) + { + rdr_log(reader, "classD0 ins36: answer too int16"); + } + else if(buff[7] > 0x0F) + { + rdr_log(reader, "classD0 ins36: encrypted - can't parse"); + } + else + { + // skipping the initial fixed fields: cmdecho (4) + length (1) + encr/rev++ (4) + int32_t i = 9; + int32_t gotUA = 0; + while(i < l) + { + if(!gotUA && buff[i] < 0xF0) // then we guess that the next 4 bytes is the UA + { + gotUA = 1; + i += 4; + } + else + { + switch(buff[i]) // object length vary depending on type + { + case 0x00: // padding + i += 1; + break; + + case 0xEF: // card status + i += 3; + break; + + case 0xD1: + i += 4; + break; + + case 0xDF: // next server contact + i += 5; + break; + + case 0xF3: // boxID + memcpy(boxID, buff + i + 1, sizeof(boxID)); + boxidOK = 1; + i += 5; + break; + + case 0xF6: + i += 6; + break; + + case 0x01: // date & time + i += 7; + break; + + case 0xFA: + i += 9; + break; + + case 0x5E: + case 0x67: // signature + case 0xDE: + case 0xE2: + case 0xE9: // tier dates + case 0xF8: // Old PPV Event Record + case 0xFD: + i += buff[i + 1] + 2; // skip length + 2 bytes (type and length) + break; + + default: // default to assume a length byte + rdr_log(reader, "classD0 ins36: returned unknown type=0x%02X - parsing may fail", buff[i]); + i += buff[i + 1] + 2; + } + } + } + } + + if(!boxidOK) + { + rdr_log(reader, "no boxID available"); + return ERROR; + } + } + + uint8_t ins4C[5] = { 0xD0, 0x4C, 0x00, 0x00, 0x09 }; + uint8_t len4c = 0, mode = 0; + uint8_t payload4C[0xF] = { 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + if(cmd_table_get_info(reader, ins4C, &len4c, &mode)) + { + ins4C[4] = len4c; // don't mind if payload is > of ins len, it will be cutted after write_cmd_vg() + if(len4c > 9) + { + rdr_log_dbg(reader, D_READER, "extended ins4C detected"); + } + } + memcpy(payload4C, boxID, 4); + if(!write_cmd_vg(ins4C, payload4C)) + { + rdr_log(reader, "classD0 ins4C: failed - sending boxid failed"); + return ERROR; + } + + if(reader->ins7E11[0x01]) // the position of the ins7E is taken from v13 log + { + uint8_t ins742b[5] = { 0xD0, 0x74, 0x2b, 0x00, 0x00 }; + l = read_cmd_len(reader, ins742b); // get command len for ins742b + + if(l < 2) + { + rdr_log(reader, "No TA1 change for this card is possible by ins7E11"); + } + else + { + ins742b[4] = l; + bool ta1ok = 0; + if(!write_cmd_vg(ins742b, NULL) || !status_ok(cta_res + ins742b[4])) // get supported TA1 bytes + { + rdr_log(reader, "classD0 ins742b: failed"); + return ERROR; + } + else + { + int32_t i; + for(i = 2; i < l; i++) + { + if(cta_res[i] == reader->ins7E11[0x00]) + { + ta1ok = 1; + break; + } + } + } + + if(ta1ok == 0) + { + rdr_log(reader, "The value %02X of ins7E11 is not supported,try one between %02X and %02X", + reader->ins7E11[0x00], cta_res[2], cta_res[ins742b[4] - 1]); + } + else + { + static const uint8_t ins7E11[5] = { 0xD0, 0x7E, 0x11, 0x00, 0x01 }; + reader->ins7e11_fast_reset = 0; + l = do_cmd(reader, ins7E11, reader->ins7E11, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res)) + { + rdr_log(reader, "classD0 ins7E11: failed"); + return ERROR; + } + else + { + uint8_t TA1; + if(ATR_GetInterfaceByte(newatr, 1, ATR_INTERFACE_BYTE_TA, &TA1) == ATR_OK) + { + if(TA1 != reader->ins7E11[0x00]) + { + rdr_log(reader, "classD0 ins7E11: Scheduling card reset for TA1 change from %02X to %02X", TA1, reader->ins7E11[0x00]); + reader->ins7e11_fast_reset = 1; + +#if defined(WITH_COOLAPI) || defined(WITH_COOLAPI2) + if(reader->typ == R_MOUSE || reader->typ == R_SC8in1 || reader->typ == R_SMART || reader->typ == R_INTERNAL) + { +#else + if(reader->typ == R_MOUSE || reader->typ == R_SC8in1 || reader->typ == R_SMART) + { +#endif + add_job(reader->client, ACTION_READER_RESET_FAST, NULL, 0); + } + else + { + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + return OK; // Skip the rest of the init since the card will be reset anyway + } + } + } + } + } + } + + //int16_t int32_t SWIRDstatus = cta_res[1]; + static const uint8_t ins58[5] = { 0xD0, 0x58, 0x00, 0x00, 0x00 }; + l = do_cmd(reader, ins58, NULL, NULL, cta_res); + + if(l < 0) + { + rdr_log(reader, "classD0 ins58: failed"); + return ERROR; + } + + memset(reader->hexserial, 0, 8); + memcpy(reader->hexserial + 2, cta_res + 3, 4); + memcpy(reader->sa, cta_res + 3, 3); + reader->caid = cta_res[24] * 0x100 + cta_res[25]; + reader->VgFuse = cta_res[2]; + rdr_log(reader, "FuseByte: %02X", reader->VgFuse); + memset(reader->VgRegionC, 0, 8); + memcpy(reader->VgRegionC, cta_res + 60, 8); + + rdr_log(reader, "Region Code: %c%c%c%c%c%c%c%c", + reader->VgRegionC[0], reader->VgRegionC[1], + reader->VgRegionC[2], reader->VgRegionC[3], + reader->VgRegionC[4], reader->VgRegionC[5], + reader->VgRegionC[6], reader->VgRegionC[7]); + + memset(reader->VgCountryC, 0, 3); + memcpy(reader->VgCountryC, cta_res + 55, 3); + rdr_log(reader, "Country Code: %c%c%c", reader->VgCountryC[0], reader->VgCountryC[1], reader->VgCountryC[2]); + + // we have one provider, 0x0000 + reader->nprov = 1; + memset(reader->prid, 0x00, sizeof(reader->prid)); + + cCamCryptVG_SetSeed(reader); + + static const uint8_t insB4[5] = { 0xD0, 0xB4, 0x00, 0x00, 0x40 }; + uint16_t tbuff[32]; + cCamCryptVG_GetCamKey(reader, tbuff); + l = do_cmd(reader, insB4, (uint8_t *)tbuff, NULL, cta_res); + if(l < 0 || !status_ok(cta_res)) + { + rdr_log(reader, "classD0 insB4: failed"); + return ERROR; + } + + static const uint8_t insBC[5] = { 0xD0, 0xBC, 0x00, 0x00, 0x00 }; + l = do_cmd(reader, insBC, NULL, NULL, cta_res); + if(l < 0) + { + rdr_log(reader, "classD0 insBC: failed"); + return ERROR; + } + + // Class D1/D3 instructions only work after this point + + static const uint8_t insBE[5] = { 0xD3, 0xBE, 0x00, 0x00, 0x00 }; + l = do_cmd(reader, insBE, NULL, NULL, cta_res); + if(l < 0) + { + rdr_log(reader, "classD3 insBE: failed"); + return ERROR; + } + + static const uint8_t ins58a[5] = { 0xD1, 0x58, 0x00, 0x00, 0x00 }; + l = do_cmd(reader, ins58a, NULL, NULL, cta_res); + if(l < 0) + { + rdr_log(reader, "classD1 ins58: failed"); + return ERROR; + } + + int d37423_ok = 0; + static const uint8_t ins7403[5] = { 0xD1, 0x74, 0x03, 0x00, 0x00 }; // taken from v13 boot log + if(do_cmd(reader, ins7403, NULL, NULL, cta_res) < 0) + { + rdr_log(reader, "classD1 ins7403: failed"); + } + else + { + d37423_ok = (cta_res[2] >> 5) & 1; + } + + // new ins74 present at boot + if(d37423_ok) // from ins7403 answer + { + static const uint8_t ins7423[5] = { 0xD3, 0x74, 0x23, 0x00, 0x00 }; + if(do_cmd(reader, ins7423, NULL, NULL, cta_res) < 0) + { + rdr_log(reader, "classD1 ins7423: failed"); + } + } + + static const uint8_t ins742A[5] = { 0xD0, 0x74, 0x2A, 0x00, 0x00 }; + if(do_cmd(reader, ins742A, NULL, NULL, cta_res) < 0) + { + rdr_log(reader, "classD0 ins742A: failed"); + } + + static const uint8_t ins741B[5] = { 0xD1, 0x74, 0x1B, 0x00, 0x00 }; + if(do_cmd(reader, ins741B, NULL, NULL, cta_res) < 0) + { + rdr_log(reader, "classD1 ins741B: failed"); + } + + static const uint8_t ins4Ca[5] = { 0xD1, 0x4C, 0x00, 0x00, 0x00 }; + + payload4C[4] = 0x83; + + l = do_cmd(reader, ins4Ca, payload4C, NULL, cta_res); + if(l < 0 || !status_ok(cta_res)) + { + rdr_log(reader, "classD1 ins4Ca: failed"); + return ERROR; + } + memcpy(reader->payload4C, payload4C, 0xF); + + if(reader->ins7E[0x1A]) + { + static const uint8_t ins7E[5] = { 0xD1, 0x7E, 0x10, 0x00, 0x1A }; + l = do_cmd(reader, ins7E, reader->ins7E, NULL, cta_res); + if(l < 0 || !status_ok(cta_res)) + { + rdr_log(reader, "classD1 ins7E: failed"); + return ERROR; + } + } + + if(reader->ins42[0x25]) + { + static const uint8_t ins42[5] = { 0xD1, 0x42, 0x00, 0x00, 0x25 }; + l = do_cmd(reader, ins42, reader->ins42, NULL, cta_res); + if(l < 0 || !status_ok(cta_res)) + { + rdr_log(reader, "classD1 ins42: failed"); + return ERROR; + } + } + + // get PIN settings + static const uint8_t ins7411[5] = { 0xD3, 0x74, 0x11, 0x00, 0x00 }; + uint8_t payload2e4[4]; + uint8_t rbuff[264]; + if(do_cmd(reader, ins7411, NULL, rbuff, cta_res) < 0) + { + rdr_log(reader, "classD3 ins7411: unable to get PIN"); + return ERROR; + } + else + { + memset(payload2e4, 0, 4); + memcpy(payload2e4, rbuff + 7, 4); + reader->VgPin = (rbuff[9] << 8) + rbuff[10]; + rdr_log(reader, "Pincode read: %04hu", reader->VgPin); + } + + // get PCB(content rating) settings + static const uint8_t ins74e[5] = { 0xD1, 0x74, 0x0E, 0x00, 0x00 }; + if(do_cmd(reader, ins74e, NULL, NULL, cta_res) < 0) + { + rdr_log(reader, "classD1 ins74e: failed to get PCB settings"); + } + else + { + rdr_log(reader, "PCB settings: %X %X %X %X", cta_res[2], cta_res[3], cta_res[4], cta_res[5]); + } + + // send PIN + static const uint8_t ins2epin[5] = { 0xD1, 0x2E, 0x04, 0x00, 0x04 }; + if(cfg.ulparent) + { + l = do_cmd(reader, ins2epin, payload2e4, NULL, cta_res); + if(l < 0 || !status_ok(cta_res)) + { + rdr_log(reader, "classD1 ins2E: failed"); + rdr_log(reader, "Cannot disable parental control"); + return ERROR; + } + else + { + rdr_log(reader, "Parental control disabled"); + } + } + + // send check control for pin, needed on some cards + // the presence and the value of payloads is provider's dependent*/ + if(reader->ins2e06[4]) + { + static const uint8_t ins2e06[5] = { 0xD1, 0x2E, 0x06, 0x00, 0x04 }; + l = do_cmd(reader, ins2e06, reader->ins2e06, NULL, cta_res); + if(l < 0 || !status_ok(cta_res)) + { + rdr_log(reader, "classD1 ins2E: failed"); + return ERROR; + } + } + + // fix for 09ac cards + uint8_t dimeno_magic[0x10] = { 0xF9, 0xFB, 0xCD, 0x5A, 0x76, 0xB5, 0xC4, 0x5C, 0xC8, 0x2E, 0x1D, 0xE1, 0xCC, 0x5B, 0x6B, 0x02 }; + int32_t a; + for(a = 0; a < 4; a++) + { + dimeno_magic[a] = dimeno_magic[a] ^ boxID[a]; + } + AES_set_decrypt_key(dimeno_magic, 128, &(csystem_data->astrokey)); + + rdr_log(reader, "type: VideoGuard2 Card, caid: %04X", reader->caid); + rdr_log_sensitive(reader, "serial: {%02X%02X%02X%02X}, BoxID: {%02X%02X%02X%02X}, base tiers start date: %i/%i, base tiers expiration date: %i/%i", + reader->hexserial[2], + reader->hexserial[3], + reader->hexserial[4], + reader->hexserial[5], + boxID[0], + boxID[1], + boxID[2], + boxID[3], + reader->card_startdate_basemonth, + reader->card_startdate_baseyear, + reader->card_expiredate_basemonth, + reader->card_expiredate_baseyear); + + rdr_log(reader, "ready for requests"); + + return OK; +} + +static int32_t videoguard2_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea) +{ + uint8_t cta_res[CTA_RES_LEN]; + uint8_t ins40[5] = { 0xD1, 0x40, 0x60, 0x80, 0xFF }; + static const uint8_t ins54[5] = { 0xD3, 0x54, 0x00, 0x00, 0x00}; + int32_t posECMpart2 = er->ecm[6] + 7; + int32_t lenECMpart2 = er->ecm[posECMpart2] + 1; + uint8_t tbuff[264], rbuff[264]; + const uint8_t *EcmIrdHeader = er->ecm + 5; + tbuff[0] = 0; + + memset(ea->cw + 0, 0, 16); // set cw to 0 so client will know it is invalid unless it is overwritten with a valid cw + int32_t chk; + chk = checksum_ok(EcmIrdHeader); + + if((er->ecm[3] != 0) || chk == 0 || (er->ecm[4] != 0 && 4 != er->ecm[2]- er->ecm[4])) + { + rdr_log(reader, "Not a valid ecm"); + return E_CORRUPT; + } + memcpy(tbuff + 1, er->ecm + posECMpart2 + 1, lenECMpart2 - 1); + + int32_t new_len = lenECMpart2; + if(reader->fix_9993 && reader->caid == 0x919 && tbuff[1] == 0x7F) + { + tbuff[1] = 0x47; + tbuff[2] = 0x08; + memmove(tbuff + 11, tbuff + 13, new_len - 11); + new_len -= 2; + } + + ins40[4] = new_len; + int32_t l; + l = do_cmd(reader, ins40, tbuff, NULL, cta_res); + + if(l < 0 || !status_ok(cta_res)) + { + rdr_log(reader, "classD1 ins40: (%d) status not ok %02x %02x", l, cta_res[0], cta_res[1]); + rdr_log(reader, "The card is not answering correctly! Restarting reader for safety"); + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + return ERROR; + } + else + { + l = do_cmd(reader, ins54, NULL, rbuff, cta_res); + if(l < 0 || !status_ok(cta_res + l)) + { + rdr_log(reader, "classD3 ins54: (%d) status not ok %02x %02x", l, cta_res[0], cta_res[1]); + rdr_log(reader, "The card is not answering correctly! Restarting reader for safety"); + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + return ERROR; + } + else + { + struct videoguard_data *csystem_data = reader->csystem_data; + uint8_t *payload = rbuff + 5; + uint8_t buff_0F[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t buff_56[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t buff_55[1] = { 0x00 }; + uint8_t tag, t_len; + uint8_t *t_body; + int32_t payloadLen = rbuff[4]; + int32_t ind = 8 + 6; // +8 (CW1), +2 (cw checksum) + 2 (tier used) +2 (result byte) + + while(ind < payloadLen) + { + tag = payload[ind]; + t_len = payload[ind + 1]; // len of the tag + t_body = payload + ind + 2; // body of the tag + + switch(tag) + { + case 0x0F: // Debug ecm info + if(t_len > 6) + { + t_len = 6; + } + memcpy(buff_0F, t_body, t_len); + break; + + case 0x25: // CW2 tag + memcpy(ea->cw + 8, t_body +1, 8); + break; + + case 0x55: // cw crypt info tag + memcpy(buff_55, t_body, 1 ); + break; + + case 0x56: // tag data for aes + memcpy(buff_56, t_body, 8); + break; + + default: + break; + } + ind += t_len + 2; + } + + if(12 < payloadLen) + { + ea->tier = b2i(2, &payload[10]); + } + + memcpy(reader->VgLastPayload, buff_0F, 6); + + int32_t test_0F = 1; + if(!cw_is_valid(rbuff + 5)) // sky cards report 90 00 = ok but send cw = 00 when something goes wrong :( + { + if(buff_0F[0] & 1) // case 0f_0x 01 xx xx xx xx xx + { + rdr_log(reader, "classD3 ins54: no cw --> Bad/wrong ECM"); + return E_CORRUPT; + } + + if(buff_0F[1] & 1) // case 0f_0x xx 01 xx xx xx xx + { + rdr_log(reader, "classD3 ins54: no cw --> Card appears in error"); + test_0F = 0; + } + + if((buff_0F[0] >> 1) & 1) // case 0f_0x 02 xx xx xx xx xx + { + rdr_log(reader, "classD3 ins54: no cw --> Card isn't active"); + test_0F = 0; + } + else // These Messages are only necessary if the Card is active + { + if((buff_0F[1] >> 4) & 1) // case 0f_0x xx 10 xx xx xx xx + { + rdr_log(reader, "classD3 ins54: no cw --> Card needs pairing/extra data"); + if((reader->caid == 0x98C || reader->caid == 0x98D) && (buff_0F[5] == 0)){ //case 0f_0x xx 10 xx xx xx 00 + rdr_log(reader, "classD3 ins54: no cw --> unassigned Boxid"); + } + test_0F = 0; + } + + if((buff_0F[1] >> 5) & 1) // case 0f_0x xx 20 xx xx xx xx + { + rdr_log(reader, "classD3 ins54: no cw --> No tier found"); //check this + test_0F = 0; + } + + if((buff_0F[2] >> 5) & 1) // case 0f_0x xx xx 20 xx xx xx + { + rdr_log(reader, "classD3 ins54: no cw --> Tier expired"); + test_0F = 0; + } + + if((buff_0F[1] >> 6) & 1) // case 0f_0x xx 40 xx xx xx xx + { + rdr_log(reader, "classD3 ins54: no cw --> Card needs pin"); + test_0F = 0; + } + + if((reader->caid == 0x98C || reader->caid == 0x98D) && ((buff_0F[5] >> 3) & 1)) //case 0f_0x xx xx xx xx xx XX = binary xxxx1xxx + { + rdr_log(reader, "classD3 ins54: no cw --> CW-overcrypt%s is required! (Debug-ECM-Info: 0F_06 %02X %02X %02X %02X %02X %02X)", + (((buff_0F[5] >> 1) & 1) ? " (and assignment)" : ""), buff_0F[0], buff_0F[1], buff_0F[2], buff_0F[3], buff_0F[4], buff_0F[5]); //case 0f_0x xx xx xx xx xx XX = binary xxxx1x?x + } + } + + if(test_0F) // case unknown error + { + rdr_log(reader, "classD3 ins54: status 90 00 = ok but cw=00 tag 0F: %02X %02X %02X %02X %02X %02X, please report to the developers with decrypted ins54", + buff_0F[0], buff_0F[1], buff_0F[2], buff_0F[3], buff_0F[4], buff_0F[5]); + } + return ERROR; + } + + // copy cw1 in place + memcpy(ea->cw + 0, rbuff + 5, 8); + + //case 55_01 xx where bit3==1, bit2==0, bit1==0, and bit0==1, ins7e and CW-Overcrypt may not required + if(((buff_55[0] >> 3) & 1) && (~((buff_55[0] >> 2) & 1)) && (~((buff_55[0] >> 1) & 1)) && (buff_55[0] & 1)) + { + rdr_log_dbg(reader, D_READER, "classD3 ins54: Tag55_01 = %02X, ins7e and CW-overcrypt may not required", buff_55[0]); + } + + // case 55_01 xx where bit0==1, CW is crypted + if(buff_55[0] & 1) + { + if(~((buff_55[0] >> 2) & 1)) //case 55_01 xx where bit2==0 + { + if((buff_55[0] >> 1) & 1) //case 55_01 xx where bit1==1, unique Pairing + { + rdr_log_dbg(reader, D_READER, "classD3 ins54: CW is crypted, trying to decrypt unique pairing mode 0x%02X", buff_55[0]); + if((buff_56[0]|buff_56[1]|buff_56[2]|buff_56[3]|buff_56[4]|buff_56[5]|buff_56[6]|buff_56[7]) != 0) { //when 56_08 is non-zero use AES + rdr_log_dbg(reader, D_READER, "encrypted AES buffer is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7], buff_56[0], buff_56[1], buff_56[2], buff_56[3], buff_56[4], buff_56[5], buff_56[6], buff_56[7]); + uint8_t aesbuf[0x10]; + uint8_t keybuf[0x10]; + AES_KEY aeskey; + memcpy(aesbuf, rbuff + 5, 8); + memcpy(aesbuf + 8, buff_56, 8); + memcpy(keybuf, &(reader->k1_unique), 16); + if(reader->k1_unique[16] == 0x10) { + rdr_log_dbg(reader, D_READER, "use k1(AES) for AES buffer decryption in unique pairing mode"); + AES_set_decrypt_key(keybuf, 128, &aeskey); + AES_decrypt(aesbuf, aesbuf, &aeskey); + if(er->ecm[0] & 1){ //log decrypted CW + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15], aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7]); + } else { + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7], aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15]); + } + memcpy(ea->cw + 0, aesbuf, 8); + } + else { + rdr_log_dbg(reader, D_READER, "k1 for unique pairing mode is not set correctly"); + return ERROR; + } + } + else { //case 56_08 is zero, DES or 3DES + if(er->ecm[0] & 1){ //log crypted CW + rdr_log_dbg(reader, D_READER, "crypted CW is: 0000000000000000%02X%02X%02X%02X%02X%02X%02X%02X", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7]); + } else { + rdr_log_dbg(reader, D_READER, "crypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X0000000000000000", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7]); + } + if((reader->k1_unique[16] == 0x08) || (reader->k1_unique[16] == 0x10)) //check k1 for unique pairing mode is DES(8 bytes) or 3DES(16 bytes) long + { + if(reader->k1_unique[16] == 0x08){ + rdr_log_dbg(reader, D_READER, "use k1(DES) for CW decryption in unique pairing mode"); + des_ecb_decrypt(ea->cw, reader->k1_unique, 0x08); + } + else + { + rdr_log_dbg(reader, D_READER, "use k1(3DES) for CW decryption in unique pairing mode"); + des_ecb3_decrypt(ea->cw, reader->k1_unique); + } + if(er->ecm[0] & 1){ //log decrypted CW + rdr_log_dbg(reader, D_READER, "decrypted CW is: 0000000000000000%02X%02X%02X%02X%02X%02X%02X%02X", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7]); + } else { + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X0000000000000000", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7]); + } + } + else + { + rdr_log_dbg(reader, D_READER, "k1 for unique pairing mode is not set"); + return ERROR; + } + } + } + else //case 55_01 xx where bit1==0, generic Pairing + { + rdr_log_dbg(reader, D_READER, "classD3 ins54: CW is crypted, trying to decrypt generic pairing mode 0x%02X", buff_55[0]); + if((buff_56[0]|buff_56[1]|buff_56[2]|buff_56[3]|buff_56[4]|buff_56[5]|buff_56[6]|buff_56[7]) != 0) { //when 56_08 is non-zero use AES + rdr_log_dbg(reader, D_READER, "encrypted AES buffer is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7], buff_56[0], buff_56[1], buff_56[2], buff_56[3], buff_56[4], buff_56[5], buff_56[6], buff_56[7]); + uint8_t aesbuf[0x10]; + uint8_t keybuf[0x10]; + AES_KEY aeskey; + memcpy(aesbuf, rbuff + 5, 8); + memcpy(aesbuf + 8, buff_56, 8); + memcpy(keybuf, &(reader->k1_generic), 16); + if(reader->k1_generic[16] == 0x10) { + rdr_log_dbg(reader, D_READER, "use k1(AES) for AES buffer decryption in generic pairing mode"); + AES_set_decrypt_key(keybuf, 128, &aeskey); + AES_decrypt(aesbuf, aesbuf, &aeskey); + if(er->ecm[0] & 1){ //log decrypted CW + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15], aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7]); + } else { + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7], aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15]); + } + memcpy(ea->cw + 0, aesbuf, 8); + } + else { + rdr_log_dbg(reader, D_READER, "k1 for generic pairing mode is not set correctly"); + return ERROR; + } + } + else { // case 56_08 is zero, DES or 3DES + if(er->ecm[0] & 1){ //log crypted CW + rdr_log_dbg(reader, D_READER, "crypted CW is: 0000000000000000%02X%02X%02X%02X%02X%02X%02X%02X", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7]); + } else { + rdr_log_dbg(reader, D_READER, "crypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X0000000000000000", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7]); + } + if((reader->k1_generic[16] == 0x08) || (reader->k1_generic[16] == 0x10)) //check k1 for generic pairing mode is DES(8 bytes) or 3DES(16 bytes) long + { + if(reader->k1_generic[16] == 0x08){ + rdr_log_dbg(reader, D_READER, "use k1(DES) for CW decryption in generic pairing mode"); + des_ecb_decrypt(ea->cw, reader->k1_generic, 0x08); + } + else + { + rdr_log_dbg(reader, D_READER, "use k1(3DES) for CW decryption in generic pairing mode"); + des_ecb3_decrypt(ea->cw, reader->k1_generic); + } + if(er->ecm[0] & 1){ //log decrypted CW + rdr_log_dbg(reader, D_READER, "decrypted CW is: 0000000000000000%02X%02X%02X%02X%02X%02X%02X%02X", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7]); + } else { + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X0000000000000000", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7]); + } + } + else + { + rdr_log_dbg(reader, D_READER, "k1 for generic pairing mode is not set"); + return ERROR; + } + } + } + } + else //unknown pairing mode + { + rdr_log_dbg(reader, D_READER, "classD3 ins54: CW is crypted, unknown pairing mode 0x%02X", buff_55[0]); + if(er->ecm[0] & 1){ //log crypted CW + rdr_log_dbg(reader, D_READER, "crypted CW is: 0000000000000000%02X%02X%02X%02X%02X%02X%02X%02X", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7]); + } else { + rdr_log_dbg(reader, D_READER, "crypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X0000000000000000", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7]); + } + return ERROR; + } + } + + // case 55_01 xx where bit2==1, old dimeno_PostProcess_Decrypt(reader, rbuff, ea->cw); + if((buff_55[0] >> 2) & 1) + { + rdr_log_dbg(reader, D_READER, "classD3 ins54: CW is crypted, trying to decrypt AES boxkey mode 0x%02X", buff_55[0]); + rdr_log_dbg(reader, D_READER, "encrypted AES buffer is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", ea->cw[0], ea->cw[1], ea->cw[2], ea->cw[3], ea->cw[4], ea->cw[5], ea->cw[6], ea->cw[7], buff_56[0], buff_56[1], buff_56[2], buff_56[3], buff_56[4], buff_56[5], buff_56[6], buff_56[7]); + uint8_t aesbuf[0x10]; + memcpy(aesbuf, rbuff + 5, 8); + memcpy(aesbuf + 8, buff_56, 8); + rdr_log_dbg(reader, D_READER, "use dimeno magic for AES buffer decryption"); + AES_decrypt(aesbuf, aesbuf, &(csystem_data->astrokey)); + if(er->ecm[0] & 1){ //log decrypted CW + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15], aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7]); + } else { + rdr_log_dbg(reader, D_READER, "decrypted CW is: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", aesbuf[0], aesbuf[1], aesbuf[2], aesbuf[3], aesbuf[4], aesbuf[5], aesbuf[6], aesbuf[7], aesbuf[8], aesbuf[9], aesbuf[10], aesbuf[11], aesbuf[12], aesbuf[13], aesbuf[14], aesbuf[15]); + } + memcpy(ea->cw + 0, aesbuf, 8); // copy calculated CW in right place + } + + if(new_len != lenECMpart2) + { + memcpy(ea->cw, ea->cw + 8, 8); + memset(ea->cw + 8, 0, 8); + } + + // test for postprocessing marker + int32_t posB0 = -1; + int32_t i; + for(i = 6; i < posECMpart2; i++) + { + if(er->ecm[i - 3] == 0x80 && er->ecm[i] == 0xB0 && ((er->ecm[i + 1] == 0x01) || (er->ecm[i + 1] == 0x02) || (er->ecm[i + 1] == 0x03))) + { + posB0 = i; + break; + } + } + + if(posB0 != -1 && (reader->caid == 0x919 || reader->caid == 0x93B || reader->caid == 0x9CD || reader->caid == 0x9C1)) + { + do_post_dw_hash(reader, ea->cw + 0, &er->ecm[posB0 - 2]); + do_post_dw_hash(reader, ea->cw + 8, &er->ecm[posB0 - 2]); + } + + if(reader->caid == 0x0907) // quickfix: cw2 is not a valid cw, something went wrong before + { + memset(ea->cw + 8, 0, 8); + if(er->ecm[0] & 1) + { + memcpy(ea->cw + 8, ea->cw, 8); + memset(ea->cw, 0, 8); + } + } + else + { + if(er->ecm[0] & 1) + { + uint8_t tmpcw[8]; + memcpy(tmpcw, ea->cw + 8, 8); + memcpy(ea->cw + 8, ea->cw + 0, 8); + memcpy(ea->cw + 0, tmpcw, 8); + } + } + + return OK; + } + } +} + +static int32_t videoguard2_do_emm(struct s_reader *reader, EMM_PACKET *ep) +{ + return videoguard_do_emm(reader, ep, 0xD1, vg2_read_tiers, do_cmd); +} + +static int32_t videoguard2_do_rawcmd(struct s_reader *reader, CMD_PACKET *cp) +{ + return videoguard_do_rawcmd(reader, cp); +} + +static int32_t videoguard2_card_info(struct s_reader *reader) +{ + // info is displayed in init, or when processing info + rdr_log(reader, "card detected"); + rdr_log(reader, "type: VideoGuard2 Card"); + + if(reader->ins7e11_fast_reset != 1) + { + vg2_read_tiers(reader); + } + return OK; +} + +static void videoguard2_card_done(struct s_reader *reader) +{ + struct videoguard_data *csystem_data = reader->csystem_data; + if(csystem_data) + { + NULLFREE(csystem_data->cmd_table); + } +} + +const struct s_cardsystem reader_videoguard2 = +{ + .desc = "videoguard2", + .caids = (uint16_t[]){ 0x09, 0 }, + .do_emm = videoguard2_do_emm, + .do_rawcmd = videoguard2_do_rawcmd, + .do_ecm = videoguard2_do_ecm, + .card_info = videoguard2_card_info, + .card_init = videoguard2_card_init, + .poll_status = videoguard2_poll_status, + .card_done = videoguard2_card_done, + .get_emm_type = videoguard_get_emm_type, + .get_emm_filter = videoguard_get_emm_filter, +}; + +#endif diff --git a/readers.h b/readers.h new file mode 100644 index 0000000..316c2be --- /dev/null +++ b/readers.h @@ -0,0 +1,22 @@ +#ifndef READERS_H_ +#define READERS_H_ + +extern const struct s_cardsystem reader_nagra; +extern const struct s_cardsystem reader_nagracak7; +extern const struct s_cardsystem reader_irdeto; +extern const struct s_cardsystem reader_cryptoworks; +extern const struct s_cardsystem reader_viaccess; +extern const struct s_cardsystem reader_conax; +extern const struct s_cardsystem reader_seca; +extern const struct s_cardsystem reader_videoguard1; +extern const struct s_cardsystem reader_videoguard2; +extern const struct s_cardsystem reader_videoguard12; +extern const struct s_cardsystem reader_dre; +extern const struct s_cardsystem reader_drecas; +extern const struct s_cardsystem reader_tongfang; +extern const struct s_cardsystem reader_bulcrypt; +extern const struct s_cardsystem reader_griffin; +extern const struct s_cardsystem reader_dgcrypt; +extern const struct s_cardsystem reader_emu; + +#endif diff --git a/tests.c b/tests.c new file mode 100644 index 0000000..01d2c26 --- /dev/null +++ b/tests.c @@ -0,0 +1,317 @@ +/* + * OSCam self tests + * This file contains tests for different config parsers and generators + * Build this file using `make tests` + */ +#include "globals.h" + +#include "oscam-array.h" +#include "oscam-string.h" +#include "oscam-conf-chk.h" +#include "oscam-conf-mk.h" + +struct test_vec +{ + const char *in; // Input data + const char *out; // Expected output data (if out is NULL, then assume in == out) +}; + +typedef void (CHK_FN) (char *, void *); +typedef char *(MK_T_FN) (void *); +typedef void (CLEAR_FN)(void *); +typedef void (CLONE_FN)(void *, void *); + +struct test_type +{ + char *desc; // Test textual description + void *data; // Pointer to basic data structure + void *data_c; // Pointer to data structure that will hold cloned data (for clone_ tests) + size_t data_sz; // Data structure size + CHK_FN *chk_fn; // chk_XXX() func for the data type + MK_T_FN *mk_t_fn; // mk_t_XXX() func for the data type + CLEAR_FN *clear_fn; // clear_XXX() func for the data type + CLONE_FN *clone_fn; // clone_XXX() func for the data type + const struct test_vec *test_vec; // Array of test vectors +}; + +static void run_parser_test(struct test_type *t) +{ + memset(t->data, 0, t->data_sz); + memset(t->data_c, 0, t->data_sz); + printf("%s\n", t->desc); + const struct test_vec *vec = t->test_vec; + while (vec->in) + { + bool ok; + printf(" Testing \"%s\"", vec->in); + char *input_setting = cs_strdup(vec->in); + t->chk_fn(input_setting, t->data); + t->clone_fn(t->data, t->data_c); // Check if 'clone' works + t->clear_fn(t->data); // Check if 'clear' works + char *generated = t->mk_t_fn(t->data_c); // Use cloned data + if (vec->out) + ok = strcmp(vec->out, generated) == 0; + else + ok = strcmp(vec->in, generated) == 0; + if (ok) + { + printf(" [OK]\n"); + } else { + printf("\n"); + printf(" === ERROR ===\n"); + printf(" Input data: \"%s\"\n", vec->in); + printf(" Got result: \"%s\"\n", generated); + printf(" Expected out: \"%s\"\n", vec->out ? vec->out : vec->in); + printf("\n"); + } + free_mk_t(generated); + free(input_setting); + fflush(stdout); + vec++; + } + t->clear_fn(t->data_c); +} + +void run_all_tests(void) +{ + ECM_WHITELIST ecm_whitelist, ecm_whitelist_c; + struct test_type ecm_whitelist_test = + { + .desc = "ECM whitelist setting (READER: 'ecmwhitelist')", + .data = &ecm_whitelist, + .data_c = &ecm_whitelist_c, + .data_sz = sizeof(ecm_whitelist), + .chk_fn = (CHK_FN *)&chk_ecm_whitelist, + .mk_t_fn = (MK_T_FN *)&mk_t_ecm_whitelist, + .clear_fn = (CLEAR_FN *)&ecm_whitelist_clear, + .clone_fn = (CLONE_FN *)&ecm_whitelist_clone, + .test_vec = (const struct test_vec[]) + { + { .in = "0500@043800:70,6E,6C,66,7A,61,67,75,5D,6B;0600@070800:11,22,33,44,55,66;0700:AA,BB,CC,DD,EE;01,02,03,04;0123@456789:01,02,03,04" }, + { .in = "0500@043800:70,6E,6C,66,7A,61,67,75,5D,6B" }, + { .in = "0500@043800:70,6E,6C,66" }, + { .in = "0500@043800:70,6E,6C" }, + { .in = "0500@043800:70" }, + { .in = "0500:81,82,83;0600:91" }, + { .in = "0500:81,82" }, + { .in = "0500:81" }, + { .in = "@123456:81" }, + { .in = "@123456:81;@000789:AA,BB,CC" }, + { .in = "81" }, + { .in = "81,82,83" }, + { .in = "81,82,83,84" }, + { .in = "0500@043800:70;0600@070800:11;0123@456789:01,02" }, + { .in = "" }, + { .in = "0500:81,32;0600:aa,bb", .out = "0500:81,32;0600:AA,BB" }, + { .in = "500:1,2;60@77:a,b,z,,", .out = "0500:01,02;0060@000077:0A,0B" }, + { .in = "@ff:81;@bb:11,22", .out = "@0000FF:81;@0000BB:11,22" }, + { .in = "@:81", .out = "81" }, + { .in = "81;zzs;;;;;ab", .out = "81,AB" }, + { .in = ":@", .out = "" }, + { .in = ",:,@,", .out = "" }, + { .in = "@:", .out = "" }, + { .in = "@:,,", .out = "" }, + { .in = "@:;;;", .out = "" }, + { .in = ",", .out = "" }, + { .in = NULL }, + }, + }; + run_parser_test(&ecm_whitelist_test); + + ECM_HDR_WHITELIST ecm_hdr_whitelist, ecm_hdr_whitelist_c; + struct test_type ecm_hdr_whitelist_test = + { + .desc = "ECM header whitelist setting (READER: 'ecmhdrwhitelist')", + .data = &ecm_hdr_whitelist, + .data_c = &ecm_hdr_whitelist_c, + .data_sz = sizeof(ecm_hdr_whitelist), + .chk_fn = (CHK_FN *)&chk_ecm_hdr_whitelist, + .mk_t_fn = (MK_T_FN *)&mk_t_ecm_hdr_whitelist, + .clear_fn = (CLEAR_FN *)&ecm_hdr_whitelist_clear, + .clone_fn = (CLONE_FN *)&ecm_hdr_whitelist_clone, + .test_vec = (const struct test_vec[]) + { + { .in = "1830@123456:80308F078D,81308F078D;1702@007878:807090C7000000011010008712078400,817090C7000000011010008713078400" }, + { .in = "1830:80308F078D,81308F078D;1702:807090C7000000011010008712078400,817090C7000000011010008713078400" }, + { .in = "813061006A00075C00,803061006A00075C00" }, + { .in = "813061006A00075C00" }, + { .in = "1122334455667788991011121314151617182021222324252627282930", .out = "1122334455667788991011121314151617182021" }, + { .in = "9999@999999:1122334455667788991011121314151617182021,2233334455667788991011121314151617182021;AAAA@BBBBBB:1122334455667788991011121314151617182021" }, + { .in = "0500:81,82,83;0600:91" }, + { .in = "0500:81,82" }, + { .in = "0500:81" }, + { .in = "@123456:81" }, + { .in = "@123456:81;@000789:AA,BB,CC" }, + { .in = "81" }, + { .in = "81,82,83" }, + { .in = "81,82,83,84" }, + { .in = "0500@043800:70;0600@070800:11;0123@456789:01,02" }, + { .in = "" }, + { .in = "00,82,83" }, + { .in = "0500:81,32;0600:aa,bb", .out = "0500:81,32;0600:AA,BB" }, + { .in = "@ff:81;@bb:11,22", .out = "@0000FF:81;@0000BB:11,22" }, + { .in = "0500:,,,;0060@000077:,,;0700:,;0800", .out = "0800" }, + { .in = "@:81", .out = "81" }, + { .in = "81;zzs;;;;;ab", .out = "81,EF,AB" }, + { .in = "1830@123456:", .out = "" }, + { .in = "500:1,2;60@77:a,b,z,,", .out = "" }, + { .in = ":@", .out = "" }, + { .in = ",:,@,", .out = "" }, + { .in = "@:", .out = "" }, + { .in = "@:,,", .out = "" }, + { .in = "@:;;;", .out = "" }, + { .in = ",", .out = "" }, + { .in = NULL }, + }, + }; + run_parser_test(&ecm_hdr_whitelist_test); + + TUNTAB tuntab, tuntab_c; + struct test_type tuntab_test = + { + .desc = "Beta tunnel (tuntab) (ACCOUNT: 'betatunnel')", + .data = &tuntab, + .data_c = &tuntab_c, + .data_sz = sizeof(tuntab), + .chk_fn = (CHK_FN *)&chk_tuntab, + .mk_t_fn = (MK_T_FN *)&mk_t_tuntab, + .clear_fn = (CLEAR_FN *)&tuntab_clear, + .clone_fn = (CLONE_FN *)&tuntab_clone, + .test_vec = (const struct test_vec[]) + { + { .in = "1833.007A:1702,1833.007B:1702,1833.007C:1702,1833.007E:1702,1833.007F:1702,1833.0080:1702,1833.0081:1702,1833.0082:1702,1833.0083:1702,1833.0084:1702" }, + { .in = "1833.007A:1702,1833.007B:1702,1833.007C:1702,1833.007E:1702" }, + { .in = "1833.007A:1702" }, + { .in = "" }, + { .in = "1833.007A" }, + { .in = "1833:1702", .out = "" }, + { .in = "1833", .out = "" }, + { .in = "zzzz.yyyy:tttt", .out = "" }, + { .in = "zzzz.yyyy", .out = "" }, + { .in = ",", .out = "" }, + { .in = ".:", .out = "" }, + { .in = ":.,", .out = "" }, + { .in = NULL }, + }, + }; + run_parser_test(&tuntab_test); + + FTAB ftab, ftab_c; + struct test_type ftab_test = + { + .desc = "Filters (ftab) (ACCOUNT: 'chid', 'ident'; READER: 'chid', 'ident', 'fallback_percaid', 'localcards')", + .data = &ftab, + .data_c = &ftab_c, + .data_sz = sizeof(ftab), + .chk_fn = (CHK_FN *)&chk_ftab, + .mk_t_fn = (MK_T_FN *)&mk_t_ftab, + .clear_fn = (CLEAR_FN *)&ftab_clear, + .clone_fn = (CLONE_FN *)&ftab_clone, + .test_vec = (const struct test_vec[]) + { + { .in = "0100:123456,234567;0200:345678,456789" }, + { .in = "183D:000000,005411" }, + { .in = "183D:000000" }, + { .in = "0100:000012" }, + { .in = "0100:000012;0604:0000BA,000101,00010E,000141" }, + { .in = "1234:234567;0010:345678,876543" }, + { .in = "" }, + { .in = "0200:eeee,tyut,1234", .out = "0200:00EEEE,001234" }, + { .in = "0200:eeee,tyut", .out = "0200:00EEEE" }, + { .in = "1:0", .out = "0001:000000" }, + { .in = "1:0,1,0", .out = "0001:000000,000001,000000" }, + { .in = "0:0", .out = "" }, + { .in = "zzzz:", .out = "" }, + { .in = "yyyy:rrrr,qqqq", .out = "" }, + { .in = ",", .out = "" }, + { .in = ",;,", .out = "" }, + { .in = ";;;", .out = "" }, + { .in = ".:", .out = "" }, + { .in = ":.,", .out = "" }, + { .in = ":;.,", .out = "" }, + { .in = ".:;,", .out = "" }, + { .in = NULL }, + }, + }; + run_parser_test(&ftab_test); + + CAIDVALUETAB caidvaluetab, caidvaluetab_c; + struct test_type caidvaluetab_test = + { + .desc = "caidvaluetab (ACCOUNT: 'lb_nbest_percaid'; GLOBAL: 'lb_nbest_percaid', 'fallbacktimeout_percaid', 'lb_retrylimits', 'cacheex_mode1_delay')", + .data = &caidvaluetab, + .data_c = &caidvaluetab_c, + .data_sz = sizeof(caidvaluetab), + .chk_fn = (CHK_FN *)&chk_caidvaluetab, + .mk_t_fn = (MK_T_FN *)&mk_t_caidvaluetab, + .clear_fn = (CLEAR_FN *)&caidvaluetab_clear, + .clone_fn = (CLONE_FN *)&caidvaluetab_clone, + .test_vec = (const struct test_vec[]) + { + { .in = "0100:4,0200:3,0300:2,0400:1" }, + { .in = "0100:4,02:3,03:2,04:1,0500:9999" }, + { .in = "0100:4" }, + { .in = "01:4" }, + { .in = "" }, + { .in = "0500:10000", .out = "" }, + { .in = "0200:eeee,tyut,1234", .out = "0200:0" }, + { .in = "0200:eeee,tyut", .out = "0200:0" }, + { .in = "1:0", .out = "01:0" }, + { .in = "1:0,1,0", .out = "01:0" }, + { .in = "0500:10000", .out = "" }, + { .in = "0:0", .out = "" }, + { .in = "zzzz:", .out = "" }, + { .in = "yyyy:rrrr,qqqq", .out = "" }, + { .in = ",", .out = "" }, + { .in = ",:,", .out = "" }, + { .in = ";:;", .out = "" }, + { .in = ".:", .out = "" }, + { .in = ":.,", .out = "" }, + { .in = ":;.,", .out = "" }, + { .in = ".:;,", .out = "" }, + { .in = NULL }, + }, + }; + run_parser_test(&caidvaluetab_test); + + CAIDTAB caidtab, caidtab_c; + struct test_type caidtab_test = + { + .desc = "caidtab (ACCOUNT: 'caid'; READER: 'caid'; GLOBAL: 'lb_noproviderforcaid', 'double_check_caid', 'cwcycle_check_caid')", + .data = &caidtab, + .data_c = &caidtab_c, + .data_sz = sizeof(caidtab), + .chk_fn = (CHK_FN *)&chk_caidtab, + .mk_t_fn = (MK_T_FN *)&mk_t_caidtab, + .clear_fn = (CLEAR_FN *)&caidtab_clear, + .clone_fn = (CLONE_FN *)&caidtab_clone, + .test_vec = (const struct test_vec[]) + { + { .in = "0200&FFEE:0300" }, + { .in = "0200&FF00:0300,0400&00FF:0500" }, + { .in = "0200&FF00:0300,0400,0500:0600,0600&FF0F:1234" }, + { .in = "0400&FF00:0500,0600" }, + { .in = "0702,0722" }, + { .in = "0702&FFDF" }, + { .in = "0100" }, + { .in = "01" }, + { .in = "" }, + { .in = "0500:10000", .out = "0500" }, + { .in = "1000&5FFFF5:0600", .out = "1000&FFF5:0600" }, + { .in = "10000:10000", .out = "" }, + { .in = "rrrr&zzzz:mmmm", .out = "" }, + { .in = "0:0", .out = "" }, + { .in = "zzzz:", .out = "" }, + { .in = "yyyy:rrrr,qqqq", .out = "" }, + { .in = ",", .out = "" }, + { .in = ",:,", .out = "" }, + { .in = "&:&", .out = "" }, + { .in = ".:", .out = "" }, + { .in = ":.,", .out = "" }, + { .in = ":&.,", .out = "" }, + { .in = ".:&,", .out = "" }, + { .in = NULL }, + }, + }; + run_parser_test(&caidtab_test); +} diff --git a/tommyDS_hashlin/tommychain.h b/tommyDS_hashlin/tommychain.h new file mode 100644 index 0000000..12d2514 --- /dev/null +++ b/tommyDS_hashlin/tommychain.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** \file + * Chain of nodes. + * A chain of nodes is an abstraction used to implements complex list operations + * like sorting. + * + * Do not use this directly. Use lists instead. + */ + +#ifndef __TOMMYCHAIN_H +#define __TOMMYCHAIN_H + +#include "tommytypes.h" + +/******************************************************************************/ +/* chain */ + +/** + * Chain of nodes. + * A chain of nodes is a sequence of nodes with the following properties: + * - It contains at least one node. A chains of zero nodes cannot exist. + * - The next field of the tail is of *undefined* value. + * - The prev field of the head is of *undefined* value. + * - All the other inner prev and next fields are correctly set. + */ +typedef struct tommy_chain_struct { + tommy_node* head; /**< Pointer to the head of the chain. */ + tommy_node* tail; /**< Pointer to the tail of the chain. */ +} tommy_chain; + +/** + * Splices a chain in the middle of another chain. + */ +tommy_inline void tommy_chain_splice(tommy_node* first_before, tommy_node* first_after, tommy_node* second_head, tommy_node* second_tail) +{ + /* set the prev list */ + first_after->prev = second_tail; + second_head->prev = first_before; + + /* set the next list */ + first_before->next = second_head; + second_tail->next = first_after; +} + +/** + * Concats two chains. + */ +tommy_inline void tommy_chain_concat(tommy_node* first_tail, tommy_node* second_head) +{ + /* set the prev list */ + second_head->prev = first_tail; + + /* set the next list */ + first_tail->next = second_head; +} + +/** + * Merges two chains. + */ +tommy_inline void tommy_chain_merge(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp) +{ + tommy_node* first_i = first->head; + tommy_node* second_i = second->head; + + /* merge */ + while (1) { + if (cmp(first_i->data, second_i->data) > 0) { + tommy_node* next = second_i->next; + if (first_i == first->head) { + tommy_chain_concat(second_i, first_i); + first->head = second_i; + } else { + tommy_chain_splice(first_i->prev, first_i, second_i, second_i); + } + if (second_i == second->tail) + break; + second_i = next; + } else { + if (first_i == first->tail) { + tommy_chain_concat(first_i, second_i); + first->tail = second->tail; + break; + } + first_i = first_i->next; + } + } +} + +/** + * Merges two chains managing special degenerated cases. + * It's funtionally equivalent at tommy_chain_merge() but faster with already ordered chains. + */ +tommy_inline void tommy_chain_merge_degenerated(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp) +{ + /* identify the condition first <= second */ + if (cmp(first->tail->data, second->head->data) <= 0) { + tommy_chain_concat(first->tail, second->head); + first->tail = second->tail; + return; + } + + /* identify the condition second < first */ + /* here we must be strict on comparison to keep the sort stable */ + if (cmp(second->tail->data, first->head->data) < 0) { + tommy_chain_concat(second->tail, first->head); + first->head = second->head; + return; + } + + tommy_chain_merge(first, second, cmp); +} + +/** + * Sorts a chain. + * It's a stable merge sort using power of 2 buckets, with O(N*log(N)) complexity, + * similar at the one used in the SGI STL libraries and in the Linux Kernel, + * but faster on degenerated cases like already ordered lists. + * + * SGI STL stl_list.h + * http://www.sgi.com/tech/stl/stl_list.h + * + * Linux Kernel lib/list_sort.c + * http://lxr.linux.no/#linux+v2.6.36/lib/list_sort.c + */ +tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func* cmp) +{ + /* + * Bit buckets of chains. + * Each bucket contains 2^i nodes or it's empty. + * The chain at address TOMMY_BIT_MAX is an independet variable operating as "carry". + * We keep it in the same "bit" vector to avoid reports from the valgrind tool sgcheck. + */ + tommy_chain bit[TOMMY_SIZE_BIT + 1]; + + /** + * Value stored inside the bit bucket. + * It's used to know which bucket is empty of full. + */ + tommy_size_t counter; + tommy_node* node = chain->head; + tommy_node* tail = chain->tail; + tommy_size_t mask; + tommy_size_t i; + + counter = 0; + while (1) { + tommy_node* next; + tommy_chain* last; + + /* carry bit to add */ + last = &bit[TOMMY_SIZE_BIT]; + bit[TOMMY_SIZE_BIT].head = node; + bit[TOMMY_SIZE_BIT].tail = node; + next = node->next; + + /* add the bit, propagating the carry */ + i = 0; + mask = counter; + while ((mask & 1) != 0) { + tommy_chain_merge_degenerated(&bit[i], last, cmp); + mask >>= 1; + last = &bit[i]; + ++i; + } + + /* copy the carry in the first empty bit */ + bit[i] = *last; + + /* add the carry in the counter */ + ++counter; + + if (node == tail) + break; + node = next; + } + + /* merge the buckets */ + i = tommy_ctz(counter); + mask = counter >> i; + while (mask != 1) { + mask >>= 1; + if (mask & 1) + tommy_chain_merge_degenerated(&bit[i + 1], &bit[i], cmp); + else + bit[i + 1] = bit[i]; + ++i; + } + + *chain = bit[i]; +} + +#endif + diff --git a/tommyDS_hashlin/tommyhash.c b/tommyDS_hashlin/tommyhash.c new file mode 100644 index 0000000..74c4e49 --- /dev/null +++ b/tommyDS_hashlin/tommyhash.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "tommyhash.h" + +/******************************************************************************/ +/* hash */ + +tommy_inline tommy_uint32_t tommy_le_uint32_read(const void* ptr) +{ + /* allow unaligned read on Intel x86 and x86_64 platforms */ +#if defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__x86_64__) || defined(_M_X64) + /* defines from http://predef.sourceforge.net/ */ + return *(const tommy_uint32_t*)ptr; +#else + const unsigned char* ptr8 = tommy_cast(const unsigned char*, ptr); + return ptr8[0] + ((tommy_uint32_t)ptr8[1] << 8) + ((tommy_uint32_t)ptr8[2] << 16) + ((tommy_uint32_t)ptr8[3] << 24); +#endif +} + +#define tommy_rot(x, k) \ + (((x) << (k)) | ((x) >> (32 - (k)))) + +#define tommy_mix(a, b, c) \ + do { \ + a -= c; a ^= tommy_rot(c, 4); c += b; \ + b -= a; b ^= tommy_rot(a, 6); a += c; \ + c -= b; c ^= tommy_rot(b, 8); b += a; \ + a -= c; a ^= tommy_rot(c, 16); c += b; \ + b -= a; b ^= tommy_rot(a, 19); a += c; \ + c -= b; c ^= tommy_rot(b, 4); b += a; \ + } while (0) + +#define tommy_final(a, b, c) \ + do { \ + c ^= b; c -= tommy_rot(b, 14); \ + a ^= c; a -= tommy_rot(c, 11); \ + b ^= a; b -= tommy_rot(a, 25); \ + c ^= b; c -= tommy_rot(b, 16); \ + a ^= c; a -= tommy_rot(c, 4); \ + b ^= a; b -= tommy_rot(a, 14); \ + c ^= b; c -= tommy_rot(b, 24); \ + } while (0) + +tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len) +{ + const unsigned char* key = tommy_cast(const unsigned char*, void_key); + tommy_uint32_t a, b, c; + + a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + init_val; + + while (key_len > 12) { + a += tommy_le_uint32_read(key + 0); + b += tommy_le_uint32_read(key + 4); + c += tommy_le_uint32_read(key + 8); + + tommy_mix(a, b, c); + + key_len -= 12; + key += 12; + } + + switch (key_len) { + case 0 : + return c; /* used only when called with a zero length */ + case 12 : + c += tommy_le_uint32_read(key + 8); + b += tommy_le_uint32_read(key + 4); + a += tommy_le_uint32_read(key + 0); + break; + case 11 : c += ((tommy_uint32_t)key[10]) << 16; /* fallthrough */ + case 10 : c += ((tommy_uint32_t)key[9]) << 8; /* fallthrough */ + case 9 : c += key[8]; /* fallthrough */ + case 8 : + b += tommy_le_uint32_read(key + 4); + a += tommy_le_uint32_read(key + 0); + break; + case 7 : b += ((tommy_uint32_t)key[6]) << 16; /* fallthrough */ + case 6 : b += ((tommy_uint32_t)key[5]) << 8; /* fallthrough */ + case 5 : b += key[4]; /* fallthrough */ + case 4 : + a += tommy_le_uint32_read(key + 0); + break; + case 3 : a += ((tommy_uint32_t)key[2]) << 16; /* fallthrough */ + case 2 : a += ((tommy_uint32_t)key[1]) << 8; /* fallthrough */ + case 1 : a += key[0]; /* fallthrough */ + } + + tommy_final(a, b, c); + + return c; +} + +tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len) +{ + const unsigned char* key = tommy_cast(const unsigned char*, void_key); + tommy_uint32_t a, b, c; + + a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + (init_val & 0xffffffff); + c += init_val >> 32; + + while (key_len > 12) { + a += tommy_le_uint32_read(key + 0); + b += tommy_le_uint32_read(key + 4); + c += tommy_le_uint32_read(key + 8); + + tommy_mix(a, b, c); + + key_len -= 12; + key += 12; + } + + switch (key_len) { + case 0 : + return c + ((tommy_uint64_t)b << 32); /* used only when called with a zero length */ + case 12 : + c += tommy_le_uint32_read(key + 8); + b += tommy_le_uint32_read(key + 4); + a += tommy_le_uint32_read(key + 0); + break; + case 11 : c += ((tommy_uint32_t)key[10]) << 16; /* fallthrough */ + case 10 : c += ((tommy_uint32_t)key[9]) << 8; /* fallthrough */ + case 9 : c += key[8]; /* fallthrough */ + case 8 : + b += tommy_le_uint32_read(key + 4); + a += tommy_le_uint32_read(key + 0); + break; + case 7 : b += ((tommy_uint32_t)key[6]) << 16; /* fallthrough */ + case 6 : b += ((tommy_uint32_t)key[5]) << 8; /* fallthrough */ + case 5 : b += key[4]; /* fallthrough */ + case 4 : + a += tommy_le_uint32_read(key + 0); + break; + case 3 : a += ((tommy_uint32_t)key[2]) << 16; /* fallthrough */ + case 2 : a += ((tommy_uint32_t)key[1]) << 8; /* fallthrough */ + case 1 : a += key[0]; /* fallthrough */ + } + + tommy_final(a, b, c); + + return c + ((tommy_uint64_t)b << 32); +} + +tommy_uint32_t tommy_strhash_u32(tommy_uint32_t init_val, const void* void_key) +{ + const unsigned char* key = tommy_cast(const unsigned char*, void_key); + tommy_uint32_t a, b, c; + tommy_uint32_t m[3] = { 0xff, 0xff00, 0xff0000 }; + + a = b = c = 0xdeadbeef + init_val; + /* this is different than original lookup3 and the result won't match */ + + while (1) { + tommy_uint32_t v = tommy_le_uint32_read(key); + + if (tommy_haszero_u32(v)) { + if (v & m[0]) { + a += v & m[0]; + if (v & m[1]) { + a += v & m[1]; + if (v & m[2]) + a += v & m[2]; + } + } + + break; + } + + a += v; + + v = tommy_le_uint32_read(key + 4); + + if (tommy_haszero_u32(v)) { + if (v & m[0]) { + b += v & m[0]; + if (v & m[1]) { + b += v & m[1]; + if (v & m[2]) + b += v & m[2]; + } + } + + break; + } + + b += v; + + v = tommy_le_uint32_read(key + 8); + + if (tommy_haszero_u32(v)) { + if (v & m[0]) { + c += v & m[0]; + if (v & m[1]) { + c += v & m[1]; + if (v & m[2]) + c += v & m[2]; + } + } + + break; + } + + c += v; + + tommy_mix(a, b, c); + + key += 12; + } + + /* for lengths that are multiplers of 12 we already have called mix */ + /* this is different than the original lookup3 and the result won't match */ + + tommy_final(a, b, c); + + return c; +} + diff --git a/tommyDS_hashlin/tommyhash.h b/tommyDS_hashlin/tommyhash.h new file mode 100644 index 0000000..a29c160 --- /dev/null +++ b/tommyDS_hashlin/tommyhash.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** \file + * Hash functions for the use with ::tommy_hashtable, ::tommy_hashdyn and ::tommy_hashlin. + */ + +#ifndef __TOMMYHASH_H +#define __TOMMYHASH_H + +#include "tommytypes.h" + +/******************************************************************************/ +/* hash */ + +/** + * Hash function with a 32 bits result. + * Implementation of the Robert Jenkins "lookup3" hash 32 bits version, + * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle(). + * + * This hash is designed to provide a good overall performance in all platforms, + * including 32 bits. If you target only 64 bits, you can use faster hashes, + * like SpookyHash or FarmHash. + * + * \param init_val Initialization value. + * Using a different initialization value, you can generate a completely different set of hash values. + * Use 0 if not relevant. + * \param void_key Pointer to the data to hash. + * \param key_len Size of the data to hash. + * \note + * This function is endianess independent. + * \return The hash value of 32 bits. + */ +tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len); + +/** + * Hash function with a 64 bits result. + * Implementation of the Robert Jenkins "lookup3" hash 64 bits versions, + * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle2(). + * + * This hash is designed to provide a good overall performance in all platforms, + * including 32 bits. If you target only 64 bits, you can use faster hashes, + * like SpookyHash or FarmHash. + * + * \param init_val Initialization value. + * Using a different initialization value, you can generate a completely different set of hash values. + * Use 0 if not relevant. + * \param void_key Pointer to the data to hash. + * \param key_len Size of the data to hash. + * \note + * This function is endianess independent. + * \return The hash value of 64 bits. + */ +tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len); + +/** + * String hash function with a 32 bits result. + * Implementation is based on the the Robert Jenkins "lookup3" hash 32 bits version, + * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle(). + * + * This hash is designed to handle strings with an unknown length. If you + * know the string length, the other hash functions are surely faster. + * + * \param init_val Initialization value. + * Using a different initialization value, you can generate a completely different set of hash values. + * Use 0 if not relevant. + * \param void_key Pointer to the string to hash. It has to be 0 terminated. + * \note + * This function is endianess independent. + * \return The hash value of 32 bits. + */ +tommy_uint32_t tommy_strhash_u32(tommy_uint32_t init_val, const void* void_key); + +/** + * Integer reversible hash function for 32 bits. + * Implementation of the Robert Jenkins "4-byte Integer Hashing", + * from http://burtleburtle.net/bob/hash/integer.html + */ +tommy_inline tommy_uint32_t tommy_inthash_u32(tommy_uint32_t key) +{ + key -= key << 6; + key ^= key >> 17; + key -= key << 9; + key ^= key << 4; + key -= key << 3; + key ^= key << 10; + key ^= key >> 15; + + return key; +} + +/** + * Integer reversible hash function for 64 bits. + * Implementation of the Thomas Wang "Integer Hash Function", + * from http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm + */ +tommy_inline tommy_uint64_t tommy_inthash_u64(tommy_uint64_t key) +{ + key = ~key + (key << 21); + key = key ^ (key >> 24); + key = key + (key << 3) + (key << 8); + key = key ^ (key >> 14); + key = key + (key << 2) + (key << 4); + key = key ^ (key >> 28); + key = key + (key << 31); + + return key; +} + +#endif + diff --git a/tommyDS_hashlin/tommyhashlin.c b/tommyDS_hashlin/tommyhashlin.c new file mode 100644 index 0000000..911a69b --- /dev/null +++ b/tommyDS_hashlin/tommyhashlin.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "tommyhashlin.h" +#include "tommylist.h" + +#include /* for assert */ + +/******************************************************************************/ +/* hashlin */ + +/** + * Reallocation states. + */ +#define TOMMY_HASHLIN_STATE_STABLE 0 +#define TOMMY_HASHLIN_STATE_GROW 1 +#define TOMMY_HASHLIN_STATE_SHRINK 2 + +/** + * Set the hashtable in stable state. + */ +tommy_inline void tommy_hashlin_stable(tommy_hashlin* hashlin) +{ + hashlin->state = TOMMY_HASHLIN_STATE_STABLE; + + /* setup low_mask/max/split to allow tommy_hashlin_bucket_ref() */ + /* and tommy_hashlin_foreach() to work regardless we are in stable state */ + hashlin->low_max = hashlin->bucket_max; + hashlin->low_mask = hashlin->bucket_mask; + hashlin->split = 0; +} + +void tommy_hashlin_init(tommy_hashlin* hashlin) +{ + tommy_uint_t i; + + /* fixed initial size */ + hashlin->bucket_bit = TOMMY_HASHLIN_BIT; + hashlin->bucket_max = 1 << hashlin->bucket_bit; + hashlin->bucket_mask = hashlin->bucket_max - 1; + hashlin->bucket[0] = tommy_cast(tommy_hashlin_node**, tommy_calloc(hashlin->bucket_max, sizeof(tommy_hashlin_node*))); + for (i = 1; i < TOMMY_HASHLIN_BIT; ++i) + hashlin->bucket[i] = hashlin->bucket[0]; + + /* stable state */ + tommy_hashlin_stable(hashlin); + + hashlin->count = 0; +} + +void tommy_hashlin_done(tommy_hashlin* hashlin) +{ + tommy_uint_t i; + + tommy_free(hashlin->bucket[0]); + for (i = TOMMY_HASHLIN_BIT; i < hashlin->bucket_bit; ++i) { + tommy_hashlin_node** segment = hashlin->bucket[i]; + tommy_free(&segment[((tommy_ptrdiff_t)1) << i]); + } +} + +/** + * Grow one step. + */ +tommy_inline void hashlin_grow_step(tommy_hashlin* hashlin) +{ + /* grow if more than 50% full */ + if (hashlin->state != TOMMY_HASHLIN_STATE_GROW + && hashlin->count > hashlin->bucket_max / 2 + ) { + /* if we are stable, setup a new grow state */ + /* otherwise continue with the already setup shrink one */ + /* but in backward direction */ + if (hashlin->state == TOMMY_HASHLIN_STATE_STABLE) { + tommy_hashlin_node** segment; + + /* set the lower size */ + hashlin->low_max = hashlin->bucket_max; + hashlin->low_mask = hashlin->bucket_mask; + + /* allocate the new vector using malloc() and not calloc() */ + /* because data is fully initialized in the split process */ + segment = tommy_cast(tommy_hashlin_node**, tommy_malloc(hashlin->low_max * sizeof(tommy_hashlin_node*))); + + /* store it adjusting the offset */ + /* cast to ptrdiff_t to ensure to get a negative value */ + hashlin->bucket[hashlin->bucket_bit] = &segment[-(tommy_ptrdiff_t)hashlin->low_max]; + + /* grow the hash size */ + ++hashlin->bucket_bit; + hashlin->bucket_max = 1 << hashlin->bucket_bit; + hashlin->bucket_mask = hashlin->bucket_max - 1; + + /* start from the beginning going forward */ + hashlin->split = 0; + } + + /* grow state */ + hashlin->state = TOMMY_HASHLIN_STATE_GROW; + } + + /* if we are growing */ + if (hashlin->state == TOMMY_HASHLIN_STATE_GROW) { + /* compute the split target required to finish the reallocation before the next resize */ + tommy_size_t split_target = 2 * hashlin->count; + + /* reallocate buckets until the split target */ + while (hashlin->split + hashlin->low_max < split_target) { + tommy_hashlin_node** split[2]; + tommy_hashlin_node* j; + tommy_size_t mask; + + /* get the low bucket */ + split[0] = tommy_hashlin_pos(hashlin, hashlin->split); + + /* get the high bucket */ + split[1] = tommy_hashlin_pos(hashlin, hashlin->split + hashlin->low_max); + + /* save the low bucket */ + j = *split[0]; + + /* reinitialize the buckets */ + *split[0] = 0; + *split[1] = 0; + + /* the bit used to identify the bucket */ + mask = hashlin->low_max; + + /* flush the bucket */ + while (j) { + tommy_hashlin_node* j_next = j->next; + tommy_size_t pos = (j->index & mask) != 0; + if (*split[pos]) + tommy_list_insert_tail_not_empty(*split[pos], j); + else + tommy_list_insert_first(split[pos], j); + j = j_next; + } + + /* go forward */ + ++hashlin->split; + + /* if we have finished, change the state */ + if (hashlin->split == hashlin->low_max) { + /* go in stable mode */ + tommy_hashlin_stable(hashlin); + break; + } + } + } +} + +/** + * Shrink one step. + */ +tommy_inline void hashlin_shrink_step(tommy_hashlin* hashlin) +{ + /* shrink if less than 12.5% full */ + if (hashlin->state != TOMMY_HASHLIN_STATE_SHRINK + && hashlin->count < hashlin->bucket_max / 8 + ) { + /* avoid to shrink the first bucket */ + if (hashlin->bucket_bit > TOMMY_HASHLIN_BIT) { + /* if we are stable, setup a new shrink state */ + /* otherwise continue with the already setup grow one */ + /* but in backward direction */ + if (hashlin->state == TOMMY_HASHLIN_STATE_STABLE) { + /* set the lower size */ + hashlin->low_max = hashlin->bucket_max / 2; + hashlin->low_mask = hashlin->bucket_mask / 2; + + /* start from the half going backward */ + hashlin->split = hashlin->low_max; + } + + /* start reallocation */ + hashlin->state = TOMMY_HASHLIN_STATE_SHRINK; + } + } + + /* if we are shrinking */ + if (hashlin->state == TOMMY_HASHLIN_STATE_SHRINK) { + /* compute the split target required to finish the reallocation before the next resize */ + tommy_size_t split_target = 8 * hashlin->count; + + /* reallocate buckets until the split target */ + while (hashlin->split + hashlin->low_max > split_target) { + tommy_hashlin_node** split[2]; + + /* go backward position */ + --hashlin->split; + + /* get the low bucket */ + split[0] = tommy_hashlin_pos(hashlin, hashlin->split); + + /* get the high bucket */ + split[1] = tommy_hashlin_pos(hashlin, hashlin->split + hashlin->low_max); + + /* concat the high bucket into the low one */ + tommy_list_concat(split[0], split[1]); + + /* if we have finished, clean up and change the state */ + if (hashlin->split == 0) { + tommy_hashlin_node** segment; + + /* shrink the hash size */ + --hashlin->bucket_bit; + hashlin->bucket_max = 1 << hashlin->bucket_bit; + hashlin->bucket_mask = hashlin->bucket_max - 1; + + /* free the last segment */ + segment = hashlin->bucket[hashlin->bucket_bit]; + tommy_free(&segment[((tommy_ptrdiff_t)1) << hashlin->bucket_bit]); + + /* go in stable mode */ + tommy_hashlin_stable(hashlin); + break; + } + } + } +} + +void tommy_hashlin_insert(tommy_hashlin* hashlin, tommy_hashlin_node* node, void* data, tommy_hash_t hash) +{ + tommy_list_insert_tail(tommy_hashlin_bucket_ref(hashlin, hash), node, data); + + node->index = hash; + + ++hashlin->count; + + hashlin_grow_step(hashlin); +} + +void tommy_hashlin_remove_existing(tommy_hashlin* hashlin, tommy_hashlin_node* node) +{ + tommy_list_remove_existing(tommy_hashlin_bucket_ref(hashlin, node->index), node); + + --hashlin->count; + + hashlin_shrink_step(hashlin); +} + +void* tommy_hashlin_remove(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) +{ + tommy_hashlin_node** let_ptr = tommy_hashlin_bucket_ref(hashlin, hash); + tommy_hashlin_node* node = *let_ptr; + + while (node) { + /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ + if (node->index == hash && cmp(cmp_arg, node->data) == 0) { + tommy_list_remove_existing(let_ptr, node); + + --hashlin->count; + + hashlin_shrink_step(hashlin); + + return node->data; + } + node = node->next; + } + + return 0; +} + +void tommy_hashlin_foreach(tommy_hashlin* hashlin, tommy_foreach_func* func) +{ + tommy_size_t bucket_max; + tommy_size_t pos; + + /* number of valid buckets */ + bucket_max = hashlin->low_max + hashlin->split; + + for (pos = 0; pos < bucket_max; ++pos) { + tommy_hashlin_node* node = *tommy_hashlin_pos(hashlin, pos); + + while (node) { + void* data = node->data; + node = node->next; + func(data); + } + } +} + +void tommy_hashlin_foreach_arg(tommy_hashlin* hashlin, tommy_foreach_arg_func* func, void* arg) +{ + tommy_size_t bucket_max; + tommy_size_t pos; + + /* number of valid buckets */ + bucket_max = hashlin->low_max + hashlin->split; + + for (pos = 0; pos < bucket_max; ++pos) { + tommy_hashlin_node* node = *tommy_hashlin_pos(hashlin, pos); + + while (node) { + void* data = node->data; + node = node->next; + func(arg, data); + } + } +} + +tommy_size_t tommy_hashlin_memory_usage(tommy_hashlin* hashlin) +{ + return hashlin->bucket_max * (tommy_size_t)sizeof(hashlin->bucket[0][0]) + + hashlin->count * (tommy_size_t)sizeof(tommy_hashlin_node); +} + diff --git a/tommyDS_hashlin/tommyhashlin.h b/tommyDS_hashlin/tommyhashlin.h new file mode 100644 index 0000000..dd01a63 --- /dev/null +++ b/tommyDS_hashlin/tommyhashlin.h @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** \file + * Linear chained hashtable. + * + * This hashtable resizes dynamically and progressively using a variation of the + * linear hashing algorithm described in http://en.wikipedia.org/wiki/Linear_hashing + * + * It starts with the minimal size of 16 buckets, it doubles the size then it + * reaches a load factor greater than 0.5 and it halves the size with a load + * factor lower than 0.125. + * + * The progressive resize is good for real-time and interactive applications + * as it makes insert and delete operations taking always the same time. + * + * For resizing it's used a dynamic array that supports access to not contigous + * segments. + * In this way we only allocate additional table segments on the heap, without + * freeing the previous table, and then not increasing the heap fragmentation. + * + * The resize takes place inside tommy_hashlin_insert() and tommy_hashlin_remove(). + * No resize is done in the tommy_hashlin_search() operation. + * + * To initialize the hashtable you have to call tommy_hashlin_init(). + * + * \code + * tommy_hashslin hashlin; + * + * tommy_hashlin_init(&hashlin); + * \endcode + * + * To insert elements in the hashtable you have to call tommy_hashlin_insert() for + * each element. + * In the insertion call you have to specify the address of the node, the + * address of the object, and the hash value of the key to use. + * The address of the object is used to initialize the tommy_node::data field + * of the node, and the hash to initialize the tommy_node::key field. + * + * \code + * struct object { + * int value; + * // other fields + * tommy_node node; + * }; + * + * struct object* obj = malloc(sizeof(struct object)); // creates the object + * + * obj->value = ...; // initializes the object + * + * tommy_hashlin_insert(&hashlin, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object + * \endcode + * + * To find and element in the hashtable you have to call tommy_hashtable_search() + * providing a comparison function, its argument, and the hash of the key to search. + * + * \code + * int compare(const void* arg, const void* obj) + * { + * return *(const int*)arg != ((const struct object*)obj)->value; + * } + * + * int value_to_find = 1; + * struct object* obj = tommy_hashlin_search(&hashlin, compare, &value_to_find, tommy_inthash_u32(value_to_find)); + * if (!obj) { + * // not found + * } else { + * // found + * } + * \endcode + * + * To iterate over all the elements in the hashtable with the same key, you have to + * use tommy_hashlin_bucket() and follow the tommy_node::next pointer until NULL. + * You have also to check explicitely for the key, as the bucket may contains + * different keys. + * + * \code + * int value_to_find = 1; + * tommy_node* i = tommy_hashlin_bucket(&hashlin, tommy_inthash_u32(value_to_find)); + * while (i) { + * struct object* obj = i->data; // gets the object pointer + * + * if (obj->value == value_to_find) { + * printf("%d\n", obj->value); // process the object + * } + * + * i = i->next; // goes to the next element + * } + * \endcode + * + * To remove an element from the hashtable you have to call tommy_hashlin_remove() + * providing a comparison function, its argument, and the hash of the key to search + * and remove. + * + * \code + * struct object* obj = tommy_hashlin_remove(&hashlin, compare, &value_to_remove, tommy_inthash_u32(value_to_remove)); + * if (obj) { + * free(obj); // frees the object allocated memory + * } + * \endcode + * + * To destroy the hashtable you have to remove all the elements, and deinitialize + * the hashtable calling tommy_hashlin_done(). + * + * \code + * tommy_hashlin_done(&hashlin); + * \endcode + * + * If you need to iterate over all the elements in the hashtable, you can use + * tommy_hashlin_foreach() or tommy_hashlin_foreach_arg(). + * If you need a more precise control with a real iteration, you have to insert + * all the elements also in a ::tommy_list, and use the list to iterate. + * See the \ref multiindex example for more detail. + */ + +#ifndef __TOMMYHASHLIN_H +#define __TOMMYHASHLIN_H + +#include "tommyhash.h" + +/******************************************************************************/ +/* hashlin */ + +/** \internal + * Initial and minimal size of the hashtable expressed as a power of 2. + * The initial size is 2^TOMMY_HASHLIN_BIT. + */ +#define TOMMY_HASHLIN_BIT 6 + +/** + * Hashtable node. + * This is the node that you have to include inside your objects. + */ +typedef tommy_node tommy_hashlin_node; + +/** + * Hashtable container type. + * \note Don't use internal fields directly, but access the container only using functions. + */ +typedef struct tommy_hashlin_struct { + tommy_hashlin_node** bucket[TOMMY_SIZE_BIT]; /**< Dynamic array of hash buckets. One list for each hash modulus. */ + tommy_size_t bucket_max; /**< Number of buckets. */ + tommy_size_t bucket_mask; /**< Bit mask to access the buckets. */ + tommy_size_t low_max; /**< Low order max value. */ + tommy_size_t low_mask; /**< Low order mask value. */ + tommy_size_t split; /**< Split position. */ + tommy_size_t count; /**< Number of elements. */ + tommy_uint_t bucket_bit; /**< Bits used in the bit mask. */ + tommy_uint_t state; /**< Reallocation state. */ +} tommy_hashlin; + +/** + * Initializes the hashtable. + */ +void tommy_hashlin_init(tommy_hashlin* hashlin); + +/** + * Deinitializes the hashtable. + * + * You can call this function with elements still contained, + * but such elements are not going to be freed by this call. + */ +void tommy_hashlin_done(tommy_hashlin* hashlin); + +/** + * Inserts an element in the hashtable. + */ +void tommy_hashlin_insert(tommy_hashlin* hashlin, tommy_hashlin_node* node, void* data, tommy_hash_t hash); + +/** + * Searches and removes an element from the hashtable. + * You have to provide a compare function and the hash of the element you want to remove. + * If the element is not found, 0 is returned. + * If more equal elements are present, the first one is removed. + * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. + * The function should return 0 for equal elements, anything other for different elements. + * \param cmp_arg Compare argument passed as first argument of the compare function. + * \param hash Hash of the element to find and remove. + * \return The removed element, or 0 if not found. + */ +void* tommy_hashlin_remove(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash); + +/** \internal + * Returns the bucket at the specified position. + */ +tommy_inline tommy_hashlin_node** tommy_hashlin_pos(tommy_hashlin* hashlin, tommy_hash_t pos) +{ + tommy_uint_t bsr; + + /* get the highest bit set, in case of all 0, return 0 */ + bsr = tommy_ilog2(pos | 1); + + return &hashlin->bucket[bsr][pos]; +} + +/** \internal + * Returns a pointer to the bucket of the specified hash. + */ +tommy_inline tommy_hashlin_node** tommy_hashlin_bucket_ref(tommy_hashlin* hashlin, tommy_hash_t hash) +{ + tommy_size_t pos; + tommy_size_t high_pos; + + pos = hash & hashlin->low_mask; + high_pos = hash & hashlin->bucket_mask; + + /* if this position is already allocated in the high half */ + if (pos < hashlin->split) { + /* The following assigment is expected to be implemented */ + /* with a conditional move instruction */ + /* that results in a little better and constant performance */ + /* regardless of the split position. */ + /* This affects mostly the worst case, when the split value */ + /* is near at its half, resulting in a totally unpredictable */ + /* condition by the CPU. */ + /* In such case the use of the conditional move is generally faster. */ + + /* use also the high bit */ + pos = high_pos; + } + + return tommy_hashlin_pos(hashlin, pos); +} + +/** + * Gets the bucket of the specified hash. + * The bucket is guaranteed to contain ALL the elements with the specified hash, + * but it can contain also others. + * You can access elements in the bucket following the ::next pointer until 0. + * \param hash Hash of the element to find. + * \return The head of the bucket, or 0 if empty. + */ +tommy_inline tommy_hashlin_node* tommy_hashlin_bucket(tommy_hashlin* hashlin, tommy_hash_t hash) +{ + return *tommy_hashlin_bucket_ref(hashlin, hash); +} + +/** + * Searches an element in the hashtable. + * You have to provide a compare function and the hash of the element you want to find. + * If more equal elements are present, the first one is returned. + * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. + * The function should return 0 for equal elements, anything other for different elements. + * \param cmp_arg Compare argument passed as first argument of the compare function. + * \param hash Hash of the element to find. + * \return The first element found, or 0 if none. + */ +tommy_inline void* tommy_hashlin_search(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) +{ + tommy_hashlin_node* i = tommy_hashlin_bucket(hashlin, hash); + + while (i) { + /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ + if (i->index == hash && cmp(cmp_arg, i->data) == 0) + return i->data; + i = i->next; + } + return 0; +} + +/** + * Removes an element from the hashtable. + * You must already have the address of the element to remove. + */ +void tommy_hashlin_remove_existing(tommy_hashlin* hashlin, tommy_hashlin_node* node); + +/** + * Calls the specified function for each element in the hashtable. + * + * You cannot add or remove elements from the inside of the callback, + * but can use it to deallocate them. + * + * \code + * tommy_hashlin hashlin; + * + * // initializes the hashtable + * tommy_hashlin_init(&hashlin); + * + * ... + * + * // creates an object + * struct object* obj = malloc(sizeof(struct object)); + * + * ... + * + * // insert it in the hashtable + * tommy_hashlin_insert(&hashlin, &obj->node, obj, tommy_inthash_u32(obj->value)); + * + * ... + * + * // deallocates all the objects iterating the hashtable + * tommy_hashlin_foreach(&hashlin, free); + * + * // deallocates the hashtable + * tommy_hashlin_done(&hashlin); + * \endcode + */ +void tommy_hashlin_foreach(tommy_hashlin* hashlin, tommy_foreach_func* func); + +/** + * Calls the specified function with an argument for each element in the hashtable. + */ +void tommy_hashlin_foreach_arg(tommy_hashlin* hashlin, tommy_foreach_arg_func* func, void* arg); + +/** + * Gets the number of elements. + */ +tommy_inline tommy_size_t tommy_hashlin_count(tommy_hashlin* hashlin) +{ + return hashlin->count; +} + +/** + * Gets the size of allocated memory. + * It includes the size of the ::tommy_hashlin_node of the stored elements. + */ +tommy_size_t tommy_hashlin_memory_usage(tommy_hashlin* hashlin); + +#endif + diff --git a/tommyDS_hashlin/tommylist.c b/tommyDS_hashlin/tommylist.c new file mode 100644 index 0000000..1fa1f24 --- /dev/null +++ b/tommyDS_hashlin/tommylist.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "tommylist.h" +#include "tommychain.h" + +/** \internal + * Setup a list. + */ +tommy_inline void tommy_list_set(tommy_list* list, tommy_node* head, tommy_node* tail) +{ + head->prev = tail; + tail->next = 0; + *list = head; +} + +void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp) +{ + tommy_chain chain; + tommy_node* head; + + if (tommy_list_empty(list)) + return; + + head = tommy_list_head(list); + + /* create a chain from the list */ + chain.head = head; + chain.tail = head->prev; + + tommy_chain_mergesort(&chain, cmp); + + /* restore the list */ + tommy_list_set(list, chain.head, chain.tail); +} + diff --git a/tommyDS_hashlin/tommylist.h b/tommyDS_hashlin/tommylist.h new file mode 100644 index 0000000..fda0adf --- /dev/null +++ b/tommyDS_hashlin/tommylist.h @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** \file + * Double linked list for collisions into hashtables. + * + * This list is a double linked list mainly targetted for handling collisions + * into an hashtables, but useable also as a generic list. + * + * The main feature of this list is to require only one pointer to represent the + * list, compared to a classic implementation requiring a head an a tail pointers. + * This reduces the memory usage in hashtables. + * + * Another feature is to support the insertion at the end of the list. This allow to store + * collisions in a stable order. Where for stable order we mean that equal elements keep + * their insertion order. + * + * To initialize the list, you have to call tommy_list_init(), or to simply assign + * to it NULL, as an empty list is represented by the NULL value. + * + * \code + * tommy_list list; + * + * tommy_list_init(&list); // initializes the list + * \endcode + * + * To insert elements in the list you have to call tommy_list_insert_tail() + * or tommy_list_insert_head() for each element. + * In the insertion call you have to specify the address of the node and the + * address of the object. + * The address of the object is used to initialize the tommy_node::data field + * of the node. + * + * \code + * struct object { + * int value; + * // other fields + * tommy_node node; + * }; + * + * struct object* obj = malloc(sizeof(struct object)); // creates the object + * + * obj->value = ...; // initializes the object + * + * tommy_list_insert_tail(&list, &obj->node, obj); // inserts the object + * \endcode + * + * To iterate over all the elements in the list you have to call + * tommy_list_head() to get the head of the list and follow the + * tommy_node::next pointer until NULL. + * + * \code + * tommy_node* i = tommy_list_head(&list); + * while (i) { + * struct object* obj = i->data; // gets the object pointer + * + * printf("%d\n", obj->value); // process the object + * + * i = i->next; // go to the next element + * } + * \endcode + * + * To destroy the list you have to remove all the elements, + * as the list is completely inplace and it doesn't allocate memory. + * This can be done with the tommy_list_foreach() function. + * + * \code + * // deallocates all the objects iterating the list + * tommy_list_foreach(&list, free); + * \endcode + */ + +#ifndef __TOMMYLIST_H +#define __TOMMYLIST_H + +#include "tommytypes.h" + +/******************************************************************************/ +/* list */ + +/** + * Double linked list type. + */ +typedef tommy_node* tommy_list; + +/** + * Initializes the list. + * The list is completely inplace, so it doesn't need to be deinitialized. + */ +tommy_inline void tommy_list_init(tommy_list* list) +{ + *list = 0; +} + +/** + * Gets the head of the list. + * \return The head node. For empty lists 0 is returned. + */ +tommy_inline tommy_node* tommy_list_head(tommy_list* list) +{ + return *list; +} + +/** + * Gets the tail of the list. + * \return The tail node. For empty lists 0 is returned. + */ +tommy_inline tommy_node* tommy_list_tail(tommy_list* list) +{ + tommy_node* head = tommy_list_head(list); + + if (!head) + return 0; + + return head->prev; +} + +/** \internal + * Creates a new list with a single element. + * \param list The list to initialize. + * \param node The node to insert. + */ +tommy_inline void tommy_list_insert_first(tommy_list* list, tommy_node* node) +{ + /* one element "circular" prev list */ + node->prev = node; + + /* one element "0 terminated" next list */ + node->next = 0; + + *list = node; +} + +/** \internal + * Inserts an element at the head of a not empty list. + * The element is inserted at the head of the list. The list cannot be empty. + * \param list The list. The list cannot be empty. + * \param node The node to insert. + */ +tommy_inline void tommy_list_insert_head_not_empty(tommy_list* list, tommy_node* node) +{ + tommy_node* head = tommy_list_head(list); + + /* insert in the "circular" prev list */ + node->prev = head->prev; + head->prev = node; + + /* insert in the "0 terminated" next list */ + node->next = head; + + *list = node; +} + +/** \internal + * Inserts an element at the tail of a not empty list. + * The element is inserted at the tail of the list. The list cannot be empty. + * \param head The node at the list head. It cannot be 0. + * \param node The node to insert. + */ +tommy_inline void tommy_list_insert_tail_not_empty(tommy_node* head, tommy_node* node) +{ + /* insert in the "circular" prev list */ + node->prev = head->prev; + head->prev = node; + + /* insert in the "0 terminated" next list */ + node->next = 0; + node->prev->next = node; +} + +/** + * Inserts an element at the head of a list. + * \param node The node to insert. + * \param data The object containing the node. It's used to set the tommy_node::data field of the node. + */ +tommy_inline void tommy_list_insert_head_check(tommy_list* list, tommy_node* node) +{ + tommy_node* head = tommy_list_head(list); + + if (head) + tommy_list_insert_head_not_empty(list, node); + else + tommy_list_insert_first(list, node); +} + +/** + * Inserts an element at the tail of a list. + * \param node The node to insert. + * \param data The object containing the node. It's used to set the tommy_node::data field of the node. + */ +tommy_inline void tommy_list_insert_tail_check(tommy_list* list, tommy_node* node) +{ + tommy_node* head = tommy_list_head(list); + + if (head) + tommy_list_insert_tail_not_empty(head, node); + else + tommy_list_insert_first(list, node); +} + +/** + * Inserts an element at the head of a list and sets the data. + * \param node The node to insert. + * \param data The object containing the node. It's used to set the tommy_node::data field of the node. + */ +tommy_inline void tommy_list_insert_head(tommy_list* list, tommy_node* node, void* data) +{ + tommy_list_insert_head_check(list, node); + + node->data = data; +} + +/** + * Inserts an element at the tail of a list and sets the data. + * \param node The node to insert. + * \param data The object containing the node. It's used to set the tommy_node::data field of the node. + */ +tommy_inline void tommy_list_insert_tail(tommy_list* list, tommy_node* node, void* data) +{ + tommy_list_insert_tail_check(list, node); + + node->data = data; +} + +/** + * Removes an element from the list. + * You must already have the address of the element to remove. + * \note The node content is left unchanged, including the tommy_node::next + * and tommy_node::prev fields that still contain pointers at the list. + * \param node The node to remove. The node must be in the list. + * \return The tommy_node::data field of the node removed. + */ +tommy_inline void tommy_list_remove_existing(tommy_list* list, tommy_node* node) +{ + tommy_node* head = tommy_list_head(list); + + /* remove from the "circular" prev list */ + if (node->next) + node->next->prev = node->prev; + else + head->prev = node->prev; /* the last */ + + /* remove from the "0 terminated" next list */ + if (head == node) + *list = node->next; /* the new head, in case 0 */ + else + node->prev->next = node->next; +} + +/** + * Concats two lists. + * The second list is concatenated at the first list. + * \param first The first list. + * \param second The second list. After this call the list content is undefined, + * and you should not use it anymore. + */ +tommy_inline void tommy_list_concat(tommy_list* first, tommy_list* second) +{ + tommy_node* first_head; + tommy_node* first_tail; + tommy_node* second_head; + + /* if the second is empty, nothing to do */ + second_head = tommy_list_head(second); + if (second_head == 0) + return; + + /* if the first is empty, copy the second */ + first_head = tommy_list_head(first); + if (first_head == 0) { + *first = *second; + return; + } + + /* tail of the first list */ + first_tail = first_head->prev; + + /* set the "circular" prev list */ + first_head->prev = second_head->prev; + second_head->prev = first_tail; + + /* set the "0 terminated" next list */ + first_tail->next = second_head; +} + +/** + * Sorts a list. + * It's a stable merge sort with O(N*log(N)) worst complexity. + * It's faster on degenerated cases like partially ordered lists. + * \param cmp Compare function called with two elements. + * The function should return <0 if the first element is less than the second, ==0 if equal, and >0 if greather. + */ +void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp); + +/** + * Checks if empty. + * \return If the list is empty. + */ +tommy_inline tommy_bool_t tommy_list_empty(tommy_list* list) +{ + return tommy_list_head(list) == 0; +} + +/** + * Gets the number of elements. + * \note This operation is O(n). + */ +tommy_inline tommy_size_t tommy_list_count(tommy_list* list) +{ + tommy_size_t count = 0; + tommy_node* i = tommy_list_head(list); + + while (i) { + ++count; + i = i->next; + } + + return count; +} + +/** + * Calls the specified function for each element in the list. + * + * You cannot add or remove elements from the inside of the callback, + * but can use it to deallocate them. + * + * \code + * tommy_list list; + * + * // initializes the list + * tommy_list_init(&list); + * + * ... + * + * // creates an object + * struct object* obj = malloc(sizeof(struct object)); + * + * ... + * + * // insert it in the list + * tommy_list_insert_tail(&list, &obj->node, obj); + * + * ... + * + * // deallocates all the objects iterating the list + * tommy_list_foreach(&list, free); + * \endcode + */ +tommy_inline void tommy_list_foreach(tommy_list* list, tommy_foreach_func* func) +{ + tommy_node* node = tommy_list_head(list); + + while (node) { + void* data = node->data; + node = node->next; + func(data); + } +} + +/** + * Calls the specified function with an argument for each element in the list. + */ +tommy_inline void tommy_list_foreach_arg(tommy_list* list, tommy_foreach_arg_func* func, void* arg) +{ + tommy_node* node = tommy_list_head(list); + + while (node) { + void* data = node->data; + node = node->next; + func(arg, data); + } +} + +#endif + diff --git a/tommyDS_hashlin/tommytypes.h b/tommyDS_hashlin/tommytypes.h new file mode 100644 index 0000000..49b6a47 --- /dev/null +++ b/tommyDS_hashlin/tommytypes.h @@ -0,0 +1,518 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** \file + * Generic types. + */ + +#ifndef __TOMMYTYPES_H +#define __TOMMYTYPES_H + +/******************************************************************************/ +/* types */ + +#include + +#ifdef _MSC_VER +typedef unsigned tommy_uint32_t; /**< Generic uint32_t type. */ +typedef unsigned _int64 tommy_uint64_t; /**< Generic uint64_t type. */ +typedef size_t tommy_uintptr_t; /**< Generic uintptr_t type. */ +#ifdef _WIN64 +#define TOMMY_SIZE_BIT 64 +typedef unsigned _int64_t tommy_size_t; /**< Generic size_t type. */ +typedef _int64_t tommy_ssize_t; /**< Generic ssize_t type. */ +#else +#define TOMMY_SIZE_BIT 32 +typedef unsigned tommy_size_t; /**< Generic size_t type. */ +typedef int tommy_ssize_t; /**< Generic ssize_t type. */ +#endif +#else +#include +typedef uint32_t tommy_uint32_t; /**< Generic uint32_t type. */ +typedef uint64_t tommy_uint64_t; /**< Generic uint64_t type. */ +typedef uintptr_t tommy_uintptr_t; /**< Generic uintptr_t type. */ +#if SIZE_MAX == UINT64_MAX +#define TOMMY_SIZE_BIT 64 +typedef uint64_t tommy_size_t; /**< Generic size_t type. */ +typedef int64_t tommy_ssize_t; /**< Generic ssize_t type. */ +#elif SIZE_MAX == UINT32_MAX +#define TOMMY_SIZE_BIT 32 +typedef uint32_t tommy_size_t; /**< Generic size_t type. */ +typedef int32_t tommy_ssize_t; /**< Generic ssize_t type. */ +#else +#error Unsupported SIZE_MAX +#endif +#endif + +typedef ptrdiff_t tommy_ptrdiff_t; /**< Generic ptrdiff_t type. */ +typedef int tommy_bool_t; /**< Generic boolean type. */ + +/** + * Generic unsigned integer type. + * + * It has no specific size, as is used to store only small values. + * To make the code more efficient, a full 32 bit integer is used. + */ +typedef tommy_uint32_t tommy_uint_t; + +/** \internal + * Type cast required for the C++ compilation. + * When compiling in C++ we cannot convert a void* pointer to another pointer. + * In such case we need an explicit cast. + */ +#ifdef __cplusplus +#define tommy_cast(type, value) static_cast(value) +#else +#define tommy_cast(type, value) ((type)value) +#endif + +/******************************************************************************/ +/* heap */ + +/* by default uses malloc/calloc/realloc/free */ + +/** + * Generic malloc(), calloc(), realloc() and free() functions. + * Redefine them to what you need. By default they map to the C malloc(), calloc(), realloc() and free(). + */ +#if !defined(tommy_malloc) || !defined(tommy_calloc) || !defined(tommy_realloc) || !defined(tommy_free) +#include +#endif +#if !defined(tommy_malloc) +#define tommy_malloc malloc +#endif +#if !defined(tommy_calloc) +#define tommy_calloc calloc +#endif +#if !defined(tommy_realloc) +#define tommy_realloc realloc +#endif +#if !defined(tommy_free) +#define tommy_free free +#endif + +/******************************************************************************/ +/* modificators */ + +/** \internal + * Definition of the inline keyword if available. + */ +#if !defined(tommy_inline) +#if defined(_MSC_VER) || defined(__GNUC__) +#define tommy_inline static __inline +#else +#define tommy_inline static +#endif +#endif + +/** \internal + * Definition of the restrict keyword if available. + */ +#if !defined(tommy_restrict) +#if __STDC_VERSION__ >= 199901L +#define tommy_restrict restrict +#elif defined(_MSC_VER) || defined(__GNUC__) +#define tommy_restrict __restrict +#else +#define tommy_restrict +#endif +#endif + +/** \internal + * Hints the compiler that a condition is likely true. + */ +#if !defined(tommy_likely) +#if defined(__GNUC__) +#define tommy_likely(x) __builtin_expect(!!(x), 1) +#else +#define tommy_likely(x) (x) +#endif +#endif + +/** \internal + * Hints the compiler that a condition is likely false. + */ +#if !defined(tommy_unlikely) +#if defined(__GNUC__) +#define tommy_unlikely(x) __builtin_expect(!!(x), 0) +#else +#define tommy_unlikely(x) (x) +#endif +#endif + +/******************************************************************************/ +/* key/hash */ + +/** + * Type used in indexed data structures to store the key of a object. + */ +typedef tommy_size_t tommy_key_t; + +/** + * Type used in hashtables to store the hash of a object. + */ +typedef tommy_size_t tommy_hash_t; + +/******************************************************************************/ +/* node */ + +/** + * Data structure node. + * This node type is shared between all the data structures and used to store some + * info directly into the objects you want to store. + * + * A typical declaration is: + * \code + * struct object { + * tommy_node node; + * // other fields + * }; + * \endcode + */ +typedef struct tommy_node_struct { + /** + * Next node. + * The tail node has it at 0, like a 0 terminated list. + */ + struct tommy_node_struct* next; + + /** + * Previous node. + * The head node points to the tail node, like a circular list. + */ + struct tommy_node_struct* prev; + + /** + * Pointer to the object containing the node. + * This field is initialized when inserting nodes into a data structure. + */ + void* data; + + /** + * Index of the node. + * With tries this field is used to store the key. + * With hashtables this field is used to store the hash value. + * With lists this field is not used. + */ + tommy_size_t index; +} tommy_node; + +/******************************************************************************/ +/* compare */ + +/** + * Compare function for elements. + * \param obj_a Pointer to the first object to compare. + * \param obj_b Pointer to the second object to compare. + * \return <0 if the first element is less than the second, ==0 equal, >0 if greather. + * + * This function is like the C strcmp(). + * + * \code + * struct object { + * tommy_node node; + * int value; + * }; + * + * int compare(const void* obj_a, const void* obj_b) + * { + * if (((const struct object*)obj_a)->value < ((const struct object*)obj_b)->value) + * return -1; + * if (((const struct object*)obj_a)->value > ((const struct object*)obj_b)->value) + * return 1; + * return 0; + * } + * + * tommy_list_sort(&list, compare); + * \endcode + * + */ +typedef int tommy_compare_func(const void* obj_a, const void* obj_b); + +/** + * Search function for elements. + * \param arg Pointer to the value to search as passed at the search function. + * \param obj Pointer to the object to compare to. + * \return ==0 if the value matches the element. !=0 if different. + * + * The first argument is a pointer to the value to search exactly + * as it's passed at the search function called. + * The second argument is a pointer to the object inside the hashtable to compare. + * + * The return value has to be 0 if the values are equal. != 0 if they are different. + * + * \code + * struct object { + * tommy_node node; + * int value; + * }; + * + * int compare(const void* arg, const void* obj) + * { + * const int* value_to_find = arg; + * const struct object* object_to_compare = obj; + * + * return *value_to_find != object_to_compare->value; + * } + * + * int value_to_find = 1; + * struct object* obj = tommy_hashtable_search(&hashtable, compare, &value_to_find, tommy_inthash_u32(value_to_find)); + * if (!obj) { + * // not found + * } else { + * // found + * } + * \endcode + * + */ +typedef int tommy_search_func(const void* arg, const void* obj); + +/** + * Foreach function. + * \param obj Pointer to the object to iterate. + * + * A typical example is to use free() to deallocate all the objects in a list. + * \code + * tommy_list_foreach(&list, (tommy_foreach_func*)free); + * \endcode + */ +typedef void tommy_foreach_func(void* obj); + +/** + * Foreach function with an argument. + * \param arg Pointer to a generic argument. + * \param obj Pointer to the object to iterate. + */ +typedef void tommy_foreach_arg_func(void* arg, void* obj); + +/******************************************************************************/ +/* bit hacks */ + +#if defined(_MSC_VER) && !defined(__cplusplus) +#include +#pragma intrinsic(_BitScanReverse) +#pragma intrinsic(_BitScanForward) +#if TOMMY_SIZE_BIT == 64 +#pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_BitScanForward64) +#endif +#endif + +/** \internal + * Integer log2 for constants. + * You can use it only for exact power of 2 up to 256. + */ +#define TOMMY_ILOG2(value) ((value) == 256 ? 8 : (value) == 128 ? 7 : (value) == 64 ? 6 : (value) == 32 ? 5 : (value) == 16 ? 4 : (value) == 8 ? 3 : (value) == 4 ? 2 : (value) == 2 ? 1 : 0) + +/** + * Bit scan reverse or integer log2. + * Return the bit index of the most significant 1 bit. + * + * If no bit is set, the result is undefined. + * To force a return 0 in this case, you can use tommy_ilog2_u32(value | 1). + * + * Other interesting ways for bitscan are at: + * + * Bit Twiddling Hacks + * http://graphics.stanford.edu/~seander/bithacks.html + * + * Chess Programming BitScan + * http://chessprogramming.wikispaces.com/BitScan + * + * \param value Value to scan. 0 is not allowed. + * \return The index of the most significant bit set. + */ +tommy_inline tommy_uint_t tommy_ilog2_u32(tommy_uint32_t value) +{ +#if defined(_MSC_VER) + unsigned long count; + _BitScanReverse(&count, value); + return count; + /* #elif defined(__GNUC__) + * + * GCC implements __builtin_clz(x) as "__builtin_clz(x) = bsr(x) ^ 31" + * + * Where "x ^ 31 = 31 - x", but gcc does not optimize "31 - __builtin_clz(x)" to bsr(x), + * but generates 31 - (bsr(x) xor 31). + * + * So we write "__builtin_clz(x) ^ 31" instead of "31 - __builtin_clz(x)", + * to allow the double xor to be optimized out. + * + * return __builtin_clz(value) ^ 31; */ +#else + /* Find the log base 2 of an N-bit integer in O(lg(N)) operations with multiply and lookup */ + /* from http://graphics.stanford.edu/~seander/bithacks.html */ + static unsigned char TOMMY_DE_BRUIJN_INDEX_ILOG2[32] = { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + + return TOMMY_DE_BRUIJN_INDEX_ILOG2[(tommy_uint32_t)(value * 0x07C4ACDDU) >> 27]; +#endif +} + +#if TOMMY_SIZE_BIT == 64 +/** + * Bit scan reverse or integer log2 for 64 bits. + */ +tommy_inline tommy_uint_t tommy_ilog2_u64(tommy_uint64_t value) +{ +#if defined(_MSC_VER) + unsigned long count; + _BitScanReverse64(&count, value); + return count; +#elif defined(__GNUC__) + return __builtin_clzll(value) ^ 63; +#else + uint32_t l = value & 0xFFFFFFFFU; + uint32_t h = value >> 32; + if (h) + return tommy_ilog2_u32(h) + 32; + else + return tommy_ilog2_u32(l); +#endif +} +#endif + +/** + * Bit scan forward or trailing zero count. + * Return the bit index of the least significant 1 bit. + * + * If no bit is set, the result is undefined. + * \param value Value to scan. 0 is not allowed. + * \return The index of the least significant bit set. + */ +tommy_inline tommy_uint_t tommy_ctz_u32(tommy_uint32_t value) +{ +#if defined(_MSC_VER) + unsigned long count; + _BitScanForward(&count, value); + return count; +#elif defined(__GNUC__) + return __builtin_ctz(value); +#else + /* Count the consecutive zero bits (trailing) on the right with multiply and lookup */ + /* from http://graphics.stanford.edu/~seander/bithacks.html */ + static const unsigned char TOMMY_DE_BRUIJN_INDEX_CTZ[32] = { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 + }; + + return TOMMY_DE_BRUIJN_INDEX_CTZ[(tommy_uint32_t)(((value & - value) * 0x077CB531U)) >> 27]; +#endif +} + +#if TOMMY_SIZE_BIT == 64 +/** + * Bit scan forward or trailing zero count for 64 bits. + */ +tommy_inline tommy_uint_t tommy_ctz_u64(tommy_uint64_t value) +{ +#if defined(_MSC_VER) + unsigned long count; + _BitScanForward64(&count, value); + return count; +#elif defined(__GNUC__) + return __builtin_ctzll(value); +#else + uint32_t l = value & 0xFFFFFFFFU; + uint32_t h = value >> 32; + if (l) + return tommy_ctz_u32(l); + else + return tommy_ctz_u32(h) + 32; +#endif +} +#endif + +/** + * Rounds up to the next power of 2. + * For the value 0, the result is undefined. + * \return The smallest power of 2 not less than the specified value. + */ +tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value) +{ + /* Round up to the next highest power of 2 */ + /* from http://graphics.stanford.edu/~seander/bithacks.html */ + + --value; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + ++value; + + return value; +} + +/** + * Rounds up to the next power of 2 for 64 bits. + */ +tommy_inline tommy_uint64_t tommy_roundup_pow2_u64(tommy_uint64_t value) +{ + --value; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + ++value; + + return value; +} + +/** + * Check if the specified word has a byte at 0. + * \return 0 or 1. + */ +tommy_inline int tommy_haszero_u32(tommy_uint32_t value) +{ + return ((value - 0x01010101) & ~value & 0x80808080) != 0; +} + +/* + * Bit depth mapping. + */ +#if TOMMY_SIZE_BIT == 64 +#define tommy_ilog2 tommy_ilog2_u64 +#define tommy_ctz tommy_ctz_u64 +#define tommy_roundup_pow2 tommy_roundup_pow2_u64 +#else +#define tommy_ilog2 tommy_ilog2_u32 +#define tommy_ctz tommy_ctz_u32 +#define tommy_roundup_pow2 tommy_roundup_pow2_u32 +#endif + +#endif + diff --git a/toolchains/toolchain-arm-coolstream.cmake b/toolchains/toolchain-arm-coolstream.cmake new file mode 100644 index 0000000..6fa90e7 --- /dev/null +++ b/toolchains/toolchain-arm-coolstream.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME Coolstream) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER arm-cx2450x-linux-gnueabi-gcc) +set (CMAKE_SYSTEM_PROCESSOR arm) diff --git a/toolchains/toolchain-arm-dockstar-openwrt.cmake b/toolchains/toolchain-arm-dockstar-openwrt.cmake new file mode 100644 index 0000000..7e75d11 --- /dev/null +++ b/toolchains/toolchain-arm-dockstar-openwrt.cmake @@ -0,0 +1,5 @@ +set (OSCAM_SYSTEM_NAME Dockstar) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER arm-openwrt-linux-uclibcgnueabi-gcc) +set (CMAKE_SYSTEM_PROCESSOR arm) + diff --git a/toolchains/toolchain-arm-friendlyarm.cmake b/toolchains/toolchain-arm-friendlyarm.cmake new file mode 100644 index 0000000..7827868 --- /dev/null +++ b/toolchains/toolchain-arm-friendlyarm.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME FriendlyARM) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER arm-none-linux-gnueabi-gcc) +set (CMAKE_SYSTEM_PROCESSOR arm) \ No newline at end of file diff --git a/toolchains/toolchain-arm-mca.cmake b/toolchains/toolchain-arm-mca.cmake new file mode 100644 index 0000000..4bd8ab9 --- /dev/null +++ b/toolchains/toolchain-arm-mca.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME MCA) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER arm-none-linux-gnueabi-gcc) +set (CMAKE_SYSTEM_PROCESSOR arm) diff --git a/toolchains/toolchain-arm-none.cmake b/toolchains/toolchain-arm-none.cmake new file mode 100644 index 0000000..68dd730 --- /dev/null +++ b/toolchains/toolchain-arm-none.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME none) +set (CMAKE_SYSTEM_NAME linux) +set (CMAKE_C_COMPILER arm-none-linux-gnueabi-gcc) +set (CMAKE_SYSTEM_PROCESSOR arm) diff --git a/toolchains/toolchain-arm-nslu2-openwrt.cmake b/toolchains/toolchain-arm-nslu2-openwrt.cmake new file mode 100644 index 0000000..1d6763f --- /dev/null +++ b/toolchains/toolchain-arm-nslu2-openwrt.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME NSLU2) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER armeb-linux-uclibc-gcc) +set (CMAKE_SYSTEM_PROCESSOR arm) diff --git a/toolchains/toolchain-arm-nslu2-unslung.cmake b/toolchains/toolchain-arm-nslu2-unslung.cmake new file mode 100644 index 0000000..6f8c473 --- /dev/null +++ b/toolchains/toolchain-arm-nslu2-unslung.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME NSLU2) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER armv5b-softfloat-linux-gcc) +set (CMAKE_SYSTEM_PROCESSOR armv5b) diff --git a/toolchains/toolchain-arm-su980.cmake b/toolchains/toolchain-arm-su980.cmake new file mode 100644 index 0000000..b12a22e --- /dev/null +++ b/toolchains/toolchain-arm-su980.cmake @@ -0,0 +1,4 @@ +set( OSCAM_SYSTEM_NAME SU980 ) +set( CMAKE_SYSTEM_NAME Linux ) +set( CMAKE_C_COMPILER arm-cortex-linux-gnueabi-gcc ) +set( CMAKE_SYSTEM_PROCESSOR arm ) diff --git a/toolchains/toolchain-arm-wrt350nv2-openwrt.cmake b/toolchains/toolchain-arm-wrt350nv2-openwrt.cmake new file mode 100644 index 0000000..edf7798 --- /dev/null +++ b/toolchains/toolchain-arm-wrt350nv2-openwrt.cmake @@ -0,0 +1,5 @@ +set (OSCAM_SYSTEM_NAME WRT350NV2) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER arm-openwrt-linux-gcc) +set (CMAKE_SYSTEM_PROCESSOR arm) + diff --git a/toolchains/toolchain-mips-agv2_w.cmake b/toolchains/toolchain-mips-agv2_w.cmake new file mode 100644 index 0000000..0861151 --- /dev/null +++ b/toolchains/toolchain-mips-agv2_w.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME agv2+w) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER mips-openwrt-linux-gcc) +set (CMAKE_SYSTEM_PROCESSOR mips) diff --git a/toolchains/toolchain-mips-azbox.cmake b/toolchains/toolchain-mips-azbox.cmake new file mode 100644 index 0000000..b17234c --- /dev/null +++ b/toolchains/toolchain-mips-azbox.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME AZBox) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER mipsel-linux-uclibc-gcc) +set (CMAKE_SYSTEM_PROCESSOR mips) diff --git a/toolchains/toolchain-mips-dir825.cmake b/toolchains/toolchain-mips-dir825.cmake new file mode 100644 index 0000000..83caac0 --- /dev/null +++ b/toolchains/toolchain-mips-dir825.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME DIR-825) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER mips-linux-gcc) +set (CMAKE_SYSTEM_PROCESSOR mips) \ No newline at end of file diff --git a/toolchains/toolchain-mips-fonera2.cmake b/toolchains/toolchain-mips-fonera2.cmake new file mode 100644 index 0000000..a34ab6f --- /dev/null +++ b/toolchains/toolchain-mips-fonera2.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME Fonera2) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER mips-linux-gcc) +set (CMAKE_SYSTEM_PROCESSOR mips) diff --git a/toolchains/toolchain-mips-tuxbox.cmake b/toolchains/toolchain-mips-tuxbox.cmake new file mode 100644 index 0000000..1c45667 --- /dev/null +++ b/toolchains/toolchain-mips-tuxbox.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME Tuxbox) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER mipsel-linux-gcc) +set (CMAKE_SYSTEM_PROCESSOR mips) diff --git a/toolchains/toolchain-mips-wrt54g.cmake b/toolchains/toolchain-mips-wrt54g.cmake new file mode 100644 index 0000000..5971076 --- /dev/null +++ b/toolchains/toolchain-mips-wrt54g.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME WRT54G) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER mipsel-linux-uclibc-gcc) +set (CMAKE_SYSTEM_PROCESSOR mips) diff --git a/toolchains/toolchain-mipsel-tuxbox-broken-pthread.cmake b/toolchains/toolchain-mipsel-tuxbox-broken-pthread.cmake new file mode 100644 index 0000000..ae6f5b5 --- /dev/null +++ b/toolchains/toolchain-mipsel-tuxbox-broken-pthread.cmake @@ -0,0 +1,5 @@ +set (OSCAM_SYSTEM_NAME Tuxbox) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER mipsel-unknown-linux-gnu-gcc) +set (CMAKE_SYSTEM_PROCESSOR mips) +add_definitions("'-Dpthread_attr_setstacksize(a,b)=/* a b */'") diff --git a/toolchains/toolchain-mipsel-tuxbox-linux-gnu.cmake b/toolchains/toolchain-mipsel-tuxbox-linux-gnu.cmake new file mode 100644 index 0000000..54a78b9 --- /dev/null +++ b/toolchains/toolchain-mipsel-tuxbox-linux-gnu.cmake @@ -0,0 +1,10 @@ +SET(CMAKE_FIND_ROOT_PATH /opt/cross/mipsel-tuxbox-linux-gnu/mipsel-tuxbox-linux-gnu/sysroot) +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +SET(OSCAM_SYSTEM_NAME Tuxbox) +SET(CMAKE_SYSTEM_NAME Linux) +SET(CMAKE_C_COMPILER mipsel-tuxbox-linux-gnu-gcc-4.8.1) +SET(CMAKE_CXX_COMPILER mipsel-tuxbox-linux-gnu-g++) +SET(CMAKE_SYSTEM_PROCESSOR mipsel) + diff --git a/toolchains/toolchain-mipsel-tuxbox.cmake b/toolchains/toolchain-mipsel-tuxbox.cmake new file mode 100644 index 0000000..c8b74ea --- /dev/null +++ b/toolchains/toolchain-mipsel-tuxbox.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME Tuxbox) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER mipsel-unknown-linux-gnu-gcc) +set (CMAKE_SYSTEM_PROCESSOR mips) diff --git a/toolchains/toolchain-powerpc-tuxbox.cmake b/toolchains/toolchain-powerpc-tuxbox.cmake new file mode 100644 index 0000000..ab76c53 --- /dev/null +++ b/toolchains/toolchain-powerpc-tuxbox.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME Tuxbox) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER powerpc-tuxbox-linux-gnu-gcc) +set (CMAKE_SYSTEM_PROCESSOR powerpc) diff --git a/toolchains/toolchain-sh4-amino.cmake b/toolchains/toolchain-sh4-amino.cmake new file mode 100644 index 0000000..e918ec1 --- /dev/null +++ b/toolchains/toolchain-sh4-amino.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME Amino) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER sh4-linux-gnu-gcc) +set (CMAKE_SYSTEM_PROCESSOR sh4) diff --git a/toolchains/toolchain-sh4-qboxhd.cmake b/toolchains/toolchain-sh4-qboxhd.cmake new file mode 100644 index 0000000..57eb7d4 --- /dev/null +++ b/toolchains/toolchain-sh4-qboxhd.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME QboxHD) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER sh4-linux-gcc) +set (CMAKE_SYSTEM_PROCESSOR sh4) diff --git a/toolchains/toolchain-sh4-tuxbox-stapi.cmake b/toolchains/toolchain-sh4-tuxbox-stapi.cmake new file mode 100644 index 0000000..d3239ce --- /dev/null +++ b/toolchains/toolchain-sh4-tuxbox-stapi.cmake @@ -0,0 +1,5 @@ +set (OSCAM_SYSTEM_NAME Tuxbox) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER sh4-linux-gcc) +set (CMAKE_SYSTEM_PROCESSOR sh4) +set (WITH_STAPI 1) diff --git a/toolchains/toolchain-sh4-tuxbox.cmake b/toolchains/toolchain-sh4-tuxbox.cmake new file mode 100644 index 0000000..f235467 --- /dev/null +++ b/toolchains/toolchain-sh4-tuxbox.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME Tuxbox) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER sh4-linux-gcc) +set (CMAKE_SYSTEM_PROCESSOR sh4) diff --git a/toolchains/toolchain-sparc-padre.cmake b/toolchains/toolchain-sparc-padre.cmake new file mode 100644 index 0000000..1990444 --- /dev/null +++ b/toolchains/toolchain-sparc-padre.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME Padre) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER sparc-linux-gcc) +set (CMAKE_SYSTEM_PROCESSOR sparc) diff --git a/toolchains/toolchain-tripledragon.cmake b/toolchains/toolchain-tripledragon.cmake new file mode 100644 index 0000000..c755e82 --- /dev/null +++ b/toolchains/toolchain-tripledragon.cmake @@ -0,0 +1,4 @@ +set (OSCAM_SYSTEM_NAME TripleDragon) +set (CMAKE_SYSTEM_NAME Linux) +set (CMAKE_C_COMPILER powerpc-405-linux-gnu-gcc) +set (CMAKE_SYSTEM_PROCESSOR powerpc) diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100644 index 0000000..4964986 --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,35 @@ + project (Utils C) + + + +#----------------------- file groups ------------------------------ + +file (GLOB exe_srcs "list_smargo.c") +file (GLOB exe_hdrs "*.h") +file (GLOB all_srcs ${exe_srcs}) + +#----------------------- the executable ------------------------------ + +set (util_name "list_smargo") +add_executable (${util_name} ${exe_srcs} ${exe_hdrs}) +target_link_libraries (${util_name} ${libusb_link} ${rt_link} ${setupapi_link} ${ole32_link} ${shell32_link} ${pthread_link} ${dl_link}) +#----------------------- printout resume ----------------------------- + +message (STATUS "Utils: operating system: ${OSCamOperatingSystem}") +message (STATUS "Utils: target system: ${CS_TARGET}") +if(STATIC_LIBUSB EQUAL 0) + message (STATUS "Utils: You selected to disable static libusb system libusb used") +endif(STATIC_LIBUSB EQUAL 0) +if(STATICLIBUSB AND NOT LIBUSBDIR) + message (STATUS " utils use static libusb functions") + else(STATICLIBUSB AND NOT LIBUSBDIR) + if (LIBUSBDIR AND STATIC_LIBUSB EQUAL 0) + message(STATUS " utils use system libusb from selected LIBUSBDIR functions") + elseif (LIBUSBDIR AND STATIC_LIBUSB EQUAL 1) + message(STATUS " utils use static libusb from selected LIBUSBDIR functions") + elseif(LIBUSBDIR AND NOT STATIC_LIBUSB) + message(STATUS " utils use system libusb from selected LIBUSBDIR functions") + elseif(NOT LIBUSBDIR AND NOT STATIC_LIBUSB) + message(STATUS " utils use system libusb functions") + endif(LIBUSBDIR AND STATIC_LIBUSB EQUAL 0) + endif(STATICLIBUSB AND NOT LIBUSBDIR) diff --git a/utils/list_smargo.c b/utils/list_smargo.c new file mode 100644 index 0000000..dc6d0f5 --- /dev/null +++ b/utils/list_smargo.c @@ -0,0 +1,155 @@ +/* + * libusb example program to list devices on the bus + * Copyright (C) 2007 Daniel Drake + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include "../globals.h" +#include "../csctapi/ifd_smartreader_types.h" + +#if defined(__FreeBSD__) +#include +#else +#include +#endif + +static void smartreader_check_endpoint(libusb_device *usb_dev, libusb_device_handle *handle) +{ + struct libusb_device_descriptor usbdesc; + struct libusb_config_descriptor *configDesc; + int32_t ret; + int32_t j, k, l; + uint32_t m; + uint8_t tmpEndpointAddress; + int32_t nb_endpoint_ok = 0; + int32_t busid, devid; + unsigned char iserialbuffer[128], iproductbuffer[128]; + char *productptr = (char *)iproductbuffer; + static const char *const typename_str[6] = {"SR", "Infinity", "SRv2", "TripleP1", "TripleP2", "TripleP3"}; + + ret = libusb_get_device_descriptor(usb_dev, &usbdesc); + if(ret < 0) + { + printf("Smartreader : couldn't read device descriptor, assuming this is not a smartreader"); + return; + } + if(usbdesc.bNumConfigurations) + { + ret = libusb_get_active_config_descriptor(usb_dev, &configDesc); + if(ret) + { + printf("Smartreader : couldn't read config descriptor , assuming this is not a smartreader"); + return; + } + for(m = 0; m < sizeof(reader_types) / sizeof(struct s_reader_types); ++m) + { + nb_endpoint_ok = 0; + for(j = 0; j < (configDesc->bNumInterfaces); j++) + { + for(k = 0; k < configDesc->interface[j].num_altsetting; k++) + { + for(l = 0; l < configDesc->interface[j].altsetting[k].bNumEndpoints; l++) + { + tmpEndpointAddress = configDesc->interface[j].altsetting[k].endpoint[l].bEndpointAddress; + if((tmpEndpointAddress == reader_types[m].in_ep || tmpEndpointAddress == reader_types[m].out_ep)) + { + nb_endpoint_ok++; + } + } + } + } + + if(nb_endpoint_ok == 2) + { + busid = libusb_get_bus_number(usb_dev); + devid = libusb_get_device_address(usb_dev); + memset(iserialbuffer, 0, sizeof(iserialbuffer)); + memset(iproductbuffer, 0, sizeof(iproductbuffer)); + libusb_get_string_descriptor_ascii(handle, usbdesc.iSerialNumber, iserialbuffer, sizeof(iserialbuffer)); + libusb_get_string_descriptor_ascii(handle, usbdesc.iProduct, iproductbuffer, sizeof(iproductbuffer)); + if ((!((!strcasecmp(productptr, "Triple Reader+")) && (m == 2))) && (!((!strcasecmp(productptr, "Smartreader2 plus")) && (m == 3)))) { + printf("bus %03d, device %03d : %04x:%04x %s (type=%s, in_ep=%02x, out_ep=%02x; insert in oscam.server 'device = %s%sSerial:%s')\n", + busid, devid, + usbdesc.idVendor, usbdesc.idProduct, strlen(productptr) > 0 ? productptr : "Smartreader", + typename_str[reader_types[m].rdrtypename], reader_types[m].in_ep, reader_types[m].out_ep, + reader_types[m].rdrtypename == 0 ? "" : typename_str[reader_types[m].rdrtypename] , reader_types[m].rdrtypename == 0 ? "" : ";", iserialbuffer + );} + } + } + } +} + +static void print_devs(libusb_device **devs) +{ + libusb_device *dev; + libusb_device_handle *handle; + int32_t i = 0; + int32_t ret; + + while((dev = devs[i++]) != NULL) + { + struct libusb_device_descriptor usbdesc; + int32_t r = libusb_get_device_descriptor(dev, &usbdesc); + if(r < 0) + { + fprintf(stderr, "failed to get device descriptor"); + return; + } + if(usbdesc.idVendor == 0x0403 && (usbdesc.idProduct == 0x6001 || usbdesc.idProduct == 0x6011)) + { + ret = libusb_open(dev, &handle); + if(ret) + { + printf("couldn't open device %03d:%03d\n", libusb_get_bus_number(dev), libusb_get_device_address(dev)); + continue; + } + // check for smargo endpoints. + smartreader_check_endpoint(dev, handle); + + libusb_close(handle); + } + } +} + +int32_t main(void) +{ + libusb_device **devs; + int32_t r; + ssize_t cnt; + + r = libusb_init(NULL); + if(r < 0) + { return r; } + + printf("Looking for smartreader compatible devices...\n"); + + cnt = libusb_get_device_list(NULL, &devs); + if(cnt < 0) + { return (int32_t) cnt; } + + print_devs(devs); + libusb_free_device_list(devs, 1); + + libusb_exit(NULL); + + return 0; +} + diff --git a/webif/.gitignore b/webif/.gitignore new file mode 100644 index 0000000..f0bfbcd --- /dev/null +++ b/webif/.gitignore @@ -0,0 +1,6 @@ +pages.c +pages.dep +pages.bin +pages.bin.compressed +pages.h +pages_gen diff --git a/webif/Makefile b/webif/Makefile new file mode 100644 index 0000000..2d1e37a --- /dev/null +++ b/webif/Makefile @@ -0,0 +1,56 @@ +HOSTCC ?= gcc +HOSTCFLAGS ?= -O2 \ + -W -Wall -Wextra -Wno-sign-compare \ + -Wshadow -Wformat-security -Wstrict-prototypes + +# Setup quiet build +Q = +SAY = @true +ifndef V +Q = @ +NP = --no-print-directory +SAY = @echo +endif + +# Check if WEBIF_WIKI is enabled +WEBIF_WIKI_ENABLED := $(shell ../config.sh --enabled WEBIF_WIKI 2>/dev/null) + +ifeq ($(WEBIF_WIKI_ENABLED),Y) +all: pages.c pages_wiki.c +else +all: pages.c +endif + +pages.c: pages_gen ../config.h + $(SAY) "GEN webif/$@" + $(Q)./pages_mkdep + $(Q)./pages_gen + +-include pages.dep + +pages_gen: Makefile pages_gen.c + $(SAY) "HOSTCC webif/$@" + $(Q)$(HOSTCC) $(HOSTCFLAGS) ../minilzo/minilzo.c pages_gen.c -o $@ + +# Wiki help generation (only if WEBIF_WIKI is enabled and wiki submodule exists) +pages_wiki.c: wiki_gen ../config.h + $(SAY) "GEN webif/$@" + $(Q)if [ -d "../wiki/pages/configuration" ]; then \ + ./wiki_gen ../wiki/pages/configuration; \ + else \ + echo "WIKI ERROR: wiki submodule not found!"; \ + echo "WIKI Run: git submodule update --init"; \ + exit 1; \ + fi + +wiki_gen: Makefile wiki_gen.c + $(SAY) "HOSTCC webif/$@" + $(Q)$(HOSTCC) $(HOSTCFLAGS) ../minilzo/minilzo.c wiki_gen.c -o $@ + +clean: + @-for FILE in pages_gen pages.dep pages.bin pages.bin.compressed pages.h pages.c wiki_gen pages_wiki.h pages_wiki.c is_defined.txt; do \ + if [ -f $$FILE ]; then echo "RM webif/$$FILE"; fi; \ + rm -rf $$FILE; \ + done + +distclean: clean diff --git a/webif/README b/webif/README new file mode 100644 index 0000000..687db8e --- /dev/null +++ b/webif/README @@ -0,0 +1,19 @@ +This directory holds all pages that are used in OSCam WebIf along with +program that builds pages.{c,h} files used by the OSCam build system. + +The build system is intelligent enough to rebuild pages.{c,h} files when +any file mentioned in pages_index.txt is changed or pages_index.txt itself +is changed. + +If you want change WebIf pages just edit files in the directory that +interests you then build OSCam as you normally do. + +Any file must be described in pages_index.txt + +Files in the root directory: + Makefile - Build system file. + pages_gen.c - The program that generates pages.c and pages.h files. + pages_index.txt - This file contains mapping of template names to + file names and describes template's config dependecies. + pages_index_check - Script that checks if every file is found. + pages_mkdep - Used by the build system to generate file dependencies. diff --git a/webif/api.json/cacheex.json b/webif/api.json/cacheex.json new file mode 100644 index 0000000..52497f6 --- /dev/null +++ b/webif/api.json/cacheex.json @@ -0,0 +1,5 @@ +##TPLJSONHEADER## +"rows":[ + ##JSONCACHEEXBITS## +] +##TPLJSONFOOTER## \ No newline at end of file diff --git a/webif/api.json/cacheexaiobit.json b/webif/api.json/cacheexaiobit.json new file mode 100644 index 0000000..2de0c53 --- /dev/null +++ b/webif/api.json/cacheexaiobit.json @@ -0,0 +1,18 @@ + ##JSONDELIMITER##{ + "rowtype":"##ROWTYPE##", + "id":"##ROWID##", + "dirimg":"##DIRECTIONIMG##", + "type":"##TYPE##", + "name":"##NAME##", + "ip":"##IP##", + "node":"##NODE##", + "level":"##LEVEL##", + "push":"##PUSH##", + "pushlg":"##PUSHLG##", + "got":"##GOT##", + "gotlg":"GOTLG", + "cwinfo":"##CWCINFO##", + "hit":"##HIT##", + "err":"##ERR##", + "errcw":"##ERRCW##" + } \ No newline at end of file diff --git a/webif/api.json/cacheexbit.json b/webif/api.json/cacheexbit.json new file mode 100644 index 0000000..e22fbe0 --- /dev/null +++ b/webif/api.json/cacheexbit.json @@ -0,0 +1,16 @@ + ##JSONDELIMITER##{ + "rowtype":"##ROWTYPE##", + "id":"##ROWID##", + "dirimg":"##DIRECTIONIMG##", + "type":"##TYPE##", + "name":"##NAME##", + "ip":"##IP##", + "node":"##NODE##", + "level":"##LEVEL##", + "push":"##PUSH##", + "got":"##GOT##", + "cwinfo":"##CWCINFO##", + "hit":"##HIT##", + "err":"##ERR##", + "errcw":"##ERRCW##" + } \ No newline at end of file diff --git a/webif/api.json/entitlementbit.json b/webif/api.json/entitlementbit.json new file mode 100644 index 0000000..097d9c6 --- /dev/null +++ b/webif/api.json/entitlementbit.json @@ -0,0 +1 @@ +##JSONDELIMITER##{"type":"##ENTTYPE##", "caid": "##ENTCAID##", "provid": "##ENTPROVID##", "id": "##ENTID##", "class": "##ENTCLASS##", "startDate": "##ENTSTARTDATE##", "expireDate": "##ENTENDDATE##", "name": "##ENTRESNAME##"} diff --git a/webif/api.json/entitlements.json b/webif/api.json/entitlements.json new file mode 100644 index 0000000..7ff9932 --- /dev/null +++ b/webif/api.json/entitlements.json @@ -0,0 +1 @@ +##TPLJSONHEADER##"entitlements":[##APIENTITLEMENTLIST##]##TPLJSONFOOTER## diff --git a/webif/api.json/footer.json b/webif/api.json/footer.json new file mode 100644 index 0000000..c07e4d4 --- /dev/null +++ b/webif/api.json/footer.json @@ -0,0 +1,2 @@ + } +}##ENDBRACKET## diff --git a/webif/api.json/header.json b/webif/api.json/header.json new file mode 100644 index 0000000..9842499 --- /dev/null +++ b/webif/api.json/header.json @@ -0,0 +1,107 @@ +##CALLBACK##{ +"oscam": { + "version": "##CS_VERSION##", + "revision": "", + "commit": "##CS_GIT_COMMIT##", + "starttime": "##APISTARTTIME##", + "apiruntime": "##APIRUNTIME##", + "runtime": "##RUNTIME##", + "uptime":"##UPTIME##", + "curdate": "##CURDATE##", + "curtime": "##CURTIME##", + "readonly": "##APIREADONLY##", + "piconenabled":"##PICONENABLED##", + "srvidfile":"##SRVIDFILE##", + "lbdefined":"##LBISDEFINED##", + "failbannotifier":"##FAILBANNOTIFIERPOLL##", + "sysinfo":{ + "mem_cur_total":"##MEM_CUR_TOTAL##", + "mem_cur_free":"##MEM_CUR_FREE##", + "mem_cur_used":"##MEM_CUR_USED##", + "mem_cur_buff":"##MEM_CUR_BUFF##", + "mem_cur_cached":"##MEM_CUR_CACHED##", + "mem_cur_freem":"##MEM_CUR_FREEM##", + "mem_cur_totalsw":"##MEM_CUR_TOTSW##", + "mem_cur_freesw":"##MEM_CUR_FRESW##", + "mem_cur_usedsw":"##MEM_CUR_USESW##", + "mem_cur_shared":"##MEM_CUR_SHARE##", + "oscam_vmsize":"##OSCAM_VMSIZE##", + "oscam_rsssize":"##OSCAM_RSSSIZE##", + "server_procs":"##SERVER_PROCS##", + "cpu_load_0":"##CPU_LOAD_0##", + "cpu_load_1":"##CPU_LOAD_1##", + "cpu_load_2":"##CPU_LOAD_2##", + "oscam_refresh":"##OSCAM_REFRESH##", + "oscam_cpu_user":"##OSCAM_CPU_USER##", + "oscam_cpu_sys":"##OSCAM_CPU_SYS##", + "oscam_cpu_sum":"##OSCAM_CPU_SUM##" + }, + "totals":{ + "total_users":"##TOTAL_USERS##", + "total_active":"##TOTAL_ACTIVE##", + "total_connected":"##TOTAL_CONNECTED##", + "total_online":"##TOTAL_ONLINE##", + "total_disabled":"##TOTAL_DISABLED##", + "total_expired":"##TOTAL_EXPIRED##", + "total_readers":"##TOTAL_READERS##", + "total_active_readers":"##TOTAL_ACTIVE_READERS##", + "total_connected_readers":"##TOTAL_CONNECTED_READERS##", + "total_disabled_readers":"##TOTAL_DISABLED_READERS##", + "total_cwok":"##TOTAL_CWOK##", + "total_cwok_readers":"##TOTAL_CWOK_READERS##", + "rel_cwok":"##REL_CWOK##", + "rel_cwok_readers":"##REL_CWOK_READERS##", + "total_cwcache":"##TOTAL_CWCACHE##", + "rel_cwcache":"##REL_CWCACHE##", + "total_cwnok":"##TOTAL_CWNOK##", + "total_cwnok_readers":"##TOTAL_CWNOK_READERS##", + "rel_cwnok":"##REL_CWNOK##", + "total_cwtout":"##TOTAL_CWTOUT##", + "total_cwtout_readers":"##TOTAL_CWTOUT_READERS##", + "rel_cwnok_readers":"##REL_CWNOK_READERS##", + "rel_cwtout":"##REL_CWTOUT##", + "rel_cwtout_readers":"##REL_CWTOUT_READERS##", + "total_cwign":"##TOTAL_CWIGN##", + "rel_cwign":"##REL_CWIGN##", + "total_ecm_min":"##TOTAL_ECM_MIN##", + "total_cw":"##TOTAL_CW##", + "total_cwpos":"##TOTAL_CWPOS##", + "total_cwpos_readers":"##TOTAL_CWPOS_READERS##", + "rel_cwpos":"##REL_CWPOS##", + "rel_cwpos_readers":"##REL_CWPOS_READERS##", + "total_cwneg":"##TOTAL_CWNEG##", + "total_cwneg_readers":"##TOTAL_CWNEG_READERS##", + "rel_cwneg":"##REL_CWNEG##", + "rel_cwneg_readers":"##REL_CWNEG_READERS##", + "total_emok":"##TOTAL_EMOK##", + "rel_emok":"##REL_EMOK##", + "total_emnok":"##TOTAL_EMNOK##", + "rel_emnok":"##REL_EMNOK##", + "total_em":"##TOTAL_EM##", + "total_cachexpush":"##TOTAL_CACHEXPUSH##", + "total_cachexgot":"##TOTAL_CACHEXGOT##", + "total_cachexhit":"##TOTAL_CACHEXHIT##", + "rel_cachexhit":"##REL_CACHEXHIT##", + "total_cachesize":"##TOTAL_CACHESIZE##", + "total_cachesize_lg":"##TOTAL_CACHESIZE_LG##", + "total_elenr":"##TOTAL_ELENR##", + "total_eheadr":"##TOTAL_EHEADR##", + "total_emmerroruk_readers":"##TOTAL_EMMERRORUK_READERS##", + "total_emmerrorg_readers":"##TOTAL_EMMERRORG_READERS##", + "total_emmerrors_readers":"##TOTAL_EMMERRORS_READERS##", + "total_emmerroruq_readers":"##TOTAL_EMMERRORUQ_READERS##", + "total_emmwrittenuk_readers":"##TOTAL_EMMWRITTENUK_READERS##", + "total_emmwritteng_readers":"##TOTAL_EMMWRITTENG_READERS##", + "total_emmwrittens_readers":"##TOTAL_EMMWRITTENS_READERS##", + "total_emmwrittenuq_readers":"##TOTAL_EMMWRITTENUQ_READERS##", + "total_emmskippeduk_readers":"##TOTAL_EMMSKIPPEDUK_READERS##", + "total_emmskippedg_readers":"##TOTAL_EMMSKIPPEDG_READERS##", + "total_emmskippeds_readers":"##TOTAL_EMMSKIPPEDS_READERS##", + "total_emmskippeduq_readers":"##TOTAL_EMMSKIPPEDUQ_READERS##", + "total_emmblockeduk_readers":"##TOTAL_EMMBLOCKEDUK_READERS##", + "total_emmblockedg_readers":"##TOTAL_EMMBLOCKEDG_READERS##", + "total_emmblockeds_readers":"##TOTAL_EMMBLOCKEDS_READERS##", + "total_emmblockeduq_readers":"##TOTAL_EMMBLOCKEDUQ_READERS##", + "total_sum_all_readers_ecm":"##TOTAL_SUM_ALL_READERS_ECM##", + "total_sum_all_readers_emm":"##TOTAL_SUM_ALL_READERS_EMM##" + }, \ No newline at end of file diff --git a/webif/api.json/reader.json b/webif/api.json/reader.json new file mode 100644 index 0000000..80a3fc8 --- /dev/null +++ b/webif/api.json/reader.json @@ -0,0 +1 @@ +##TPLJSONHEADER##"readers":[##APIREADERLIST##]##TPLJSONFOOTER## \ No newline at end of file diff --git a/webif/api.json/readerbit.json b/webif/api.json/readerbit.json new file mode 100644 index 0000000..d60ce9c --- /dev/null +++ b/webif/api.json/readerbit.json @@ -0,0 +1 @@ +##JSONDELIMITER##{"labelmd5":"##LABELMD5##","label":"##READERNAMEENC##","classname":"##READERCLASS##","ip":"##READERIP##","status":"##RSTATUS##","protocol":"##CLIENTPROTO##","protoicon":"##PROTOICON##","prototitle":"##CLIENTPROTOTITLE##","protosort":"##CLIENTPROTOSORT##","type":"##APIREADERTYPE##","enabled":"##APIREADERENABLED##","last_gsms":"##LASTGSMS##","stats":{"ecmsok":"##ECMSOK##","ecmsokrel":"##ECMSOKREL##","ecmsoklg":"##ECMSOKLG##","ecmsoklgrel":"##ECMSOKLGREL##","ecmsnok":"##ECMSNOK##","ecmsnokrel":"##ECMSNOKREL##","ecmstout":"##ECMSTOUT##","ecmstoutrel":"##ECMSTOUTREL##","ecmsfiltered":"##ECMSFILTEREDHEAD## / ##ECMSFILTEREDLEN##","emmerror":"##EMMERRORUK## / ##EMMERRORG## / ##EMMERRORS## / ##EMMERRORUQ##","emmwritten":"##EMMWRITTENUK## / ##EMMWRITTENG## / ##EMMWRITTENS## / ##EMMWRITTENUQ##", "emmskipped":"##EMMSKIPPEDUK## / ##EMMSKIPPEDG## / ##EMMSKIPPEDS## / ##EMMSKIPPEDUQ##","emmblocked":"##EMMBLOCKEDUK## / ##EMMBLOCKEDG## / ##EMMBLOCKEDS## / ##EMMBLOCKEDUQ##","lbweight":"##LBWEIGHT##"}} diff --git a/webif/api.json/status.json b/webif/api.json/status.json new file mode 100644 index 0000000..d1e22d8 --- /dev/null +++ b/webif/api.json/status.json @@ -0,0 +1,21 @@ +##TPLJSONHEADER## + "status": { + "ucs":"##UCS##", + "uca":"##UCA##", + "ucac":"##UCAC##", + "cfgh":"##CFGH##", + "scs":"##SCS##", + "sch":"##SCH##", + "sca":"##SCA##", + "mcs":"##MCS##", + "mca":"##MCA##", + "rcc":"##RCC##", + "rca":"##RCA##", + "rco":"##RCO##", + "pcc":"##PCC##", + "pca":"##PCA##", + "pco":"##PCO##", + "client":[ + ##JSONSTATUSBITS## + ]} +##TPLJSONFOOTER## diff --git a/webif/api.json/status_statusbits.json b/webif/api.json/status_statusbits.json new file mode 100644 index 0000000..12de6e9 --- /dev/null +++ b/webif/api.json/status_statusbits.json @@ -0,0 +1,42 @@ +##JSONARRAYDELIMITER##{ +"thid": "##CSIDX##", +"type": "##CLIENTTYPE##", +"name_enc": "##USERENC##", +"rname_enc": "##READERNAMEENC##", +"upicmissing": "##UPICMISSING##", +"desc": "##CLIENTDESCRIPTION##", +"protocol": "##CLIENTPROTO##", +"protocolext": "##CLIENTPROTOTITLE##", +"protoicon":"##PROTOICON##", +"au": "##CLIENTCAU##", +"aufmt":"##CLIENTCAUHTTP##", +"request": { + "caid": "##CLIENTCAID##", + "provid": "##CLIENTPROVID##", + "srvid": "##CLIENTSRVID##", + "ecmtime": "##CLIENTLASTRESPONSETIME##", + "ecmhistory": "##CLIENTLASTRESPONSETIMEHIST##", + "answered": "##LASTREADER##", + "picon":"##PICONNAME##", + "chprovider":"##CLIENTSRVPROVIDER##", + "chname":"##CLIENTSRVNAME##", + "chantitle":"##LASTCHANNELTITLE##", + "msvalue":"##MSVALUE##", + "lbvalue":"##LBLRPSTRVALUE##", + "$": "##CLIENTSRVNAME####CLIENTSRVPROVIDER##" +}, +"times": { + "login": "##CLIENTLOGINDATE##", + "loginfmt":"##CLIENTLOGINDATEFMT##", + "online": "##CLIENTLOGINSECS##", + "idle": "##CLIENTIDLESECS##" +}, +"connection": { + "ip": "##CLIENTIP##", + "port": "##CLIENTPORT##", + "status":"##CLIENTCON##", + "totentitlements":"##TOTENTITLEMENTS##", + "entitlements":[##ENTITLEMENTS##], + "$": "##CLIENTCON##" +} +} diff --git a/webif/api.json/user.json b/webif/api.json/user.json new file mode 100644 index 0000000..6fc736e --- /dev/null +++ b/webif/api.json/user.json @@ -0,0 +1,3 @@ +##TPLJSONHEADER## +"users":[##APIUSERCONFIGS##] +##TPLJSONFOOTER## \ No newline at end of file diff --git a/webif/api.json/userbit.json b/webif/api.json/userbit.json new file mode 100644 index 0000000..9bbe507 --- /dev/null +++ b/webif/api.json/userbit.json @@ -0,0 +1,46 @@ +##JSONDELIMITER##{ + "user":{ + "usermd5":"##USERMD5##", + "status":"##STATUS##", + "classname":"##CLASSNAME##", + "ip":"##CLIENTIP##", + "unotify":"##UNOTIFY##", + "protocol":"##CLIENTPROTO##", + "protoicon":"##PROTOICON##", + "prototitle":"##CLIENTPROTOTITLE##", + "protosort":"##CLIENTPROTOSORT##", + "grpviewid":"##GRPVIEW##", + "groups":"##GROUPS##", + "lastchannel":"##LASTCHANNEL##", + "lastchanneltitle":"##LASTCHANNELTITLE##", + "lastchannelsort":"##LASTCHANNELSORT##", + "expdate":"##EXPDATE##", + "expview":"##EXPIREVIEW##", + "lca":"##LCA##", + "lcb":"##LCB##", + "stats":{ + "idle":"##IDLESECS##", + "cwok":"##CWOK##", + "cwnok":"##CWNOK##", + "cwignore":"##CWIGN##", + "cwtimeout":"##CWTOUT##", + "cwcache":"##CWCACHE##", + "cwtun":"##CWTUN##", + "cwlastresptime":"##CWLASTRESPONSET##", + "cwlastresptimems":"##CWLASTRESPONSETMS##", + "emmok":"##EMMOK##", + "emmnok":"##EMMNOK##", + "cwrate":"##CWRATE##", + "cwrate2":"##CWRATE2##", + "cascusercomb":"##CASCUSERSCOMB##", + "timeonchannel":"##CLIENTTIMEONCHANNELAPI##", + "expectsleep":"##CLIENTTIMETOSLEEPAPI##", + "n_requ_m":"##N_REQUEST_MIN##", + "cwccycview":"##CWCCYCVIEW##", + "cwccyclechecked":"##CWCYCLECHECKED##", + "cwcycleok":"##CWCYCLEOK##", + "cwcyclenok":"##CWCYCLENOK##", + "cwcycleign":"##CWCYCLEIGN##" + } + } +} diff --git a/webif/api.xml/cccamcardlist.xml b/webif/api.xml/cccamcardlist.xml new file mode 100644 index 0000000..dc51a0c --- /dev/null +++ b/webif/api.xml/cccamcardlist.xml @@ -0,0 +1,7 @@ +##TPLAPIHEADER## + + +##CARDLIST## + + +##TPLAPIFOOTER## diff --git a/webif/api.xml/cccamcardlist_cardlist.xml b/webif/api.xml/cccamcardlist_cardlist.xml new file mode 100644 index 0000000..83cbd17 --- /dev/null +++ b/webif/api.xml/cccamcardlist_cardlist.xml @@ -0,0 +1,10 @@ + + ##SHAREID## + ##REMOTEID## + +##PROVIDERLIST## + + +##NODELIST## + + diff --git a/webif/api.xml/cccamcardlist_cardlist_nodelist.xml b/webif/api.xml/cccamcardlist_cardlist_nodelist.xml new file mode 100644 index 0000000..cb7e56b --- /dev/null +++ b/webif/api.xml/cccamcardlist_cardlist_nodelist.xml @@ -0,0 +1 @@ + ##APINODE## diff --git a/webif/api.xml/cccamcardlist_cardlist_providerlist.xml b/webif/api.xml/cccamcardlist_cardlist_providerlist.xml new file mode 100644 index 0000000..b42db6e --- /dev/null +++ b/webif/api.xml/cccamcardlist_cardlist_providerlist.xml @@ -0,0 +1 @@ + ##APIPROVIDERNAME## diff --git a/webif/api.xml/confirmation.xml b/webif/api.xml/confirmation.xml new file mode 100644 index 0000000..cf87bf4 --- /dev/null +++ b/webif/api.xml/confirmation.xml @@ -0,0 +1,3 @@ +##TPLAPIHEADER## + ##APICONFIRMMESSAGE## +##TPLAPIFOOTER## diff --git a/webif/api.xml/error.xml b/webif/api.xml/error.xml new file mode 100644 index 0000000..cbeb7bd --- /dev/null +++ b/webif/api.xml/error.xml @@ -0,0 +1,3 @@ +##TPLAPIHEADER## + ##APIERRORMESSAGE## +##TPLAPIFOOTER## diff --git a/webif/api.xml/failban.xml b/webif/api.xml/failban.xml new file mode 100644 index 0000000..07689ec --- /dev/null +++ b/webif/api.xml/failban.xml @@ -0,0 +1,5 @@ +##TPLAPIHEADER## + +##APIFAILBANROW## + +##TPLAPIFOOTER## diff --git a/webif/api.xml/failban_failbanrow.xml b/webif/api.xml/failban_failbanrow.xml new file mode 100644 index 0000000..bc0b341 --- /dev/null +++ b/webif/api.xml/failban_failbanrow.xml @@ -0,0 +1 @@ + ##IPADDRESS## diff --git a/webif/api.xml/file.xml b/webif/api.xml/file.xml new file mode 100644 index 0000000..2ca8d47 --- /dev/null +++ b/webif/api.xml/file.xml @@ -0,0 +1,5 @@ +##TPLAPIHEADER## + + + +##TPLAPIFOOTER## diff --git a/webif/api.xml/footer.xml b/webif/api.xml/footer.xml new file mode 100644 index 0000000..a3a1a22 --- /dev/null +++ b/webif/api.xml/footer.xml @@ -0,0 +1 @@ + diff --git a/webif/api.xml/header.xml b/webif/api.xml/header.xml new file mode 100644 index 0000000..13f3b10 --- /dev/null +++ b/webif/api.xml/header.xml @@ -0,0 +1,2 @@ + + diff --git a/webif/api.xml/readers.xml b/webif/api.xml/readers.xml new file mode 100644 index 0000000..81f74b7 --- /dev/null +++ b/webif/api.xml/readers.xml @@ -0,0 +1,5 @@ +##TPLAPIHEADER## + +##APIREADERLIST## + +##TPLAPIFOOTER## diff --git a/webif/api.xml/readers_readerlist.xml b/webif/api.xml/readers_readerlist.xml new file mode 100644 index 0000000..fc1efe5 --- /dev/null +++ b/webif/api.xml/readers_readerlist.xml @@ -0,0 +1 @@ + diff --git a/webif/api.xml/readerstats.xml b/webif/api.xml/readerstats.xml new file mode 100644 index 0000000..318ef60 --- /dev/null +++ b/webif/api.xml/readerstats.xml @@ -0,0 +1,11 @@ +##TPLAPIHEADER## + + +##EMMSTATS## + + +##ECMSTATS## + + ##ECMHISTORY## + +##TPLAPIFOOTER## diff --git a/webif/api.xml/readerstats_ecmstats.xml b/webif/api.xml/readerstats_ecmstats.xml new file mode 100644 index 0000000..194d3c0 --- /dev/null +++ b/webif/api.xml/readerstats_ecmstats.xml @@ -0,0 +1 @@ + ##ECMCOUNT## diff --git a/webif/api.xml/readerstats_emmstats.xml b/webif/api.xml/readerstats_emmstats.xml new file mode 100644 index 0000000..4db6af0 --- /dev/null +++ b/webif/api.xml/readerstats_emmstats.xml @@ -0,0 +1 @@ + ##EMMCOUNT## diff --git a/webif/api.xml/status.xml b/webif/api.xml/status.xml new file mode 100644 index 0000000..738727b --- /dev/null +++ b/webif/api.xml/status.xml @@ -0,0 +1,7 @@ +##TPLAPIHEADER## + +##APISTATUSBITS## + + ##TPLAPIFOOTER## diff --git a/webif/api.xml/status_statusbits.xml b/webif/api.xml/status_statusbits.xml new file mode 100644 index 0000000..ce2f049 --- /dev/null +++ b/webif/api.xml/status_statusbits.xml @@ -0,0 +1,5 @@ + + ##CLIENTSRVNAME####CLIENTSRVPROVIDER## + + ##CLIENTCON## + diff --git a/webif/api.xml/userconfiglist.xml b/webif/api.xml/userconfiglist.xml new file mode 100644 index 0000000..8f90ea3 --- /dev/null +++ b/webif/api.xml/userconfiglist.xml @@ -0,0 +1,46 @@ +##TPLAPIHEADER## + +##APIUSERCONFIGS## + + + ##TOTAL_CWOK## + ##TOTAL_CWNOK## + ##TOTAL_CWIGN## + ##TOTAL_CWTOUT## + ##TOTAL_CWCACHE## + ##TOTAL_CWTUN## + ##TOTAL_USERS## + ##TOTAL_DISABLED## + ##TOTAL_EXPIRED## + ##TOTAL_ACTIVE## + ##TOTAL_CONNECTED## + ##TOTAL_ONLINE## + ##TOTAL_READERS## + ##TOTAL_DISABLED_READERS## + ##TOTAL_ACTIVE_READERS## + ##TOTAL_CONNECTED_READERS## + ##TOTAL_CWOK_READERS## + ##TOTAL_CWNOK_READERS## + ##TOTAL_CWTOUT_READERS## + ##TOTAL_EHEADR## + ##TOTAL_ELENR## + ##TOTAL_EMMERRORUK_READERS## + ##TOTAL_EMMERRORG_READERS## + ##TOTAL_EMMERRORS_READERS## + ##TOTAL_EMMERRORUQ_READERS## + ##TOTAL_EMMWRITTENUK_READERS## + ##TOTAL_EMMWRITTENG_READERS## + ##TOTAL_EMMWRITTENS_READERS## + ##TOTAL_EMMWRITTENUQ_READERS## + ##TOTAL_EMMSKIPPEDUK_READERS## + ##TOTAL_EMMSKIPPEDG_READERS## + ##TOTAL_EMMSKIPPEDS_READERS## + ##TOTAL_EMMSKIPPEDUQ_READERS## + ##TOTAL_EMMBLOCKEDUK_READERS## + ##TOTAL_EMMBLOCKEDG_READERS## + ##TOTAL_EMMBLOCKEDS_READERS## + ##TOTAL_EMMBLOCKEDUQ_READERS## + ##TOTAL_SUM_ALL_READERS_ECM## + ##TOTAL_SUM_ALL_READERS_EMM## + +##TPLAPIFOOTER## diff --git a/webif/api.xml/userconfiglist_userconfigs.xml b/webif/api.xml/userconfiglist_userconfigs.xml new file mode 100644 index 0000000..7530826 --- /dev/null +++ b/webif/api.xml/userconfiglist_userconfigs.xml @@ -0,0 +1,16 @@ + + + ##CWOK## + ##CWNOK## + ##CWIGN## + ##CWTOUT## + ##CWCACHE## + ##CWTUN## + ##CWLASTRESPONSET## + ##EMMOK## + ##EMMNOK## + ##CWRATE## + ##CLIENTTIMEONCHANNELAPI## + ##CLIENTTIMETOSLEEPAPI## + + diff --git a/webif/api.xml/useredit.xml b/webif/api.xml/useredit.xml new file mode 100644 index 0000000..c9a2f34 --- /dev/null +++ b/webif/api.xml/useredit.xml @@ -0,0 +1,32 @@ +##TPLAPIHEADER## + + ##USERNAME## + ##PASSWORD## + ##DESCRIPTION## + ##DISABLEDVALUE## + ##EXPDATE## + ##FAILBAN## + ##ALLOWEDTIMEFRAME## + ##GROUPS## + ##DYNDNS## + ##UNIQVALUE## + ##SLEEP## + ##MONVALUE## + ##AUREADER## + ##SERVICES## + ##CAIDS## + ##IDENTS## + ##CHIDS## + ##CLASS## + ##BETATUNNELS## + ##SUPPRESSCMD08VALUE## + ##SLEEPSEND## + ##AC_USERS## + ##PENALTYVALUE## + ##CCCMAXHOPS## + ##CCCRESHARE## + ##CCCIGNORERESHARE## + ##CCCSTEALTH## + ##KEEPALIVEVALUE## + +##TPLAPIFOOTER## diff --git a/webif/cacheex/cacheex.html b/webif/cacheex/cacheex.html new file mode 100644 index 0000000..4d76808 --- /dev/null +++ b/webif/cacheex/cacheex.html @@ -0,0 +1,56 @@ +##TPLHEADERSHORT## +##REFRESH## + +##TPLBODY## +##TPLMENU## + +##TPLMESSAGE## +
+ + +
+
+ + + + + + + + + + + + + + + + + + + +##TABLECLIENTROWS## +##TABLEREADERROWS## + +
CacheEX Stats for ##OWN_CACHEEX_NODEID##
DirectionTypeNameIPNODECache EX ModePushGotCWC InfoHitErrCW Diff
+
+ +##TPLCACHEEXINFOBIT## +
+
+##TPLFOOTER## \ No newline at end of file diff --git a/webif/cacheex/cacheex_tablerow.html b/webif/cacheex/cacheex_tablerow.html new file mode 100644 index 0000000..adbe5eb --- /dev/null +++ b/webif/cacheex/cacheex_tablerow.html @@ -0,0 +1 @@ +   ##DIRECTIONIMG##  ##TYPE####NAME####IP####NODE####LEVEL####PUSH####GOT####CWCINFO####HIT####ERR####ERRCW## diff --git a/webif/cacheexaio/cacheex.html b/webif/cacheexaio/cacheex.html new file mode 100644 index 0000000..8c83950 --- /dev/null +++ b/webif/cacheexaio/cacheex.html @@ -0,0 +1,59 @@ +##TPLHEADERSHORT## +##REFRESH## + +##TPLBODY## +##TPLMENU## + +##TPLMESSAGE## +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + +##TABLECLIENTROWS## +##TABLEREADERROWS## + +
CacheEX Stats for ##OWN_CACHEEX_NODEID##
DirectionTypeNameIPNODECache EX ModePushPush(lg)GotGot(lg)HitHit %CWC InfoCW DiffCW Err
+
+ +##TPLCACHEEXAIOINFOBIT## +
+
+##TPLFOOTER## \ No newline at end of file diff --git a/webif/cacheexaio/cacheex_tablerow.html b/webif/cacheexaio/cacheex_tablerow.html new file mode 100644 index 0000000..1aefc90 --- /dev/null +++ b/webif/cacheexaio/cacheex_tablerow.html @@ -0,0 +1 @@ +  ##DIRECTIONIMG##  ##TYPE####NAME####IP####NODE####LEVEL####PUSH####PUSHLG####GOT####GOTLG####HIT####REL_CACHEXHITGOT####CWCINFO####ERRCW####ERR## diff --git a/webif/cacheexaio/cacheex_tablerow_stats.html b/webif/cacheexaio/cacheex_tablerow_stats.html new file mode 100644 index 0000000..8e6d52d --- /dev/null +++ b/webif/cacheexaio/cacheex_tablerow_stats.html @@ -0,0 +1 @@ +   ##DIRECTIONIMG##  ##TYPE####NAME####IP####NODE####LEVEL####PUSH####PUSHLG####GOT####GOTLG####HIT####REL_CACHEXHITGOT####CWCINFO####ERRCW####ERR## diff --git a/webif/config/anticasc.html b/webif/config/anticasc.html new file mode 100644 index 0000000..9533ac8 --- /dev/null +++ b/webif/config/anticasc.html @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/webif/config/cache.html b/webif/config/cache.html new file mode 100644 index 0000000..584895b --- /dev/null +++ b/webif/config/cache.html @@ -0,0 +1,7 @@ + +
Edit Anticascading Config
Enabled:
Numusers:
Sampletime: min
Samples:
Penalty:
AClogfile:
Fakedelay:
Denysamples:
Edit Anticascading over Sid Count (ACoSC) Config
ACoSC Enabled:
ACoSC max_ecms_per_minute:
ACoSC max_active_sids:
ACoSC zap_limit:
ACoSC penalty:
ACoSC penalty_duration:
ACoSC delay:
+ + + +##TPLCONFIGCACHEEXCSP## +##TPLCONFIGCWCYCLE## diff --git a/webif/config/cache_cacheexaiocsp.html b/webif/config/cache_cacheexaiocsp.html new file mode 100644 index 0000000..ede412e --- /dev/null +++ b/webif/config/cache_cacheexaiocsp.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webif/config/cache_cacheexcsp.html b/webif/config/cache_cacheexcsp.html new file mode 100644 index 0000000..bedbb5b --- /dev/null +++ b/webif/config/cache_cacheexcsp.html @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/webif/config/cache_cwcycle.html b/webif/config/cache_cwcycle.html new file mode 100644 index 0000000..70c3559 --- /dev/null +++ b/webif/config/cache_cwcycle.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/webif/config/cacheaio.html b/webif/config/cacheaio.html new file mode 100644 index 0000000..8e3683c --- /dev/null +++ b/webif/config/cacheaio.html @@ -0,0 +1,7 @@ + +
Global Cache Settings
Delay: ms delaying answers from cache
Max time: s keep ECMs in cache
CacheEx / CSP
CW Cache Settings:
Format: caid[&mask][@provid][$servid]:mode:timediff_old_cw[,n]
CW Cache Size: count of max. CWs for CW cache
CW Cache Memory: MB of max. memory used for CW cache size
ECM droptime:
s after known ECM requests are dropped
ECM Cache Size: count of max. ECMs for ECM cache
ECM Cache Memory: MB of max. memory used for ECM cache size
CacheEx CW Check:
Format: caid[&mask][@provid][$servid]:mode:counter[,n]
Wait time: ms
Wait time block start count:count of wait time timeouts to start blocking
Wait time block time:s block wait time
Mode1 delay time: ms
Max hit time: s keep hit for dynamic wait time
Write statistic:
Wait until ctimeout:
Drop diff CWs:
No CW push after (from local/proxy-reader): ms
Format: caid:time[,n]
Allow client to overwrite 'Forward lg-only settings':(if disabled, only more restrictive settings are added)
Forward localgenerated flagged CWs only:
Forward localgenerated flagged CWs only(caid:provid[,N];):
Drop incoming not localgenerated flagged CWs:
Drop incoming not localgenerated flagged CWs(caid:provid[,N];):
Drop incoming settings only for CX-AIO peers:
Cache-EX ECM filter:
Cache-EX ECM filter(aio only):
Push localgenerated flagged CWs always to these groups: Valid values 1-64
CSP
Port:
Serverip:
ECM filter:
ECM filter adv.:
Reforward cacheex:
Block fake cws:
CacheEx / CSP
CacheEx CW Check:
Format: caid[&mask][@provid][$servid]:mode:counter[,n]
Wait time: ms
Mode1 delay time: ms
Max hit time: s keep hit for dynamic wait time
Write statistic:
Wait until ctimeout:
CSP
Port:
Serverip:
ECM filter:
ECM filter adv.:
Reforward cacheex:
Block fake cws:
CW Cycle Check
Enable Cycle Check:
Cycle Check Caids:
Max Cycle List Entries: max Count of Cyclelist Entries *Default 500 ** max 4000 Entries*
Keep Cycletime: min Keep Learned Cycletime in Memory *Default 15 ** max 240 Min*
On Bad Cycle:
Drop Old CW:
Sensitive new CW:
Allow Bad Cycle from Fixed Fallback:
Use CWC Info from Cacheex Sources:
+ + + +##TPLCONFIGCACHEEXAIOCSP## +##TPLCONFIGCWCYCLE## diff --git a/webif/config/camd33.html b/webif/config/camd33.html new file mode 100644 index 0000000..c446edf --- /dev/null +++ b/webif/config/camd33.html @@ -0,0 +1,8 @@ + +
Global Cache Settings
Delay: ms delaying answers from cache
Max time: s keep ECMs in cache
+ + + + + + diff --git a/webif/config/camd35.html b/webif/config/camd35.html new file mode 100644 index 0000000..dc9a168 --- /dev/null +++ b/webif/config/camd35.html @@ -0,0 +1,7 @@ + + +
Edit Camd33 Config
Port:
Serverip:
Key:
Passive:
Nocrypt:
+ + + + diff --git a/webif/config/camd35tcp.html b/webif/config/camd35tcp.html new file mode 100644 index 0000000..e7eed3f --- /dev/null +++ b/webif/config/camd35tcp.html @@ -0,0 +1,7 @@ + + +
Edit Cs357x (Camd35 UDP) Config
Port:
Serverip:
Suppress cmd08:
+ + + + diff --git a/webif/config/cccam.html b/webif/config/cccam.html new file mode 100644 index 0000000..64f69c7 --- /dev/null +++ b/webif/config/cccam.html @@ -0,0 +1,68 @@ + +
Edit Cs378x (Camd35 TCP) Config
Port:port[@CAID[:ident][,ident]...][;port[@CAID[:ident][,ident]...]]...
Serverip:
Suppress cmd08:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webif/config/cccam_control.html b/webif/config/cccam_control.html new file mode 100644 index 0000000..c17a1af --- /dev/null +++ b/webif/config/cccam_control.html @@ -0,0 +1,19 @@ +
Edit Cccam Config
Port:
Serverip:
Reshare: -1 no reshare or lowest value off cccreshare reader account
Ignore reshare: + +
Forward origin card: + +
Stealth mode: + +
Node Id:
Keep clients connected: + +
Version: + +
Update Interval: s
Receive timeout: ms
Minimize cards: + +
Reshare mode: + +
+ + + + + +
Global List Control
+
+ + +
+
+
+ + +       + +
+
diff --git a/webif/config/cccreshare.html b/webif/config/cccreshare.html new file mode 100644 index 0000000..846878b --- /dev/null +++ b/webif/config/cccreshare.html @@ -0,0 +1,6 @@ + +Reshare ccc-cards -> gbox-net + + + + diff --git a/webif/config/config.html b/webif/config/config.html new file mode 100644 index 0000000..e7b7b4d --- /dev/null +++ b/webif/config/config.html @@ -0,0 +1,18 @@ +##TPLHEADERSHORT## + +##TPLBODY## +##TPLMENU## +##TPLCONFIGMENU## +##TPLMESSAGE## +
+ +##CONFIG_CONTENT## + + + +
+##CONFIG_CONTROL## +##TPLFOOTER## diff --git a/webif/config/dvbapi.html b/webif/config/dvbapi.html new file mode 100644 index 0000000..33f0d5b --- /dev/null +++ b/webif/config/dvbapi.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webif/config/dvbapi_extended_cw_api.html b/webif/config/dvbapi_extended_cw_api.html new file mode 100644 index 0000000..aaa0c8f --- /dev/null +++ b/webif/config/dvbapi_extended_cw_api.html @@ -0,0 +1,9 @@ + + + diff --git a/webif/config/gbox.html b/webif/config/gbox.html new file mode 100644 index 0000000..d9f77e7 --- /dev/null +++ b/webif/config/gbox.html @@ -0,0 +1,73 @@ + +
Edit DVB Api Config
Enabled: +
AU: +
Boxtype:
User:
PMT Mode: + +
Request Mode: + +
Listen port: TCP port to listen instead of camd.socket
Server IP:
Delayer: ms
Enable ecm.info file: +
ecm.info type: + +
Detect channel name: + +
Write detected prov name to srvid: +##TPLEXTENDEDCWAPI## +##TPLDEMUXERFIX## diff --git a/webif/config/dvbapi_demuxerfix.html b/webif/config/dvbapi_demuxerfix.html new file mode 100644 index 0000000..55ff073 --- /dev/null +++ b/webif/config/dvbapi_demuxerfix.html @@ -0,0 +1,2 @@ +
Refers to Stream Relay
Fix Demuxer:
API for extended CWs + +
+ + + + + + + + + + +##CCCDEPENDINGCONFIG## + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webif/config/global.html b/webif/config/global.html new file mode 100644 index 0000000..140b232 --- /dev/null +++ b/webif/config/global.html @@ -0,0 +1,170 @@ + + +
Edit Gbox Config
My Port(s):Maximum 32 UDP ports, comma-separated

My Hostname / IP:
My Password:
Reconnect time: sec - default:180, min:60, max:300
My Version:
Informations:
Gbox tmp dir: default: Oscam tmp dir
Ignored peer(s):Maximum 16 peers (4 hex chars each), comma-separated

Block ECM for peer(s):Maximum 16 peers (4 hex chars each), comma-separated

Accept REMM from peer(s): max 8 peers, comma-separated
Proxy cards:Maximum 32 Cards (8 hex chars each), comma-separated

GBox Short Message Service - Send GSMS
Save this GSMS in conf:
Message type: +
Destination peer(s):Maximum 16 peers, comma separated. For broadcast to all use FFFF.
+ +
Text to send:From 6 to 127 alphanumerical characters.
+ +
Action for GSMS + + + +
+ + + + + + + + + + +##TPLLOCALCARDS## + +##TPLUNLOCKPARENTAL## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +##TPLCACHEEXAIOLOGGING## + + + + + + + + + + + + + + + + + + + + + + + + + + + +##TPLSUPPRESSCMD08## +##TPLGETBLOCKEMMAUPROVID## +##TPLENABLELEDBIT## diff --git a/webif/config/global_cacheex_aio_logging.html b/webif/config/global_cacheex_aio_logging.html new file mode 100644 index 0000000..733c9a3 --- /dev/null +++ b/webif/config/global_cacheex_aio_logging.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/webif/config/global_enableledbit.html b/webif/config/global_enableledbit.html new file mode 100644 index 0000000..e8102be --- /dev/null +++ b/webif/config/global_enableledbit.html @@ -0,0 +1,2 @@ + + diff --git a/webif/config/global_getblockemmauprovid.html b/webif/config/global_getblockemmauprovid.html new file mode 100644 index 0000000..195b160 --- /dev/null +++ b/webif/config/global_getblockemmauprovid.html @@ -0,0 +1 @@ + diff --git a/webif/config/global_localcards.html b/webif/config/global_localcards.html new file mode 100644 index 0000000..f414a39 --- /dev/null +++ b/webif/config/global_localcards.html @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/webif/config/global_suppresscmd08.html b/webif/config/global_suppresscmd08.html new file mode 100644 index 0000000..11b0d3c --- /dev/null +++ b/webif/config/global_suppresscmd08.html @@ -0,0 +1,2 @@ + + diff --git a/webif/config/global_unlockparental.html b/webif/config/global_unlockparental.html new file mode 100644 index 0000000..f12b654 --- /dev/null +++ b/webif/config/global_unlockparental.html @@ -0,0 +1 @@ + diff --git a/webif/config/lcd.html b/webif/config/lcd.html new file mode 100644 index 0000000..1d2bc01 --- /dev/null +++ b/webif/config/lcd.html @@ -0,0 +1,7 @@ + +
Edit Global Config
Serverip:
Nice:
Net prio: + +
Bind wait: s
Resolver: + +
Prefer local cards:
SIGHUP reload:
Simple block:
Drop duplicate users: + +
Skip CWs checksum test: + +
Skip CWs checksum test only for: +
Logging
Usr file:
Mail file:
Log file / max size:
Log duplicated lines:
Initial debug level:
Pid file:
CW log dir:
EMM log dir:
ECM log format:
Loghistory Lines:
Syslog server:
Syslog port:
Failban
Failban time: min blocking IP based
Failban count: chances with wrong credentials
Timeouts / Times
Client timeout: ms to give up and return timeout
Client timeout per caid: (Format: CAID:time)
Fallback timeout: ms to switch to fallback reader
Fallback timeout per caid:
Client max idle: s to disconnect idle clients
Global sleep: min to switch a client in sleepmode
Reader restart seconds: s waittime to restart a reader
Doublecheck
ECM Doublecheck:
Doublecheck caids:
Ai Fake DCW Detector
Ai Vote Enabled:
Ai Vote Log Enabled:
Ai Vote Timeout (ms): (default 1000)
Ai Vote Min Votes: (default min. 2)
Ai Vote Local Weight: (default 2.0)
Ai Vote Max Candidates: (default 8 caution! use max 16)
Ai Vote Compare Length: + +
Ai Vote Fallback Mode: + +
Ai Vote CAIDs:
CacheEX AIO Logging
Show CacheEX Source in WebIF:
LED
Enable LED:
Get BlockEmm & AU Provid Settings from Server:
Wait for cards: + +
Wait for cards delay: ms
Cs357x/Cs378x
Suppress cmd08:
Unlock parental:
+ + + + + diff --git a/webif/config/loadbalancer.html b/webif/config/loadbalancer.html new file mode 100644 index 0000000..5807a8a --- /dev/null +++ b/webif/config/loadbalancer.html @@ -0,0 +1,35 @@ + +
LCD Config
Enable LCD:
LCD Output Path:
LCD Write Interval:
LCD Hide idle Readers:
+ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webif/config/loadbalancer_control.html b/webif/config/loadbalancer_control.html new file mode 100644 index 0000000..f44babd --- /dev/null +++ b/webif/config/loadbalancer_control.html @@ -0,0 +1,16 @@ + + +
Edit Loadbalancer Config
Loadbalance Mode: + +
Loadbalance save every: ECM's
Statistics save path:
Number of best readers:
Number of best readers per caid:
Number of fallback readers:
Max Readers:
Min ECM count:
Max ECM count:
Retry limit: ms
Special retry limit per caid:
Time to reopen: s
Hours to cleanup older than: h
Reopen Invalid:
Force always reopen:
Ignore provider for:
Auto Betatunnel:
Auto Betatunnel Mode:
Prefer Beta over Nagra: %
Auto timeout:
Auto timeout percent: %
Auto timeout time: ms
+ + + + + + + + + + + +
Loadbalancer Statistic Control
+ diff --git a/webif/config/menu.html b/webif/config/menu.html new file mode 100644 index 0000000..d3733f8 --- /dev/null +++ b/webif/config/menu.html @@ -0,0 +1,23 @@ + + diff --git a/webif/config/menu_anticasc.html b/webif/config/menu_anticasc.html new file mode 100644 index 0000000..c8526f4 --- /dev/null +++ b/webif/config/menu_anticasc.html @@ -0,0 +1 @@ +
  • Anticascading
  • \ No newline at end of file diff --git a/webif/config/menu_camd33.html b/webif/config/menu_camd33.html new file mode 100644 index 0000000..1e8467e --- /dev/null +++ b/webif/config/menu_camd33.html @@ -0,0 +1 @@ +
  • Camd3.3
  • \ No newline at end of file diff --git a/webif/config/menu_camd35.html b/webif/config/menu_camd35.html new file mode 100644 index 0000000..9362207 --- /dev/null +++ b/webif/config/menu_camd35.html @@ -0,0 +1 @@ +
  • Cs357x
  • \ No newline at end of file diff --git a/webif/config/menu_camd35tcp.html b/webif/config/menu_camd35tcp.html new file mode 100644 index 0000000..57f54e8 --- /dev/null +++ b/webif/config/menu_camd35tcp.html @@ -0,0 +1 @@ +
  • Cs378x
  • \ No newline at end of file diff --git a/webif/config/menu_cccam.html b/webif/config/menu_cccam.html new file mode 100644 index 0000000..3c73e89 --- /dev/null +++ b/webif/config/menu_cccam.html @@ -0,0 +1 @@ +
  • CCcam
  • \ No newline at end of file diff --git a/webif/config/menu_cmcaptioncwc.html b/webif/config/menu_cmcaptioncwc.html new file mode 100644 index 0000000..2b93cd5 --- /dev/null +++ b/webif/config/menu_cmcaptioncwc.html @@ -0,0 +1 @@ + / CW Cycle \ No newline at end of file diff --git a/webif/config/menu_dvbapi.html b/webif/config/menu_dvbapi.html new file mode 100644 index 0000000..ad72bee --- /dev/null +++ b/webif/config/menu_dvbapi.html @@ -0,0 +1 @@ +
  • DVB-Api
  • \ No newline at end of file diff --git a/webif/config/menu_gbox.html b/webif/config/menu_gbox.html new file mode 100644 index 0000000..91fed32 --- /dev/null +++ b/webif/config/menu_gbox.html @@ -0,0 +1 @@ +
  • GBox
  • \ No newline at end of file diff --git a/webif/config/menu_lcd.html b/webif/config/menu_lcd.html new file mode 100644 index 0000000..0fbf191 --- /dev/null +++ b/webif/config/menu_lcd.html @@ -0,0 +1 @@ +
  • LCD
  • \ No newline at end of file diff --git a/webif/config/menu_loadbalancer.html b/webif/config/menu_loadbalancer.html new file mode 100644 index 0000000..079ce5a --- /dev/null +++ b/webif/config/menu_loadbalancer.html @@ -0,0 +1 @@ +
  • Loadbalancer
  • \ No newline at end of file diff --git a/webif/config/menu_monitor.html b/webif/config/menu_monitor.html new file mode 100644 index 0000000..0651181 --- /dev/null +++ b/webif/config/menu_monitor.html @@ -0,0 +1 @@ +
  • Monitor
  • \ No newline at end of file diff --git a/webif/config/menu_newcamd.html b/webif/config/menu_newcamd.html new file mode 100644 index 0000000..8ef9897 --- /dev/null +++ b/webif/config/menu_newcamd.html @@ -0,0 +1 @@ +
  • Newcamd
  • \ No newline at end of file diff --git a/webif/config/menu_radegast.html b/webif/config/menu_radegast.html new file mode 100644 index 0000000..fab21ba --- /dev/null +++ b/webif/config/menu_radegast.html @@ -0,0 +1 @@ +
  • Radegast
  • \ No newline at end of file diff --git a/webif/config/menu_scam.html b/webif/config/menu_scam.html new file mode 100644 index 0000000..1707c50 --- /dev/null +++ b/webif/config/menu_scam.html @@ -0,0 +1 @@ +
  • Scam
  • \ No newline at end of file diff --git a/webif/config/menu_serial.html b/webif/config/menu_serial.html new file mode 100644 index 0000000..b26a822 --- /dev/null +++ b/webif/config/menu_serial.html @@ -0,0 +1 @@ +
  • Serial
  • \ No newline at end of file diff --git a/webif/config/menu_streamrelay.html b/webif/config/menu_streamrelay.html new file mode 100644 index 0000000..ae9a86e --- /dev/null +++ b/webif/config/menu_streamrelay.html @@ -0,0 +1 @@ +
  • Stream Relay
  • diff --git a/webif/config/monitor.html b/webif/config/monitor.html new file mode 100644 index 0000000..b60ad9b --- /dev/null +++ b/webif/config/monitor.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/webif/config/newcamd.html b/webif/config/newcamd.html new file mode 100644 index 0000000..651c1b0 --- /dev/null +++ b/webif/config/newcamd.html @@ -0,0 +1,11 @@ + + + +
    Edit Monitor Config
    Port:
    Serverip:
    No crypt:
    Au low: min
    Hide client to: s
    Monlevel: +
    + + + + + + + diff --git a/webif/config/radegast.html b/webif/config/radegast.html new file mode 100644 index 0000000..a099d2b --- /dev/null +++ b/webif/config/radegast.html @@ -0,0 +1,7 @@ + +
    Edit Newcamd Config
    Port:
    Serverip:
    Key:
    Allowed:
    Keepalive:
    Mgclient:
    + + + + + diff --git a/webif/config/scam.html b/webif/config/scam.html new file mode 100644 index 0000000..3b4b149 --- /dev/null +++ b/webif/config/scam.html @@ -0,0 +1,6 @@ + +
    Edit Radegast Config
    Port:
    Serverip:
    User:
    Allowed:
    + + + + diff --git a/webif/config/serial.html b/webif/config/serial.html new file mode 100644 index 0000000..b331f72 --- /dev/null +++ b/webif/config/serial.html @@ -0,0 +1,4 @@ + +
    Edit Scam Config
    Port:
    Serverip:
    Allowed:
    + +##DEVICES## diff --git a/webif/config/serial_devices.html b/webif/config/serial_devices.html new file mode 100644 index 0000000..1fdf9ba --- /dev/null +++ b/webif/config/serial_devices.html @@ -0,0 +1 @@ + diff --git a/webif/config/streamrelay.html b/webif/config/streamrelay.html new file mode 100644 index 0000000..3804d00 --- /dev/null +++ b/webif/config/streamrelay.html @@ -0,0 +1,44 @@ + +
    Edit Serial Config
    Device:
    + + + + + + + + + + + + + + + +##TPLSTREAMEMUSETTINGS## + + + diff --git a/webif/config/streamrelay_emusettings.html b/webif/config/streamrelay_emusettings.html new file mode 100644 index 0000000..dcb0618 --- /dev/null +++ b/webif/config/streamrelay_emusettings.html @@ -0,0 +1,9 @@ + + + + diff --git a/webif/config/webif.html b/webif/config/webif.html new file mode 100644 index 0000000..be458f0 --- /dev/null +++ b/webif/config/webif.html @@ -0,0 +1,104 @@ + + + + +
    Edit Stream Relay Config
    Mode (requires OSCam restart) + +
    Relay Port:
    Relay User:
    CAID:
    Source Stream Host:
    Source Stream Port:
    Source Stream User:
    Source Stream Password:
    Relay Buffer Time:
    Relay Reconnect Count:
    Relay Client Display Options:
    Process EMM from stream (emu): + +
    ECM fix delay (emu):
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +##CONFIGWEBIFJQUERY## + + + + + + + + + + + + + + + +##TPLHTTPSSL## diff --git a/webif/config/webif_httpssl.html b/webif/config/webif_httpssl.html new file mode 100644 index 0000000..fea8edf --- /dev/null +++ b/webif/config/webif_httpssl.html @@ -0,0 +1,4 @@ + + + + diff --git a/webif/config/webif_show_jquery.html b/webif/config/webif_show_jquery.html new file mode 100644 index 0000000..27d4a43 --- /dev/null +++ b/webif/config/webif_show_jquery.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webif/config/webif_showcacheexinfo.html b/webif/config/webif_showcacheexinfo.html new file mode 100644 index 0000000..7ae9036 --- /dev/null +++ b/webif/config/webif_showcacheexinfo.html @@ -0,0 +1 @@ + diff --git a/webif/emm/emm.html b/webif/emm/emm.html new file mode 100644 index 0000000..d2c8be2 --- /dev/null +++ b/webif/emm/emm.html @@ -0,0 +1,80 @@ +##TPLHEADER## +##TPLMENU## +##TPLMESSAGE## + + + + +
    Edit WebIf Config
    Http port:
    Serverip:
    Http user:
    Http pwd:
    OSCam Label:
    Refresh:
    Http allowed:
    Http Help Language: (en|de|fr|it)
    Http Locale: e.g. en_US, de_DE.utf8 (if available and works on the server)
    Http dyndns:
    Au low: min
    Save configuration behavior:
    Emm settings
    Http emmlog cleanup size:
    Css style settings
    Http prepend embedded css:
    Http css: + +
    Script settings
    Http javascript:
    Http script:
    Picons settings
    Http Show Picons:
    Path for picons:
    Show/Hide in Status
    Http Hide Type:
    Http Hide Idle Clients:
    Show in Status:
    Template settings
    Http tpl:
    SSL
    SSL Certificate:
    Force secure HTTPS mode:
    Auto create certificate:
    jQuery Source:
    + + + + + + +
    Selected reader: ##READER##
    CAID:
    Single EMM to write:
    File path with EMM's:
    +
    +

    Saved EMM's for Reader: ##READER##

    +

    Unique EMM    ##RDREMMUNIQUE_TXT##

    +
    ##RDREMMUNIQUE##
    +

    Shared EMM    ##RDREMMSHARED_TXT##

    +
    ##RDREMMSHARED##
    +

    Global EMM    ##RDREMMGLOBAL_TXT##

    +
    ##RDREMMGLOBAL##
    +
    + +##TPLFOOTER## diff --git a/webif/emm_running/emm_running.html b/webif/emm_running/emm_running.html new file mode 100644 index 0000000..e9bc680 --- /dev/null +++ b/webif/emm_running/emm_running.html @@ -0,0 +1,24 @@ +##TPLHEADER## +##TPLMENU## +##TPLMESSAGE## + +
    + + + + + + + + + + + + + + +
    Selected reader : ##READER##
    System:##SYSTEM##
    CAID:##CAID##
    SINGLE EMM
    EMM:
    Size: ##SIZE##
    EMM FILE
    File path:
    File size: ##FSIZE##
    Num of read lines: ##NUMRLINE##
    Lines with errors: ##ERRLINE##
    Num of written EMMs: ##NUMWEMM##
    +
    +##TPLFOOTER## diff --git a/webif/entitlements/entitlements.html b/webif/entitlements/entitlements.html new file mode 100644 index 0000000..9d31761 --- /dev/null +++ b/webif/entitlements/entitlements.html @@ -0,0 +1,10 @@ +##TPLHEADER## +##TPLMENU## +##TPLMESSAGE## + +##ENTITLEMENTCONTENT## +##TPLFOOTER## diff --git a/webif/entitlements/entitlements_bit.html b/webif/entitlements/entitlements_bit.html new file mode 100644 index 0000000..d679a7b --- /dev/null +++ b/webif/entitlements/entitlements_bit.html @@ -0,0 +1,28 @@ + + + + + + + + + + +##ENTITLEMENTCONTENTNDS## + +##READERENTENTRY## +
    CardsystemValid ToIRD ID (NAGRA)MaturityProvider
    ##READERCSYSTEM####READERCARDVALIDTO####READERMATURITY##
    SerialROMATR
    ##READERROM####READERATR##
    TypeCAIDProvidIDClassStart DateExpire DateName
    diff --git a/webif/entitlements/entitlements_bit_nds.html b/webif/entitlements/entitlements_bit_nds.html new file mode 100644 index 0000000..5fb2b7f --- /dev/null +++ b/webif/entitlements/entitlements_bit_nds.html @@ -0,0 +1,2 @@ + Country Code (NDS)Region Code (NDS)Pin (NDS)Credit (NDS)Fuse (NDS)Payload (NDS) 0F 06 + ##READERCOUNTRYC####READER_RCODE####READERPIN####READERCREDIT####READERFUSE####READERPAYLOAD## diff --git a/webif/entitlements/entitlements_cccambit.html b/webif/entitlements/entitlements_cccambit.html new file mode 100644 index 0000000..0149313 --- /dev/null +++ b/webif/entitlements/entitlements_cccambit.html @@ -0,0 +1,6 @@ + + +##CCCAMSTATSENTRY## +
    HostCaidSystemTypeShare idRemote idUphopsReshareProvidersNodesGood sidsBad sids
    +
    ##TOTALS##
    +
    ##CONTROLS##
    diff --git a/webif/entitlements/entitlements_cccambit_statsentry.html b/webif/entitlements/entitlements_cccambit_statsentry.html new file mode 100644 index 0000000..d066598 --- /dev/null +++ b/webif/entitlements/entitlements_cccambit_statsentry.html @@ -0,0 +1 @@ + ##HOST####CAID####SYSTEM####CARDTYPE####SHAREID####REMOTEID####UPHOPS####MAXDOWN####PROVIDERS####NODES####SERVICESGOOD####SERVICESBAD## diff --git a/webif/entitlements/entitlements_genericbit.html b/webif/entitlements/entitlements_genericbit.html new file mode 100644 index 0000000..8beb8e8 --- /dev/null +++ b/webif/entitlements/entitlements_genericbit.html @@ -0,0 +1,4 @@ +
    +##LOGSUMMARY## +##LOGHISTORY## +
    diff --git a/webif/entitlements/entitlements_itembit.html b/webif/entitlements/entitlements_itembit.html new file mode 100644 index 0000000..70655cb --- /dev/null +++ b/webif/entitlements/entitlements_itembit.html @@ -0,0 +1 @@ + ##ENTTYPE####ENTCAID####ENTPROVID####ENTID####ENTCLASS####ENTSTARTDATE####ENTENDDATE####ENTRESNAME## diff --git a/webif/failban/failban.html b/webif/failban/failban.html new file mode 100644 index 0000000..1f17eb2 --- /dev/null +++ b/webif/failban/failban.html @@ -0,0 +1,14 @@ +##TPLHEADER## +##TPLMENU## +##TPLMESSAGE## + + + + +##FAILBANROW## +
    List of banned IP Addresses
    UserIP AddressViolation dateViolation countLeft ban timeAction
    +##TPLFOOTER## \ No newline at end of file diff --git a/webif/failban/failban_failbanrow.html b/webif/failban/failban_failbanrow.html new file mode 100644 index 0000000..79f9cba --- /dev/null +++ b/webif/failban/failban_failbanrow.html @@ -0,0 +1 @@ + ##VIOLATIONUSER####IPADDRESS####VIOLATIONDATE####VIOLATIONCOUNT####LEFTTIME## Delete Entry diff --git a/webif/files/file.html b/webif/files/file.html new file mode 100644 index 0000000..abe2585 --- /dev/null +++ b/webif/files/file.html @@ -0,0 +1,15 @@ +##TPLHEADER## +##TPLMENU## +##TPLFILEMENU## + +

    ##APIFILENAME##

    +##TPLMESSAGE## +##SDEBUG## +
    ##LOGMENU##
    +
    ##FILTERFORM##
    +
    + + +

    ##WRITEPROTECTION##

    +
    +##TPLFOOTER## \ No newline at end of file diff --git a/webif/files/file_edit_css.html b/webif/files/file_edit_css.html new file mode 100644 index 0000000..97c42dc --- /dev/null +++ b/webif/files/file_edit_css.html @@ -0,0 +1 @@ +
  • ##FILE_USER_CSS##
  • \ No newline at end of file diff --git a/webif/files/file_filterform.html b/webif/files/file_filterform.html new file mode 100644 index 0000000..1efe1fd --- /dev/null +++ b/webif/files/file_filterform.html @@ -0,0 +1,7 @@ +
    + + + +
    diff --git a/webif/files/file_writeprotection.html b/webif/files/file_writeprotection.html new file mode 100644 index 0000000..3137b3a --- /dev/null +++ b/webif/files/file_writeprotection.html @@ -0,0 +1 @@ +You cannot change the content of this file!
    diff --git a/webif/files/menu.html b/webif/files/menu.html new file mode 100644 index 0000000..2e4e6b6 --- /dev/null +++ b/webif/files/menu.html @@ -0,0 +1,29 @@ + diff --git a/webif/files/menu_anticasc.html b/webif/files/menu_anticasc.html new file mode 100644 index 0000000..0a3e990 --- /dev/null +++ b/webif/files/menu_anticasc.html @@ -0,0 +1 @@ +
  • ac log
  • diff --git a/webif/files/menu_constantcw.html b/webif/files/menu_constantcw.html new file mode 100644 index 0000000..159662a --- /dev/null +++ b/webif/files/menu_constantcw.html @@ -0,0 +1 @@ +
  • constant.cw
  • \ No newline at end of file diff --git a/webif/files/menu_dvbapi.html b/webif/files/menu_dvbapi.html new file mode 100644 index 0000000..c5f6520 --- /dev/null +++ b/webif/files/menu_dvbapi.html @@ -0,0 +1 @@ +
  • oscam.dvbapi
  • \ No newline at end of file diff --git a/webif/files/menu_fakecws.html b/webif/files/menu_fakecws.html new file mode 100644 index 0000000..ea15fe7 --- /dev/null +++ b/webif/files/menu_fakecws.html @@ -0,0 +1 @@ +
  • oscam.fakecws
  • \ No newline at end of file diff --git a/webif/files/menu_gbox.html b/webif/files/menu_gbox.html new file mode 100644 index 0000000..f67d6aa --- /dev/null +++ b/webif/files/menu_gbox.html @@ -0,0 +1,14 @@ +
  • gbox files + diff --git a/webif/files/menu_softcamkey.html b/webif/files/menu_softcamkey.html new file mode 100644 index 0000000..cf71877 --- /dev/null +++ b/webif/files/menu_softcamkey.html @@ -0,0 +1 @@ +
  • SoftCam.Key
  • diff --git a/webif/files/menu_twin.html b/webif/files/menu_twin.html new file mode 100644 index 0000000..46365c9 --- /dev/null +++ b/webif/files/menu_twin.html @@ -0,0 +1 @@ +
  • oscam.twin
  • \ No newline at end of file diff --git a/webif/ghttp/autoconf.html b/webif/ghttp/autoconf.html new file mode 100644 index 0000000..f2925a3 --- /dev/null +++ b/webif/ghttp/autoconf.html @@ -0,0 +1,5 @@ +##TPLHEADER## +##TPLMENU## +##TPLMESSAGE## +

    GHttp auto-configured - Ready in ##SECONDS## Seconds



    +##TPLFOOTER## diff --git a/webif/ghttp/pre_autoconf.html b/webif/ghttp/pre_autoconf.html new file mode 100644 index 0000000..e50fe52 --- /dev/null +++ b/webif/ghttp/pre_autoconf.html @@ -0,0 +1,24 @@ +##TPLHEADER## +##TPLMENU## +##TPLMESSAGE## +


    +
    Auto-configure GHttp?
    Any existing GHttp readers will be replaced.
    + 3 new GHttp readers will be created (2 primary and 1 fallback).
    +
    +
    +
    + + + + + + + + + + + +
    User ##USERREQ##Password ##PWDREQ##Name ##NAMEREQ##

    + +
    +##TPLFOOTER## diff --git a/webif/graph/graph.svg b/webif/graph/graph.svg new file mode 100644 index 0000000..c35b3ba --- /dev/null +++ b/webif/graph/graph.svg @@ -0,0 +1,249 @@ + + + ##HTTPOSCAMLABEL## ##CS_VERSION## (ECM Graph) + + + Error occured! + + + + Change + - + + + - + - + - + - + + + diff --git a/webif/images/ICARRL.svg b/webif/images/ICARRL.svg new file mode 100644 index 0000000..b955a6e --- /dev/null +++ b/webif/images/ICARRL.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIzNnB4IiBoZWlnaHQ9IjEycHgiIHZpZXdCb3g9IjAgMCAzNiAxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMzYgMTIiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnIGlkPSJDIj48ZGVmcz48cG9seWdvbiBpZD0iTSIgcG9pbnRzPSIzNiwxMiA2LDEyIDAsNiA2LDAgMzYsMCAzMCw2IiAvPjwvZGVmcz48Y2xpcFBhdGggaWQ9IlkiPjx1c2UgeGxpbms6aHJlZj0iI00iIC8+PC9jbGlwUGF0aD48cmVjdCBjbGlwLXBhdGg9InVybCgjWSkiIGZpbGw9IiNDRjMiIHdpZHRoPSIzNiIgaGVpZ2h0PSIxMiIgLz48ZyBpZD0iYXJyb3dzIiBjbGlwLXBhdGg9InVybCgjWSkiPjxnPjxwb2x5Z29uIGZpbGw9IiMwOTMiIHBvaW50cz0iMzYsNiA0MiwwIDQ1LDAgMzksNiA0NSwxMiA0MiwxMiIgLz48cG9seWdvbiBmaWxsPSIjMDkzIiBwb2ludHM9IjMwLDYgMzYsMCAzOSwwIDMzLDYgMzksMTIgMzYsMTIiIC8+PHBvbHlnb24gZmlsbD0iIzA5MyIgcG9pbnRzPSIyNCw2IDMwLDAgMzMsMCAyNyw2IDMzLDEyIDMwLDEyIiAvPjxwb2x5Z29uIGZpbGw9IiMwOTMiIHBvaW50cz0iMTgsNiAyNCwwIDI3LDAgMjEsNiAyNywxMiAyNCwxMiIgLz48cG9seWdvbiBmaWxsPSIjMDkzIiBwb2ludHM9IjEyLDYgMTgsMCAyMSwwIDE1LDYgMjEsMTIgMTgsMTIiIC8+PHBvbHlnb24gZmlsbD0iIzA5MyIgcG9pbnRzPSI2LDYgMTIsMCAxNSwwIDksNiAxNSwxMiAxMiwxMiIgLz48cG9seWdvbiBmaWxsPSIjMDkzIiBwb2ludHM9IjAsNiA2LDAgOSwwIDMsNiA5LDEyIDYsMTIiIC8+PHBvbHlnb24gZmlsbD0iIzA5MyIgcG9pbnRzPSItNiw2IDAsMCAzLDAgLTMsNiAzLDEyIDAsMTIiIC8+PGFuaW1hdGVNb3Rpb24gZnJvbT0iMC4wLDAuMCIgdG89Ii02LjAsMC4wIiBkdXI9IjAuNXMiIHJlcGVhdER1cj0iaW5kZWZpbml0ZSIgLz48L2c+PHJlY3QgeT0iNiIgb3BhY2l0eT0iMC4zIiBjbGlwLXBhdGg9InVybCgjWSkiIHdpZHRoPSIzNiIgaGVpZ2h0PSI2IiAvPjwvZz48L2c+PC9zdmc+ diff --git a/webif/images/ICARRR.svg b/webif/images/ICARRR.svg new file mode 100644 index 0000000..f8f0bdb --- /dev/null +++ b/webif/images/ICARRR.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIzNnB4IiBoZWlnaHQ9IjEycHgiIHZpZXdCb3g9IjAgMCAzNiAxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMzYgMTIiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnIGlkPSJjbGlwZ3JvdXAiPjxkZWZzPjxwb2x5Z29uIGlkPSJNIiBwb2ludHM9IjAsMCAzMCwwIDM2LDYgMzAsMTIgMCwxMiA2LDYgIi8+PC9kZWZzPjxjbGlwUGF0aCBpZD0iWSI+PHVzZSB4bGluazpocmVmPSIjTSIvPjwvY2xpcFBhdGg+PHJlY3QgY2xpcC1wYXRoPSJ1cmwoI1kpIiBmaWxsPSIjQ0NGRjMzIiB3aWR0aD0iMzYiIGhlaWdodD0iMTIiLz48ZyBpZD0iYXJyb3dzIiBjbGlwLXBhdGg9InVybCgjWSkiPjxnPjxwb2x5Z29uIGZpbGw9IiMwOTMiIHBvaW50cz0iMCw2IC02LDEyIC05LDEyIC0zLDYgLTksMCAtNiwwICIvPjxwb2x5Z29uIGZpbGw9IiMwOTMiIHBvaW50cz0iNiw2IDAsMTIgLTMsMTIgMyw2IC0zLDAgMCwwICIvPjxwb2x5Z29uIGZpbGw9IiMwOTMiIHBvaW50cz0iMTIsNiA2LDEyIDMsMTIgOSw2IDMsMCA2LDAgIi8+PHBvbHlnb24gZmlsbD0iIzA5MyIgcG9pbnRzPSIxOCw2IDEyLDEyIDksMTIgMTUsNiA5LDAgMTIsMCAiLz48cG9seWdvbiBmaWxsPSIjMDkzIiBwb2ludHM9IjI0LDYgMTgsMTIgMTUsMTIgMjEsNiAxNSwwIDE4LDAgIi8+PHBvbHlnb24gZmlsbD0iIzA5MyIgcG9pbnRzPSIzMCw2IDI0LDEyIDIxLDEyIDI3LDYgMjEsMCAyNCwwICIvPjxwb2x5Z29uIGZpbGw9IiMwOTMiIHBvaW50cz0iMzYsNiAzMCwxMiAyNywxMiAzMyw2IDI3LDAgMzAsMCAiLz48cG9seWdvbiBmaWxsPSIjMDkzIiBwb2ludHM9IjQyLDYgMzYsMTIgMzMsMTIgMzksNiAzMywwIDM2LDAgIi8+PGFuaW1hdGVNb3Rpb24gZnJvbT0iMC4wLDAuMCIgdG89IjYuMCwwLjAiIGR1cj0iMC41cyIgcmVwZWF0RHVyPSJpbmRlZmluaXRlIiAvPjwvZz48cmVjdCB5PSI2IiBvcGFjaXR5PSIwLjMiIGNsaXAtcGF0aD0idXJsKCNZKSIgd2lkdGg9IjM2IiBoZWlnaHQ9IjYiLz48L2c+PC9nPjwvc3ZnPg== diff --git a/webif/images/ICDEL.svg b/webif/images/ICDEL.svg new file mode 100644 index 0000000..5fb8290 --- /dev/null +++ b/webif/images/ICDEL.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJUcmFzaCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjIgMjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxyZWN0IGZpbGw9Im5vbmUiIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIvPgogIDxyZWN0IHg9IjQiIHk9IjUiIGZpbGw9IiNGRkZGRkYiIHdpZHRoPSIxNCIgaGVpZ2h0PSIzIi8+CiAgPHJlY3QgeD0iMTMuOTM4IiB5PSIzIiBmaWxsPSIjRkZGRkZGIiB3aWR0aD0iMSIgaGVpZ2h0PSIyIi8+CiAgPHJlY3QgeD0iNi45MzgiIHk9IjMiIGZpbGw9IiNGRkZGRkYiIHdpZHRoPSIxIiBoZWlnaHQ9IjIiLz4KICA8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNNi45MzggM2MwLTAuNTUgMC40NS0xIDEtMWg2YzAuNTUgMCAxIDAuNDUgMSAxSDYuOTM4eiIvPgogIDxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xNyAyMEg1VjloMTJWMjB6TTEwIDExSDh2N2gyVjExek0xNCAxMWgtMnY3aDJWMTF6Ii8+Cjwvc3ZnPgo= \ No newline at end of file diff --git a/webif/images/ICDIS.svg b/webif/images/ICDIS.svg new file mode 100644 index 0000000..e8fb720 --- /dev/null +++ b/webif/images/ICDIS.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjIycHgiIGhlaWdodD0iMjJweCIgdmlld0JveD0iMCAwIDIyIDIyIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyMiAyMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xMSAzYy00LjQgMC04IDMuNi04IDhzMy42IDggOCA4IDgtMy42IDgtOFMxNS40IDMgMTEgM3pNMTEgMTYuNWMtMyAwLTUuNS0yLjUtNS41LTUuNUM1LjUgOCA4IDUuNSAxMSA1LjVTMTYuNSA4IDE2LjUgMTFDMTYuNSAxNCAxNCAxNi41IDExIDE2LjV6Ii8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiNGRkZGRkYiIGQ9Ik05LjggMTIuOGMwIDAuNyAwLjYgMS4zIDEuMyAxLjNzMS4zLTAuNiAxLjMtMS4zVjkuM0MxMi4zIDguNiAxMS43IDggMTEgOFM5LjggOC42IDkuOCA5LjNWMTIuOHoiLz48L3N2Zz4= \ No newline at end of file diff --git a/webif/images/ICEDI.svg b/webif/images/ICEDI.svg new file mode 100644 index 0000000..e012b18 --- /dev/null +++ b/webif/images/ICEDI.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJJQ0VESSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjIgMjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxyZWN0IGZpbGw9Im5vbmUiIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIvPgogIDxwb2x5Z29uIGZpbGw9IiNGRkZGRkYiIHBvaW50cz0iNCwxMyAzLDE5IDksMTggIi8+CiAgPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTUgMTJsOS40MTQtOS4yNDNjMC4zODktMC4zODkgMS4wMjUtMC4zODkgMS40MTQgMGwzLjQxNSAzLjQxNWMwLjM4OSAwLjM4OSAwLjM4OSAxLjAyNSAwIDEuNDE0TDEwIDE3IDUgMTJ6Ii8+Cjwvc3ZnPg== \ No newline at end of file diff --git a/webif/images/ICEMM.svg b/webif/images/ICEMM.svg new file mode 100644 index 0000000..a70e13c --- /dev/null +++ b/webif/images/ICEMM.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJJQ0VNTSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjIgMjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxyZWN0IGZpbGw9Im5vbmUiIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIvPgogIDxyZWN0IHg9IjkiIHk9IjMiIGZpbGw9IiNGRkZGRkYiIHdpZHRoPSI0IiBoZWlnaHQ9IjEzIi8+CiAgPHJlY3QgeD0iNSIgeT0iMTYiIGZpbGw9IiNGRkZGRkYiIHdpZHRoPSIxMiIgaGVpZ2h0PSIzIi8+CiAgPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTMuNzA3IDEwLjcwN0MzLjMxOCAxMC4zMTggMy40NSAxMCA0IDEwaDNsNCA0IDMuOTU4LTRIMThjMC41NSAwIDAuNjgyIDAuMzE4IDAuMjkzIDAuNzA3TDEzIDE2SDlMMy43MDcgMTAuNzA3eiIvPgo8L3N2Zz4= \ No newline at end of file diff --git a/webif/images/ICENA.svg b/webif/images/ICENA.svg new file mode 100644 index 0000000..d93fbf9 --- /dev/null +++ b/webif/images/ICENA.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjIycHgiIGhlaWdodD0iMjJweCIgdmlld0JveD0iMCAwIDIyIDIyIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyMiAyMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xMC41IDE3YzMuMyAwIDUuNi0yIDYtNC44IDAuMi0xLjctMC4zLTMuMS0xLjQtNC40IC0wLjItMC4yLTAuNC0wLjUtMC41LTAuOCAtMC4xLTAuNSAwLjItMSAwLjYtMS4yIDAuNS0wLjIgMS0wLjIgMS40IDAuMiAxLjIgMS4yIDEuOSAyLjYgMi4yIDQuMiAwLjcgNC40LTIuNSA4LjYtNyA5IC0zLjYgMC4zLTYuOS0xLjYtOC4yLTQuOSAtMS4xLTIuOC0wLjUtNi4xIDEuNy04LjIgMC41LTAuNSAxLjMtMC42IDEuNy0wLjFDNy41IDYuNiA3LjUgNy4zIDcgNy44Yy0xLjkgMi0yLjEgNS0wLjQgNy4xQzcuOCAxNi4zIDkuMiAxNyAxMC41IDE3eiIvPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsPSIjRkZGRkZGIiBkPSJNOS44IDEwLjFjMCAwLjcgMC42IDEuMyAxLjMgMS4zczEuMy0wLjYgMS4zLTEuM1Y0LjZjMC0wLjctMC42LTEuMy0xLjMtMS4zUzkuOCAzLjkgOS44IDQuNlYxMC4xeiIvPjwvc3ZnPg== \ No newline at end of file diff --git a/webif/images/ICENT.svg b/webif/images/ICENT.svg new file mode 100644 index 0000000..60737e7 --- /dev/null +++ b/webif/images/ICENT.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJJQ0VOVCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjIgMjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxyZWN0IGZpbGw9Im5vbmUiIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIvPgogIDxjaXJjbGUgZmlsbD0iI0ZGRkZGRiIgY3g9IjEzIiBjeT0iNSIgcj0iMiIvPgogIDxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xNC4wMDEgOC4wODdjMCAwLTIuOTU3IDguMjc4LTMuMDc1IDguNTk0czAuMTA2IDAuODMyIDAuNjU4IDAuMzE5IDEuMjc2LTEuNTA3IDEuMzk2LTEuNTQyYzAuMjM5LTAuMDY4IDAuNzY1IDAuMDczIDAuNDU4IDAuNzI5IC0wLjQ4MyAxLjAzNS0xLjM3NSAyLjYyNS0zLjE0MiAyLjkzOCAtMS44NyAwLjMzMS00LjEtMC4zMTUtMi44NzgtMy4xMTRzMi42NDEtNi42MjMgMi42NDEtNi42MjNMOC4xNjcgOS40MjhsMC4wNzktMC45ODVMMTQuMDAxIDguMDg3eiIvPgo8L3N2Zz4= \ No newline at end of file diff --git a/webif/images/ICHID.svg b/webif/images/ICHID.svg new file mode 100644 index 0000000..ffb6274 --- /dev/null +++ b/webif/images/ICHID.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJJQ0hJRCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjIgMjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxyZWN0IGZpbGw9Im5vbmUiIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIvPgogIDxyZWN0IHg9IjUiIHk9IjciIGZpbGw9IiNGRkZGRkYiIHdpZHRoPSI2IiBoZWlnaHQ9IjIiLz4KICA8Zz4KICAgIDxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0yMC4yOTMgMTguNzA3YzAuMzg5LTAuMzg5IDAuMzg5LTEuMDI1IDAtMS40MTRsLTUuNzQ2LTUuNzQ2Yy0wLjM4OS0wLjM4OS0wLjU1NC0xLjEzLTAuMzY2LTEuNjQ3IDAgMCAwLjMxOS0wLjg4IDAuMzE5LTEuOSAwLTMuNTktMi45MS02LjUtNi41LTYuNVMxLjUgNC40MSAxLjUgOHMyLjkxIDYuNSA2LjUgNi41YzEuMDIgMCAxLjktMC4zMTkgMS45LTAuMzE5IDAuNTE3LTAuMTg4IDEuMjU4LTAuMDIyIDEuNjQ3IDAuMzY2bDUuNzQ2IDUuNzQ2YzAuMzg5IDAuMzg5IDEuMDI1IDAuMzg5IDEuNDE0IDBMMjAuMjkzIDE4LjcwN3pNMy41IDhjMC0yLjQ4NSAyLjAxNS00LjUgNC41LTQuNXM0LjUgMi4wMTUgNC41IDQuNSAtMi4wMTUgNC41LTQuNSA0LjVTMy41IDEwLjQ4NSAzLjUgOHoiLz4KICA8L2c+Cjwvc3ZnPg== \ No newline at end of file diff --git a/webif/images/ICKIL.svg b/webif/images/ICKIL.svg new file mode 100644 index 0000000..5598ab1 --- /dev/null +++ b/webif/images/ICKIL.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJJQ0tJTCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjIgMjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxyZWN0IGZpbGw9Im5vbmUiIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIvPgogIDxjaXJjbGUgZmlsbD0iI0ZGRkZGRiIgY3g9IjEwLjgzMyIgY3k9IjEwLjk3OSIgcj0iMiIvPgogIDxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xMC44MzMgNy40NzljMC42MTQgMCAxLjE4MyAwLjE3MiAxLjY4NSAwLjQ1bDIuNzQ0LTQuODU2QzEzLjk5NCAyLjM4OSAxMi41NDMgMiAxMSAyIDkuMjU5IDIgNy42MzkgMi41MDIgNi4yNjIgMy4zNThMOS4wNyA3Ljk3MkM5LjU4OSA3LjY2NyAxMC4xODcgNy40NzkgMTAuODMzIDcuNDc5eiIvPgogIDxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xNC4zMzIgMTFjLTAuMDA4IDEuMjM3LTAuNjU5IDIuMzE1LTEuNjM0IDIuOTMybDIuOTE0IDQuNzg3QzE4LjIzNiAxNy4xNDYgMjAgMTQuMjgzIDIwIDExSDE0LjMzMnoiLz4KICA8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNNy4zMzUgMTFIMmMwIDMuMjkyIDEuNzczIDYuMTY0IDQuNDEyIDcuNzMybDIuNjc3LTQuNzM2QzguMDUgMTMuMzk1IDcuMzQzIDEyLjI4NiA3LjMzNSAxMXoiLz4KPC9zdmc+ \ No newline at end of file diff --git a/webif/images/ICMLOGO.svg b/webif/images/ICMLOGO.svg new file mode 100644 index 0000000..a088ad2 --- /dev/null +++ b/webif/images/ICMLOGO.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webif/images/ICREF.svg b/webif/images/ICREF.svg new file mode 100644 index 0000000..9557f72 --- /dev/null +++ b/webif/images/ICREF.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJJQ1JFRiIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjIgMjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxyZWN0IGZpbGw9Im5vbmUiIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIvPgogIDxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xOS45NTcgNy44NjdsLTUuODM1LTYuODc5IC0wLjkgMy4xNDJjLTEuMjI5LTAuMTMtMy44NzgtMC4zNTQtNS40MTQtMC4wNzJDNS42IDQuNDY1IDMuNjQ0IDUuNjgzIDIuMzQ0IDExLjEyM2MxLjU0Ny0yLjQ0MSAyLjU0LTMuNzgzIDQuODUzLTQuMDIgMC45OTUtMC4xMDIgMy45MjEgMC4wNTggNS4xMzUgMC4xM2wtMS4wMTEgMy41MjVMMTkuOTU3IDcuODY3eiIvPgogIDxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xLjUzMiAxNC4wMzVsNS44MzYgNi44NzcgMC45LTMuMTRjMS4yMjggMC4xMyAzLjg3OCAwLjM1NCA1LjQxMyAwLjA3MiAyLjIwOC0wLjQwOCA0LjE2NC0xLjYyNSA1LjQ2NC03LjA2NSAtMS41NDcgMi40NDEtMi41NCAzLjc4My00Ljg1MiA0LjAyMSAtMC45OTcgMC4xMDItMy45MjItMC4wNTctNS4xMzYtMC4xM2wxLjAxLTMuNTI1TDEuNTMyIDE0LjAzNXoiLz4KPC9zdmc+ \ No newline at end of file diff --git a/webif/images/ICRES.svg b/webif/images/ICRES.svg new file mode 100644 index 0000000..b5c4595 --- /dev/null +++ b/webif/images/ICRES.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJJQ1JFUyIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjIgMjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxyZWN0IGZpbGw9Im5vbmUiIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIvPgogIDxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xNCA3aC00VjMuNWMwLTAuNTUtMC4zNi0wLjczLTAuOC0wLjRMMi44IDcuOWMtMC40NCAwLjMzLTAuNDQgMC44NyAwIDEuMmw2LjQgNC44YzAuNDQgMC4zMyAwLjggMC4xNSAwLjgtMC40VjEwaDRjMS42NTYgMCAzIDEuMzQzIDMgM3MtMS4zNDQgMy0zIDNINnYzaDhjMy4zMTMgMCA2LTIuNjg3IDYtNlMxNy4zMTMgNyAxNCA3eiIvPgo8L3N2Zz4= \ No newline at end of file diff --git a/webif/images/ICSHW.svg b/webif/images/ICSHW.svg new file mode 100644 index 0000000..4a82246 --- /dev/null +++ b/webif/images/ICSHW.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJJQ1NIVyIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjIgMjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxyZWN0IGZpbGw9Im5vbmUiIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIvPgogIDxyZWN0IHg9IjUiIHk9IjciIGZpbGw9IiNGRkZGRkYiIHdpZHRoPSI2IiBoZWlnaHQ9IjIiLz4KICA8cmVjdCB4PSI3IiB5PSI1IiBmaWxsPSIjRkZGRkZGIiB3aWR0aD0iMiIgaGVpZ2h0PSI2Ii8+CiAgPGc+CiAgICA8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNMjAuMjkzIDE4LjcwN2MwLjM4OS0wLjM4OSAwLjM4OS0xLjAyNSAwLTEuNDE0bC01Ljc0Ni01Ljc0NmMtMC4zODktMC4zODktMC41NTQtMS4xMy0wLjM2Ni0xLjY0NyAwIDAgMC4zMTktMC44OCAwLjMxOS0xLjkgMC0zLjU5LTIuOTEtNi41LTYuNS02LjVTMS41IDQuNDEgMS41IDhzMi45MSA2LjUgNi41IDYuNWMxLjAyIDAgMS45LTAuMzE5IDEuOS0wLjMxOSAwLjUxNy0wLjE4OCAxLjI1OC0wLjAyMiAxLjY0NyAwLjM2Nmw1Ljc0NiA1Ljc0NmMwLjM4OSAwLjM4OSAxLjAyNSAwLjM4OSAxLjQxNCAwTDIwLjI5MyAxOC43MDd6TTMuNSA4YzAtMi40ODUgMi4wMTUtNC41IDQuNS00LjVzNC41IDIuMDE1IDQuNSA0LjUgLTIuMDE1IDQuNS00LjUgNC41UzMuNSAxMC40ODUgMy41IDh6Ii8+CiAgPC9nPgo8L3N2Zz4= \ No newline at end of file diff --git a/webif/images/ICSPAC.gif b/webif/images/ICSPAC.gif new file mode 100644 index 0000000000000000000000000000000000000000..4bcc753a12e9854923af4b9b5b9a4b76f1bc53a6 GIT binary patch literal 42 ocmZ?wbhEHbWMp7uXkY+=|Ns9h{$ybUF?B!$NXCJQ(S^Yp0J?7nHvj+t literal 0 HcmV?d00001 diff --git a/webif/images/ICSTA.svg b/webif/images/ICSTA.svg new file mode 100644 index 0000000..87852ec --- /dev/null +++ b/webif/images/ICSTA.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJJQ1NUQSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjIgMjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxyZWN0IGZpbGw9Im5vbmUiIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIvPgogIDxnPgogICAgPHBvbHlnb24gZmlsbD0iI0ZGRkZGRiIgcG9pbnRzPSI0LDE4IDQsMiAyLDIgMiwyMCAyMCwyMCAyMCwxOCAgIi8+CiAgICA8cmVjdCB4PSI5IiB5PSI1IiBmaWxsPSIjRkZGRkZGIiB3aWR0aD0iMyIgaGVpZ2h0PSIxMiIvPgogICAgPHJlY3QgeD0iMTMiIHk9IjgiIGZpbGw9IiNGRkZGRkYiIHdpZHRoPSIzIiBoZWlnaHQ9IjkiLz4KICAgIDxyZWN0IHg9IjUiIHk9IjEwIiBmaWxsPSIjRkZGRkZGIiB3aWR0aD0iMyIgaGVpZ2h0PSI3Ii8+CiAgPC9nPgo8L3N2Zz4= \ No newline at end of file diff --git a/webif/images/ICSTART.svg b/webif/images/ICSTART.svg new file mode 100644 index 0000000..496503a --- /dev/null +++ b/webif/images/ICSTART.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJJQ0VOQSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjIgMjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxyZWN0IGZpbGw9Im5vbmUiIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIvPgogIDxwb2x5Z29uIGZpbGw9IiNGRkZGRkYiIHBvaW50cz0iNyw1IDcsMTcgMTcsMTEgIi8+Cjwvc3ZnPg== \ No newline at end of file diff --git a/webif/images/ICSTOP.svg b/webif/images/ICSTOP.svg new file mode 100644 index 0000000..7fe3a96 --- /dev/null +++ b/webif/images/ICSTOP.svg @@ -0,0 +1 @@ +data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJJQ0RJUyIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjIgMjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPgogIDxyZWN0IGZpbGw9Im5vbmUiIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIvPgogIDxyZWN0IHg9IjYiIHk9IjYiIGZpbGw9IiNGRkZGRkYiIHdpZHRoPSI0IiBoZWlnaHQ9IjEwIi8+CiAgPHJlY3QgeD0iMTIiIHk9IjYiIGZpbGw9IiNGRkZGRkYiIHdpZHRoPSI0IiBoZWlnaHQ9IjEwIi8+Cjwvc3ZnPg== \ No newline at end of file diff --git a/webif/images/favicon.ico b/webif/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..964d96ef42fa6be8d99299d2d2acd8fb3c3315b2 GIT binary patch literal 3259 zcmZ8kdpMM78-K>3h8(8YZPG-Ok|_;oWXPgH#+hNFk{p+DJ`4#-HB=I9#xb*^rq<+; zV#v9bi7;fAai|<-HAxIR4mnicYxnzOzwf=S=e@7@d4AV@U-$hyzx(;UHyrIPd%7q&dCT`*yL?vX%1QFSuv}>1R0rd;;ASAu<9EF z7Rxk=*_ez8Kkg6N-G7g3s|5@IWCN|tkGr_Fww;U*cINN+&J`Gc9&BR!9EPD7T0O9TueK@qwP+LN0@unVH#QaE@-P zu`Gk0tJ*}kf{4r{8D2)*VTZVGK@$k@xE`ggcaQ=clX?Z=ZICV)FSwSBAn)_cs!Ku) z%L9Lk%!MIIQ~2d#%}N%F<=W^G=5Y4xJ3Il-;cz5Y5)vucocQd+0~qE@IQlk8f{B}q ze4gl!% zzPzzilg3Ogh!=PcSLxe+JaoEBM_KkOZ+g+Xf`2e5Fi@e~qj9+Z%^TQnP$0S1LEXcL zBM@lZ4uxy_Oj60Re#kJ*p#c9Tt8?<`ZrYR1$+(K5qVd7%Abu)~m1yt_aXmRobt%@U zB6PIr>q^eK#JrwDlVi)Xj@Zr2^0r@_>h%X;rsO zADkX2<2@Y;t_c|#*EPWl4L7(Kb3@U z+lAs$@3ceMOb1?-;E_G!b?^v#1`DzuQm$S$1>TjpqY(uU|9be>L;-*Qwi|hLQVUX1 z7JWNp#A}58LMUcL)xX<}VPewesjFEq7(ftop=wag%p|Gj&r5<9s=f1$% zbw{B1j1mo+qb-j*#Fl4=2$KOVZP)f_80zbL>O-Y2R1=DN56}XhXidX9 zpBfqn^+?t*HiH3^cfDg(eCa=bHrL zmA6VtN^NdhF`?n%a~bOD>hX1Tb-%L8%Fa&5)Q=M2R@M|~$I9{XOP}K6%H1V)wA#VIK%CjI^WyJkhA=BfGEL=!!|Mc(}UJj+^mz>276x25)7Xh3kVmS*amI$k`1 zSX5VMI1^J368k zI?PEr%pt(88+e`?c+KT@CFWzZUE8YO=q2+47X zi;sVlt$i+3htp&Yx005)e0Ixrth;D2?)+ocuqC`hi4bf(MK7PeB){ou&a)w{KO>)S z#tV;g23bs-oRft{7ygP-07*}@f~<^Gzke7T8>?va5M@El<E4jmPSzYza?=5in_EHqR> zUQRA}I{`7mRzvODHFH2&HVitd8LuSk%4;UYE4K*nz>%1>gB1;TLV|*(O`<;7uD-mX z#9x{jS-!A$Tiz#;$W^QJG^4|JDUxdjF^8y;DW5J#i{vUjcgOPhe0@4`FIM|plTp(J z@y~0Yhn|Ti=D##~SJtY6C-gg>Y5iL=K-H!i2JjTT`N@Y z!O<Xxyp}5eIW0sg>6YI`%sypV>7+B6$}aZKaI}T> z4ry>Nu~{v_cytG6Fa}Mdo$;Y-q039J^S)beLV(+{sLA5XBK%dl>D*t&G=!d<00n@R zM&^^ie%3<0s42d(q=aG)FM7;kX{PF`+&W>Bo16Q^7h;A>ZHt~44Vc81u1f3 z*H;&(Yc{o;?glSBpB%Z{{p~G#CK=p=C6%x~O*v{Vpum@HH(my@Luw?FuuNs70CSd~ z(Vcr(Ak7&pg{adkgT`4nZAxvMxC+Kj!1o!c_MmyVW5FXHazF}Ahg!Ias;cU@$$_#n zutR6qayUCN$Gft%&3itGc3WGAQ)q+nmrUY|mQ6&eXR5Iuj{)Ot^lkKpb$x$8X4%dw~I6ji08c)dU5K+c5idIN5e~ zc3*+dXBSsj6W-w97j=LypI$9g|ApA|>re5a9<|})gQcgQrB~I|sO_gE+_u$l4d>n` zTz{;ZAtYgQMh-9^ql#eqiAME&#b673t~?(LL%?s(D$iFh53t%i;^WS2SoVvLPRkU6P7ixR{g&FuD)4V zn08uvdZK=2$9mV(QcD_Q2SCC=OvO(~pHueJskp2kVnUCkV3@mUwJ>0TVHO@9K;3Nf zd~roO3hQ1wHJ3CuZRb72?mrufEVvA^hGrt&$Yzm*UVUijl z_UjrLJU2lpRH*IT8Se()daKP_dpAv%>0LE9_TuX3hk~9g5BWfzV>L(e;!Wb-$f$&bhEvuuDPD3= zN3US9BIOko^`p3unu6MpkoEX+_4xSs3IXno?oQJuqnrk?kT^aCJvSlnf2>#%5N+hfZ;slz+c_smg8r(a)(ua6_aaX4pUXO~rV zpY|bapEj4MW!=AQuF2Qifb}|bI1Kc3*;tkJc4~HX!$6rmvXuG*HV6uW-U#9n5FO%e zXlNeh6CL6gN;Nce^z!$Eq;c%<5h(z4fv()P2}oc657;UM!WbF`_)+`=AXx%IaYrNp z3=e)?$r>BoP|yGJ{9mvu#g`iJKiKG`52+A#&%dyCln_7ah48;K?%c?@U%TCAGuRq0 z8GFvaa^n9y+qppSe_XP0)wmiR{ijl3OR~hSm}^hBLRgi5d3L76{R8D4>9&^ulEpB8 sr=1fUe+{UAp~xhw4SzO&SCh}*4CL}90_dhrEC>gzEbPt837%K}19Xk`^Z)<= literal 0 HcmV?d00001 diff --git a/webif/include/body.html b/webif/include/body.html new file mode 100644 index 0000000..536ea94 --- /dev/null +++ b/webif/include/body.html @@ -0,0 +1,6 @@ + + + +
    +##LOGO## +
    \ No newline at end of file diff --git a/webif/include/cccamentitlements.html b/webif/include/cccamentitlements.html new file mode 100644 index 0000000..279f4af --- /dev/null +++ b/webif/include/cccamentitlements.html @@ -0,0 +1 @@ +card_count=##CCCOUNT##
    hop1=##CCCHOP1##
    hop2=##CCCHOP2##
    hopx=##CCCHOPX##
    currenthops=##CCCCURR##

    reshare0=##CCCRES0##
    reshare1=##CCCRES1##
    reshare2=##CCCRES2##
    resharex=##CCCRESX##
    \ No newline at end of file diff --git a/webif/include/cccamentitletooltip.html b/webif/include/cccamentitletooltip.html new file mode 100644 index 0000000..b3a0df8 --- /dev/null +++ b/webif/include/cccamentitletooltip.html @@ -0,0 +1 @@ +
    ##CCCTMP####CCCTMPSPAN## \ No newline at end of file diff --git a/webif/include/css.css b/webif/include/css.css new file mode 100644 index 0000000..b572ece --- /dev/null +++ b/webif/include/css.css @@ -0,0 +1,1289 @@ +/* Table of Content +===================================== + # 1. RESET + # 2. BASIC DIVISION PAGES + # 3. BASIC SETTINGS + # 4. LINKS + # 5. MAIN MENU + # 6. SUBNAV + # 7. TABLE + - STATUS + - CONFIG + - READERS + - USERS + - SERVICES + - FAILBAN + - CACHEEX + - OTHER TABLES + # 8. TABLE TR + # 9. TABLE TH + # 10. TABLE TD + # 11. DIV + # 12. TEXTAREA,SELECT,INPUT + # 13. IMAGE + # 14. SPAN + # 15. GRAPH + # 16. SORTING IN TABLE + # 17. POLLING + # 18. ECM BAR HISTORY + # 19. FILE MENU + # 20. OTHER SETTINGS +===================================== */ +/* **************** 1. RESET */ +* {margin:0;padding:0} +/* **************** 2. BASIC DIVISION PAGES */ +html,body,* html #wrapper {height:100%} +#wrapper {position:relative;min-height:100%} +#content {margin-bottom:0;padding:0 8px 115px} +#footer {position:absolute;bottom:0;width:100%} + +/* **************** 3. BASIC SETTINGS - BODY,P,H4,LI,B */ +body { + font-family:Arial; + font-size:11px; + text-align:center; + background-color:#FFF; + color:#000; + text-decoration:none +} +/* pre_shutdown.html */ +h4 { + font-size:18px; + line-height:9px; + margin:20px 0; + color:#F00 +} +/* shutdown.html, autoconf.html */ +p.text-large-red { + font-weight:700; + font-size:large; + color:#F00 +} +/* file.html, script.html */ +#filename,#filesubmit { + font-weight:700; + font-size:14px +} +#filesubmit { + height:20px +} +li { + list-style:none +} +/* readerstats.html for P */ +#text_totalecm { + font-weight:bold; + margin-top:5px +} +/* footer.html */ +li.styleauthor:after { + content:"Streamboard Team" +} +/* Solution for vertical align in tbody.statusecminfo */ +tbody.statusecminfo tr:nth-child(5) td:last-child b { + position:relative; + top:1px +} +/* **************** 4. LINKS */ +a { + text-decoration:none; +} +a:link, a:visited, a:active { + color:#000 +} +a:hover { + color:#FF9E5F +} +/* logpage_debugmenu.html */ +a.debugl:link, a.sizeml:link { + margin:0 1px +} +a.debugl:hover, a.debugs:hover, a.sizeml:hover, a.sizems:hover { + background-color:#A00; + color:#FFF +} +/* module-webif.c */ +a.debugls:link, a.sizemls:link { + padding:1px 2px 2px +} +a.debugls:link, a.debugls:visited, a.sizemls:link, a.sizemls:visited { + background-color:#A00; + color:#FFF +} +/* cccamentitletooltip.html, foundentitlements.html, noentitlements.html and module-webif.c */ +a.tooltip, +a.tooltip1 { + position:relative; + cursor:default +} +a.tooltip span, +a.tooltip1 span { + display:none; + z-index:99; + white-space:nowrap +} +a.tooltip1 { + color:#F00 +} +a:hover span { + display:block; + position:absolute; + top:1em; + right:1em; + padding:4px; + font-weight:normal; + text-align:left; + background-color:#FFF; + border:1px solid #000; + color:#000 +} +a img.icon { + background-color:#A00 +} +a:hover img.icon { + background-color:#F00 +} +td.statuscol14 a, td.statuscol16 a, td.subheadline a { + text-decoration:none +} +span a { + text-align:center +} +td.statuscol4 a, +td.statuscol9 a, +td.statuscol13 a { + text-align:center +} +/* module-webif.c class for services.html */ +div.sidlistclose a { + padding:0 3px; + color:#FFF +} +/* help link readers/servicesedit/useredit/config.html */ +form table a { + cursor:pointer +} +/* **************** 5. MAIN MENU */ +#main {} +#mainmenu { + height:27px; + padding-top:6px; + white-space:nowrap; + display:inline-block; + position:relative; + background-color:#FFF +} +#mainmenu li { + display:inline; + margin:0 2px; + padding:4px 3px 3px; + font-size:18px; + font-weight:700 +} +li.menu a,li.menu a:hover, +li.menu_selected a,li.menu_selected a:hover { + color:#000 +} +li.menu:hover, li.menu_selected, +li.configmenu:hover, li.configmenu_selected { + background-color:#e8e8e8; +} +/* **************** 6. SUBNAV */ +#subnav {} +#nav { + height:22px; + white-space:nowrap +} +#nav li { + display:inline-block; + position:relative; + margin:0 2px; + padding:0 3px; + font-size:14px +} +li.configmenu a, li.configmenu a:hover, +li.configmenu_selected a, li.configmenu_selected a:hover { + color:#000 +} +/* entitlements.html, readerconfig.html and user_edit.html */ +li.text_entitle { + font-weight:700 +} +/* **************** 7. TABLE */ +table { + border-spacing:1px; + border:0; + margin:0 auto +} +/* ------- STATUS ------- */ +/* status.html */ +table.status { + width:100%; + empty-cells:show; + background-color:#FFF +} +/* Table statusmeminfo - in status_systeminfo.html + * Table statususerinfo - in status_userinfo.html + * Table statuscacheexinfo - in status_cacheexinfo.html + */ +table.infotable { + width:60%; + white-space:nowrap +} +/* ------- CONFIG ------- */ +/* for all part in CONFIGURATION item */ +/* include readerconfig.html & user_edit.html */ +table.config, table.configreader,table.configuser { + width:80%; + margin-bottom:-1px; + max-width:610px; + white-space:nowrap +} +/* ------- READERS ------- */ +/* readers.html */ +table.readers { + width:100% +} +#newinsert table.readers, #searchTable table.readers { + border-spacing:1px 0px; + margin-top:1px +} +/* entitlements_bit.html, entitlements_cccambit.html */ +table.stats {} +/* emm.html */ +table.writeemm { + width:750px +} +/* emm_running.html */ +table.emmrunning { + width:750px +} +/* readerstats.html */ +table.statsbalance td:last-child { + line-height:20px; + width:125px +} +table.statsbalance td:last-child a { + float:right; + margin-left:10px +} +/* scanusb.html */ +table.scanusb {} +/* On/Off button for readers and users in readers.html and userconfig.html*/ +td.readercol0 a img.icon, +td.usercol0 a img.icon { + background-color:#0A0 +} +td.readercol0 a:hover img.icon, +td.usercol0 a:hover img.icon { + background-color:#F00 +} + +/* ------- USERS ------- */ +/* userconfig.html */ +table.users { + width:100% +} +#newinsert table.users, #searchTable table.users { + border-spacing:1px 0px; + margin-top:1px +} +/* ------- SERVICES ------- */ +/* services.html */ +#addnewservice,#servicesedit,table.configservices { + width:40%; + max-width:510px; + text-align:center +} +#addnewservice { + border-spacing:1px 0px; + margin-top:1px +} +/* services_edit.html */ +table.configservices { + margin-bottom:-1px; + text-align:left +} +/* ------- FAILBAN ------- */ +/* failban.html */ +table.failban {} + +/* ------- CACHEEX ------- */ +/* cacheex.html */ +table.cacheex, table#dataTable.stats { + width:80% +} +table#cachexstats { + width:50%; + max-width:1300px; + white-space:nowrap; + margin:20px auto 0 +} +/* ------- OTHER TABLES ------- */ +.invisible table { + border-spacing:0; + margin-left:0 +} +td.subservice {} +table.subservicetable { + width:100%; + background-color:#FFF +} +.invisible table tr, +.invisible table td { + background-color: transparent; +} +table.configreader > tbody > tr:not(.subservicetable):hover > td, +table.configuser > tbody > tr:not(.subservicetable):hover > td { + background-color: #DDD; + color: #000; +} +table.subservicetable tr:hover td { + background-color: #DDD; + color: #000 +} +tr.online:hover td, +tr.offline:hover td, +tr.disabled:hover td, +tr.connected:hover td, +tr.expired:hover td, +tr.statuscacheex:hover td, +tr.disabledreader:hover td, +tr.enabledreader:hover td, +tr.r_undefined:hover td, +tr.r_connected:hover td, +tr.a:hover td, +tr.c:hover td, +tr.h:hover td, +tr.s:hover td, +tr.n:hover td, +#servicesedit tr:hover td, +table.statususericon tr:hover td, +table.failban tr:hover td, +table.statsbalance tr:hover td, +table.stats tr:hover td, +table.config tr:hover td, +#statuscacheex tr:hover td, +table.scanusb tr.scanusbsubhead:hover td { + filter: brightness(95%); + color: #000 +} +table.scanusb tr:hover td { + filter: brightness(95%); + color: #000; +} +.scanusb td b { + color: red; +} +tr.r:hover td { + background-color:#faecdb; + color: #000 +} +tr.p:hover td { + background-color:#f4f2de; + color: #000 +} +/* **************** 8. TABLE TR */ +/* for entitlement of readers */ +tr.e_valid td, tr.e_expired td, tr.e_header td { + font-family:"Courier New", monospace +} +/* hiding formatline for width of firstrow in config, readers and users / width in info */ +tr.configfirstrow, tr.configfirstrow td, +tr.infocolswidth, tr.infocolswidth td, tr.infocolswidth th { + border:0; + height:0; + padding:0 +} +/* rowcolors for cacheex rows in cachex depend on status */ +tr.e_valid td {background-color:#E6FEBF} +tr.e_expired td {background-color:#FFF3E7} +/* rowcolors for users rows in unserconfig.html depend on status */ +tr.online td.usercol6 {background-color:#BFA} +tr.online td {background-color:#BFA} +tr.offline td {} +tr.expired td {background-color:#FBA} +tr.connected td {background-color:#FFA} +tr.r_undefined td {background-color:#FFA} +tr.r_connected td {background-color:#BFA} +/* color of button for readers and user row in readers.html and userconfig.html*/ +tr.disabled td {} +tr.disabled td:first-child img.icon, +tr.disabledreader td:first-child img.icon {background-color:#B0B0B0} +tr.disabled td:first-child a:hover img.icon, +tr.disabledreader td:first-child a:hover img.icon {background-color:#CECECE} +/* colors for status rows in status.html depend on status */ +tr.a td {background-color:#3F0} +tr.c td {background-color:#F1F5E6} +tr.s td, tr.l td, +tr.n td,tr.h td {background-color:#E1E1EF} +tr.r td {background-color:#FFF3E7} +tr.p td {background-color:#FDFBE1} +tr.scanusbsubhead td {background-color:#EEE} +/* scanusb.html */ +tr.scanusbsubhead {} +table.scanusb th { + background-color: #DDD; + color: #000; + text-align: left; + border-spacing:1px; +} +/* **************** 9. TABLE TH */ +th { + height:10px; + padding:5px; + background-color:#CCC +} +th.right { + text-align:right +} +/* status_cacheexinfo.html, status_systeminfo.html, status_userinfo.html */ +th.nameinfo { + text-transform:uppercase; + text-align:left; + background-color:#AAA +} +th .nameinfo_second { + text-transform:none; + font-weight:normal +} +table.infotable th:first-child { + width:100px +} +/* status.html */ +th.statuscol0 { + width:0 +} +/* readerconfig.html */ +th.withservices, th.notservices, th.lbservices {} +/* **************** 10. TABLE TD */ +td { + height:10px; + text-align:left; + padding:5px; + background-color:#EEE +} +/* width of firstrow in config, readers and users */ +.configfirstrow td:first-child { + width:140px +} +/* height config, readers and users */ +.config td, .configreader td, .configuser td { + height:22px +} +/* for inline table in config, user_edit.html, readerconfig.html */ +td.subservice, td.invisible { + padding:0px +} +/* services.html */ +#servicesedit td { + text-align:center +} +/* readerstatsnotfound.html, readerstatstimeoutbit.html + * status_*headline.html */ +td.subheadline { + height:10px; + padding:5px; + background-color:#CCC; + text-align:center +} +td.subheadline p { + float:left; + text-align:left; + font-weight:bold; + margin-top:4px +} +/* default alignment for all rows status.html/userconfig.html/readers.html */ +td.centered, +td[class*="statuscol"], /* shorthand notation td[class*='statuscol'] for td.statuscol0 - td.statuscol16 */ +td[class*="usercol"], /* shorthand notation td[class*='usercol'] for td.usercol0 - td.usercol25 */ +td[class*="readercol"] { /* shorthand notation td[class*='readercol'] for td.readercol0 - td.readercol18 */ + text-align:center +} +td.statuscol0 { + width:10px +} +td.statuscol1 { + width:0 +} +.usercol1 img{ + vertical-align:middle +} +/* readerconfig_sidokbit.html */ +td.servicescol1, td.servicescol2, td.servicescol3 { + width:30% +} +/* Actionbutton readers.html */ +td.readercol14,td.readercol15, +td.readercol16 { + min-width:22px +} +/* cols width info status and users.html */ +.infocolswidth td { + width:7.5% +} +/* **************** 11. DIV */ +/* Log status.html, livelog.html, file.html */ +div.log, div#livelog, textarea.editor, div.sidlist { + font-family:"Courier New", monospace; + text-align:left; + border:1px dotted #AAA; + background-color:#FAFAFA; + color:#666 +} +div.log { + margin:4px 0 0; + padding:4px; + max-height:300px; + overflow:scroll; +} +/* module-webif.c class for services.html */ +div.sidlist { + padding:2px; + background-color:#FFF; +} +div.sidlistclose { + float:right; + background-color:#F00; + color:#FFF +} +/* message.html */ +div.message { + font-size:12px; + font-weight:700; + margin:5px 0 +} +/* file.html */ +div.filterform { + margin:5px 0 +} +/* pre_autoconf.html, pre_shutdown.html */ +div.warning { + margin:42px 0 14px +} +/* logobit.html - Logo for Oscam */ +div.logo { + position:absolute; + top:6px; + left:9px; + z-index:0 +} +/* READERS and USERS items */ +div.groups { + cursor:default; + overflow:hidden; + margin:0 auto; + text-align:center; + word-wrap:normal; + width:40px +} +/* footer.html */ +div.footer { + background-color:#EEE; + border-top:3px solid #CCC +} +div.footer .top_link { + position:absolute; + bottom:5px; + right:10px; + font-weight:bold +} +div.footer .top_link a:after { + content:" \25B2" +} +DIV.bottom_link { + position:absolute; + top:6px; + right:10px; + font-weight:bold +} +DIV.bottom_link a:after { + content:" \25BC" +} +div.footer li { + margin:5px +} +/* status.html, main DIV for statistics info */ +div.info { + max-width:1300px; + white-space:nowrap; + margin:14px auto 0 +} +/* debugmenu - status_sdebug.html + * logmenu - file.html +*/ +div.debugmenu, div.logmenu, div.logsizemenu { + font-size:12px; + line-height:16px; + margin-top:10px +} +#regexdatainput { + margin-top:10px +} +/* entitlements_cccambit.html */ +div.cccamentitlementtotals, +div.cccamentitlementcontrols { + font-weight:bold; + margin-top:5px +} +/* definition only for show or hide columns or DIV + * #newinsert - in userconfig.html, readers.html + * #exp, #cwc, #acasc - only in module-webif.c for show/hide columns in userconfig.html + */ +#newinsert, +.exp, .cwc, .acas, .grp, .hidden { + display:none +} +/* **************** 12. TEXTAREA,SELECT,INPUT */ +/* texteditor on file.html */ +textarea.editor { + font-size:11px; + width:100%; + max-width:1500px; + height:556px; + margin:0 auto 10px; + padding:4px +} +/* textarea for config */ +textarea.bt { + width:380px; + font-size:12px +} +/* textarea for emm.html */ +table.writeemm textarea { + width:597px +} +/* generaly settings for all select */ +select { + padding:2px +} +/* settings for select in script.html */ +#scriptselect { + height: 22px +} +/* generaly settings for all input */ +input,textarea { + -moz-box-sizing:border-box; + box-sizing:border-box +} +/* generaly settings for all checkbox input */ +input[type=checkbox] { + cursor:pointer +} +/* generaly settings for all text input */ +input[type=text] { + padding:2px; + width:380px +} +input.short[type=text] { + width:52px +} +input.colorinput[type=text] { + width:60px +} +input.medium[type=text] { + width:125px +} +input.longer[type=text] { + width:250px +} +.readers input[type=text], +#addnewservice input[type=text], +.users input[type=text], +.cacheex input[type=text] { + text-align:center; + background-color:#F8F8F8; + width:180px +} +/* all config input they are with units e.g. sec or min in config.html */ +input.withunit { + text-align:left +} +/* solution for align vertical checkbox with text */ +input[type=checkbox] { + vertical-align:text-bottom; + _vertical-align:middle; + margin-right:5px +} +/* generaly settings for all buttons/submits */ +input[type=button], input[type=submit] { + -webkit-appearance:none; + border-radius:3px; + border:1px solid #AAA; + cursor:pointer; + text-align:center; + padding:0 2px; + margin:0 3px; + display:inline-block; + width:70px; + height:20px; + color:#333; + background-color:#F8F8F8 +} +/* generaly hover settings for all buttons/submits */ +input[type=button]:hover, input[type=submit]:hover { + background-color:#F2F2F2; + box-shadow:inset 1px 1px #555; + padding:0px 1px 0px 3px +} +/* only for statusheadline button in status.html */ +td.subheadline input[type=button] { + float:right; + margin:0 0 0 2px; + width:95px; + font-weight:normal +} +/* only for save button lb/cccam control in config.html */ +.configcontrol input[type=submit] { + width:115px +} +/* only for button in readers.html/userconfig.html/services.html for addnew */ +.users input[type=submit], .readers input[type=submit], #addnewservice input[type=submit] { + margin-left:10px +} +/* only for diasabled button in file.html*/ +#filesubmit input[type=submit]:disabled { + display:none +} +/* only save button in config.html/readerconfig.html/services.html */ +td.configcolsave { + text-align:center +} +/* settings for first option in select + * otherdropdown - in script.html, menu.html + */ +#otherdropdown { + margin:5px +} +#otherdropdown option:first-child { + display:none +} +/* for extract in script.html and emm.html */ +div.extract { + width:750px; + margin:10px auto; + text-align:left +} +div.extract p { + font-weight:700; + margin:10px 6px 0 6px +} +div.extract p.emmhead { + font-size:13px; + text-align:center +} +div.extract p .emminfo { + font-weight:normal +} +div.extract pre { + overflow:auto; + margin:5px 0; + padding:5px; + font-family:courier,monospace; + border:1px dotted #AAA; + white-space:pre +} +div.extract pre.script { + min-height: 100px +} +/* **************** 13. IMAGE */ +/* generaly settings for images */ +img { + border:0 solid +} +/* for icons with following Action */ +img.icon { + border-radius:4px; + width:22px; + height:22px; + max-height:22px; + background-color:#A00 +} +/* statususericon - in STATUS item + * protoicon - in STATUS, READERS item + * usericon - in USERS item + * readericon - in READERS item + */ +img.statususericon, img.protoicon, +img.usericon, img.readericon { + min-height:15px; + height:auto; + max-height:22px; + max-width:150px +} +/* ECM Restarticon */ +.statusecminfo img { + vertical-align:middle +} +/* logo IC_LOGO.tpL */ +div.logo img { + max-height:44px +} +div.logo svg.OSCam_logo { + fill:#111111 +} +td#out.centered img, +td#in.centered img { + margin:0 10px -2px +} +/* **************** 14. SPAN */ +/* module-webif.c and userconfig_notify.html */ +span.span_notifier { + margin-left:2px; + padding:1px 3px; + color:#FFF; + background-color:#F00 +} +/* module-webif.c value for STATUS item ( more info in Changeset 6668 by alno ) + * Class for formating values in statusview column "Idle" + * In new WebIf column Idle is not used. Info about Idle is in title */ +span.idlesec_normal, span.idlesec_alert { + font-size:9px; + color:#F00 +} +/* readerconfig_cccambit.html, user_edit_anticasc.html and user_edit_cccam.html */ +span.global_conf { + font-size:12px; + cursor:default; + padding:4px; + color:blue +} +/* cachex status */ +span.e_expired {background-color:#FFF3E7} +.span, span { + text-align:center +} +/* **************** 15. GRAPH */ +svg.graph {background-color:#F0F0F0;font-family:Arial;font-size:9px} +rect.graph_bg {fill:white} +rect.graph_button {fill:#A9D0F5} +text.graph_error {text-anchor:middle;fill:#F00} +text.graph_top_txt {text-anchor:end} +text.graph_grid_txt {text-anchor:end;fill:gray} +path.graph_grid {stroke-opacity:0.5;stroke-width:0.7px;stroke:gray} +path.graph_curve {stroke-opacity:0.8;stroke-width:0.7px;fill:none} +/* **************** 16. SORTING IN TABLE */ +.users th { + font-size:11px +} +.sortable { + background-image: url(data:image/gif;base64,R0lGODlhBQAIAJEAAAAAAP///yMtMP///yH5BAEAAAMALAAAAAAFAAgAAAIL3GQnuJ2f2lLI1AIAOw==); + background-repeat:no-repeat; + background-position:4px center; + cursor:pointer; + padding-left:12px; + padding-right:8px +} +.sorting-desc { + background-image: url(data:image/gif;base64,R0lGODlhBQADAJEAAAAAAP///yMtMP///yH5BAEAAAMALAAAAAAFAAMAAAIFlD03K1sAOw==); + background-repeat:no-repeat; + background-position:4px center; + cursor:pointer; + padding-left:10px; + padding-right:8px +} +.sorting-asc { + background-image: url(data:image/gif;base64,R0lGODlhBQADAJEAAAAAAP///yMtMP///yH5BAEAAAMALAAAAAAFAAMAAAIF3GQnuF0AOw==); + background-repeat:no-repeat; + background-position:4px center; + cursor:pointer; + padding-left:10px; + padding-right:8px +} +.sorting-asc, .sorting-desc {background-color:#AAA} +.readers .td-sorting, +.readers tr.r_undefined .td-sorting {background-color: #EAEA95} +.readers tr.r_connected .td-sorting {background-color: #A6EA95} +.readers tr.enabledreader .td-sorting {background-color: #D9D9D9} +.readers tr.disabledreader .td-sorting {background-color: #D9D9D9} +.users tr.offline .td-sorting, +.users tr.disabled .td-sorting, +.statsbalance .td-sorting {background-color: #D9D9D9} +.users tr.connected .td-sorting {background-color: #EAEA95} +.users tr.online .td-sorting {background-color: #BFA} +.users tr.expired .td-sorting {background-color: #EAA69F} +/* **************** 17. POLLING */ +/* for UL in logpoll.html */ +#livelog { + resize:vertical; + height:574px; + max-width:1500px; + overflow-y:auto; + margin:1px auto 15px; + padding:0px 4px +} +#livelogdata { + white-space:pre +} +li.regex, li.regexdata_nav, li.regexdata_save{ + white-space:nowrap +} +input.regexinput { + cursor:text; + padding:2px; + margin:3px 15px 1px 3px; + width:400px +} +.regexdata_nav input { + width:100px +} +.regexdata_save input { + width:90px; + margin-top:10px +} +.colorPicker_def_color { + color:#DDD +} +.colorPicker_def_fcolor { + color:#00F +} +div.colorPicker-picker { + height:16px; + width:16px; + display:inline; + margin:3px; + padding:4px 8px; + border:1px solid #CCC; + cursor:pointer; + line-height:16px; + font-size:0.75em; + font-weight:bold; + text-align:center +} +div.colorPicker-palette { + width:110px; + position:absolute; + border:1px solid #598FEF; + background-color:#EFEFEF; + padding:2px; + z-index:9999 +} +div.colorPicker_hexWrap {width:100%;float:left} +div.colorPicker_hexWrap label {font-size:95%;color:#2F2F2F;margin:5px 2px;width:25%} +div.colorPicker_hexWrap input {margin:5px 2px;padding:0;font-size:95%;border:1px solid #000;width:65%} +div.colorPicker-swatch { + height:12px; + width:12px; + border:1px solid #000; + margin:2px; + float:left; + cursor:pointer; + line-height:12px +} +/* User-Page polling */ +#inc,#polling,#dec,.regexbutton { + margin:-3px 2px; + border:none; + color:#FFF; + cursor:pointer; + border-radius:4px; + font-size:14px; + font-weight:700; + width:22px; + height:22px; + line-height:0px +} +#inc,#dec,.regexbutton { + background-color:#A00 +} +#inc:hover,#dec:hover,.regexbutton:hover { + background-color:#F00 +} +#inc[disabled], +#dec[disabled] { + color: #6D6C6D; + cursor: inherit; + background-color: #DDD; +} +.pollingenabled { + background-image:url(image?i=ICSTOP); + background-repeat:no-repeat; + background-position:center; + background-color:#A00 +} +.pollingdisabled { + background-image:url(image?i=ICSTART); + background-repeat:no-repeat; + background-position:center; + background-color:#A00 +} +.pollingenabled:hover { + background-color:#F00 +} +.pollingdisabled:hover { + background-color:#F00 +} +#poll button { + margin-bottom:1px; + vertical-align:middle +} +#nav li.pollselect { + position:absolute; + right:10px +} +input.pintervall { + font-size:14px; + font-weight:500; + width:18px; + height:18px; + color:#000; + background-color:transparent; + border:none; + border-radius:4px; + text-align:center; + margin:0 4px; + padding:0 +} +/* polling heartbeat */ +#picolor { + background-color:#A00 +} +/* **************** 18. ECM BAR HISTORY */ +#chart { + position:absolute; + width: 150px; + height: 90px; + border: 1px dotted #F00; + background-color: #EAEAEA; + padding:3px; + z-index:1000 +} +#charthead { + background-color:transparent; + width:100%; + height:5px +} +#graph { + width:98%; + bottom:0; + position:absolute +} +.disabledtable { + opacity:0.5 +} +/* Statuspage Functions: Generate a Bar for Barchart */ +div.bar { + width:5px; + margin:2px; + display:inline-block; + position:relative; + background-color:green; + vertical-align:baseline +} +/* **************** 19. FILE MENU */ +/* For filemenu - change dropdown menu */ +.drop .subcaret { + margin-top:7px; + margin-left:7px +} +.subcaret { + display:inline-block; + width:0; + height:0; + vertical-align:top; + border-top:4px solid #606060; + border-right:4px solid transparent; + border-left:4px solid transparent; + content:""; + opacity:1; + filter:alpha(opacity=100) +} +#nav ul.dropdown_nav li { + margin:0; + padding:1px 0; + width:100% +} +#nav li ul.dropdown_nav li a { + color:#000; + padding:0 5px; + display:block +} +#nav li ul.dropdown_nav { + display:none; + white-space:normal; + position:absolute; + text-align:left; + top:16px; + right:0; + width:auto; + background-color:#FFF; + border:1px solid #DDD +} +#nav li:hover ul.dropdown_nav { + display:block +} +a[title*="expdate"] { + color: red; +} +/* **************** 20. OTHER SETTINGS */ +/* iPhone */ +@media +only screen and (min-device-width : 320px) and (max-device-width : 480px), +only screen and (min-device-width : 320px) and (max-device-width : 568px){ + div.footer { + height:92px + } +} +/* iPhone Landscape */ +@media +only screen and (min-device-width : 320px) and (max-device-width : 480px) and (orientation : landscape), +only screen and (min-device-width : 320px) and (max-device-width : 568px) and (orientation : landscape) { + #livelog { + height:382px + } + textarea.editor { + height:400px + } +} +/* iPad Landscape */ +@media only screen and (min-device-width : 768px) and (max-device-width : 1024px) and (orientation : landscape) { + #livelog { + height:494px + } + textarea.editor { + height:508px + } +} +/* **************** WIKI HELP POPUP */ +#wikiPopup { + display:none; + position:absolute; + z-index:10; + width:400px; + background-color:#FFF; + border:2px solid #AAA; + border-radius:5px; + box-shadow:0 4px 12px rgba(0,0,0,0.3); + font-size:12px; + resize:both; + overflow:auto; + min-width:250px; + min-height:150px +} +.wiki-popup-header { + background-color:#CCC; + color:#000; + padding:8px 12px; + font-weight:bold; + font-size:13px; + border-radius:3px 3px 0 0; + display:flex; + justify-content:space-between; + align-items:center +} +.wiki-popup-title { + font-family:Arial; + color:#000; + text-decoration:none; + cursor:pointer +} +.wiki-popup-title:hover { + color:#FF9E5F; + text-decoration:underline +} +.wiki-popup-close { + cursor:pointer; + font-size:18px; + font-weight:bold; + line-height:1; + padding:0 4px +} +.wiki-popup-close:hover { + color:#FF9E5F +} +.wiki-popup-reset { + cursor:pointer; + font-size:16px; + line-height:1; + padding:0 4px; + margin-left:auto +} +.wiki-popup-reset:hover { + color:#FF9E5F +} +.wiki-popup-content { + padding:12px; + overflow-y:auto; + line-height:1.5; + color:#333 +} +.wiki-popup-content h3, +.wiki-popup-content h4 { + margin:10px 0 5px 0; + color:#000 +} +.wiki-popup-content h3 { + font-size:14px +} +.wiki-popup-content h4 { + font-size:12px +} +.wiki-popup-content pre.wiki-code { + background-color:#f4f4f4; + border:1px solid #ddd; + border-radius:3px; + padding:8px; + margin:8px 0; + overflow-x:auto; + font-family:'Courier New',monospace; + font-size:11px; + white-space:pre; + line-height:1.4 +} +.wiki-popup-content code { + background-color:#f4f4f4; + padding:1px 4px; + border-radius:2px; + font-family:'Courier New',monospace; + font-size:11px +} +.wiki-popup-content strong { + color:#000 +} +.wiki-text { + word-wrap:break-word +} +.wiki-loading { + text-align:center; + padding:20px; + color:#666; + font-style:italic +} +.wiki-nohelp, +.wiki-error { + text-align:center; + padding:20px; + color:#666 +} +.wiki-nohelp a, +.wiki-error a { + color:#000; + text-decoration:underline +} +.wiki-nohelp a:hover, +.wiki-error a:hover { + color:#FF9E5F +} +/* Wiki status icons - keep inline on hover */ +.wiki-status-icon { + margin-right:2px; + display:none !important +} +.wiki-status-icon.visible { + display:inline !important +} +a:hover .wiki-status-icon { + position:static !important; + top:auto !important; + right:auto !important; + padding:0 !important; + background-color:transparent !important; + border:none !important +} +/* Wiki active link highlight */ +a.wiki-active { + background-color:#ffffcc !important; + outline:2px solid #ffcc00; + position:relative; + z-index:2 +} diff --git a/webif/include/footer.html b/webif/include/footer.html new file mode 100644 index 0000000..628b371 --- /dev/null +++ b/webif/include/footer.html @@ -0,0 +1,14 @@ +
    + +
    + + \ No newline at end of file diff --git a/webif/include/foundentitlements.html b/webif/include/foundentitlements.html new file mode 100644 index 0000000..cf25da3 --- /dev/null +++ b/webif/include/foundentitlements.html @@ -0,0 +1 @@ +
    ##TMP####TMPSPAN## \ No newline at end of file diff --git a/webif/include/header.html b/webif/include/header.html new file mode 100644 index 0000000..5ea04de --- /dev/null +++ b/webif/include/header.html @@ -0,0 +1,3 @@ +##TPLHEADERSHORT## +##REFRESH## +##TPLBODY## \ No newline at end of file diff --git a/webif/include/header_short.html b/webif/include/header_short.html new file mode 100644 index 0000000..dc304a4 --- /dev/null +++ b/webif/include/header_short.html @@ -0,0 +1,13 @@ + + + + ##HTTPOSCAMLABEL## ##CS_VERSION## + + + + + + + + \ No newline at end of file diff --git a/webif/include/jquery.js b/webif/include/jquery.js new file mode 100644 index 0000000..7f37b5d --- /dev/null +++ b/webif/include/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",le.option=!!xe.lastChild;var ke={thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n",""]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0 98000 || polling) return; + $(":text[name='pintervall']").val(Number($(":text[name='pintervall']").val()) + 1); + pollintervall = $(":text[name='pintervall']").val() * 1000; + if (!nostorage) { + sessionStorage.pollintervall = pollintervall; + } + }); + // Pollinterval PAUSE + $("#polling").click(function () { + if (polling < 1) { + polling = 1; + $(":text[name='pintervall']").val('--'); + $('#polling').attr('class','pollingdisabled'); + $('#inc, #dec').attr('disabled','disabled'); + } else { + polling = 0; + $(":text[name='pintervall']").val(pollintervall/1000); + $('#polling').attr('class','pollingenabled'); + $('#inc, #dec').removeAttr('disabled'); + clearTimeout(timer_ID); + timer_ID = setTimeout("waitForMsg()", pollintervall); + } + if (!nostorage) { + sessionStorage.polling = polling; + } + }); + // Pollinterval DOWN + $("#dec").click(function () { + if (pollintervall < 2000 || polling) return; + $(":text[name='pintervall']").val(Number($(":text[name='pintervall']").val()) - 1); + pollintervall = $(":text[name='pintervall']").val() * 1000; + if (!nostorage) { + sessionStorage.pollintervall = pollintervall; + } + }); + // Hover for showing Chart on Statuspage + $('table.status').on('mouseover', 'tr > td.statuscol14', function (e) { + var uid = '#' + $(this).parent().attr('id'); + if ('pcr'.indexOf($(uid).attr('class')) >= 0) { + if ($(uid).data('ecmhistory')) { + var head = $(uid + ' > td:nth-child(3)').attr('title').indexOf('(') > -1 ? $(uid + ' > td:nth-child(3)').attr('title').substring(0, $(uid + ' > td:nth-child(3)').attr('title').indexOf('(')-1) : $(uid + ' > td:nth-child(3)').attr('title'); + $('#charthead').text(head + ' History'); + $("#graph").html(''); + var arry = $(uid).data('ecmhistory').split(","); + $.each(arry, function (index, value) { + $("#graph").append(generateBar(value)); + }); + $("#chart").show(); + } + $("#chart").offset({ + left: e.pageX + 20, + top: e.pageY - 20 + }); + } + }); + // Mousout for hiding Chart on Statuspage + $('table.status').on('mouseout', 'tr > td.statuscol14', function () { + $("#chart").hide(); + }); + + $("#add1regex").click(function () { + if (MAX_SEARCH_PATTERN > 98) return; + MAX_SEARCH_PATTERN++; + localStorage.MAX_SEARCH_PATTERN = MAX_SEARCH_PATTERN; + var i = MAX_SEARCH_PATTERN; + var beep_disabled = ' disabled="disabled" title="Not supported by your browser"'; + var contextClass = (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext); + if (contextClass) { beep_disabled = ''; } + var prefix = "0"; + if ( i > 9 ) { prefix = ""; } + $('
  • Search' + prefix + i + ': Found only: Hide: Back Color: Color: Beep:
  • ').insertBefore(".regexdata_save"); + $('#color' + i).val($('.colorPicker_def_color').css('color')); + $('#fcolor' + i).val($('.colorPicker_def_fcolor').css('color')); + $('#color' + i).colorPicker(); + $('#fcolor' + i).colorPicker(); + }); + + $("#del1regex").click(function () { + var i = MAX_SEARCH_PATTERN; + if (i < 2) return; + if ($('#regex' + i).val() != '') if (!confirm('Search' + i + ' is not empty! Delete?')) return; + $("#regexrow" + i).remove(); + localStorage.removeItem('regex' + i); + localStorage.removeItem('color' + i); + localStorage.removeItem('fcolor' + i); + localStorage.removeItem('whitelisted' + i); + localStorage.removeItem('hidden' + i); + localStorage.removeItem('beep' + i); + MAX_SEARCH_PATTERN--; + localStorage.MAX_SEARCH_PATTERN = MAX_SEARCH_PATTERN; + }); + + $("#regexok").click(function () { + + for (var i = 1; i < MAX_SEARCH_PATTERN + 1; i++) { + var pattern = $('#regex' + i).val(); + if (pattern) { + var color = $('#color' + i).val(); + var fcolor = $('#fcolor' + i).val(); + } else { + var color = ''; + var fcolor = ''; + } + localStorage['regex' + i] = pattern ? pattern : ''; + localStorage['color' + i] = color ? color : ''; + localStorage['fcolor' + i] = fcolor ? fcolor : ''; + localStorage['whitelisted' + i] = $('#whitelisted' + i).prop('checked') ? '1' : '0'; + localStorage['hidden' + i] = $('#hidden' + i).prop('checked') ? '1' : '0'; + localStorage['beep' + i] = $('#beep' + i).prop('checked') ? '1' : '0'; + } + + }); + + $("#regexreset").click(function () { + + if (confirm('Delete all Filters and Colors?')) { + for (var i = 1; i < MAX_SEARCH_PATTERN + 1; i++) { + $('#regex' + i).val(''); + $('#whitelisted' + i).prop('checked', false); + $('#hidden' + i).prop('checked', false); + $('#color' + i).val($('.colorPicker_def_color').css('color')); + $('#color' + i).change(); + $('#fcolor' + i).val($('.colorPicker_def_fcolor').css('color')); + $('#fcolor' + i).change(); + $('#beep' + i).prop('checked', false); + localStorage['regex' + i] = ''; + localStorage['color' + i] = ''; + localStorage['fcolor' + i] = ''; + localStorage['whitelisted' + i] = '0'; + localStorage['hidden' + i] = '0'; + localStorage['beep' + i] = '0'; + } + } + + }); + + $(".sizemls a, .sizeml a").click(function () { + maxloglines = parseInt($(this).attr('sendval')); + $("#sizemfrom").text(' Switch displayed log lines from ' + maxloglines + ' to '); + for (var i = 32; i <= 512; i *= 2) { + $("#sizem" + i).attr('class', (maxloglines == i) ? 'sizemls' : 'sizeml'); + } + return false; + }); + + $(".debugls a, .debugl a").click(function () { + parameters = parameters + "&debug=" + $(this).attr('sendval'); + return false; + }); + + $("#savelog").on('click', function (event) { + var txt = ''; + $("#livelogdata li").each(function (i) { + txt += $(this).text() + '\n'; + }); + // Data URI + txtData = 'data:application/txt;charset=utf-8,' + encodeURIComponent(txt); + $(this).attr({ + 'href': txtData, + 'target': '_blank' + }); + }); + + $("#showhidesettings").click(function () { + if ($("#showhidesettings").val() == 'Show Settings') { + $("#showhidesettings").val('Hide Settings'); + $("#regexdata").fadeIn('slow'); + } else { + $("#showhidesettings").val('Show Settings'); + $("#regexdata").fadeOut('slow'); + } + }); + + $("#stoplog").click(function () { + if ($("#stoplog").val() == 'Stop Log') { + $("#stoplog").val('Start Log'); + stoppoll = 1; + } else { + $("#stoplog").val('Stop Log'); + stoppoll = 0; + waitForMsg(); + } + }); + + $("#onlineidle").click(function () { + if ($("#onlineidle").text() == 'Login*') { + $("#onlineidle") + .text('Online & Idle*') + .attr('title', 'Login info (click to switch)'); + } else { + $("#onlineidle") + .text('Login*') + .attr('title', 'Online & Idle info (click to switch)'); + } + if (!nostorage) localStorage.loi = $("#onlineidle").text(); + waitForMsg(); + }); + + // switch reader ON/OFF + $("a.switchreader").click(function (e) { + e.preventDefault(); + var parameters_old = parameters; + parameters += '&label=' + $(this).data('reader-name') + '&action=' + $(this).data('next-action'); + var rowid = '#' + $(this).data('md5'); + var img = $(this).children("img"); + waitForMsg(); + if ($(this).data('next-action') == 'enable') { + $(this).data('next-action', 'disable').attr('title', 'Disable Reader: ' + $(this).data('reader-name') + $(this).data('desc')); + $(rowid).attr('class', 'enabledreader'); + img.attr('src', 'image?i=ICDIS').attr('alt', 'Disable'); + } else { + $(this).data('next-action', 'enable').attr('title', 'Enable Reader: ' + $(this).data('reader-name') + $(this).data('desc')); + $(rowid).attr('class', 'disabledreader'); + img.attr('src', 'image?i=ICENA').attr('alt', 'Enable'); + } + parameters = parameters_old; + }); + + // delete reader + $("a.deletereader").click(function (e) { + e.preventDefault(); + if (confirm("Delete Reader " + $(this).data('reader-name') + "?")) { + var parameters_old = parameters; + parameters += '&label=' + $(this).data('reader-name') + '&action=' + $(this).data('next-action'); + cleaninsert($(this).data('reader-name')); + waitForMsg(); + parameters = parameters_old; + $('#' + $(this).data('md5')).fadeOut('slow'); + } + }); + + // switch user ON/OFF + $("a.switchuser").click(function (e) { + e.preventDefault(); + var parameters_old = parameters; + parameters += '&user=' + $(this).data('user-name') + '&action=' + $(this).data('next-action'); + var rowid = '#' + $(this).data('md5'); + var img = $(this).children("img"); + waitForMsg(); + if ($(this).data('next-action') == 'enable') { + $(this).data('next-action', 'disable').attr('title', 'Disable User: ' + $(this).data('user-name') + $(this).data('desc')); + $(rowid).attr('class', 'offline'); + $(rowid + ' > td.usercol2').text('offline'); + img.attr('src', 'image?i=ICDIS').attr('alt', 'Disable'); + } else { + $(this).data('next-action', 'enable').attr('title', 'Enable User: ' + $(this).data('user-name') + $(this).data('desc')); + $(rowid).attr('class', 'disabled'); + $(rowid + ' > td.usercol2').text('offline (disabled)'); + img.attr('src', 'image?i=ICENA').attr('alt', 'Enable'); + } + parameters = parameters_old; + }); + + // reset user stats + $("a.resetuser").click(function (e) { + e.preventDefault(); + if (confirm("Reset Stats for " + $(this).data('user-name') + "?")) { + var parameters_old = parameters; + parameters += '&user=' + $(this).data('user-name') + '&action=' + $(this).data('next-action'); + waitForMsg(); + parameters = parameters_old; + } + }); + + // delete user + $("a.deleteuser").click(function (e) { + e.preventDefault(); + if (confirm("Delete User " + $(this).data('user-name') + "?")) { + var parameters_old = parameters; + parameters += '&user=' + $(this).data('user-name') + '&action=' + $(this).data('next-action'); + cleaninsert($(this).data('user-name')); + waitForMsg(); + parameters = parameters_old; + $('#' + $(this).data('md5')).fadeOut('slow'); + } + }); + + // search related events + $("#searchTerm").keyup(function () { + var value = $("#searchTerm").val().toLowerCase().trim(); + $("#dataTable tr").each(function (index) { + if (!index) return; + $(this).find("td").each(function () { + var id = (($(this).data('sort-value') == undefined || $(this).hasClass("usercol2")) ? $(this).text() : $(this).data('sort-value').toString()).toLowerCase().trim(); + var not_found = (id.indexOf(value) == -1); + $(this).closest('tr').toggle(!not_found); + return not_found; + }); + }); + }); + + $("#searchTerm").click(function () { + cdpause(); + }); + + $("#searchTerm").blur(function () { + initDoc(); + }); + + var table = $('#dataTable').stupidtable(); + + table.bind('beforetablesort', function (event, data) { + lockpoll = 1; + table.addClass("disabledtable"); + }); + + table.bind('aftertablesort', function (event, data) { + // data.column - the index of the column sorted after a click + // data.direction - the sorting direction (either asc or desc) + lockpoll = 0; + table.removeClass("disabledtable"); + }); + + // copy emm to single write emm + $("a.tosingleemm").click(function (e) { + var ins_emm = (/\s+[0-9a-fA-F]+\s+([0-9a-fA-F]+)\s+/).exec($(this).text()); + $('#singleemm').val(ins_emm[1]); + $('#singleemm').change(); + }); +}); + +/* + * General: Update page footer and failbannotifier + */ +function updateFooter(data) { + $("#curtime").text(' ' + data.oscam.curdate + ' | ' + data.oscam.curtime + ' '); + $("#runtime").text(' ' + data.oscam.runtime); + $("#uptime") .text(' ' + data.oscam.uptime); + + if ($("#fbnotifier > span.span_notifier").length) { + if (data.oscam.failbannotifier > 0) { + $("#fbnotifier > span.span_notifier") + .text(data.oscam.failbannotifier); + } + else { + $("#fbnotifier > span.span_notifier").remove(); + } + } + else if (data.oscam.failbannotifier > 0) { + $("#fbnotifier") + .append(''+ data.oscam.failbannotifier + ''); + } +} + +/* + * identfy an element within string of elements + */ +var poll_excluded; + +function is_nopoll(value) { + return (poll_excluded.indexOf(value) > (-1)) ? true : false; +} + +/* + * Userpage Functions: Update Page + */ +function updateUserpage(data) { + + // update user lines + $.each(data.oscam.users, function (i, item) { + var uid = "#" + item.user.usermd5; + poll_excluded = ($(uid).attr('nopoll') != undefined) ? $(uid).attr('nopoll') : ''; + + switch (item.user.classname) { + case 'online': + $(uid).attr('class', item.user.classname); + + if (!is_nopoll('usercol1')) { + if ($(uid + " td.usercol1 > span.span_notifier").length) { + if(item.user.unotify){ + $(uid + " td.usercol1 > span.span_notifier") + .text(item.user.unotify); + } + else { + $(uid + " td.usercol1 > span.span_notifier").remove(); + } + } + else if(item.user.unotify) { + $(uid + " td.usercol1") + .append(''+ item.user.unotify + ''); + } + } + + if (!is_nopoll('usercol2')) { + $(uid + " td.usercol2") + .attr('title', item.user.stats.expectsleep != 'undefined' ? (item.user.stats.expectsleep > 0 ? 'Sleeping in ' + item.user.stats.expectsleep + ' minutes' : 'Sleeping') : '') + .data('sort-value', item.user.ip) + .html("" + item.user.status + "
    " + item.user.ip); + } + + if (!is_nopoll('usercol3')) { + $(uid + " td.usercol3").html(item.user.stats.idle + "
    " + item.user.stats.timeonchannel.toHHMMSS()); + } + + if (!is_nopoll('usercol4')) { + if (item.user.protoicon.length > 0) { + if (!$(uid + " td.usercol4 > img").length || $(uid + " td.usercol4 > img").attr('src')!='image?i=IC_' + item.user.protoicon) { + var protoimage = $(''); + protoimage.hide(); + $(uid + " td.usercol4").html(protoimage); + protoimage.fadeIn('slow'); + } + } else { + $(uid + " td.usercol4").text(item.user.protocol); + } + + $(uid + " td.usercol4") + .attr('title', item.user.prototitle) + .data('sort-value', item.user.protosort); + } + + // channel icon + if (!is_nopoll('usercol6')) { + $(uid + " td.usercol6") + .attr('title', item.user.lastchanneltitle) + .data('sort-value', item.user.lastchannelsort); + + if (item.user.lca.length > 0) { + // if we already have a picon within link + if ($(uid + " > td.usercol6 > img.usericon").length) { + // we compare the picon name and switch if different + var image = $(uid + " > td.usercol6 > img.usericon"); + if (image.attr('src') != 'image?i=IC_' + item.user.lca) { + // set title of link as tooltip + image.hide(); + image.attr('src', 'image?i=IC_' + item.user.lca); + image.fadeIn('slow'); + image.attr('alt', item.user.lastchanneltitle); + image.attr('title', item.user.lastchanneltitle); + } + } else { + // we have no image so we have to create one + + // if we have picon clear text + $(uid + " > td.usercol6").text(''); + + // just to be sure that class of image is set + if ($(uid + " > td.usercol6 > img").length) { + $(uid + " > td.usercol6 > img").attr('class', 'usericon'); + } + + newimage = $(''); + newimage.hide(); + $(uid + " > td.usercol6").append(newimage); + newimage.fadeIn('slow'); + newimage.attr('alt', item.user.lastchanneltitle); + newimage.attr('title', item.user.lastchanneltitle); + } + } else { + $(uid + " td.usercol6").html(item.user.lastchannel); + } + } + + if (!is_nopoll('usercol7')) { + $(uid + " td.usercol7") + .text(item.user.stats.cwlastresptimems); + } + //usercol8 ??? + if (!is_nopoll('usercol9')) { + $(uid + " td.usercol9").text(item.user.stats.cwok); + } + if (!is_nopoll('usercol10')) { + $(uid + " td.usercol10").text(item.user.stats.cwnok); + } + if (!is_nopoll('usercol11')) { + $(uid + " td.usercol11").text(item.user.stats.cwignore); + } + if (!is_nopoll('usercol12')) { + $(uid + " td.usercol12").text(item.user.stats.cwtimeout); + } + if (!is_nopoll('usercol13')) { + $(uid + " td.usercol13").text(item.user.stats.cwccyclechecked + ' / ' + item.user.stats.cwcycleok + ' / ' + item.user.stats.cwcyclenok + ' / ' + item.user.stats.cwcycleign); + } + if (!is_nopoll('usercol14')) { + $(uid + " td.usercol14").text(item.user.stats.cwcache); + } + if (!is_nopoll('usercol15')) { + $(uid + " td.usercol15").text(item.user.stats.cwtun); + } + if (!is_nopoll('usercol16')) { + $(uid + " td.usercol16").text(item.user.stats.cwcache); + } + if (!is_nopoll('usercol17')) { + $(uid + " td.usercol17").text(item.user.stats.emmok); + } + if (!is_nopoll('usercol18')) { + $(uid + " td.usercol18").text(item.user.stats.emmnok); + } + if (!is_nopoll('usercol19')) { + $(uid + " td.usercol19").text(item.user.stats.cwrate + item.user.stats.cwrate2); + } + if (!is_nopoll('usercol22')) { + $(uid + " td.usercol22").text(item.user.stats.cascusercomb); + } + if (!is_nopoll('usercol21')) { + $(uid + " td.usercol21").text(item.user.stats.n_requ_m); + } + if (!is_nopoll('usercol20')) { + $(uid + " td.usercol20") + .attr('title', item.user.expview) + .text(item.user.stats.expdate); + } + break; + + case 'connected': + $(uid).attr('class', item.user.classname); + + if (!is_nopoll('usercol1')) { + if ($(uid + " td.usercol1 > span.span_notifier").length) { + if(item.user.unotify){ + $(uid + " td.usercol1 > span.span_notifier") + .text(item.user.unotify); + } + else { + $(uid + " td.usercol1 > span.span_notifier").remove(); + } + } + else if(item.user.unotify) { + $(uid + " td.usercol1") + .append(''+ item.user.unotify + ''); + } + } + + if (!is_nopoll('usercol2')) { + $(uid + " td.usercol2") + .attr('title', '') + .data('sort-value', item.user.ip) + .html("" + item.user.status + "
    " + item.user.ip); + } + + if (!is_nopoll('usercol3')) { + $(uid + " td.usercol3").html(item.user.stats.idle + "
    " + item.user.stats.timeonchannel.toHHMMSS()); + } + + if (!is_nopoll('usercol4')) { + if (item.user.protoicon.length > 0) { + if (!$(uid + " td.usercol4 > img").length || $(uid + " td.usercol4 > img").attr('src')!='image?i=IC_' + item.user.protoicon) { + var protoimage = $(''); + protoimage.hide(); + $(uid + " td.usercol4").html(protoimage); + protoimage.fadeIn('slow'); + } + } else { + $(uid + " td.usercol4").text(item.user.protocol); + } + $(uid + " td.usercol4") + .attr('title', item.user.prototitle) + .data('sort-value', item.user.protosort); + } + + if (!is_nopoll('usercol6')) { + // channel icon + $(uid + " td.usercol6") + .attr('title', item.user.lastchanneltitle) + .data('sort-value', item.user.lastchannelsort); + + if (item.user.lca.length > 0) { + var image; + if ($(uid + " td.usercol6").html().length == 0) { + image = $(''); + image.hide(); + $(uid + " td.usercol6").prepend(image); + image.fadeIn('slow'); + } else { + image = $(uid + " td.usercol6 img.usericon"); + if (image.attr('src') != ('image?i=IC_' + item.user.lca)) { + image.fadeOut('fast', function () { + image.attr('src', 'image?i=IC_' + item.user.lca); + image.fadeIn('slow'); + }); + image.attr('alt', item.user.lcb); + image.attr('title', item.user.lastchanneltitle); + } + } + } else { + $(uid + " td.usercol6").html(item.user.lastchannel); + } + } + + if (!is_nopoll('usercol7')) { + $(uid + " td.usercol7") + .text(item.user.stats.cwlastresptimems); + } + if (!is_nopoll('usercol19')) { + $(uid + " td.usercol19").text(item.user.stats.cwrate); + } + break; + + default: + //check the last status + if ('online,connected'.indexOf($(uid).attr('class')) > (-1)) { + // last status was online so cleanup offline + $(uid).attr('class', item.user.classname); + if (!is_nopoll('usercol1')) { + if ($(uid + " td.usercol1 > span.span_notifier").length) { + $(uid + " td.usercol1 > span.span_notifier").remove(); + } + } + if (!is_nopoll('usercol2')) { + $(uid + " td.usercol2") + .attr('title', '') + .html(item.user.status); + } + if (!is_nopoll('usercol3')) { + $(uid + " td.usercol3").text(''); + } + if (!is_nopoll('usercol4')) { + $(uid + " td.usercol4") + .text('') + .attr('title', ''); + var protoimage = $(uid + " td.usercol4 img.protoicon"); + if (image) { + protoimage.fadeOut('slow'); + protoimage.remove(); + } + } + + //channelicon + if (!is_nopoll('usercol6')) { + $(uid + " td.usercol6") + .text('') + .data('sort-value', ''); + + var image = $(uid + " td.usercol6 img.usericon"); + if (image) { + image.fadeOut('slow'); + image.remove(); + } + } + if (!is_nopoll('usercol7')) { + $(uid + " td.usercol7") + .text(''); + } + } + break; + } + + if (typeof custompoll == 'function') { + custompoll(item); + } + + }); + + // update user totals + ECM + updateTotals(data); + + // update footer + updateFooter(data); +} + +/* + * Readerpage Functions: Update Page + */ +function updateReaderpage(data) { + + // update reader lines + $.each(data.oscam.readers, function (i, item) { + var uid = "#" + item.labelmd5; + poll_excluded = ($(uid).attr('nopoll') != undefined) ? $(uid).attr('nopoll') : ''; + + $(uid).attr('class', item.classname); + + if (!is_nopoll('readercol4')) { + $(uid + " td.readercol4").text(item.stats.ecmsok + item.stats.ecmsokrel) + .data('sort-value', item.stats.ecmsok); + } + if (!is_nopoll('readercol19')) { + $(uid + " td.readercol19").text(item.stats.ecmsoklg + item.stats.ecmsoklgrel) + .data('sort-value', item.stats.ecmsoklg); + } + if (!is_nopoll('readercol5')) { + $(uid + " td.readercol5").text(item.stats.ecmsnok + item.stats.ecmsnokrel) + .data('sort-value', item.stats.ecmsnok); + } + if (!is_nopoll('readercol6')) { + $(uid + " td.readercol6").text(item.stats.ecmstout + item.stats.ecmstoutrel) + .data('sort-value', item.stats.ecmstout); + } + if (!is_nopoll('readercol7')) { + $(uid + " td.readercol7").text(item.stats.ecmsfiltered); + } + if (!is_nopoll('readercol8')) { + $(uid + " td.readercol8").text(item.stats.emmerror); + } + if (!is_nopoll('readercol9')) { + $(uid + " td.readercol9").text(item.stats.emmwritten); + } + if (!is_nopoll('readercol20')) { + $(uid + " td.readercol20").html(item.status + "
    " + item.ip); + } + if (!is_nopoll('readercol10')) { + $(uid + " td.readercol10").text(item.stats.emmskipped); + } + if (!is_nopoll('readercol11')) { + $(uid + " td.readercol11").text(item.stats.emmblocked); + } + if (!is_nopoll('readercol12')) { + $(uid + " td.readercol12").text(item.stats.lbweight); + } + if (!is_nopoll('readercol2')) { + if (data.oscam.piconenabled == "1" && item.protoicon) { + $(uid + " td.readercol2").html('IC_' + item.protoicon + ''); + } else { + $(uid + " td.readercol2").text(item.protocol); + } + + $(uid + " td.readercol2") + .data('sort-value', item.protosort); + } + + if (typeof custompoll == 'function') { + custompoll(item); + } + }); + + // update user totals + ECM + updateTotals(data); + + // update footer + updateFooter(data); + +} + +/* + * LiveLog Functions: format the debuglevel switcher + */ +function setDebuglevel(debug, maxdebug) { + var cs_dblevel = parseInt(debug); + var maxlevel = parseInt(maxdebug); + if (lastdebuglevel != cs_dblevel) { + var lvl = 0; + $("#debugfrom").text(' Switch Debug from ' + cs_dblevel + ' to '); + for (var i = 0; i < maxlevel; i++) { + lvl = 1 << i; + if (cs_dblevel & lvl) { + $("#debug" + lvl).attr('sendval', cs_dblevel - lvl); + } else { + $("#debug" + lvl).attr('sendval', cs_dblevel + lvl); + } + $("#debug" + lvl).attr('class', (cs_dblevel & lvl) ? 'debugls' : 'debugl'); + } + lastdebuglevel = cs_dblevel; + } +} + +/* + * Livelog Functions: get filter color + */ +function getLogColor(text) { + + if (nostorage) { + return null; + } + + for (var i = 1; i < MAX_SEARCH_PATTERN + 1; i++) { + var pattern = localStorage['regex' + i]; + var color = localStorage['color' + i]; + var fcolor = localStorage['fcolor' + i]; + var hidden = localStorage['hidden' + i]; + var beep = localStorage['beep' + i]; + var regex = new RegExp(pattern); + if (pattern && (pattern != '') && (regex.exec(text))) { + return { + color: color, + fcolor: fcolor, + hidden: hidden, + beep: beep + } + } + } + return null; +} + +/* + * Livelog Functions: get whitelist state + */ +function isWhitelisted(text) { + + if (nostorage) { + return 1; + } + + var numwhite = 0; + for (var i = 1; i < MAX_SEARCH_PATTERN + 1; i++) { + numwhite += parseInt(localStorage['whitelisted' + i]); + } + if (numwhite > 0) { + for (var i = 1; i < MAX_SEARCH_PATTERN + 1; i++) { + var whitelisted = localStorage['whitelisted' + i]; + var pattern = localStorage['regex' + i]; + var regex = new RegExp(pattern); + if (pattern && (pattern != '') && (whitelisted == '1') && (regex.exec(text))) { + return 1; + } + } + return 0; + } else { + return 1; + } +} + +/* + * LiveLog Functions: manage the delivered data / loglines + */ +function updateLogpage(data) { + + lockpoll = 1; + + if (data.oscam.debug) { + setDebuglevel(data.oscam.debug, data.oscam.maxdebug); + } + if (data.oscam.logdisabled) { + stoppoll = 1; + $("#livelogdata").append('
  • Log is disabled
  • \n'); + } + + $.each(data.oscam.lines, function (i, item) { + + if (isWhitelisted(Base64.decode(item.line))) { + var newcolor = getLogColor(Base64.decode(item.line)); + var newline = $('
  • ' + Base64.decode(item.line) + '
  • \n'); + var hiddenline = 0; + if (newcolor) { + if (newcolor.hidden != '1') { + if (newcolor.color && newcolor.color != '') { + newline.css('background-color', newcolor.color); + } + if (newcolor.fcolor && newcolor.fcolor != '') { + newline.css('color', newcolor.fcolor); + } + $("#livelogdata").append(newline); + if (newcolor.beep == 1) { + beep(50, 4, 1000, 0.2); + } + } else { + hiddenline = 1; + } + } else { + $("#livelogdata").append(newline); + } + + if (!hiddenline) { + if ($("#livelog:hover").length) { + $('#livelog').stop(true); + } else { + $("#livelog").scrollTop($("#livelog").prop("scrollHeight")); + } + } + } + parameters = "?lastid=" + item.id; + }); + + var len = $("#livelogdata li").length; + if (len > maxloglines) { + $("#livelogdata li").slice(0, len - maxloglines).remove(); + } + + // update footer + updateFooter(data); + + lockpoll = 0; + +} + +/* + * Statuspage Functions: JQuery Extensions + */ +$.fn.toHtmlString = function () { + return $('').html($(this).clone()).html(); +}; + +/* + * Statuspage Functions: Generate a Bar for Barchart + */ +function generateBar(value) { + var bar = $('
    '); + var maxheight = 75; //$( "#graph" ).height() -15; + var numval = parseInt(value); + numval = Math.floor(numval / 30); + if (numval >= maxheight) { + bar.css('background-color', '#FF0000'); + numval = maxheight; + } + bar.css('height', numval + 'px'); + return bar; +} + +/* + * Statuspage Functions: Add/Remove Subheadline + */ +function addremoveSubheadline(remove, data, container, subheadline, type) { + + if (remove == 1 && $("#" + subheadline).length) { + $("#" + subheadline) + .fadeOut('slow') + .remove(); + $(".status tbody:empty").hide(); + } + + if (remove == 0 && !$("#" + subheadline).length) { + $(container).removeAttr('style'); + var strheadline = ''; + if (type == 'c') { + if (data.oscam.status.ucac != '') { //hide idle clients + strheadline += '

    Clients ' + data.oscam.status.ucs + '/' + data.oscam.status.uca + ' (' + data.oscam.status.ucac + ' with ECM within last ' + data.oscam.status.cfgh + ' seconds)

    ' + } else { + strheadline += '

    Clients ' + data.oscam.status.ucs + '/' + data.oscam.status.uca + '

    ' + } + strheadline += '
    '; + strheadline += ''; + strheadline += ''; + } else if (type == 'm') { + strheadline += '

    Server ' + data.oscam.status.scs + '/' + data.oscam.status.sca + ' & Monitors ' + data.oscam.status.mcs + '/' + data.oscam.status.mca + '

    ' + strheadline += '
    '; + } + strheadline += '
    '; + var headline = $(strheadline); + headline.hide(); + $(container).append(headline); + headline.fadeIn('slow'); + } +} + +function fillStatus(list, type, data) { + list.forEach(function(key) { + $("#" + key).text(data.oscam[type][key]); + }); +} + + +/* + * Statuspage Functions: Update Totals cacheEx + */ +function updateCacheextotals(data) { + fillStatus(["total_cachexpush", "total_cachexgot", "rel_cachexhit", "total_cachesize"], "totals", data); +} + +/* + * Statuspage Functions: Update Totals User + Totals Reader + ECM + EMM + */ +function updateTotals(data) { + fillStatus(["total_users", "total_active", "total_connected", "total_online", "total_disabled", "total_expired", "total_readers", "total_active_readers", "total_connected_readers", "total_disabled_readers", "total_cwok", "total_cwok_readers", "rel_cwok", "rel_cwok_readers", "total_cwcache", "rel_cwcache", "total_cwnok", "total_cwnok_readers", "rel_cwnok", "rel_cwnok_readers", "total_cwtout", "total_cwtout_readers", "rel_cwtout", "rel_cwtout_readers", "total_cwign", "total_ecm_min", "total_cw", "total_cwpos", "total_cwpos_readers", "rel_cwpos", "rel_cwpos_readers", "total_cwneg", "total_cwneg_readers", "rel_cwneg", "rel_cwneg_readers", "total_emok", "rel_emok", "total_emnok", "rel_emnok", "total_em", "total_elenr", "total_eheadr", "total_emmerroruk_readers", "total_emmerrorg_readers", "total_emmerrors_readers", "total_emmerroruq_readers", "total_emmwrittenuk_readers", "total_emmwritteng_readers", "total_emmwrittens_readers", "total_emmwrittenuq_readers", "total_emmskippeduk_readers", "total_emmskippedg_readers", "total_emmskippeds_readers", "total_emmskippeduq_readers", "total_emmblockeduk_readers", "total_emmblockedg_readers", "total_emmblockeds_readers", "total_emmblockeduq_readers", "total_sum_all_readers_ecm", "total_sum_all_readers_emm"], "totals", data); +} + +/* + * Statuspage Functions: Update Totals Sysinfo + */ +var first_run = 1; + +function updateSysinfo(data) { + fillStatus(["mem_cur_total", "mem_cur_free", "mem_cur_used", "mem_cur_buff", "mem_cur_cached", "mem_cur_totalsw", "mem_cur_freesw", "mem_cur_usedsw", "mem_cur_shared", "oscam_vmsize", "oscam_rsssize", "server_procs", "cpu_load_0", "cpu_load_1", "cpu_load_2"], "sysinfo", data); + $("#mem_cur_freem").attr('title', 'max Free: ' + data.oscam.sysinfo.mem_cur_freem + ' \n(incl. Buffer & Cached)'); + if (!first_run) { + fillStatus(["oscam_refresh", "oscam_cpu_user", "oscam_cpu_sys", "oscam_cpu_sum"], "sysinfo", data); + } + first_run = 0; +} + +/* + * Statuspage Functions: Update Page + */ +function updateStatuspage(data) { + + var updatedclients = ""; + // update status lines + $.each(data.oscam.status.client, function (i, item) { + var newrow; + + //add ID's for type c and m to list of existing elements. We need this to delete all not longer existing + updatedclients += item.thid + ","; + + var uid = "#" + item.thid; + poll_excluded = ($(uid).attr('nopoll') != undefined) ? $(uid).attr('nopoll') : ''; + + if (!$(uid).length && 'rpcxm'.indexOf(item.type) > (-1)) { + //build new row + var rowcontent = ''; + rowcontent += ''; + rowcontent += ''; + rowcontent += ''; + newrow = $(rowcontent); + newrow.hide(); + // if we have no clients we have to add the headline first + + // append new clientrow to table + var container = ''; + if ('hms'.indexOf(item.type) > (-1)) { + container = '#tbodys'; + if (item.type == 'm') { + if (!$("#Serverheadline").length) { + addremoveSubheadline(0, data, container, "Serverheadline", item.type); + } else if (!$("#mca").length) { + $("#shead").append(' & Monitors ' + data.oscam.status.mcs + '/' + data.oscam.status.mca + ''); + } + } + } else if ('px'.indexOf(item.type) > (-1)) { + container = '#tbodyp'; + } else { + container = '#tbody' + item.type; + if (!$("#Userheadline").length && item.type == 'c') { + addremoveSubheadline(0, data, container, "Userheadline", item.type); + } + } + $(container).append(newrow); + + var name1, name2, name3, kill1, kill2, kill3, edit1; + switch (item.type) { + case 'c': + case 'm': + name1 = 'User'; + name2 = item.name_enc; + kill1 = '" href="status.html?action=kill&threadid=' + item.thid.substring(3, item.thid.length); + kill2 = 'Kill' + kill3 = 'ICKIL'; + edit1 = 'user_edit.html?user='; + break; + case 'r': + case 'p': + case 'x': + name1 = (item.type == 'r') ? 'Reader' : 'Proxy'; + name2 = item.rname_enc; + kill1 = '" href="status.html?action=restart&label=' + name2; + kill2 = 'Restart'; + kill3 = 'ICRES'; + edit1 = 'readerconfig.html?label='; + break; + } + name3 = decodeURIComponent(name2); + + if (!is_nopoll('statuscol0')) { + $(uid + " > td.statuscol0").append('Hide'); + } + + if (!is_nopoll('statuscol1')) { + $(uid + " > td.statuscol1").append('' + kill2 +
+					''); + } + + if (!is_nopoll('statuscol4')) { + if (data.oscam.piconenabled == "1" && !item.upicmissing) { + $(uid + " > td.statuscol4").append(''); + } else { + $(uid + " > td.statuscol4").append('' + name3 + ''); + } + } + + if (!is_nopoll('statuscol13')) { + $(uid + " > td.statuscol13").append(''); + } + + if (!is_nopoll('statuscol9')) { + if (data.oscam.piconenabled == "1" && item.protoicon) { + $(uid + " > td.statuscol9").append('IC_' + item.protoicon + ''); + } else { + $(uid + " > td.statuscol9").attr('title', item.protocolext).text(item.protocol); + } + } + } + + $(uid).attr('class', item.type).data('ecmhistory', item.request.ecmhistory).removeAttr('style'); + + // fix for anonymous newcamd-clients + if ($(uid + " > td.statuscol4").text().match('anonymous')) { + if (!is_nopoll('statuscol9')) { + if (data.oscam.piconenabled == "1" && item.protoicon) { + $(uid + " > td.statuscol9").html('IC_' + item.protoicon + ''); + } else { + $(uid + " > td.statuscol9").attr('title', item.protocolext).text(item.protocol); + } + } + + if (!is_nopoll('statuscol4')) { + if (data.oscam.piconenabled == "1" && !item.upicmissing) { + $(uid + " > td.statuscol4").html(''); + } else { + $(uid + " > td.statuscol4").html('' + decodeURIComponent(item.name_enc) + ''); + } + } + } + + if (!is_nopoll('statuscol5')) { + switch (item.au) { + case '0': + $(uid + " > td.statuscol5").text('OFF').attr('class', 'statuscol5 statuscol5OFF'); + break; + case '-1': + $(uid + " > td.statuscol5").html('ON' + item.aufmt + '').attr('class', 'statuscol5 statuscol5ON'); + break; + default: + $(uid + " > td.statuscol5").html('ACTIVE' + item.aufmt + '').attr('class', 'statuscol5 statuscol5ACTIVE'); + break; + } + } + + if (!is_nopoll('statuscol4')) { + $(uid + " > td.statuscol4").attr('title', decodeURIComponent(item.type == 'c' ? item.name_enc : item.rname_enc) + (item.desc ? '\n' + item.desc.replace(' ', '') : '')); + } + if (!is_nopoll('statuscol7')) { + $(uid + " > td.statuscol7").text(item.connection.ip); + } + if (!is_nopoll('statuscol8')) { + $(uid + " > td.statuscol8").text(item.connection.port); + } + if (!is_nopoll('statuscol9')) { + $(uid + " > td.statuscol9").attr('title', item.protocolext); + } + if (!is_nopoll('statuscol12')) { + $(uid + " > td.statuscol12").text(item.request.srvid + ':' + item.request.caid + '@' + item.request.provid); + } + + if (!is_nopoll('statuscol13')) { + var newimage; + + if (data.oscam.piconenabled == '1' && item.request.srvid != '0000' && item.request.picon) { + + // if we already have a picon within link + if ($(uid + " > td.statuscol13 > a > img.statususericon").length) { + // we compare the picon name and switch if different + var image = $(uid + " > td.statuscol13 > a > img.statususericon"); + if (image.attr('src') != 'image?i=IC_' + item.request.picon) { + // set title of link as tooltip + $(uid + " > td.statuscol13 > a").attr('title', item.request.chname + item.request.chprovider); + image.hide(); + image.attr('src', 'image?i=IC_' + item.request.picon); + image.fadeIn('slow'); + } + } else { + // we have no image so we have to create one + + // if we have picon clear text + $(uid + " > td.statuscol13").text(''); + + // if we have no link we create one + if (!$(uid + " > td.statuscol13 > a").length) { + $(uid + " > td.statuscol13").append(''); + } + // set title of link as tooltip + $(uid + " > td.statuscol13 > a").attr('title', item.request.chname + item.request.chprovider); + + // just to be sure that class of image is set + if ($(uid + " > td.statuscol13 > a > img").length) { + $(uid + " > td.statuscol13 > a > img").attr('class', 'statususericon'); + } + + newimage = $(''); + newimage.hide(); + $(uid + " > td.statuscol13 > a").append(newimage); + newimage.fadeIn('slow'); + } + + } else { + // picon is not delivered in JSON - we set the text of column + if (item.request.chname && item.request.srvid != '0000') { + $(uid + " > td.statuscol13").html(''); + $(uid + " > td.statuscol13 > a").html(item.request.chname + item.request.chprovider); + $(uid + " > td.statuscol13 > a").attr('title', item.request.chname + item.request.chprovider); + } else { + $(uid + " > td.statuscol13").html(''); + } + } + } + + if (!is_nopoll('statuscol14')) { + if ('hms'.indexOf(item.type) > (-1)) { + $(uid + " > td.statuscol14").text(''); + } else { + var value = item.type == 'c' ? (item.request.answered ? item.request.answered + ' (' + item.request.msvalue + ' ms)' : '') : item.request.lbvalue; + if (data.oscam.lbdefined) { + var label = item.rname_enc.replace('+%28cache%29', ''); + var name = item.type == 'c' ? item.request.answered.replace(' (cache)', '') : decodeURIComponent(label); + if (!$(uid + " > td.statuscol14 > a").length) { + $(uid + " > td.statuscol14") + .text('') + .append(''); + } else { + $(uid + " > td.statuscol14 > a") + .attr('href','readerstats.html?label=' + label + '&show=0') + .attr('title','Show statistics for: ' + name); + } + $(uid + " > td.statuscol14 > a").text(value); + } else { + $(uid + " > td.statuscol14").text(value); + } + } + } + + if (!is_nopoll('statuscol15')) { + if ($("#onlineidle").text() != 'Login*') { + $(uid + " > td.statuscol15") + .html(item.times.online.toHHMMSS() + '
    ' + item.times.idle.toHHMMSS()) + .attr('title', 'Login: ' + item.times.loginfmt); + } else { + $(uid + " > td.statuscol15") + .html(item.times.loginfmt.substring(0, 8) + '
    ' + item.times.loginfmt.substring(10, 18)) + .attr('title', 'Online: ' + item.times.online.toHHMMSS() + '\nIDLE: ' + item.times.idle.toHHMMSS()); + } + } + + if (!is_nopoll('statuscol16')) { + var entitlement = ''; + + switch (item.type) { + case 'r': + // entitlement for native cards + + var activeentitlements = item.connection.entitlements.length; + if (activeentitlements > 0) { + entitlement += '
    '; + entitlement += '(' + activeentitlements + ' entitlement' + ((activeentitlements != 1) ? 's)' : ')'); + entitlement += ''; + $.each(item.connection.entitlements, function (i, obj) { + entitlement += obj.caid + ':' + obj.provid + '
    ' + obj.exp + '

    '; + }); + entitlement = entitlement.substring(0, entitlement.length - 4); + entitlement += '
    '; + } else { + entitlement += '
    (no entitlements)No active entitlements found'; + } + break; + + case 'p': + if (item.connection.entitlements.length > 0 && item.protocol.indexOf('cccam') > -1) { + // cccam + var entobj = item.connection.entitlements[0]; + entitlement += '
    '; + entitlement += '(' + entobj.locals + ' of ' + entobj.cccount + ' card' + (entobj.cccount > 1 ? "s" : "") + ')'; + entitlement += 'card_count=' + entobj.cccount + '
    '; + entitlement += 'hop1=' + entobj.ccchop1 + '
    '; + entitlement += 'hop2=' + entobj.ccchop2 + '
    '; + entitlement += 'hopx=' + entobj.ccchopx + '
    '; + entitlement += 'currenthops=' + entobj.ccccurr + '

    '; + entitlement += 'reshare0=' + entobj.cccres0 + '
    '; + entitlement += 'reshare1=' + entobj.cccres1 + '
    '; + entitlement += 'reshare2=' + entobj.cccres2 + '
    '; + entitlement += 'resharex=' + entobj.cccresx + '
    '; + } + if (item.protocol.indexOf('gbox') > -1) { + // TO DO gbox + var $html = $(uid + " > td.statuscol16").toHtmlString(); + if ($html != undefined) { + entitlement = $html.substring($html.indexOf('
    '), $html.indexOf('')); + if (entitlement) entitlement += ''; + } + } + break; + } + + $(uid + " > td.statuscol16").empty().html(item.connection.status + entitlement).attr('class', 'statuscol16 statuscol16' + item.connection.status); + } + + if (newrow) { + newrow.fadeIn("slow"); + } + + if (typeof custompoll == 'function') { + custompoll(item); + } + + }); + + //remove non existing elements + $("tr.c, tr.m, tr.r, tr.p, tr.h").each(function () { + if (updatedclients.indexOf($(this).attr('id')) == -1) { + $(this).fadeOut('slow').remove(); + } + }); + + // if we have no clients left we remove the headline + if (!$("tr.c").length && data.oscam.status.uca == '0') { + addremoveSubheadline(1, '', '', "Userheadline", 'c'); + } + // if we have no servers/monitors left we remove the headline + if (!$("tr.m").length && data.oscam.status.mca == '0') { + if ($("#mca").length) { + $("#shead").replaceWith('

    Server ' + data.oscam.status.scs + '/' + data.oscam.status.sca + '

    '); + } + if (!$("tr.s").length && !$("tr.h").length && data.oscam.status.sch == '0') { + addremoveSubheadline(1, '', '', "Serverheadline", 'm'); + } + } + + //update client-headline + if (data.oscam.status.uca != '0') { + if (!$("#Userheadline").length) { + addremoveSubheadline(0, data, "#tbodyc", "Userheadline", "c"); + } else { + $("#ucs").text(data.oscam.status.ucs); + $("#uca").text(data.oscam.status.uca); + if (data.oscam.status.ucac != '0') $("#ucac").text(data.oscam.status.ucac); + } + } + //update server/monitor-headline + if (data.oscam.status.mca != '0' && $("#mcs")) { + $("#mcs").text(data.oscam.status.mcs); + $("#mca").text(data.oscam.status.mca); + } + + //update reader-headline + if(data.oscam.status.rco != '0') { + var rcon = (data.oscam.status.rca - data.oscam.status.rco); + if($("#rco").length) { + $("#rcc").text(data.oscam.status.rcc); + $("#rca").text(data.oscam.status.rca); + $("#rco").text(rcon); + } else { + $("#rhead").html('Readers ' + data.oscam.status.rcc + '/' + data.oscam.status.rca + ' (' + rcon + ' of ' + data.oscam.status.rca + ' CARDOK)'); + } + } else if($("#rco").length) { + $("#rhead").html('Readers ' + data.oscam.status.rcc + '/' + data.oscam.status.rca); + } else { + $("#rcc").text(data.oscam.status.rcc); + $("#rca").text(data.oscam.status.rca); + } + + //update proxy-headline + if(data.oscam.status.pco != '0') { + var pcon = (data.oscam.status.pca - data.oscam.status.pco); + if($("#pco").length) { + $("#pcc").text(data.oscam.status.pcc); + $("#pca").text(data.oscam.status.pca); + $("#pco").text(pcon); + } else { + $("#phead").html('Proxies ' + data.oscam.status.pcc + '/' + data.oscam.status.pca + ' (' + pcon + ' of ' + data.oscam.status.pca + ' online)'); + } + } else if($("#pco").length) { + $("#phead").html('Proxies ' + data.oscam.status.pcc + '/' + data.oscam.status.pca); + } else { + $("#pcc").text(data.oscam.status.pcc); + $("#pca").text(data.oscam.status.pca); + } + + // update footer + updateFooter(data); + + // sysinfos + if ($("#mem_cur_total").length) updateSysinfo(data); + + // user + ecm totals + if ($("#total_users").length) updateTotals(data); + + // cachex + if ($("#total_cachexpush").length) updateCacheextotals(data); + +} + + +/* + * General fork into page refresh functions + */ +function updatePage(data) { + + // show heartbeat + if ($("input.pintervall").length && $("input.pintervall").css("background-color") != $("#picolor").css("background-color")) { + var orgstyle = $("input.pintervall").css("background-color"); + $("input.pintervall").css("background-color", $("#picolor").css("background-color")); + } + + switch (page) { + case 'status': + updateStatuspage(data); + break; + case 'user': + updateUserpage(data); + break; + case 'reader': + updateReaderpage(data); + break; + case 'livelog': + updateLogpage(data); + break; + case 'cacheex': + updateCacheextotals(data); + break; + default: + break; + } + + // hide heartbeat + if ($("input.pintervall").length && $("input.pintervall").css("background-color") == $("#picolor").css("background-color")) { + setTimeout(function () { + $("input.pintervall").css("background-color", orgstyle); + }, 300); + } + + if (typeof afterpoll == 'function') { + afterpoll(); + } +} + +function setPollerr(error) { + if (error && !$("#pollerr").length) { + $("body").append('
    POLLERR
    '); + } else { + if ($("#pollerr").length) { + $("#pollerr").fadeOut('slow').remove(); + } + } +} + +/* + * General Polling + */ +var lockpoll = 0; +var timer_ID; + +function waitForMsg() { + + if (typeof pollrefresh == 'undefined') return; + + if (lockpoll > 0) { + /* assumed that previous poll is not finnished yet we not + call new data and just set the next intervall */ + clearTimeout(timer_ID); + timer_ID = setTimeout("waitForMsg()", pollintervall); + return; + } + + $.ajax({ + type: "GET", + url: jsonurl + parameters, + dataType: "JSON", + async: true, + cache: false, + success: function (data) { + setPollerr(0); + if ((!pollrefresh || polling ) && page != 'livelog') return; + updatePage(data); + if (!stoppoll) { + clearTimeout(timer_ID); + timer_ID = setTimeout("waitForMsg()", pollintervall); + } + }, + error: function (XMLHttpRequest, textStatus, errorThrown) { + clearTimeout(timer_ID); + timer_ID = setTimeout("waitForMsg()", 15000); + setPollerr(1); + } + }); +} + +/* + * General: Set Poll Interval + */ +function setPollrefresh() { + // Set pollintervall, if pollrefresh set to 0 disable polling + if (pollrefresh) { + pollintervall = parseInt(pollrefresh) * 1000; + if (pollintervall > 99000) pollintervall = 99000; + if (!nostorage) { + if (sessionStorage.pollintervall) pollintervall = sessionStorage.pollintervall; + else sessionStorage.pollintervall = pollintervall; + if (sessionStorage.polling == 1) polling = 1; + } + } +} + +// static for paranoid Browsers +var nostorage = 0; + +/* + * General: Start Polling + */ +$(document).ready(function () { + + try { + if (!localStorage) { + nostorage = 1; + // remove whole filter block - makes no sense + // without saving + $('#regex').remove(); + } + } catch(err){ + nostorage = 1; + $('#regex').remove(); + } + + // set default to nothing excluded + poll_excluded = ''; + + // help wiki links + if (typeof oscamconf != "undefined") { + var wikihref = "https://git.streamboard.tv/common/oscam/-/wikis/pages/configuration/oscam." + oscamconf + "#"; + $("form table a").click(function () { + if (!$(this).attr("href") && !$(this).attr("name")) { + if ($(this).data('p')) { + var parm = $(this).data('p'); + } else { + var parm = $(this).parent().next().find("input,select,textarea").attr('name'); + } + window.open(wikihref + parm); + } + }); + } + + // Title + var pagename = (typeof page != 'undefined' ? page : $(location).attr('pathname').replace(/.*\/|\.[^.]*$/g, '')); + $(document).attr('title', $(document).attr('title') + ' (' + pagename[0].toUpperCase() + pagename.slice(1) + ')'); + + if (typeof page != 'undefined') { + + switch (page) { + + case 'livelog': + + var saved_regex = localStorage.MAX_SEARCH_PATTERN; + MAX_SEARCH_PATTERN = parseInt(saved_regex ? saved_regex : MAX_SEARCH_PATTERN); + $('
  • ').insertBefore(".regexdata_save"); + + var beep_disabled = ' disabled="disabled" title="Not supported by your browser"'; + var contextClass = (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext); + if (contextClass) { beep_disabled = ''; } + + for (var i = 1; i < MAX_SEARCH_PATTERN + 1; i++) { + + var prefix = "0"; + if ( i > 9 ) { prefix = ""; } + + $('
  • Search' + prefix + i + ': Found only: Hide: Back Color: Color: Beep:
  • ').insertBefore(".regexdata_save"); + } + + if (!nostorage) { + for (var i = 1; i < MAX_SEARCH_PATTERN + 1; i++) { + var pattern = localStorage['regex' + i]; + var color = localStorage['color' + i]; + var fcolor = localStorage['fcolor' + i]; + $('#regex' + i).val(pattern ? pattern : ''); + $('#color' + i).val(color ? color : $('.colorPicker_def_color').css('color')); + $('#fcolor' + i).val(fcolor ? fcolor : $('.colorPicker_def_fcolor').css('color')); + $('#color' + i).colorPicker(); + $('#fcolor' + i).colorPicker(); + $('#whitelisted' + i).prop('checked', localStorage['whitelisted' + i] == '1' ? true : false); + $('#hidden' + i).prop('checked', localStorage['hidden' + i] == '1' ? true : false); + $('#beep' + i).prop('checked', localStorage['beep' + i] == '1' ? true : false); + } + } + waitForMsg(); + + break; + + case 'status': + + $(".status tbody:empty").hide(); + $("#chart").hide(); + if (!nostorage) { + if (localStorage.loi == 'Login*') { + $("#onlineidle") + .text('Login*') + .css('cursor','pointer') + .attr('title', 'Online & Idle info (click to switch)'); + } else { + $("#onlineidle") + .text('Online & Idle*') + .css('cursor','pointer') + .attr('title', 'Login info (click to switch)'); + } + } + break; + + default: + + break; + } + + // if pollrefresh set to 0 hide pollselector + setPollrefresh(); + if (pollrefresh) { + if (polling) { + $(":text[name='pintervall']").val('--'); + $('#polling').attr('class','pollingdisabled'); + $('#inc, #dec').attr('disabled','disabled'); + } else { + $(":text[name='pintervall']").val(pollintervall / 1000); + $('#polling').attr('class','pollingenabled'); + $('#inc, #dec').removeAttr('disabled'); + waitForMsg(); + } + $("#poll").show(); + } + } +}); + +function decodeVideoguardEMM(text, target, addHideButton) { + + text = text.trim(); + + var bytes = text.replace(/[^A-Fa-f0-9]/g, "").toUpperCase().match(/.{1,2}/g) || []; + var html = ''; + + if (addHideButton) { + html += '


    '; + } + + var AddTextType = {"data":"Data", "length":"Length", "type":"Type", "emmType":"EMM-Type", "encryptionType":"Encryption Type", + "keyIndex":"Key-Index", "keyIndex2":"Key-Index2", "fixedValue":"Fixed Value", "pairingDevice":"Pairing Device", "date":"Date", + "checksum":"Checksum", "emmStartMarker":"EMM Marker", "cardSerial":"Serial Number (Smartcard)", + "boxSerial":"Serial Number (Receiver)", "emmEndMarker":"Sub-EMM End", "rest":"rest ????", + "cardEmmLength":"Card EMM Length", "cardNanoLength":"Card Nano Length", "irdEmmLength":"IRD EMM Length", + "filterSectionLength":"Filter Section Length", "irdNanoLength":"IRD Nano Length", "cardNanoType":"Card Nano Type", + "irdNanoType":"IRD Nano Type", "filterNanoType":"Filter Nano Type", "cardGroupSerial":"Serial Number (Card Group)", + "mpegSectionLength":"EMM Length", "irdEmmChecksum":"IRD EMM Checksum", "pairingdeviceCount":"Pairing Device Count"}; + + function addText(count, color, text, parm) { + + html += ''; + + var ret = ''; + for (var i = 0; i < count; i++) { + var v = bytes.shift(); + if (typeof v === 'undefined') { + v = 'XX'; + } + html += v + ' '; + ret += v; + if (((i + 1) % 16) == 0) { + html += '
    '; + } + } + + switch (text) { + case AddTextType.emmStartMarker: + if(ret.substring(2) == '00') { + text += " (cccam)"; + } + break; + + case AddTextType.mpegSectionLength: + var len = ((parseInt(parm, 16) << 8) + parseInt(ret, 16)) & 0x0FFF; + text += ' - ' + len + ''; + + if (bytes.length >= len) { + text += ' - OK (' + bytes.length + ')'; + } else { + text += ' - FAIL (' + bytes.length + ')'; + } + ret = len; + break; + + case AddTextType.length: + case AddTextType.cardEmmLength: + case AddTextType.cardNanoLength: + case AddTextType.irdEmmLength: + case AddTextType.filterSectionLength: + case AddTextType.irdNanoLength: + + var len = parseInt(ret, 16); + text += ' - ' + len + ''; + + if (bytes.length >= len) { + text += ' - OK (' + bytes.length + ')'; + } else { + text += ' - FAIL (' + bytes.length + ')'; + } + ret = len; + break; + case AddTextType.type: + + var type = parseInt(ret, 16) & 0xC0; + var subEmmCount = ((parseInt(ret, 16) & 0x30) >> 16) + 1; + + if (type == 0x40) { + text += ' - unique EMM For Smartcard'; + } + else if (type == 0xC0) { + text += ' - unique EMM For Receiver/CAM'; + } + else if (type == 0x80) { + text += ' - shared EMM For Smartcard'; + } + else { + text += ' - global EMM'; + } + + text += ' (' + subEmmCount + ' Sub EMMs)'; + + break; + + case AddTextType.emmType: + switch (ret) { + case '02': + text += ' - Default'; + break; + case '07': + text += ' - With Filter Nanos'; + break; + default: + text += ' - unknown'; + break; + } + break; + + case AddTextType.encryptionType: + switch (ret) { + case '44': + text += ' - User-Encryption'; + break; + case '60': + text += ' - System-Encryption'; + break; + case '40': + text += ' - System-Encryption'; + break; + default: + text += ' - unknown'; + break; + } + + break; + + case AddTextType.cardNanoType: + switch (ret) { + case '01': + text += ' - Set date'; + break; + case '90': + text += ' - Encrypted Nano'; + break; + case '41': + text += ' - Add a ChID'; + break; + case '42': + text += ' - Delete a ChID'; + break; + default: + text += ' - unknown'; + break; + } + break; + + case AddTextType.irdNanoType: + switch (ret) { + case '02': + text += ' - Set Date/Time'; + break; + case '07': + case '08': + text += ' - Download Firmware'; + break; + case '2B': + text += ' - Pairing Data'; + break; + default: + text += ' - unknown'; + break; + } + break; + + case AddTextType.filterNanoType: + switch (ret) { + case '30': + text += ' - Always Valid'; + break; + case '31': + text += ' - Card Address'; + break; + default: + text += ' - unknown'; + break; + } + break; + + case AddTextType.keyIndex: + + if (!isV14V15) { + text += ' - ' + ret + ''; + } + + switch (ret) { + case '01': + text += ' - V14'; + break; + case '02': + text += ' - V15'; + break; + default: + text += ' - unknown'; + break; + } + + break; + case AddTextType.keyIndex2: + text += ' - unknown'; + break; + case AddTextType.fixedValue: + if (ret == parm) { + text += ' - OK'; + } else { + text += ' - FAIL (' + parm + ')'; + } + break; + case AddTextType.pairingDevice: + switch (ret) { + default: text += ' - Device ' + ret + ''; + break; + } + break; + case AddTextType.date: + var b = ret.replace(/[^A-Fa-f0-9]/g, "").match(/.{1,2}/g) || []; + + var bin = parseInt(b[2] + b[3], 16); + var time = {}; + time.s = ((bin & parseInt('11111', 2)) * 2); + bin = bin >>> 5; + time.m = (bin & parseInt('111111', 2)); + bin = bin >>> 6; + time.h = (bin & parseInt('11111', 2)); + bin = bin >>> 5; + + bin = parseInt(b[0] + b[1], 16); + var date = {}; + date.d = (bin & parseInt('11111111', 2)); + bin = bin >>> 8; + date.m = (bin & parseInt('11111111', 2)); + bin = bin >>> 8; + + date.y = parseInt(date.m / 12) + 2004; + date.m -= (((date.y - 2004) * 12) - 1); + + text += ' - ' + date.d + '.' + date.m + '.' + date.y + ''; + text += ' ' + time.h + ':' + time.m + ':' + time.s + ' UTC'; + break; + case AddTextType.checksum: + case AddTextType.irdEmmChecksum: + var checksumData = parseInt(ret, 16); + var checksum = 0x00; + + for(var i = 0; i < parm.length; i++) { + checksum += parseInt(parm[i], 16); + checksum &= 0xFF; + } + + if (checksumData == checksum) { + text += ' - OK'; + } else { + text += ' - FAIL (' + checksum.toString(16).toUpperCase() + ')'; + } + break; + } + + html += '
    - ' + text + '
    '; + return ret; + } + + function ReadSingleCardEMM() { + var cardEmmLength = addText(1, '#00F', AddTextType.cardEmmLength); + var remainingDataLength = cardEmmLength; + + while (remainingDataLength > 0 && bytes.length) { + var cardNanoType = addText(1, '#008000', AddTextType.cardNanoType); + + var fixedSizeNanos = { + "01": 0x04, + "09": 0x03, + "10": 0x02, + "19": 0x01, + "1E": 0x08, + "24": 0x00, + "25": 0x00, + "27": 0x0D, + "2B": 0x02,//0x03 + "2D": 0x04, + "30": 0x00, + "31": 0x04,//0x03 + "32": 0x03, + "33": 0x23, + "3D": 0x02, + "3E": 0x00, + "41": 0x05,//0x04 + "42": 0x02, + "44": 0x04, + "4E": 0x04, + "7A": 0x02, + + "02": 0x01, + "03": 0x03, + "04": 0x00, + "48": 0x00, + "C0": 0x00, + + }; + + if (fixedSizeNanos[cardNanoType] != undefined) { + + if (cardNanoType == "01") { + addText(4, '#00A0A0', AddTextType.date); + } + else { + addText(fixedSizeNanos[cardNanoType], '#000', AddTextType.data); + } + remainingDataLength -= fixedSizeNanos[cardNanoType] + 1; + } + else { + var cardNanoLength = addText(1, '#00F', AddTextType.cardNanoLength); + + if (cardNanoType == "90") { + var encryptionType = addText(1, '#b22222', AddTextType.encryptionType); + switch (encryptionType) { + case '40': + case '44': + case '60': + addText(1, '#b22222', AddTextType.keyIndex); + addText((cardNanoLength - 2), '#000', AddTextType.data); + + break; + default: + addText(1, '#b22222', AddTextType.keyIndex2); + addText((cardNanoLength - 2), '#000', AddTextType.data); + break; + } + } + else { + addText(cardNanoLength, '#000', AddTextType.data); + } + + remainingDataLength -= cardNanoLength + 2; + } + + } + + } + + function ReadIrdNano() { + + var irdNanoType = addText(1, '#008000', AddTextType.irdNanoType); + + var fixedSizeNanos = { + "02": 0x04, + }; + + if (fixedSizeNanos[irdNanoType] != undefined) { + + if (irdNanoType == "02") { + addText(4, '#00A0A0', AddTextType.date); + } + else { + addText(fixedSizeNanos[cardNanoType], '#000', AddTextType.data); + } + return fixedSizeNanos[irdNanoType] + 1; + } + else { + var irdNanoLength = addText(1, '#00F', AddTextType.irdNanoLength); + + switch (irdNanoType) { + case '2B': + addText(1, '#000', AddTextType.pairingdeviceCount); + + var remainingDataLengthNano = irdNanoLength - 1; + while (remainingDataLengthNano > 0 && bytes.length) { + var startLength = bytes.length; + addText(1, '#E000E0', AddTextType.pairingDevice); + addText(4, '#cc7000', AddTextType.boxSerial); + ReadSingleCardEMM(); + remainingDataLengthNano -= startLength - bytes.length; + } + break; + + default: + addText(irdNanoLength, '#000', AddTextType.data); + break; + } + + return irdNanoLength + 2; + } +} + + var partOfLength = bytes[1]; + addText(2, '#000', AddTextType.emmStartMarker); + addText(1, '#00F', AddTextType.mpegSectionLength, partOfLength); + + var filterByte = parseInt(addText(1, '#199a8d', AddTextType.type), 16); + var type = filterByte & 0xC0; + var subEmmCount = ((filterByte & 0x30) >> 16) + 1; + + if(partOfLength != 0) { // partOfLength == 0 for emms by cccam clients, these do not have the serials part + for(var i = 0; i < subEmmCount; i++) { + if (type == 0x40) { // unique: card + addText(4, '#cc7000', AddTextType.cardSerial); + } else if (type == 0xC0) { // unique: receiver/cam + addText(4, '#cc7000', AddTextType.boxSerial); + } else if (type == 0x80) { // shared: card group + addText(3, '#cc7000', AddTextType.cardGroupSerial); + addText(1, '#000', AddTextType.fixedValue, '01'); + } + } + } + + while (bytes.length) { + var emmtype = addText(1, '#008000', AddTextType.emmType); + + var irdEmmLength = addText(1, '#00F', AddTextType.irdEmmLength); + var checksumData = {}; + + if (irdEmmLength > 0) { + checksumData = bytes.slice(0, irdEmmLength - 1); + checksumData.unshift(irdEmmLength.toString(16)); + checksumData.unshift(emmtype); + } + + if (irdEmmLength > 0) { + switch (emmtype) { + case '02': + + var remainingDataLength = irdEmmLength - 1; + while(remainingDataLength > 0 && bytes.length) { + remainingDataLength -= ReadIrdNano(); + } + break; + + case '07': + var filterSectionLength = addText(1, '#00F', AddTextType.filterSectionLength); + var remainingDataLength = filterSectionLength; + + while (remainingDataLength > 0 && bytes.length) { + var filterNano = addText(1, '#008000', AddTextType.filterNanoType); + switch (filterNano) { + case '30': + remainingDataLength -= 1; + break; + + case '31': + addText(4, '#cc7000', AddTextType.cardSerial); + remainingDataLength -= 5; + break; + + default: + addText(remainingDataLength - 1, '#000', AddTextType.data); + remainingDataLength = 0; + break; + } + } + + var remainingDataLength = irdEmmLength - 1 - filterSectionLength - 1; + while(remainingDataLength > 0 && bytes.length) { + remainingDataLength -= ReadIrdNano(); + } + break; + + default: + addText(irdEmmLength - 1, '#000', AddTextType.data); + break; + } + + addText(1, '#800080', AddTextType.irdEmmChecksum, checksumData); + } + + ReadSingleCardEMM(); + } + + $(target).html(html); +} + +/** + * Really Simple Color Picker in jQuery + * + * Licensed under the MIT (MIT-LICENSE.txt) licenses. + * + * Copyright (c) 2008-2012 + * Lakshan Perera (www.laktek.com) & Daniel Lacy (daniellacy.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +(function (e) { + var t, n, r = 0, + i = { + control: e('
     
    '), + palette: e('
    '), + swatch: e('
     
    '), + hexLabel: e(''), + hexField: e('') + }, + s = "transparent", + o; + e.fn.colorPicker = function (t) { + return this.each(function () { + var n = e(this), + o = e.extend({}, e.fn.colorPicker.defaults, t), + u = e.fn.colorPicker.toHex(n.val().length > 0 ? n.val() : o.pickerDefault), + a = i.control.clone(), + f = i.palette.clone().attr("id", "colorPicker_palette-" + r), + l = i.hexLabel.clone(), + c = i.hexField.clone(), + h = f[0].id, + p, d; + e.each(o.colors, function (t) { + p = i.swatch.clone(); + if (o.colors[t] === s) { + p.addClass(s).text("X"); + e.fn.colorPicker.bindPalette(c, p, s) + } else { + p.css("background-color", "#" + this); + e.fn.colorPicker.bindPalette(c, p) + } + p.appendTo(f) + }); + l.attr("for", "colorPicker_hex-" + r); + c.attr({ + id: "colorPicker_hex-" + r, + value: u + }); + c.bind("keydown", function (t) { + if (t.keyCode === 13) { + var r = e.fn.colorPicker.toHex(e(this).val()); + e.fn.colorPicker.changeColor(r ? r : n.val()) + } + if (t.keyCode === 27) { + e.fn.colorPicker.hidePalette() + } + }); + c.bind("keyup", function (t) { + var r = e.fn.colorPicker.toHex(e(t.target).val()); + e.fn.colorPicker.previewColor(r ? r : n.val()) + }); + c.bind("blur", function (t) { + var r = e.fn.colorPicker.toHex(e(this).val()); + e.fn.colorPicker.changeColor(r ? r : n.val()) + }); + e('
    ').append(l).appendTo(f); + f.find(".colorPicker_hexWrap").append(c); + if (o.showHexField === false) { + c.hide(); + l.hide() + } + e("body").append(f); + f.hide(); + a.css("background-color", u); + a.bind("click", function () { + if (n.is(":not(:disabled)")) { + e.fn.colorPicker.togglePalette(e("#" + h), e(this)) + } + }); + if (t && t.onColorChange) { + a.data("onColorChange", t.onColorChange) + } else { + a.data("onColorChange", function () {}) + } if (d = n.data("text")) a.html(d); + n.after(a); + n.bind("change", function () { + n.next(".colorPicker-picker").css("background-color", e.fn.colorPicker.toHex(e(this).val())) + }); + n.val(u); + if (n[0].tagName.toLowerCase() === "input") { + try { + n.attr("type", "hidden") + } catch (v) { + n.css("visibility", "hidden").css("position", "absolute") + } + } else { + n.hide() + } + r++ + }) + }; + e.extend(true, e.fn.colorPicker, { + toHex: function (e) { + if (e.match(/[0-9A-F]{6}|[0-9A-F]{3}$/i)) { + return e.charAt(0) === "#" ? e : "#" + e + } else if (e.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/)) { + var t = [parseInt(RegExp.$1, 10), parseInt(RegExp.$2, 10), parseInt(RegExp.$3, 10)], + n = function (e) { + if (e.length < 2) { + for (var t = 0, n = 2 - e.length; t < n; t++) { + e = "0" + e + } + } + return e + }; + if (t.length === 3) { + var r = n(t[0].toString(16)), + i = n(t[1].toString(16)), + s = n(t[2].toString(16)); + return "#" + r + i + s + } + } else { + return false + } + }, + checkMouse: function (r, i) { + var s = n, + o = e(r.target).parents("#" + s.attr("id")).length; + if (r.target === e(s)[0] || r.target === t[0] || o > 0) { + return + } + e.fn.colorPicker.hidePalette() + }, + hidePalette: function () { + e(document).unbind("mousedown", e.fn.colorPicker.checkMouse); + e(".colorPicker-palette").hide() + }, + showPalette: function (n) { + var r = t.prev("input").val(); + n.css({ + top: t.offset().top + t.outerHeight(), + left: t.offset().left + }); + e("#color_value").val(r); + n.show(); + e(document).bind("mousedown", e.fn.colorPicker.checkMouse) + }, + togglePalette: function (r, i) { + if (i) { + t = i + } + n = r; + if (n.is(":visible")) { + e.fn.colorPicker.hidePalette() + } else { + e.fn.colorPicker.showPalette(r) + } + }, + changeColor: function (n) { + t.css("background-color", n); + t.prev("input").val(n).change(); + e.fn.colorPicker.hidePalette(); + t.data("onColorChange").call(t, e(t).prev("input").attr("id"), n) + }, + previewColor: function (e) { + t.css("background-color", e) + }, + bindPalette: function (n, r, i) { + i = i ? i : e.fn.colorPicker.toHex(r.css("background-color")); + r.bind({ + click: function (t) { + o = i; + e.fn.colorPicker.changeColor(i) + }, + mouseover: function (t) { + o = n.val(); + e(this).css("border-color", "#598FEF"); + n.val(i); + e.fn.colorPicker.previewColor(i) + }, + mouseout: function (r) { + e(this).css("border-color", "#000"); + n.val(t.css("background-color")); + n.val(o); + e.fn.colorPicker.previewColor(o) + } + }) + } + }); + e.fn.colorPicker.defaults = { + pickerDefault: "FFFFFF", + colors: ["000000", "993300", "333300", "000080", "333399", "333333", "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080", "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "999999", "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0", "FF99CC", "FFCC99", "FFFF99", "CCFFFF", "99CCFF", "FFFFFF"], + addColors: [], + showHexField: true + } +})(jQuery); +/** + * Stupid jQuery Table Sort + * Copyright (c) 2012 Joseph McCullough + * https://github.com/joequery/Stupid-Table-Plugin#readme + */ +(function (e) { + e.fn.stupidtable = function (t) { + return this.each(function () { + var n = e(this); + t = t || {}; + t = e.extend({}, e.fn.stupidtable.default_sort_fns, t); + n.on("click.stupidtable", "th", function () { + var r = e(this); + var i = 0; + var s = e.fn.stupidtable.dir; + n.find("#headline > th").slice(0, r.index()).each(function () { + var t = e(this).attr("colspan") || 1; + i += parseInt(t, 10) + }); + var o = r.data("sort-default") || s.ASC; + if (r.data("sort-dir")) o = r.data("sort-dir") === s.ASC ? s.DESC : s.ASC; + var u = r.data("sort") || null; + if (u === null) { + return + } + n.trigger("beforetablesort", { + column: i, + direction: o + }); + n.css("display"); + setTimeout(function () { + var a = []; + var f = t[u]; + var l = n.children("tbody").children("tr"); + l.each(function (t, n) { + var r = e(n).children().eq(i); + var s = r.data("sort-value"); + var o = typeof s !== "undefined" ? s : r.text(); + a.push([o, n]) + }); + a.sort(function (e, t, s) { + return f(e[0], t[0], o) + }); + if (o != s.ASC) a.reverse(); + l = e.map(a, function (e) { + return e[1] + }); + n.children("tbody").append(l); + n.find("th.sorting-desc, th.sorting-asc").data("sort-dir", null).removeClass("sorting-desc sorting-asc").addClass("sortable"); + r.data("sort-dir", o).removeClass("sortable").addClass("sorting-" + o); + $('tr').find('td.td-sorting').removeClass('td-sorting'); + $('tr').find('td:eq(' + i + ')').addClass('td-sorting'); + n.trigger("aftertablesort", { + column: i, + direction: o + }); + n.css("display") + }, 10) + }) + }) + }; + e.fn.stupidtable.dir = { + ASC: "asc", + DESC: "desc" + }; + var convert_locale = function (c) { + if (c == "") return 0; + if(locale_decpoint == ",") { + c = c.toString().replace( /\./g,"" ).replace( /,/,"." ); + }else if(locale_decpoint == "."){ + c = c.toString().replace( /,/g,"" ); + } + return(c); + } + var ip2int = function dot2num(dot, s) { + if (dot == "" && s == "asc") return 4300000000; + if (dot == "" && s == "desc") return 1; + var d = dot.split('.'); + return ((((((+d[0])*256)+(+d[1]))*256)+(+d[2]))*256)+(+d[3]); + } + e.fn.stupidtable.default_sort_fns = { + "int": function (e, t, s) { + return parseInt(convert_locale(e), 10) - parseInt(convert_locale(t), 10) + }, + "float": function (e, t, s) { + return parseFloat(convert_locale(e)) - parseFloat(convert_locale(t)) + }, + "ip": function (a, b, s) { + aIP = ip2int(a, s); + bIP = ip2int(b, s); + return aIP - bIP; + }, + "string": function (e, t, s) { + if (e == "" && s == "asc") return +1; + if (t == "" && s == "asc") return -1; + if (e < t) return -1; + if (e > t) return +1; + return 0 + }, + "string-ins": function (e, t, s) { + e = e.toString().toLowerCase(); + t = t.toString().toLowerCase(); + if (e == "" && s == "asc") return +1; + if (t == "" && s == "asc") return -1; + if (e < t) return -1; + if (e > t) return +1; + return 0 + } + } +})(jQuery) + +// Create Base64 Object +var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(f>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t="";for(var n=0;n127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}} + +/* + * OSCam WebIf Wiki Help System + * If wikiInternal is true (set by server when WEBIF_WIKI is compiled in), + * show local wiki popup. Otherwise, open external wiki directly (old behavior). + */ +var wikiPopupHtml = '
    ' + + '
    ' + + '' + + '' + + '×' + + '
    ' + + '
    ' + + '
    '; + +var wikiDragState = { isDragging: false, startX: 0, startY: 0, startLeft: 0, startTop: 0 }; +var wikiSavedLeft = localStorage.getItem('wikiPopupLeft'); +var wikiSavedWidth = localStorage.getItem('wikiPopupWidth'); +var wikiSavedHeight = localStorage.getItem('wikiPopupHeight'); +var wikiDefaultWidth = 400; +var wikiLastLinkOffset = 0; +var wikiUserResized = !!(wikiSavedWidth || wikiSavedHeight); +var wikiCurrentLink = null; +var wikiConnectorLine = null; + +/* Draw connector line between link and popup */ +function updateWikiConnector() { + if (!wikiCurrentLink || !$('#wikiPopup').is(':visible')) { + if (wikiConnectorLine) { + wikiConnectorLine.remove(); + wikiConnectorLine = null; + } + return; + } + + var $link = wikiCurrentLink; + var $popup = $('#wikiPopup'); + + /* Calculate positions */ + var linkOffset = $link.offset(); + var linkRight = linkOffset.left + $link.outerWidth(); + var linkCenterY = linkOffset.top + $link.outerHeight() / 2; + + var popupOffset = $popup.offset(); + var popupLeft = popupOffset.left; + var popupCenterY = popupOffset.top + 20; /* Connect to header area */ + + /* Create or update SVG line */ + if (!wikiConnectorLine) { + wikiConnectorLine = $(''); + $('body').append(wikiConnectorLine); + } + + /* Position SVG to cover the area between link and popup */ + var minX = Math.min(linkRight, popupLeft) - 5; + var maxX = Math.max(linkRight, popupLeft) + 5; + var minY = Math.min(linkCenterY, popupCenterY) - 5; + var maxY = Math.max(linkCenterY, popupCenterY) + 5; + + wikiConnectorLine.css({ + position: 'absolute', + left: minX, + top: minY, + width: maxX - minX, + height: maxY - minY, + pointerEvents: 'none', + zIndex: 1 + }); + + wikiConnectorLine.find('line').attr({ + x1: linkRight - minX, + y1: linkCenterY - minY, + x2: popupLeft - minX, + y2: popupCenterY - minY, + stroke: '#ffcc00', + 'stroke-width': 2 + }); +} + +function removeWikiConnector() { + if (wikiConnectorLine) { + wikiConnectorLine.remove(); + wikiConnectorLine = null; + } +} + +$(function() { + if (typeof oscamconf === 'undefined') return; + + /* Check if internal wiki is enabled (WEBIF_WIKI compiled in) */ + if (typeof wikiInternal === 'undefined' || wikiInternal !== true) { + /* No internal wiki - external wiki links are handled in document.ready above */ + return; + } + + /* Internal wiki enabled - setup popup and override click handler */ + $('body').append(wikiPopupHtml); + + $('.wiki-popup-close').click(function() { + $('#wikiPopup').fadeOut(200); + removeWikiConnector(); + if (wikiCurrentLink) { + wikiCurrentLink.removeClass('wiki-active'); + wikiCurrentLink = null; + } + }); + + $('.wiki-popup-reset').click(function() { + localStorage.removeItem('wikiPopupLeft'); + localStorage.removeItem('wikiPopupWidth'); + localStorage.removeItem('wikiPopupHeight'); + wikiSavedLeft = null; + wikiSavedWidth = null; + wikiSavedHeight = null; + wikiUserResized = false; + var $popup = $('#wikiPopup'); + /* Recalculate position based on current link */ + if (wikiCurrentLink) { + var offset = wikiCurrentLink.offset(); + wikiLastLinkOffset = offset.left + wikiCurrentLink.outerWidth(); + var defaultLeft = wikiLastLinkOffset + 10; + if (defaultLeft + 420 > $(window).width()) { + defaultLeft = $(window).width() - 420; + } + $popup.css({ width: wikiDefaultWidth, height: 'auto', left: defaultLeft, top: offset.top - 10 }); + } else { + var defaultLeft = wikiLastLinkOffset + 10; + if (defaultLeft + 420 > $(window).width()) { + defaultLeft = $(window).width() - 420; + } + $popup.css({ width: wikiDefaultWidth, height: 'auto', left: defaultLeft }); + } + setTimeout(updateWikiConnector, 50); + }); + + $(document).click(function(e) { + if (!$(e.target).closest('#wikiPopup').length && + !$(e.target).closest('form table a').length) { + $('#wikiPopup').fadeOut(200); + removeWikiConnector(); + if (wikiCurrentLink) { + wikiCurrentLink.removeClass('wiki-active'); + wikiCurrentLink = null; + } + } + }); + + $(document).keydown(function(e) { + if (e.keyCode === 27) { + $('#wikiPopup').fadeOut(200); + removeWikiConnector(); + if (wikiCurrentLink) { + wikiCurrentLink.removeClass('wiki-active'); + wikiCurrentLink = null; + } + } + /* Arrow keys to navigate between parameters when popup is open */ + if ((e.keyCode === 38 || e.keyCode === 40) && $('#wikiPopup').is(':visible') && wikiCurrentLink) { + var $allLinks = $('form table a').filter(function() { + return !$(this).attr('href') && $(this).text().trim(); + }); + var currentIndex = $allLinks.index(wikiCurrentLink); + var newIndex; + if (e.keyCode === 38) { + /* Arrow up - previous parameter */ + if (currentIndex > 0) { + e.preventDefault(); + newIndex = currentIndex - 1; + var $newLink = $allLinks.eq(newIndex); + $newLink.click(); + /* Scroll into view if needed */ + var linkTop = $newLink.offset().top; + var scrollTop = $(window).scrollTop(); + if (linkTop < scrollTop + 50) { + $('html, body').animate({ scrollTop: linkTop - 100 }, 150); + } + } + } else { + /* Arrow down - next parameter */ + if (currentIndex < $allLinks.length - 1) { + e.preventDefault(); + newIndex = currentIndex + 1; + var $newLink = $allLinks.eq(newIndex); + $newLink.click(); + /* Scroll into view if needed */ + var linkBottom = $newLink.offset().top + $newLink.outerHeight(); + var windowBottom = $(window).scrollTop() + $(window).height(); + if (linkBottom > windowBottom - 50) { + $('html, body').animate({ scrollTop: $newLink.offset().top - $(window).height() + 150 }, 150); + } + } + } + } + }); + + /* Drag functionality for popup */ + $('.wiki-popup-header').css('cursor', 'move').on('mousedown', function(e) { + if ($(e.target).hasClass('wiki-popup-close') || $(e.target).hasClass('wiki-popup-reset') || $(e.target).hasClass('wiki-popup-title')) return; + wikiDragState.isDragging = true; + wikiDragState.startX = e.pageX; + wikiDragState.startY = e.pageY; + wikiDragState.startLeft = parseInt($('#wikiPopup').css('left')) || 0; + wikiDragState.startTop = parseInt($('#wikiPopup').css('top')) || 0; + e.preventDefault(); + }); + + /* Touch support for drag */ + $('.wiki-popup-header').on('touchstart', function(e) { + if ($(e.target).hasClass('wiki-popup-close') || $(e.target).hasClass('wiki-popup-reset') || $(e.target).hasClass('wiki-popup-title')) return; + var touch = e.originalEvent.touches[0]; + wikiDragState.isDragging = true; + wikiDragState.startX = touch.pageX; + wikiDragState.startY = touch.pageY; + wikiDragState.startLeft = parseInt($('#wikiPopup').css('left')) || 0; + wikiDragState.startTop = parseInt($('#wikiPopup').css('top')) || 0; + }); + + $(document).on('touchmove', function(e) { + if (!wikiDragState.isDragging) return; + var touch = e.originalEvent.touches[0]; + var newLeft = wikiDragState.startLeft + (touch.pageX - wikiDragState.startX); + var newTop = wikiDragState.startTop + (touch.pageY - wikiDragState.startY); + newLeft = Math.max(0, Math.min(newLeft, $(window).width() - 420)); + newTop = Math.max(0, newTop); + $('#wikiPopup').css({ left: newLeft, top: newTop }); + updateWikiConnector(); + e.preventDefault(); + }); + + $(document).on('touchend', function() { + if (wikiDragState.isDragging) { + wikiDragState.isDragging = false; + wikiSavedLeft = $('#wikiPopup').css('left'); + localStorage.setItem('wikiPopupLeft', wikiSavedLeft); + } + }); + + $(document).on('mousemove', function(e) { + if (!wikiDragState.isDragging) return; + var newLeft = wikiDragState.startLeft + (e.pageX - wikiDragState.startX); + var newTop = wikiDragState.startTop + (e.pageY - wikiDragState.startY); + newLeft = Math.max(0, Math.min(newLeft, $(window).width() - 420)); + newTop = Math.max(0, newTop); + $('#wikiPopup').css({ left: newLeft, top: newTop }); + updateWikiConnector(); + }); + + $(document).on('mouseup', function() { + if (wikiDragState.isDragging) { + wikiDragState.isDragging = false; + wikiSavedLeft = $('#wikiPopup').css('left'); + localStorage.setItem('wikiPopupLeft', wikiSavedLeft); + } + }); + + /* Save size after user resize */ + var wikiLastWidth = 0; + var wikiLastHeight = 0; + $('#wikiPopup').on('mouseup', function() { + var w = $(this).width(); + var h = $(this).height(); + if (wikiLastWidth && wikiLastHeight && (w !== wikiLastWidth || h !== wikiLastHeight)) { + wikiUserResized = true; + wikiSavedWidth = w; + wikiSavedHeight = h; + localStorage.setItem('wikiPopupWidth', wikiSavedWidth); + localStorage.setItem('wikiPopupHeight', wikiSavedHeight); + } + wikiLastWidth = w; + wikiLastHeight = h; + }); + + $('form table a').off('click').click(function(e) { + e.preventDefault(); + e.stopPropagation(); + + /* Remove highlight from previous link */ + if (wikiCurrentLink) { + wikiCurrentLink.removeClass('wiki-active'); + } + + /* Store current link for keyboard navigation and highlight it */ + wikiCurrentLink = $(this); + wikiCurrentLink.addClass('wiki-active'); + + /* Get real input name for internal wiki lookup */ + var $cell = $(this).parent(); + var inputName = null; + + /* Try 1: Input in next cell (standard layout) */ + var $input = $cell.next().find('input,select,textarea').first(); + + /* Try 2: Input in same cell */ + if (!$input.length) { + $input = $cell.find('input,select,textarea').first(); + } + + /* Try 3: For TH headers (like services), look in same column of next row */ + if (!$input.length && $cell.is('th')) { + var cellIndex = $cell.index(); + var $nextRow = $cell.closest('tr').next('tr'); + if ($nextRow.length) { + var $correspondingCell = $nextRow.find('td, th').eq(cellIndex); + $input = $correspondingCell.find('input,select,textarea').first(); + } + } + + if ($input.length) { + inputName = $input.attr('name'); + } + + /* Get param for external wiki link (use data-p override if present) */ + var externalParam = $(this).data('p') || inputName; + + if (!inputName) return; + + /* Extract section from form's hidden part input */ + var section = $('input[name="part"]').val() || ''; + + var $link = $(this); + var $popup = $('#wikiPopup'); + + $('.wiki-popup-title').text(inputName).attr('href', + 'https://git.streamboard.tv/common/oscam/-/wikis/pages/configuration/oscam.' + oscamconf + '#' + externalParam); + $('.wiki-popup-content').html('
    Loading...
    '); + + var offset = $link.offset(); + wikiLastLinkOffset = offset.left + $link.outerWidth(); + var leftPos; + if (wikiSavedLeft) { + leftPos = parseInt(wikiSavedLeft); + } else { + leftPos = offset.left + $link.outerWidth() + 10; + if (leftPos + 420 > $(window).width()) { + leftPos = $(window).width() - 420; + } + } + $popup.css({ + top: offset.top - 10, + left: leftPos, + width: wikiUserResized && wikiSavedWidth ? parseInt(wikiSavedWidth) : wikiDefaultWidth, + height: wikiUserResized && wikiSavedHeight ? parseInt(wikiSavedHeight) : 'auto', + 'max-width': $(window).width() - 20, + 'max-height': $(window).height() - 20 + }).fadeIn(200); + + /* Draw connector line */ + setTimeout(updateWikiConnector, 50); + + /* Update last size for resize detection */ + setTimeout(function() { + wikiLastWidth = $popup.width(); + wikiLastHeight = $popup.height(); + }, 250); + + $.ajax({ + url: 'wiki.json', + type: 'GET', + data: { config: oscamconf, section: section, param: inputName }, + dataType: 'json', + success: function(data) { + if (data && data.text) { + var html = formatWikiText(data.text); + $('.wiki-popup-content').html(html); + } else { + $('.wiki-popup-content').html( + '
    No help available for this parameter.

    ' + + 'View Wiki →
    ' + ); + } + }, + error: function() { + $('.wiki-popup-content').html( + '
    Failed to load help.

    ' + + 'View Wiki →
    ' + ); + } + }); + }); +}); + +function formatWikiText(text) { + if (!text) return ''; + + /* Convert escaped newlines to real newlines first */ + text = text.replace(/\\n/g, '\n'); + + text = text.replace(/&/g, '&').replace(//g, '>'); + text = text.replace(/```(\w*)\n([\s\S]*?)```/g, '
    $2
    '); + text = text.replace(/```([\s\S]*?)```/g, '
    $1
    '); + text = text.replace(/`([^`]+)`/g, '$1'); + text = text.replace(/\*\*([^*]+)\*\*/g, '$1'); + text = text.replace(/^### (.+)$/gm, '

    $1

    '); + text = text.replace(/^## (.+)$/gm, '

    $1

    '); + text = text.replace(/\n/g, '
    '); + text = text.replace(/<\/pre>
    /g, ''); + + return '
    ' + text + '
    '; +} + +/* Wiki documentation status toggle - shows icons for missing/review/ok documentation */ +$(document).ready(function() { + if (typeof oscamconf === 'undefined' || typeof wikiInternal === 'undefined' || wikiInternal !== true) { + return; + } + + var wikiStatusLoaded = false; + var wikiStatusVisible = false; + + /* Support different table classes: config, configreader, configuser, configservices, writeemm */ + var $allTables = $('table.config, table.configreader, table.configuser, table.configservices, table.writeemm'); + if (!$allTables.length) return; + + /* Only make the FIRST table header clickable */ + var $firstHeader = $allTables.find('th[colspan]').first(); + if (!$firstHeader.length) return; + + $firstHeader.css('cursor', 'pointer').attr('title', 'Click to toggle documentation status icons'); + + $firstHeader.click(function() { + if (!wikiStatusLoaded) { + /* Load status for all parameters */ + $.ajax({ + url: 'wiki_status.json', + type: 'GET', + data: { config: oscamconf }, + dataType: 'json', + success: function(data) { + if (data) { + /* Method 1: Find all tags that are parameter labels */ + $allTables.find('a').each(function() { + var $a = $(this); + + /* Skip if already has icon */ + if ($a.find('.wiki-status-icon').length) return; + + /* Skip links with href (real links, not labels) */ + if ($a.attr('href')) return; + + /* Skip if no text */ + var labelText = $a.text().trim(); + if (!labelText) return; + + /* Find associated input name */ + var paramName = null; + var $cell = $a.closest('td, th'); + + /* Try 1: Input in same cell */ + var $input = $cell.find('input,select,textarea').first(); + + /* Try 2: Input in next cell */ + if (!$input.length) { + $input = $cell.next('td, th').find('input,select,textarea').first(); + } + + /* Try 3: For TH headers, look in same column of next row */ + if (!$input.length && $cell.is('th')) { + var cellIndex = $cell.index(); + var $nextRow = $cell.closest('tr').next('tr'); + if ($nextRow.length) { + var $correspondingCell = $nextRow.find('td, th').eq(cellIndex); + $input = $correspondingCell.find('input,select,textarea').first(); + } + } + + if ($input.length) { + paramName = $input.attr('name'); + } + + if (!paramName) return; + + var status = data[paramName]; + var icon = ''; + var tooltip = ''; + if (status === 'missing') { icon = '❗ '; tooltip = 'Documentation missing'; } + else if (status === 'review') { icon = '⚠️ '; tooltip = 'Documentation needs review'; } + else if (status === 'ok') { icon = '✅ '; tooltip = 'Documentation complete'; } + + if (icon) { + $a.prepend('' + icon + ''); + } + }); + + wikiStatusLoaded = true; + wikiStatusVisible = true; + $('.wiki-status-icon').addClass('visible'); + } + } + }); + } else { + /* Toggle visibility for ALL icons */ + wikiStatusVisible = !wikiStatusVisible; + if (wikiStatusVisible) { + $('.wiki-status-icon').addClass('visible'); + } else { + $('.wiki-status-icon').removeClass('visible'); + } + } + }); +}); diff --git a/webif/include/logobit.html b/webif/include/logobit.html new file mode 100644 index 0000000..a708858 --- /dev/null +++ b/webif/include/logobit.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webif/include/logobit_img.html b/webif/include/logobit_img.html new file mode 100644 index 0000000..5b65c8e --- /dev/null +++ b/webif/include/logobit_img.html @@ -0,0 +1 @@ +OSCam Streamboard \ No newline at end of file diff --git a/webif/include/menu.html b/webif/include/menu.html new file mode 100644 index 0000000..27cb99a --- /dev/null +++ b/webif/include/menu.html @@ -0,0 +1,16 @@ +
    + + +
    \ No newline at end of file diff --git a/webif/include/menu_cacheexmenuitem.html b/webif/include/menu_cacheexmenuitem.html new file mode 100644 index 0000000..cf01439 --- /dev/null +++ b/webif/include/menu_cacheexmenuitem.html @@ -0,0 +1 @@ +
  • CacheEX
  • \ No newline at end of file diff --git a/webif/include/message.html b/webif/include/message.html new file mode 100644 index 0000000..d1a7ba7 --- /dev/null +++ b/webif/include/message.html @@ -0,0 +1 @@ +
    ##MESSAGES##
    \ No newline at end of file diff --git a/webif/include/message_bit.html b/webif/include/message_bit.html new file mode 100644 index 0000000..63cc404 --- /dev/null +++ b/webif/include/message_bit.html @@ -0,0 +1 @@ +##MESSAGE## \ No newline at end of file diff --git a/webif/include/noentitlements.html b/webif/include/noentitlements.html new file mode 100644 index 0000000..1f75f7d --- /dev/null +++ b/webif/include/noentitlements.html @@ -0,0 +1 @@ +
    (no entitlements)No active entitlements found \ No newline at end of file diff --git a/webif/include/poll.html b/webif/include/poll.html new file mode 100644 index 0000000..6a7ba66 --- /dev/null +++ b/webif/include/poll.html @@ -0,0 +1 @@ +##TPLJSONHEADER####DATA####TPLJSONFOOTER## \ No newline at end of file diff --git a/webif/include/pollingset.html b/webif/include/pollingset.html new file mode 100644 index 0000000..e16d2fa --- /dev/null +++ b/webif/include/pollingset.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webif/include/protocamd3aiopic.html b/webif/include/protocamd3aiopic.html new file mode 100644 index 0000000..2be254c --- /dev/null +++ b/webif/include/protocamd3aiopic.html @@ -0,0 +1 @@ +IC_##CAMD3A## \ No newline at end of file diff --git a/webif/include/protocccampic.html b/webif/include/protocccampic.html new file mode 100644 index 0000000..0f78e2e --- /dev/null +++ b/webif/include/protocccampic.html @@ -0,0 +1 @@ +IC_##CCA##_##CCB##-##CCC## \ No newline at end of file diff --git a/webif/include/protonewcamdpic.html b/webif/include/protonewcamdpic.html new file mode 100644 index 0000000..ab881d4 --- /dev/null +++ b/webif/include/protonewcamdpic.html @@ -0,0 +1 @@ +IC_##NCMDA##_##NCMDB## \ No newline at end of file diff --git a/webif/include/protootherpic.html b/webif/include/protootherpic.html new file mode 100644 index 0000000..a7caf79 --- /dev/null +++ b/webif/include/protootherpic.html @@ -0,0 +1 @@ +IC_##OTHER## \ No newline at end of file diff --git a/webif/include/refresh.html b/webif/include/refresh.html new file mode 100644 index 0000000..de70214 --- /dev/null +++ b/webif/include/refresh.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webif/logmenu/log_clearlog.html b/webif/logmenu/log_clearlog.html new file mode 100644 index 0000000..1be3509 --- /dev/null +++ b/webif/logmenu/log_clearlog.html @@ -0,0 +1 @@ +Clear Log diff --git a/webif/logmenu/log_clearuserlog.html b/webif/logmenu/log_clearuserlog.html new file mode 100644 index 0000000..39d7064 --- /dev/null +++ b/webif/logmenu/log_clearuserlog.html @@ -0,0 +1 @@ +Clear Log diff --git a/webif/logmenu/log_disablelogmenu.html b/webif/logmenu/log_disablelogmenu.html new file mode 100644 index 0000000..cf9746b --- /dev/null +++ b/webif/logmenu/log_disablelogmenu.html @@ -0,0 +1 @@ +##TEXT##  |   \ No newline at end of file diff --git a/webif/logmenu/log_filterform.html b/webif/logmenu/log_filterform.html new file mode 100644 index 0000000..0d3e973 --- /dev/null +++ b/webif/logmenu/log_filterform.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webif/logmenu/log_logmenuonoff.html b/webif/logmenu/log_logmenuonoff.html new file mode 100644 index 0000000..27e95eb --- /dev/null +++ b/webif/logmenu/log_logmenuonoff.html @@ -0,0 +1 @@ +##TEXT##  |   \ No newline at end of file diff --git a/webif/logpage/logpage.html b/webif/logpage/logpage.html new file mode 100644 index 0000000..66a7af5 --- /dev/null +++ b/webif/logpage/logpage.html @@ -0,0 +1,37 @@ +##TPLHEADERSHORT## + +##TPLBODY## +##TPLMENU## + +##TPLMESSAGE## +
    +
      +
      +
      +

      + +
      +##TPLFOOTER## diff --git a/webif/logpage/logpage_debugmenu.html b/webif/logpage/logpage_debugmenu.html new file mode 100644 index 0000000..271e935 --- /dev/null +++ b/webif/logpage/logpage_debugmenu.html @@ -0,0 +1,19 @@ +
      + Switch Debug from 0 to  +  0  +  1  +  2  +  4  +  8  +  16  +  32  +  64  +  128  +  256  +  512  +  1024  +  2048  +  4096  +  8192  +  ALL  +
      \ No newline at end of file diff --git a/webif/logpage/logpage_menu.html b/webif/logpage/logpage_menu.html new file mode 100644 index 0000000..40d922f --- /dev/null +++ b/webif/logpage/logpage_menu.html @@ -0,0 +1 @@ +
    • Live Log
    • \ No newline at end of file diff --git a/webif/logpage/logpage_sizemenu.html b/webif/logpage/logpage_sizemenu.html new file mode 100644 index 0000000..f57acda --- /dev/null +++ b/webif/logpage/logpage_sizemenu.html @@ -0,0 +1,8 @@ +
      + Switch displayed log lines from 512 to  +  32  +  64  +  128  +  256  +  512  +
      \ No newline at end of file diff --git a/webif/pages_gen.c b/webif/pages_gen.c new file mode 100644 index 0000000..e18c77b --- /dev/null +++ b/webif/pages_gen.c @@ -0,0 +1,557 @@ +/* + * OSCam WebIf pages generator + * Copyright (C) 2013 Unix Solutions Ltd. + * + * Authors: Georgi Chorbadzhiyski (gf@unixsol.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../config.h" +#ifdef WITH_COMPRESS_WEBIF +#include "../minilzo/minilzo.h" +#define USE_COMPRESSION 1 +#endif + +#define MAX_TEMPLATES 512 +static char *index_filename = "pages_index.txt"; +static char *defined_file = "is_defined.txt"; +static char *output_pages_c = "pages.c"; +static char *output_pages_h = "pages.h"; + +struct template +{ + char ident[64]; + char file[128]; + char deps[1024]; + uint32_t data_len; + enum { TXT, BIN } type; + uint8_t mime_type; +#ifdef USE_COMPRESSION + uint8_t *buf; + size_t buf_len; + uint32_t ident_ofs; + uint32_t data_ofs; + uint32_t deps_ofs; +#endif +}; + +struct templates +{ + unsigned int num; + struct template data[MAX_TEMPLATES]; +}; + +static struct templates templates; +static FILE *output_file; + +__attribute__ ((noreturn)) static void die(const char *s, ...) +{ + va_list args; + va_start(args, s); + fprintf(stderr, "ERROR: "); + vfprintf(stderr, s, args); + if(s[strlen(s) - 1] != '\n') + { fprintf(stderr, "\n"); } + va_end(args); + exit(EXIT_FAILURE); +} + +static FILE *xfopen(char *filename, char *mode) +{ + FILE *fh = fopen(filename, mode); + if(!fh) + { die("fopen(%s, %s): %s\n", filename, mode, strerror(errno)); } + return fh; +} + +static void readfile(const char *filename, uint8_t **data, size_t *data_len) +{ + struct stat sb; + if(stat(filename, &sb) != 0) + { die("stat(%s): %s\n", filename, strerror(errno)); } + int fd = open(filename, O_RDONLY); + if(fd < 0) + { die("open(%s): %s\n", filename, strerror(errno)); } + *data_len = sb.st_size; + *data = malloc(*data_len); + if(!*data) + { die("%s(%s): can't alloc %zd bytes\n", __func__, filename, *data_len); } + if(read(fd, *data, *data_len) < 0) + { die("read(%d, %zd): %s\n", fd, *data_len, strerror(errno)); } + close(fd); +} + +static bool is_text(char *filename) +{ + char *ext = strchr(basename(filename), '.'); + if(ext) + { + ext++; + if(strcmp(ext, "html") == 0) { return true; } + else if(strcmp(ext, "json") == 0) { return true; } + else if(strcmp(ext, "xml") == 0) { return true; } + else if(strcmp(ext, "css") == 0) { return true; } + else if(strcmp(ext, "svg") == 0) { return true; } + else if(strcmp(ext, "js") == 0) { return true; } + } + return false; +} + +static uint8_t mime_type_from_filename(char *filename) +{ + char *ext = strchr(basename(filename), '.'); + if(ext) + { + ext++; + // See "enum template_types" bellow + if(strcmp(ext, "png") == 0) { return 1; } + else if(strcmp(ext, "gif") == 0) { return 2; } + else if(strcmp(ext, "ico") == 0) { return 3; } + else if(strcmp(ext, "jpg") == 0) { return 4; } + } + return 0; +} + +static void parse_index_file(char *filename) +{ + uint8_t *is_defined = NULL; + size_t is_defined_len = 0; + if(access(defined_file, R_OK) != -1) + readfile(defined_file, &is_defined, &is_defined_len); + FILE *f = xfopen(filename, "r"); + int max_fields = 3; + char line[1024]; + while(fgets(line, sizeof(line) - 1, f)) + { + int field = 0, pos = 0; + char *ident = "", *file = "", *deps = ""; + int len = strlen(line); + if(!len || !isalnum((unsigned char)line[0])) // Skip comments and junk + { continue; } + // Parse text[ ]text[ ]text + do + { + while(line[pos] == ' ' || line[pos] == '\t') // Skip white space + { pos++; } + if(line[pos] == '\n') + { break; } + int start = pos; + while(line[pos] != ' ' && line[pos] != '\t' && line[pos] != '\n') // Data + { pos++; } + switch(++field) + { + case 1: + ident = line + start; + line[pos] = '\0'; + break; + case 2: + file = line + start; + line[pos] = '\0'; + break; + case 3: + deps = line + start; + line[pos] = '\0'; + break; + } + if(field >= max_fields) + { break; } + pos++; + } + while(pos < len); + + if(deps && strlen(deps) && is_defined){ + if(strstr(deps, ",")){ + int i,def_found=0; + char *ptr, *saveptr1 = NULL; + char *deps_sep = strdup(deps); + for(i = 0, ptr = strtok_r(deps_sep, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1), i++) + { + if(strstr((char *)is_defined, ptr)) + { def_found = 1; } + } + free(deps_sep); + if(!def_found) + { continue; } + } + else if( !strstr((char *)is_defined, deps)) + { continue; } + } + if(!strlen(ident) || !strlen(file)) + { continue; } + +#define template_set(var) \ + do { \ + len = strlen(var); \ + pos = sizeof(templates.data[0].var); \ + if (len > pos - 1) \ + die("%s=%s length exceeds maxlen (%d > %d)\n", #var, var, len, pos - 1); \ + snprintf(templates.data[templates.num].var, pos, "%s", var); \ + } while (0) + template_set(ident); + template_set(file); + template_set(deps); + + templates.data[templates.num].type = is_text(file) ? TXT : BIN; + templates.data[templates.num].mime_type = mime_type_from_filename(file); + templates.num++; + if(templates.num == MAX_TEMPLATES - 1) + { + die("Too many templates in %s. Maximum is %d. Increase MAX_TEMPLATES!\n", + filename, MAX_TEMPLATES); + } + } + fclose(f); +} + +static void print_template(int tpl_idx) +{ + static bool ifdef_open = 0; + char *prev_deps = ""; + char *next_deps = ""; + char *ident = templates.data[tpl_idx].ident; + char *deps = templates.data[tpl_idx].deps; + if(tpl_idx > 0) + { prev_deps = templates.data[tpl_idx - 1].deps; } + if(tpl_idx + 1 < templates.num) + { next_deps = templates.data[tpl_idx + 1].deps; } + int deps_len = strlen(deps); + + // Put guards + if(deps_len && strcmp(deps, prev_deps) != 0) + { + int i, commas = 0; + for(i = 0; i < deps_len; i++) + { + if(deps[i] == ',') + { commas++; } + } + if(commas == 0) + { + fprintf(output_file, "#ifdef %s\n", deps); + } + else + { + char *ptr, *saveptr1 = NULL; + char *split_deps = strdup(deps); + for(i = 0, ptr = strtok_r(split_deps, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1), i++) + { + if(i == 0) + { fprintf(output_file, "#if defined(%s)", ptr); } + else + { fprintf(output_file, " || defined(%s)", ptr); } + } + fprintf(output_file, "\n"); + free(split_deps); + } + ifdef_open = 1; + } + +#ifdef USE_COMPRESSION + fprintf(output_file, "\t{ .tpl_name_ofs=%5u, .tpl_data_ofs=%5u, .tpl_deps_ofs=%5u, .tpl_data_len=%5u, .tpl_type=%u }, /* %s %s %s */\n", + templates.data[tpl_idx].ident_ofs, + templates.data[tpl_idx].data_ofs, + templates.data[tpl_idx].deps_ofs, + templates.data[tpl_idx].data_len, + templates.data[tpl_idx].mime_type, + ident, + templates.data[tpl_idx].file, + deps + ); +#else + fprintf(output_file, "\t{ .tpl_name=\"%s\", .tpl_data=TPL%s, .tpl_deps=\"%s\", .tpl_data_len=%u, .tpl_type=%u },\n", + ident, ident, deps, templates.data[tpl_idx].data_len, templates.data[tpl_idx].mime_type + ); +#endif + + if(ifdef_open && strcmp(deps, next_deps) != 0) + { + fprintf(output_file, "#endif\n"); + ifdef_open = 0; + } +} + +#ifdef USE_COMPRESSION +static void dump_cbinary(char *var_name, uint8_t *buf, size_t buf_len, size_t obuf_len) +{ + fprintf(output_file, "static const char *%s = \"", var_name); + int i; + for(i = 0; i < buf_len; i++) + { + fprintf(output_file, "\\x%02x", buf[i]); + } + fprintf(output_file, "\";\n"); + fprintf(output_file, "static const size_t %s_len = %zu;\n" , var_name, buf_len); + fprintf(output_file, "static const size_t %s_olen = %zu;\n\n", var_name, obuf_len); +} + +#define HEAP_ALLOC(var, size) \ + lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ] + +static HEAP_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS); +#else + +static void dump_text(char *ident, uint8_t *buf, size_t buf_len) +{ + int i; + fprintf(output_file, "#define TPL%s \\\n\"", ident); + for(i = 0; i < buf_len; i++) + { + switch(buf[i]) + { + case '\n': + if(i < buf_len - 1) + { fprintf(output_file, "\\n\\\n"); } + else + { fprintf(output_file, "\\n"); } + break; + case '\\': + fprintf(output_file, "\\\\"); + break; + case '"' : + fprintf(output_file, "\\\""); + break; + default : + fprintf(output_file, "%c", buf[i]); + break; + } + } + fprintf(output_file, "\"\n\n"); +} + +static void dump_binary(char *ident, uint8_t *buf, size_t buf_len) +{ + fprintf(output_file, "#define TPL%s \\\n\"", ident); + int i; + for(i = 0; i < buf_len; i++) + { + fprintf(output_file, "\\x%02x", buf[i]); + } + fprintf(output_file, "\"\n\n"); +} +#endif + +int main(void) +{ + int i; + + parse_index_file(index_filename); + + output_file = xfopen(output_pages_h, "w"); + fprintf(output_file, "#ifndef WEBIF_PAGES_H_\n"); + fprintf(output_file, "#define WEBIF_PAGES_H_\n"); + fprintf(output_file, "\n"); + fprintf(output_file, "enum template_types {\n"); + fprintf(output_file, " TEMPLATE_TYPE_TEXT = 0,\n"); + fprintf(output_file, " TEMPLATE_TYPE_PNG = 1,\n"); + fprintf(output_file, " TEMPLATE_TYPE_GIF = 2,\n"); + fprintf(output_file, " TEMPLATE_TYPE_ICO = 3,\n"); + fprintf(output_file, " TEMPLATE_TYPE_JPG = 4,\n"); + fprintf(output_file, "};\n"); + fprintf(output_file, "\n"); +#ifdef USE_COMPRESSION + fprintf(output_file, "#define COMPRESSED_TEMPLATES 1\n\n"); + fprintf(output_file, "struct template {\n"); + fprintf(output_file, " uint32_t tpl_name_ofs;\n"); + fprintf(output_file, " uint32_t tpl_data_ofs;\n"); + fprintf(output_file, " uint32_t tpl_deps_ofs;\n"); + fprintf(output_file, " uint32_t tpl_data_len;\n"); + fprintf(output_file, " uint8_t tpl_type;\n"); + fprintf(output_file, "};\n"); +#else + fprintf(output_file, "struct template {\n"); + fprintf(output_file, " char *tpl_name;\n"); + fprintf(output_file, " char *tpl_data;\n"); + fprintf(output_file, " char *tpl_deps;\n"); + fprintf(output_file, " uint32_t tpl_data_len;\n"); + fprintf(output_file, " uint8_t tpl_type;\n"); + fprintf(output_file, "};\n"); +#endif + fprintf(output_file, "\n"); + fprintf(output_file, "int32_t templates_count(void);\n"); + fprintf(output_file, "bool template_is_image(enum template_types tpl_type);\n"); + fprintf(output_file, "const char *template_get_mimetype(enum template_types tpl_type);\n"); + fprintf(output_file, "const struct template *templates_get(void);\n"); +#ifdef USE_COMPRESSION + fprintf(output_file, "void templates_get_data(const char **data, size_t *data_len, size_t *odata_len);\n"); +#endif + fprintf(output_file, "\n"); + fprintf(output_file, "#endif\n"); + fclose(output_file); + + output_file = xfopen(output_pages_c, "w"); + fprintf(output_file, "#include \"../globals.h\"\n"); + fprintf(output_file, "\n"); + fprintf(output_file, "#ifdef WEBIF\n"); + fprintf(output_file, "\n"); + fprintf(output_file, "#include \"pages.h\"\n"); + fprintf(output_file, "\n"); + +#ifdef USE_COMPRESSION + // Calculate positions at which the values would be storred + uint32_t cur_pos = 0; +#define align_up(val, align) (val += (align - val % align)) + for(i = 0; i < templates.num; i++) + { + struct template *t = &templates.data[i]; + readfile(t->file, &t->buf, &t->buf_len); + t->data_len = t->buf_len; + // +1 to leave space for \0 + t->ident_ofs = cur_pos; + cur_pos += strlen(t->ident) + 1; + align_up(cur_pos, sizeof(void *)); + t->data_ofs = cur_pos; + cur_pos += t->data_len + 1; + align_up(cur_pos, sizeof(void *)); + t->deps_ofs = cur_pos; + cur_pos += strlen(t->deps) + 1; + align_up(cur_pos, sizeof(void *)); + } + + // Allocate template data and populate it +#define data_len cur_pos + if(!data_len) + die("No defined templates"); + uint8_t *data = calloc(1, data_len); + if(!data) + { die("Can't alloc %u bytes", data_len); } + for(i = 0; i < templates.num; i++) + { + struct template *t = &templates.data[i]; + memcpy(data + t->ident_ofs, t->ident, strlen(t->ident)); + memcpy(data + t->data_ofs , t->buf , t->buf_len); + free(t->buf); + if(!t->deps[0]) // No need to copy empty deps + { continue; } + memcpy(data + t->deps_ofs, t->deps, strlen(t->deps)); + } + FILE *bin = xfopen("pages.bin", "w"); + fwrite(data, data_len, 1, bin); + fclose(bin); + + // Compress template data + lzo_uint in_len = data_len; + lzo_uint out_len = data_len + data_len / 16 + 64 + 3; // Leave enough space in the output + uint8_t *out = malloc(out_len); + if(!out) + { die("Can't alloc %zu bytes", out_len); } + + if(lzo_init() != LZO_E_OK) + { + fprintf(stderr, "internal error - lzo_init() failed !!!\n"); + fprintf(stderr, "(this usually indicates a compiler bug - try recompiling\nwithout optimizations, and enable '-DLZO_DEBUG' for diagnostics)\n"); + free(out); + free(data); + return 3; + } + + int r = lzo1x_1_compress(data, in_len, out, &out_len, wrkmem); + if(r == LZO_E_OK) + { + int64_t saved_bytes = (int64_t)in_len - (int64_t)out_len; + printf("GEN\tCompressed %lu template bytes into %lu bytes. %" PRId64 " saved bytes (%.2f%%).\n", + (unsigned long)in_len, (unsigned long)out_len, + saved_bytes, 100 - ((float)out_len / in_len) * 100); + } + else + { + /* this should NEVER happen */ + printf("internal error - compression failed: %d\n", r); + free(out); + free(data); + return 2; + } + + bin = xfopen("pages.bin.compressed", "w"); + fwrite(out, out_len, 1, bin); + fclose(bin); + + dump_cbinary("templates_data", out, out_len, data_len); + free(out); + free(data); +#else + for(i = 0; i < templates.num; i++) + { + uint8_t *buf; + size_t buf_len; + readfile(templates.data[i].file, &buf, &buf_len); + templates.data[i].data_len = buf_len; + switch(templates.data[i].type) + { + case TXT: + dump_text(templates.data[i].ident, buf, buf_len); + break; + case BIN: + dump_binary(templates.data[i].ident, buf, buf_len); + break; + } + free(buf); + } +#endif + + fprintf(output_file, "static const struct template templates[] = {\n"); + for(i = 0; i < templates.num; i++) + { + print_template(i); + } + fprintf(output_file, "};\n"); + fprintf(output_file, "\n"); + fprintf(output_file, "int32_t templates_count(void) { return sizeof(templates) / sizeof(struct template); }\n"); + fprintf(output_file, "const struct template *templates_get(void) { return templates; }\n"); +#ifdef USE_COMPRESSION + fprintf(output_file, "void templates_get_data(const char **data, size_t *data_len, size_t *data_olen) { *data = templates_data; *data_len = templates_data_len; *data_olen = templates_data_olen; }\n"); +#endif + fprintf(output_file, "\n"); + fprintf(output_file, "bool template_is_image(enum template_types tpl_type) {\n"); + fprintf(output_file, " switch (tpl_type) {\n"); + fprintf(output_file, " case TEMPLATE_TYPE_PNG:\n"); + fprintf(output_file, " case TEMPLATE_TYPE_GIF:\n"); + fprintf(output_file, " case TEMPLATE_TYPE_ICO:\n"); + fprintf(output_file, " case TEMPLATE_TYPE_JPG:\n"); + fprintf(output_file, " return true;\n"); + fprintf(output_file, " default:\n"); + fprintf(output_file, " return false;\n"); + fprintf(output_file, " }\n"); + fprintf(output_file, " return false;\n"); + fprintf(output_file, "}\n"); + fprintf(output_file, "\n"); + fprintf(output_file, "const char *template_get_mimetype(enum template_types tpl_type) {\n"); + fprintf(output_file, " switch (tpl_type) {\n"); + fprintf(output_file, " case TEMPLATE_TYPE_TEXT: return \"text/plain\";\n"); + fprintf(output_file, " case TEMPLATE_TYPE_PNG : return \"image/png\";\n"); + fprintf(output_file, " case TEMPLATE_TYPE_GIF : return \"image/gif\";\n"); + fprintf(output_file, " case TEMPLATE_TYPE_ICO : return \"image/x-icon\";\n"); + fprintf(output_file, " case TEMPLATE_TYPE_JPG : return \"image/jpg\";\n"); + fprintf(output_file, " }\n"); + fprintf(output_file, " return \"\";\n"); + fprintf(output_file, "}\n"); + fprintf(output_file, "\n"); + fprintf(output_file, "#endif\n"); + fclose(output_file); + + return 0; +} diff --git a/webif/pages_index.txt b/webif/pages_index.txt new file mode 100644 index 0000000..654fc69 --- /dev/null +++ b/webif/pages_index.txt @@ -0,0 +1,345 @@ +# This file contains index of the templates. +# The format is simple "TEMPLATE_NAME FILENAME DEPENDENCY1,DEPENDANCYx" +# Lines starting with # are ignorred +# TEMPLATE_NAME - name of the template and also name of the file that +# contains the template +# FILENAME - The file that contains the template +# DEPENDANCYx - The config variable which is responsible for this template + +JSONCACHEEX api.json/cacheex.json CS_CACHEEX +JSONCACHEEXBIT api.json/cacheexbit.json CS_CACHEEX +JSONCACHEEXAIOBIT api.json/cacheexaiobit.json CS_CACHEEX_AIO +JSONENTITLEMENTS api.json/entitlements.json +JSONENTITLEMENTBIT api.json/entitlementbit.json +JSONFOOTER api.json/footer.json +JSONHEADER api.json/header.json +JSONREADER api.json/reader.json +JSONREADERBIT api.json/readerbit.json +JSONSTATUS api.json/status.json +JSONSTATUSBIT api.json/status_statusbits.json +JSONUSER api.json/user.json +JSONUSERBIT api.json/userbit.json + +APICCCAMCARDLIST api.xml/cccamcardlist.xml MODULE_CCCAM +APICCCAMCARDBIT api.xml/cccamcardlist_cardlist.xml MODULE_CCCAM +APICCCAMCARDNODEBIT api.xml/cccamcardlist_cardlist_nodelist.xml MODULE_CCCAM +APICCCAMCARDPROVIDERBIT api.xml/cccamcardlist_cardlist_providerlist.xml MODULE_CCCAM +APICONFIRMATION api.xml/confirmation.xml +APIERROR api.xml/error.xml +APIFAILBAN api.xml/failban.xml +APIFAILBANBIT api.xml/failban_failbanrow.xml +APIFILE api.xml/file.xml +APIFOOTER api.xml/footer.xml +APIHEADER api.xml/header.xml +APIREADERS api.xml/readers.xml +APIREADERSBIT api.xml/readers_readerlist.xml +APIREADERSTATS api.xml/readerstats.xml +APIREADERSTATSECMBIT api.xml/readerstats_ecmstats.xml +APIREADERSTATSEMMBIT api.xml/readerstats_emmstats.xml +APISTATUS api.xml/status.xml +APISTATUSBIT api.xml/status_statusbits.xml +APIUSERCONFIGLIST api.xml/userconfiglist.xml +APIUSERCONFIGLISTBIT api.xml/userconfiglist_userconfigs.xml +APIUSEREDIT api.xml/useredit.xml + +CACHEEXPAGE cacheex/cacheex.html CS_CACHEEX +CACHEEXTABLEROW cacheex/cacheex_tablerow.html CS_CACHEEX + +CACHEEXAIOPAGE cacheexaio/cacheex.html CS_CACHEEX_AIO +CACHEEXAIOTABLEROW cacheexaio/cacheex_tablerow.html CS_CACHEEX_AIO +CACHEEXAIOTABLEROWSTATS cacheexaio/cacheex_tablerow_stats.html CS_CACHEEX_AIO + +CONFIGANTICASC config/anticasc.html CS_ANTICASC +CONFIGCACHE config/cache.html +CONFIGCACHEAIO config/cacheaio.html CS_CACHEEX_AIO +CONFIGCACHEEXCSP config/cache_cacheexcsp.html CS_CACHEEX +CONFIGCACHEEXAIOCSP config/cache_cacheexaiocsp.html CS_CACHEEX_AIO +CONFIGCWCYCLE config/cache_cwcycle.html CW_CYCLE_CHECK +CONFIGCAMD33 config/camd33.html MODULE_CAMD33 +CONFIGCAMD35 config/camd35.html MODULE_CAMD35 +CONFIGCAMD35TCP config/camd35tcp.html MODULE_CAMD35_TCP +CONFIGCCCAM config/cccam.html MODULE_CCCSHARE +CONFIGCCCAMCTRL config/cccam_control.html MODULE_CCCSHARE +CONFIGCONTENT config/config.html +CONFIGDVBAPI config/dvbapi.html HAVE_DVBAPI +EXTENDEDCWAPI config/dvbapi_extended_cw_api.html WITH_EXTENDED_CW,WITH_EMU +DEMUXERFIX config/dvbapi_demuxerfix.html MODULE_STREAMRELAY +CCCAMRESHAREBIT config/cccreshare.html MODULE_GBOX +CONFIGGBOX config/gbox.html MODULE_GBOX +CONFIGGLOBAL config/global.html +CACHEEXAIOLOGGING config/global_cacheex_aio_logging.html CS_CACHEEX_AIO +ENABLELEDBIT config/global_enableledbit.html LEDSUPPORT +LOCALCARDS config/global_localcards.html WITH_CARDREADER +SUPPRESSCMD08 config/global_suppresscmd08.html MODULE_CAMD35,MODULE_CAMD35_TCP +GETBLOCKEMMAUPROVID config/global_getblockemmauprovid.html MODULE_CAMD35,MODULE_CAMD35_TCP +UNLOCKPARENTAL config/global_unlockparental.html READER_SECA,READER_VIACCESS +CONFIGLCD config/lcd.html LCDSUPPORT +CONFIGLOADBALANCER config/loadbalancer.html WITH_LB +CONFIGLOADBALANCERCTRL config/loadbalancer_control.html WITH_LB +CONFIGMENU config/menu.html +CONFIGMENUANTICASC config/menu_anticasc.html CS_ANTICASC +CONFIGMENUCAMD33 config/menu_camd33.html MODULE_CAMD33 +CONFIGMENUCAMD35 config/menu_camd35.html MODULE_CAMD35 +CONFIGMENUCAMD35TCP config/menu_camd35tcp.html MODULE_CAMD35_TCP +CONFIGMENUCCCAM config/menu_cccam.html MODULE_CCCSHARE +CMCAPTIONCWC config/menu_cmcaptioncwc.html CW_CYCLE_CHECK +CONFIGMENUDVBAPI config/menu_dvbapi.html HAVE_DVBAPI +CONFIGMENUGBOX config/menu_gbox.html MODULE_GBOX +CONFIGMENULCD config/menu_lcd.html LCDSUPPORT +CONFIGMENULB config/menu_loadbalancer.html WITH_LB +CONFIGMENUMONITOR config/menu_monitor.html MODULE_MONITOR +CONFIGMENUNEWCAMD config/menu_newcamd.html MODULE_NEWCAMD +CONFIGMENURADEGAST config/menu_radegast.html MODULE_RADEGAST +CONFIGMENUSCAM config/menu_scam.html MODULE_SCAM +CONFIGMENUSTREAMRELAY config/menu_streamrelay.html MODULE_STREAMRELAY +STREAMEMUSETTINGS config/streamrelay_emusettings.html WITH_EMU +CONFIGMENUSERIAL config/menu_serial.html MODULE_SERIAL +CONFIGMONITOR config/monitor.html MODULE_MONITOR +CONFIGNEWCAMD config/newcamd.html MODULE_NEWCAMD +CONFIGRADEGAST config/radegast.html MODULE_RADEGAST +CONFIGSCAM config/scam.html MODULE_SCAM +CONFIGSTREAMRELAY config/streamrelay.html MODULE_STREAMRELAY +CONFIGSERIAL config/serial.html MODULE_SERIAL +CONFIGSERIALDEVICEBIT config/serial_devices.html MODULE_SERIAL +CONFIGWEBIF config/webif.html +HTTPSSL config/webif_httpssl.html WITH_SSL +SHOWCACHEEXINFO config/webif_showcacheexinfo.html CS_CACHEEX +CONFIGWEBIFJQUERYBIT config/webif_show_jquery.html + +ASKEMM emm/emm.html + +EMM_RUNNING emm_running/emm_running.html + +ENTITLEMENTS entitlements/entitlements.html WITH_CARDREADER,MODULE_CCCAM +ENTITLEMENTBIT entitlements/entitlements_bit.html WITH_CARDREADER +ENTITLEMENTBITNDS entitlements/entitlements_bit_nds.html READER_VIDEOGUARD +ENTITLEMENTCCCAMBIT entitlements/entitlements_cccambit.html MODULE_CCCAM +ENTITLEMENTCCCAMENTRYBIT entitlements/entitlements_cccambit_statsentry.html MODULE_CCCAM +ENTITLEMENTGENERICBIT entitlements/entitlements_genericbit.html WITH_CARDREADER +ENTITLEMENTITEMBIT entitlements/entitlements_itembit.html WITH_CARDREADER + +FAILBAN failban/failban.html +FAILBANBIT failban/failban_failbanrow.html + +FILE files/file.html +FILEMENUCSS files/file_edit_css.html +FILTERFORM files/file_filterform.html +WRITEPROTECTION files/file_writeprotection.html +FILEMENU files/menu.html +FILEMENUANTICASC files/menu_anticasc.html CS_ANTICASC +FILEMENUCONSTCW files/menu_constantcw.html MODULE_CONSTCW +FILEMENUDVBAPI files/menu_dvbapi.html HAVE_DVBAPI +FILEMENUFAKECWS files/menu_fakecws.html CS_CACHEEX +FILEMENUGBOX files/menu_gbox.html MODULE_GBOX +FILEMENUTWIN files/menu_twin.html MODULE_SERIAL +FILEMENUSOFTCAMKEY files/menu_softcamkey.html WITH_EMU + +AUTOCONF ghttp/autoconf.html MODULE_GHTTP +PREAUTOCONF ghttp/pre_autoconf.html MODULE_GHTTP + +GRAPH graph/graph.svg + +ICMAI images/favicon.ico +ICARRL images/ICARRL.svg CS_CACHEEX +ICARRR images/ICARRR.svg CS_CACHEEX +ICDEL images/ICDEL.svg +ICDIS images/ICDIS.svg +ICEDI images/ICEDI.svg +ICEMM images/ICEMM.svg +ICENA images/ICENA.svg +ICENT images/ICENT.svg +ICHID images/ICHID.svg +ICKIL images/ICKIL.svg +LOGOBITSVG images/ICMLOGO.svg +ICREF images/ICREF.svg +ICRES images/ICRES.svg +ICSPAC images/ICSPAC.gif +ICSTA images/ICSTA.svg +ICSTART images/ICSTART.svg +ICSTOP images/ICSTOP.svg + +BODY include/body.html +CCENTITLEMENTS include/cccamentitlements.html MODULE_CCCAM +CCENTITLETOOLTIP include/cccamentitletooltip.html MODULE_CCCAM +CSS include/css.css +FOOTER include/footer.html +FOUNDENTITLEMENTS include/foundentitlements.html +HEADER include/header.html +HEADERSHORT include/header_short.html +JQUERY include/jquery.js WEBIF_JQUERY +JSCRIPT include/jscript.js +LOGOBIT include/logobit.html +LOGOBITIMG include/logobit_img.html +MENU include/menu.html +CACHEEXMENUITEM include/menu_cacheexmenuitem.html CS_CACHEEX +MESSAGE include/message.html +MESSAGEBIT include/message_bit.html +NOENTITLEMENTS include/noentitlements.html +POLL include/poll.html +POLLINGSET include/pollingset.html +PROTOCCCAMPIC include/protocccampic.html MODULE_CCCAM +PROTONEWCAMDPIC include/protonewcamdpic.html MODULE_NEWCAMD +PROTOCAMD3AIOPIC include/protocamd3aiopic.html MODULE_CAMD35,MODULE_CAMD35_TCP +PROTOOTHERPIC include/protootherpic.html +REFRESH include/refresh.html + +CLEARLOG logmenu/log_clearlog.html +CLEARUSERLOG logmenu/log_clearuserlog.html +LOGMENUDISABLELOG logmenu/log_disablelogmenu.html +LOGMENUFILTERFORM logmenu/log_filterform.html +LOGMENUONOFF logmenu/log_logmenuonoff.html + +LOGPAGE logpage/logpage.html WEBIF_LIVELOG +LOGMENU logpage/logpage_menu.html WEBIF_LIVELOG +LOGDEBUGMENU logpage/logpage_debugmenu.html WEBIF_LIVELOG +LOGSIZEMENU logpage/logpage_sizemenu.html WEBIF_LIVELOG + +READERCONFIG readerconfig/readerconfig.html +READERCONFIGAIO readerconfig/readerconfigaio.html CS_CACHEEX_AIO +READERCONFIGIPV6BIT readerconfig/readerconfig_ipv6bit.html IPV6SUPPORT +READEREDITCACHEEXBIT readerconfig/readerconfig_cacheexbit.html CS_CACHEEX +READEREDITCACHEEXAIOBIT readerconfig/readerconfig_cacheexaiobit.html CS_CACHEEX_AIO +READERCONFIGCAMD35BIT readerconfig/readerconfig_camd35bit.html MODULE_CAMD35 +READERCONFIGCCCAMBIT readerconfig/readerconfig_cccambit.html MODULE_CCCAM +READERCONFIGEMUBIT readerconfig/readerconfig_emubit.html WITH_EMU +READERCONFIGCS378XBIT readerconfig/readerconfig_cs378xbit.html MODULE_CAMD35_TCP +READERCONFIGGBOXBIT readerconfig/readerconfig_gboxbit.html MODULE_GBOX +READERINFOGBOXREMM readerconfig/readerinfo_gbox_remm.html MODULE_GBOX +GBOXCCCAMRESHAREBIT readerconfig/readerconfig_gboxcccresharebit.html MODULE_GBOX +READERCONFIGGHTTPBIT readerconfig/readerconfig_ghttpbit.html MODULE_GHTTP +READERCONFIGHOPBIT readerconfig/readerconfig_hopbit.html MODULE_CCCAM +READERCONFIGSTDHWREADERBIT readerconfig/readerconfig_hwreader.html WITH_CARDREADER +READERCONFIGCRYPTOWORKS readerconfig/readerconfig_hwreader_cryptoworks.html READER_CRYPTOWORKS +READERCONFIGBOXKEY readerconfig/readerconfig_hwreader_boxkey.html READER_NAGRA,READER_IRDETO,READER_SECA,READER_VIACCESS +READERCONFIGIRDETO readerconfig/readerconfig_hwreader_irdeto.html READER_IRDETO +READERCONFIGNAGRA readerconfig/readerconfig_hwreader_nagra.html READER_NAGRA +READERCONFIGNAGRACAK7 readerconfig/readerconfig_hwreader_nagracak7.html READER_NAGRA_MERLIN +READERCONFIGCONAX readerconfig/readerconfig_hwreader_conax.html READER_CONAX +READERCONFIGNANO readerconfig/readerconfig_hwreader_nano.html WITH_CARDREADER +READERPINCODE readerconfig/readerconfig_hwreader_pincode.html READER_CONAX,READER_CRYPTOWORKS,READER_VIACCESS,READER_SECA +READERCONFIGRSAKEY readerconfig/readerconfig_hwreader_rsakey.html READER_NAGRA,READER_IRDETO,READER_CONAX +READERCONFIGDESKEY readerconfig/readerconfig_hwreader_deskey.html READER_VIACCESS,READER_DRE +READERCONFIGSC8IN1 readerconfig/readerconfig_hwreader_sc8in1.html CARDREADER_SC8IN1 +READERCONFIGSMARGO readerconfig/readerconfig_hwreader_smargo.html CARDREADER_SMARGO,CARDREADER_PHOENIX +READERCONFIGVIACCESS readerconfig/readerconfig_hwreader_viaccess.html READER_VIACCESS +READERCONFIGDRE readerconfig/readerconfig_hwreader_dre.html READER_DRE +READERCONFIGVIDEOGUARD readerconfig/readerconfig_hwreader_videoguard.html READER_VIDEOGUARD +READERCONFIGTONGFANG readerconfig/readerconfig_hwreader_tongfang.html READER_TONGFANG +READERCONFIGLBWEIGHT readerconfig/readerconfig_lbweight.html WITH_LB +READERCONFIGNCD525BIT readerconfig/readerconfig_ncd525bit.html MODULE_NEWCAMD +READERCONFIGNCD524BIT readerconfig/readerconfig_ncd524bit.html MODULE_NEWCAMD +READERCONFIGRADEGASTBIT readerconfig/readerconfig_radegastbit.html MODULE_RADEGAST +READERCONFIGSCAMBIT readerconfig/readerconfig_scambit.html MODULE_SCAM +READERCONFIGSID readerconfig/readerconfig_sid.html +READERCONFIGSIDLBOKBIT readerconfig/readerconfig_sidlbokbit.html +READERCONFIGSIDNOBIT readerconfig/readerconfig_sidnobit.html +READERCONFIGSIDOKBIT readerconfig/readerconfig_sidokbit.html + +READERS readers/readers.html +READERSAIO readers/readersaio.html CS_CACHEEX_AIO +READERCTYPBIT readers/readerctypbit.html +READERCTYPNOICON readers/readerctypnoicon.html +READERLABEL readers/readerlabel.html +READERNAMEBIT readers/readernamebit.html +READERNOICON readers/readernoicon.html +READERLBBIT readers/readers_lblweightbit.html WITH_LB +READERLBWD readers/readers_lblweightd.html WITH_LB +READERLBWU readers/readers_lblweightu.html WITH_LB +READERSBIT readers/readers_readerlist.html +READERSBITAIO readers/readersaio_readerlist.html CS_CACHEEX_AIO +READERENTITLEBIT readers/readers_readerlist_entitlement.html +READERLBSTAT readers/readers_readerlist_lbstat.html WITH_LB +READERREFRESHBIT readers/readers_readerlist_refresh.html +READERWRITEEMMBIT readers/readers_readerlist_writeemm.html + +READERSTATS readerstats/readerstats.html WITH_LB +READERSTATSNOSTATS readerstats/readerstats_nostats.html WITH_LB +READERSTATSBIT readerstats/readerstats_statsbit.html WITH_LB +READERSTATSROWNOTFOUNDBIT readerstats/readerstatsnotfound.html WITH_LB +READERSTATSROWBIT readerstats/readerstatsrowbit.html WITH_LB +READERSTATSROWTIMEOUTBIT readerstats/readerstatstimeoutbit.html WITH_LB +READERSTATSROWINVALIDBIT readerstats/readerstatsinvalid.html WITH_LB + +SAVETEMPLATES savetemplates/savetemplates.html + +SCANUSB scanusb/scanusb.html +SCANUSBBIT scanusb/scanusb_usbbit.html +SCANUDEVBIT scanusb/scanusb_udevbit.html +SCANPCSCBIT scanusb/scanusb_pcscbit.html CARDREADER_PCSC + +SCRIPT script/script.html + +SERVICECONFIGLIST services/services.html +SERVICECONFIGLISTBIT services/services_servicetabs.html +SERVICECONFIGSIDBIT services/services_servicetabs_sidlist.html + +SERVICEEDIT services_edit/services_edit.html +SERVICEEDITAIO services_edit/services_editaio.html CS_CACHEEX_AIO + +PRESHUTDOWN shutdown/pre_shutdown.html +SHUTDOWN shutdown/shutdown.html + +STATUS status/status.html +CACHEEXINFOBIT status/status_cacheexinfo.html CS_CACHEEX +CACHEEXAIOINFOBIT status/status_cacheexaioinfo.html CS_CACHEEX_AIO +CLIENTHEADLINE status/status_cheadline.html +CLIENTHEADLINEADD status/status_cheadlineadd.html +CLIENTHEADLINEBIT status/status_clientheadlinebit.html +CLIENTSTATUSBIT status/status_clientstatusbit.html +CLIENTCURRENTCHANNEL status/status_currentchannel.html +CLIENTCURRENTCHANNELBIT status/status_currentchannelbit.html +CLIENTCURRENTCHANNELPIC status/status_currentchannelpic.html +STATUSHEADLINE status/status_headline.html +STATUSHBUTTON status/status_hidebutton.html +STATUSKBUTTON status/status_killbutton.html +CLIENTLBLVALUEBIT status/status_lblvaluereaderbit.html WITH_LB +CLIENTLBLVALUERP status/status_lbvaluereaderproxy.html WITH_LB +LOGHISTORYBIT status/status_loghistory.html +CLIENTMHEADLINE status/status_mheadline.html MODULE_MONITOR +CLIENTPHEADLINE status/status_pheadline.html +CLIENTPHEADLINEADD status/status_pheadlineadd.html +SREADER status/status_reader.html +SREADERICON status/status_readericon.html +STATUSRBUTTON status/status_restartbutton.html +CLIENTRHEADLINE status/status_rheadline.html +CLIENTRHEADLINEADD status/status_rheadlineadd.html +DEBUGSELECT status/status_sdebug.html WITH_DEBUG +DEBUGSELECTAIO status/status_sdebugaio.html WITH_DEBUG +CLIENTSHEADLINE status/status_sheadline.html +SYSTEMINFOBIT status/status_systeminfo.html +SUSER status/status_user.html +SUSERICON status/status_usericon.html +USERINFOBIT status/status_userinfo.html +READERINFOBIT status/status_readerinfo.html + +USEREDIT user_edit/user_edit.html +USEREDITAIO user_edit/user_editaio.html CS_CACHEEX_AIO +USEREDITANTICASC user_edit/user_edit_anticasc.html CS_ANTICASC +USEREDITCACHEEXBIT user_edit/user_edit_cacheexbit.html CS_CACHEEX +USEREDITCACHEEXAIOBIT user_edit/user_edit_cacheexaiobit.html CS_CACHEEX_AIO +USEREDITCWCYCLE user_edit/user_edit_cwcycle.html CW_CYCLE_CHECK +USEREDITCCCAM user_edit/user_edit_cccam.html MODULE_CCCAM +USEREDITMONLEVEL user_edit/user_edit_monlevel.html MODULE_MONITOR +USEREDITSID user_edit/user_edit_sid.html +USEREDITSIDNOBIT user_edit/user_edit_sidnobit.html +USEREDITSIDOKBIT user_edit/user_edit_sidokbit.html + +USERCONFIGLIST userconfig/userconfig.html +USERANTICASC userconfig/userconfig_anticascbit.html CS_ANTICASC +CWANTICASCTHV userconfig/userconfig_cwanticascthv.html CS_ANTICASC +USERCWCYCLE userconfig/userconfig_cwcyclebit.html CW_CYCLE_CHECK +CWCYCLETHV userconfig/userconfig_cwcyclethv.html CW_CYCLE_CHECK +USERCONFIGLISTBIT userconfig/userconfig_entry.html +CWANTICASCTBV userconfig/userconfig_entry_cwanticasctbv.html CS_ANTICASC +CWCYCLETBV userconfig/userconfig_entry_cwcycletbv.html CW_CYCLE_CHECK +USERCONFIGLASTCHANEL userconfig/userconfig_lastchannelicon.html +ADDNEWUSER userconfig/userconfig_newuserform.html +CLIENTCOUNTNOTIFIERBIT userconfig/userconfig_notify.html +USERICON userconfig/userconfig_usericon.html +USERLABEL userconfig/userconfig_userlabel.html +USERNOICON userconfig/userconfig_usernoicon.html + +WIKIJSON wiki/wiki.json WEBIF_WIKI +WIKIERROR wiki/wikierror.json WEBIF_WIKI +WIKINOTFOUND wiki/wikinotfound.json WEBIF_WIKI +WIKISTATUSJSON wiki/wikistatus.json WEBIF_WIKI diff --git a/webif/pages_index_check b/webif/pages_index_check new file mode 100644 index 0000000..f66ceea --- /dev/null +++ b/webif/pages_index_check @@ -0,0 +1,21 @@ +#!/bin/sh + +echo "= Checking for files that are not described in pages_index.txt" +for FILE in $(find . -name '*.html' -o -name '*.css' -o -name '*.js' -o -name '*.json' -o -name '*.xml' -o -name '*.png' -o -name '*.gif' -o -name '*.jpg' | sed -e 's|^\./||') +do + grep -w $FILE pages_index.txt >/dev/null + if [ $? != 0 ] + then + echo " *** FILE NOT IN pages_index.txt: $FILE" + fi +done + +echo "= Checking for files that are in pages_index.txt but do not exist" +while read TPL FILE DEPS +do + [ "$TPL" = "#" ] && continue + if [ ! -f $FILE ] + then + echo " *** FILE NOT FOUND: $FILE" + fi +done < pages_index.txt diff --git a/webif/pages_mkdep b/webif/pages_mkdep new file mode 100644 index 0000000..9baeace --- /dev/null +++ b/webif/pages_mkdep @@ -0,0 +1,10 @@ +#!/bin/sh + +FILES=$(while read TPL FILE DEP ; do [ "$TPL" != "#" ] && echo $FILE ; done < pages_index.txt | sort | uniq | xargs echo) + +echo "pages.c: pages_index.txt pages.h $FILES" > pages.dep +for FILE in pages_index.txt pages.h $FILES +do + echo "" + echo "$FILE:" +done >> pages.dep diff --git a/webif/pages_wiki.c b/webif/pages_wiki.c new file mode 100644 index 0000000..6773c61 --- /dev/null +++ b/webif/pages_wiki.c @@ -0,0 +1,655 @@ +/* + * OSCam WebIf Wiki data - AUTO GENERATED, DO NOT EDIT! + * Generated by wiki_gen from wiki markdown files + */ +#define MODULE_LOG_PREFIX "webif" +#include "../globals.h" + +#if defined(WEBIF) && defined(WEBIF_WIKI) + +#include "pages_wiki.h" +#include + +static const struct wiki_entry wiki_entries[] = { + { .param = "Parameters", .config = "ratelimit", .section = "", .text = "- **CAID**: Conditional Access Identification in hexadecimal (e.g., 0100, 0500, 1702)\n- **provider ID**: Provider ID in hexadecimal (e.g., 000000, 003411)\n- **service ID**: Service ID in hexadecimal (specific channel or use wildcard)\n- **ChID**: Channel ID in hexadecimal\n- **ratelimitecm**: Number of different SIDs (channels) allowed within the time interval\n- **ratelimitseconds**: Time interval in seconds for the rate limit\n- **srvidholdseconds**: Extra time in seconds a service ID is kept in a slot before another service ID can take its place", .status = 0 }, + { .param = "Rate", .config = "ratelimit", .section = "", .text = "1. OSCam tracks ECM requests for each CAID:ProvID combination\n2. It maintains a list of recently accessed service IDs (channels)\n3. When a new channel is requested, OSCam checks if the limit is reached\n4. If the limit is exceeded within the time window, the request is denied\n5. After the time interval expires, the counter resets", .status = 0 }, + { .param = "Slot", .config = "ratelimit", .section = "", .text = "The `srvidholdseconds` parameter adds \"stickiness\" to channel slots:\n- When you switch to a channel, it occupies a slot\n- That slot is held for the specified extra time\n- This prevents rapid slot turnover and encourages staying on channels longer\n- After the hold time expires, the slot can be reused for a different channel", .status = 0 }, + { .param = "Preventing", .config = "ratelimit", .section = "", .text = "Limit users to 3 channels per 30 seconds:\n\n```\n1702:000000::::0003:0030:0010\n```\n\nThis allows accessing 3 different channels in 30 seconds, with each channel held for 10 extra seconds.", .status = 0 }, + { .param = "Provider-Specific", .config = "ratelimit", .section = "", .text = "Different limits for different providers:\n\n```\n# Sky Germany - 2 channels per 15 seconds\n1702:000000::::0002:0015:0005\n\n# HD+ - 4 channels per 20 seconds\n1830:003411::::0004:0020:0008\n\n# Canal+ - 3 channels per 30 seconds\n0100:003311::::0003:0030:0010\n```", .status = 0 }, + { .param = "Strict", .config = "ratelimit", .section = "", .text = "Very restrictive limit for shared cards:\n\n```\n0500:032830::::0001:0060:0030\n```\n\nThis allows only 1 channel per 60 seconds, effectively preventing channel switching for a full minute.", .status = 0 }, + { .param = "Moderate", .config = "ratelimit", .section = "", .text = "Balanced approach for normal usage:\n\n```\n# Allow 5 channels per 20 seconds\n1702:000000::::0005:0020:0005\n```", .status = 0 }, + { .param = "All", .config = "ratelimit", .section = "", .text = "```\n0100:::::0003:0030:0010\n```\n\nApplies to all providers under CAID 0100.", .status = 0 }, + { .param = "All", .config = "ratelimit", .section = "", .text = "```\n:::::0002:0015:0005\n```\n\nApplies globally to all CAIDs and providers (use with caution).", .status = 0 }, + { .param = "Setting", .config = "ratelimit", .section = "", .text = "- Consider normal viewing patterns (most users watch 1-2 channels)\n- Allow some flexibility for legitimate channel browsing\n- Balance between preventing abuse and user experience\n- Test limits with real usage before deploying", .status = 0 }, + { .param = "Time", .config = "ratelimit", .section = "", .text = "- Shorter intervals (10-20 seconds) provide tighter control\n- Longer intervals (30-60 seconds) are more lenient\n- Match intervals to your usage policy goals", .status = 0 }, + { .param = "Hold", .config = "ratelimit", .section = "", .text = "- Hold times should be shorter than the rate limit interval\n- Typical values: 3-10 seconds\n- Longer hold times discourage rapid switching\n- Too long may frustrate legitimate users", .status = 0 }, + { .param = "Monitoring", .config = "ratelimit", .section = "", .text = "- Monitor logs for rate limit violations\n- Adjust limits based on actual usage patterns\n- Consider different limits for different user groups\n- Review and update limits periodically", .status = 0 }, + { .param = "Users", .config = "ratelimit", .section = "", .text = "- Limits may be too restrictive\n- Increase `ratelimitecm` value\n- Increase `ratelimitseconds` interval\n- Reduce `srvidholdseconds` hold time", .status = 0 }, + { .param = "Abuse", .config = "ratelimit", .section = "", .text = "- Limits may be too lenient\n- Decrease `ratelimitecm` value\n- Decrease `ratelimitseconds` interval\n- Increase `srvidholdseconds` hold time", .status = 0 }, + { .param = "Legitimate", .config = "ratelimit", .section = "", .text = "- Review actual usage patterns\n- Adjust limits to accommodate normal behavior\n- Consider different limits for trusted users\n- Use per-user limits in oscam.user instead", .status = 0 }, + { .param = "Parameters", .config = "srvid2", .section = "", .text = "- **service ID**: Service ID (channel ID) in hexadecimal\n- **CAID**: One or more CAIDs in hexadecimal\n- **@PROVID**: Optional provider ID(s) in hexadecimal\n- **channel_name**: Channel name (optional)\n- **type**: Channel type like TV, Radio, etc. (optional)\n- **description**: Additional description (optional)\n- **provider**: Provider or satellite position (optional)", .status = 0 }, + { .param = "Simple", .config = "srvid2", .section = "", .text = "```\n000a:0001,0002,0003|tv name|tv|my description 1|provider 1\n000a:0004,0005,0006|radio name 2|radio|my description 2|provider 2\n000b:0006|tv name 3||my description 3|provider 3\n000b:0007|tv name 4|||provider 4\n```", .status = 0 }, + { .param = "Service", .config = "srvid2", .section = "", .text = "```\n# Service ID 1111, CAID 0100, all provider IDs\n1111:0100|x|y|z|v\n```", .status = 0 }, + { .param = "Service", .config = "srvid2", .section = "", .text = "```\n# Service ID 1111, CAID 0100, provider ID 123456\n1111:0100@123456|x|y|z|v\n```", .status = 0 }, + { .param = "Service", .config = "srvid2", .section = "", .text = "```\n# Service ID 1111, CAID 0100, all providers\n1111:0100@000000|x|y|z|v\n```", .status = 0 }, + { .param = "Multiple", .config = "srvid2", .section = "", .text = "```\n# Service ID 1111, CAID 0100 with provider 123456 AND CAID 0200 with provider 654321\n1111:0100@123456,0200@654321|x|y|z|v\n```", .status = 0 }, + { .param = "Sky", .config = "srvid2", .section = "", .text = "**Sky SAT:**\n\n```\n007F:098C,098D,09F0|13th Street|TV|Sky Starter Paket|Sky\n0085:098C,098D,09F0|Beate Uhse|TV|Sky Starter Paket|Sky\n0194:098C,098D,09F0|Cartoon Network|TV|Sky Starter Paket|Sky\n```\n\n**Sky Vodafone:**\n\n```\n0074:098E,1838,1850,1854,1868|Sky Cinema Action HD|TV|Sky Cinema Paket|Sky\n006B:098E,1838,1850,1854,1868|Sky Cinema Classics HD|TV|Sky Cinema Paket|Sky\n008B:098E,1838,1850,1854,1868|Sky Cinema Family HD|TV|Sky Cinema Paket|Sky\n```", .status = 0 }, + { .param = "HD", .config = "srvid2", .section = "", .text = "```\nEF10:1830,1843,1860,186A,186D,09C4,098C,098D|RTL HD|TV||Astra HD+\nEF11:1830,1843,1860,186A,186D,09C4,098C,098D|VOX HD|TV||Astra HD+\nEF74:1830,1843,1860,186A,186D,09C4,098C,098D|SAT.1 HD|TV||Astra HD+\n```", .status = 0 }, + { .param = "ORF", .config = "srvid2", .section = "", .text = "```\n132F:0D95,0D98,0648,0650,09C4,098C|ORF1 HD|||ORF Digital\n1330:0D95,0D98,0648,0650,09C4,098C|ORF2 HD|||ORF Digital\n33AC:0D95,0D98,0648,0650,09C4,098C|ATV HD|||ORF Digital\n```", .status = 0 }, + { .param = "Multicrypt", .config = "srvid2", .section = "", .text = "For multicrypt channels, list all CAIDs including the management CAID 0000:\n\n```\n0025:0000,1702,1837,1833,09C4,098C,0D05,0D95,0648,0D98,0650|AXN Action|TV||austriasat 19.2°E\n```\n\nThis prevents \"unknown program\" messages in the web interface and shows all CAIDs used by the channel.", .status = 0 }, + { .param = "Finding", .config = "srvid2", .section = "", .text = "Switch to the channel and check the OSCam logs:\n\n```\n2013/10/12 17:45:22 4DB510 c [DVBAPI] Receiver sends PMT command 3 for channel 0025\n2013/10/12 17:45:22 4DB510 c [ADD PID 0] CAID: 1702 ECM_PID: 1725 PROVID: 000000\n2013/10/12 17:45:22 4DB510 c [ADD PID 1] CAID: 1837 ECM_PID: 1FD1 PROVID: 000000\n2013/10/12 17:45:22 4DB510 c [ADD PID 2] CAID: 1833 ECM_PID: 1825 PROVID: 000000\n```\n\nCreate the entry:\n```\n0025:0000,1702,1837,1833,09C4,098C,0D05,0D95,0648,0D98,0650|AXN Action|TV||austriasat 19.2°E\n```", .status = 0 }, + { .param = "Free-to-Air", .config = "srvid2", .section = "", .text = "For FTA channels, use CAID FFFE and provider ID FFFFFE:\n\n```\n2404:FFFE@FFFFFE|Film4|TV||Freesat 28.2°E\n```\n\nNote: FTA CAID changed from 0000 to FFFE, and provider ID changed from 000000 to FFFFFE. Management CAID 0000 is reserved for multicrypt channels.", .status = 0 }, + { .param = "oscam", .config = "srvid2", .section = "", .text = "```\n098C,098D,09F0:007F|Sky|13th Street|TV|Sky Starter Paket\n```", .status = 0 }, + { .param = "oscam", .config = "srvid2", .section = "", .text = "```\n007F:098C,098D,09F0|13th Street|TV|Sky Starter Paket|Sky\n```\n\nThe srvid2 format:\n- Places service ID first (easier to sort by channel)\n- Swaps the position of provider and name fields\n- Otherwise functions identically to oscam.srvid", .status = 0 }, + { .param = "Finding", .config = "srvid2", .section = "", .text = "1. Check OSCam logs for service ID information\n2. Use DVB analysis tools to scan channels\n3. Monitor ECM requests in OSCam logs\n4. Check the web interface status page\n5. Consult online channel databases", .status = 0 }, + { .param = "Extracting", .config = "srvid2", .section = "", .text = "Look for lines like:\n```\n[DVBAPI] Receiver wants to demux srvid 0025 on adapter 0000\n```\n\nThe service ID is 0025 in this example.", .status = 0 }, + { .param = "Organization", .config = "srvid2", .section = "", .text = "- Sort entries by service ID for easy lookup\n- Group related channels together\n- Use comments to separate sections\n- Document special entries", .status = 0 }, + { .param = "Naming", .config = "srvid2", .section = "", .text = "- Use clear, descriptive channel names\n- Include satellite position in provider field\n- Specify channel type (TV, Radio, etc.)\n- Add useful descriptions", .status = 0 }, + { .param = "Maintenance", .config = "srvid2", .section = "", .text = "- Update when channel lineups change\n- Remove obsolete channels\n- Add new channels as they appear\n- Keep synchronized with actual channel availability", .status = 0 }, + { .param = "Example", .config = "srvid2", .section = "", .text = "```\n# Sky Germany SAT - 19.2E (Service IDs 007F-0194)\n007F:098C,098D,09F0|13th Street|TV|Sky Starter Paket|Sky\n0085:098C,098D,09F0|Beate Uhse|TV|Sky Starter Paket|Sky\n\n# Sky Germany Vodafone - Cable (Service IDs 0074-008B)\n0074:098E,1838,1850,1854,1868|Sky Cinema Action HD|TV|Sky Cinema Paket|Sky\n006B:098E,1838,1850,1854,1868|Sky Cinema Classics HD|TV|Sky Cinema Paket|Sky\n\n# HD+ - 19.2E (Service IDs EF10-EF74)\nEF10:1830,1843,1860,186A,186D,09C4,098C,098D|RTL HD|TV||Astra HD+\nEF11:1830,1843,1860,186A,186D,09C4,098C,098D|VOX HD|TV||Astra HD+\n\n# ORF Digital - 19.2E (Service IDs 132F-33AC)\n132F:0D95,0D98,0648,0650,09C4,098C|ORF1 HD|||ORF Digital\n1330:0D95,0D98,0648,0650,09C4,098C|ORF2 HD|||ORF Digital\n```", .status = 0 }, + { .param = "Channel", .config = "srvid2", .section = "", .text = "- Verify file is named oscam.srvid2\n- Check file is in correct configuration directory\n- Ensure Unix line endings (not Windows CRLF)\n- Verify CAID:ProvID:SrvID matches exactly\n- Restart OSCam after changes", .status = 0 }, + { .param = "Wrong", .config = "srvid2", .section = "", .text = "- Check for duplicate entries\n- Verify provider ID priority rules\n- Ensure hexadecimal values are correct\n- Check for typos in CAID/ProvID/SrvID", .status = 0 }, + { .param = "Memory", .config = "srvid2", .section = "", .text = "- Reduce file size by removing unused entries\n- Keep descriptions short\n- Remove unnecessary fields\n- Consider external service ID management", .status = 0 }, + { .param = "enableled", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nLED-Support.\n\n```ini\nenableled = 0 # LED support disabled (default)\nenableled = 1 # LED support enabled for routers\nenableled = 2 # LED support enabled for Qbox HD\n```\n\n**Format:**\n```ini\nenableled = 0|1|2\n```\n\n**Example:**\n```ini\nenableled = 1\n```\n\n**Default:**\n```ini\nenableled = 0\n```", .status = 0 }, + { .param = "disablelog", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nSwitches logfile **off**.\n\n```ini\ndisablelog = 0 # log (default)\ndisablelog = 1 # no log\n```\n\n**Format:**\n```ini\ndisablelog = 0|1\n```\n\n**Example:**\n```ini\ndisablelog = 1\n```\n\n**Default:**\n```ini\ndisablelog = 0\n```", .status = 0 }, + { .param = "loghistorylines", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` or `MODULE_MONITOR` compilation flag)**\n\nNumber of log lines to keep in memory for display in WebIF status page.\n\n**Format:**\n```ini\nloghistorylines = count\n```\n\n**Example:**\n```ini\nloghistorylines = 512\n```\n\n**Default:**\n```ini\nloghistorylines = 256\n```\n\nMinimum value is 64. Values below 64 are adjusted automatically.", .status = 0 }, + { .param = "serverip", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nBind service to specified IP address.\n\n**Example:**\n```ini\nserverip = 192.168.178.1\n```\n\n**Default:**\n```ini\nserverip = # OSCam listens to all IPs\n```", .status = 0 }, + { .param = "logfile", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nLogging target.\n\n**Format:**\n```ini\nlogfile = [filename][;syslog][;stdout]\n```\n\nYou can define a maximum of one filename (not more!) and additionally to log to stdout or syslog (you can also only log to stdout or syslog and omit the filename).\n\n**Opportunities:**\n```ini\nlogfile = /var/log/oscam.log # file or linux device (for example /dev/tty)\nlogfile = syslog # log to syslogd\nlogfile = stdout # showing the log on the console\n```\n\n**Example:**\n```ini\nlogfile = /etc/oscam/logs/oscam.log;stdout\n```\n\n**Default:**\n```ini\nlogfile = /var/log/oscam.log\n```\n\n**Format logfile:**\n```\nCAID&ident/provider/length:checksum\n```", .status = 0 }, + { .param = "initial_debuglevel", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nSet the debuglevel on startup.\n\n**Example:**\n```ini\ninitial_debuglevel = 8\n```\n\n**Default:**\n```ini\ninitial_debuglevel = 0\n```", .status = 0 }, + { .param = "sysloghost", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nIf set, remote syslog server will be activated. Messages will be sent to this IPv4 address. If not set, remote syslog server does not work.\n\n**Format:**\n```ini\nsysloghost = IP_address\n```\n\n**Example:**\n```ini\nsysloghost = 192.168.1.167\n```\n\n**Default:**\n```ini\nsysloghost =\n```", .status = 0 }, + { .param = "syslogport", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nIf set *sysloghost*, remote syslog server will be activated. Messages will be sent to this port. If not set, port 514 is used as default.\n\n**Example:**\n```ini\nsyslogport = 515\n```\n\n**Default:**\n```ini\nsyslogport = 514\n```", .status = 0 }, + { .param = "logduplicatelines", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n```ini\nlogduplicatelines = 0 # enable detection of duplicate lines in logfile (default)\nlogduplicatelines = 1 # disable detection of duplicate lines in log file\n```\n\n**Format:**\n```ini\nlogduplicatelines = 0|1\n```\n\n**Example:**\n```ini\nlogduplicatelines = 1\n```\n\n**Default:**\n```ini\nlogduplicatelines = 0\n```", .status = 0 }, + { .param = "pidfile", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nChange PID file name and path. If default = oscam.pid in /tmp\n\n**Format:**\n```ini\npidfile = path/filename\n```\n\n**Example:**\n```ini\npidfile = /var/run/oscam.pid\n```\n\n**Default:**\n```ini\npidfile =\n```", .status = 0 }, + { .param = "disableuserfile", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nEven if a logfile is switched on (see logfile), the userfile can be turned off!\n\n```ini\ndisableuserfile = 0 # userfile (default)\ndisableuserfile = 1 # no userfile\n```\n\n**Format:**\n```ini\ndisableuserfile = 0|1\n```\n\n**Example:**\n```ini\ndisableuserfile = 1\n```\n\n**Default:**\n```ini\ndisableuserfile = 0\n```", .status = 0 }, + { .param = "disablemail", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nDisable saving NDS Videoguard mail messages from provider.\n\n```ini\ndisablemail = 0 # enabled\ndisablemail = 1 # disabled (default)\n```\n\n**Format:**\n```ini\ndisablemail = 0|1\n```\n\n**Example:**\n```ini\ndisablemail = 0\n```\n\n**Default:**\n```ini\ndisablemail = 1\n```", .status = 0 }, + { .param = "usrfileflag", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nSetting the logging-mode.\n\n```ini\nusrfileflag = 0 # Only login and logout will be logged (default)\nusrfileflag = 1 # Each zapping from client will be logged\n```\n\n**Format:**\n```ini\nusrfileflag = 0|1\n```\n\n**Example:**\n```ini\nusrfileflag = 1\n```\n\n**Default:**\n```ini\nusrfileflag = 0\n```", .status = 0 }, + { .param = "clienttimeout", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nValue in milliseconds for client process to wait for key.\n\n**Examples:**\n```ini\nclienttimeout = 10000 # after 10 seconds timeout\nclienttimeout = 10 # timeout after 10 milliseconds! (see note below)\nclienttimeout = 8 # timeout after 8000 milliseconds (= 8 seconds), see note below\n```\n\n**Default:**\n```ini\nclienttimeout = 5000 # timeout after 5000 milliseconds (= 5 seconds)\n```\n\n⚠️ **Attention:** Values < 10 (so 1 to 9) are multiplied with 1000, so a \"5\" results in 5000 ms.", .status = 0 }, + { .param = "fallbacktimeout", .config = "conf", .section = "global", .text = "**🔵 Parameter depends on setup/program version**\n\nMaximum waiting time of the cardserver in milliseconds for primary reader, before fallback reader (if any!) are questioned.\n\n**Example:**\n```ini\nfallbacktimeout = 1500\n```\n\n**Default:**\n```ini\nfallbacktimeout = 2500\n```\n\n⚠️ **Attention:** The value must be greater than the value in \"serialreadertimeout\", otherwise the default is used.", .status = 0 }, + { .param = "fallbacktimeout_percaid", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nTime falling back to CAID restricted fallback reader.\n\n**Format:**\n```ini\nfallbacktimeout_percaid = milliseconds\n```\n\n**Default:**\n```ini\nfallbacktimeout_percaid = 2500\n```", .status = 0 }, + { .param = "clientmaxidle", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nValue for client process being idle before disconnect in seconds.\n\n**Example:**\n```ini\nclientmaxidle = 200 # Disconnect after 200 seconds\nclientmaxidle = 0 # idle disconnect disabled\n```\n\n**Default:**\n```ini\nclientmaxidle = 120 # Disconnect after 120 seconds\n```", .status = 0 }, + { .param = "bindwait", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nValue in seconds to wait for bind request to complete. If OSCam within this time cannot bind, it finishes with \"Bind Request failed\".\n\nCauses may include:\n- Double assignment of ports\n- \"Crashed\" OSCam has not released the ports again\n- OSCam was started several times\n\n**Example:**\n```ini\nbindwait = 20 # waiting time 20 seconds\n```\n\n**Default:**\n```ini\nbindwait = 120 # waiting time 120 seconds\n```", .status = 0 }, + { .param = "netprio", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nNetwork priority for QoS (Quality of Service). This parameter sets two values:\n\n- **IPP (IP Precedence)**: Applied to `SO_PRIORITY` for system-internal socket prioritization\n- **DSCP (Differentiated Services Code Point)**: Applied to `IP_TOS` / `IPV6_TCLASS` for the TOS field in the IP packet header\n\nThis allows routers and network equipment to prioritize OSCam traffic appropriately.\n\n**Format:**\n```ini\nnetprio = 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20\n```\n\n**Values:**\n\n- **0** (IPP 0, CS0): Best Effort (default)\n- **1** (IPP 1, CS1): Scavenger / Background\n- **2** (IPP 1, AF11): Assured Forwarding (Low Drop)\n- **3** (IPP 1, AF12): Assured Forwarding (Medium Drop)\n- **4** (IPP 1, AF13): Assured Forwarding (High Drop)\n- **5** (IPP 2, CS2): OAM (Operations/Administration/Management)\n- **6** (IPP 2, AF21): Assured Forwarding (Low Drop)\n- **7** (IPP 2, AF22): Assured Forwarding (Medium Drop)\n- **8** (IPP 2, AF23): Assured Forwarding (High Drop)\n- **9** (IPP 3, CS3): Broadcast Video\n- **10** (IPP 3, AF31): Assured Forwarding (Low Drop)\n- **11** (IPP 3, AF32): Assured Forwarding (Medium Drop)\n- **12** (IPP 3, AF33): Assured Forwarding (High Drop)\n- **13** (IPP 4, CS4): Real-Time Interactive\n- **14** (IPP 4, AF41): Assured Forwarding (Low Drop)\n- **15** (IPP 4, AF42): Assured Forwarding (Medium Drop)\n- **16** (IPP 4, AF43): Assured Forwarding (High Drop)\n- **17** (IPP 5, CS5): Signaling\n- **18** (IPP 5, EF): Expedited Forwarding (Voice/Video)\n- **19** (IPP 6, CS6): Network Control\n- **20** (IPP 7, CS7): Reserved (Highest Priority)\n\n**DSCP Classes Explained:**\n- **CS (Class Selector)**: Backward compatible with IP Precedence (CS0-CS7)\n- **AF (Assured Forwarding)**: AFxy where x=class (1-4), y=drop precedence (1=low, 2=medium, 3=high)\n- **EF (Expedited Forwarding)**: Low latency, low jitter - ideal for real-time traffic\n\n**Example:**\n```ini\nnetprio = 18 # EF - Expedited Forwarding, best for time-critical CW delivery\n```\n\n**Default:**\n```ini\nnetprio = 0 # CS0 - Best Effort, no special treatment\n```\n\n⚠️ **Note:** The effectiveness depends on your network equipment supporting and respecting DSCP markings. Many consumer routers ignore these values.", .status = 0 }, + { .param = "sleep", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nTime waiting for inactive users in minutes, can be overwritten per user in oscam.user.\n\n**Example:**\n```ini\nsleep = 5 # waiting time 5 minutes\n```\n\n**Default:**\n```ini\nsleep = # no waiting time\n```", .status = 0 }, + { .param = "unlockparental", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**Only Seca and Viaccess**\n\nUnlock parental mode option to disable PIN code request for adult movie. In order to work properly, this option may require that you define the card PIN code in your card reader configuration.\n\n**Example:**\n```ini\nunlockparental = 1 # parental lock mode disabled\n```\n\n**Default:**\n```ini\nunlockparental = 0 # parental lock mode enabled\n```", .status = 0 }, + { .param = "nice", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**System priority**\n\nOSCam requires almost no CPU-power. But if CPU-power is required, it should be available immediately. Values between 20 and -20 are possible. 20 = low, -20 = very high. If this parameter is commented out (#), this function is not called. Important for example for dbox2, if the error messages \"*relocation error:setpriority*\". See [Priority Scheduling](http://de.wikipedia.org/wiki/Prioritätsscheduling).\n\n**Example:**\n```ini\nnice = -1\n```\n\n**Default:**\n```ini\nnice = 99\n```", .status = 0 }, + { .param = "maxlogsize", .config = "conf", .section = "global", .text = "**🔴 Required parameter**\n\nMaximum size of logfiles in KBytes. This is especially important for servers with only a small amount of memory (dbox2). Therefore it is strongly recommended to determine carefully that size at such servers. Not infrequently a system \"bursts\" because the logfile uncontrolled large scale adopted!\n\n**Example:**\n```ini\nmaxlogsize = 20 # Logfile reaches to 20 KBytes\nmaxlogsize = 0 # Logfile unlimited\n```\n\n**Default:**\n```ini\nmaxlogsize = 10 # Logfile reaches to 10 KBytes\n```", .status = 0 }, + { .param = "waitforcards", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nWait for local SCs on startup before opening network ports.\n\n**Example:**\n```ini\nwaitforcards = 0 # don't wait\n```\n\n**Default:**\n```ini\nwaitforcards = 1 # wait\n```", .status = 0 }, + { .param = "waitforcards_extra_delay", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nAdditional delay in milliseconds after waiting for local SCs on startup before opening network ports.\n\n**Format:**\n```ini\nwaitforcards_extra_delay = milliseconds\n```\n\n**Default:**\n```ini\nwaitforcards_extra_delay = 500\n```", .status = 0 }, + { .param = "preferlocalcards", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nTry decoding on local cards first.\n\n**Example:**\n```ini\npreferlocalcards = 0 # local cards are used like remote reader\npreferlocalcards = 1 # prefer CacheEx sources\npreferlocalcards = 2 # prefer local cards\n```\n\n**Default:**\n```ini\npreferlocalcards = 1 # prefer CacheEx sources\n```", .status = 0 }, + { .param = "readerrestartseconds", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nWaiting time in seconds between restarts.\n\n**Example:**\n```ini\nreaderrestartseconds = 10 # waiting time 10 seconds\nreaderrestartseconds = 0 # no restarts!\n```\n\n**Default:**\n```ini\nreaderrestartseconds = 5 # waiting time 5 seconds\n```", .status = 0 }, + { .param = "dropdups", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nMode for duplicate client connections.\n\n```ini\ndropdups = 0 # mark client as duplicate, but don't disconnect them (default)\ndropdups = 1 # drop duplicate connections instead of marking as duplicate\n```\n\n**Format:**\n```ini\ndropdups = 0|1\n```\n\n**Example:**\n```ini\ndropdups = 1\n```\n\n**Default:**\n```ini\ndropdups = 0\n```", .status = 0 }, + { .param = "SIGHUP", .config = "conf", .section = "global", .text = "The following parameters control what OSCam reloads when it receives a SIGHUP signal. When enabled, sending SIGHUP to the OSCam process will reload the corresponding configuration without requiring a full restart.", .status = 0 }, + { .param = "reload_useraccounts", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nReload user accounts (oscam.user) on SIGHUP signal.\n\n**Format:**\n```ini\nreload_useraccounts = 0|1\n```\n\n**Example:**\n```ini\nreload_useraccounts = 1\n```\n\n**Default:**\n```ini\nreload_useraccounts = 0\n```", .status = 0 }, + { .param = "reload_readers", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nReload readers (oscam.server) on SIGHUP signal.\n\n**Format:**\n```ini\nreload_readers = 0|1\n```\n\n**Example:**\n```ini\nreload_readers = 1\n```\n\n**Default:**\n```ini\nreload_readers = 0\n```", .status = 0 }, + { .param = "reload_provid", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nReload provider IDs (oscam.provid) on SIGHUP signal.\n\n**Format:**\n```ini\nreload_provid = 0|1\n```\n\n**Example:**\n```ini\nreload_provid = 1\n```\n\n**Default:**\n```ini\nreload_provid = 0\n```", .status = 0 }, + { .param = "reload_services_ids", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nReload services IDs (oscam.srvid / oscam.srvid2) on SIGHUP signal.\n\n**Format:**\n```ini\nreload_services_ids = 0|1\n```\n\n**Example:**\n```ini\nreload_services_ids = 1\n```\n\n**Default:**\n```ini\nreload_services_ids = 0\n```", .status = 0 }, + { .param = "reload_tier_ids", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nReload tier IDs (oscam.tiers) on SIGHUP signal.\n\n**Format:**\n```ini\nreload_tier_ids = 0|1\n```\n\n**Example:**\n```ini\nreload_tier_ids = 1\n```\n\n**Default:**\n```ini\nreload_tier_ids = 0\n```", .status = 0 }, + { .param = "reload_fakecws", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nReload fake control words (oscam.fakecws) on SIGHUP signal.\n\n**Format:**\n```ini\nreload_fakecws = 0|1\n```\n\n**Example:**\n```ini\nreload_fakecws = 1\n```\n\n**Default:**\n```ini\nreload_fakecws = 0\n```", .status = 0 }, + { .param = "reload_ac_stat", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nReload anti-cascading statistics on SIGHUP signal.\n\n**Format:**\n```ini\nreload_ac_stat = 0|1\n```\n\n**Example:**\n```ini\nreload_ac_stat = 1\n```\n\n**Default:**\n```ini\nreload_ac_stat = 0\n```", .status = 0 }, + { .param = "reload_log", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nReopen log file on SIGHUP signal. Useful for log rotation.\n\n**Format:**\n```ini\nreload_log = 0|1\n```\n\n**Example:**\n```ini\nreload_log = 1\n```\n\n**Default:**\n```ini\nreload_log = 0\n```", .status = 0 }, + { .param = "block_same_ip", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nReject looping ECMs from clients to reader with the same IP address.\n\n```ini\nblock_same_ip = 0 # no reject\nblock_same_ip = 1 # reject looping ECMs (default)\n```\n\n**Format:**\n```ini\nblock_same_ip = 0|1\n```\n\n**Example:**\n```ini\nblock_same_ip = 0\n```\n\n**Default:**\n```ini\nblock_same_ip = 1\n```", .status = 0 }, + { .param = "block_same_name", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nReject looping ECMs from clients to reader with the same name.\n\n```ini\nblock_same_name = 0 # no reject\nblock_same_name = 1 # reject looping ECMs (default)\n```\n\n**Format:**\n```ini\nblock_same_name = 0|1\n```\n\n**Example:**\n```ini\nblock_same_name = 0\n```\n\n**Default:**\n```ini\nblock_same_name = 1\n```", .status = 0 }, + { .param = "usrfile", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nSet a name for userfile.\n\n**Format of the userfile:**\n```\ndate\ntime\nCWs per second\nusername\nIP address of client\nTCP/IP port\nCWs found\nCWs from cache\nCWs not found\nCWs ignored\nCWs timeout\nCWs tunneled\nlogin time in UNIX/POSIX format\nlogout time in UNIX/POSIX format\nprotocol\n```\n\n**Example:**\n```ini\nusrfile = userfile\n```\n\n**Default:**\n```ini\nusrfile = 0\n```", .status = 0 }, + { .param = "mailfile", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nDefine file saving NDS Videoguard mail messages from provider.\n\n**Default:**\n```ini\nmailfile = # none\n```", .status = 0 }, + { .param = "cwlogdir", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nA path for CW logging can be configured here. The path is relative to the work directory and must exist. Otherwise no logs are created and no errors are reported.\n\nCWs (control words) are the answers of the card which decrypts a certain channel for some seconds before the next CW is transmitted. The CW log file can be used for delayed decoding of this channel. This is also called offline decoding.\n\nOSCam creates a new `*.cwl` file for each channel and each day. The file name contains the date, service ID and service name, e.g., `110808_I000B_Sky_Cinema_+1.cwl` with these contents:\n\n```\n# OSCam cardserver v0.99.4 - http://streamboard.gmc.to:8001/oscam/wiki\n# control word log file for use with tsdec offline decrypter\n# DATE 2011-08-08, TIME 23:49:10, TZ CEST\n# CAID 0x1702, SID 0x000B, SERVICE \"Sky_Cinema_+1\"\n1 9E E3 BB 3C A4 C3 BE 25 # 23:49:10\n0 D0 24 B3 A7 95 82 3B 52 # 23:49:17\n1 A9 76 35 54 37 38 F3 62 # 23:49:23\n0 55 70 87 4C 22 26 C4 0C # 23:49:30\n1 F0 16 22 28 36 3A 03 73 # 23:49:38\n0 5D 83 44 24 8F C7 5D B3 # 23:49:44\n```\n\nAll CWs are logged if at least one user watches the channel. Please choose a path with enough space, since per day and channel some 10KB of data are created. A script that moves/deletes the log files regularly is recommended.\n\n**Example:**\n```ini\ncwlogdir = /var/log/\n```\n\n**Default:**\n```ini\ncwlogdir = # no path for cw-log, logging disabled\n```", .status = 0 }, + { .param = "emmlogdir", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nSetting a path for EMM-log. You can define with the \"saveemm-*\"-Parameters in oscam.server which EMMs should be logged.\n\n**Example:**\n```ini\nemmlogdir = /var/log/\n```\n\n**Default:**\n```ini\nemmlogdir = # the config folder will be used for emm logging\n```", .status = 0 }, + { .param = "lb_mode", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nLoadbalance mode.\n\n```ini\nlb_mode = 0 # loadbalance disabled, ECMs go to all readers (default)\nlb_mode = 1 # fastest reader first, after 5 ECMs the reader with the fastest\n # response time will be selected\nlb_mode = 2 # oldest reader first, reader with the longest no answer\nlb_mode = 3 # lowest usage level, the usage level will be calculated by the\n # sum of 5 ECMs response times, the higher a reader is busy,\n # the higher is usage level\nlb_mode = 10 # LB is switched off, but statistics are built\n```\n\n**Format:**\n```ini\nlb_mode = 0|1|2|3|10\n```\n\n**Example:**\n```ini\nlb_mode = 1\n```\n\n**Default:**\n```ini\nlb_mode = 0\n```", .status = 0 }, + { .param = "lb_save", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nSave autoloadbalance statistics (counts). To save CPU power a minimum count of 100 is recommended.\n\n**Example:**\n```ini\nlb_save = 100 # counts of ECMs after statistics are saved\n```\n\n**Default:**\n```ini\nlb_save = 0 # no save\n```", .status = 0 }, + { .param = "lb_nbest_readers", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nSet count of best readers for loadbalancing.\n\n**Example:**\n```ini\nlb_nbest_readers = 2 # requests always go to two readers\n```\n\n**Default:**\n```ini\nlb_nbest_readers = 1 # requests go to one reader\n```", .status = 0 }, + { .param = "lb_nfb_readers", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nSet count of fallback readers for loadbalancing.\n\n**Example:**\n```ini\nlb_nfb_readers = 2 # two fallback readers\n```\n\n**Default:**\n```ini\nlb_nfb_readers = 1 # one fallback reader\n```", .status = 0 }, + { .param = "lb_min_ecmcount", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nMinimal ECM count to evaluate loadbalancing values.\n\n**Example:**\n```ini\nlb_min_ecmcount = 2 # 2 counts\n```\n\n**Default:**\n```ini\nlb_min_ecmcount = 5 # 5 counts\n```", .status = 0 }, + { .param = "lb_max_ecmcount", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nMaximum ECM count before resetting loadbalancing values.\n\n**Example:**\n```ini\nlb_max_ecmcount = 200 # 200 counts\n```\n\n**Default:**\n```ini\nlb_max_ecmcount = 500 # 500 counts\n```", .status = 0 }, + { .param = "lb_reopen_seconds", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nTime in seconds between retrying failed readers/CAIDs/providers/services.\n\n**Example:**\n```ini\nlb_reopen_seconds = 500 # retry after 500 seconds\n```\n\n**Default:**\n```ini\nlb_reopen_seconds = 900 # retry after 900 seconds\n```", .status = 0 }, + { .param = "lb_reopen_invalid", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\n```ini\nlb_reopen_invalid = 0 # E_INVALID will be blocked until statistics has been cleaned\nlb_reopen_invalid = 1 # default\n```\n\n**Format:**\n```ini\nlb_reopen_invalid = 0|1\n```\n\n**Example:**\n```ini\nlb_reopen_invalid = 0\n```\n\n**Default:**\n```ini\nlb_reopen_invalid = 1\n```", .status = 0 }, + { .param = "lb_force_reopen_always", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nForce reopening immediately all failing readers if no matching reader was found.\n\n```ini\nlb_force_reopen_always = 0 # default\nlb_force_reopen_always = 1 # force reopening immediately\n```\n\n**Format:**\n```ini\nlb_force_reopen_always = 0|1\n```\n\n**Example:**\n```ini\nlb_force_reopen_always = 1\n```\n\n**Default:**\n```ini\nlb_force_reopen_always = 0\n```", .status = 0 }, + { .param = "lb_retrylimit", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nRetry next readers **only** if response time is higher than lb_retrylimit in milliseconds.\n\n**Example:**\n```ini\nlb_retrylimit = 500 # retry next reader only if response time is higher than 500 ms\n```\n\n**Default:**\n```ini\nlb_retrylimit = 800\n```", .status = 0 }, + { .param = "lb_stat_cleanup", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nHours after the loadbalancing statistics will be deleted.\n\n**Default:**\n```ini\nlb_stat_cleanup = 336\n```", .status = 0 }, + { .param = "lb_max_readers", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nRestrict the reader count to limit during learning.\n\n**Format:**\n```ini\nlb_max_readers = limit\n```\n\n```ini\nlb_max_readers = 0 # unlimited (default)\nlb_max_readers = x # restrict loadbalancer readers to limit x (number)\n```", .status = 0 }, + { .param = "lb_auto_betatunnel", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nEnable automatic Betacrypt tunneling for CAIDs 1801, 1833, 1834 and 1835 in loadbalancing mode.\n\n```ini\nlb_auto_betatunnel = 0 # disabled\nlb_auto_betatunnel = 1 # enabled (default)\n```\n\n**Format:**\n```ini\nlb_auto_betatunnel = 0|1\n```\n\n**Example:**\n```ini\nlb_auto_betatunnel = 0\n```\n\n**Default:**\n```ini\nlb_auto_betatunnel = 1\n```\n\n⚠️ **Attention:** Betacrypt definition in oscam.user with betatunnel will be preferred!", .status = 0 }, + { .param = "lb_auto_betatunnel_mode", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nSet mode for automatic Betacrypt tunneling.\n\n```ini\nlb_auto_betatunnel_mode = 0 # CAID 18XX tunneling to CAID 17X2 only (default)\nlb_auto_betatunnel_mode = 1 # CAID 18XX tunneling to CAID 17X2 and CAID 17X2\n # tunneling to CAID 18XX (CAID 1833/1801)\nlb_auto_betatunnel_mode = 2 # CAID 18XX tunneling to CAID 17X2 and CAID 17X2\n # tunneling to CAID 18XX (CAID 1833/1834)\nlb_auto_betatunnel_mode = 3 # CAID 18XX tunneling to CAID 17X2 and CAID 17X2\n # tunneling to CAID 18XX (CAID 1833/1835)\nlb_auto_betatunnel_mode = 4 # CAID 17X2 tunneling to CAID 18XX (CAID 1833/1801 only)\nlb_auto_betatunnel_mode = 5 # CAID 17X2 tunneling to CAID 18XX (CAID 1833/1834 only)\nlb_auto_betatunnel_mode = 6 # CAID 17X2 tunneling to CAID 18XX (CAID 1833/1835 only)\n```\n\n**Format:**\n```ini\nlb_auto_betatunnel_mode = 0|1|2|3|4|5|6\n```\n\n**Example:**\n```ini\nlb_auto_betatunnel_mode = 1\n```\n\n**Default:**\n```ini\nlb_auto_betatunnel_mode = 0\n```", .status = 0 }, + { .param = "lb_auto_betatunnel_prefer_beta", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nSet preference for automatic selection of Betacrypt/Nagravision.\n\n**Format:**\n```ini\nlb_auto_betatunnel_prefer_beta = number\n```\n\n```ini\nlb_auto_betatunnel_prefer_beta = 0 # disabled (default)\nlb_auto_betatunnel_prefer_beta = 1 # always Betacrypt\nlb_auto_betatunnel_prefer_beta = 105 # represents the middle\nlb_auto_betatunnel_prefer_beta = 200 # always Nagravision\n```", .status = 0 }, + { .param = "lb_savepath", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nFilename for saving loadbalancing statistics.\n\n**Default:**\n```ini\nlb_savepath = /tmp/.oscam/stat\n```", .status = 0 }, + { .param = "lb_retrylimits", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nLoadbalancing retry limit time per CAID in milliseconds, wildcard CAIDs with 2-digit CAIDs possible.\n\n**Format:**\n```ini\nlb_retrylimits = CAID1:time1[,CAID2:time2]...\n```\n\n**Example:**\n```ini\nlb_retrylimits = 12:0100,34:0200,5678:0300 # wildcard CAIDs 12xx and 34xx\n```\n\n**Default:** none", .status = 0 }, + { .param = "lb_nbest_percaid", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nSet count of best readers per CAIDs for loadbalancing, wildcard CAIDs with two-digit CAIDs possible.\n\n**Format:**\n```ini\nlb_nbest_percaid = CAID1:count[,CAID2:count]...\n```\n\n**Example:**\n```ini\nlb_nbest_percaid = 0100:4,0200:3,03:2,04:1 # wildcard CAIDs 03xx and 04xx\n```\n\n**Default:** none", .status = 0 }, + { .param = "lb_noproviderforcaid", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nIgnore provider information CAIDs to reduce statistic data, wildcard CAIDs with two-digit CAIDs possible.\n\n**Format:**\n```ini\nlb_noproviderforcaid = CAID1[,CAID2]...\n```\n\n**Example:**\n```ini\nlb_noproviderforcaid = 12,34,5678 # wildcard CAIDs 12xx and 34xx\n```\n\n**Default:** none", .status = 0 }, + { .param = "lb_auto_timeout", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nEnable automatic timeout based on load balancing statistics.\n\n```ini\nlb_auto_timeout = 0 # default\nlb_auto_timeout = 1 # enable automatic timeout\n```\n\n**Format:**\n```ini\nlb_auto_timeout = 0|1\n```\n\n**Example:**\n```ini\nlb_auto_timeout = 1\n```\n\n**Default:**\n```ini\nlb_auto_timeout = 0\n```", .status = 0 }, + { .param = "lb_auto_timeout_p", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nPercent added to average time as timeout time.\n\n**Format:**\n```ini\nlb_auto_timeout_p = percent\n```\n\n**Default:**\n```ini\nlb_auto_timeout_p = 30\n```", .status = 0 }, + { .param = "lb_auto_timeout_t", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_LB` compilation flag)**\n\nMinimal time added to average time as timeout time.\n\n**Format:**\n```ini\nlb_auto_timeout_t = milliseconds\n```\n\n**Default:**\n```ini\nlb_auto_timeout_t = 300\n```", .status = 0 }, + { .param = "double_check_caid", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nECM will be sent to two or more readers with the same SC and the CWs will be verified against each other for defined CAID or first two bytes of CAID. `lb_nbest_readers` must be set to 2 or higher.\n\n**Format:**\n```ini\ndouble_check_caid = [CAID1|first two digits of CAID1],[CAID2|first two digits of CAID2]...\n```\n\n**Default:** none", .status = 0 }, + { .param = "ecmfmt", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nDefine ECM log format.\n\n**Format:**\n```ini\necmfmt = format\n```\n\n**Possible variables:**\n```\nc = CAID\nd = PID\ne = CSP hash\ng = ID of origin gbox peer\nh = checksum\ni = Channel ID\nj = distance of gbox and CCcam hops\nl = length\no = ONID\np = provider ID\ns = service ID\nt = tier ID (this ID will be replaced with the tier-description from oscam.tiers if found)\nw = CW\ny = payload (short, 3 bytes)\nY = payload (long, 6 bytes)\n```\n\nUse a value as prefix to hide variable with this value, control characters will be escaped by \"\\\\\".\n\n**Example:**\n```ini\necmfmt = c&0p/i/d/s/l:h.e_w HOP:j # hide provider ID if 0\n```\n\n**Default:**\n```ini\necmfmt = c&p/i/s/l:h\n```", .status = 0 }, + { .param = "resolvegethostbyname", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nSet mode for DNS resolving. These are different libc calls to resolve DNS names and some older systems have problems with the newer implementation *getaddrinfo* (getaddrinfo is also threadsafe whereas *gethostbyname* is not and thus it has some locks around) or have stripped them from their images (dbox2 was such a candidate) even though it is available at compile time.\n\n```ini\nresolvegethostbyname = 0 # getaddrinfo (default)\nresolvegethostbyname = 1 # gethostbyname\n```\n\n**Format:**\n```ini\nresolvegethostbyname = 0|1\n```\n\n**Example:**\n```ini\nresolvegethostbyname = 1\n```\n\n**Default:**\n```ini\nresolvegethostbyname = 0\n```", .status = 0 }, + { .param = "failbantime", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nTime for IP based blocking for clients with an invalid login attempt in minutes.\n\n**Example:**\n```ini\nfailbantime = 5 # IP will be blocked for 5 minutes\n # New logins are denied\n # No clientthread will be created\n```\n\n**Default:**\n```ini\nfailbantime = 0 # failban off (default)\n```", .status = 0 }, + { .param = "failbancount", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nNumber of allowed failed attempts with incorrect data.\n\n```ini\nfailbancount = 0 # IP is blocked on the first try with incorrect data (default)\nfailbancount = n # number of allowed attempts\n```\n\nIf within **failbantime** more than in **failbancount** allowed trials are registered, the IP for the remainder of failbantime is locked. After a period of failbantime, full failbancount is available again. Failbancount is thus a relaxation of rules!", .status = 0 }, + { .param = "suppresscmd08", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nTell camd35 [cs3.57x]-clients not to request again for rejected CAID, service ID and provider ID combination.\n\n```ini\nsuppresscmd08 = 0 # enabled (default)\nsuppresscmd08 = 1 # disabled\n```\n\n**Format:**\n```ini\nsuppresscmd08 = 0|1\n```\n\n**Example:**\n```ini\nsuppresscmd08 = 1\n```\n\n**Default:**\n```ini\nsuppresscmd08 = 0\n```\n\nCan be overwritten in oscam.user!\n\nMore information: [Streamboard-Thread](http://www.streamboard.tv/wbb2/thread.php?threadid=27459)", .status = 0 }, + { .param = "getblockemmauprovid", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nGet provider ID from CAID for blocking EMM AU updates.\n\n**Format:**\n```ini\ngetblockemmauprovid = 0|1\n```\n\n**Example:**\n```ini\ngetblockemmauprovid = 1\n```\n\n**Default:**\n```ini\ngetblockemmauprovid = 0\n```", .status = 0 }, + { .param = "double_check", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nECM will be sent to two or more readers with the same SC and the CWs will be verified against each other. `lb_nbest_readers` must be set to 2 or higher.\n\n**Example:**\n```ini\ndouble_check = 1 # on\n```\n\n**Default:**\n```ini\ndouble_check = 0 # off\n```", .status = 0 }, + { .param = "disablecrccws", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nSkip CW checksum test globally. Some providers send CWs that don't pass the checksum test but are still valid.\n\n**Format:**\n```ini\ndisablecrccws = 0|1\n```\n\n**Example:**\n```ini\ndisablecrccws = 1\n```\n\n**Default:**\n```ini\ndisablecrccws = 0\n```", .status = 0 }, + { .param = "disablecrccws_only_for", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\nSkip CW checksum test only for specific CAIDs/providers/services. Use this for selective disabling instead of the global `disablecrccws` option.\n\n**Format:**\n```ini\ndisablecrccws_only_for = CAID1[:ident1][,CAID2[:ident2]]...\n```\n\n**Example:**\n```ini\ndisablecrccws_only_for = 0100:000030,0500\n```\n\n**Default:** none", .status = 0 }, + { .param = "cacheex_srcname_webif", .config = "conf", .section = "global", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nDisplay CacheEx source name in WebIF status.\n\n**Format:**\n```ini\ncacheex_srcname_webif = 0|1\n```\n\n**Example:**\n```ini\ncacheex_srcname_webif = 1\n```\n\n**Default:**\n```ini\ncacheex_srcname_webif = 0\n```", .status = 0 }, + { .param = "CW", .config = "conf", .section = "cache", .text = "Cache of CWs which are saved to detect/drop old CWs arriving via CacheEx.\nTo enable this feature, you have to define `cw_cache_size` or `cw_cache_memory` to set the cache-size and `cw_cache_settings` to define for what CWs which action is taken.\nIf `cw_cache_size` or `cw_cache_memory` is defined, the default is that incoming known CWs older than 15 seconds are dropped, because they are useless.", .status = 0 }, + { .param = "ECM", .config = "conf", .section = "cache", .text = "Cache of ECMs which are saved to detect/drop old ECMs.\nIf you are facing problems with getting old ECMs, with this feature you are able to drop known ECMs. To enable this feature, you have to define `ecm_cache_size` or `ecm_cache_memory` to set the cache-size.", .status = 0 }, + { .param = "delay", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\nValue to delay cached requests.\n\n**Example:**\n```ini\ndelay = 300\n```\n\n**Default:**\n```ini\ndelay = 0\n```", .status = 0 }, + { .param = "max_time", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\nMaximum time CWs resist in cache after 1st client request (seconds). The time must be 2 seconds higher than the parameter clienttimeout.\n\n**Format:**\n```ini\nmax_time = seconds\n```\n\n**Default:**\n```ini\nmax_time = 15\n```", .status = 0 }, + { .param = "cw_cache_size", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nCount of max. CWs for cw_cache.\n\n**Default:**\n```ini\ncw_cache_size = 0\n```", .status = 0 }, + { .param = "cw_cache_memory", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nSize (in MB) of the cw_cache.\n\n**Default:**\n```ini\ncw_cache_memory = 0\n```", .status = 0 }, + { .param = "cw_cache_settings", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\n**Format:**\n```ini\ncw_cache_settings = caid[&mask][@provid][$servid]:mode:timediff_old_cw[,n]\n```\n\n**Mode:**\n- `1` = just write logs @ log-level 8192\n- `2` = drop CWs which match this rule & write logs @ log-level 8192\n\n**timediff_old_cw:**\n- `time` = time in milliseconds after a known CW is detected/dropped\n\n**Example:**\n```ini\ncw_cache_settings = 0:2:1500,BEEF@012345:2:620,1337:1:170\n```\n\n- For all CWs not matching any other rule → drop if the CW is known for more than 1500ms\n- Drop CWs of caid BEEF with provid 012345, if they are known for more than 620ms\n- Log CWs of caid 1337 with all provids, if they are known for more than 170ms\n\n**Default:**\n```ini\ncw_cache_settings = # disabled\n```", .status = 0 }, + { .param = "ecm_cache_size", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nCount of max. ECMs for ecm_cache.\n\n**Default:**\n```ini\necm_cache_size = 0\n```", .status = 0 }, + { .param = "ecm_cache_memory", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nSize (in MB) of the ecm_cache.\n\n**Default:**\n```ini\necm_cache_memory = 0\n```", .status = 0 }, + { .param = "ecm_cache_droptime", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nTime in seconds after a known ECM is dropped with return-code E_INVALID.\n\n**Default:**\n```ini\necm_cache_droptime = 0\n```", .status = 0 }, + { .param = "max_hit_time", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX` compilation flag)**\n\nMaximum time for cache exchange hits resists in cache for evaluating wait_time (in seconds).\n\n**Default:**\n```ini\nmax_hit_time = 15\n```", .status = 0 }, + { .param = "wait_time", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX` compilation flag)**\n\nWait time for cache exchange (mode 2 & 3 only!) and Cardserver proxy before sending ECM to reader or proxy.\n\n**Format:**\n```ini\nwait_time = milliseconds\n```\n\n**Example:**\n```ini\nwait_time = 0:50:250,0200@00009X:50:150:950,0500@000001:150:1602&ffdf:1200\n```\n\n**Default:**\n```ini\nwait_time = # none\n```", .status = 0 }, + { .param = "cacheex_mode1_delay", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX` compilation flag)**\n\nDelay in milliseconds for asking cache exchange mode 1 readers.\n\n**Format:**\n```ini\ncacheex_mode1_delay = CAID1:time[,CAID2:time]...\n```\n\n**Default:** none", .status = 0 }, + { .param = "cacheexenablestats", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX` compilation flag)**\n\nEnable statistics for cache exchange mode.\n\n```ini\ncacheexenablestats = 0 # disabled (default)\ncacheexenablestats = 1 # enable statistics\n```\n\n**Format:**\n```ini\ncacheexenablestats = 0|1\n```\n\n**Example:**\n```ini\ncacheexenablestats = 1\n```\n\n**Default:**\n```ini\ncacheexenablestats = 0\n```\n\n⚠️ **Warning:** Please consider memory consumption!", .status = 0 }, + { .param = "cacheex_dropdiffs", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nDrop incoming not-localgenerated-flagged CWs which differ from the first CW.\n\n```ini\ncacheex_dropdiffs = 0 # default\ncacheex_dropdiffs = 1 # drop different CWs\n```\n\n**Format:**\n```ini\ncacheex_dropdiffs = 0|1\n```\n\n**Example:**\n```ini\ncacheex_dropdiffs = 1\n```\n\n**Default:**\n```ini\ncacheex_dropdiffs = 0\n```", .status = 0 }, + { .param = "cacheex_push_lg_groups", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nPush all lg-flagged CWs to this/these group/s, without checking the groupmembership of the peer.\n\n**Format:**\n```ini\ncacheex_push_lg_groups = 1[,n]\n```\n\n**Default:**\n```ini\ncacheex_push_lg_groups = # none\n```", .status = 0 }, + { .param = "cacheex_lg_only_remote_settings", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nPossibility to deny cacheex-receiver/client (cx2: user / cx3: reader) to overwrite the following settings @ cacheex-sending-instance:\n- `cacheex_localgenerated_only`\n- `cacheex_lg_only_tab`\n\nIt makes sense if you want to force your local settings. If the user/reader isn't allowed to overwrite your values:\n- It is still possible for the cacheex-receiver to set `cacheex_localgenerated_only=1`, if the user/reader defines `cacheex_localgenerated_only_in=1`; but it is only @ cacheex-sending-instance possible to disable it\n- It is still possible for the cacheex-receiver to add `cacheex_lg_only_tab`, if the user/reader defines `cacheex_lg_only_in_tab=1234,1337`; but it is only merged to the sender-instance `cacheex_lg_only_tab` values\n\nIf `cacheex_lg_only_remote_settings` @oscam.conf is enabled, the user/reader-settings are ignored. If `cacheex_lg_only_remote_settings` @oscam.conf is disabled, the user/reader-settings are used.\n\n```ini\ncacheex_lg_only_remote_settings = 0 # default (opt-out)\ncacheex_lg_only_remote_settings = 1\n```\n\n**Format:**\n```ini\ncacheex_lg_only_remote_settings = 0|1\n```\n\n**Example:**\n```ini\ncacheex_lg_only_remote_settings = 1\n```\n\n**Default:**\n```ini\ncacheex_lg_only_remote_settings = 0\n```", .status = 0 }, + { .param = "cacheex_localgenerated_only", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nForward only CWs which are flagged as localgenerated. Here in oscam.conf, the default value is set; user/reader-settings are prioritized.\n\n```ini\ncacheex_localgenerated_only = 0 # default (opt-in)\ncacheex_localgenerated_only = 1\n```\n\n**Format:**\n```ini\ncacheex_localgenerated_only = 0|1\n```\n\n**Example:**\n```ini\ncacheex_localgenerated_only = 1\n```\n\n**Default:**\n```ini\ncacheex_localgenerated_only = 0\n```", .status = 0 }, + { .param = "cacheex_localgenerated_only_caid", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nCAID table for localgenerated-only filter. Forward only locally generated CWs for these CAIDs. Deprecated: use `cacheex_lg_only_tab` instead.\n\n**Format:**\n```ini\ncacheex_localgenerated_only_caid = CAID1[,CAID2]...\n```\n\n**Example:**\n```ini\ncacheex_localgenerated_only_caid = 0100,0500,1702\n```\n\n**Default:**\n```ini\ncacheex_localgenerated_only_caid =\n```", .status = 0 }, + { .param = "cacheex_lg_only_tab", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nForward only CWs which are lg-flagged for these list of caid/provid(s). Here in oscam.conf, the default value is set; user/reader-settings are prioritized.\n\nIf provid `FFFFFE` is set, all provids for this caid are valid!\n\n**Format:**\n```ini\ncacheex_lg_only_tab = caid1:provid1[,provid2];caid2:provid1;\n```", .status = 0 }, + { .param = "cacheex_lg_only_in_aio_only", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nUse drop lg-only settings (`cacheex_localgenerated_only_in` & `cacheex_lg_only_in_tab`) only if peer is detected as cx-aio-patched or svn >= 11588. Here in oscam.conf, the default value is set; user/reader-settings are prioritized.\n\n```ini\ncacheex_lg_only_in_aio_only = 0 # default (opt-in)\ncacheex_lg_only_in_aio_only = 1\n```\n\n**Format:**\n```ini\ncacheex_lg_only_in_aio_only = 0|1\n```\n\n**Example:**\n```ini\ncacheex_lg_only_in_aio_only = 1\n```\n\n**Default:**\n```ini\ncacheex_lg_only_in_aio_only = 0\n```", .status = 0 }, + { .param = "cacheex_localgenerated_only_in", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nAccept only CWs which are flagged as localgenerated for incoming CacheEx traffic. This is the global enable/disable switch for the incoming localgenerated-only filter. Works in conjunction with `cacheex_localgenerated_only_in_caid` (deprecated) or `cacheex_lg_only_in_tab`. Here in oscam.conf, the default value is set; user/reader-settings are prioritized.\n\n```ini\ncacheex_localgenerated_only_in = 0 # accept all CWs (default)\ncacheex_localgenerated_only_in = 1 # accept only lg-flagged CWs\n```\n\n**Format:**\n```ini\ncacheex_localgenerated_only_in = 0|1\n```\n\n**Example:**\n```ini\ncacheex_localgenerated_only_in = 1\n```\n\n**Default:**\n```ini\ncacheex_localgenerated_only_in = 0\n```", .status = 0 }, + { .param = "cacheex_localgenerated_only_in_caid", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nCAID table for incoming localgenerated-only filter. Accept only locally generated CWs for these CAIDs. Deprecated: use `cacheex_lg_only_in_tab` instead.\n\n**Format:**\n```ini\ncacheex_localgenerated_only_in_caid = CAID1[,CAID2]...\n```\n\n**Example:**\n```ini\ncacheex_localgenerated_only_in_caid = 0100,0500,1702\n```\n\n**Default:**\n```ini\ncacheex_localgenerated_only_in_caid =\n```", .status = 0 }, + { .param = "cacheex_lg_only_in_tab", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nAllow incoming CWs only if they are lg-flagged for these list of caid/provid(s). Here in oscam.conf, the default value is set; user/reader-settings are prioritized.\n\nIf provid `FFFFFE` is set, all provids for this caid are valid!\n\n**Format:**\n```ini\ncacheex_lg_only_in_tab = caid1:provid1[,provid2];caid2:provid1;\n```", .status = 0 }, + { .param = "cacheex_ecm_filter", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nSet a global cacheex_ecm_filter. Here in oscam.conf, the default value is set; user/reader-settings are prioritized.\n\n**Format:**\n```ini\ncacheex_ecm_filter = [caid][&mask][@provid][$servid],n\n```", .status = 0 }, + { .param = "cacheex_ecm_filter_aio", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nSet a global cacheex_ecm_filter which is used if peer is detected as cx-aio-patched or svn >= 11588. The default limit of 15/30 (camd3/cccam) filters is obsolete.\n\nHere in oscam.conf, the default value is set; user/reader-settings (cacheex_ecm_filter) are prioritized.\n\n**Format:**\n```ini\ncacheex_ecm_filter_aio = [caid][&mask][@provid][$servid],n\n```", .status = 0 }, + { .param = "csp_port", .config = "conf", .section = "cache", .text = "**🔴 Required parameter**\n\n**(requires `CS_CACHEEX` compilation flag)**\n\nThis is a UDP (**not TCP**) listen port for incoming traffic from CSP nodes.\n\n**Example:**\n```ini\ncsp_port = 12345\n```\n\n**Default:**\n```ini\ncsp_port = # none\n```", .status = 0 }, + { .param = "csp_serverip", .config = "conf", .section = "cache", .text = "**🔴 Required parameter**\n\n**(requires `CS_CACHEEX` compilation flag)**\n\nThis is the local bind IP of csp_port.\n\n**Example:**\n```ini\ncsp_serverip = 192.168.1.1\n```\n\n**Default:**\n```ini\ncsp_serverip = # none\n```", .status = 0 }, + { .param = "csp_ecm_filter", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX` compilation flag)**\n\n**Format:**\n```ini\ncsp_ecm_filter = [caid][&mask][@provid][$servid]\n```", .status = 0 }, + { .param = "csp_allow_request", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX` compilation flag)**\n\n```ini\ncsp_allow_request = 0\ncsp_allow_request = 1 # default\n```\n\n**Format:**\n```ini\ncsp_allow_request = 0|1\n```\n\n**Example:**\n```ini\ncsp_allow_request = 0\n```\n\n**Default:**\n```ini\ncsp_allow_request = 1\n```", .status = 0 }, + { .param = "csp_allow_reforward", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX` compilation flag)**\n\nAllow reforwarding of CacheEx CWs via CSP (Cardservproxy protocol).\n\n**Format:**\n```ini\ncsp_allow_reforward = 0|1\n```\n\n**Example:**\n```ini\ncsp_allow_reforward = 1\n```\n\n**Default:**\n```ini\ncsp_allow_reforward = 0\n```", .status = 0 }, + { .param = "cacheex_cw_check", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX` compilation flag)**\n\n**Format:**\n```ini\ncacheex_cw_check = caid[&mask][@provid][$servid]:mode:counter[,n]\n```\n\n**Mode:**\n\nSpecify behaviour for counter parameter:\n- `0` = when wait_time expires, serve highest counter's CW got anyway, even if no counter reached (default)\n- `1` = never serve CW (coming from cacheex) stored in cache if its counter not reaches counter. When wait_time expires, requests will go to normal readers! Only when a CW reaches counter, it can be served to clients.\n\n**Counter:**\n\nSet minimum CW counter to allow CW is used.\n\n**Default:** `1` (use default behaviour: use CW with highest counter when cache is checked)", .status = 0 }, + { .param = "wait_until_ctimeout", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX` compilation flag)**\n\nAnswer when cache exchange timeout expires, if no normal readers are available for sending ECMs.\n\n```ini\nwait_until_ctimeout = 0 # immediately send not found to client (default)\nwait_until_ctimeout = 1 # wait for cache exchange answer until client timeout expires\n```\n\n**Format:**\n```ini\nwait_until_ctimeout = 0|1\n```\n\n**Example:**\n```ini\nwait_until_ctimeout = 1\n```\n\n**Default:**\n```ini\nwait_until_ctimeout = 0\n```", .status = 0 }, + { .param = "csp_block_fakecws", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX` compilation flag)**\n\nBlock fake CWs received via CSP. Fake CWs are control words that match patterns defined in oscam.fakecws.\n\n**Format:**\n```ini\ncsp_block_fakecws = 0|1\n```\n\n**Example:**\n```ini\ncsp_block_fakecws = 1\n```\n\n**Default:**\n```ini\ncsp_block_fakecws = 0\n```", .status = 0 }, + { .param = "cacheex_nopushafter", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nDon't forward CWs from local/proxy-reader via CacheEx, if the defined time_in_ms for the CAID is reached. Here in oscam.conf this setting is for evaluating the response-time for local/proxy-readers and is used for CacheEx2-reader/CacheEx3-user as default, when no reader/user cacheex_nopushafter is set.\n\n**Format:**\n```ini\ncacheex_nopushafter = CAID:time_in_ms[,n]\n```", .status = 0 }, + { .param = "waittime_block_start", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nNumber of (dyn)wait_time timeouts to start blocking the use of (dyn)wait_time for this service (caid:provid:srvid).\n\n**Default:**\n```ini\nwaittime_block_start = 0\n```", .status = 0 }, + { .param = "waittime_block_time", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CS_CACHEEX_AIO` compilation flag)**\n\nSeconds the (dyn)wait_time isn't used and ECMs are forwarded to the reader/s.\n\n**Default:**\n```ini\nwaittime_block_time = 0\n```", .status = 0 }, + { .param = "cwcycle_check_enable", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CW_CYCLE_CHECK` compilation flag)**\n\nEnable CW cycle check.\n\n```ini\ncwcycle_check_enable = 0 # disable (default)\ncwcycle_check_enable = 1 # enable\n```\n\n**Format:**\n```ini\ncwcycle_check_enable = 0|1\n```\n\n**Example:**\n```ini\ncwcycle_check_enable = 1\n```\n\n**Default:**\n```ini\ncwcycle_check_enable = 0\n```", .status = 0 }, + { .param = "cwcycle_check_caid", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CW_CYCLE_CHECK` compilation flag)**\n\n⚠️ **Attention: not for all NDS SCs!**\n\nCAID enabled for CW cycle check.\n\n**Format:**\n```ini\ncwcycle_check_caid = CAID[,CAID]...\n```\n\n**Default:** none", .status = 0 }, + { .param = "cwcycle_maxlist", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CW_CYCLE_CHECK` compilation flag)**\n\nMaximum CW cycle list entries.\n\n**Format:**\n```ini\ncwcycle_maxlist = count\n```\n\n**Default:**\n```ini\ncwcycle_maxlist = 500 # maximum = 4000\n```", .status = 0 }, + { .param = "cwcycle_keeptime", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CW_CYCLE_CHECK` compilation flag)**\n\nMinimum time a learned cycletime resists in memory (minutes).\n\n**Default:**\n```ini\ncwcycle_keeptime = 0 # maximum = 15\n```", .status = 0 }, + { .param = "cwcycle_onbad", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CW_CYCLE_CHECK` compilation flag)**\n\n```ini\ncwcycle_onbad = 0 # log bad CW cycle only\ncwcycle_onbad = 1 # drop bad CW cycle (default)\n```\n\n**Format:**\n```ini\ncwcycle_onbad = 0|1\n```\n\n**Example:**\n```ini\ncwcycle_onbad = 0\n```\n\n**Default:**\n```ini\ncwcycle_onbad = 1\n```", .status = 0 }, + { .param = "cwcycle_dropold", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CW_CYCLE_CHECK` compilation flag)**\n\n```ini\ncwcycle_dropold = 0 # no drop (default)\ncwcycle_dropold = 1 # drop old CW cycle\n```\n\n**Format:**\n```ini\ncwcycle_dropold = 0|1\n```\n\n**Example:**\n```ini\ncwcycle_dropold = 1\n```\n\n**Default:**\n```ini\ncwcycle_dropold = 0\n```", .status = 0 }, + { .param = "cwcycle_sensitive", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CW_CYCLE_CHECK` compilation flag)**\n\n```ini\ncwcycle_sensitive = 0 # disabled (default)\ncwcycle_sensitive = 2 # 2 (or more) same bytes and drop new CW\ncwcycle_sensitive = 3 # 3 (or more) same bytes and drop new CW\ncwcycle_sensitive = 4 # 4 (or more) same bytes and drop new CW\n```\n\n**Format:**\n```ini\ncwcycle_sensitive = 0|2|3|4\n```\n\n**Example:**\n```ini\ncwcycle_sensitive = 3\n```\n\n**Default:**\n```ini\ncwcycle_sensitive = 0\n```", .status = 0 }, + { .param = "cwcycle_allowbadfromffb", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CW_CYCLE_CHECK` compilation flag)**\n\nAllow bad cycles from a fixed fallback reader.\n\n```ini\ncwcycle_allowbadfromffb = 0 # default\ncwcycle_allowbadfromffb = 1 # allow bad cycles from fixed fallback reader\n```\n\n**Format:**\n```ini\ncwcycle_allowbadfromffb = 0|1\n```\n\n**Example:**\n```ini\ncwcycle_allowbadfromffb = 1\n```\n\n**Default:**\n```ini\ncwcycle_allowbadfromffb = 0\n```", .status = 0 }, + { .param = "cwcycle_usecwcfromce", .config = "conf", .section = "cache", .text = "**🟢 Optional parameter**\n\n**(requires `CW_CYCLE_CHECK` compilation flag)**\n\nUse CW info from cache exchange.\n\n```ini\ncwcycle_usecwcfromce = 0 # default\ncwcycle_usecwcfromce = 1 # use CW info from cache exchange\n```\n\n**Format:**\n```ini\ncwcycle_usecwcfromce = 0|1\n```\n\n**Example:**\n```ini\ncwcycle_usecwcfromce = 1\n```\n\n**Default:**\n```ini\ncwcycle_usecwcfromce = 0\n```", .status = 0 }, + { .param = "port", .config = "conf", .section = "newcamd", .text = "**🔴 Required parameter**\n\n**(requires `MODULE_NEWCAMD` compilation flag)**\n\nTCP port/DES key/CAID/ident definitions. Each CAID requires a separate port. If you don't specify a DES key for a port, the default DES Key will be used!\n\n**Format:**\n```ini\nport = port[{DES key}]@CAID[:ident][,ident]...[;port[{DES key}]@CAID[:ident][,ident]...]...\n```\n\n**Example:**\n```ini\nport = 15000@1234:000000\n# CAID 1234 connected with port 15000\n\nport = 15000@1234:000000;15001@5678:000000\n# CAID 1234 connected with port 15000, CAID 5678 with port 15001\n```", .status = 0 }, + { .param = "serverip", .config = "conf", .section = "newcamd", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_NEWCAMD` compilation flag)**\n\nBind service to specified IP address.\n\n**Example:**\n```ini\nserverip = 192.168.178.1\n```\n\n**Default:**\n```ini\nserverip =\n```", .status = 0 }, + { .param = "key", .config = "conf", .section = "newcamd", .text = "**🔴 Required parameter**\n\n**(requires `MODULE_NEWCAMD` compilation flag)**\n\nDES key - Default key for newcamd client encryption.\n\n**Example:**\n```ini\nkey = 0102030405060708091011121314\n```", .status = 0 }, + { .param = "keepalive", .config = "conf", .section = "newcamd", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_NEWCAMD` compilation flag)**\n\nEnable keepalive.\n\n```ini\nkeepalive = 0 # disabled (default)\nkeepalive = 1 # enabled\n```\n\n**Format:**\n```ini\nkeepalive = 0|1\n```\n\n**Example:**\n```ini\nkeepalive = 1\n```\n\n**Default:**\n```ini\nkeepalive = 0\n```", .status = 0 }, + { .param = "mgclient", .config = "conf", .section = "newcamd", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_NEWCAMD` compilation flag)**\n\nEnable mgcamd extended newcamd protocol, allowing for a single connection to work with multiple providers.\n\n```ini\nmgclient = 0 # disabled (default)\nmgclient = 1 # enabled\n```\n\n**Format:**\n```ini\nmgclient = 0|1\n```\n\n**Example:**\n```ini\nmgclient = 1\n```\n\n**Default:**\n```ini\nmgclient = 0\n```", .status = 0 }, + { .param = "port", .config = "conf", .section = "cccam", .text = "**🔴 Required parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nTCP/IP-Ports for CCcam clients, enables CCcam protocol!\n\n**Format:**\n```ini\nport = 0|port[,port...]\n```\n\n**Example:**\n```ini\nport = 12000\nport = 12000,12001\nport = 0 # CCcam protocol disabled (default)\n```", .status = 0 }, + { .param = "serverip", .config = "conf", .section = "cccam", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nBind service to specified IP address.\n\n**Example:**\n```ini\nserverip = 192.168.178.1\n```\n\n**Default:**\n```ini\nserverip =\n```", .status = 0 }, + { .param = "nodeid", .config = "conf", .section = "cccam", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nSet CCcam node ID in hex.\n\n**Format:**\n```ini\nnodeid = ID\n```\n\n**Example:**\n```ini\nnodeid = 0a0b0c0d0e0f1011\n```\n\n**Default:** none", .status = 0 }, + { .param = "version", .config = "conf", .section = "cccam", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nDefine CCcam version, minimum CCcam version 2.0.11, used with original CCcam only.\n\n**Format:**\n```ini\nversion =
      ,,\n```\n\n**Example:**\n```ini\nversion = 2.0.11\n```\n\n**Default:** none", .status = 0 }, + { .param = "reshare", .config = "conf", .section = "cccam", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nReshare level for CCcam clients.\n\n**Format:**\n```ini\nreshare = -1 # no resharing\nreshare = 0 # resharing for direct peer only\nreshare = 1 # resharing for direct peer and next level\nreshare = x # resharing for direct peer and next x level (x for number of level)\nreshare = 10 # default\n```\n\n**Example:**\n```ini\nreshare = 1\n```\n\n**Default:**\n```ini\nreshare = 10\n```\n\nReshare could be defined in 2 ways:\n- reader (`cccreshare`)\n- user-account (`cccreshare`)\n\nA card's reshare value is set to the smallest value of the 2 parameters!", .status = 0 }, + { .param = "reshare_mode", .config = "conf", .section = "cccam", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nCCcam reshare mode.\n\n**Format:**\n```ini\nreshare_mode = 0 # reader reshares only received SCs for CCcam readers,\n # defined filters/CAIDs/idents on other readers (default)\nreshare_mode = 1 # reader reshares received SCs (like=0) and defined services\nreshare_mode = 2 # reader reshares only defined reader services as virtual SCs\nreshare_mode = 3 # reader reshares only defined user services as virtual SCs\nreshare_mode = 4 # reader reshares only received cards\n```\n\n**Example:**\n```ini\nreshare_mode = 1\n```\n\n**Default:**\n```ini\nreshare_mode = 0\n```\n\nEvery server is shared as hop=0 and with defined reshare values.\n\nService reshare only works if positive services defined: no service - no reshare!", .status = 0 }, + { .param = "ignorereshare", .config = "conf", .section = "cccam", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nCCcam reshare setting.\n\n```ini\nignorereshare = 0 # use reshare setting of server (default)\nignorereshare = 1 # use reshare setting of reader or user\n```\n\n**Format:**\n```ini\nignorereshare = 0|1\n```\n\n**Example:**\n```ini\nignorereshare = 1\n```\n\n**Default:**\n```ini\nignorereshare = 0\n```", .status = 0 }, + { .param = "forward_origin_card", .config = "conf", .section = "cccam", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nForward ECM request to reader holding this card, loadbalancer, fallback and caching will be disabled.\n\n```ini\nforward_origin_card = 0 # default\nforward_origin_card = 1 # forward ECM request to reader holding this card\n```\n\n**Format:**\n```ini\nforward_origin_card = 0|1\n```\n\n**Example:**\n```ini\nforward_origin_card = 1\n```\n\n**Default:**\n```ini\nforward_origin_card = 0\n```", .status = 0 }, + { .param = "stealth", .config = "conf", .section = "cccam", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nBehaviour like the original CCcam: no activate partner detection and extended OSCam-CCcam protocol, prevent other OSCam to detect the server as OSCam server.\n\n```ini\nstealth = 0 # stealth disabled (default)\nstealth = 1 # stealth enabled\n```\n\n**Format:**\n```ini\nstealth = 0|1\n```\n\n**Example:**\n```ini\nstealth = 1\n```\n\n**Default:**\n```ini\nstealth = 0\n```", .status = 0 }, + { .param = "updateinterval", .config = "conf", .section = "cccam", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nInterval to provide share list update to CCcam clients in seconds.\n\n**Format:**\n```ini\nupdateinterval = 0 # update based on server updates, values <= 10 are invalid and will be set to 30\nupdateinterval = 240 # default\n```\n\n**Example:**\n```ini\nupdateinterval = 120\n```\n\n**Default:**\n```ini\nupdateinterval = 240\n```", .status = 0 }, + { .param = "minimizecards", .config = "conf", .section = "cccam", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nMode how to provide CCcam servers to CCcam clients.\n\n```ini\nminimizecards = 0 # no aggregation, remove duplicates only (default)\nminimizecards = 1 # based on minimum hop: two SCs with different hops are summarized,\n # new SCs get a smaller hop\n```\n\n**Format:**\n```ini\nminimizecards = 0|1\n```\n\n**Example:**\n```ini\nminimizecards = 1\n```\n\n**Default:**\n```ini\nminimizecards = 0\n```", .status = 0 }, + { .param = "keepconnected", .config = "conf", .section = "cccam", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nSet CCcam keepalive mode.\n\n```ini\nkeepconnected = 0 # disconnect client when max idle time is reached\nkeepconnected = 1 # keep client connected (default)\n```\n\n**Format:**\n```ini\nkeepconnected = 0|1\n```\n\n**Example:**\n```ini\nkeepconnected = 0\n```\n\n**Default:**\n```ini\nkeepconnected = 1\n```", .status = 0 }, + { .param = "recv_timeout", .config = "conf", .section = "cccam", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CCCAM` compilation flag)**\n\nSet network timeout for receiving data.\n\n**Format:**\n```ini\nrecv_timeout = milliseconds\n```\n\n**Default:**\n```ini\nrecv_timeout = 2000\n```", .status = 0 }, + { .param = "port", .config = "conf", .section = "camd33", .text = "**🔴 Required parameter**\n\n**(requires `MODULE_CAMD33` compilation flag)**\n\nTCP/IP port for camd 3.3x clients.\n\n**Example:**\n```ini\nport = 15000\nport = 0 # disabled (default)\n```", .status = 0 }, + { .param = "serverip", .config = "conf", .section = "camd33", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CAMD33` compilation flag)**\n\nBind service to specified IP address.\n\n**Example:**\n```ini\nserverip = 192.168.178.1\n```\n\n**Default:**\n```ini\nserverip = # listening to all\n```", .status = 0 }, + { .param = "nocrypt", .config = "conf", .section = "camd33", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CAMD33` compilation flag)**\n\nUnsecured camd 3.3x client connection.\n\n**Format:**\n```ini\nnocrypt = IP address|IP address range[,IP address|IP address range]...\n```\n\n**Example:**\n```ini\nnocrypt = 127.0.0.1,192.168.0.0-192.168.255.255\n```\n\n**Default:**\n```ini\nnocrypt = # none\n```", .status = 0 }, + { .param = "passive", .config = "conf", .section = "camd33", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CAMD33` compilation flag)**\n\nForce passive camd 3.3x client.\n\nCamd3.3x protocol generally has active clients. In certain circumstances the client must be switched to \"passive\" mode only.\n\n**Example:**\n```ini\npassive = 1\n```\n\n**Default:**\n```ini\npassive = 0\n```", .status = 0 }, + { .param = "key", .config = "conf", .section = "camd33", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CAMD33` compilation flag)**\n\n128 bit key for camd 3.3x client encryption.\n\n**Example:**\n```ini\nkey = 01020304050607080910111213141516\n```\n\n**Default:**\n```ini\nkey = # none\n```", .status = 0 }, + { .param = "port", .config = "conf", .section = "cs378x", .text = "**🔴 Required parameter**\n\n**(requires `MODULE_CAMD35_TCP` compilation flag)**\n\nPort(s) TCP for cs378x clients (camd 3.5x in TCP mode). Port(s) must be opened at router!\n\n**Format:**\n```ini\nport = port@CAID[:ident][,ident]...[;port@CAID[:ident][,ident]...]...\n```\n\n**Example:**\n```ini\nport = 15000@0500:030B00;22764@0648\n```", .status = 0 }, + { .param = "serverip", .config = "conf", .section = "cs378x", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CAMD35_TCP` compilation flag)**\n\nBind service to specified IP address.\n\n**Example:**\n```ini\nserverip = 192.168.178.1\n```\n\n**Default:**\n```ini\nserverip = # all (default)\n```", .status = 0 }, + { .param = "suppresscmd08", .config = "conf", .section = "cs378x", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_CAMD35_TCP` compilation flag)**\n\nTell cs3.78x clients not to request again for rejected CAID, service ID and provider ID combination.\n\n```ini\nsuppresscmd08 = 0 # enabled (default)\nsuppresscmd08 = 1 # disabled\n```\n\n**Format:**\n```ini\nsuppresscmd08 = 0|1\n```\n\n**Example:**\n```ini\nsuppresscmd08 = 1\n```\n\n**Default:**\n```ini\nsuppresscmd08 = 0\n```\n\nMore information: [Streamboard-Thread](http://www.streamboard.tv/wbb2/thread.php?threadid=27459)", .status = 0 }, + { .param = "Minimal", .config = "conf", .section = "gbox", .text = "Let's say the gbox runs on host 192.168.1.101 and OSCam is on 192.168.1.123. In file **cwshare.cfg** on gbox side we have, for example, the following lines:\n\n```\nM: { 192.168.1.101 { A123BCDE }}\nD: { 192.168.1.123 { 3820 3819 { 98AF3E25 {3 3}}}}\n```\n\nIf oscam_gbox should connect to the original GBOX above, then you should configure OSCam as follows:\n\n**oscam.conf:**\n```ini\n[gbox]\nhostname = 192.168.1.123\nmy_password = 98AF3E25\nport = 3819\n```\n\n**oscam.server:**\n```ini\n[reader]\nprotocol = gbox\ngroup = 1\ndevice = 192.168.1.101,3820\npassword = A123BCDE\nuser = gbox_client_1\n```\n\n**oscam.user:**\n```ini\n[account]\nuser = gbox_client_1\ngroup = 1\n```", .status = 0 }, + { .param = "Minimal", .config = "conf", .section = "streamrelay", .text = "The following example shows a basic Streamrelay setup where OSCam connects to a local Enigma2 receiver as stream source:\n\n```ini\n[streamrelay]\nstream_relay_enabled = 1\nstream_relay_port = 17999\nstream_relay_user = streamuser\nstream_source_host = 192.168.1.100\nstream_source_port = 8001\n```\n\nIn this setup:\n- OSCam listens on port 17999 for incoming client connections\n- The stream source is an Enigma2 box at 192.168.1.100:8001\n- The user `streamuser` must be defined in oscam.user\n\nClients can then connect to: `http://oscam-ip:17999/caid:srvid`", .status = 0 }, + { .param = "stream_relay_enabled", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nEnable or disable the Streamrelay server.\n\n```ini\nstream_relay_enabled = 0 # disabled (default)\nstream_relay_enabled = 1 # enabled\n```\n\n**Format:**\n```ini\nstream_relay_enabled = 0|1\n```\n\n**Example:**\n```ini\nstream_relay_enabled = 1\n```\n\n**Default:**\n```ini\nstream_relay_enabled = 0\n```", .status = 0 }, + { .param = "stream_relay_port", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nTCP port on which the Streamrelay server listens for incoming client connections.\n\n**Example:**\n```ini\nstream_relay_port = 17999\n```\n\n**Default:**\n```ini\nstream_relay_port = 17999\n```", .status = 0 }, + { .param = "stream_relay_user", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nUsername for the Streamrelay client. This user must be defined in oscam.user with appropriate permissions. If not set, the first valid user account will be used.\n\n**Example:**\n```ini\nstream_relay_user = streamclient\n```\n\n**Default:** none (uses first available user)", .status = 0 }, + { .param = "stream_relay_ctab", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nCAID table to restrict which CAIDs are handled by Streamrelay. Only streams with matching CAIDs will be descrambled.\n\n**Format:**\n```ini\nstream_relay_ctab = CAID1[,CAID2]...\n```\n\n**Example:**\n```ini\nstream_relay_ctab = 0963,098D,09CD\n```\n\n**Default:** none (CAIDs are detected automatically)", .status = 0 }, + { .param = "stream_source_host", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nIP address or hostname of the stream source server (e.g., Enigma2 receiver, SAT>IP server, or Tvheadend). This is where OSCam fetches the encrypted stream from.\n\n**Example:**\n```ini\nstream_source_host = 192.168.1.100\nstream_source_host = my-enigma2-box.local\n```\n\n**Default:**\n```ini\nstream_source_host = 127.0.0.1\n```", .status = 0 }, + { .param = "stream_client_source_host", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nDetermines how the stream source host is resolved for client connections.\n\n```ini\nstream_client_source_host = 0 # always use stream_source_host\nstream_client_source_host = 1 # use source host from client request if available (default)\n```\n\nWhen enabled, OSCam can use the stream source information provided by the connecting client, allowing more flexible multi-source setups.\n\n**Format:**\n```ini\nstream_client_source_host = 0|1\n```\n\n**Example:**\n```ini\nstream_client_source_host = 0\n```\n\n**Default:**\n```ini\nstream_client_source_host = 1\n```", .status = 0 }, + { .param = "stream_source_port", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nTCP port of the stream source server.\n\n**Example:**\n```ini\nstream_source_port = 8001\n```\n\n**Default:**\n```ini\nstream_source_port = 8001 # for Enigma2-based builds\nstream_source_port = 31339 # for Neutrino-based builds\n```\n\n⚠️ **Note:** The default value depends on the platform OSCam was compiled for.", .status = 0 }, + { .param = "stream_source_auth_user", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nUsername for HTTP Basic Authentication when connecting to the stream source. Required if the stream source server requires authentication.\n\n**Example:**\n```ini\nstream_source_auth_user = admin\n```\n\n**Default:** none", .status = 0 }, + { .param = "stream_source_auth_password", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nPassword for HTTP Basic Authentication when connecting to the stream source. Used together with `stream_source_auth_user`.\n\n**Example:**\n```ini\nstream_source_auth_password = secret123\n```\n\n**Default:** none", .status = 0 }, + { .param = "stream_relay_buffer_time", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nBuffer time in milliseconds before starting to relay the stream. A higher value provides more stable playback but increases latency. Useful when experiencing stream interruptions.\n\n**Format:**\n```ini\nstream_relay_buffer_time = milliseconds\n```\n\n**Example:**\n```ini\nstream_relay_buffer_time = 500 # 500ms buffer\n```\n\n**Default:**\n```ini\nstream_relay_buffer_time = 0 # no additional buffering\n```", .status = 0 }, + { .param = "stream_relay_reconnect_count", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nNumber of reconnection attempts to the stream source if the connection is lost. After this limit is reached, the stream is terminated.\n\n**Example:**\n```ini\nstream_relay_reconnect_count = 5 # try 5 reconnects\n```\n\n**Default:**\n```ini\nstream_relay_reconnect_count = 0 # no automatic reconnect\n```", .status = 0 }, + { .param = "stream_display_client", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nDetermines which address is displayed for Streamrelay connections in the status display and WebIF.\n\n```ini\nstream_display_client = 0 # show stream client address (default)\nstream_display_client = 1 # show stream source host address\n```\n\n**Format:**\n```ini\nstream_display_client = 0|1\n```\n\n**Example:**\n```ini\nstream_display_client = 1\n```\n\n**Default:**\n```ini\nstream_display_client = 0\n```", .status = 0 }, + { .param = "stream_reuse_client", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nReuse existing client connections when the same client reconnects. This can reduce overhead for clients that frequently disconnect and reconnect (e.g., during channel changes).\n\n```ini\nstream_reuse_client = 0 # create new client connection each time (default)\nstream_reuse_client = 1 # reuse existing client connection\n```\n\n**Format:**\n```ini\nstream_reuse_client = 0|1\n```\n\n**Example:**\n```ini\nstream_reuse_client = 1\n```\n\n**Default:**\n```ini\nstream_reuse_client = 0\n```", .status = 0 }, + { .param = "stream_hide_client", .config = "conf", .section = "streamrelay", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` and `WEBIF` compilation flags)**\n\nHide Streamrelay clients from the WebIF status display. Useful to reduce clutter in environments with many stream connections.\n\n```ini\nstream_hide_client = 0 # show clients in WebIF (default)\nstream_hide_client = 1 # hide clients from WebIF\n```\n\n**Format:**\n```ini\nstream_hide_client = 0|1\n```\n\n**Example:**\n```ini\nstream_hide_client = 1\n```\n\n**Default:**\n```ini\nstream_hide_client = 0\n```\n\n⚠️ **Note:** This parameter is only available when OSCam is compiled with WebIF support.", .status = 0 }, + { .param = "extended_cw_api", .config = "conf", .section = "dvbapi", .text = "**🟢 Optional parameter**\n\n**(requires `WITH_EXTENDED_CW` compilation flag)**\n\nAPI for extended control words.\n\n**Format:**\n```ini\nextended_cw_api = 0 # none / disabled (default)\nextended_cw_api = 1 # OE2.2+ (OpenEmbedded 2.2, 2.5)\nextended_cw_api = 2 # OE2.0 (OpenEmbedded 2.0)\n```\n\n**Example:**\n```ini\nextended_cw_api = 1\n```\n\n**Default:**\n```ini\nextended_cw_api = 0\n```", .status = 0 }, + { .param = "demuxer_fix", .config = "conf", .section = "dvbapi", .text = "**🟢 Optional parameter**\n\n**(requires `MODULE_STREAMRELAY` compilation flag)**\n\nFix for demuxer issues related to Stream Relay. Enable this if you experience problems with stream relay and demuxing.\n\n**Format:**\n```ini\ndemuxer_fix = 0|1\n```\n\n**Example:**\n```ini\ndemuxer_fix = 1\n```\n\n**Default:**\n```ini\ndemuxer_fix = 0\n```", .status = 0 }, + { .param = "File", .config = "conf", .section = "webif", .text = "⚠️ **Attention:**\n- File has to be in TXT format\n- The EMM-file should contain each PRE-CAM EMM in a new line\n- EMMs should be in HEX representation:\n - Example: `AABBCCDD...`\n- Spaces and tabs between the bytes are allowed:\n - `AA BB CC DD...`\n- The HEX representation of each EMM's byte can be represented by upper or lower case - even mixed is allowed:\n - `AA aa Aa aA...`\n- Other chars and comments are not allowed", .status = 0 }, + { .param = "httpport", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nPort for web interface.\n\n**Format:**\n```ini\nhttpport = 0 # disabled\nhttpport = port # Webinterface listening on http-port\nhttpport = +port # Webinterface listening on https-port (SSL), prefix + enables SSL\n```\n\n**Example:**\n```ini\nhttpport = 8888\nhttpport = +443\n```\n\n**Default:**\n```ini\nhttpport = 8888\n```", .status = 0 }, + { .param = "httpuser", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nUsername for password protection. Blank is default, which is strongly discouraged!\n\n**Example:**\n```ini\nhttpuser = myusername # all characters and figures are allowed\n```", .status = 0 }, + { .param = "httppwd", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nPassword for password protection.\n\n**Example:**\n```ini\nhttppwd = mypassword\n```\n\n⚠️ **Note:** The *user* and *pwd* parameters are queried when connecting to the webinterface in a message box. If they are missing or corrupted, a message \"Forbidden\" appears and the connection is aborted!", .status = 0 }, + { .param = "httpcss", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nPath for external CSS file.\n\nFor webinterface different styles are available. Put custom CSS files (ending with .css extension) in the oscam config directory to manage your custom styles with WebIf.\n\n**Example:**\n```ini\nhttpcss = /etc/oscam/harvest_round_Firefox.css\n```\n\n**Default:**\n```ini\nhttpcss = # embedded style is used (default)\n```\n\nTo see the current CSS style, use the URL: `http://url_to_oscam_webif/site.css`\n\nStyles can be found here: [Streamboard-Thread](http://www.streamboard.tv/wbb2/thread.php?threadid=30083)", .status = 0 }, + { .param = "httpjscript", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nPath for oscam.js javascript.\n\n**Format:**\n```ini\nhttpjscript = path\n```\n\n**Default:** none", .status = 0 }, + { .param = "httpscript", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nPath to an executable script which you wish to start from web interface.\n\n**Example:**\n```ini\nhttpscript = /path/to/script.sh\n```\n\n**Default:**\n```ini\nhttpscript = # no path\n```\n\nYou can run script file from script.html (SCRIPTS item in main menu). If the script file contains \"echo\", it will be displayed in web interface.\n\n**Error messages:**\n- If the file cannot run: `[Error]: Script \"name_of_script.sh\" not executable!`\n- If the file does not exist: `[Error]: Script \"name_of_script.sh\" not found!`", .status = 0 }, + { .param = "httptpl", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nPath for external templates and picons.\n\nMultiple simultaneously templates and picons are possible by creating sub folders (maximum length of 32 alphanumeric characters). Sub folders naming is corresponding to sub folder in URL.\n\n**Example:**\n```ini\nhttptpl = /this/is/my/path\n\n# Folder with multiple templates:\n# /this/is/my/path/template\n# /this/is/my/path/template2\n\n# Valid URLs:\n# http://host:port/template1\n# http://host:port/template2\n```\n\n**Default:**\n```ini\nhttptpl = # No external templates\n```", .status = 0 }, + { .param = "httppiconpath", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nPath to picon (channel logo) files. Picons are small images displayed in the WebIF to identify channels. The picons should be stored in this directory with specific naming conventions.\n\n**Supported picon naming formats:**\n```\nIC_caid_srvid.tpl\nIC_caid_provid_srvid.tpl\nIC_servicename.tpl\n```\n\n**Service name conversion rules:**\n- Convert all upper-case letters to lower case\n- Replace `*` with `star`\n- Replace `&` with `and`\n- Replace `+` with `plus`\n- Remove everything but a-z and 0-9\n- If service name ends with `hd` and picon is not found, fallback to name without `hd`\n\n**Format:**\n```ini\nhttppiconpath = path\n```\n\n**Example:**\n```ini\nhttppiconpath = /usr/share/oscam/picons\nhttppiconpath = /etc/oscam/picons\n```\n\n**Default:**\n```ini\nhttppiconpath =\n```\n\n⚠️ **Note:** To display picons in the user list, enable `httpshowpicons = 1`.", .status = 0 }, + { .param = "httphelplang", .config = "conf", .section = "webif", .text = "**🔴 Required parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nSet right language for wiki entry point.\n\n**Format:**\n```ini\nhttphelplang = de|en|fr # available wiki languages\n```", .status = 0 }, + { .param = "httplocale", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nSet the \"locale environment\".\n\n**Format:**\n```ini\nhttplocale = environment\n```\n\n**Default:** none\n\nSee: [http://en.wikipedia.org/wiki/locale](http://en.wikipedia.org/wiki/locale)", .status = 0 }, + { .param = "http_prepend_embedded_css", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nAdd embedded CSS before custom CSS.\n\n```ini\nhttp_prepend_embedded_css = 0 # Do not add the content of the embedded CSS\n # before the content of the custom CSS (default)\nhttp_prepend_embedded_css = 1 # embedded CSS will be added before external\n # custom CSS (specified by the httpcss parameter)\n```\n\n**Format:**\n```ini\nhttp_prepend_embedded_css = 0|1\n```\n\n**Example:**\n```ini\nhttp_prepend_embedded_css = 1\n```\n\n**Default:**\n```ini\nhttp_prepend_embedded_css = 0\n```\n\nThis parameter can be used to override the embedded CSS with custom styles. To see the current CSS styles use the URL: `http://url_to_oscam_webif/site.css`", .status = 0 }, + { .param = "httprefresh", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nStatus refresh in seconds.\n\n**Example:**\n```ini\nhttprefresh = 7 # status will be refreshed after 7 seconds\n```\n\n**Default:**\n```ini\nhttprefresh = # no refreshing\n```", .status = 0 }, + { .param = "httppollrefresh", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nRefresh time for polling in seconds.\n\n**Example:**\n```ini\nhttppollrefresh = 60 # polling will be refreshed after 60 seconds\n```\n\n**Default:**\n```ini\nhttppollrefresh = 0 # no refreshing poll\n```", .status = 0 }, + { .param = "httphideidleclients", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nEnables hiding clients after idle time set with parameter **hideclient_to** in section [monitor].\n\n```ini\nhttphideidleclients = 0 # hide not (default)\nhttphideidleclients = 1 # hide\n```\n\n**Format:**\n```ini\nhttphideidleclients = 0|1\n```\n\n**Example:**\n```ini\nhttphideidleclients = 1\n```\n\n**Default:**\n```ini\nhttphideidleclients = 0\n```", .status = 0 }, + { .param = "httphidetype", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nHide types in web interface status page.\n\n**Format:**\n```ini\nhttphidetype = type[type]...\n```\n\nWhere type = letter which defines types to hide (see Typ column):\n- s = server\n- h = http\n- m = monitor\n- r = reader\n- p = proxy\n- x = cacheex\n- c = client\n\n**Example:**\n```ini\nhttphidetype = sh # hide server- and http-type columns\n```\n\n**Default:**\n```ini\nhttphidetype = # hide none (default)\n```", .status = 0 }, + { .param = "httpshowpicons", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nShow picons in user list.\n\n```ini\nhttpshowpicons = 0 # default\nhttpshowpicons = 1 # show picons in userlist\n```\n\nIf this parameter is enabled, then a new column is added to the USERS tab which displays the picons of the last watched channels by active users. The folder where the picons must be configured in the httppiconpath.\n\n**Possible formats are:**\n```\nIC_caid_srvid.tpl\nIC_caid_provid_srvid.tpl\nIC_servicename.tpl\n```\n\nAdded support for picons by channel name:\n- Take service-name from oscam.srvid\n- Replace all upper-case letters with lower case\n- Replace * with star\n- Replace & with and\n- Replace + with plus\n- Remove everything but a-z and 0-9\n- If the result is name, the picon file will be `IC_name.tpl`\n\n**Example:**\n- Service-name: \"BBC-ONE +HD\"\n- Picon file: \"IC_bbconeplushd.tpl\"\n\nIf service-name ends with hd, and picon is not found, fallback to name without hd.", .status = 0 }, + { .param = "httppiconsize", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nHeight of picon images in pixels when displayed in WebIF.\n\n**Format:**\n```ini\nhttppiconsize = pixels\n```\n\n**Example:**\n```ini\nhttppiconsize = 40\n```\n\n**Default:**\n```ini\nhttppiconsize = 0\n```", .status = 0 }, + { .param = "httpshowmeminfo", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nDisplay memory usage information in Status screen.\n\n```ini\nhttpshowmeminfo = 0 # do not show (default)\nhttpshowmeminfo = 1 # display memory usage information in Status screen\n```\n\n**Format:**\n```ini\nhttpshowmeminfo = 0|1\n```\n\n**Example:**\n```ini\nhttpshowmeminfo = 1\n```\n\n**Default:**\n```ini\nhttpshowmeminfo = 0\n```", .status = 0 }, + { .param = "httpshowuserinfo", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nDisplay users information in Status screen.\n\n```ini\nhttpshowuserinfo = 0 # do not show (default)\nhttpshowuserinfo = 1 # display users information in Status screen\n```\n\n**Format:**\n```ini\nhttpshowuserinfo = 0|1\n```\n\n**Example:**\n```ini\nhttpshowuserinfo = 1\n```\n\n**Default:**\n```ini\nhttpshowuserinfo = 0\n```", .status = 0 }, + { .param = "httpshowreaderinfo", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nDisplay readers information in Status screen.\n\n```ini\nhttpshowreaderinfo = 0 # do not show (default)\nhttpshowreaderinfo = 1 # display readers information in Status screen\n```\n\n**Format:**\n```ini\nhttpshowreaderinfo = 0|1\n```\n\n**Example:**\n```ini\nhttpshowreaderinfo = 1\n```\n\n**Default:**\n```ini\nhttpshowreaderinfo = 0\n```", .status = 0 }, + { .param = "httpshowcacheexinfo", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nDisplay CacheEX information in Status screen.\n\n```ini\nhttpshowcacheexinfo = 0 # do not show (default)\nhttpshowcacheexinfo = 1 # display CacheEX information in Status screen\n```\n\n**Format:**\n```ini\nhttpshowcacheexinfo = 0|1\n```\n\n**Example:**\n```ini\nhttpshowcacheexinfo = 1\n```\n\n**Default:**\n```ini\nhttpshowcacheexinfo = 0\n```", .status = 0 }, + { .param = "httpshowecminfo", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nDisplay readers and users ECM information in Status screen.\n\n```ini\nhttpshowecminfo = 0 # do not show (default)\nhttpshowecminfo = 1 # display readers and users ECM information in Status screen\n```\n\n**Format:**\n```ini\nhttpshowecminfo = 0|1\n```\n\n**Example:**\n```ini\nhttpshowecminfo = 1\n```\n\n**Default:**\n```ini\nhttpshowecminfo = 0\n```", .status = 0 }, + { .param = "httpshowloadinfo", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nDisplay load average and CPU usage information in Status screen.\n\n```ini\nhttpshowloadinfo = 0 # do not show (default)\nhttpshowloadinfo = 1 # display load average and CPU usage information in Status screen\n```\n\n**Format:**\n```ini\nhttpshowloadinfo = 0|1\n```\n\n**Example:**\n```ini\nhttpshowloadinfo = 1\n```\n\n**Default:**\n```ini\nhttpshowloadinfo = 0\n```", .status = 0 }, + { .param = "httpallowed", .config = "conf", .section = "webif", .text = "**🔴 Required parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nHTTP web interface connections allowed. Blank is allowed (default), but that makes no sense! If the webinterface is to be used, then there must be a way to connect with it. Therefore we declare this parameter as **mandatory**!\n\nWith **::1** OSCam decides to resolve 'localhost' to **IPv6**!\n\n**Format:**\n```ini\nhttpallowed = IP address|IP address range[,IP address|IP address range,[::1]]...\n```\n\n**Example:**\n```ini\nhttpallowed = 127.0.0.1,192.168.0.0-192.168.255.255\n# Host and range 192.168.0 are allowed\n\nhttpallowed = 127.0.0.1,192.168.0.0-192.168.255.255,::1\n# Host and range 192.168.0 are allowed, resolving to IPv6\n```\n\n**Default:**\n```ini\nhttpallowed = 127.0.0.1,192.168.0.0-192.168.255.255,10.0.0.0-10.255.255.255,::1\n```", .status = 0 }, + { .param = "httpreadonly", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nRead only mode for web interface.\n\n```ini\nhttpreadonly = 0 # all is possible (default)\nhttpreadonly = 1 # only read\n```\n\n**Format:**\n```ini\nhttpreadonly = 0|1\n```\n\n**Example:**\n```ini\nhttpreadonly = 1\n```\n\n**Default:**\n```ini\nhttpreadonly = 0\n```", .status = 0 }, + { .param = "httpsavefullcfg", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nWrite config mode.\n\n```ini\nhttpsavefullcfg = 0 # all not empty parameters, all not default parameters,\n # all parameters not containing the same value as the same\n # parameter in global configuration (default)\nhttpsavefullcfg = 1 # all parameters\n```\n\n**Format:**\n```ini\nhttpsavefullcfg = 0|1\n```\n\n**Example:**\n```ini\nhttpsavefullcfg = 1\n```\n\n**Default:**\n```ini\nhttpsavefullcfg = 0\n```", .status = 0 }, + { .param = "httpoverwritebakfile", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nOverwrite backup configuration files (*.bak).\n\n```ini\nhttpoverwritebakfile = 0 # default\nhttpoverwritebakfile = 1 # overwrite backup configurations files\n```\n\n**Format:**\n```ini\nhttpoverwritebakfile = 0|1\n```\n\n**Example:**\n```ini\nhttpoverwritebakfile = 1\n```\n\n**Default:**\n```ini\nhttpoverwritebakfile = 0\n```", .status = 0 }, + { .param = "httpcert", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nFile for HTTP SSL certificate.\n\n**Example:**\n```ini\nhttpcert = oscam.cert\n```\n\n**Default:**\n```ini\nhttpcert = # file oscam.pem (embedded) is used\n```", .status = 0 }, + { .param = "https_force_secure_mode", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nForce secure HTTPS mode. When enabled, HTTP connections are not allowed.\n\n**Format:**\n```ini\nhttps_force_secure_mode = 0|1\n```\n\n**Example:**\n```ini\nhttps_force_secure_mode = 1\n```\n\n**Default:**\n```ini\nhttps_force_secure_mode = 1\n```", .status = 0 }, + { .param = "https_auto_create_cert", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nAutomatically create SSL certificate if no certificate file is available.\n\n**Format:**\n```ini\nhttps_auto_create_cert = 0|1\n```\n\n**Example:**\n```ini\nhttps_auto_create_cert = 1\n```\n\n**Default:**\n```ini\nhttps_auto_create_cert = 1\n```", .status = 0 }, + { .param = "httpdyndns", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nHTTP web interface connections allowed via hostname.\n\n**Format:**\n```ini\nhttpdyndns = hostname[,hostname][,hostname]\n```\n\n**Example:**\n```ini\nhttpdyndns = host1.example.com,host2.example.com\n```", .status = 0 }, + { .param = "aulow", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nTime no EMM occurs so that client is set to low in minutes.\n\n**Example:**\n```ini\naulow = 30 # After 30 minutes without EMM client will be switched\n # from \"active\" to \"on\" (default)\n```", .status = 0 }, + { .param = "hideclient_to", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nTime to hide clients in the monitor if not sending requests. See also parameter **httphideidleclients** in section [webif].\n\n```ini\nhideclient_to = 0 # no hide\nhideclient_to = 1 # hide clients\n```\n\n**Format:**\n```ini\nhideclient_to = seconds\n```\n\n**Example:**\n```ini\nhideclient_to = 120\n```\n\n**Default:**\n```ini\nhideclient_to = 0\n```", .status = 0 }, + { .param = "httposcamlabel", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nSet individual label in web interface header.\n\n**Format:**\n```ini\nhttposcamlabel = text\n```\n\n**Default:**\n```ini\nhttposcamlabel = OSCam\n```", .status = 0 }, + { .param = "httpemmuclean", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nMaximum size of unique EMM log in kB. When exceeded, older entries are cleaned up.\n\n**Format:**\n```ini\nhttpemmuclean = size_in_kb\n```\n\n**Example:**\n```ini\nhttpemmuclean = 512\n```\n\n**Default:**\n```ini\nhttpemmuclean = 256\n```\n\n`0` = no cleanup", .status = 0 }, + { .param = "httpemmsclean", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nMaximum size of shared EMM log in kB. When exceeded, older entries are cleaned up.\n\n**Format:**\n```ini\nhttpemmsclean = size_in_kb\n```\n\n**Example:**\n```ini\nhttpemmsclean = 256\n```\n\n**Default:**\n```ini\nhttpemmsclean = -1\n```\n\n`-1` = do not show log, `0` = no cleanup", .status = 0 }, + { .param = "httpemmgclean", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag)**\n\nMaximum size of global EMM log in kB. When exceeded, older entries are cleaned up.\n\n**Format:**\n```ini\nhttpemmgclean = size_in_kb\n```\n\n**Example:**\n```ini\nhttpemmgclean = 256\n```\n\n**Default:**\n```ini\nhttpemmgclean = -1\n```\n\n`-1` = do not show log, `0` = no cleanup", .status = 0 }, + { .param = "http_status_log", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF_LIVELOG` compilation flag)**\n\nDisplay log in Status screen.\n\n```ini\nhttp_status_log = 0 # do not show (default)\nhttp_status_log = 1 # display log in Status screen\n```\n\n**Format:**\n```ini\nhttp_status_log = 0|1\n```\n\n**Example:**\n```ini\nhttp_status_log = 1\n```\n\n**Default:**\n```ini\nhttp_status_log = 0\n```", .status = 0 }, + { .param = "http_extern_jquery", .config = "conf", .section = "webif", .text = "**🟢 Optional parameter**\n\n**(requires `WEBIF` compilation flag, only when `WEBIF_JQUERY` is not defined)**\n\nExternal URL for jQuery library. Used when jQuery is not embedded in the WebIF build.\n\n**Format:**\n```ini\nhttp_extern_jquery = URL\n```\n\n**Default:**\n```ini\nhttp_extern_jquery = //code.jquery.com/jquery-3.7.1.min.js\n```", .status = 0 }, + { .param = "Format", .config = "cacheex", .section = "", .text = "```ini\nm:[CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]=\n [CAID][:][provider ID][:][service ID][:][ECM PID][:][CHID][:][ECM length 1[,ECM length 2]...]]\n```", .status = 0 }, + { .param = "Field", .config = "cacheex", .section = "", .text = "| Field | Description | Format | Required |\n|-------|-------------|--------|----------|\n| **m** | Mapping prefix | Fixed | Yes |\n| **CAID** | Conditional Access ID | 4-digit hexadecimal | Optional |\n| **provider ID** | Provider identifier | Hexadecimal | Optional |\n| **service ID** | Service identifier | Hexadecimal | Optional |\n| **ECM PID** | ECM Process ID | Hexadecimal | Optional |\n| **CHID** | Channel ID | Hexadecimal | Optional |\n| **ECM length** | ECM length in bytes | Decimal, comma-separated | Optional |\n| **=** | Separator between source and target | Fixed | Yes |\n\n**Syntax Rules**:\n- Empty fields are represented by consecutive colons (`::`)\n- Multiple ECM lengths are comma-separated (e.g., `93,95`)\n- Left side of `=` is the incoming request (from cache exchange partner)\n- Right side of `=` is the local cache lookup (what to search for locally)", .status = 0 }, + { .param = "Basic", .config = "cacheex", .section = "", .text = "Map CAID 1702 to CAID 1833 for ECM length 93:\n\n```ini\n# Sky Germany S02 to S02-Tunnel\nm:1702:::::93=1833:::::93\n```", .status = 0 }, + { .param = "Multiple", .config = "cacheex", .section = "", .text = "Match multiple ECM lengths for the same CAID mapping:\n\n```ini\n# ORF ICE mapping with multiple lengths\nm:09C4:::::46,58,90,95=09C7:::::46,58,90,95\n```", .status = 0 }, + { .param = "Provider-Specific", .config = "cacheex", .section = "", .text = "Map specific provider IDs:\n\n```ini\n# HD+ with provider ID\nm:1830:000000::::92=1830:003411::::92\n```", .status = 0 }, + { .param = "Service-Specific", .config = "cacheex", .section = "", .text = "Map specific service IDs:\n\n```ini\n# Map service 1234 to 5678\nm:1702::1234:::93=1833::5678:::93\n```", .status = 0 }, + { .param = "Real-World", .config = "cacheex", .section = "", .text = "```ini\n# Sky Germany mappings\nm:1702:::::93=1833:::::93\nm:1702:::::95=1833:::::95\n\n# HD+ mappings\nm:1830:003411::::92=1830:000000::::92\nm:1830:003411::::134=1830:000000::::134\n\n# ORF mappings\nm:0D95:::::144=0648:::::144\nm:0D98:::::144=0650:::::144\n\n# Viaccess mappings\nm:0500:023800::::188=0500:032830::::188\nm:0500:023800::::164=0500:032830::::164\n```", .status = 0 }, + { .param = "Complex", .config = "cacheex", .section = "", .text = "```ini\n# Complete oscam.cacheex file example\n\n# Sky Germany S02 standard lengths\nm:1702:::::93=1833:::::93\nm:1702:::::95=1833:::::95\nm:1702:::::144=1833:::::144\n\n# HD+ provider mapping\nm:1830:003411::::92=1830:000000::::92\nm:1830:003411::::134=1830:000000::::134\nm:1830:003411::::188=1830:000000::::188\n\n# ORF ICE to Cryptoworks mapping\nm:09C4:::::46,58,90,95=09C7:::::46,58,90,95\nm:0D95:::::144=0648:::::144\nm:0D98:::::144=0650:::::144\n\n# Viaccess TNTSAT to CSAT mapping\nm:0500:030B00::::188=0500:032830::::188\nm:0500:023800::::188=0500:032830::::188\n```", .status = 0 }, + { .param = "When", .config = "cacheex", .section = "", .text = "Use oscam.cacheex mappings when:\n- Partners use different CAIDs for the same content\n- You need to translate between tunnel CAIDs (e.g., 1702 ↔ 1833)\n- Provider IDs differ but ECM structure is identical\n- Service IDs need mapping between systems", .status = 0 }, + { .param = "When", .config = "cacheex", .section = "", .text = "You don't need oscam.cacheex if:\n- All partners use the same CAID structure\n- Using CE2 or CE3 modes without CAID translation\n- Simple cache sharing without mapping requirements", .status = 0 }, + { .param = "File", .config = "cacheex", .section = "", .text = "- **Comment Your Mappings**: Use `#` for comments to document each mapping purpose\n- **Test Incrementally**: Add mappings one at a time and verify functionality\n- **Monitor Cache Hits**: Check WebIF statistics to confirm mappings work\n- **Keep It Simple**: Only add mappings you actually need", .status = 0 }, + { .param = "Performance", .config = "cacheex", .section = "", .text = "- Each mapping adds minimal overhead to cache lookups\n- More specific mappings (with service IDs) are faster than generic ones\n- Use ECM length matching when possible to reduce false matches", .status = 0 }, + { .param = "Mapping", .config = "cacheex", .section = "", .text = "**Check the following**:\n1. Verify ECM lengths match exactly (check logs)\n2. Confirm CAID values are correct (4-digit hex)\n3. Ensure provider IDs match if specified\n4. Check that CE1 mode is enabled on the reader\n5. Review cache exchange statistics in WebIF\n\n**Common Issues**:\n- **Wrong ECM Length**: ECM length must match exactly between source and target\n- **Syntax Errors**: Missing colons or incorrect separator (must use `=`)\n- **Case Sensitivity**: CAID values must be uppercase hexadecimal\n- **File Not Loaded**: Check OSCam startup log for oscam.cacheex parsing errors", .status = 0 }, + { .param = "Verification", .config = "cacheex", .section = "", .text = "**Log Analysis**:\n```\n# Enable debug logging in oscam.conf\n[global]\nlogfile = /var/log/oscam/oscam.log\ndisablelog = 0\n```\n\nLook for cache exchange activity in logs showing CAID translation.\n\n**WebIF Statistics**:\n- Navigate to Status → Cache Exchange\n- Check \"ECM-CW received\" counters\n- Verify mapped CAIDs appear in statistics\n\n**Test Procedure**:\n1. Add mapping to oscam.cacheex\n2. Restart OSCam (or reload config if supported)\n3. Monitor logs during channel change\n4. Verify cache hit for mapped CAID\n5. Check that correct CW is returned", .status = 0 }, + { .param = "Syntax", .config = "cacheex", .section = "", .text = "**Valid Examples**:\n```ini\nm:1702:::::93=1833:::::93 # Valid: Basic CAID mapping\nm:1830:003411::::92=1830:000000::::92 # Valid: Provider mapping\nm:09C4:::::46,58,90=09C7:::::46,58,90 # Valid: Multiple lengths\n```\n\n**Invalid Examples**:\n```ini\nm:1702:::93=1833:::93 # Invalid: Missing colons\n1702:::::93=1833:::::93 # Invalid: Missing 'm:' prefix\nm:1702:::::93-1833:::::93 # Invalid: Wrong separator (use =)\nm:1702:::::93= # Invalid: Missing target\n```", .status = 0 }, + { .param = "Field", .config = "cacheex", .section = "", .text = "The field order is strict and must follow this pattern:\n```\nm:[CAID]:[ProvID]:[SrvID]:[PID]:[CHID]:[Length]=[CAID]:[ProvID]:[SrvID]:[PID]:[CHID]:[Length]\n └─1──┘ └──2──┘ └──3──┘ └─4─┘ └─5──┘ └──6───┘ └─1──┘ └──2──┘ └──3──┘ └─4─┘ └─5──┘ └──6───┘\n```", .status = 0 }, + { .param = "Comments", .config = "cacheex", .section = "", .text = "Comments can be added using `#`:\n```ini\n# This is a comment\nm:1702:::::93=1833:::::93 # Inline comment\n```", .status = 0 }, + { .param = "oscam", .config = "cacheex", .section = "", .text = "- `cacheexwaittime` - Wait time for CE1 response\n- `max_cache_time` - Cache retention time\n- `max_cache_count` - Maximum ECMs in cache\n\nSee the oscam.conf documentation for complete cache exchange settings.", .status = 0 }, + { .param = "oscam", .config = "cacheex", .section = "", .text = "- `cacheex` - Cache exchange mode (1, 2, or 3)\n- `cacheex_maxhop` - Maximum hops for cache forwarding\n- `cacheex_ecm_filter` - Filter which CAIDs are exchanged\n\nSee the oscam.server documentation for reader-specific cache exchange parameters.", .status = 0 }, + { .param = "oscam", .config = "cacheex", .section = "", .text = "- `cacheex` - User cache exchange permission\n- `cacheex_maxhop` - Maximum hops for this user\n\nSee the oscam.user documentation for user cache exchange settings.", .status = 0 }, + { .param = "Parameters", .config = "provid", .section = "", .text = "- **caid**: Conditional Access Identification in hexadecimal (e.g., 0100, 0500, 1702)\n- **provid**: Provider ID in hexadecimal (e.g., 000000, 003411, 000401)\n- **provider**: Human-readable provider name (e.g., \"Sky Germany\", \"Canal+\")\n- **satellite**: Satellite position or transmission method (e.g., \"19.2E\", \"Astra\", \"Cable\")\n- **language**: Language code or description (e.g., \"de\", \"en\", \"fr\")", .status = 0 }, + { .param = "Basic", .config = "provid", .section = "", .text = "```\n0100:012345|MyPay-TV|Astra 19E|German\n```\n\nThis maps CAID 0100 with Provider ID 012345 to \"MyPay-TV\" on Astra 19.2E in German.", .status = 0 }, + { .param = "Real-World", .config = "provid", .section = "", .text = "```\n# Sky Germany\n1702:000000|Sky Germany S02|19.2E|de\n1833:000401|Sky Germany HD+|19.2E|de\n\n# Canal+\n0100:003311|Canal+ France|19.2E|fr\n0100:003315|Canal+ Espana|19.2E|es\n\n# HD+\n1830:003411|HDplus HD01|19.2E|de\n\n# ORF\n0D05:000000|ORF|19.2E|de\n0D05:000004|ORF ICE|19.2E|de\n\n# Mediaset\n0100:00000B|Mediaset Italy|13E|it\n```", .status = 0 }, + { .param = "With", .config = "provid", .section = "", .text = "```\n# oscam.provid\n# Provider table for OSCam\n# Format: :|||\n\n# German Providers\n1702:000000|Sky Germany S02|19.2E|de\n1830:003411|HDplus HD01|19.2E|de\n\n# French Providers\n0100:003311|Canal+ France|19.2E|fr\n0500:032830|Fransat|5W|fr\n```", .status = 0 }, + { .param = "Sky", .config = "provid", .section = "", .text = "```\n1702:000000|Sky Germany S02|19.2E|de\n1833:000401|Sky Germany NDS|19.2E|de\n09C4:000000|Sky Germany V13|19.2E|de\n```", .status = 0 }, + { .param = "HD", .config = "provid", .section = "", .text = "```\n1830:003411|HDplus HD01|19.2E|de\n1843:003411|HDplus HD02|19.2E|de\n```", .status = 0 }, + { .param = "Canal", .config = "provid", .section = "", .text = "```\n0100:003311|Canal+ France|19.2E|fr\n0100:003315|Canal+ Espana|19.2E|es\n0100:003317|Canal+ Polska|13E|pl\n```", .status = 0 }, + { .param = "ORF", .config = "provid", .section = "", .text = "```\n0D05:000000|ORF|19.2E|de\n0D05:000004|ORF ICE|19.2E|de\n0D95:000000|ORF|19.2E|de\n0D95:000004|ORF ICE|19.2E|de\n```", .status = 0 }, + { .param = "Viaccess", .config = "provid", .section = "", .text = "```\n0500:032830|Fransat|5W|fr\n0500:042400|SRG Swiss|13E|de/fr/it\n0500:050F00|Bis TV|19.2E|fr\n```", .status = 0 }, + { .param = "Finding", .config = "provid", .section = "", .text = "1. Check OSCam logs for CAID:ProvID values\n2. Look at reader statistics in the web interface\n3. Consult online provider databases\n4. Use card information tools", .status = 0 }, + { .param = "Adding", .config = "provid", .section = "", .text = "1. Identify the CAID:ProvID from logs or web interface\n2. Research the provider name and details\n3. Add a new line with the appropriate format\n4. Restart OSCam or reload configuration\n5. Verify the display in web interface", .status = 0 }, + { .param = "Organization", .config = "provid", .section = "", .text = "- Group providers by country or encryption system\n- Use comments to separate sections\n- Keep entries alphabetically sorted within sections\n- Include satellite position for clarity", .status = 0 }, + { .param = "Naming", .config = "provid", .section = "", .text = "- Use clear, descriptive provider names\n- Include card generation if applicable (S02, HD01, etc.)\n- Specify satellite position in standard format (19.2E, 13E, etc.)\n- Use standard language codes (de, en, fr, es, it, etc.)", .status = 0 }, + { .param = "Maintenance", .config = "provid", .section = "", .text = "- Update entries when providers change\n- Remove obsolete providers marked as \"(Old)\"\n- Add new providers as they become available\n- Keep the file synchronized across your OSCam instances", .status = 0 }, + { .param = "Parameters", .config = "ird", .section = "", .text = "- **byte3**: The value of byte 3 in the ECM signature (hexadecimal)\n- **byte4-7**: The values of bytes 4 through 7 in the ECM signature (hexadecimal)\n- **CAID**: The Conditional Access Identification number (hexadecimal)\n- **SID**: The Service ID (hexadecimal)", .status = 0 }, + { .param = "Irdeto", .config = "ird", .section = "", .text = "The primary use case is with Irdeto conditional access systems:\n- Irdeto 1 (CAID 0x0600)\n- Irdeto 2 (CAID 0x0602, 0x0604, 0x0606, 0x0608, etc.)\n- Various Irdeto providers with different signature patterns", .status = 0 }, + { .param = "Automatic", .config = "ird", .section = "", .text = "When working with Irdeto cards or emulators where CAID needs to be determined from ECM signatures rather than being explicitly provided.", .status = 0 }, + { .param = "Empty", .config = "ird", .section = "", .text = "Even if you don't need signature mapping, create an empty file to suppress warnings:\n\n```bash\ntouch oscam.ird\n```", .status = 0 }, + { .param = "Signature", .config = "ird", .section = "", .text = "To populate this file, you typically need to:\n1. Analyze ECM messages from your Irdeto provider\n2. Extract the signature bytes from positions 3-7\n3. Determine the correct CAID and SID for each signature\n4. Add entries to the file", .status = 0 }, + { .param = "Testing", .config = "ird", .section = "", .text = "After adding entries:\n1. Restart OSCam\n2. Monitor logs for CAID detection\n3. Verify that channels open correctly\n4. Adjust mappings if needed", .status = 0 }, + { .param = "Parameters", .config = "ac", .section = "", .text = "- **CAID**: Conditional Access Identification - numeric identifier for the encryption provider\n- **provider ID**: Numeric identifier for the Pay TV provider\n- **seconds**: Time interval in seconds defining the minimum time between control word changes\n\nThe asterisk (*) can be used to define a default value for all providers not explicitly listed.", .status = 0 }, + { .param = "Basic", .config = "ac", .section = "", .text = "```\n0100:000000=10\n*=7\n```\n\nFor CAID 0100 with provider 000000, the time interval is set to 10 seconds. For all other CAIDs not explicitly defined, the default interval is 7 seconds.", .status = 0 }, + { .param = "Sky", .config = "ac", .section = "", .text = "```\n1702:000000 = 7 # Sky S02 - 7 second interval\n1833:000401 = 7 # HD+ - 7 second interval\n*=7 # Default 7 seconds for all other providers\n```", .status = 0 }, + { .param = "Alternative", .config = "ac", .section = "", .text = "```\n1702:000000 = 10 # Sky S02 - 10 second interval\n1833:000401 = 10 # HD+ - 10 second interval\n*=7 # Default 7 seconds for all other providers\n```", .status = 0 }, + { .param = "Multiple", .config = "ac", .section = "", .text = "```\n0100:000000=10 # Provider 1 - 10 seconds\n0200:000001=8 # Provider 2 - 8 seconds\n0300:000002=12 # Provider 3 - 12 seconds\n*=7 # Default for all others\n```", .status = 0 }, + { .param = "Choosing", .config = "ac", .section = "", .text = "- Typical intervals range from 7 to 10 seconds\n- Shorter intervals (5-7 seconds) provide stricter anti-cascading protection\n- Longer intervals (10-15 seconds) are more lenient but may miss some cascading attempts\n- Consider the normal channel switching behavior of legitimate users", .status = 0 }, + { .param = "Configuration", .config = "ac", .section = "", .text = "1. Start with a default value using the wildcard (*) entry\n2. Add specific entries for high-value providers that need stricter control\n3. Test the configuration to ensure legitimate users aren't affected\n4. Monitor logs for potential cascading attempts", .status = 0 }, + { .param = "Common", .config = "ac", .section = "", .text = "- **1702:000000**: Sky Germany (S02 cards)\n- **1833:000401**: HD+ Germany\n- **0100:000000**: Seca/Mediaguard systems\n- **0500:000000**: Viaccess systems\n- **0D95:000000**: ORF ICE", .status = 0 }, + { .param = "Required", .config = "services", .section = "", .text = "All parameters are required for each service definition:\n\n- **[service name]**: Unique name for the service group (section header)\n- **caid**: One or more CAIDs in hexadecimal, comma-separated\n- **provid**: One or more Provider IDs in hexadecimal, comma-separated\n- **srvid**: One or more Service IDs in hexadecimal, comma-separated", .status = 0 }, + { .param = "Optional", .config = "services", .section = "", .text = "These parameters were added in SVN revision 11588 (August 23, 2020):\n\n- **disablecrccws_only_for_exception**: Define service as exception for disablecrccws_only_for settings (0|1, default: 0)\n- **no_wait_time**: Define service as wait_time exception (0|1, default: 0)\n- **lg_only_exception**: Define service as localgenerated-only exception for cache exchange (0|1, default: 0)", .status = 0 }, + { .param = "HD", .config = "services", .section = "", .text = "```\n[hdplus]\ncaid = 1830,1843,1860,186A,186D\nprovid =\nsrvid = 0002,126E,126F,1519,151A,157C,157F,1581,183D,2774,2E9B,2EAF,307A,30D6,5274,6FEC,6FEE,EF10,EF11,EF14,EF15,EF16,EF17,EF74,EF75,EF76,EF77,EF78,EF79,EF7A,07E4,07D0,07EE,07F8\n```", .status = 0 }, + { .param = "Sky", .config = "services", .section = "", .text = "```\n[SAT-Sky-Starter-Paket]\ncaid = 098C,098D,09F0\nprovid =\nsrvid = 007F,0085,0194,0192,0016,0071,0191,00A8,0082,0077,0206,000D,0070,0017,0076,0093,007C,008E,006C,007E,0065,0088,007B\n```", .status = 0 }, + { .param = "Sky", .config = "services", .section = "", .text = "```\n[SAT-Sky-Sport-UHD]\ncaid = 098C,098D,09F0\nprovid =\nsrvid = 0228,0229\n```", .status = 0 }, + { .param = "Sky", .config = "services", .section = "", .text = "```\n[UM-Sky-Sport-Paket]\ncaid = 098E,1838,1850,1854,1868\nprovid =\nsrvid = 010C,0116,0120,012A,0134,013E,0148,0152,0102,010D,0011,0091,0090,008D,0072,0081\n```", .status = 0 }, + { .param = "ORF", .config = "services", .section = "", .text = "```\n[orfcw]\ncaid = 0D95,0D98\nprovid =\nsrvid = 32C9,32CA,32CB,32CC,32CD,32CE,32CF,32D0,32D1,32D2,32D3,32D6,32D4,33A7,4E27,33A5,332D,3337,132F,1330,33AC,33FD,1334,33FA,33FB,33FC,33F5,33F6,33F7,33F8,33F9,1331,33FC,14B8\n```", .status = 0 }, + { .param = "With", .config = "services", .section = "", .text = "```\n[special_service]\ncaid = 0100\nprovid = 000001\nsrvid = 5000,5001\ndisablecrccws_only_for_exception = 1\nno_wait_time = 1\nlg_only_exception = 1\n```\n\nThis service is configured with all exception flags enabled for special handling.", .status = 0 }, + { .param = "Separate", .config = "services", .section = "", .text = "```\n[sky_sport_channels]\ncaid = 098C,098D,09F0\nprovid =\nsrvid = 0228,0229,010C,0116\n\n[sky_cinema_channels]\ncaid = 098E,1838,1850,1854,1868\nprovid =\nsrvid = 0074,006B,008B\n\n[hd_basic_channels]\ncaid = 1830,1843,1860,186A,186D\nprovid =\nsrvid = EF10,EF11,EF74,EF75\n```", .status = 0 }, + { .param = "In", .config = "services", .section = "", .text = "```\n[account]\nuser = john\npwd = password\nservices = hdplus,SAT-Sky-Starter-Paket\n```\n\nUser \"john\" can only access channels defined in hdplus and SAT-Sky-Starter-Paket service groups.", .status = 0 }, + { .param = "In", .config = "services", .section = "", .text = "```\n[reader]\nlabel = myreader\nprotocol = internal\ndevice = /dev/sci0\nservices = orfcw,hdplus\n```\n\nThis reader is restricted to only serve channels defined in the orfcw and hdplus service groups.", .status = 0 }, + { .param = "Naming", .config = "services", .section = "", .text = "- Use descriptive, meaningful service names\n- Use lowercase with underscores (e.g., sky_de_sport)\n- Group related channels logically\n- Keep names short but clear", .status = 0 }, + { .param = "Organization", .config = "services", .section = "", .text = "- Group services by provider or package\n- Separate by content type (sports, movies, kids, etc.)\n- Use comments to document service groups\n- Keep related services together in the file", .status = 0 }, + { .param = "Maintenance", .config = "services", .section = "", .text = "- Document which channels belong to each service\n- Update service definitions when channel lineups change\n- Remove obsolete service definitions\n- Test service restrictions after changes", .status = 0 }, + { .param = "Performance", .config = "services", .section = "", .text = "- Stay within the 64-service limit per reader\n- Combine related channels into single services when possible\n- Avoid overly granular service definitions\n- Balance between flexibility and simplicity", .status = 0 }, + { .param = "User", .config = "services", .section = "", .text = "- Verify the service is assigned to the user in oscam.user\n- Check that the service definition includes the correct CAID/ProvID/SrvID\n- Ensure service names match exactly (case-sensitive)\n- Verify the reader has access to the service", .status = 0 }, + { .param = "Service", .config = "services", .section = "", .text = "- Check for typos in service names\n- Verify hexadecimal values are correct\n- Ensure all required parameters are present\n- Check for duplicate service names", .status = 0 }, + { .param = "64-Service", .config = "services", .section = "", .text = "- Consolidate similar services\n- Remove unused service definitions\n- Use broader service definitions\n- Consider splitting across multiple readers", .status = 0 }, + { .param = "Parameters", .config = "srvid", .section = "", .text = "- **CAID**: One or more CAIDs in hexadecimal (max 10 per line)\n- **@PROVID**: Optional provider ID(s) in hexadecimal\n- **service ID**: Service ID (channel ID) in hexadecimal\n- **provider**: Provider or satellite position (optional)\n- **name**: Channel name (optional)\n- **type**: Channel type like TV, Radio, etc. (optional)\n- **description**: Additional description (optional)", .status = 0 }, + { .param = "Simple", .config = "srvid", .section = "", .text = "```\n0001,0002,0003:000a|my provider 1|tv name|tv|my tv package\n0004,0005,0006:000a|my provider 2|radio name 2|radio|my radio package\n0006:000b|my provider 3|tv name 3|\n```", .status = 0 }, + { .param = "CAID", .config = "srvid", .section = "", .text = "```\n# CAID 0100, all provider IDs, service ID 1111\n0100:1111:x|y|z|v\n```", .status = 0 }, + { .param = "CAID", .config = "srvid", .section = "", .text = "```\n# CAID 0100, provider ID 123456, service ID 1111\n0100@123456:1111:x|y|z|v\n```", .status = 0 }, + { .param = "CAID", .config = "srvid", .section = "", .text = "```\n# CAID 0100, all providers, service ID 1111\n0100@000000:1111:x|y|z|v\n```", .status = 0 }, + { .param = "Multiple", .config = "srvid", .section = "", .text = "```\n# CAID 0100 with provider 123456 AND CAID 0200 with provider 654321, both for service ID 1111\n0100@123456,0200@654321:1111:x|y|z|v\n```", .status = 0 }, + { .param = "Sky", .config = "srvid", .section = "", .text = "**Sky SAT:**\n\n```\n098C,098D,09F0:007F|Sky|13th Street|TV|Sky Starter Paket\n098C,098D,09F0:0085|Sky|Beate Uhse|TV|Sky Starter Paket\n098C,098D,09F0:0194|Sky|Cartoon Network|TV|Sky Starter Paket\n```\n\n**Sky Vodafone:**\n\n```\n098E,1838,1850,1854,1868:0074|Sky|Sky Cinema Action HD|TV|Sky Cinema Paket\n098E,1838,1850,1854,1868:006B|Sky|Sky Cinema Classics HD|TV|Sky Cinema Paket\n098E,1838,1850,1854,1868:008B|Sky|Sky Cinema Family HD|TV|Sky Cinema Paket\n```", .status = 0 }, + { .param = "HD", .config = "srvid", .section = "", .text = "```\n1830,1843,1860,186A,186D,09C4,098C,098D:EF10|Astra HD+|RTL HD|TV|\n1830,1843,1860,186A,186D,09C4,098C,098D:EF11|Astra HD+|VOX HD|TV|\n1830,1843,1860,186A,186D,09C4,098C,098D:EF74|Astra HD+|SAT.1 HD|TV|\n```", .status = 0 }, + { .param = "ORF", .config = "srvid", .section = "", .text = "```\n0D95,0D98,0648,0650,09C4,098C:132F|ORF Digital|ORF1 HD\n0D95,0D98,0648,0650,09C4,098C:1330|ORF Digital|ORF2 HD\n0D95,0D98,0648,0650,09C4,098C:33AC|ORF Digital|ATV HD\n```", .status = 0 }, + { .param = "Multicrypt", .config = "srvid", .section = "", .text = "For multicrypt channels, you can list all CAIDs used by the channel, including the management CAID 0000:\n\n```\n0000,1702,1837,1833,09C4,098C,0D05,0D95,0648,0D98,0650:0025|austriasat 19.2°E|AXN Action|TV|\n```\n\nThis prevents \"unknown program\" messages in the web interface and makes it easy to see all CAIDs used by a channel.", .status = 0 }, + { .param = "Finding", .config = "srvid", .section = "", .text = "Switch to the channel and check the OSCam logs:\n\n```\n2013/10/12 17:45:22 4DB510 c [DVBAPI] Receiver sends PMT command 3 for channel 0025\n2013/10/12 17:45:22 4DB510 c [ADD PID 0] CAID: 1702 ECM_PID: 1725 PROVID: 000000\n2013/10/12 17:45:22 4DB510 c [ADD PID 1] CAID: 1837 ECM_PID: 1FD1 PROVID: 000000\n2013/10/12 17:45:22 4DB510 c [ADD PID 2] CAID: 1833 ECM_PID: 1825 PROVID: 000000\n```\n\nUse these CAIDs in your oscam.srvid entry.", .status = 0 }, + { .param = "Free-to-Air", .config = "srvid", .section = "", .text = "For FTA channels, use CAID FFFE:\n\n```\nFFFE:2404|Freesat 28.2°E|Film4|TV|Movies|\n```\n\nNote: FTA CAID changed from 0000 to FFFE. Management CAID 0000 is reserved for multicrypt channels.", .status = 0 }, + { .param = "Finding", .config = "srvid", .section = "", .text = "1. Check OSCam logs for service ID information\n2. Use DVB analysis tools to scan channels\n3. Monitor ECM requests in OSCam logs\n4. Check the web interface status page\n5. Consult online channel databases", .status = 0 }, + { .param = "Extracting", .config = "srvid", .section = "", .text = "Look for lines like:\n```\n[DVBAPI] Receiver wants to demux srvid 0025 on adapter 0000\n```\n\nThe service ID is 0025 in this example.", .status = 0 }, + { .param = "Organization", .config = "srvid", .section = "", .text = "- Group channels by provider or satellite\n- Use comments to separate sections\n- Keep entries sorted by CAID and service ID\n- Document special entries", .status = 0 }, + { .param = "Naming", .config = "srvid", .section = "", .text = "- Use clear, descriptive channel names\n- Include satellite position in provider field\n- Specify channel type (TV, Radio, etc.)\n- Add useful descriptions", .status = 0 }, + { .param = "Maintenance", .config = "srvid", .section = "", .text = "- Update when channel lineups change\n- Remove obsolete channels\n- Add new channels as they appear\n- Keep synchronized with actual channel availability", .status = 0 }, + { .param = "Example", .config = "srvid", .section = "", .text = "```\n# Sky Germany SAT - 19.2E\n098C,098D,09F0:007F|Sky|13th Street|TV|Sky Starter Paket\n098C,098D,09F0:0085|Sky|Beate Uhse|TV|Sky Starter Paket\n\n# Sky Germany Vodafone - Cable\n098E,1838,1850,1854,1868:0074|Sky|Sky Cinema Action HD|TV|Sky Cinema Paket\n098E,1838,1850,1854,1868:006B|Sky|Sky Cinema Classics HD|TV|Sky Cinema Paket\n\n# HD+ - 19.2E\n1830,1843,1860,186A,186D,09C4,098C,098D:EF10|Astra HD+|RTL HD|TV|\n1830,1843,1860,186A,186D,09C4,098C,098D:EF11|Astra HD+|VOX HD|TV|\n\n# ORF Digital - 19.2E\n0D95,0D98,0648,0650,09C4,098C:132F|ORF Digital|ORF1 HD\n0D95,0D98,0648,0650,09C4,098C:1330|ORF Digital|ORF2 HD\n```", .status = 0 }, + { .param = "Channel", .config = "srvid", .section = "", .text = "- Verify file is named oscam.srvid\n- Check file is in correct configuration directory\n- Ensure Unix line endings (not Windows CRLF)\n- Verify CAID:ProvID:SrvID matches exactly\n- Restart OSCam after changes", .status = 0 }, + { .param = "Wrong", .config = "srvid", .section = "", .text = "- Check for duplicate entries\n- Verify provider ID priority rules\n- Ensure hexadecimal values are correct\n- Check for typos in CAID/ProvID/SrvID", .status = 0 }, + { .param = "Memory", .config = "srvid", .section = "", .text = "- Reduce file size by removing unused entries\n- Keep descriptions short\n- Remove unnecessary fields\n- Consider external service ID management", .status = 0 }, + { .param = "Parameters", .config = "guess", .section = "", .text = "- **length of ECM**: The length of the ECM message in hexadecimal format\n- **CAID**: The Conditional Access Identification number in hexadecimal format", .status = 0 }, + { .param = "BOMBA", .config = "guess", .section = "", .text = "The primary use case is with the BOMBA protocol, which does not transmit CAID information:\n\n```\n# BOMBA protocol CAID guessing\n10:0B00\n12:0D00\n14:0E00\n```", .status = 0 }, + { .param = "Legacy", .config = "guess", .section = "", .text = "Some older or proprietary protocols may also benefit from CAID guessing:\n- Custom card sharing protocols\n- Modified or stripped-down protocols\n- Legacy conditional access systems", .status = 0 }, + { .param = "P", .config = "dvbapi", .section = "", .text = "```\nP: [CAID]:[provider ID]:[service ID]:[ECM PID]:[CHID] [continue]\n```\nSets priority for specific CAIDs or providers. The optional `continue` parameter (value: 1) forces continuous retry attempts on the specified CAID, useful for Pay-Per-View services or card activation scenarios.", .status = 0 }, + { .param = "I", .config = "dvbapi", .section = "", .text = "```\nI: [CAID]:[provider ID]:[service ID]:[ECM PID]:[CHID]\n```\nIgnores specific encryption providers or Pay TV providers to prevent unnecessary ECM requests.", .status = 0 }, + { .param = "M", .config = "dvbapi", .section = "", .text = "```\nM: [CAID]:[provider ID]:[service ID]:[ECM PID] [target CAID]:[target provider ID]\n```\nMaps one CAID/provider to another, useful for redirecting requests (e.g., mapping Sky HD channels for S02 or D02 cards).", .status = 0 }, + { .param = "D", .config = "dvbapi", .section = "", .text = "```\nD: [CAID]:[provider ID]:[service ID]:[ECM PID] delay\n```\nAdds a delay in milliseconds before writing the control word, useful when the server responds too quickly.", .status = 0 }, + { .param = "L", .config = "dvbapi", .section = "", .text = "```\nL: [CAID]:[provider ID]:[service ID]:[ECM PID] ECM length (hexa)\n```\nFilters ECM requests to only allow specific ECM lengths (in hexadecimal), useful for filtering broken mappings.", .status = 0 }, + { .param = "X", .config = "dvbapi", .section = "", .text = "```\nX: [CAID]:[provider ID]:[service ID]:[ECM PID] demux\n```\nAdds decoding on an extra demux index on the same CA device (not supported on all set-top boxes).", .status = 0 }, + { .param = "J", .config = "dvbapi", .section = "", .text = "```\nJ: [CAID]:[provider ID]:[service ID]:[ECM PID] join\n```\nJoins a CAID/provider/ECM PID to another ECM PID.", .status = 0 }, + { .param = "A", .config = "dvbapi", .section = "", .text = "```\nA: ::[service ID]:[video PID]:[provider ID]::[ECM PID]\n```\nSets a dummy ECM request with CAID FFFF for services with constant control words (for STBs without PMT PID support only).", .status = 0 }, + { .param = "S", .config = "dvbapi", .section = "", .text = "```\nS: [device] [PMT file name]\n```\nSettings for DVB API device name and PMT file name (only valid for STAPI).", .status = 0 }, + { .param = "Priority", .config = "dvbapi", .section = "", .text = "```\nP: 0100:123456 # Prioritize CAID 0100 with provider 123456\nP: 0100 1 # Prioritize CAID 0100 with continuous retry (for PPV/card activation)\nP: : 1 # Client continues requesting after 3 failed attempts (all CAIDs)\nP: :1234 # Prioritize ECM with provider ID 1234 on all services\nP: 0200 # Prioritize CAID 0200\nP: 0300::9ABC # Prioritize CAID 0300 only on service 9ABC\nP: 1702 # Prioritize Sky S02\nP: 1830 # Prioritize HD+ HD01\nP: 0D95 # Prioritize ORF-ICE\n```", .status = 0 }, + { .param = "Ignore", .config = "dvbapi", .section = "", .text = "```\nI: :654321 # Ignore provider ID 654321 for all services\nI: 0 # Ignore all CAIDs not previously specified in this file\nI: 09C4 # Ignore Sky V13\n```", .status = 0 }, + { .param = "Mapping", .config = "dvbapi", .section = "", .text = "```\nM: 0400 0500:123456 # Map CAID 0400 to CAID 0500 with provider ID 123456\nM: 1834:000000:007E 1722 # Map CAID 1834 with provider 000000 and SID 007E to CAID 1722\n```", .status = 0 }, + { .param = "Delay", .config = "dvbapi", .section = "", .text = "```\nD: 0600 200 # Wait 200 ms before writing CW for CAID 0600\n```", .status = 0 }, + { .param = "ECM", .config = "dvbapi", .section = "", .text = "```\nL: 0700 8e # Only allow ECM length 8e (hex) for this CAID to filter broken mappings\n```", .status = 0 }, + { .param = "Multicrypt", .config = "dvbapi", .section = "", .text = "For channels with multiple encryption systems (e.g., Sky Germany with both Nagravision and NDS), prioritize the CAID matching your available cards to avoid unnecessary requests and speed up channel opening.", .status = 0 }, + { .param = "Pay-Per-View", .config = "dvbapi", .section = "", .text = "Use the `continue` parameter to keep retrying on a specific CAID until the card is activated:\n```\nP: 0100 1\n```", .status = 0 }, + { .param = "Server", .config = "dvbapi", .section = "", .text = "Ignore CAIDs for which you have no cards to prevent unnecessary server requests:\n```\nI: 09C4 # Ignore if you don't have V13 cards\n```", .status = 0 }, + { .param = "disabled", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nAccount disabled.\n\n```ini\ndisabled = 0 # account enabled\ndisabled = 1 # account disabled\n```\n\n**Format:**\n```ini\ndisabled = 0|1\n```\n\n**Example:**\n```ini\ndisabled = 1\n```\n\n**Default:**\n```ini\ndisabled = 0\n```", .status = 0 }, + { .param = "user", .config = "user", .section = "account", .text = "**🔴 Required parameter**\n\nAccount name.\n\n**Example:**\n```ini\nuser = user1\n```\n\n**Default:**\n```ini\nuser =\n```", .status = 0 }, + { .param = "pwd", .config = "user", .section = "account", .text = "**🔴 Required parameter**\n\nPassword for account.\n\n⚠️ **Note:** Gbox Protocol: pwd is not required\n\n**Example:**\n```ini\npwd = pwuser1\n```\n\n**Default:**\n```ini\npwd =\n```", .status = 0 }, + { .param = "description", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nDescription of user account (text).\n\n**Format:**\n```ini\ndescription = text\n```\n\n**Example:**\n```ini\ndescription = Living room receiver\n```\n\n**Default:**\n```ini\ndescription =\n```", .status = 0 }, + { .param = "hostname", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nHost from which user connection is allowed.\n\n**Example:**\n```ini\nhostname = client.dyndns.org\n```\n\n**Default:**\n```ini\nhostname =\n```", .status = 0 }, + { .param = "caid", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nDefine and mapping of CAIDs for reader.\n\n**Format:**\n```ini\ncaid = [&][:][,[&][:target ]]...\n```\n\n**Example:**\n```ini\ncaid = 0100\ncaid = 0200&ffee:0300\ncaid = 0400&ff00:0500,0600\ncaid = 0702,0722\ncaid = 0702&ffdf # shortcut for the example above\n```\n\n**Default:** all CAIDs with mask FFFF", .status = 0 }, + { .param = "uniq", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nUnique connection mode for user.\n\n```ini\nuniq = 0 # disabled, default\nuniq = 1 # only one connection per user is allowed\nuniq = 2 # set user to fake if source ip is different\n # (e.g. for newcamd clients with different CAIDs and ports)\nuniq = 3 # only one connection per user, but only the last login will survive (old mpcs behavior)\nuniq = 4 # set user only to fake if source ip is different, but only the last login will survive\n```\n\n**Format:**\n```ini\nuniq = 0|1|2|3|4\n```\n\n**Example:**\n```ini\nuniq = 1\n```\n\n**Default:**\n```ini\nuniq = 0\n```", .status = 0 }, + { .param = "sleepsend", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nSleep send mode.\n\n```ini\nsleepsend = 0\nsleepsend = 255 # OSCam client only: stopping requests until next zap\n # camd 3.x only: stopping requests until restart of camd3 client\n```\n\n**Format:**\n```ini\nsleepsend = 0-255\n```\n\n**Example:**\n```ini\nsleepsend = 255\n```\n\n**Default:**\n```ini\nsleepsend =\n```", .status = 0 }, + { .param = "failban", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nMask for IP based blocking:\n\n```ini\nfailban = 0 # ignore (default)\nfailban = 2 # block IP address of a disabled account on connecting\nfailban = 4 # block IP address of a sleeping account while sleeping comes up\nfailban = 8 # block duplicate IP address\n```\n\n**Format:**\n```ini\nfailban = 0|2|4|8\n```\n\n**Example:**\n```ini\nfailban = 2\n```\n\n**Default:**\n```ini\nfailban = 0\n```", .status = 0 }, + { .param = "monlevel", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nMonitor level:\n\n```ini\nmonlevel = 0 # no access to monitor\nmonlevel = 1 # only server and own procs\nmonlevel = 2 # all procs, but viewing only (default)\nmonlevel = 3 # all procs, reload of oscam.user possible\nmonlevel = 4 # complete access\n```\n\n**Format:**\n```ini\nmonlevel = 0|1|2|3|4\n```\n\n**Example:**\n```ini\nmonlevel = 4\n```\n\n**Default:**\n```ini\nmonlevel = 2\n```", .status = 0 }, + { .param = "sleep", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nTime waiting for inactive user in minutes.\n\n**Format:**\n```ini\nsleep = minutes\n```\n\n**Example:**\n```ini\nsleep = 5\n```\n\n**Default:**\n```ini\nsleep =\n```", .status = 0 }, + { .param = "suppresscmd08", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nSwitches suppress of CMD08.\n\n```ini\nsuppresscmd08 = 0 # CMD08 for camd3 3.57x/3.78x clients enabled\nsuppresscmd08 = 1 # CMD08 for camd3 3.57x/3.78x clients disabled\n```\n\n**Format:**\n```ini\nsuppresscmd08 = 0|1\n```\n\n**Example:**\n```ini\nsuppresscmd08 = 1\n```\n\n**Default:**\n```ini\nsuppresscmd08 = 0\n```", .status = 0 }, + { .param = "umaxidle", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nValue for user being idle before disconnect (seconds).\n\n```ini\numaxidle = -1 # use clientmaxidle in [global]-section\numaxidle = 0 # idle disconnect disabled (default)\n```\n\n**Format:**\n```ini\numaxidle = -1|0|seconds\n```\n\n**Example:**\n```ini\numaxidle = 300\n```\n\n**Default:**\n```ini\numaxidle = 0\n```", .status = 0 }, + { .param = "keepalive", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nDisable keepalive between newcamd-server and client connection.\n\n```ini\nkeepalive = 0 # disabled\nkeepalive = 1 # enabled (default)\n```\n\n**Format:**\n```ini\nkeepalive = 0|1\n```\n\n**Example:**\n```ini\nkeepalive = 0\n```\n\n**Default:**\n```ini\nkeepalive = 1\n```", .status = 0 }, + { .param = "au", .config = "user", .section = "account", .text = "**🟢 Optional parameter**\n\nAU (Auto Update) settings.\n\n**Format:**\n```ini\nau =