Compare commits

..

9 Commits

99 changed files with 6957 additions and 16589 deletions

View File

@ -517,7 +517,6 @@ 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)
@ -547,22 +546,6 @@ 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})
@ -905,24 +888,6 @@ 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.*")
@ -1035,11 +1000,4 @@ 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,13 +840,6 @@ 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,11 +649,6 @@ 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,9 +509,6 @@ 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,9 +32,6 @@ 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
@ -374,32 +371,6 @@ 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
@ -960,3 +931,6 @@ 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 WITH_EMU WITH_SOFTCAM
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
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,188 +1,81 @@
# OSCam with AI Fake DCW Detector Test
# OSCam: Open Source Conditional Access Module
![AI Fake DCW Detector](images/image1.jpg)
[![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)
## Overview
## Quick links
This repository contains a modified version of **OSCam** enhanced with an advanced **AI-inspired Fake DCW Detection and Voting System**.
- [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/)
The goal of this system is to improve stability and reliability when multiple Control Word (CW) sources are available, such as:
- Local readers
- CacheEx peers
- CSP sources
- Virtual readers
## Releases
Instead of accepting the first CW received, this implementation collects multiple candidates and selects the most reliable one using weighted voting logic.
For the latest changes and release history, see the
[OSCam commits](https://git.streamboard.tv/common/oscam/-/commits/master) page.
---
## GitLab repository
# 🔍 Problem It Solves
Project page:
https://git.streamboard.tv/common/oscam
In multi-reader or CacheEx environments, fake or unstable DCWs can appear.
## Building & Dependencies
Default behavior:
- First CW wins
- Possible glitches, freezing, or unstable decoding
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:
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
- [Wiki Home](https://git.streamboard.tv/common/oscam/-/wikis/home)
Result:
## License
✔ Reduced fake DCWs
✔ Increased decoding stability
✔ Better CacheEx reliability
✔ Smarter CW selection
OSCam: Open Source CAM
---
Copyright (C) 2009-2026 OSCam developers
# 🧠 Core Functions
OSCam is based on the Streamboard mp-cardserver 0.9d by dukat and has been
extended and worked on by many more since then.
## 1`cw_vote_add()`
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This function is called whenever a new CW is received.
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.
It:
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
- 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
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.
Each CW candidate is stored in a voting pool.
## Contributing
---
Contributions are welcome. If you want to help improve OSCam:
## 2`cw_vote_decide()`
- 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.
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.
## Help and Support
- 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 Normal file → Executable file
View File

@ -1,22 +1,24 @@
#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
//#define WITH_COMPRESS_WEBIF 1
>>>>>>> Stashed changes
#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
@ -24,46 +26,51 @@
#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
//#define MODULE_GHTTP 1
<<<<<<< Updated upstream
//#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
@ -72,7 +79,7 @@
//#define CARDREADER_DRECAS 1
#ifdef WITH_PCSC
//#define CARDREADER_PCSC 1
#define CARDREADER_PCSC 1
#endif
#ifdef WITH_LIBUSB

1
config.mak Executable file
View File

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

View File

@ -15,6 +15,5 @@ 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,19 +365,14 @@
#define CHECK_BIT(var,pos) (((var) & (1<<(pos)))? 1 : 0)
// CW VOTING
#define MAX_VOTE_CANDIDATES 16 // Zwiększono dla obsługi większej liczby głosów
#define MIN_VOTES_REQUIRED 2
#define CW_COMPARE_LEN 14
/* ===========================
* constants
* =========================== */
#define SCM_URL "https://github.com/oscam-mirror/oscam-emu"
#define SCM_URL "https://git.streamboard.tv/common/oscam"
#define WIKI_URL "https://git.streamboard.tv/common/oscam/-/wikis"
#define BOARD_URL "https://github.com/oscam-mirror/oscam-emu/discussions"
#define BOARD_URL "https://board.streamboard.tv"
#ifndef CS_VERSION
#define CS_VERSION "2.26.01-11942"
#define CS_VERSION "2.26.02-11943"
#endif
#ifndef CS_GIT_COMMIT
#define CS_GIT_COMMIT "a2b4c6d8"
@ -403,6 +398,9 @@
#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
@ -412,18 +410,14 @@
#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
@ -458,11 +452,10 @@
#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
@ -900,13 +893,6 @@ 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;
@ -916,7 +902,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
{
@ -1041,26 +1027,6 @@ 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];
@ -1131,6 +1097,7 @@ 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
@ -1154,10 +1121,9 @@ 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;
@ -1550,7 +1516,6 @@ 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;
@ -1570,6 +1535,7 @@ 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;
@ -1725,7 +1691,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]; // Max length 63 + null terminator
char r_pwd[64];
int32_t l_port;
CAIDTAB ctab;
uint32_t boxid;
@ -1834,7 +1800,6 @@ 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
@ -2031,11 +1996,6 @@ 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
@ -2273,7 +2233,7 @@ struct s_config
uint32_t ctimeout;
uint32_t ftimeout;
CAIDVALUETAB ftimeouttab;
CAIDVALUETAB ctimeouttab; // clienttimeout_percaid
CAIDVALUETAB ctimeouttab; // <EFBFBD> clienttimeout_percaid
uint32_t cmaxidle;
int32_t ulparent;
uint32_t delay;
@ -2485,17 +2445,6 @@ 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;
@ -2585,10 +2534,6 @@ 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
@ -2623,6 +2568,7 @@ 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
@ -2642,24 +2588,15 @@ struct s_config
uint8_t cacheex_lg_only_remote_settings;
uint8_t cacheex_localgenerated_only;
CAIDTAB cacheex_localgenerated_only_caidtab;
FTAB lg_only_tab;
uint8_t localgenerated_only_in;
CAIDTAB localgenerated_only_in_caidtab;
FTAB lg_only_in_tab;
uint8_t lg_only_in_aio_only;
uint8_t lg_only_remote_settings;
int32_t feature_bitfield;
char aio_version[CS_AIO_VERSION_LEN];
#endif
CAIDVALUETAB cacheex_nopushafter_tab;
FTAB cacheex_lg_only_tab;
uint8_t cacheex_localgenerated_only_in;
CAIDTAB cacheex_localgenerated_only_in_caidtab;
FTAB cacheex_lg_only_in_tab;
uint8_t cacheex_lg_only_in_aio_only;
CECSPVALUETAB cacheex_filter_caidtab;
CECSPVALUETAB cacheex_filter_caidtab_aio;
uint32_t cacheex_push_lg_groups;
FTAB cacheex_lg_only_tab;
uint64_t cacheex_push_lg_groups;
#endif
#endif
#ifdef CW_CYCLE_CHECK
@ -2790,18 +2727,12 @@ 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; }
@ -2819,8 +2750,4 @@ 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,19 +365,14 @@
#define CHECK_BIT(var,pos) (((var) & (1<<(pos)))? 1 : 0)
// CW VOTING
#define MAX_VOTE_CANDIDATES 16 // Zwiększono dla obsługi większej liczby głosów
#define MIN_VOTES_REQUIRED 2
#define CW_COMPARE_LEN 14
/* ===========================
* constants
* =========================== */
#define SCM_URL "https://github.com/oscam-mirror/oscam-emu"
#define SCM_URL "https://git.streamboard.tv/common/oscam"
#define WIKI_URL "https://git.streamboard.tv/common/oscam/-/wikis"
#define BOARD_URL "https://github.com/oscam-mirror/oscam-emu/discussions"
#define BOARD_URL "https://board.streamboard.tv"
#ifndef CS_VERSION
#define CS_VERSION "2.26.01-11942"
#define CS_VERSION "2.26.02-11943"
#endif
#ifndef CS_GIT_COMMIT
#define CS_GIT_COMMIT "a2b4c6d8"
@ -412,18 +407,14 @@
#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
@ -458,11 +449,10 @@
#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
@ -900,13 +890,6 @@ 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;
@ -916,7 +899,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
{
@ -1041,25 +1024,6 @@ 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];
@ -1153,10 +1117,9 @@ 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;
@ -1543,9 +1506,12 @@ 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;
@ -1565,6 +1531,7 @@ 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;
@ -1720,7 +1687,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]; // Max length 63 + null terminator
char r_pwd[64];
int32_t l_port;
CAIDTAB ctab;
uint32_t boxid;
@ -1829,7 +1796,6 @@ 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
@ -2026,11 +1992,6 @@ 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
@ -2140,6 +2101,8 @@ 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
@ -2266,7 +2229,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;
@ -2478,17 +2441,6 @@ 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;
@ -2578,10 +2530,6 @@ 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
@ -2616,35 +2564,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 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;
FTAB cacheex_lg_only_tab;
uint8_t cacheex_localgenerated_only_in;
CAIDTAB cacheex_localgenerated_only_in_caidtab;
FTAB cacheex_lg_only_in_tab;
uint8_t cacheex_lg_only_in_aio_only;
CECSPVALUETAB cacheex_filter_caidtab;
CECSPVALUETAB cacheex_filter_caidtab_aio;
uint32_t cacheex_push_lg_groups;
FTAB cacheex_lg_only_tab;
uint64_t cacheex_push_lg_groups;
#endif
#endif
#ifdef CW_CYCLE_CHECK
@ -2775,18 +2723,12 @@ 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; }
@ -2804,8 +2746,4 @@ 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

0
label Executable file
View File

View File

@ -34,6 +34,44 @@ 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 {
@ -522,7 +560,7 @@ int8_t cacheex_maxhop(struct s_client *cl, ECM_REQUEST *er)
return default_maxhop;
}
// GLOBAL override OFF reader only
// GLOBAL override OFF <EFBFBD> reader only
else
{
// Priority 1: reader percaid
@ -546,10 +584,11 @@ int8_t cacheex_maxhop(struct s_client *cl, ECM_REQUEST *er)
}
}
// NOT a cacheex reader default
// NOT a cacheex reader <EFBFBD> default
return default_maxhop;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef CS_CACHEEX_AIO
int8_t cacheex_maxhop_lg(struct s_client *cl, ECM_REQUEST *er)
@ -582,7 +621,7 @@ int8_t cacheex_maxhop_lg(struct s_client *cl, ECM_REQUEST *er)
return default_maxhop;
}
// GLOBAL override OFF reader only
// GLOBAL override OFF <EFBFBD> reader only
else
{
// Priority 1: reader percaid LG
@ -1016,9 +1055,7 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in
}
#endif
// Skip check for BISS1 - cw could be indeed zero
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
if(chk_is_null_CW(er->cw) && !caid_is_biss(er->caid))
if(chk_is_null_CW(er->cw))
{
cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received null cw from %s", csp ? "csp" : username(cl));
cl->cwcacheexerr++;
@ -1027,9 +1064,7 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in
return 0;
}
// Don't check for BISS1 and BISS2 mode 1/E or fake caid (ECM is fake for them)
// Don't check for BISS2 mode CA (ECM table is always 0x80)
if(!caid_is_biss(er->caid) && !caid_is_fake(er->caid) && get_odd_even(er) == 0)
if(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++;
@ -1065,6 +1100,99 @@ 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;

1700
module-cacheex.c.bak Executable file

File diff suppressed because it is too large Load Diff

78
module-cacheex.h.bak Executable file
View File

@ -0,0 +1,78 @@
#ifndef MODULE_CACHEEX_H_
#define MODULE_CACHEEX_H_
static inline uint64_t cacheex_node_id(void *var)
{
uint64_t *x = var;
return *x;
}
uint32_t get_cacheex_wait_time(ECM_REQUEST *er, struct s_client *cl);
CWCHECK get_cwcheck(ECM_REQUEST *er);
uint16_t get_cacheex_mode1_delay(ECM_REQUEST *er);
int32_t chk_csp_ctab(ECM_REQUEST *er, CECSPVALUETAB *tab);
uint8_t check_cacheex_filter(struct s_client *cl, ECM_REQUEST *er);
void cacheex_add_to_cache(struct s_client *cl, ECM_REQUEST *er);
void cacheex_add_to_cache_from_csp(struct s_client *cl, ECM_REQUEST *er);
void cacheex_cache_push(ECM_REQUEST *er);
int32_t cacheex_add_stats(struct s_client *cl, uint16_t caid, uint16_t srvid, uint32_t prid, uint8_t direction
#ifdef CS_CACHEEX_AIO
, uint8_t localgenerated
#endif
);
int8_t cacheex_maxhop(struct s_client *cl);
#ifdef CS_CACHEEX_AIO
int8_t cacheex_maxhop_lg(struct s_client *cl);
#endif
#ifdef CS_CACHEEX
extern void cacheex_init(void);
extern void cacheex_clear_account_stats(struct s_auth *account);
extern void cacheex_clear_client_stats(struct s_client *client);
extern void cacheex_load_config_file(void);
static inline bool cacheex_reader(struct s_reader *rdr) { return rdr ? (rdr->cacheex.mode == 1 ? 1 : 0) : 0; }
extern bool cacheex_is_match_alias(struct s_client *cl, ECM_REQUEST *er);
void cacheex_set_csp_lastnode(ECM_REQUEST *er);
void cacheex_set_cacheex_src(ECM_REQUEST *ecm, struct s_client *cl);
void cacheex_init_cacheex_src(ECM_REQUEST *ecm, ECM_REQUEST *er);
void cacheex_free_csp_lastnodes(ECM_REQUEST *er);
void checkcache_process_thread_start(void);
void cacheex_push_out(struct s_client *cl, ECM_REQUEST *er);
bool cacheex_check_queue_length(struct s_client *cl);
static inline int8_t cacheex_get_rdr_mode(struct s_reader *reader) { return reader ? reader->cacheex.mode : 0; }
void cacheex_init_hitcache(void);
void cacheex_free_hitcache(void);
void cacheex_cleanup_hitcache(bool force);
void cacheex_update_hash(ECM_REQUEST *er);
void cacheex_mode1_delay(ECM_REQUEST *er);
void cacheex_timeout(ECM_REQUEST *er);
#ifdef CS_CACHEEX_AIO
char* cxaio_ftab_to_buf(FTAB *lg_only_ftab);
FTAB caidtab2ftab(CAIDTAB *ctab);
void caidtab2ftab_add(CAIDTAB *lgonly_ctab, FTAB *lgonly_tab);
#define CACHEEX_FEATURES 127
#endif
#else
static inline void cacheex_init(void) { }
static inline void cacheex_clear_account_stats(struct s_auth *UNUSED(account)) { }
static inline void cacheex_clear_client_stats(struct s_client *UNUSED(client)) { }
static inline void cacheex_load_config_file(void) { }
static inline bool cacheex_reader(struct s_reader *UNUSED(rdr)) { return false; }
static inline bool cacheex_is_match_alias(struct s_client *UNUSED(cl), ECM_REQUEST *UNUSED(er)) { return false; }
static inline void cacheex_set_csp_lastnode(ECM_REQUEST *UNUSED(er)) { }
static inline void cacheex_free_csp_lastnodes(ECM_REQUEST *UNUSED(er)) { }
static inline void cacheex_set_cacheex_src(ECM_REQUEST *UNUSED(ecm), struct s_client *UNUSED(cl)) { }
static inline void cacheex_init_cacheex_src(ECM_REQUEST *UNUSED(ecm), ECM_REQUEST *UNUSED(er)) { }
static inline void checkcache_process_thread_start(void) { }
static inline void cacheex_push_out(struct s_client *UNUSED(cl), ECM_REQUEST *UNUSED(er)) { }
static inline bool cacheex_check_queue_length(struct s_client *UNUSED(cl)) { return 0; }
static inline int8_t cacheex_get_rdr_mode(struct s_reader *UNUSED(reader)) { return 0; }
static inline void cacheex_init_hitcache(void) { }
static inline void cacheex_free_hitcache(void) { }
static inline void cacheex_cleanup_hitcache(bool UNUSED(force)) { }
static inline void cacheex_update_hash(ECM_REQUEST *UNUSED(er)) { }
static inline void cacheex_mode1_delay(ECM_REQUEST *UNUSED(er)) { }
static inline void cacheex_timeout(ECM_REQUEST *UNUSED(er)) { }
#endif
#endif

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,9 +1476,6 @@ 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
}
@ -1572,11 +1569,6 @@ 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));
}
}
@ -1585,37 +1577,11 @@ 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(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);
memcpy(cl->ncd_skey, buf + 20, 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;
cs_log_dbg(D_CACHEEX, "cacheex: received id answer from %s: %" PRIu64 "X", username(cl), cacheex_node_id(cl->ncd_skey));
}
}
void camd35_cacheex_init_dcw(struct s_client *client, ECM_REQUEST *er)

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] = rdr->cc_want_emu; // <-- Client wants to have EMUs, 0 - NO; 1 - YES
buf[28] = 0; // <-- 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,8 +1544,7 @@ struct cc_card *get_matching_card(struct s_client *cl, ECM_REQUEST *cur_er, int8
}
if((ncard->caid == cur_er->caid // caid matches
|| (rdr->cc_want_emu && (ncard->caid == (cur_er->caid & 0xFF00))))
|| lb_match) // or system matches if caid ends with 00 (needed for wantemu)
|| lb_match)) // or system matches if caid ends with 00
{
int32_t goodSidCount = ll_count(ncard->goodsids);
int32_t badSidCount = ll_count(ncard->badsids);
@ -1584,7 +1583,7 @@ struct cc_card *get_matching_card(struct s_client *cl, ECM_REQUEST *cur_er, int8
}
}
if(!(rdr->cc_want_emu) && caid_is_nagra(ncard->caid) && (!xcard || ncard->hop < xcard->hop))
if(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,9 +123,7 @@ static uint8_t checkvalidCW(ECM_REQUEST *er)
{
uint8_t ret = 1;
// Skip check for BISS1 - cw could be indeed zero
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
if(chk_is_null_CW(er->cw) && !caid_is_biss(er->caid))
if(chk_is_null_CW(er->cw))
{ er->rc = E_NOTFOUND; }
if(er->rc == E_NOTFOUND)
@ -843,25 +841,15 @@ 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) // ignore ECM Request
if(cfg.onbadcycle > 0)
{
#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);
cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> DROP CW (LG included)", 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;
@ -870,10 +858,10 @@ uint8_t checkcwcycle(struct s_client *client, ECM_REQUEST *er, struct s_reader *
}
return 1;
}
#endif
/*
*
*/
#endif

View File

@ -312,9 +312,7 @@ void azbox_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][n], 8) && (memcmp(er->cw + (n * 8), nullcw, 8) != 0 || caid_is_biss(er->caid)))
if(memcmp(er->cw + (n * 8), demux[0].last_cw[0][n], 8) && (memcmp(er->cw + (n * 8), nullcw, 8) != 0))
{
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,10 +601,8 @@ 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 || caid_is_biss(er->caid)))
&& (memcmp(er->cw + (n * 8), nullcw, 8) != 0))
{
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,10 +730,8 @@ 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 || caid_is_biss(demux[demux_id].ECMpids[pidnum].CAID)))
&& (memcmp(cw + (l * 8), nullcw, 8) != 0))
{
for(n = 0; n < PTINUM; n++)
{

View File

@ -731,10 +731,8 @@ 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 || caid_is_biss(demux[demux_id].ECMpids[pidnum].CAID)))
&& (memcmp(cw + (l * 8), nullcw, 8) != 0))
{
ErrorCode = oscam_sttkd_KeyWrite(tkd_desc_info[demux[demux_id].dev_index].key_hndl, l, cw + (l * 8));

View File

@ -1886,15 +1886,8 @@ 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)
@ -1914,18 +1907,10 @@ 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",
@ -2595,6 +2580,7 @@ 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)
{
@ -2666,8 +2652,6 @@ 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
@ -2699,63 +2683,6 @@ 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)
@ -3865,7 +3792,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 || (!caid_is_biss_fixed(ca_system_id) && !caid_is_fake(ca_system_id) && ca_pid == 0x1FFF))
if(ca_system_id == 0x0000 || ca_pid == 0x1FFF)
{
return; // This is not a valid CAID or ECM pid
}
@ -4312,6 +4239,7 @@ 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)
@ -4402,7 +4330,13 @@ 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:
{
@ -4453,33 +4387,14 @@ static void dvbapi_capmt_notify(struct demux_s *dmx)
static void dvbapi_prepare_descrambling(int32_t demux_id, uint32_t msgid)
{
bool is_powervu = false, start_emm = true;
bool start_emm = true;
char service_name[CS_SERVICENAME_SIZE];
// The CA PMT should have given us enough info to determine if descrambling
// is possible. Parsing the (real) PMT is not necessary, unless we have a
// PowerVu encrypted channel or (for some weird reason) no stream pids at all.
// Actually, when no streams are available, we set the PMT pid as the 1st
// stream pid, so we have to check against that. Finally, if the PMT pid is
// not included in the CA PMT, we start the PAT filter instead.
#ifdef WITH_EXTENDED_CW
uint8_t i;
for(i = 0; i < demux[demux_id].ECMpidcount; i++)
{
if(caid_is_powervu(demux[demux_id].ECMpids[i].CAID))
{
is_powervu = true;
break;
}
}
#endif
if(demux[demux_id].pmtpid == 0)
{
dvbapi_start_pat_filter(demux_id);
}
else if(demux[demux_id].STREAMpids[0] == demux[demux_id].pmtpid || is_powervu)
else if(demux[demux_id].STREAMpids[0] == demux[demux_id].pmtpid)
{
dvbapi_start_pmt_filter(demux_id);
}
@ -4565,6 +4480,7 @@ 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;
@ -4728,6 +4644,7 @@ 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;
@ -5680,7 +5597,6 @@ 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!
@ -5706,25 +5622,8 @@ 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)) || pvu_skip)
if(curpid->table == buffer[0] && !caid_is_irdeto(curpid->CAID))
{
if(!(er = get_ecmtask()))
{
@ -6068,35 +5967,6 @@ 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;
@ -7326,10 +7196,8 @@ 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 || caid_is_biss(demux[demux_id].ECMpids[pid].CAID)))
&& (memcmp(cw + (n * cw_length), null_cw, cw_length) != 0))
{
// prepare ca device
uint32_t idx = dvbapi_ca_set_pid(demux_id, pid, stream_id, (algo == CA_ALGO_DES), msgid);
@ -7621,9 +7489,7 @@ 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
// 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))
if((status == 0 || status == 3 || status == 4) && er->rc < E_NOTFOUND)
{
// check for matching control word
if(memcmp(er->cw, demux[i].last_cw[0][0], 8) == 0 &&
@ -7895,8 +7761,21 @@ 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)
{
// streamserver set cw
set_dvbapi_cw = !stream_write_cw(er);
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;
}
}
if (set_dvbapi_cw)
#endif
@ -8627,9 +8506,7 @@ int32_t dvbapi_check_ecm_delayed_delivery(int32_t demux_id, ECM_REQUEST *er)
}
// Check for null cw
// Skip check for BISS1 - cw could be indeed zero
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
if(memcmp(er->cw, nullcw, 8) == 0 && memcmp(er->cw + 8, nullcw, 8) == 0 && !caid_is_biss(er->caid))
if(memcmp(er->cw, nullcw, 8) == 0 && memcmp(er->cw + 8, nullcw, 8) == 0)
{
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 // not used by OSCam
#define SERVICE_TYPE_MASK 0x85
#define DEMUX_DEVICE 0x86
#define CA_DEVICE 0x87
@ -336,9 +336,6 @@ 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
@ -371,9 +368,6 @@ 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
@ -452,6 +446,7 @@ 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,894 +0,0 @@
#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,9 +6,6 @@
#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,13 +1118,6 @@ 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);
}
@ -1191,7 +1184,7 @@ static int8_t newcamd_auth_client(IN_ADDR_T ip, uint8_t *deskey)
uint32_t rprid;
found = 0;
if(pufilt->caid == aureader->caid && aureader->typ != R_EMU)
if(pufilt->caid == aureader->caid)
{
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) || rdr->typ == R_EMU)
if(is_network_reader(rdr))
{
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) || rdr->typ == R_EMU) // reader caid is not real caid
if(is_network_reader(rdr)) // reader caid is not real caid
{
prv = ea;
continue; // proxy can convert or reject
@ -2094,13 +2094,14 @@ 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, int32_t ecm_time)
void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc)
{
if(rc >= E_99 || cacheex_reader(rdr))
{ return; }
if(!ecm_time)
ecm_time = cfg.ctimeout;
int32_t ecm_time = cfg.ctimeout;
if(ea->ecm_time && ea->rc <= E_NOTFOUND)
{ ecm_time = ea->ecm_time; }
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, int32_t ecm_time);
void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc);
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), int32_t UNUSED(ecm_time)) { }
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 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,47 +10,26 @@
#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;
@ -79,20 +58,7 @@ 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);
@ -100,30 +66,6 @@ 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,7 +456,6 @@ 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,25 +30,18 @@
#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;
@ -150,9 +143,8 @@ 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 31 // sum of items above. Use it for "All inactive" in function calls too.
#define MNU_CFG_TOTAL_ITEMS 30 // 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)
{
@ -465,7 +457,8 @@ static void refresh_oscam(enum refreshtypes refreshtype)
case REFR_SERVER:
cs_log("Refresh Server requested by WebIF from %s", cs_inet_ntoa(GET_IP()));
reload_global_config(); // Wczytaj ponownie globalną konfigurację
//kill(first_client->pid, SIGHUP);
//todo how I can refresh the server after global settings
break;
case REFR_SERVICES:
@ -617,55 +610,13 @@ 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"))
{
// Check if this is a checkbox with value=1
if(strcmp(value, "1") == 0)
{
checkbox_checked[i] = true;
{ config_set(section, token, value); }
}
}
}
// 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.");
@ -791,20 +742,6 @@ 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
@ -1408,11 +1345,6 @@ 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");
@ -2532,7 +2464,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) || rdr->typ == R_EMU) //physical readers make trouble if re-started
if(is_network_reader(rdr)) //physical readers make trouble if re-started
{
if(rdr)
{
@ -3329,8 +3261,6 @@ 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
@ -3368,23 +3298,6 @@ 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
@ -3406,9 +3319,6 @@ 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;
@ -5446,34 +5356,9 @@ 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);
@ -5970,9 +5855,6 @@ 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
}
}
@ -7564,9 +7446,6 @@ 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 },
};
@ -8039,12 +7918,8 @@ 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)
{
@ -8326,6 +8201,7 @@ static char *send_oscam_cacheex(struct templatevars * vars, struct uriparams * p
}
}
{ tpl_addVar(vars, TPLADD, "IP", ""); }
@ -8348,7 +8224,20 @@ 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);
@ -9356,11 +9245,7 @@ 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,7 +30,6 @@
#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"
@ -142,9 +141,8 @@ 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 31 // sum of items above. Use it for "All inactive" in function calls too.
#define MNU_CFG_TOTAL_ITEMS 30 // 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,7 +455,8 @@ static void refresh_oscam(enum refreshtypes refreshtype)
case REFR_SERVER:
cs_log("Refresh Server requested by WebIF from %s", cs_inet_ntoa(GET_IP()));
reload_global_config(); // Wczytaj ponownie globalną konfigurację
//kill(first_client->pid, SIGHUP);
//todo how I can refresh the server after global settings
break;
case REFR_SERVICES:
@ -609,55 +608,13 @@ 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"))
{
// Check if this is a checkbox with value=1
if(strcmp(value, "1") == 0)
{
checkbox_checked[i] = true;
{ config_set(section, token, value); }
}
}
}
// 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.");
@ -783,20 +740,6 @@ 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
@ -1039,6 +982,14 @@ 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
@ -1392,11 +1343,6 @@ 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");
@ -2516,7 +2462,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) || rdr->typ == R_EMU) //physical readers make trouble if re-started
if(is_network_reader(rdr)) //physical readers make trouble if re-started
{
if(rdr)
{
@ -3313,8 +3259,6 @@ 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
@ -3352,23 +3296,6 @@ 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
@ -3390,9 +3317,6 @@ 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;
@ -5430,34 +5354,9 @@ 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);
@ -5954,9 +5853,6 @@ 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
}
}
@ -7548,9 +7444,6 @@ 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 },
};
@ -8023,12 +7916,8 @@ 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)
{
@ -8310,6 +8199,29 @@ 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)
@ -8372,7 +8284,17 @@ static char *send_oscam_cacheex(struct templatevars * vars, struct uriparams * p
{ tpl_addVar(vars, TPLADD, "IP", "camd.socket"); }
else
{ tpl_addVar(vars, TPLADD, "IP", ""); }
tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", get_cacheex_node(cl));
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_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);
@ -9321,11 +9243,7 @@ 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);

985
oscam-cache.c.bak Executable file
View File

@ -0,0 +1,985 @@
#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(!(rdr->typ == R_EMU) && !is_network_reader(rdr) && ((rdr->caid >> 8) != ((er->caid >> 8) & 0xFF) && (rdr->caid >> 8) != ((er->ocaid >> 8) & 0xFF)))
if( !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(!(rdr->typ == R_EMU && caid_is_biss(er->caid)) && !chk_rfilter(er, rdr))
if(!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 && !(rdr->typ == R_EMU))
if(ll_count(rdr->ll_entitlements) > 0)
{
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) || rdr->typ == R_EMU)
if(is_network_reader(rdr))
{
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(!(rdr->typ == R_EMU) && !is_network_reader(rdr) && ((rdr->caid >> 8) != ((er->caid >> 8) & 0xFF) && (rdr->caid >> 8) != ((er->ocaid >> 8) & 0xFF)))
if( !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(!(rdr->typ == R_EMU && caid_is_biss(er->caid)) && !chk_rfilter(er, rdr))
if(!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 && !(rdr->typ == R_EMU))
if(ll_count(rdr->ll_entitlements) > 0)
{
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) || rdr->typ == R_EMU)
if(is_network_reader(rdr))
{
return 1; // reader caid is not real caid
}

View File

@ -1,11 +0,0 @@
--- 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,47 +622,3 @@ 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,11 +15,9 @@ void chk_port_tab(char *portasc, PTAB *ptab);
void chk_port_camd35_tab(char *portasc, PTAB *ptab);
void chk_ecm_whitelist(char *value, ECM_WHITELIST *ecm_whitelist);
void chk_ecm_hdr_whitelist(char *value, ECM_HDR_WHITELIST *ecm_hdr_whitelist);
void chk_cwvote_caidtab(char *value, CW_VOTE_CAID_TAB *cwvote_caid_table);
void clear_sip(struct s_ip **base);
void clear_sip(struct s_ip **sip);
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,26 +1109,6 @@ 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,7 +30,6 @@ 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,8 +70,7 @@ int config_list_parse(const struct config_list *clist, const char *token, char *
{
case OPT_INT8:
{
int8_t tmp = (int8_t)strToIntVal(value, c->def.d_int8);
*(int8_t *)var = tmp;
*(int8_t *)var = (int8_t)strToIntVal(value, c->def.d_int8);
return 1;
}
case OPT_UINT8:
@ -92,12 +91,6 @@ 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;
@ -180,35 +173,24 @@ void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_
{
int8_t val = *(int8_t *)var;
// always save pmt_mode, and all cwvote settings, because external tools parse it
if(save_all || val != c->def.d_int8 || !strcmp(c->config_name, "pmt_mode") ||
!strcmp(c->config_name, "cwvote_enabled") || !strcmp(c->config_name, "cwvote_log_enabled"))
{
fprintf_conf(f, c->config_name, "%d\n", val);
}
// 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); }
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));
// 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);
}
if(save_all || val != c->def.d_int32)
{ fprintf_conf(f, c->config_name, "%d\n", val); }
continue;
}
case OPT_UINT32:
@ -216,20 +198,7 @@ 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);
}
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);
}
{ fprintf_conf(f, c->config_name, "%u\n", val); }
continue;
}
case OPT_STRING:
@ -269,18 +238,8 @@ 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:
@ -351,11 +310,6 @@ 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;
@ -566,7 +520,7 @@ FILE *create_config_file(const char *conf_filename)
bool flush_config_file(FILE *f, const char *conf_filename)
{
char dst_file[220], tmp_file[220], bak_file[220]; // Zmniejszono rozmiar bufora, aby uniknąć potencjalnych problemów z przepełnieniem
char dst_file[256], tmp_file[256], bak_file[256];
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));
@ -576,6 +530,5 @@ bool flush_config_file(FILE *f, const char *conf_filename)
{
fclose(f);
}
int32_t result = safe_overwrite_with_bak(dst_file, tmp_file, bak_file, cfg.http_overwrite_bak_file);
return result;
return safe_overwrite_with_bak(dst_file, tmp_file, bak_file, cfg.http_overwrite_bak_file);
}

View File

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

View File

@ -478,10 +478,6 @@ 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,40 +199,6 @@ 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)
{
@ -298,6 +264,12 @@ 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; }
@ -345,7 +317,6 @@ 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
@ -427,15 +398,6 @@ 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
@ -987,10 +949,6 @@ 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
@ -1441,7 +1399,6 @@ 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);
@ -1503,7 +1460,6 @@ 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;
}
@ -1573,73 +1529,3 @@ 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;
}

1522
oscam-config-global.c.bak Executable file

File diff suppressed because it is too large Load Diff

View File

@ -114,7 +114,6 @@ 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;
@ -691,9 +690,6 @@ 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
@ -1200,10 +1196,6 @@ 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),
@ -1358,7 +1350,6 @@ 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),
@ -1373,10 +1364,6 @@ 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
@ -1479,7 +1466,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", "cccwantemu", "ccckeepalive",
"cccversion", "cccmaxhops", "cccmindown", "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, __attribute__((unused)) c, have_fakecw = 0;
uint8_t cw[16], wrong_checksum, c, have_fakecw = 0;
FILE *fp;
memset(alloccount, 0, sizeof(count));
@ -827,6 +827,15 @@ 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);
@ -877,6 +886,15 @@ 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);

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,6 @@ 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);
@ -65,7 +64,6 @@ 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,6 +2,54 @@
#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"
@ -28,6 +76,91 @@
#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;
@ -788,7 +921,127 @@ 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;
@ -2418,10 +2671,6 @@ 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)
{
@ -2441,7 +2690,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) && !caid_is_biss(er->caid))
if(rc < E_NOTFOUND && cw && chk_is_null_CW(cw))
{
rc = E_NOTFOUND;
cs_log_dbg(D_TRACE | D_LB, "WARNING: reader %s send fake cw, set rc=E_NOTFOUND!", reader ? reader->label : "-");
@ -2583,6 +2832,116 @@ 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)
{
@ -2628,9 +2987,7 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui
if(!ea->is_pending) // not for pending ea - only once for ea
{
// cache update
// Skip check for BISS1 - cw could be indeed zero
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
if(ea && (ea->rc < E_NOTFOUND) && (!chk_is_null_CW(ea->cw) && !caid_is_biss(er->caid)))
if(ea && (ea->rc < E_NOTFOUND) && !chk_is_null_CW(ea->cw))
{
#ifdef CS_CACHEEX_AIO
int32_t ecmtime = ea->ecm_time;
@ -2850,7 +3207,18 @@ 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
@ -2979,7 +3347,20 @@ 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))
{
@ -3273,9 +3654,7 @@ void get_cw(struct s_client *client, ECM_REQUEST *er)
}
// Check for odd/even byte
// Don't check for BISS1 and BISS2 mode 1/E or fake caid (ECM is fake for them)
// Don't check for BISS2 mode CA (ECM table is always 0x80)
if(!caid_is_biss(er->caid) && !caid_is_fake(er->caid) && get_odd_even(er) == 0)
if(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,12 +33,6 @@ 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,7 +40,6 @@ 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()
@ -243,24 +242,6 @@ 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,10 +143,6 @@ 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);
}
@ -157,17 +153,16 @@ 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: safe_overwrite_with_bak - Error copying original config file %s to %s. The original config will be left untouched!", destfile, bakfile);
cs_log("Error copying original config file %s to %s. The original config will be left untouched!", destfile, bakfile);
if(unlink(temp_file) < 0)
{
cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
cs_log("Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
}
return 1;
}
@ -177,21 +172,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("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));
cs_log("An error occured while writing the new config file %s.", destfile);
if(rc == -2)
{
cs_log("ERROR: safe_overwrite_with_bak - The config will be missing or only partly filled upon next startup as this is a non-recoverable error! Please restore from backup or try again.");
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.");
}
if(unlink(temp_file) < 0)
{
cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
cs_log("Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
}
return 1;
}
if(unlink(temp_file) < 0)
{
cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
cs_log("Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
}
return 0;
}

View File

@ -468,8 +468,6 @@ 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"; }
@ -478,7 +476,6 @@ 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,10 +44,6 @@
#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>
@ -376,9 +372,9 @@ static void write_versionfile(bool use_stdout)
st.tm_hour, st.tm_min, st.tm_sec);
}
fprintf(fp, "Build Date: %s\n", "UNKNOWN"); // Placeholder if not defined by build system
fprintf(fp, "Version: %s@%s\n", "UNKNOWN", "UNKNOWN"); // Placeholder if not defined by build system
fprintf(fp, "Compiler: %s\n", "UNKNOWN"); // Placeholder if not defined by build system
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);
#ifdef USE_COMPRESS
fprintf(fp, "Compression: %s, level %s\n", COMP_VERSION, COMP_LEVEL);
#endif
@ -461,8 +457,6 @@ 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");
@ -1688,9 +1682,6 @@ const struct s_cardreader *cardreaders[] =
#ifdef CARDREADER_STINGER
&cardreader_stinger,
#endif
#ifdef WITH_EMU
&cardreader_emu,
#endif
NULL
};
@ -1809,8 +1800,6 @@ 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));
@ -1872,9 +1861,6 @@ 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 Executable file
View File

View File

@ -15,7 +15,6 @@
#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[];
@ -153,18 +152,6 @@ 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,6 +17,5 @@ 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 Executable file
View File

View File

@ -0,0 +1,32 @@
<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,29 +142,6 @@
<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##

145
webif/config/global.html.bak Executable file
View File

@ -0,0 +1,145 @@
<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,7 +27,6 @@
<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

@ -1,9 +0,0 @@
<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## ##CMENUACTIVE30##"><A HREF="#" class="drop">other files<b class="subcaret"></b></A>
<LI CLASS="##CMENUACTIVE9## ##CMENUACTIVE10## ##CMENUACTIVE11## ##CMENUACTIVE12## ##CMENUACTIVE13## ##CMENUACTIVE14## ##CMENUACTIVE15## ##CMENUACTIVE16## ##CMENUACTIVE17## ##CMENUACTIVE18##"><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,7 +22,6 @@
##VIEW_FILEMENUCSS## <!-- CMENUACTIVE16 -->
##TPLFILEMENUTWIN## <!-- CMENUACTIVE17 -->
##TPLFILEMENUCONSTCW## <!-- CMENUACTIVE18 -->
##TPLFILEMENUSOFTCAMKEY## <!-- CMENUACTIVE30 -->
</UL>
</LI>
</UL>

View File

@ -1 +0,0 @@
<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,DEPENDANCYx"
# The format is simple "TEMPLATE_NAME FILENAME DEPENDENCY1,DEPENDENCYx"
# Lines starting with # are ignorred
# TEMPLATE_NAME - name of the template and also name of the file that
# contains the template
# FILENAME - The file that contains the template
# DEPENDANCYx - The config variable which is responsible for this template
# DEPENDENCYx - 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,WITH_EMU
EXTENDEDCWAPI config/dvbapi_extended_cw_api.html WITH_EXTENDED_CW
DEMUXERFIX config/dvbapi_demuxerfix.html MODULE_STREAMRELAY
CCCAMRESHAREBIT config/cccreshare.html MODULE_GBOX
CONFIGGBOX config/gbox.html MODULE_GBOX
@ -92,7 +92,6 @@ 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
@ -132,7 +131,6 @@ 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
@ -201,7 +199,6 @@ 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,11 +7,13 @@
#ifdef WEBIF_WIKI
#define COMPRESSED_WIKI 1
struct wiki_entry {
const char *param;
const char *config;
const char *section;
const char *text;
uint32_t param_ofs;
uint32_t config_ofs;
uint32_t section_ofs;
uint32_t text_ofs;
int8_t status;
};
@ -19,6 +21,8 @@ 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,5 +22,4 @@
<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

@ -1,2 +0,0 @@
<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.