Compare commits

..

No commits in common. "main" and "fix-test-pawel" have entirely different histories.

99 changed files with 16595 additions and 6963 deletions

View File

@ -517,6 +517,7 @@ if (CONFIG_STREAMRELAY MATCHES "Y" AND NOT MODULE_STREAMRELAY EQUAL 1)
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)
@ -546,6 +547,22 @@ 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})
@ -888,6 +905,24 @@ 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.*")
@ -1000,4 +1035,11 @@ if (HAVE_LIBDVBCSA)
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 "")

View File

@ -840,6 +840,13 @@ set reader's CCcam reshare hop, default:0
<P>
<B>cccwantemu</B> = <B>0</B>|<B>1</B>
<DL COMPACT><DT><DD>
1 = request to provide emu from CCCam server, too, default:0
</DL>
<P>
<B>ccckeepalive</B> = <B>0</B>|<B>1</B>
<DL COMPACT><DT><DD>
1 = send keepalive messages to keep connection to remote CCCam server up, default:0

View File

@ -649,6 +649,11 @@ set reader's CCcam reshare hop, default:0
\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

View File

@ -509,6 +509,9 @@ DESCRIPTIONS
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

View File

@ -32,6 +32,9 @@ 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
@ -371,6 +374,32 @@ 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
@ -931,6 +960,3 @@ debug: all
-include Makefile.extra
-include Makefile.local
EXTRA_LIBS += -lm
LIBS += $(EXTRA_LIBS)

View File

@ -81,7 +81,7 @@ Examples:
./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
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

221
README.md
View File

@ -1,81 +1,188 @@
# OSCam: Open Source Conditional Access Module
# OSCam with AI Fake DCW Detector Test
[![GitLab Last Commit](https://img.shields.io/gitlab/last-commit/11?gitlab_url=https%3A%2F%2Fgit.streamboard.tv&style=for-the-badge)](https://git.streamboard.tv/common/oscam/-/commits/master)
[![GitLab Tag](https://img.shields.io/gitlab/v/tag/11?gitlab_url=https%3A%2F%2Fgit.streamboard.tv&style=for-the-badge)](https://git.streamboard.tv/common/oscam/-/tags)
[![GitLab License](https://img.shields.io/gitlab/license/11?gitlab_url=https%3A%2F%2Fgit.streamboard.tv&style=for-the-badge)](https://git.streamboard.tv/common/oscam/-/blob/master/COPYING)
![AI Fake DCW Detector](images/image1.jpg)
## Quick links
## Overview
- [Releases](https://git.streamboard.tv/common/oscam/-/commits/master)
- [GitLab repository](https://git.streamboard.tv/common/oscam)
- [Wiki](https://git.streamboard.tv/common/oscam/-/wikis/home)
- [Issue tracker](https://git.streamboard.tv/common/oscam/-/issues)
- [Support forum](https://board.streamboard.tv/forum/)
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:
## Releases
- Local readers
- CacheEx peers
- CSP sources
- Virtual readers
For the latest changes and release history, see the
[OSCam commits](https://git.streamboard.tv/common/oscam/-/commits/master) page.
Instead of accepting the first CW received, this implementation collects multiple candidates and selects the most reliable one using weighted voting logic.
## GitLab repository
---
Project page:
https://git.streamboard.tv/common/oscam
# 🔍 Problem It Solves
## Building & Dependencies
In multi-reader or CacheEx environments, fake or unstable DCWs can appear.
For detailed information about building OSCam, cross-compilation for
different CPUs, required and optional dependencies, SSL support, hardware
modules, and platform-specific or distribution-specific notes, please
refer to the OSCam wiki:
Default behavior:
- First CW wins
- Possible glitches, freezing, or unstable decoding
- [Wiki Home](https://git.streamboard.tv/common/oscam/-/wikis/home)
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
## License
Result:
OSCam: Open Source CAM
✔ Reduced fake DCWs
✔ Increased decoding stability
✔ Better CacheEx reliability
✔ Smarter CW selection
Copyright (C) 2009-2026 OSCam developers
---
OSCam is based on the Streamboard mp-cardserver 0.9d by dukat and has been
extended and worked on by many more since then.
# 🧠 Core Functions
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.
## 1`cw_vote_add()`
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.
This function is called whenever a new CW is received.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
It:
For the full text of the license, please see the
[COPYING](https://git.streamboard.tv/common/oscam/-/blob/master/COPYING)
file in the OSCam repository.
- 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
## Contributing
Each CW candidate is stored in a voting pool.
Contributions are welcome. If you want to help improve OSCam:
---
- Browse the existing [issues](https://git.streamboard.tv/common/oscam/-/issues)
and open a new issue if you find a bug or have a feature request.
- Fork the [GitLab repository](https://git.streamboard.tv/common/oscam),
create a topic branch, and submit a merge request.
- Check the [OSCam wiki](https://git.streamboard.tv/common/oscam/-/wikis/home)
for additional project information and guidelines.
## 2`cw_vote_decide()`
## Help and Support
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.
- Man pages and configuration examples are available in the
`Distribution/doc` directory of the source tree.
- For bug reports and feature requests, please use the
[GitLab issue tracker](https://git.streamboard.tv/common/oscam/-/issues).
- For community help and general discussion (mainly German and English),
visit the [support forum](https://board.streamboard.tv/forum/).
- For configuration guides, FAQs, and detailed documentation, see the
[OSCam wiki](https://git.streamboard.tv/common/oscam/-/wikis/home).

View File

69
config.h Executable file → Normal file
View File

@ -1,24 +1,22 @@
#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
<<<<<<< Updated upstream
//#define WEBIF_WIKI 1
=======
#define WEBIF_WIKI 1
>>>>>>> Stashed changes
#define WITH_COMPRESS_WEBIF 1
//#define WITH_COMPRESS_WEBIF 1
//#define WITH_SSL 1
#if defined(__linux__) || defined(__CYGWIN__)
//#define HAVE_DVBAPI 1
//#define WITH_EXTENDED_CW 1
#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 CS_ANTICASC 1
#define WITH_DEBUG 1
#define WITH_LB 1
#define CS_CACHEEX 1
@ -26,51 +24,46 @@
#define CW_CYCLE_CHECK 1
//#define LCDSUPPORT 1
//#define LEDSUPPORT 1
//#define IPV6SUPPORT 1
#define IPV6SUPPORT 1
//#define WITH_ARM_NEON 1
//#define WITH_SIGNING 1
//#define MODULE_MONITOR 1
#define MODULE_MONITOR 1
//#define MODULE_CAMD33 1
//#define MODULE_CAMD35 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_GBOX 1
#define MODULE_RADEGAST 1
//#define MODULE_SERIAL 1
//#define MODULE_CONSTCW 1
#define MODULE_CONSTCW 1
//#define MODULE_PANDORA 1
//#define MODULE_GHTTP 1
<<<<<<< Updated upstream
#define MODULE_GHTTP 1
//#define MODULE_SCAM 1
//#define MODULE_STREAMRELAY 1
=======
#define MODULE_SCAM 1
#define MODULE_STREAMRELAY 1
>>>>>>> Stashed changes
//#define WITH_CARDREADER 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 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_PHOENIX 1
#define CARDREADER_INTERNAL 1
//#define CARDREADER_MP35 1
//#define CARDREADER_SC8IN1 1
//#define CARDREADER_SMARGO 1
@ -79,7 +72,7 @@
//#define CARDREADER_DRECAS 1
#ifdef WITH_PCSC
#define CARDREADER_PCSC 1
//#define CARDREADER_PCSC 1
#endif
#ifdef WITH_LIBUSB

View File

@ -1 +0,0 @@
EXTRA_LIBS += -lm

View File

@ -1,6 +1,6 @@
#!/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"
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"
@ -9,40 +9,42 @@ defconfig="
CONFIG_WEBIF=y
CONFIG_WEBIF_LIVELOG=y
CONFIG_WEBIF_JQUERY=y
# CONFIG_WEBIF_WIKI=n
CONFIG_WEBIF_WIKI=y
CONFIG_WITH_COMPRESS_WEBIF=y
# CONFIG_WITH_SSL=n
CONFIG_WITH_SSL=y
CONFIG_HAVE_DVBAPI=y
# CONFIG_WITH_EXTENDED_CW=n
CONFIG_WITH_EXTENDED_CW=y
# CONFIG_WITH_NEUTRINO=n
CONFIG_READ_SDT_CHARSETS=y
# CONFIG_CS_ANTICASC=n
CONFIG_CS_ANTICASC=y
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_CS_CACHEEX=y
CONFIG_CS_CACHEEX_AIO=y
CONFIG_CW_CYCLE_CHECK=y
CONFIG_LCDSUPPORT=y
CONFIG_LEDSUPPORT=y
CONFIG_CLOCKFIX=y
CONFIG_IPV6SUPPORT=y
# CONFIG_WITH_ARM_NEON=n
# CONFIG_WITH_SIGNING=n
# CONFIG_MODULE_CAMD33=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_MODULE_RADEGAST=y
CONFIG_MODULE_SERIAL=y
CONFIG_MODULE_CONSTCW=y
CONFIG_MODULE_PANDORA=y
CONFIG_MODULE_SCAM=y
CONFIG_MODULE_GHTTP=y
CONFIG_MODULE_STREAMRELAY=y
CONFIG_WITH_CARDREADER=y
CONFIG_READER_NAGRA_COMMON=y
CONFIG_READER_NAGRA=y
@ -331,8 +333,8 @@ get_opts() {
update_deps() {
# Calculate dependencies
enabled_any $(get_opts readers) $(get_opts card_readers) && enable_opt WITH_CARDREADER >/dev/null
disabled_all $(get_opts readers) $(get_opts card_readers) && disable_opt WITH_CARDREADER >/dev/null
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
@ -341,6 +343,9 @@ update_deps() {
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() {
@ -392,9 +397,9 @@ list_config() {
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 && echo "CONFIG_LIB_DES=y" || echo "# CONFIG_LIB_DES=n"
enabled_any MODULE_CCCAM READER_NAGRA READER_NAGRA_MERLIN READER_SECA && 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 && echo "CONFIG_LIB_BIGNUM=y" || echo "# CONFIG_LIB_BIGNUM=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"
@ -524,6 +529,8 @@ menu_addons() {
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=${?}
@ -906,7 +913,8 @@ do
;;
'-v'|'--oscam-version')
version=`grep '^#define CS_VERSION' globals.h | cut -d\" -f2`
echo $version
emuversion=`grep EMU_VERSION module-emulator-osemu.h | awk '{ print $3 }'`
echo $version-$emuversion
break
;;
'--submodule')
@ -957,7 +965,7 @@ do
break
;;
'-c'|'--oscam-commit')
sha=`git log 2>/dev/null | sed -n 1p | cut -d ' ' -f2 | cut -c1-8`
sha=`git log --no-merges 2>/dev/null | sed -n 1p | cut -d ' ' -f2 | cut -c1-8`
echo $sha
break
;;

View File

@ -15,5 +15,6 @@ 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

109
globals.h
View File

@ -365,14 +365,19 @@
#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://git.streamboard.tv/common/oscam"
#define SCM_URL "https://github.com/oscam-mirror/oscam-emu"
#define WIKI_URL "https://git.streamboard.tv/common/oscam/-/wikis"
#define BOARD_URL "https://board.streamboard.tv"
#define BOARD_URL "https://github.com/oscam-mirror/oscam-emu/discussions"
#ifndef CS_VERSION
#define CS_VERSION "2.26.02-11943"
#define CS_VERSION "2.26.01-11942"
#endif
#ifndef CS_GIT_COMMIT
#define CS_GIT_COMMIT "a2b4c6d8"
@ -398,9 +403,6 @@
#define CS_CLIENT_HASHBUCKETS 32
#define CS_SERVICENAME_SIZE 32
#define WEBIF_MAX_NODES 16
extern uint32_t webif_last_nodeid[WEBIF_MAX_NODES];
#define CS_ECMSTORESIZE 16 // use MD5()
#define CS_EMMSTORESIZE 16 // use MD5()
#define CS_CLIENT_TIMEOUT 5000
@ -410,14 +412,18 @@ extern uint32_t webif_last_nodeid[WEBIF_MAX_NODES];
#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 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
@ -452,10 +458,11 @@ extern uint32_t webif_last_nodeid[WEBIF_MAX_NODES];
#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_SMART 0x7 // Smartreader+
#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
@ -893,6 +900,13 @@ typedef struct s_entitlement // contains entitlement Info
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;
@ -902,7 +916,7 @@ struct cmd_packet_t;
struct s_ecm_answer;
struct demux_s;
#define DEFAULT_MODULE_BUFSIZE 1024
#define DEFAULT_MODULE_BUFsize 1024
struct s_module
{
@ -1027,6 +1041,26 @@ typedef struct cw_extendted_t
#endif
} EXTENDED_CW;
typedef struct s_cw_vote {
uint8_t cw[16];
uint8_t votes;
uint8_t local_votes;
uint8_t has_cacheex_vote; // FIX #2: flaga czy głos pochodzi z cacheex
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];
@ -1097,7 +1131,6 @@ typedef struct ecm_request_t
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
uint8_t entropy_blocked; // =1 if entropy check blocked CW
#endif
#ifdef CS_CACHEEX_AIO
int32_t ecm_time; // ecm-time in ms
@ -1121,9 +1154,10 @@ typedef struct ecm_request_t
#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;
@ -1516,6 +1550,7 @@ typedef struct ce_csp_t
CAIDVALUETAB maxhop_lg_percaid;
#endif
CECSPVALUETAB filter_caidtab;
CAIDVALUETAB cacheex_nopushafter_tab;
uint8_t allow_request;
uint8_t allow_reforward;
uint8_t drop_csp;
@ -1535,7 +1570,6 @@ typedef struct ce_csp_t
uint8_t lg_only_in_aio_only;
uint8_t lg_only_remote_settings;
int32_t feature_bitfield;
CAIDVALUETAB cacheex_nopushafter_tab;
char aio_version[CS_AIO_VERSION_LEN];
#endif
} CECSP;
@ -1691,7 +1725,7 @@ struct s_reader // contains device info, reader info and card info
#endif
int32_t r_port;
char r_usr[64];
char r_pwd[64];
char r_pwd[64]; // Max length 63 + null terminator
int32_t l_port;
CAIDTAB ctab;
uint32_t boxid;
@ -1800,6 +1834,7 @@ struct s_reader // contains device info, reader info and card info
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
@ -1996,6 +2031,11 @@ struct s_reader // contains device info, reader info and card info
#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
@ -2233,7 +2273,7 @@ struct s_config
uint32_t ctimeout;
uint32_t ftimeout;
CAIDVALUETAB ftimeouttab;
CAIDVALUETAB ctimeouttab; // <EFBFBD> clienttimeout_percaid
CAIDVALUETAB ctimeouttab; // clienttimeout_percaid
uint32_t cmaxidle;
int32_t ulparent;
uint32_t delay;
@ -2445,6 +2485,17 @@ struct s_config
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;
@ -2534,6 +2585,10 @@ struct s_config
#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
@ -2568,7 +2623,6 @@ struct s_config
CECSPVALUETAB cacheex_wait_timetab;
CAIDVALUETAB cacheex_mode1_delay_tab;
#ifdef CS_CACHEEX_AIO
CAIDVALUETAB cacheex_nopushafter_tab;
uint8_t waittime_block_start;
uint16_t waittime_block_time;
#endif
@ -2588,15 +2642,24 @@ struct s_config
uint8_t cacheex_lg_only_remote_settings;
uint8_t cacheex_localgenerated_only;
CAIDTAB cacheex_localgenerated_only_caidtab;
FTAB cacheex_lg_only_tab;
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;
uint64_t cacheex_push_lg_groups;
#endif
uint32_t cacheex_push_lg_groups;
FTAB cacheex_lg_only_tab;
#endif
#ifdef CW_CYCLE_CHECK
@ -2727,12 +2790,18 @@ 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; }
@ -2750,4 +2819,8 @@ static inline uint8_t get_ecm_mode(const ECM_REQUEST *er) {
#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

View File

@ -365,14 +365,19 @@
#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://git.streamboard.tv/common/oscam"
#define SCM_URL "https://github.com/oscam-mirror/oscam-emu"
#define WIKI_URL "https://git.streamboard.tv/common/oscam/-/wikis"
#define BOARD_URL "https://board.streamboard.tv"
#define BOARD_URL "https://github.com/oscam-mirror/oscam-emu/discussions"
#ifndef CS_VERSION
#define CS_VERSION "2.26.02-11943"
#define CS_VERSION "2.26.01-11942"
#endif
#ifndef CS_GIT_COMMIT
#define CS_GIT_COMMIT "a2b4c6d8"
@ -407,14 +412,18 @@
#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 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
@ -449,10 +458,11 @@
#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_SMART 0x7 // Smartreader+
#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
@ -890,6 +900,13 @@ typedef struct s_entitlement // contains entitlement Info
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;
@ -899,7 +916,7 @@ struct cmd_packet_t;
struct s_ecm_answer;
struct demux_s;
#define DEFAULT_MODULE_BUFSIZE 1024
#define DEFAULT_MODULE_BUFsize 1024
struct s_module
{
@ -1024,6 +1041,25 @@ typedef struct cw_extendted_t
#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];
@ -1117,9 +1153,10 @@ typedef struct ecm_request_t
#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;
@ -1506,12 +1543,9 @@ typedef struct ce_csp_t
int8_t maxhop;
#ifdef CS_CACHEEX_AIO
int8_t maxhop_lg;
#endif
CAIDVALUETAB maxhop_percaid;
#ifdef CS_CACHEEX_AIO
CAIDVALUETAB maxhop_lg_percaid;
#endif
CECSPVALUETAB filter_caidtab;
CAIDVALUETAB cacheex_nopushafter_tab;
uint8_t allow_request;
uint8_t allow_reforward;
uint8_t drop_csp;
@ -1531,7 +1565,6 @@ typedef struct ce_csp_t
uint8_t lg_only_in_aio_only;
uint8_t lg_only_remote_settings;
int32_t feature_bitfield;
CAIDVALUETAB cacheex_nopushafter_tab;
char aio_version[CS_AIO_VERSION_LEN];
#endif
} CECSP;
@ -1687,7 +1720,7 @@ struct s_reader // contains device info, reader info and card info
#endif
int32_t r_port;
char r_usr[64];
char r_pwd[64];
char r_pwd[64]; // Max length 63 + null terminator
int32_t l_port;
CAIDTAB ctab;
uint32_t boxid;
@ -1796,6 +1829,7 @@ struct s_reader // contains device info, reader info and card info
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
@ -1992,6 +2026,11 @@ struct s_reader // contains device info, reader info and card info
#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
@ -2101,8 +2140,6 @@ struct s_auth
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
@ -2229,7 +2266,7 @@ struct s_config
uint32_t ctimeout;
uint32_t ftimeout;
CAIDVALUETAB ftimeouttab;
CAIDVALUETAB ctimeouttab; // clienttimeout_percaid
CAIDVALUETAB ctimeouttab; // clienttimeout_percaid
uint32_t cmaxidle;
int32_t ulparent;
uint32_t delay;
@ -2441,6 +2478,17 @@ struct s_config
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;
@ -2530,6 +2578,10 @@ struct s_config
#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
@ -2564,35 +2616,35 @@ struct s_config
CECSPVALUETAB cacheex_wait_timetab;
CAIDVALUETAB cacheex_mode1_delay_tab;
#ifdef CS_CACHEEX_AIO
CAIDVALUETAB cacheex_nopushafter_tab;
uint8_t waittime_block_start;
uint16_t waittime_block_time;
#endif
CECSP csp; // CSP Settings
uint8_t cacheex_enable_stats; // enable stats
uint8_t cacheex_global_maxhop;
uint8_t cacheex_maxhop;
uint8_t cacheex_maxhop_lg;
CAIDVALUETAB cacheex_maxhop_percaid;
CAIDVALUETAB cacheex_maxhop_lg_percaid;
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 cacheex_lg_only_tab;
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;
uint64_t cacheex_push_lg_groups;
#endif
uint32_t cacheex_push_lg_groups;
FTAB cacheex_lg_only_tab;
#endif
#ifdef CW_CYCLE_CHECK
@ -2723,12 +2775,18 @@ 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; }
@ -2746,4 +2804,8 @@ static inline uint8_t get_ecm_mode(const ECM_REQUEST *er) {
#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

BIN
images/image1.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

0
label
View File

View File

@ -34,44 +34,6 @@ extern struct ecm_request_t *ecm_pushed_deleted;
extern CS_MUTEX_LOCK ecmcache_lock;
extern struct ecm_request_t *ecmcwcache;
/* 48/64 learning structure (defined in oscam-ecm.c) */
#define CW_DETECT_MAX 8192
#define CW_LEARN_THRESHOLD 5
#define CW_CACHEEX_NODEID_MAX 5
typedef struct
{
uint16_t caid;
uint32_t prid;
uint16_t srvid;
uint8_t is64;
uint8_t is48;
uint8_t learn64_count;
uint8_t learn48_count;
uint8_t strict_logged;
/* CacheEX learning: track unique nodeid (64-bit) */
uint64_t cacheex_nodeid_48[CW_CACHEEX_NODEID_MAX];
uint8_t cacheex_nodeid_48_count;
uint64_t cacheex_nodeid_64[CW_CACHEEX_NODEID_MAX];
uint8_t cacheex_nodeid_64_count;
/* finalized flags (no more learning) */
uint8_t cacheex_48_finalized;
uint8_t cacheex_64_finalized;
} cw_detect_entry;
/* Forward declares for 48/64 learning (from oscam-ecm.c) */
typedef struct ecm_request_t ECM_REQUEST;
extern cw_detect_entry *cw_detect_get(ECM_REQUEST *er);
extern int cw_checksum_ok(uint8_t *cw);
extern int cacheex_learn_add_nodeid(uint64_t *nodeid_list, uint8_t *count, uint64_t new_nodeid);
// HIT CACHE functions **************************************************************
typedef struct hit_key_t {
@ -560,7 +522,7 @@ int8_t cacheex_maxhop(struct s_client *cl, ECM_REQUEST *er)
return default_maxhop;
}
// GLOBAL override OFF <EFBFBD> reader only
// GLOBAL override OFF reader only
else
{
// Priority 1: reader percaid
@ -584,11 +546,10 @@ int8_t cacheex_maxhop(struct s_client *cl, ECM_REQUEST *er)
}
}
// NOT a cacheex reader <EFBFBD> default
// NOT a cacheex reader default
return default_maxhop;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef CS_CACHEEX_AIO
int8_t cacheex_maxhop_lg(struct s_client *cl, ECM_REQUEST *er)
@ -621,7 +582,7 @@ int8_t cacheex_maxhop_lg(struct s_client *cl, ECM_REQUEST *er)
return default_maxhop;
}
// GLOBAL override OFF <EFBFBD> reader only
// GLOBAL override OFF reader only
else
{
// Priority 1: reader percaid LG
@ -1055,7 +1016,9 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in
}
#endif
if(chk_is_null_CW(er->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(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++;
@ -1064,7 +1027,9 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in
return 0;
}
if(get_odd_even(er) == 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++;
@ -1100,99 +1065,6 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in
}
}
/////////////////////////////////////////////////////////////
// CACHEEX 48/64 LEARNING (reuse oscam-ecm.c structures)
/////////////////////////////////////////////////////////////
if (!csp && !er->entropy_blocked)
{
/* Get learning entry from shared cw_detect_table */
extern cw_detect_entry cw_detect_table[];
(void)cw_detect_table; /* Table is accessed via cw_detect_get */
cw_detect_entry *e = cw_detect_get(er);
uint8_t checksum_ok = cw_checksum_ok(er->cw);
/* Get CacheEX source nodeid */
uint64_t src_nodeid = 0;
if (er->csp_lastnodes && ll_count(er->csp_lastnodes) > 0)
{
uint8_t remotenodeid[8];
cacheex_get_srcnodeid(er, remotenodeid);
memcpy(&src_nodeid, remotenodeid, 8);
}
uint8_t whitelist = chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for) ||
(cl->account && chk_if_ignore_checksum(er, &cl->account->disablecrccacheex_only_for));
/////////////////////////////////////////////////////////////
// CACHEEX LEARNING: ONLY FOR WHITELIST CAIDs IN UNKNOWN STATE
/////////////////////////////////////////////////////////////
if (whitelist && src_nodeid)
{
/* Learn 64bit (invalid checksum) */
if (!checksum_ok && !e->cacheex_64_finalized)
{
int finalized = cacheex_learn_add_nodeid(
e->cacheex_nodeid_64,
&e->cacheex_nodeid_64_count,
src_nodeid
);
if (finalized)
{
e->cacheex_64_finalized = 1;
e->is64 = 1;
cs_log("CACHEEX FINALIZED 64bit after %d unique nodes caid=%04X srvid=%04X",
CW_CACHEEX_NODEID_MAX, er->caid, er->srvid);
}
else
{
cs_log_dbg(D_CACHEEX, "CACHEEX LEARN 64bit (%d/%d nodes) caid=%04X srvid=%04X src_nodeid=%" PRIu64 "X",
e->cacheex_nodeid_64_count, CW_CACHEEX_NODEID_MAX, er->caid, er->srvid, src_nodeid);
}
}
/* Learn 48bit (valid checksum) */
if (checksum_ok && !e->cacheex_48_finalized)
{
int finalized = cacheex_learn_add_nodeid(
e->cacheex_nodeid_48,
&e->cacheex_nodeid_48_count,
src_nodeid
);
if (finalized)
{
e->cacheex_48_finalized = 1;
e->is48 = 1;
cs_log("CACHEEX FINALIZED 48bit after %d unique nodes caid=%04X srvid=%04X",
CW_CACHEEX_NODEID_MAX, er->caid, er->srvid);
}
else
{
cs_log_dbg(D_CACHEEX, "CACHEEX LEARN 48bit (%d/%d nodes) caid=%04X srvid=%04X src_nodeid=%" PRIu64 "X",
e->cacheex_nodeid_48_count, CW_CACHEEX_NODEID_MAX, er->caid, er->srvid, src_nodeid);
}
}
}
else if (!whitelist && checksum_ok)
{
/* STRICT MODE: only 48bit allowed, no learning */
cs_log_dbg(D_CACHEEX, "CACHEEX STRICT 48bit (CAID outside whitelist) caid=%04X srvid=%04X",
er->caid, er->srvid);
}
else if (!whitelist && !checksum_ok)
{
/* STRICT MODE: reject 64bit, no learning */
cs_log_dbg(D_CACHEEX, "CACHEEX STRICT REJECT 64bit (CAID outside whitelist) caid=%04X srvid=%04X",
er->caid, er->srvid);
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;

File diff suppressed because it is too large Load Diff

View File

@ -1,78 +0,0 @@
#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

View File

@ -879,7 +879,7 @@ void camd35_cacheex_feature_request_save(struct s_client *cl, uint8_t *buf)
camd35_cacheex_feature_trigger(cl, 2, 2);
}
// // flag 4 => set cacheex_ecm_filter (extended)
// flag 4 => set cacheex_ecm_filter (extended)
if(rdr->cacheex.feature_bitfield & 4)
{
camd35_cacheex_feature_trigger(cl, 4, 2);
@ -1476,6 +1476,9 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf)
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
}
@ -1569,6 +1572,11 @@ 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));
}
}
@ -1577,12 +1585,38 @@ static void camd35_server_client_init(struct s_client *cl)
*/
static void camd35_cacheex_push_receive_remote_id(struct s_client *cl, uint8_t *buf)
{
uint8_t new_nodeid[8];
memcpy(cl->ncd_skey, buf + 20, 8);
cl->ncd_skey[8] = 1;
cs_log_dbg(D_CACHEEX, "cacheex: received id answer from %s: %" PRIu64 "X", username(cl), cacheex_node_id(cl->ncd_skey));
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)
{

1754
module-camd35-cacheex.c.orig Executable file

File diff suppressed because it is too large Load Diff

View File

@ -1009,7 +1009,7 @@ int32_t cc_send_cli_data(struct s_client *cl)
memcpy(buf, rdr->r_usr, sizeof(rdr->r_usr));
memcpy(buf + 20, cc->node_id, 8);
buf[28] = 0; // <-- Client wants to have EMUs, 0 - NO; 1 - YES
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)
@ -1544,7 +1544,8 @@ struct cc_card *get_matching_card(struct s_client *cl, ECM_REQUEST *cur_er, int8
}
if((ncard->caid == cur_er->caid // caid matches
|| lb_match)) // or system matches if caid ends with 00
|| (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);
@ -1583,7 +1584,7 @@ struct cc_card *get_matching_card(struct s_client *cl, ECM_REQUEST *cur_er, int8
}
}
if(caid_is_nagra(ncard->caid) && (!xcard || ncard->hop < xcard->hop))
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
}

View File

@ -123,7 +123,9 @@ static uint8_t checkvalidCW(ECM_REQUEST *er)
{
uint8_t ret = 1;
if(chk_is_null_CW(er->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(chk_is_null_CW(er->cw) && !caid_is_biss(er->caid))
{ er->rc = E_NOTFOUND; }
if(er->rc == E_NOTFOUND)
@ -841,15 +843,25 @@ uint8_t checkcwcycle(struct s_client *client, ECM_REQUEST *er, struct s_reader *
break;
case 9: // CWCYCLE NOK without counting
snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc NOK");
if(cfg.onbadcycle > 0)
if(cfg.onbadcycle > 0) // ignore ECM Request
{
cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> DROP CW (LG included)", user, er_ecmf, c_reader);
#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;
@ -858,10 +870,10 @@ uint8_t checkcwcycle(struct s_client *client, ECM_REQUEST *er, struct s_reader *
}
return 1;
}
#endif
/*
*
*/
#endif

View File

@ -312,7 +312,9 @@ void azbox_send_dcw(struct s_client *client, ECM_REQUEST *er)
int32_t n;
for(n = 0; n < 2; n++)
{
if(memcmp(er->cw + (n * 8), demux[0].last_cw[0][n], 8) && (memcmp(er->cw + (n * 8), nullcw, 8) != 0))
// 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);

View File

@ -601,8 +601,10 @@ void mca_send_dcw(struct s_client *client, ECM_REQUEST *er)
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))
&& (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);

View File

@ -730,8 +730,10 @@ int32_t stapi_write_cw(int32_t demux_id, uint8_t *cw, uint16_t *STREAMpids, int3
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))
&& (memcmp(cw + (l * 8), nullcw, 8) != 0 || caid_is_biss(demux[demux_id].ECMpids[pidnum].CAID)))
{
for(n = 0; n < PTINUM; n++)
{

View File

@ -731,8 +731,10 @@ int32_t stapi_write_cw(int32_t demux_id, uint8_t *cw, uint16_t *STREAMpids, int3
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))
&& (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));

View File

@ -1886,8 +1886,15 @@ void dvbapi_start_emm_filter(int32_t demux_id)
}
if(match)
{
if(rdr->typ == R_EMU)
{
csystem = rdr->csystem;
}
else
{
csystem = get_cardsystem_by_caid(caid);
}
if(csystem)
{
if(caid != ncaid)
@ -1907,10 +1914,18 @@ void dvbapi_start_emm_filter(int32_t demux_id)
}
}
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",
@ -2580,7 +2595,6 @@ void dvbapi_stop_descrambling(int32_t demux_id, uint32_t msgid)
}
demux[demux_id].pidindex = -1;
demux[demux_id].curindex = -1;
demux[demux_id].srvtype = -1;
if(!dvbapi_listenport_active && cfg.dvbapi_boxtype != BOXTYPE_PC_NODMX)
{
@ -2652,6 +2666,8 @@ int32_t dvbapi_start_descrambling(int32_t demux_id, int32_t pid, int8_t checked,
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
@ -2683,6 +2699,63 @@ int32_t dvbapi_start_descrambling(int32_t demux_id, int32_t pid, int8_t checked,
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)
@ -3792,7 +3865,7 @@ static void dvbapi_parse_pmt_ca_descriptor(int32_t demux_id, const uint8_t *buff
ca_system_id = b2i(2, buffer);
ca_pid = b2i(2, buffer + 2) & 0x1FFF;
if(ca_system_id == 0x0000 || ca_pid == 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
}
@ -4239,7 +4312,6 @@ typedef struct demux_parameters
uint16_t onid;
uint16_t tsid;
uint32_t ens;
int64_t srvtype; // service type mask from descriptor 0x85, or -1 if not present
} demux_parameters_t;
static void get_demux_parameters(const uint8_t *buffer, demux_parameters_t *parameters)
@ -4330,13 +4402,7 @@ static void get_demux_parameters(const uint8_t *buffer, demux_parameters_t *para
}
case SERVICE_TYPE_MASK:
{
if(descriptor_length == 0x04)
{
parameters->srvtype = b2i(4, buffer + pos + 2);
}
break;
}
case DEMUX_DEVICE:
{
@ -4387,14 +4453,33 @@ static void dvbapi_capmt_notify(struct demux_s *dmx)
static void dvbapi_prepare_descrambling(int32_t demux_id, uint32_t msgid)
{
bool start_emm = true;
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)
else if(demux[demux_id].STREAMpids[0] == demux[demux_id].pmtpid || is_powervu)
{
dvbapi_start_pmt_filter(demux_id);
}
@ -4480,7 +4565,6 @@ int32_t dvbapi_parse_capmt(const uint8_t *buffer, uint32_t length, int32_t connf
bool is_update = false;
demux_parameters_t parameters;
memset(&parameters, 0, sizeof(parameters));
parameters.srvtype = -1;
#if defined WITH_COOLAPI || defined WITH_COOLAPI2
ca_pmt_list_management = CA_PMT_LIST_ONLY;
@ -4644,7 +4728,6 @@ int32_t dvbapi_parse_capmt(const uint8_t *buffer, uint32_t length, int32_t connf
demux[demux_id].ens = parameters.ens;
demux[demux_id].tsid = parameters.tsid;
demux[demux_id].onid = parameters.onid;
demux[demux_id].srvtype = parameters.srvtype;
demux[demux_id].stop_descrambling = false;
demux[demux_id].running = false;
demux[demux_id].sdt_filter = -1;
@ -5597,6 +5680,7 @@ void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer,
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!
@ -5622,8 +5706,25 @@ void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer,
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))
if((curpid->table == buffer[0] && !caid_is_irdeto(curpid->CAID)) || pvu_skip)
{
if(!(er = get_ecmtask()))
{
@ -5967,6 +6068,35 @@ void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer,
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;
@ -7196,8 +7326,10 @@ void dvbapi_write_cw(int32_t demux_id, int32_t pid, int32_t stream_id, uint8_t *
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))
&& (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);
@ -7489,7 +7621,9 @@ void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er)
// 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
if((status == 0 || status == 3 || status == 4) && er->rc < E_NOTFOUND)
// 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 &&
@ -7761,21 +7895,8 @@ void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er)
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)
{
bool sr_handled = stream_write_cw(er);
if(demux[i].srvtype >= 0 && !(demux[i].srvtype & 0x180))
{
// E2 sent service type mask without streamrelay bits (type 7/8),
// so this is a live-TV/softcsa service. Always send CW via dvbapi,
// even if a dying streamrelay client still consumed it.
set_dvbapi_cw = true;
}
else
{
// No service type mask (old E2) or streamrelay bits set:
// preserve original behavior - let stream_write_cw() decide.
set_dvbapi_cw = !sr_handled;
}
// streamserver set cw
set_dvbapi_cw = !stream_write_cw(er);
}
if (set_dvbapi_cw)
#endif
@ -8506,7 +8627,9 @@ int32_t dvbapi_check_ecm_delayed_delivery(int32_t demux_id, ECM_REQUEST *er)
}
// Check for null cw
if(memcmp(er->cw, nullcw, 8) == 0 && memcmp(er->cw + 8, nullcw, 8) == 0)
// 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;
}

View File

@ -145,7 +145,7 @@
#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
#define SERVICE_TYPE_MASK 0x85 // not used by OSCam
#define DEMUX_DEVICE 0x86
#define CA_DEVICE 0x87
@ -336,6 +336,9 @@ typedef struct filter_s
uint32_t SlotHandle[10];
uint32_t BufferHandle[10];
#endif
#ifdef WITH_EMU
uint32_t cadata;
#endif
} FILTERTYPE;
#ifdef WITH_EXTENDED_CW
@ -368,6 +371,9 @@ typedef struct s_ecmpid
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
@ -446,7 +452,6 @@ typedef struct demux_s
struct timeb decstart;
struct timeb decend;
uint32_t msgid;
int64_t srvtype; // service type mask from CA PMT descriptor 0x85, or -1 if not present
} DEMUXTYPE;
typedef struct s_streampid

883
module-emulator-biss.c Executable file
View File

@ -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 <openssl/evp.h>
#include <openssl/pem.h>
//#include <openssl/rsa.h>
#include <openssl/x509.h>
// 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

22
module-emulator-biss.h Executable file
View File

@ -0,0 +1,22 @@
#ifndef MODULE_EMULATOR_BISS_H
#define MODULE_EMULATOR_BISS_H
#ifdef WITH_EMU
#include <openssl/rsa.h>
#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

688
module-emulator-cryptoworks.c Executable file
View File

@ -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

10
module-emulator-cryptoworks.h Executable file
View File

@ -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

644
module-emulator-director.c Executable file
View File

@ -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

11
module-emulator-director.h Executable file
View File

@ -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

602
module-emulator-irdeto.c Executable file
View File

@ -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

15
module-emulator-irdeto.h Executable file
View File

@ -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

376
module-emulator-nagravision.c Executable file
View File

@ -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

10
module-emulator-nagravision.h Executable file
View File

@ -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

72
module-emulator-omnicrypt.c Executable file
View File

@ -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

10
module-emulator-omnicrypt.h Executable file
View File

@ -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

986
module-emulator-osemu.c Executable file
View File

@ -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

96
module-emulator-osemu.h Executable file
View File

@ -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_

2795
module-emulator-powervu.c Executable file

File diff suppressed because it is too large Load Diff

63
module-emulator-powervu.h Executable file
View File

@ -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

1183
module-emulator-viaccess.c Executable file

File diff suppressed because it is too large Load Diff

11
module-emulator-viaccess.h Executable file
View File

@ -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

894
module-emulator.c Executable file
View File

@ -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

View File

@ -6,6 +6,9 @@
#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);

View File

@ -1118,6 +1118,13 @@ static int8_t newcamd_auth_client(IN_ADDR_T ip, uint8_t *deskey)
// 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);
}
@ -1184,7 +1191,7 @@ static int8_t newcamd_auth_client(IN_ADDR_T ip, uint8_t *deskey)
uint32_t rprid;
found = 0;
if(pufilt->caid == aureader->caid)
if(pufilt->caid == aureader->caid && aureader->typ != R_EMU)
{
for(k = 0; k < aureader->nprov; k++)
{

View File

@ -901,7 +901,7 @@ void check_lb_auto_betatunnel_mode(ECM_REQUEST *er)
uint16_t get_rdr_caid(struct s_reader *rdr)
{
if(is_network_reader(rdr))
if(is_network_reader(rdr) || rdr->typ == R_EMU)
{
return 0; // reader caid is not real caid
}
@ -1292,7 +1292,7 @@ void stat_get_best_reader(ECM_REQUEST *er)
for(ea = er->matching_rdr; ea; ea = ea->next)
{
rdr = ea->reader;
if(is_network_reader(rdr)) // reader caid is not real caid
if(is_network_reader(rdr) || rdr->typ == R_EMU) // reader caid is not real caid
{
prv = ea;
continue; // proxy can convert or reject
@ -2094,14 +2094,13 @@ void lb_update_last(struct s_ecm_answer *ea_er, struct s_reader *reader)
cs_ftime(&reader->lb_last);
}
void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc)
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; }
int32_t ecm_time = cfg.ctimeout;
if(ea->ecm_time && ea->rc <= E_NOTFOUND)
{ ecm_time = ea->ecm_time; }
if(!ecm_time)
ecm_time = cfg.ctimeout;
add_stat(rdr, er, ecm_time, rc, ea->rcEx);
}

View File

@ -15,7 +15,7 @@ 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);
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);
@ -32,7 +32,7 @@ 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)) { }
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)) { }

File diff suppressed because it is too large Load Diff

View File

@ -10,26 +10,47 @@
#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 <dvbcsa/dvbcsa.h>
#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
#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;
@ -58,7 +79,20 @@ typedef struct
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);
@ -66,6 +100,30 @@ 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_

View File

@ -456,6 +456,7 @@ char *tpl_getUnparsedTpl(const char *name, int8_t removeHeader, const char *subd
check_conf(WITH_SSL, ptr2);
check_conf(WITH_STAPI, ptr2);
check_conf(WITH_STAPI5, ptr2);
check_conf(WITH_EMU, ptr2);
} // for
if(ok == 0)
{

View File

@ -30,18 +30,25 @@
#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
uint32_t webif_last_nodeid[WEBIF_MAX_NODES];
extern const struct s_cardreader *cardreaders[];
extern char cs_confdir[];
extern uint32_t ecmcwcache_size;
@ -143,8 +150,9 @@ static bool use_srvid2 = false;
#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 30 // sum of items above. Use it for "All inactive" in function calls too.
#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)
{
@ -457,8 +465,7 @@ static void refresh_oscam(enum refreshtypes refreshtype)
case REFR_SERVER:
cs_log("Refresh Server requested by WebIF from %s", cs_inet_ntoa(GET_IP()));
//kill(first_client->pid, SIGHUP);
//todo how I can refresh the server after global settings
reload_global_config(); // Wczytaj ponownie globalną konfigurację
break;
case REFR_SERVICES:
@ -610,13 +617,55 @@ static void webif_save_config(char *section, struct templatevars *vars, struct u
}
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"))
{ config_set(section, token, value); }
{
// 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.");
@ -742,6 +791,20 @@ static char *send_oscam_config_global(struct templatevars *vars, struct uriparam
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
@ -1345,6 +1408,11 @@ static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uri
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");
@ -2464,7 +2532,7 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
chk_reader("services", servicelabels, rdr);
chk_reader("lb_whitelist_services", servicelabelslb, rdr);
if(is_network_reader(rdr)) //physical readers make trouble if re-started
if(is_network_reader(rdr) || rdr->typ == R_EMU) //physical readers make trouble if re-started
{
if(rdr)
{
@ -3261,6 +3329,8 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
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
@ -3298,6 +3368,23 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
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
@ -3319,6 +3406,9 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
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;
@ -5356,9 +5446,34 @@ static char *send_oscam_entitlement(struct templatevars *vars, struct uriparams
tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", "<BR><BR>New Structure:<BR>");
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);
@ -5855,6 +5970,9 @@ static char *send_oscam_status(struct templatevars * vars, struct uriparams * pa
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
}
}
@ -7446,6 +7564,9 @@ static char *send_oscam_files(struct templatevars * vars, struct uriparams * par
{ "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 },
};
@ -7918,8 +8039,12 @@ static char *send_oscam_EMM_running(struct templatevars * vars, struct uriparams
else if(!proxy && rdr->csystem_active) // local active reader
{
csystem = rdr->csystem;
if(rdr->typ != R_EMU)
{
caid = rdr->caid;
}
}
if(csystem)
{
@ -8201,7 +8326,6 @@ static char *send_oscam_cacheex(struct templatevars * vars, struct uriparams * p
}
}
{ tpl_addVar(vars, TPLADD, "IP", ""); }
@ -8224,20 +8348,7 @@ else
webif_last_nodeid[idx] = current_node;
// --- end detection ---
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);
@ -9245,7 +9356,11 @@ static int32_t readRequest(FILE * f, IN_ADDR_T in, char **result, int8_t forcePl
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);

View File

@ -30,6 +30,7 @@
#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"
@ -141,8 +142,9 @@ static bool use_srvid2 = false;
#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 30 // sum of items above. Use it for "All inactive" in function calls too.
#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)
{
@ -455,8 +457,7 @@ static void refresh_oscam(enum refreshtypes refreshtype)
case REFR_SERVER:
cs_log("Refresh Server requested by WebIF from %s", cs_inet_ntoa(GET_IP()));
//kill(first_client->pid, SIGHUP);
//todo how I can refresh the server after global settings
reload_global_config(); // Wczytaj ponownie globalną konfigurację
break;
case REFR_SERVICES:
@ -608,13 +609,55 @@ static void webif_save_config(char *section, struct templatevars *vars, struct u
}
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"))
{ config_set(section, token, value); }
{
// 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.");
@ -740,6 +783,20 @@ static char *send_oscam_config_global(struct templatevars *vars, struct uriparam
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
@ -982,14 +1039,6 @@ static char *send_oscam_config_cache(struct templatevars *vars, struct uriparams
tpl_addVar(vars, TPLADD, "CACHEEXSTATSSELECTED", (cfg.cacheex_enable_stats == 1) ? "checked" : "");
/* GLOBAL CACHEEX MAXHOP */
tpl_printf(vars, TPLADD, "CACHEEX_GLOBAL_MAXHOP",cfg.cacheex_global_maxhop ? "checked" : "");
tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_GLOBAL", "%d", cfg.cacheex_maxhop);
tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_LG_GLOBAL", "%d", cfg.cacheex_maxhop_lg);
tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_PERCAID", "%s", mk_t_caidvaluetab(&cfg.cacheex_maxhop_percaid));
tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_LG_PERCAID", "%s", mk_t_caidvaluetab(&cfg.cacheex_maxhop_lg_percaid));
tpl_addVar(vars, TPLADD, "WTTCHECKED", (cfg.wait_until_ctimeout == 1) ? "checked" : "");
#ifdef CS_CACHEEX_AIO
@ -1343,6 +1392,11 @@ static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uri
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");
@ -2462,7 +2516,7 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
chk_reader("services", servicelabels, rdr);
chk_reader("lb_whitelist_services", servicelabelslb, rdr);
if(is_network_reader(rdr)) //physical readers make trouble if re-started
if(is_network_reader(rdr) || rdr->typ == R_EMU) //physical readers make trouble if re-started
{
if(rdr)
{
@ -3259,6 +3313,8 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
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
@ -3296,6 +3352,23 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
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
@ -3317,6 +3390,9 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
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;
@ -5354,9 +5430,34 @@ static char *send_oscam_entitlement(struct templatevars *vars, struct uriparams
tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", "<BR><BR>New Structure:<BR>");
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);
@ -5853,6 +5954,9 @@ static char *send_oscam_status(struct templatevars * vars, struct uriparams * pa
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
}
}
@ -7444,6 +7548,9 @@ static char *send_oscam_files(struct templatevars * vars, struct uriparams * par
{ "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 },
};
@ -7916,8 +8023,12 @@ static char *send_oscam_EMM_running(struct templatevars * vars, struct uriparams
else if(!proxy && rdr->csystem_active) // local active reader
{
csystem = rdr->csystem;
if(rdr->typ != R_EMU)
{
caid = rdr->caid;
}
}
if(csystem)
{
@ -8199,29 +8310,6 @@ static char *send_oscam_cacheex(struct templatevars * vars, struct uriparams * p
}
}
{ 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", "<font color=\"red\"><b>%" PRIu64 "X</b></font>", 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 ---
if(IP_ISSET(cl->ip))
{ tpl_addVar(vars, TPLADD, "IP", cs_inet_ntoa(cl->ip)); }
else if(cl->login > cl->logout)
@ -8284,17 +8372,7 @@ webif_last_nodeid[idx] = current_node;
{ 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", "<font color=\"red\"><b>%" PRIu64 "X</b></font>", current_node);
}
else
{
tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", current_node);
}
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);
@ -9243,7 +9321,11 @@ static int32_t readRequest(FILE * f, IN_ADDR_T in, char **result, int8_t forcePl
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);

View File

@ -1,985 +0,0 @@
#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;
}
}
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

View File

@ -894,7 +894,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
return 0;
}
if( !is_network_reader(rdr) && (rdr->caid >> 8 != ((er->caid >> 8) & 0xFF)) && (rdr->caid >> 8 != ((er->ocaid >> 8) & 0xFF)) )
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; }
@ -932,7 +932,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
}
// Checking ident:
if(!chk_rfilter(er, rdr))
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);
@ -993,7 +993,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
}
// Checking entitlements:
if(ll_count(rdr->ll_entitlements) > 0)
if(ll_count(rdr->ll_entitlements) > 0 && !(rdr->typ == R_EMU))
{
LL_ITER itr = ll_iter_create(rdr->ll_entitlements);
S_ENTITLEMENT *item;
@ -1174,7 +1174,7 @@ int32_t chk_caid(uint16_t caid, CAIDTAB *ctab)
int32_t chk_caid_rdr(struct s_reader *rdr, uint16_t caid)
{
if(is_network_reader(rdr))
if(is_network_reader(rdr) || rdr->typ == R_EMU)
{
return 1; // reader caid is not real caid
}
@ -1268,7 +1268,7 @@ int8_t chk_fullCW(ECM_REQUEST *er, uint8_t *cw)
return -1;
}
// jeœli to halfCW system — pomijamy
// jeśli to halfCW system — pomijamy
if(is_halfCW_er(er))
{
return -1;
@ -1277,13 +1277,13 @@ int8_t chk_fullCW(ECM_REQUEST *er, uint8_t *cw)
int8_t part1 = checkCWpart(cw, 0);
int8_t part2 = checkCWpart(cw, 1);
// full CW musi mieæ obie czêœci poprawne
// full CW musi mieć obie części poprawne
if(part1 && part2)
{
return 1; // OK
}
return 0; // Z£Y FULL CW
return 0; // ZŁY FULL CW
}

View File

@ -894,7 +894,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
return 0;
}
if( !is_network_reader(rdr) && (rdr->caid >> 8 != ((er->caid >> 8) & 0xFF)) && (rdr->caid >> 8 != ((er->ocaid >> 8) & 0xFF)) )
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; }
@ -932,7 +932,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
}
// Checking ident:
if(!chk_rfilter(er, rdr))
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);
@ -993,7 +993,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
}
// Checking entitlements:
if(ll_count(rdr->ll_entitlements) > 0)
if(ll_count(rdr->ll_entitlements) > 0 && !(rdr->typ == R_EMU))
{
LL_ITER itr = ll_iter_create(rdr->ll_entitlements);
S_ENTITLEMENT *item;
@ -1174,7 +1174,7 @@ int32_t chk_caid(uint16_t caid, CAIDTAB *ctab)
int32_t chk_caid_rdr(struct s_reader *rdr, uint16_t caid)
{
if(is_network_reader(rdr))
if(is_network_reader(rdr) || rdr->typ == R_EMU)
{
return 1; // reader caid is not real caid
}

11
oscam-chk.c.rej Executable file
View File

@ -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;
}

View File

@ -622,3 +622,47 @@ 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;
}

View File

@ -15,9 +15,11 @@ 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 **sip);
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

View File

@ -1109,6 +1109,26 @@ char *mk_t_allowedtimeframe(struct s_auth *account)
* 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); }

View File

@ -30,6 +30,7 @@ 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

View File

@ -70,7 +70,8 @@ int config_list_parse(const struct config_list *clist, const char *token, char *
{
case OPT_INT8:
{
*(int8_t *)var = (int8_t)strToIntVal(value, c->def.d_int8);
int8_t tmp = (int8_t)strToIntVal(value, c->def.d_int8);
*(int8_t *)var = tmp;
return 1;
}
case OPT_UINT8:
@ -91,6 +92,12 @@ int config_list_parse(const struct config_list *clist, const char *token, char *
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;
@ -173,24 +180,35 @@ void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_
{
int8_t val = *(int8_t *)var;
// always save pmt_mode, because external tools parse it
if(save_all || val != c->def.d_int8 || !strcmp(c->config_name, "pmt_mode"))
{ fprintf_conf(f, c->config_name, "%d\n", val); }
// 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); }
{
fprintf_conf(f, c->config_name, "%u\n", val);
}
continue;
}
case OPT_INT32:
{
int32_t val;
memcpy(&val, var, sizeof(int32_t));
if(save_all || val != c->def.d_int32)
{ fprintf_conf(f, c->config_name, "%d\n", val); }
// 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:
@ -198,7 +216,20 @@ void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_
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); }
{
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:
@ -238,8 +269,18 @@ void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_
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:
@ -310,6 +351,11 @@ void config_list_set_defaults(const struct config_list *clist, void *config_data
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;
@ -520,7 +566,7 @@ FILE *create_config_file(const char *conf_filename)
bool flush_config_file(FILE *f, const char *conf_filename)
{
char dst_file[256], tmp_file[256], bak_file[256];
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));
@ -530,5 +576,6 @@ bool flush_config_file(FILE *f, const char *conf_filename)
{
fclose(f);
}
return safe_overwrite_with_bak(dst_file, tmp_file, bak_file, cfg.http_overwrite_bak_file);
int32_t result = safe_overwrite_with_bak(dst_file, tmp_file, bak_file, cfg.http_overwrite_bak_file);
return result;
}

View File

@ -10,6 +10,7 @@ enum opt_types
OPT_UINT8,
OPT_INT32,
OPT_UINT32,
OPT_FLOAT,
OPT_STRING,
OPT_SSTRING,
OPT_HEX_ARRAY,
@ -31,6 +32,7 @@ struct config_list
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;
@ -51,7 +53,10 @@ struct config_list
.opt_type = OPT_INT8, \
.config_name = __name, \
.var_offset = __var_ofs, \
.def.d_int8 = __default \
.str_size = 0, \
.def.d_int8 = __default, \
.ops.process_fn = NULL, \
.free_value = NULL \
}
#define DEF_OPT_UINT8(__name, __var_ofs, __default) \
@ -59,7 +64,10 @@ struct config_list
.opt_type = OPT_UINT8, \
.config_name = __name, \
.var_offset = __var_ofs, \
.def.d_uint8 = __default \
.str_size = 0, \
.def.d_uint8 = __default, \
.ops.process_fn = NULL, \
.free_value = NULL \
}
#define DEF_OPT_INT32(__name, __var_ofs, __default) \
@ -67,7 +75,10 @@ struct config_list
.opt_type = OPT_INT32, \
.config_name = __name, \
.var_offset = __var_ofs, \
.def.d_int32 = __default \
.str_size = 0, \
.def.d_int32 = __default, \
.ops.process_fn = NULL, \
.free_value = NULL \
}
#define DEF_OPT_UINT32(__name, __var_ofs, __default) \
@ -75,7 +86,21 @@ struct config_list
.opt_type = OPT_UINT32, \
.config_name = __name, \
.var_offset = __var_ofs, \
.def.d_uint32 = __default \
.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) \
@ -83,7 +108,10 @@ struct config_list
.opt_type = OPT_STRING, \
.config_name = __name, \
.var_offset = __var_ofs, \
.def.d_char = __default \
.str_size = 0, \
.def.d_char = __default, \
.ops.process_fn = NULL, \
.free_value = NULL \
}
#define DEF_OPT_SSTR(__name, __var_ofs, __default, __str_size) \
@ -92,7 +120,9 @@ struct config_list
.config_name = __name, \
.var_offset = __var_ofs, \
.str_size = __str_size, \
.def.d_char = __default \
.def.d_char = __default, \
.ops.process_fn = NULL, \
.free_value = NULL \
}
#define DEF_OPT_HEX(__name, __var_ofs, __array_size) \
@ -100,7 +130,10 @@ struct config_list
.opt_type = OPT_HEX_ARRAY, \
.config_name = __name, \
.var_offset = __var_ofs, \
.def.array_size = __array_size \
.str_size = 0, \
.def.array_size = __array_size, \
.ops.process_fn = NULL, \
.free_value = NULL \
}
#define DEF_OPT_FUNC(__name, __var_ofs, __process_fn, ...) \
@ -108,6 +141,8 @@ struct config_list
.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__ \
}
@ -117,26 +152,43 @@ struct config_list
.opt_type = OPT_FUNC_EXTRA, \
.config_name = __name, \
.var_offset = __var_ofs, \
.ops.process_fn_extra = __process_fn_extra, \
.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, \
.ops.should_save_fn = __fn \
.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, \
.ops.fixup_fn = __fn \
.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 \
.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

View File

@ -478,6 +478,10 @@ static const struct config_list account_opts[] =
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_maxhop_percaid" , OFS(cacheex.maxhop_percaid), caidvaluetab_fn),
#ifdef CS_CACHEEX_AIO
DEF_OPT_FUNC("cacheex_maxhop_lg_percaid" , OFS(cacheex.maxhop_lg_percaid),caidvaluetab_fn),
#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),

View File

@ -199,6 +199,40 @@ void caidvaluetab_fn(const char *token, char *value, void *setting, FILE *f)
}
}
#ifdef __CYGWIN__
#include <windows.h>
#else
#include <sys/resource.h> // 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);
}
}
static void cwvote_local_weight_fixups_fn(void *UNUSED(var))
{
if(cfg.cwvote_local_weight < 0.0f || cfg.cwvote_local_weight > 10.0f) {
cfg.cwvote_local_weight = 1.0f;
cs_log("CW Vote: local_weight out of range (0.0-10.0), using default value 1.0");
}
}
#ifdef CS_CACHEEX
void cacheex_valuetab_fn(const char *token, char *value, void *setting, FILE *f)
{
@ -264,12 +298,6 @@ void cacheex_hitvaluetab_fn(const char *token, char *value, void *setting, FILE
}
#endif
#ifdef __CYGWIN__
#include <windows.h>
#else
#include <sys/resource.h> // for setpriority
#endif
void global_fixups_fn(void *UNUSED(var))
{
if(!cfg.usrfile) { cfg.disableuserfile = 1; }
@ -317,6 +345,7 @@ void global_fixups_fn(void *UNUSED(var))
static const struct config_list global_opts[] =
{
DEF_OPT_FIXUP_FUNC(global_fixups_fn),
DEF_OPT_FIXUP_FUNC(cwvote_local_weight_fixups_fn),
#ifdef LEDSUPPORT
DEF_OPT_INT8("enableled" , OFS(enableled) , 0),
#endif
@ -398,6 +427,15 @@ static const struct config_list global_opts[] =
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
@ -949,6 +987,10 @@ static const struct config_list streamrelay_opts[] =
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
@ -1399,6 +1441,7 @@ void config_free(void)
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);
@ -1460,6 +1503,7 @@ int32_t init_config(void)
#ifdef HAVE_DVBAPI
cfg.dvbapi_enabled = 1;
#endif
cs_log("DEBUG: init_config - No oscam.conf found, using default settings.");
return 0;
}
@ -1529,3 +1573,73 @@ int32_t write_config(void)
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -114,6 +114,7 @@ static void protocol_fn(const char *token, char *value, void *setting, FILE *f)
{ "newcamd525", R_NEWCAMD },
{ "newcamd524", R_NEWCAMD },
{ "drecas", R_DRECAS },
{ "emu", R_EMU },
{ NULL, 0 }
}, *p;
int i;
@ -690,6 +691,9 @@ void ftab_fn(const char *token, char *value, void *setting, long ftab_type, FILE
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
@ -1196,6 +1200,10 @@ static const struct config_list reader_opts[] =
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_maxhop_percaid" , OFS(cacheex.maxhop_percaid), caidvaluetab_fn),
#ifdef CS_CACHEEX_AIO
DEF_OPT_FUNC("cacheex_maxhop_lg_percaid" , OFS(cacheex.maxhop_lg_percaid), caidvaluetab_fn),
#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),
@ -1350,6 +1358,7 @@ static const struct config_list reader_opts[] =
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),
@ -1364,6 +1373,10 @@ static const struct config_list reader_opts[] =
#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
@ -1466,7 +1479,7 @@ static bool reader_check_setting(const struct config_list *UNUSED(clist), void *
// These are written only when the reader is CCCAM
static const char *cccam_settings[] =
{
"cccversion", "cccmaxhops", "cccmindown", "ccckeepalive",
"cccversion", "cccmaxhops", "cccmindown", "cccwantemu", "ccckeepalive",
"cccreconnect",
0
};

View File

@ -796,7 +796,7 @@ 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;
uint8_t cw[16], wrong_checksum, __attribute__((unused)) c, have_fakecw = 0;
FILE *fp;
memset(alloccount, 0, sizeof(count));
@ -827,15 +827,6 @@ int32_t init_fakecws(void)
{
wrong_checksum = 0;
for(i = 0; i < 16; i += 4)
{
c = ((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff);
if(cw[i + 3] != c)
{
wrong_checksum = 1;
}
}
if(wrong_checksum)
{
cs_log("skipping fake cw %s because of wrong checksum!", cw_string);
@ -886,15 +877,6 @@ int32_t init_fakecws(void)
{
wrong_checksum = 0;
for(i = 0; i < 16; i += 4)
{
c = ((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff);
if(cw[i + 3] != c)
{
wrong_checksum = 1;
}
}
if(!wrong_checksum)
{
idx = ((cw[0] & 0xF) << 4) | (cw[8] & 0xF);

1494
oscam-config.c.orig Executable file

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@ 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);
@ -64,6 +65,7 @@ enum ftab_fn
FTAB_LOCALCARDS = 0x20,
FTAB_IGNCHKSMCAID = 0x40,
FTAB_IGNCRCCEX4USERONLYFOR = 0x80,
FTAB_EMUAU = 0x100,
FTAB_CCCGBXRESHARE = 0x200
};

File diff suppressed because it is too large Load Diff

View File

@ -2,54 +2,6 @@
#include "globals.h"
#include <math.h>
// ---------- ENTROPY FUNCTIONS START ----------
static double entropy_calc(const uint8_t *data, int len)
{
if(!data || len <= 0)
return 0.0;
int freq[256] = {0};
for(int i = 0; i < len; i++)
freq[data[i]]++;
double entropy = 0.0;
for(int i = 0; i < 256; i++)
{
if(freq[i] == 0)
continue;
double p = (double)freq[i] / len;
entropy -= p * (log(p) / log(2));
}
return entropy;
}
static void entropy_log_ecm(ECM_REQUEST *er, struct s_reader *reader)
{
if(!er)
return;
double full = entropy_calc(er->cw, 16);
double half1 = entropy_calc(er->cw, 8);
double half2 = entropy_calc(er->cw + 8, 8);
cs_log_dbg(D_TRACE,
"ENTROPY reader=%s caid=%04X FULL=%.4f HALF1=%.4f HALF2=%.4f",
reader ? reader->label : "?",
er->caid,
full,
half1,
half2);
}
// ---------- ENTROPY FUNCTIONS END ----------
#define ENTROPY_THRESHOLD_DEFAULT 3.0
#define ENTROPY_THRESHOLD_NDS 2.3
#include "cscrypt/md5.h"
#include "module-anticasc.h"
#include "module-cacheex.h"
@ -76,91 +28,6 @@ static void entropy_log_ecm(ECM_REQUEST *er, struct s_reader *reader)
#include "oscam-hashtable.h"
#endif
// ADD THIS LINE:
static int cw_checksum_ok(uint8_t *cw);
/////////////////////////////////////////////////////////////
// AUTODETECT STRUCT AND TABLE (ADD THIS HERE)
/////////////////////////////////////////////////////////////
#define CW_DETECT_MAX 8192
#define CW_LEARN_THRESHOLD 5
typedef struct
{
uint16_t caid;
uint32_t prid;
uint16_t srvid;
uint8_t is64;
uint8_t is48;
uint8_t learn64_count;
uint8_t learn48_count;
uint8_t strict_logged;
} cw_detect_entry;
static cw_detect_entry cw_detect_table[CW_DETECT_MAX];
/////////////////////////////////////////////////////////////////////////////////////////////////////
// GET OR CREATE ENTRY
//////////////////////////////////////////////////////////////////////////////////////////////////////
static cw_detect_entry *cw_detect_get(ECM_REQUEST *er)
{
int i;
// search existing entry
for(i = 0; i < CW_DETECT_MAX; i++)
{
if(cw_detect_table[i].caid == er->caid &&
cw_detect_table[i].prid == er->prid &&
cw_detect_table[i].srvid == er->srvid)
{
return &cw_detect_table[i];
}
}
// create new entry
for(i = 0; i < CW_DETECT_MAX; i++)
{
if(cw_detect_table[i].caid == 0)
{
cw_detect_table[i].caid = er->caid;
cw_detect_table[i].prid = er->prid;
cw_detect_table[i].srvid = er->srvid;
cw_detect_table[i].is64 = 0;
cw_detect_table[i].is48 = 0;
cw_detect_table[i].learn64_count = 0;
cw_detect_table[i].learn48_count = 0;
cw_detect_table[i].strict_logged = 0;
return &cw_detect_table[i];
}
}
return &cw_detect_table[0];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CW CHECKSUM FUNCTION (ADD THIS BELOW cw_detect_get)
///////////////////////////////////////////////////////////////////////////////////////////////////////////
static int cw_checksum_ok(uint8_t *cw)
{
if(((cw[0]+cw[1]+cw[2])&0xFF)!=cw[3]) return 0;
if(((cw[4]+cw[5]+cw[6])&0xFF)!=cw[7]) return 0;
if(((cw[8]+cw[9]+cw[10])&0xFF)!=cw[11]) return 0;
if(((cw[12]+cw[13]+cw[14])&0xFF)!=cw[15]) return 0;
return 1;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
extern CS_MUTEX_LOCK ecmcache_lock;
extern struct ecm_request_t *ecmcwcache;
extern uint32_t ecmcwcache_size;
@ -921,127 +788,7 @@ uint8_t checkCWpart(uint8_t *cw, int8_t part)
if(cw[i + eo]) { return 1; }
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ===== CW AUTODETECT PATCH WITH LEARN THRESHOLD (48 + 64 LEARNING) =====
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define CW_LEARN_THRESHOLD 5
static int cw_autodetect_and_block(struct s_reader *reader, ECM_REQUEST *er)
{
if(!er || !er->cw)
return 1;
cw_detect_entry *e = cw_detect_get(er);
const char *reader_name =
(reader && reader->label) ? reader->label : "?";
int checksum_ok = cw_checksum_ok(er->cw);
int whitelist =
chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for) ||
(reader && chk_if_ignore_checksum(er, &reader->disablecrccws_only_for));
/////////////////////////////////////////////////////////////
// STRICT MODE (NOT WHITELIST) → ONLY 48bit ALLOWED
/////////////////////////////////////////////////////////////
if(!whitelist)
{
if(!e->strict_logged)
{
cs_log("AUTODETECT STRICT 48bit MODE reader=%s (%04X@%06X/%04X)",
reader_name, er->caid, er->prid, er->srvid);
e->strict_logged = 1;
}
// block 64bit
if(!checksum_ok)
{
cs_log("AUTODETECT BLOCK 64bit reader=%s (%04X@%06X/%04X)",
reader_name, er->caid, er->prid, er->srvid);
return 0;
}
return 1;
}
/////////////////////////////////////////////////////////////
// WHITELIST MODE → LEARN 48bit AND 64bit SEPARATELY
/////////////////////////////////////////////////////////////
// ---------- LEARN 64bit ----------
if(!checksum_ok)
{
if(!e->is64)
{
if(e->learn64_count < CW_LEARN_THRESHOLD)
{
e->learn64_count++;
cs_log("AUTODETECT LEARN 64bit STEP %d/%d reader=%s (%04X@%06X/%04X)",
e->learn64_count,
CW_LEARN_THRESHOLD,
reader_name,
er->caid,
er->prid,
er->srvid);
}
if(e->learn64_count >= CW_LEARN_THRESHOLD)
{
e->is64 = 1;
cs_log("AUTODETECT LOCKED 64bit reader=%s (%04X@%06X/%04X)",
reader_name,
er->caid,
er->prid,
er->srvid);
}
}
return 1;
}
// ---------- LEARN 48bit ----------
if(checksum_ok)
{
if(!e->is48)
{
if(e->learn48_count < CW_LEARN_THRESHOLD)
{
e->learn48_count++;
cs_log("AUTODETECT LEARN 48bit STEP %d/%d reader=%s (%04X@%06X/%04X)",
e->learn48_count,
CW_LEARN_THRESHOLD,
reader_name,
er->caid,
er->prid,
er->srvid);
}
if(e->learn48_count >= CW_LEARN_THRESHOLD)
{
e->is48 = 1;
cs_log("AUTODETECT LOCKED 48bit reader=%s (%04X@%06X/%04X)",
reader_name,
er->caid,
er->prid,
er->srvid);
}
}
return 1;
}
return 1;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void update_n_request(void)
{
struct s_client *cl;
@ -2671,6 +2418,10 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui
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)
{
@ -2690,7 +2441,7 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui
}
}
if(rc < E_NOTFOUND && cw && chk_is_null_CW(cw))
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 : "-");
@ -2832,116 +2583,6 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui
#endif
//END -- SPECIAL CHECKs for rc
//////////////// TRUST / ENTROPY / AUTODETECT BLOCK ////////////////////////////////////////////////////////////////
if(rc == E_FOUND && cw && !chk_is_null_CW(cw))
{
/* SKIP CACHEEX readers */
if(reader && reader->cacheex.mode)
goto TRUST_DONE;
/* Używamy lokalnej kopii CW do testów */
uint8_t testcw[16];
memcpy(testcw, cw, 16);
////////////////////////////////////////////////////////////
// ENTROPY CALC
////////////////////////////////////////////////////////////
double full = entropy_calc(testcw, 16);
double half1 = entropy_calc(testcw, 8);
double half2 = entropy_calc(testcw + 8, 8);
double threshold;
double entropy_value;
// NDS HALF entropy (always 64bit CAID)
if(er->caid == 0x098D || er->caid == 0x098C)
{
entropy_value = (half1 > half2) ? half1 : half2;
threshold = 1.35;
}
else
{
entropy_value = full;
threshold = 2.5778;
}
////////////////////////////////////////////////////////////
// ENTROPY BLOCK (LEVEL 1)
////////////////////////////////////////////////////////////
if(entropy_value < threshold)
{
cs_log("TRUST BLOCK ENTROPY reader=%s (%04X@%06X/%04X) entropy=%.4f threshold=%.4f",
reader ? reader->label : "?",
er->caid,
er->prid,
er->srvid,
entropy_value,
threshold);
rc = E_NOTFOUND;
rcEx = E2_WRONG_CHKSUM;
cw = NULL;
goto TRUST_DONE;
}
////////////////////////////////////////////////////////////
// AUTODETECT BLOCK (LEVEL 2)
////////////////////////////////////////////////////////////
/* ustaw testcw tymczasowo do er->cw dla autodetect */
uint8_t original_cw[16];
memcpy(original_cw, er->cw, 16);
memcpy(er->cw, testcw, 16);
if(!cw_autodetect_and_block(reader, er))
{
cs_log("TRUST BLOCK AUTODETECT reader=%s (%04X@%06X/%04X) entropy=%.4f threshold=%.4f",
reader ? reader->label : "?",
er->caid,
er->prid,
er->srvid,
entropy_value,
threshold);
rc = E_NOTFOUND;
rcEx = E2_WRONG_CHKSUM;
memset(er->cw, 0, 16);
cw = NULL;
goto TRUST_DONE;
}
////////////////////////////////////////////////////////////
// TRUST OK → FINAL COPY
////////////////////////////////////////////////////////////
memcpy(er->cw, testcw, 16);
cs_log("TRUST OK reader=%s (%04X@%06X/%04X) entropy=%.4f threshold=%.4f",
reader ? reader->label : "?",
er->caid,
er->prid,
er->srvid,
entropy_value,
threshold);
TRUST_DONE:
;
}
// global protection
if(rc != E_FOUND)
{
cw = NULL;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef CS_CACHEEX_AIO
if(!dontsetAnswered)
{
@ -2987,7 +2628,9 @@ if(rc != E_FOUND)
if(!ea->is_pending) // not for pending ea - only once for ea
{
// cache update
if(ea && (ea->rc < E_NOTFOUND) && !chk_is_null_CW(ea->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(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;
@ -3207,18 +2850,7 @@ void write_ecm_answer_fromcache(struct s_write_from_cache *wfc)
{ er->rc=E_CACHE1; } // from normal readers
memcpy(er->cw, ecm->cw, 16);
er->selected_reader = ecm->selected_reader;
entropy_log_ecm(er, er->selected_reader);
//////////////// AUTODETECT INVALID CW BLOCK ////////////////////////////////////////////////
if(!cw_autodetect_and_block(er->selected_reader, er))
{
return;
}
////////////////////////////////////////////////////////////////////////////////////////////
er->cw_count = ecm->cw_count;
#ifdef CS_CACHEEX
@ -3347,20 +2979,7 @@ static bool ecm_cache_check(ECM_REQUEST *er)
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))
{
@ -3654,7 +3273,9 @@ void get_cw(struct s_client *client, ECM_REQUEST *er)
}
// Check for odd/even byte
if(get_odd_even(er) == 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_TRACE, "warning: ecm with null odd/even byte from %s", (check_client(er->client) ? er->client->account->usr : "-"));
er->rc = E_INVALID;

View File

@ -33,6 +33,12 @@ 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);

View File

@ -40,6 +40,7 @@ static int8_t cs_emmlen_is_blocked(struct s_reader *rdr, int16_t len)
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()
@ -242,6 +243,24 @@ int32_t emm_reader_match(struct s_reader *reader, uint16_t caid, uint32_t provid
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);

View File

@ -143,6 +143,10 @@ int32_t file_copy(char *srcfile, char *destfile)
}
}
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);
}
@ -153,16 +157,17 @@ int32_t file_copy(char *srcfile, char *destfile)
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 copying original config file %s to %s. The original config will be left untouched!", destfile, bakfile);
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 removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
}
return 1;
}
@ -172,21 +177,21 @@ int32_t safe_overwrite_with_bak(char *destfile, char *temp_file, char *bakfile,
rc = file_copy(temp_file, destfile);
if(rc < 0)
{
cs_log("An error occured while writing the new config file %s.", destfile);
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("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.");
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 removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
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 removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
}
return 0;
}

View File

@ -468,6 +468,8 @@ const char *get_cardsystem_desc_by_caid(uint16_t caid)
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"; }
@ -476,6 +478,7 @@ const char *get_cardsystem_desc_by_caid(uint16_t caid)
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";
}

View File

@ -119,7 +119,7 @@ void *work_thread(void *ptr)
struct s_module *module = get_module(cl);
uint16_t bufsize = module->bufsize; // CCCam needs more than 1024bytes!
if(!bufsize)
{ bufsize = DEFAULT_MODULE_BUFSIZE; }
{ bufsize = DEFAULT_MODULE_BUFsize; }
uint8_t *mbuf;
if(!cs_malloc(&mbuf, bufsize))

20
oscam.c
View File

@ -44,6 +44,10 @@
#include "reader-common.h"
#include "module-gbox.h"
#ifdef WITH_EMU
void add_emu_reader(void);
#endif
#ifdef WITH_SSL
#include <openssl/crypto.h>
#include <openssl/ssl.h>
@ -372,9 +376,9 @@ static void write_versionfile(bool use_stdout)
st.tm_hour, st.tm_min, st.tm_sec);
}
fprintf(fp, "Build Date: %s\n", CS_BUILD_DATE);
fprintf(fp, "Version: %s@%s\n", CS_VERSION, CS_GIT_COMMIT);
fprintf(fp, "Compiler: %s\n", CS_TARGET);
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
@ -457,6 +461,8 @@ static void write_versionfile(bool use_stdout)
#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");
@ -1682,6 +1688,9 @@ const struct s_cardreader *cardreaders[] =
#ifdef CARDREADER_STINGER
&cardreader_stinger,
#endif
#ifdef WITH_EMU
&cardreader_emu,
#endif
NULL
};
@ -1800,6 +1809,8 @@ int32_t main(int32_t argc, char *argv[])
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));
@ -1861,6 +1872,9 @@ int32_t main(int32_t argc, char *argv[])
init_sidtab();
init_readerdb();
#ifdef WITH_EMU
add_emu_reader();
#endif
cfg.account = init_userdb();
init_signal();
init_provid();

0
rcEx
View File

View File

@ -15,6 +15,7 @@
#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[];
@ -152,6 +153,18 @@ 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);

View File

@ -17,5 +17,6 @@ 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

0
tier
View File

View File

@ -1,32 +0,0 @@
<TR><TH COLSPAN="2">CacheEx / CSP</TH></TR>
<TR><TD><A>CW Cache Settings:</A></TD><TD><input name="cw_cache_settings" type="text" maxlength="1400" value="##CWCACHESETTINGS##"><br />Format: caid[&amp;mask][@provid][$servid]:mode:timediff_old_cw[,n]</TD></TR>
<TR><TD><A>CW Cache Size:</A></TD><TD><input name="cw_cache_size" class="withunit short" type="text" maxlength="20" value="##CWCACHESIZE##"> count of max. CWs for CW cache </TD></TR>
<TR><TD><A>CW Cache Memory:</A></TD><TD><input name="cw_cache_memory" class="withunit short" type="text" maxlength="5" value="##CWCACHEMEMORY##"> MB of max. memory used for CW cache size</TD></TR>
<TR><TD><A>ECM droptime:</A></TD><TD><input name="ecm_cache_droptime" class="withunit" type="text" maxlength="2" value="##ECMDROPTIME##"><br /> s after known ECM requests are dropped</TD></TR>
<TR><TD><A>ECM Cache Size:</A></TD><TD><input name="ecm_cache_size" class="withunit short" type="text" maxlength="20" value="##ECMCACHESIZE##"> count of max. ECMs for ECM cache </TD></TR>
<TR><TD><A>ECM Cache Memory:</A></TD><TD><input name="ecm_cache_memory" class="withunit short" type="text" maxlength="5" value="##ECMCACHEMEMORY##"> MB of max. memory used for ECM cache size</TD></TR>
<TR><TD><A>CacheEx CW Check:</A></TD><TD><input name="cacheex_cw_check" type="text" maxlength="320" value="##CACHEEXCWCHECK##"><br />Format: caid[&amp;mask][@provid][$servid]:mode:counter[,n]</TD></TR>
<TR><TD><A>Wait time:</A></TD><TD><input name="wait_time" type="text" value="##WAIT_TIME##"> ms</TD></TR>
<TR><TD><A>Wait time block start count:</A></TD><TD><input name="waittime_block_start" class="withunit short" type="text" maxlength="2" value="##WAITTIME_BLOCK_START##">count of wait time timeouts to start blocking</TD></TR>
<TR><TD><A>Wait time block time:</A></TD><TD><input name="waittime_block_time" class="withunit short" type="text" maxlength="4" value="##WAITTIME_BLOCK_TIME##">s block wait time</TD></TR>
<TR><TD><A>Mode1 delay time:</A></TD><TD><input name="cacheex_mode1_delay" type="text" maxlength="320" value="##CACHEEXMODE1DELAY##"> ms</TD></TR>
<TR><TD><A>Max hit time:</A></TD><TD><input name="max_hit_time" class="withunit short" type="text" maxlength="5" value="##MAX_HIT_TIME##"> s keep hit for dynamic wait time</TD></TR>
<TR><TD><A data-p="cacheexenablestats_2">Write statistic:</A></TD><TD><input name="cacheexenablestats" value="0" type="hidden"><input name="cacheexenablestats" value="1" type="checkbox" ##CACHEEXSTATSSELECTED##><label></label></TD></TR> <TR><TD><A>Wait until ctimeout:</A></TD><TD><input name="wait_until_ctimeout" value="0" type="hidden"><input name="wait_until_ctimeout" value="1" type="checkbox" ##WTTCHECKED##><label></label></TD></TR>
<TR><TD><A>Drop diff CWs:</A></TD><TD><input name="cacheex_dropdiffs" value="0" type="hidden"><input name="cacheex_dropdiffs" value="1" type="checkbox" ##CACHEEXDROPDIFFS##></TD></TR>
<TR><TD><A>No CW push after (from local/proxy-reader):</A></TD><TD><input name="cacheex_nopushafter" type="text" maxlength="320" value="##CACHEEXNOPUSHAFTER##"> ms<br />Format: caid:time[,n]</TD></TR>
<TR><TD><A>Allow client to overwrite 'Forward lg-only settings':</A></TD><TD><input name="cacheex_lg_only_remote_settings" value="0" type="hidden"><input name="cacheex_lg_only_remote_settings" value="1" type="checkbox" ##LGONLYREMOTESETTINGSCHECKED##>(if disabled, only more restrictive settings are added)</TD></TR>
<TR><TD><A>Forward localgenerated flagged CWs only:</A></TD><TD><input name="cacheex_localgenerated_only" value="0" type="hidden"><input name="cacheex_localgenerated_only" value="1" type="checkbox" ##LOCALGENERATEDONLYCHECKED##></TD></TR>
<TR><TD><A>Forward localgenerated flagged CWs only(caid:provid[,N];):</A></TD><TD><textarea name="cacheex_lg_only_tab" rows="3" class="bt">##LGONLYTAB##</textarea></TD></TR>
<TR><TD><A>Drop incoming not localgenerated flagged CWs:</A></TD><TD><input name="cacheex_localgenerated_only_in" value="0" type="hidden"><input name="cacheex_localgenerated_only_in" value="1" type="checkbox" ##LOCALGENERATEDONLYINCHECKED##></TD></TR>
<TR><TD><A>Drop incoming not localgenerated flagged CWs(caid:provid[,N];):</A></TD><TD><textarea name="cacheex_lg_only_in_tab" rows="3" class="bt">##LGONLYINTAB##</textarea></TD></TR>
<TR><TD><A>Drop incoming settings only for CX-AIO peers:</A></TD><TD><input name="cacheex_lg_only_in_aio_only" value="0" type="hidden"><input name="cacheex_lg_only_in_aio_only" value="1" type="checkbox" ##LGONLYINAIOONLYCHECKED##></TD></TR>
<TR><TD><A>Cache-EX ECM filter:</A></TD><TD><input name="cacheex_ecm_filter" type="text" maxlength="1385" value="##CACHEEXECMFILTER##"></TD></TR>
<TR><TD><A>Cache-EX ECM filter(aio only):</A></TD><TD><input name="cacheex_ecm_filter_aio" type="text" maxlength="1385" value="##CACHEEXECMFILTERAIO##"></TD></TR>
<TR><TD><A>Push localgenerated flagged CWs always to these groups:</A></TD><TD><input name="cacheex_push_lg_groups" class="longer" type="text" maxlength="155" value="##CACHEEXPUSHLGGRPS##"> Valid values 1-64</TD></TR>
<TR><TH COLSPAN="2">CSP</TH></TR>
<TR><TD><A>Port:</A></TD><TD><input name="csp_port" type="text" class="short" maxlength="5" value="##PORT##"></TD></TR>
<TR><TD><A>Serverip:</A></TD><TD><input name="csp_serverip" type="text" class="medium" maxlength="15" value="##SERVERIP##"></TD></TR>
<TR><TD><A>ECM filter:</A></TD><TD><input name="csp_ecm_filter" type="text" maxlength="320" value="##CSP_ECM_FILTER##"></TD></TR>
<TR><TD><A>ECM filter adv.:</A></TD><TD><input name="csp_allow_request" value="0" type="hidden"><input name="csp_allow_request" value="1" type="checkbox" ##ARCHECKED##><label>allow request</label></TD></TR>
<TR><TD><A>Reforward cacheex:</A></TD><TD><input name="csp_allow_reforward" value="0" type="hidden"><input name="csp_allow_reforward" value="1" type="checkbox" ##ARFCHECKED##><label>allow reforward</label></TD></TR>
<TR><TD><A>Block fake cws:</A></TD><TD><input name="csp_block_fakecws" value="0" type="hidden"><input name="csp_block_fakecws" value="1" type="checkbox" ##BLOCKFAKECWSCHECKED##><label>block fake cws</label></TD></TR>

View File

@ -142,6 +142,29 @@
<TR><TH COLSPAN="2">Doublecheck</TH></TR>
<TR><TD><A>ECM Doublecheck:</A></TD><TD><input name="double_check" value="0" type="hidden"><input name="double_check" value="1" type="checkbox" ##DCHECKCSELECTED##><label></label></TD></TR>
<TR><TD><A>Doublecheck caids:</A></TD><TD><input name="double_check_caid" type="text" maxlength="160" value="##DOUBLECHECKCAID##"></TD></TR>
<TR><TH COLSPAN="2">Ai Fake DCW Detector</TH></TR>
<TR><TD><A>Ai Vote Enabled:</A></TD><TD><input name="cwvote_enabled" value="0" type="hidden"><input name="cwvote_enabled" value="1" type="checkbox" ##CWVOTEENABLEDCHECKED##></TD></TR>
<TR><TD><A>Ai Vote Log Enabled:</A></TD><TD><input name="cwvote_log_enabled" value="0" type="hidden"><input name="cwvote_log_enabled" value="1" type="checkbox" ##CWVOTELOGENABLEDCHECKED##></TD></TR>
<TR><TD><A>Ai Vote Timeout (ms):</A></TD><TD><input name="cwvote_timeout" type="text" size="8" maxlength="8" value="##CWVOTETIMEOUT##"> <small>(default 1000)</small></TD></TR>
<TR><TD><A>Ai Vote Min Votes:</A></TD><TD><input name="cwvote_min_votes" type="text" size="4" maxlength="3" value="##CWVOTEMINVOTES##"> <small>(default min. 2)</small></TD></TR>
<TR><TD><A>Ai Vote Local Weight:</A></TD><TD><input name="cwvote_local_weight" type="text" size="5" maxlength="5" value="##CWVOTELOCALWEIGHT##"> <small>(0.0-10.0, default 2.0)</small></TD></TR>
<TR><TD><A>Ai Vote Max Candidates:</A></TD><TD><input name="cwvote_max_candidates" type="text" size="4" maxlength="3" value="##CWVOTEMAXCANDIDATES##"> <small>(default 8 caution! use max 16)</small></TD></TR>
<TR><TD><A>Ai Vote Compare Length:</A></TD><TD>
<select name="cwvote_compare_len">
<option value="8" ##CWVOTECOMPARE8##>8 bytes (recomended)</option>
<option value="16" ##CWVOTECOMPARE16##>16 bytes (pełne)</option>
</select>
</TD></TR>
<TR><TD><A>Ai Vote Fallback Mode:</A></TD><TD>
<select name="cwvote_fallback">
<option value="0" ##CWVOTEFALLBACK0##>0 - Keep Waiting</option>
<option value="1" ##CWVOTEFALLBACK1##>1 - Take best CW (recomended)</option>
<option value="2" ##CWVOTEFALLBACK2##>2 - Take first CW</option>
</select>
</TD></TR>
<TR><TD><A>Ai Vote CAIDs:</A></TD><TD><input name="cwvote_caids" type="text" maxlength="1024" value="##CWVOTECAIDS##"></TD></TR>
##TPLSUPPRESSCMD08##
##TPLGETBLOCKEMMAUPROVID##
##TPLENABLELEDBIT##

View File

@ -1,145 +0,0 @@
<input name="part" type="hidden" value="global">
<input name="suppresscmd08" type="hidden" value="0">
<TABLE CLASS="config">
<TR><TH COLSPAN="2">Edit Global Config</TH></TR>
<TR><TD><A>Serverip:</A></TD><TD><input name="serverip" class="medium" type="text" maxlength="15" value="##SERVERIP##"></TD></TR>
<TR><TD><A>Nice:</A></TD><TD><input name="nice" class="short" type="text" maxlength="3" value="##NICE##"></TD></TR>
<TR><TD><A>Net prio:</A></TD>
<TD>
<select name="netprio">
<option value="0" ##NETPRIO0##>0 - IPP=0; DSCP=CS0 (default)</option>
<option value="1" ##NETPRIO1##>1 - IPP=1; DSCP=CS1</option>
<option value="2" ##NETPRIO2##>2 - IPP=1; DSCP=AF11</option>
<option value="3" ##NETPRIO3##>3 - IPP=1; DSCP=AF12</option>
<option value="4" ##NETPRIO4##>4 - IPP=1; DSCP=AF13</option>
<option value="5" ##NETPRIO5##>5 - IPP=2; DSCP=CS2</option>
<option value="6" ##NETPRIO6##>6 - IPP=2; DSCP=AF21</option>
<option value="7" ##NETPRIO7##>7 - IPP=2; DSCP=AF22</option>
<option value="8" ##NETPRIO8##>8 - IPP=2; DSCP=AF23</option>
<option value="9" ##NETPRIO9##>9 - IPP=3; DSCP=CS3</option>
<option value="10" ##NETPRIO10##>10 - IPP=3; DSCP=AF31</option>
<option value="11" ##NETPRIO11##>11 - IPP=3; DSCP=AF32</option>
<option value="12" ##NETPRIO12##>12 - IPP=3; DSCP=AF33</option>
<option value="13" ##NETPRIO13##>13 - IPP=4; DSCP=CS4</option>
<option value="14" ##NETPRIO14##>14 - IPP=4; DSCP=AF41</option>
<option value="15" ##NETPRIO15##>15 - IPP=4; DSCP=AF42</option>
<option value="16" ##NETPRIO16##>16 - IPP=4; DSCP=AF43</option>
<option value="17" ##NETPRIO17##>17 - IPP=5; DSCP=CS5</option>
<option value="18" ##NETPRIO18##>18 - IPP=5; DSCP=EF</option>
<option value="19" ##NETPRIO19##>19 - IPP=6; DSCP=CS6</option>
<option value="20" ##NETPRIO20##>20 - IPP=7; DSCP=CS7</option>
</select>
</TD>
</TR>
<TR><TD><A>Bind wait:</A></TD><TD><input name="bindwait" class="withunit short" type="text" maxlength="5" value="##BINDWAIT##"> s</TD></TR>
<TR><TD><A>Resolver:</A></TD>
<TD>
<select name="resolvegethostbyname">
<option value="0" ##RESOLVER0##>0 - getadressinfo()</option>
<option value="1" ##RESOLVER1##>1 - gethostbyname()</option>
</select>
</TD>
</TR>
##TPLLOCALCARDS##
<TR><TD><A>Prefer local cards:</A></TD><TD><select name="preferlocalcards"><option value="0">0 - local cards like proxied</option><option value="1" ##PREFERCACHEEX##>1 - prefer cache-ex then local cards</option><option value="2" ##PREFERLOCALCARDS##>2 - prefer local cards above cache-ex</option></select></TD></TR>
##TPLUNLOCKPARENTAL##
<TR><TD>SIGHUP reload:</TD>
<TD CLASS="invisible">
<TABLE>
<TR>
<TD><A>accounts:</A></TD><TD><input name="reload_useraccounts" type="hidden" value="0"><input name="reload_useraccounts" type="checkbox" value="1" ##RELOADUSERACCOUNTSCHECKED##></TD>
<TD><A>readers:</A></TD><TD><input name="reload_readers" type="hidden" value="0"><input name="reload_readers" type="checkbox" value="1" ##RELOADREADERSCHECKED##></TD>
<TD><A>provid:</A></TD><TD><input name="reload_provid" type="hidden" value="0"><input name="reload_provid" type="checkbox" value="1" ##RELOADPROVIDCHECKED##></TD>
<TD><A>services ids:</A></TD><TD><input name="reload_services_ids" type="hidden" value="0"><input name="reload_services_ids" type="checkbox" value="1" ##RELOADSERVICESIDSCHECKED##></TD>
</TR>
<TR>
<TD><A>tier ids:</A></TD><TD><input name="reload_tier_ids" type="hidden" value="0"><input name="reload_tier_ids" type="checkbox" value="1" ##RELOADTIERUDSCHECKED##></TD>
<TD><A>fake CWs:</A></TD><TD><input name="reload_fakecws" type="hidden" value="0"><input name="reload_fakecws" type="checkbox" value="1" ##RELOADFAKECWSCHECKED##></TD>
<TD><A>AC stats:</A></TD><TD><input name="reload_ac_stat" type="hidden" value="0"><input name="reload_ac_stat" type="checkbox" value="1" ##RELOADACSTATCHECKED##></TD>
<TD><A>log file:</A></TD><TD><input name="reload_log" type="hidden" value="0"><input name="reload_log" type="checkbox" value="1" ##RELOADLOGCHECKED##></TD>
</TR>
</TABLE>
</TD>
</TR>
<TR><TD>Simple block:</TD>
<TD CLASS="invisible">
<TABLE>
<TR>
<TD><A>same IP:</A></TD><TD><input name="block_same_ip" type="hidden" value="0"><input name="block_same_ip" type="checkbox" value="1" ##BLOCKSAMEIPCHECKED##></TD>
<TD><A>same Name:</A></TD><TD><input name="block_same_name" type="hidden" value="0"><input name="block_same_name" type="checkbox" value="1" ##BLOCKSAMENAMECHECKED##></TD>
</TR>
</TABLE>
</TD>
</TR>
<TR><TD><A>Drop duplicate users:</A></TD>
<TD>
<input name="dropdups" value="0" type="hidden"><input name="dropdups" value="1" type="checkbox" ##DROPDUPSCHECKED##>
</TD>
</TR>
<TR><TD><A>Skip CWs checksum test:</A></TD>
<TD>
<input name="disablecrccws" type="hidden" value="0"><input name="disablecrccws" type="checkbox" value="1" ##DISABLECRCCWSCHECKEDGLOBAL##>
</TD>
</TR>
<TR><TD><A>Skip CWs checksum test only for:</A></TD><TD><textarea name="disablecrccws_only_for" rows="3" class="bt">##IGNCHKSUMONLYFORGLOBAL##</textarea>
</TD>
</TR>
<TR><TH COLSPAN="2">Logging</TH></TR>
<TR><TD><A>Usr file:</A></TD>
<TD CLASS="invisible">
<TABLE>
<TR>
<TD><input name="usrfile" type="text" maxlength="128" value="##USERFILE##"></TD>
<TD><A>enabled:</A></TD><TD><input name="disableuserfile" type="hidden" value="1"><input name="disableuserfile" type="checkbox" value="0" ##DISABLEUSERFILECHECKED##></TD>
<TD><A>flag:</A></TD><TD><select name="usrfileflag"><option VALUE="0">0 - just join/leave</option><option VALUE="1" ##USERFILEFLAGCHECKED##>1 - each zap</option></select></TD>
</TR>
</TABLE>
</TD>
</TR>
<TR><TD><A>Mail file:</A></TD>
<TD CLASS="invisible">
<TABLE>
<TR>
<TD><input name="mailfile" type="text" maxlength="128" value="##MAILFILE##"></TD>
<TD><A>enabled:</A></TD><TD><input name="disablemail" type="hidden" value="1"><input name="disablemail" type="checkbox" value="0" ##DISABLEMAILCHECKED##></TD>
</TR>
</TABLE>
</TD>
</TR>
<TR><TD><A>Log file / max size:</A></TD>
<TD CLASS="invisible">
<TABLE>
<TR>
<TD><input name="logfile" type="text" maxlength="128" value="##LOGFILE##"></TD>
<TD><A>enabled:</A></TD><TD><input name="disablelog" type="hidden" value="1"><input name="disablelog" type="checkbox" value="0" ##DISABLELOGCHECKED##></TD>
<TD><A>max size:</A></TD><TD><input name="maxlogsize" class="withunit short" type="text" maxlength="5" value="##MAXLOGSIZE##"> kB</TD>
</TR>
</TABLE>
</TD>
</TR>
<TR><TD><A>Log duplicated lines:</A></TD><TD><input name="logduplicatelines" value="0" type="hidden"><input name="logduplicatelines" value="1" type="checkbox" ##LOGDUPSCHECKED##><label></label></TD></TR>
<TR><TD><A>Initial debug level:</A></TD><TD><input name="initial_debuglevel" type="text" maxlength="10" value="##INITIALDEBUGLEVEL##"></TD></TR>
<TR><TD><A>Pid file:</A></TD><TD><input name="pidfile" type="text" maxlength="128" value="##PIDFILE##"></TD></TR>
<TR><TD><A>CW log dir:</A></TD><TD><input name="cwlogdir" type="text" maxlength="128" value="##CWLOGDIR##"></TD></TR>
<TR><TD><A>EMM log dir:</A></TD><TD><input name="emmlogdir" type="text" maxlength="128" value="##EMMLOGDIR##"></TD></TR>
<TR><TD><A>ECM log format:</A></TD><TD><input name="ecmfmt" type="text" maxlength="128" value="##ECMFMT##"></TD></TR>
<TR><TD><A>Loghistory Lines:</A></TD><TD><input name="loghistorylines" class="short" type="text" maxlength="4" value="##LOGHISTORYLINES##"></TD></TR>
<TR><TD><A>Syslog server:</A></TD><TD><input name="sysloghost" type="text" maxlength="128" value="##SYSLOGHOST##"></TD></TR>
<TR><TD><A>Syslog port:</A></TD><TD><input name="syslogport" class="short" type="text" maxlength="5" value="##SYSLOGPORT##"></TD></TR>
##TPLCACHEEXAIOLOGGING##
<TR><TH COLSPAN="2">Failban</TH></TR>
<TR><TD><A>Failban time:</A></TD><TD><input name="failbantime" class="withunit short" type="text" maxlength="6" value="##FAILBANTIME##"> min blocking IP based</TD></TR>
<TR><TD><A>Failban count:</A></TD><TD><input name="failbancount" class="withunit short" type="text" maxlength="2" value="##FAILBANCOUNT##"> chances with wrong credentials</TD></TR>
<TR><TH COLSPAN="2">Timeouts / Times</TH></TR>
<TR><TD><A>Client timeout:</A></TD><TD><input name="clienttimeout" class="withunit short" type="text" maxlength="5" value="##CLIENTTIMEOUT##"> ms to give up and return timeout</TD></TR>
<TR><TD><A>Fallback timeout:</A></TD><TD><input name="fallbacktimeout" class="withunit short" type="text" maxlength="5" value="##FALLBACKTIMEOUT##"> ms to switch to fallback reader</TD></TR>
<TR><TD><A>Fallback timeout per caid:</A></TD><TD><input name="fallbacktimeout_percaid" type="text" maxlength="320" value="##FALLBACKTIMEOUT_PERCAID##"></TD></TR>
<TR><TD><A>Client max idle:</A></TD><TD><input name="clientmaxidle" class="withunit short" type="text" maxlength="5" value="##CLIENTMAXIDLE##"> s to disconnect idle clients</TD></TR>
<TR><TD><A>Global sleep:</A></TD><TD><input name="sleep" class="withunit short" type="text" maxlength="5" value="##SLEEP##"> min to switch a client in sleepmode</TD></TR>
<TR><TD><A>Reader restart seconds:</A></TD><TD><input name="readerrestartseconds" class="withunit short" type="text" maxlength="5" value="##READERRESTARTSECONDS##"> s waittime to restart a reader</TD></TR>
<TR><TH COLSPAN="2">Doublecheck</TH></TR>
<TR><TD><A>ECM Doublecheck:</A></TD><TD><input name="double_check" value="0" type="hidden"><input name="double_check" value="1" type="checkbox" ##DCHECKCSELECTED##><label></label></TD></TR>
<TR><TD><A>Doublecheck caids:</A></TD><TD><input name="double_check_caid" type="text" maxlength="160" value="##DOUBLECHECKCAID##"></TD></TR>
##TPLSUPPRESSCMD08##
##TPLGETBLOCKEMMAUPROVID##
##TPLENABLELEDBIT##

View File

@ -27,6 +27,7 @@
<TR><TD><A>Source Stream Password:</A></TD><TD><input name="stream_source_auth_password" type="text" value="##STREAM_SOURCE_AUTH_PASSWORD##"></TD></TR>
<TR><TD><A>Relay Buffer Time:</A></TD><TD><input name="stream_relay_buffer_time" class="short" type="text" maxlength="5" value="##STREAM_RELAY_BUFFER_TIME##"><label> ms (delay for stream processing)</label></TD></TR>
<TR><TD><A>Relay Reconnect Count:</A></TD><TD><input name="stream_relay_reconnect_count" class="short" type="text" maxlength="2" value="##STREAM_RELAY_RECONNECT_COUNT##"><label> attempts until an interrupted stream is disconnected (0 = disabled)</label></TD></TR>
##TPLSTREAMEMUSETTINGS##
<TR><TD><A>Relay Client Display Options:</A></TD>
<TD CLASS="invisible">
<TABLE>

View File

@ -0,0 +1,9 @@
<TR><TD><A>Process EMM from stream (emu):</A></TD>
<TD>
<select name="stream_emm_enabled">
<option value="0" ##STREAMEMMENABLEDSELECTED0##>0 - disabled</option>
<option value="1" ##STREAMEMMENABLEDSELECTED1##>1 - enabled</option>
</select>
</TD>
</TR>
<TR><TD><A>ECM fix delay (emu):</A></TD><TD><input name="stream_ecm_delay" class="short" type="text" maxlength="5" value="##STREAM_ECM_DELAY##"><label> ms (delay for PowerVU processing)</label></TD></TR>

View File

@ -10,7 +10,7 @@
<LI CLASS="##CMENUACTIVE7##"><A HREF="files.html?file=logfile">logfile</A></LI>
<LI CLASS="##CMENUACTIVE8##"><A HREF="files.html?file=userfile">userfile</A></LI>
##TPLFILEMENUGBOX## <!-- CMENUACTIVE19-29 -->
<LI CLASS="##CMENUACTIVE9## ##CMENUACTIVE10## ##CMENUACTIVE11## ##CMENUACTIVE12## ##CMENUACTIVE13## ##CMENUACTIVE14## ##CMENUACTIVE15## ##CMENUACTIVE16## ##CMENUACTIVE17## ##CMENUACTIVE18##"><A HREF="#" class="drop">other files<b class="subcaret"></b></A>
<LI CLASS="##CMENUACTIVE9## ##CMENUACTIVE10## ##CMENUACTIVE11## ##CMENUACTIVE12## ##CMENUACTIVE13## ##CMENUACTIVE14## ##CMENUACTIVE15## ##CMENUACTIVE16## ##CMENUACTIVE17## ##CMENUACTIVE18## ##CMENUACTIVE30##"><A HREF="#" class="drop">other files<b class="subcaret"></b></A>
<UL CLASS="dropdown_nav">
<LI CLASS="##CMENUACTIVE9##"><A HREF="files.html?file=oscam.services">oscam.services</A></LI>
<LI CLASS="##CMENUACTIVE10##"><A HREF="files.html?file=oscam.provid">oscam.provid</A></LI>
@ -22,6 +22,7 @@
##VIEW_FILEMENUCSS## <!-- CMENUACTIVE16 -->
##TPLFILEMENUTWIN## <!-- CMENUACTIVE17 -->
##TPLFILEMENUCONSTCW## <!-- CMENUACTIVE18 -->
##TPLFILEMENUSOFTCAMKEY## <!-- CMENUACTIVE30 -->
</UL>
</LI>
</UL>

View File

@ -0,0 +1 @@
<LI CLASS="##CMENUACTIVE30##"><A HREF="files.html?file=SoftCam.Key">SoftCam.Key</A></LI>

View File

@ -1,10 +1,10 @@
# This file contains index of the templates.
# The format is simple "TEMPLATE_NAME FILENAME DEPENDENCY1,DEPENDENCYx"
# 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
# DEPENDENCYx - The config variable which is responsible for this 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
@ -62,7 +62,7 @@ CONFIGCCCAM config/cccam.html
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
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
@ -92,6 +92,7 @@ CONFIGMENUNEWCAMD config/menu_newcamd.html
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
@ -131,6 +132,7 @@ FILEMENUDVBAPI files/menu_dvbapi.html
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
@ -199,6 +201,7 @@ READEREDITCACHEEXBIT readerconfig/readerconfig_cacheexbit.html
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

File diff suppressed because one or more lines are too long

View File

@ -7,13 +7,11 @@
#ifdef WEBIF_WIKI
#define COMPRESSED_WIKI 1
struct wiki_entry {
uint32_t param_ofs;
uint32_t config_ofs;
uint32_t section_ofs;
uint32_t text_ofs;
const char *param;
const char *config;
const char *section;
const char *text;
int8_t status;
};
@ -21,8 +19,6 @@ int32_t wiki_count(void);
const struct wiki_entry *wiki_get_entries(void);
const char *wiki_get_help(const char *config, const char *section, const char *param);
int8_t wiki_get_status(const char *config, const char *section, const char *param);
void wiki_get_data(const char **data, size_t *data_len, size_t *data_olen);
char *wiki_get_decompressed_data(void);
void webif_wiki_prepare(void);
void webif_wiki_free(void);

View File

@ -22,4 +22,5 @@
<TR><TD><A>Maxhop:</A></TD><TD><input name="cccmaxhops" class="short" type="text" maxlength="2" value="##CCCMAXHOPS##"></TD></TR>
<TR><TD><A>Mindown:</A></TD><TD><input name="cccmindown" class="short" type="text" maxlength="2" value="##CCCMINDOWN##"></TD></TR>
<TR><TD><A>Reshare:</A></TD><TD><input name="cccreshare" class="short" type="text" maxlength="2" value="##CCCRESHARE##"> &nbsp;Global CCCam Reshare:<SPAN CLASS="global_conf" TITLE="This value is used if Reshare = -1"><A HREF="config.html?part=cccam">##RESHARE##</A></SPAN></TD></TR>
<TR><TD><A>Want Emu:</A></TD><TD><input name="cccwantemu" type="hidden" value="0"><input name="cccwantemu" type="checkbox" value="1" ##CCCWANTEMUCHECKED##><label></label></TD></TR>
<TR><TD><A>Keep alive:</A></TD><TD><input name="ccckeepalive" type="hidden" value="0"><input name="ccckeepalive" type="checkbox" value="1" ##KEEPALIVECHECKED##><label></label></TD></TR>

View File

@ -0,0 +1,2 @@
<TR><TD><A>AU providers:</A></TD><TD><textarea name="emu_auproviders" rows="3" class="bt">##EMUAUPROVIDERS##</textarea></TD></TR>
<TR><TD><A>[BISS] Enable date-coded keys:</A></TD><TD><input name="emu_datecodedenabled" type="hidden" value="0"><input name="emu_datecodedenabled" type="checkbox" value="1" ##EMUDATECODEDENABLED##><label></label></TD></TR>

Binary file not shown.