Compare commits
No commits in common. "main" and "fix-test-pawel" have entirely different histories.
main
...
fix-test-p
|
|
@ -517,6 +517,7 @@ if (CONFIG_STREAMRELAY MATCHES "Y" AND NOT MODULE_STREAMRELAY EQUAL 1)
|
|||
set (LIBDVBCSA_LIBRARY "${LIBDVBCSADIR}/libdvbcsa.so")
|
||||
else (LIBDVBCSADIR)
|
||||
check_include_file ("dvbcsa/dvbcsa.h" FOUND_LIBDVBCSA)
|
||||
set(HAVE_LIBDVBCSA ${FOUND_LIBDVBCSA})
|
||||
find_library (LIBADVBCSA_LIBRARY NAMES libdvbcsa.a)
|
||||
find_library (LIBDVBCSA_LIBRARY NAMES dvbcsa)
|
||||
endif (LIBDVBCSADIR)
|
||||
|
|
@ -546,6 +547,22 @@ endif (CONFIG_STREAMRELAY MATCHES "Y" AND NOT MODULE_STREAMRELAY EQUAL 1)
|
|||
# Manage config.h based on command line parameters
|
||||
# Manipulate config file based on given parameters and read unset parameters
|
||||
|
||||
execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_EMU OUTPUT_VARIABLE CONFIG_WITH_EMU OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (CONFIG_WITH_EMU MATCHES "Y" AND NOT WITH_EMU EQUAL 1)
|
||||
add_definitions ("-DWITH_EMU")
|
||||
set (WITH_EMU "1")
|
||||
message (STATUS " EMU is added by config compiling with EMU")
|
||||
endif (CONFIG_WITH_EMU MATCHES "Y" AND NOT WITH_EMU EQUAL 1)
|
||||
|
||||
if (WITH_EMU)
|
||||
execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_SOFTCAM OUTPUT_VARIABLE CONFIG_WITH_SOFTCAM OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (CONFIG_WITH_SOFTCAM MATCHES "Y" AND NOT WITH_SOFTCAM EQUAL 1)
|
||||
add_definitions ("-DWITH_SOFTCAM")
|
||||
set (WITH_SOFTCAM "1")
|
||||
message (STATUS " SOFTCAM is added by config linking SoftCam.Key")
|
||||
endif (CONFIG_WITH_SOFTCAM MATCHES "Y" AND NOT WITH_SOFTCAM EQUAL 1)
|
||||
endif (WITH_EMU)
|
||||
|
||||
execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --show-valid OUTPUT_VARIABLE config_vars_string OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
string(REGEX MATCHALL "[A-Z0-9_]+" config_vars ${config_vars_string})
|
||||
|
||||
|
|
@ -888,6 +905,24 @@ endif( HAVE_LIBRT OR HAVE_LIBRT_STATIC)
|
|||
|
||||
#--------------------------------------------------------------------------------
|
||||
|
||||
if (NOT OSCamOperatingSystem MATCHES "Mac OS X")
|
||||
if (NOT DEFINED ENV{ANDROID_NDK})
|
||||
if (NOT DEFINED ENV{ANDROID_STANDALONE_TOOLCHAIN})
|
||||
if (WITH_SOFTCAM)
|
||||
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key)
|
||||
execute_process (COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key ${CMAKE_CURRENT_BINARY_DIR}/SoftCam.Key)
|
||||
else (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key)
|
||||
execute_process (COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/SoftCam.Key)
|
||||
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key)
|
||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--format=binary -Wl,SoftCam.Key -Wl,--format=default")
|
||||
if (NOT OSCamOperatingSystem MATCHES "Windows/Cygwin")
|
||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,noexecstack")
|
||||
endif (NOT OSCamOperatingSystem MATCHES "Windows/Cygwin")
|
||||
endif (WITH_SOFTCAM)
|
||||
endif (NOT DEFINED ENV{ANDROID_STANDALONE_TOOLCHAIN})
|
||||
endif (NOT DEFINED ENV{ANDROID_NDK})
|
||||
endif (NOT OSCamOperatingSystem MATCHES "Mac OS X")
|
||||
|
||||
#----------------------- installation -----------------------------
|
||||
|
||||
file (GLOB config_files "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/oscam.*")
|
||||
|
|
@ -1000,4 +1035,11 @@ if (HAVE_LIBDVBCSA)
|
|||
endif(STATICLIBDVBCSA AND NOT LIBDVBCSADIR)
|
||||
endif (HAVE_LIBDVBCSA)
|
||||
|
||||
if (WITH_EMU)
|
||||
message (STATUS " Compile with EMU support")
|
||||
if (WITH_SOFTCAM)
|
||||
message (STATUS " SoftCam.Key will be linked as well")
|
||||
endif (WITH_SOFTCAM)
|
||||
endif (WITH_EMU)
|
||||
|
||||
message (STATUS "")
|
||||
|
|
|
|||
|
|
@ -840,6 +840,13 @@ set reader's CCcam reshare hop, default:0
|
|||
|
||||
<P>
|
||||
|
||||
<B>cccwantemu</B> = <B>0</B>|<B>1</B>
|
||||
<DL COMPACT><DT><DD>
|
||||
1 = request to provide emu from CCCam server, too, default:0
|
||||
</DL>
|
||||
|
||||
<P>
|
||||
|
||||
<B>ccckeepalive</B> = <B>0</B>|<B>1</B>
|
||||
<DL COMPACT><DT><DD>
|
||||
1 = send keepalive messages to keep connection to remote CCCam server up, default:0
|
||||
|
|
|
|||
|
|
@ -649,6 +649,11 @@ set reader's CCcam reshare hop, default:0
|
|||
\fBx\fP = resharing for direct peer and share level x
|
||||
.RE
|
||||
.PP
|
||||
\fBcccwantemu\fP = \fB0\fP|\fB1\fP
|
||||
.RS 3n
|
||||
1 = request to provide emu from CCCam server, too, default:0
|
||||
.RE
|
||||
.PP
|
||||
\fBccckeepalive\fP = \fB0\fP|\fB1\fP
|
||||
.RS 3n
|
||||
1 = send keepalive messages to keep connection to remote CCCam server up, default:0
|
||||
|
|
|
|||
|
|
@ -509,6 +509,9 @@ DESCRIPTIONS
|
|||
0 = resharing for direct peer only
|
||||
x = resharing for direct peer and share level x
|
||||
|
||||
cccwantemu = 0|1
|
||||
1 = request to provide emu from CCCam server, too, default:0
|
||||
|
||||
ccckeepalive = 0|1
|
||||
1 = send keepalive messages to keep connection to remote CCCam server up, default:0
|
||||
|
||||
|
|
|
|||
32
Makefile
32
Makefile
|
|
@ -32,6 +32,9 @@ ifeq "$(shell ./config.sh --enabled WITH_SSL)" "Y"
|
|||
override USE_SSL=1
|
||||
override USE_LIBCRYPTO=1
|
||||
endif
|
||||
ifeq "$(shell ./config.sh --enabled WITH_EMU)" "Y"
|
||||
override USE_LIBCRYPTO=1
|
||||
endif
|
||||
ifdef USE_SSL
|
||||
override USE_LIBCRYPTO=1
|
||||
endif
|
||||
|
|
@ -371,6 +374,32 @@ SRC-$(CONFIG_CS_CACHEEX) += module-cccam-cacheex.c
|
|||
SRC-$(CONFIG_MODULE_CCCAM) += module-cccam.c
|
||||
SRC-$(CONFIG_MODULE_CCCSHARE) += module-cccshare.c
|
||||
SRC-$(CONFIG_MODULE_CONSTCW) += module-constcw.c
|
||||
SRC-$(CONFIG_WITH_EMU) += module-emulator.c
|
||||
SRC-$(CONFIG_WITH_EMU) += module-emulator-osemu.c
|
||||
SRC-$(CONFIG_WITH_EMU) += module-emulator-biss.c
|
||||
SRC-$(CONFIG_WITH_EMU) += module-emulator-cryptoworks.c
|
||||
SRC-$(CONFIG_WITH_EMU) += module-emulator-director.c
|
||||
SRC-$(CONFIG_WITH_EMU) += module-emulator-irdeto.c
|
||||
SRC-$(CONFIG_WITH_EMU) += module-emulator-nagravision.c
|
||||
SRC-$(CONFIG_WITH_EMU) += module-emulator-omnicrypt.c
|
||||
SRC-$(CONFIG_WITH_EMU) += module-emulator-powervu.c
|
||||
SRC-$(CONFIG_WITH_EMU) += module-emulator-viaccess.c
|
||||
ifeq "$(CONFIG_WITH_EMU)" "y"
|
||||
ifeq "$(CONFIG_WITH_SOFTCAM)" "y"
|
||||
UNAME := $(shell uname -s)
|
||||
ifneq ($(UNAME),Darwin)
|
||||
ifndef ANDROID_NDK
|
||||
ifndef ANDROID_STANDALONE_TOOLCHAIN
|
||||
TOUCH_SK := $(shell touch SoftCam.Key)
|
||||
override LDFLAGS += -Wl,--format=binary -Wl,SoftCam.Key -Wl,--format=default
|
||||
ifneq ($(uname_S),Cygwin)
|
||||
override LDFLAGS += -Wl,-z,noexecstack
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
SRC-$(CONFIG_CS_CACHEEX) += module-csp.c
|
||||
SRC-$(CONFIG_CW_CYCLE_CHECK) += module-cw-cycle-check.c
|
||||
SRC-$(CONFIG_WITH_AZBOX) += module-dvbapi-azbox.c
|
||||
|
|
@ -931,6 +960,3 @@ debug: all
|
|||
|
||||
-include Makefile.extra
|
||||
-include Makefile.local
|
||||
|
||||
EXTRA_LIBS += -lm
|
||||
LIBS += $(EXTRA_LIBS)
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ Examples:
|
|||
./config.sh --create-cert ecdsa prime256v1 ca 'My OSCam Distribution'
|
||||
|
||||
Available options:
|
||||
addons: WEBIF WEBIF_LIVELOG WEBIF_JQUERY WITH_COMPRESS_WEBIF WITH_SSL HAVE_DVBAPI WITH_EXTENDED_CW WITH_NEUTRINO READ_SDT_CHARSETS CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_ARM_NEON WITH_SIGNING
|
||||
addons: WEBIF WEBIF_LIVELOG WEBIF_JQUERY WITH_COMPRESS_WEBIF WITH_SSL HAVE_DVBAPI WITH_EXTENDED_CW WITH_NEUTRINO READ_SDT_CHARSETS CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_ARM_NEON WITH_SIGNING WITH_EMU WITH_SOFTCAM
|
||||
protocols: MODULE_CAMD33 MODULE_CAMD35 MODULE_CAMD35_TCP MODULE_NEWCAMD MODULE_CCCAM MODULE_CCCSHARE MODULE_GBOX MODULE_RADEGAST MODULE_SCAM MODULE_SERIAL MODULE_CONSTCW MODULE_PANDORA MODULE_GHTTP MODULE_STREAMRELAY
|
||||
readers: READER_NAGRA READER_NAGRA_MERLIN READER_IRDETO READER_CONAX READER_CRYPTOWORKS READER_SECA READER_VIACCESS READER_VIDEOGUARD READER_DRE READER_TONGFANG READER_BULCRYPT READER_GRIFFIN READER_DGCRYPT
|
||||
card_readers: CARDREADER_PHOENIX CARDREADER_INTERNAL CARDREADER_SC8IN1 CARDREADER_MP35 CARDREADER_SMARGO CARDREADER_DB2COM CARDREADER_STAPI CARDREADER_STAPI5 CARDREADER_STINGER CARDREADER_DRECAS
|
||||
|
|
|
|||
221
README.md
221
README.md
|
|
@ -1,81 +1,188 @@
|
|||
# OSCam: Open Source Conditional Access Module
|
||||
# OSCam with AI Fake DCW Detector Test
|
||||
|
||||
[](https://git.streamboard.tv/common/oscam/-/commits/master)
|
||||
[](https://git.streamboard.tv/common/oscam/-/tags)
|
||||
[](https://git.streamboard.tv/common/oscam/-/blob/master/COPYING)
|
||||

|
||||
|
||||
## Quick links
|
||||
## Overview
|
||||
|
||||
- [Releases](https://git.streamboard.tv/common/oscam/-/commits/master)
|
||||
- [GitLab repository](https://git.streamboard.tv/common/oscam)
|
||||
- [Wiki](https://git.streamboard.tv/common/oscam/-/wikis/home)
|
||||
- [Issue tracker](https://git.streamboard.tv/common/oscam/-/issues)
|
||||
- [Support forum](https://board.streamboard.tv/forum/)
|
||||
This repository contains a modified version of **OSCam** enhanced with an advanced **AI-inspired Fake DCW Detection and Voting System**.
|
||||
|
||||
The goal of this system is to improve stability and reliability when multiple Control Word (CW) sources are available, such as:
|
||||
|
||||
## Releases
|
||||
- Local readers
|
||||
- CacheEx peers
|
||||
- CSP sources
|
||||
- Virtual readers
|
||||
|
||||
For the latest changes and release history, see the
|
||||
[OSCam commits](https://git.streamboard.tv/common/oscam/-/commits/master) page.
|
||||
Instead of accepting the first CW received, this implementation collects multiple candidates and selects the most reliable one using weighted voting logic.
|
||||
|
||||
## GitLab repository
|
||||
---
|
||||
|
||||
Project page:
|
||||
https://git.streamboard.tv/common/oscam
|
||||
# 🔍 Problem It Solves
|
||||
|
||||
## Building & Dependencies
|
||||
In multi-reader or CacheEx environments, fake or unstable DCWs can appear.
|
||||
|
||||
For detailed information about building OSCam, cross-compilation for
|
||||
different CPUs, required and optional dependencies, SSL support, hardware
|
||||
modules, and platform-specific or distribution-specific notes, please
|
||||
refer to the OSCam wiki:
|
||||
Default behavior:
|
||||
- First CW wins
|
||||
- Possible glitches, freezing, or unstable decoding
|
||||
|
||||
- [Wiki Home](https://git.streamboard.tv/common/oscam/-/wikis/home)
|
||||
With AI Fake DCW Detector:
|
||||
- Multiple CWs are collected
|
||||
- Sources are tracked
|
||||
- Votes are counted
|
||||
- Local readers can be weighted higher
|
||||
- Majority or timeout logic is applied
|
||||
- A reliable CW is selected
|
||||
|
||||
## License
|
||||
Result:
|
||||
|
||||
OSCam: Open Source CAM
|
||||
✔ Reduced fake DCWs
|
||||
✔ Increased decoding stability
|
||||
✔ Better CacheEx reliability
|
||||
✔ Smarter CW selection
|
||||
|
||||
Copyright (C) 2009-2026 OSCam developers
|
||||
---
|
||||
|
||||
OSCam is based on the Streamboard mp-cardserver 0.9d by dukat and has been
|
||||
extended and worked on by many more since then.
|
||||
# 🧠 Core Functions
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
## 1️⃣ `cw_vote_add()`
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
This function is called whenever a new CW is received.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
It:
|
||||
|
||||
For the full text of the license, please see the
|
||||
[COPYING](https://git.streamboard.tv/common/oscam/-/blob/master/COPYING)
|
||||
file in the OSCam repository.
|
||||
- Compares the CW with existing candidates
|
||||
- Increases vote count if it matches
|
||||
- Adds a new candidate if it is unique
|
||||
- Tracks:
|
||||
- Total votes
|
||||
- Local votes
|
||||
- Reader source
|
||||
- CW value
|
||||
- Identifies whether the source is:
|
||||
- Local reader
|
||||
- Virtual reader
|
||||
- CacheEx client
|
||||
- CSP source
|
||||
|
||||
## Contributing
|
||||
Each CW candidate is stored in a voting pool.
|
||||
|
||||
Contributions are welcome. If you want to help improve OSCam:
|
||||
---
|
||||
|
||||
- Browse the existing [issues](https://git.streamboard.tv/common/oscam/-/issues)
|
||||
and open a new issue if you find a bug or have a feature request.
|
||||
- Fork the [GitLab repository](https://git.streamboard.tv/common/oscam),
|
||||
create a topic branch, and submit a merge request.
|
||||
- Check the [OSCam wiki](https://git.streamboard.tv/common/oscam/-/wikis/home)
|
||||
for additional project information and guidelines.
|
||||
## 2️⃣ `cw_vote_decide()`
|
||||
|
||||
## Help and Support
|
||||
This function determines the final CW to use.
|
||||
|
||||
### Decision Process
|
||||
|
||||
1. Count total votes
|
||||
2. Verify minimum vote requirement
|
||||
3. Calculate effective score:
|
||||
4. Check for majority (> 50%)
|
||||
5. Apply timeout logic
|
||||
6. Apply fallback strategy if necessary
|
||||
|
||||
If a winner is found:
|
||||
|
||||
- CW is copied into `er->cw`
|
||||
- CacheEx hit statistics are updated
|
||||
- Optional logging is performed
|
||||
|
||||
If no clear winner exists, the system waits for more votes unless timeout rules apply.
|
||||
|
||||
---
|
||||
|
||||
# ⚙ Configuration Options
|
||||
|
||||
| Parameter | Description |
|
||||
|------------|------------|
|
||||
| `cwvote_enabled` | Enable/disable voting system |
|
||||
| `cwvote_max_candidates` | Maximum CW candidates stored |
|
||||
| `cwvote_compare_len` | Number of bytes used for CW comparison |
|
||||
| `cwvote_local_weight` | Weight multiplier for local votes |
|
||||
| `cwvote_min_votes` | Minimum required votes before decision |
|
||||
| `cwvote_timeout` | Timeout in milliseconds |
|
||||
| `cwvote_fallback` | Fallback mode (1 = best candidate, 2 = first candidate) |
|
||||
| `cwvote_log_enabled` | Enable detailed debug logging |
|
||||
|
||||
---
|
||||
|
||||
# 🏆 Winner Selection Logic
|
||||
|
||||
A CW is selected if:
|
||||
|
||||
- It has a strict majority (> 50%)
|
||||
OR
|
||||
- Timeout is reached and fallback mode allows selection
|
||||
|
||||
If timeout occurs and:
|
||||
|
||||
- `fallback = 1` → best scoring candidate is selected
|
||||
- `fallback = 2` → first candidate (slot 0) is selected
|
||||
|
||||
---
|
||||
|
||||
# 📊 Intelligent Source Awareness
|
||||
|
||||
The system distinguishes between:
|
||||
|
||||
- Trusted local readers
|
||||
- Virtual readers
|
||||
- CacheEx clients
|
||||
- CSP sources
|
||||
|
||||
Local readers can be weighted higher using `cwvote_local_weight`, improving reliability in hybrid environments.
|
||||
|
||||
---
|
||||
|
||||
# 📈 CacheEx Integration
|
||||
|
||||
If the winning CW originates from CacheEx:
|
||||
|
||||
- `cwcacheexhit` counters are incremented
|
||||
- Client and account statistics are updated
|
||||
- Global hit statistics are updated
|
||||
|
||||
This improves monitoring accuracy and performance analysis.
|
||||
|
||||
---
|
||||
|
||||
# 🚀 Advantages
|
||||
|
||||
- Detects and filters fake DCWs
|
||||
- Improves stability in multi-reader setups
|
||||
- Reduces glitching and freezing
|
||||
- Configurable and flexible logic
|
||||
- Seamlessly integrated into OSCam core
|
||||
|
||||
---
|
||||
|
||||
# 🧩 AI Concept
|
||||
|
||||
Although not based on machine learning, this system applies AI-style decision logic:
|
||||
|
||||
- Aggregates multiple inputs
|
||||
- Applies weighted scoring
|
||||
- Uses majority validation
|
||||
- Implements adaptive timeout fallback
|
||||
|
||||
It behaves like deterministic intelligence for CW selection.
|
||||
|
||||
---
|
||||
|
||||
# Intended Use
|
||||
|
||||
Designed for:
|
||||
|
||||
- Emulated environments
|
||||
- CacheEx-heavy setups
|
||||
- Multi-reader configurations
|
||||
- Advanced OSCam tuning
|
||||
- High-stability streaming environments
|
||||
|
||||
---
|
||||
|
||||
# License
|
||||
|
||||
Based on the OSCam open-source project.
|
||||
This repository includes custom modifications implementing advanced CW voting and fake DCW detection logic.
|
||||
|
||||
- Man pages and configuration examples are available in the
|
||||
`Distribution/doc` directory of the source tree.
|
||||
- For bug reports and feature requests, please use the
|
||||
[GitLab issue tracker](https://git.streamboard.tv/common/oscam/-/issues).
|
||||
- For community help and general discussion (mainly German and English),
|
||||
visit the [support forum](https://board.streamboard.tv/forum/).
|
||||
- For configuration guides, FAQs, and detailed documentation, see the
|
||||
[OSCam wiki](https://git.streamboard.tv/common/oscam/-/wikis/home).
|
||||
|
|
|
|||
69
config.h
Executable file → Normal file
69
config.h
Executable file → Normal file
|
|
@ -1,24 +1,22 @@
|
|||
#ifndef CONFIG_H_
|
||||
#define CONFIG_H_
|
||||
|
||||
#define WITH_EMU 1
|
||||
#define WITH_SOFTCAM 1
|
||||
#define WEBIF 1
|
||||
#define WEBIF_LIVELOG 1
|
||||
#define WEBIF_JQUERY 1
|
||||
<<<<<<< Updated upstream
|
||||
//#define WEBIF_WIKI 1
|
||||
=======
|
||||
#define WEBIF_WIKI 1
|
||||
>>>>>>> Stashed changes
|
||||
#define WITH_COMPRESS_WEBIF 1
|
||||
//#define WITH_COMPRESS_WEBIF 1
|
||||
//#define WITH_SSL 1
|
||||
#if defined(__linux__) || defined(__CYGWIN__)
|
||||
//#define HAVE_DVBAPI 1
|
||||
//#define WITH_EXTENDED_CW 1
|
||||
#define HAVE_DVBAPI 1
|
||||
#define WITH_EXTENDED_CW 1
|
||||
#endif
|
||||
//#define WITH_NEUTRINO 1
|
||||
#define READ_SDT_CHARSETS 1
|
||||
//#define CLOCKFIX 1
|
||||
#define CS_ANTICASC 1
|
||||
//#define CS_ANTICASC 1
|
||||
#define WITH_DEBUG 1
|
||||
#define WITH_LB 1
|
||||
#define CS_CACHEEX 1
|
||||
|
|
@ -26,51 +24,46 @@
|
|||
#define CW_CYCLE_CHECK 1
|
||||
//#define LCDSUPPORT 1
|
||||
//#define LEDSUPPORT 1
|
||||
//#define IPV6SUPPORT 1
|
||||
#define IPV6SUPPORT 1
|
||||
//#define WITH_ARM_NEON 1
|
||||
//#define WITH_SIGNING 1
|
||||
//#define MODULE_MONITOR 1
|
||||
#define MODULE_MONITOR 1
|
||||
|
||||
//#define MODULE_CAMD33 1
|
||||
//#define MODULE_CAMD35 1
|
||||
#define MODULE_CAMD33 1
|
||||
#define MODULE_CAMD35 1
|
||||
#define MODULE_CAMD35_TCP 1
|
||||
#define MODULE_NEWCAMD 1
|
||||
#define MODULE_CCCAM 1
|
||||
#define MODULE_CCCSHARE 1
|
||||
//#define MODULE_GBOX 1
|
||||
//#define MODULE_RADEGAST 1
|
||||
#define MODULE_GBOX 1
|
||||
#define MODULE_RADEGAST 1
|
||||
//#define MODULE_SERIAL 1
|
||||
//#define MODULE_CONSTCW 1
|
||||
#define MODULE_CONSTCW 1
|
||||
//#define MODULE_PANDORA 1
|
||||
//#define MODULE_GHTTP 1
|
||||
<<<<<<< Updated upstream
|
||||
#define MODULE_GHTTP 1
|
||||
//#define MODULE_SCAM 1
|
||||
//#define MODULE_STREAMRELAY 1
|
||||
=======
|
||||
#define MODULE_SCAM 1
|
||||
#define MODULE_STREAMRELAY 1
|
||||
>>>>>>> Stashed changes
|
||||
|
||||
//#define WITH_CARDREADER 1
|
||||
#define WITH_CARDREADER 1
|
||||
|
||||
#ifdef WITH_CARDREADER
|
||||
|
||||
//#define READER_NAGRA 1
|
||||
//#define READER_NAGRA_MERLIN 1
|
||||
//#define READER_IRDETO 1
|
||||
//#define READER_CONAX 1
|
||||
//#define READER_CRYPTOWORKS 1
|
||||
//#define READER_SECA 1
|
||||
//#define READER_VIACCESS 1
|
||||
//#define READER_VIDEOGUARD 1
|
||||
//#define READER_DRE 1
|
||||
//#define READER_TONGFANG 1
|
||||
//#define READER_BULCRYPT 1
|
||||
//#define READER_GRIFFIN 1
|
||||
//#define READER_DGCRYPT 1
|
||||
#define READER_NAGRA 1
|
||||
#define READER_NAGRA_MERLIN 1
|
||||
#define READER_IRDETO 1
|
||||
#define READER_CONAX 1
|
||||
#define READER_CRYPTOWORKS 1
|
||||
#define READER_SECA 1
|
||||
#define READER_VIACCESS 1
|
||||
#define READER_VIDEOGUARD 1
|
||||
#define READER_DRE 1
|
||||
#define READER_TONGFANG 1
|
||||
#define READER_BULCRYPT 1
|
||||
#define READER_GRIFFIN 1
|
||||
#define READER_DGCRYPT 1
|
||||
|
||||
//#define CARDREADER_PHOENIX 1
|
||||
//#define CARDREADER_INTERNAL 1
|
||||
#define CARDREADER_PHOENIX 1
|
||||
#define CARDREADER_INTERNAL 1
|
||||
//#define CARDREADER_MP35 1
|
||||
//#define CARDREADER_SC8IN1 1
|
||||
//#define CARDREADER_SMARGO 1
|
||||
|
|
@ -79,7 +72,7 @@
|
|||
//#define CARDREADER_DRECAS 1
|
||||
|
||||
#ifdef WITH_PCSC
|
||||
#define CARDREADER_PCSC 1
|
||||
//#define CARDREADER_PCSC 1
|
||||
#endif
|
||||
|
||||
#ifdef WITH_LIBUSB
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
EXTRA_LIBS += -lm
|
||||
64
config.sh
64
config.sh
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY WEBIF_WIKI WITH_COMPRESS_WEBIF WITH_SSL HAVE_DVBAPI WITH_EXTENDED_CW WITH_NEUTRINO READ_SDT_CHARSETS CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_ARM_NEON WITH_SIGNING"
|
||||
addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY WEBIF_WIKI WITH_COMPRESS_WEBIF WITH_SSL HAVE_DVBAPI WITH_EXTENDED_CW WITH_NEUTRINO READ_SDT_CHARSETS CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_ARM_NEON WITH_SIGNING WITH_EMU WITH_SOFTCAM"
|
||||
protocols="MODULE_CAMD33 MODULE_CAMD35 MODULE_CAMD35_TCP MODULE_NEWCAMD MODULE_CCCAM MODULE_CCCSHARE MODULE_GBOX MODULE_RADEGAST MODULE_SCAM MODULE_SERIAL MODULE_CONSTCW MODULE_PANDORA MODULE_GHTTP MODULE_STREAMRELAY"
|
||||
readers="READER_NAGRA READER_NAGRA_MERLIN READER_IRDETO READER_CONAX READER_CRYPTOWORKS READER_SECA READER_VIACCESS READER_VIDEOGUARD READER_DRE READER_TONGFANG READER_BULCRYPT READER_GRIFFIN READER_DGCRYPT"
|
||||
card_readers="CARDREADER_PHOENIX CARDREADER_INTERNAL CARDREADER_SC8IN1 CARDREADER_MP35 CARDREADER_SMARGO CARDREADER_DB2COM CARDREADER_STAPI CARDREADER_STAPI5 CARDREADER_STINGER CARDREADER_DRECAS"
|
||||
|
|
@ -9,40 +9,42 @@ defconfig="
|
|||
CONFIG_WEBIF=y
|
||||
CONFIG_WEBIF_LIVELOG=y
|
||||
CONFIG_WEBIF_JQUERY=y
|
||||
# CONFIG_WEBIF_WIKI=n
|
||||
CONFIG_WEBIF_WIKI=y
|
||||
CONFIG_WITH_COMPRESS_WEBIF=y
|
||||
# CONFIG_WITH_SSL=n
|
||||
CONFIG_WITH_SSL=y
|
||||
CONFIG_HAVE_DVBAPI=y
|
||||
# CONFIG_WITH_EXTENDED_CW=n
|
||||
CONFIG_WITH_EXTENDED_CW=y
|
||||
# CONFIG_WITH_NEUTRINO=n
|
||||
CONFIG_READ_SDT_CHARSETS=y
|
||||
# CONFIG_CS_ANTICASC=n
|
||||
CONFIG_CS_ANTICASC=y
|
||||
CONFIG_WITH_DEBUG=y
|
||||
CONFIG_MODULE_MONITOR=y
|
||||
CONFIG_WITH_LB=y
|
||||
# CONFIG_CS_CACHEEX=n
|
||||
# CONFIG_CS_CACHEEX_AIO=n
|
||||
# CONFIG_CW_CYCLE_CHECK=n
|
||||
# CONFIG_LCDSUPPORT=n
|
||||
# CONFIG_LEDSUPPORT=n
|
||||
# CONFIG_CLOCKFIX=n
|
||||
# CONFIG_IPV6SUPPORT=n
|
||||
CONFIG_CS_CACHEEX=y
|
||||
CONFIG_CS_CACHEEX_AIO=y
|
||||
CONFIG_CW_CYCLE_CHECK=y
|
||||
CONFIG_LCDSUPPORT=y
|
||||
CONFIG_LEDSUPPORT=y
|
||||
CONFIG_CLOCKFIX=y
|
||||
CONFIG_IPV6SUPPORT=y
|
||||
# CONFIG_WITH_ARM_NEON=n
|
||||
# CONFIG_WITH_SIGNING=n
|
||||
# CONFIG_MODULE_CAMD33=n
|
||||
CONFIG_WITH_SIGNING=n
|
||||
CONFIG_WITH_EMU=y
|
||||
CONFIG_WITH_SOFTCAM=y
|
||||
CONFIG_MODULE_CAMD33=n
|
||||
CONFIG_MODULE_CAMD35=y
|
||||
CONFIG_MODULE_CAMD35_TCP=y
|
||||
CONFIG_MODULE_NEWCAMD=y
|
||||
CONFIG_MODULE_CCCAM=y
|
||||
CONFIG_MODULE_CCCSHARE=y
|
||||
CONFIG_MODULE_GBOX=y
|
||||
# CONFIG_MODULE_RADEGAST=n
|
||||
# CONFIG_MODULE_SERIAL=n
|
||||
# CONFIG_MODULE_CONSTCW=n
|
||||
# CONFIG_MODULE_PANDORA=n
|
||||
# CONFIG_MODULE_SCAM=n
|
||||
# CONFIG_MODULE_GHTTP=n
|
||||
# CONFIG_MODULE_STREAMRELAY=n
|
||||
CONFIG_MODULE_RADEGAST=y
|
||||
CONFIG_MODULE_SERIAL=y
|
||||
CONFIG_MODULE_CONSTCW=y
|
||||
CONFIG_MODULE_PANDORA=y
|
||||
CONFIG_MODULE_SCAM=y
|
||||
CONFIG_MODULE_GHTTP=y
|
||||
CONFIG_MODULE_STREAMRELAY=y
|
||||
CONFIG_WITH_CARDREADER=y
|
||||
CONFIG_READER_NAGRA_COMMON=y
|
||||
CONFIG_READER_NAGRA=y
|
||||
|
|
@ -331,8 +333,8 @@ get_opts() {
|
|||
|
||||
update_deps() {
|
||||
# Calculate dependencies
|
||||
enabled_any $(get_opts readers) $(get_opts card_readers) && enable_opt WITH_CARDREADER >/dev/null
|
||||
disabled_all $(get_opts readers) $(get_opts card_readers) && disable_opt WITH_CARDREADER >/dev/null
|
||||
enabled_any $(get_opts readers) $(get_opts card_readers) WITH_EMU && enable_opt WITH_CARDREADER >/dev/null
|
||||
disabled_all $(get_opts readers) $(get_opts card_readers) WITH_EMU && disable_opt WITH_CARDREADER >/dev/null
|
||||
disabled WEBIF && disable_opt WEBIF_LIVELOG >/dev/null
|
||||
disabled WEBIF && disable_opt WEBIF_JQUERY >/dev/null
|
||||
disabled WEBIF && disable_opt WEBIF_WIKI >/dev/null
|
||||
|
|
@ -341,6 +343,9 @@ update_deps() {
|
|||
enabled_any CARDREADER_DB2COM CARDREADER_MP35 CARDREADER_SC8IN1 CARDREADER_STINGER && enable_opt CARDREADER_PHOENIX >/dev/null
|
||||
enabled CS_CACHEEX_AIO && enable_opt CS_CACHEEX >/dev/null
|
||||
enabled WITH_SIGNING && enable_opt WITH_SSL >/dev/null
|
||||
enabled WITH_EMU && enable_opt READER_VIACCESS >/dev/null
|
||||
enabled WITH_EMU && enable_opt MODULE_NEWCAMD >/dev/null
|
||||
disabled WITH_EMU && disable_opt WITH_SOFTCAM >/dev/null
|
||||
}
|
||||
|
||||
list_config() {
|
||||
|
|
@ -392,9 +397,9 @@ list_config() {
|
|||
not_have_flag USE_LIBCRYPTO && echo "CONFIG_LIB_AES=y" || echo "# CONFIG_LIB_AES=n"
|
||||
enabled MODULE_CCCAM && echo "CONFIG_LIB_RC6=y" || echo "# CONFIG_LIB_RC6=n"
|
||||
not_have_flag USE_LIBCRYPTO && enabled MODULE_CCCAM && echo "CONFIG_LIB_SHA1=y" || echo "# CONFIG_LIB_SHA1=n"
|
||||
enabled_any READER_DRE MODULE_SCAM READER_VIACCESS READER_NAGRA READER_NAGRA_MERLIN READER_VIDEOGUARD READER_CONAX READER_TONGFANG && echo "CONFIG_LIB_DES=y" || echo "# CONFIG_LIB_DES=n"
|
||||
enabled_any MODULE_CCCAM READER_NAGRA READER_NAGRA_MERLIN READER_SECA && echo "CONFIG_LIB_IDEA=y" || echo "# CONFIG_LIB_IDEA=n"
|
||||
not_have_flag USE_LIBCRYPTO && enabled_any READER_CONAX READER_CRYPTOWORKS READER_NAGRA READER_NAGRA_MERLIN && echo "CONFIG_LIB_BIGNUM=y" || echo "# CONFIG_LIB_BIGNUM=n"
|
||||
enabled_any READER_DRE MODULE_SCAM READER_VIACCESS READER_NAGRA READER_NAGRA_MERLIN READER_VIDEOGUARD READER_CONAX READER_TONGFANG WITH_EMU && echo "CONFIG_LIB_DES=y" || echo "# CONFIG_LIB_DES=n"
|
||||
enabled_any MODULE_CCCAM READER_NAGRA READER_NAGRA_MERLIN READER_SECA WITH_EMU && echo "CONFIG_LIB_IDEA=y" || echo "# CONFIG_LIB_IDEA=n"
|
||||
not_have_flag USE_LIBCRYPTO && enabled_any READER_CONAX READER_CRYPTOWORKS READER_NAGRA READER_NAGRA_MERLIN WITH_EMU && echo "CONFIG_LIB_BIGNUM=y" || echo "# CONFIG_LIB_BIGNUM=n"
|
||||
enabled READER_NAGRA_MERLIN && echo "CONFIG_LIB_MDC2=y" || echo "# CONFIG_LIB_MDC2=n"
|
||||
enabled READER_NAGRA_MERLIN && echo "CONFIG_LIB_FAST_AES=y" || echo "# CONFIG_LIB_FAST_AES=n"
|
||||
enabled_any READER_NAGRA_MERLIN WITH_SIGNING && echo "CONFIG_LIB_SHA256=y" || echo "# CONFIG_LIB_SHA256=n"
|
||||
|
|
@ -524,6 +529,8 @@ menu_addons() {
|
|||
IPV6SUPPORT "IPv6 support (experimental)" $(check_test "IPV6SUPPORT") \
|
||||
WITH_ARM_NEON "ARM NEON (SIMD/MPE) support" $(check_test "WITH_ARM_NEON") \
|
||||
WITH_SIGNING "Binary signing with X.509 certificate" $(check_test "WITH_SIGNING") \
|
||||
WITH_EMU "Emulator support" $(check_test "WITH_EMU") \
|
||||
WITH_SOFTCAM "Built-in SoftCam.Key" $(check_test "WITH_SOFTCAM") \
|
||||
2> ${tempfile}
|
||||
|
||||
opt=${?}
|
||||
|
|
@ -906,7 +913,8 @@ do
|
|||
;;
|
||||
'-v'|'--oscam-version')
|
||||
version=`grep '^#define CS_VERSION' globals.h | cut -d\" -f2`
|
||||
echo $version
|
||||
emuversion=`grep EMU_VERSION module-emulator-osemu.h | awk '{ print $3 }'`
|
||||
echo $version-$emuversion
|
||||
break
|
||||
;;
|
||||
'--submodule')
|
||||
|
|
@ -957,7 +965,7 @@ do
|
|||
break
|
||||
;;
|
||||
'-c'|'--oscam-commit')
|
||||
sha=`git log 2>/dev/null | sed -n 1p | cut -d ' ' -f2 | cut -c1-8`
|
||||
sha=`git log --no-merges 2>/dev/null | sed -n 1p | cut -d ' ' -f2 | cut -c1-8`
|
||||
echo $sha
|
||||
break
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -15,5 +15,6 @@ extern const struct s_cardreader cardreader_smartreader;
|
|||
extern const struct s_cardreader cardreader_stapi;
|
||||
extern const struct s_cardreader cardreader_stinger;
|
||||
extern const struct s_cardreader cardreader_drecas;
|
||||
extern const struct s_cardreader cardreader_emu;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
109
globals.h
109
globals.h
|
|
@ -365,14 +365,19 @@
|
|||
|
||||
#define CHECK_BIT(var,pos) (((var) & (1<<(pos)))? 1 : 0)
|
||||
|
||||
// CW VOTING
|
||||
#define MAX_VOTE_CANDIDATES 16 // Zwiększono dla obsługi większej liczby głosów
|
||||
#define MIN_VOTES_REQUIRED 2
|
||||
#define CW_COMPARE_LEN 14
|
||||
|
||||
/* ===========================
|
||||
* constants
|
||||
* =========================== */
|
||||
#define SCM_URL "https://git.streamboard.tv/common/oscam"
|
||||
#define SCM_URL "https://github.com/oscam-mirror/oscam-emu"
|
||||
#define WIKI_URL "https://git.streamboard.tv/common/oscam/-/wikis"
|
||||
#define BOARD_URL "https://board.streamboard.tv"
|
||||
#define BOARD_URL "https://github.com/oscam-mirror/oscam-emu/discussions"
|
||||
#ifndef CS_VERSION
|
||||
#define CS_VERSION "2.26.02-11943"
|
||||
#define CS_VERSION "2.26.01-11942"
|
||||
#endif
|
||||
#ifndef CS_GIT_COMMIT
|
||||
#define CS_GIT_COMMIT "a2b4c6d8"
|
||||
|
|
@ -398,9 +403,6 @@
|
|||
#define CS_CLIENT_HASHBUCKETS 32
|
||||
#define CS_SERVICENAME_SIZE 32
|
||||
|
||||
#define WEBIF_MAX_NODES 16
|
||||
extern uint32_t webif_last_nodeid[WEBIF_MAX_NODES];
|
||||
|
||||
#define CS_ECMSTORESIZE 16 // use MD5()
|
||||
#define CS_EMMSTORESIZE 16 // use MD5()
|
||||
#define CS_CLIENT_TIMEOUT 5000
|
||||
|
|
@ -410,14 +412,18 @@ extern uint32_t webif_last_nodeid[WEBIF_MAX_NODES];
|
|||
#define CS_ECM_RINGBUFFER_MAX 0x10 // max size for ECM last responsetimes ringbuffer. Keep this set to power of 2 values!
|
||||
|
||||
// Support for multiple CWs per channel and other encryption algos
|
||||
//#define WITH_EXTENDED_CW 1
|
||||
#define WITH_EXTENDED_CW 1
|
||||
|
||||
#define MAX_ECM_SIZE 1024
|
||||
#define MAX_EMM_SIZE 1024
|
||||
|
||||
#define MAX_CMD_SIZE 0xff + 5 // maximum value from length byte + command header
|
||||
|
||||
#ifdef WITH_EMU
|
||||
#define CS_EMMCACHESIZE 1024 // nr of EMMs that EMU reader will cache
|
||||
#else
|
||||
#define CS_EMMCACHESIZE 512 // nr of EMMs that each reader will cache
|
||||
#endif
|
||||
#define MSGLOGSIZE 64 // size of string buffer for a ecm to return messages
|
||||
|
||||
#define D_TRACE 0x0001 // Generate very detailed error/trace messages per routine
|
||||
|
|
@ -452,10 +458,11 @@ extern uint32_t webif_last_nodeid[WEBIF_MAX_NODES];
|
|||
#define R_MOUSE 0x5 // Reader smartcard mouse
|
||||
/////// internal readers (Dreambox, Coolstream, IPBox) are all R_INTERNAL, they are determined compile-time
|
||||
#define R_INTERNAL 0x6 // Reader smartcard intern
|
||||
#define R_SMART 0x7 // Reader smartcard (generic)
|
||||
/////// readers that do not reed baudrate setting and timings are guarded by reader itself (large buffer built in): AFTER R_SMART
|
||||
#define R_SMART 0x7 // Smartreader+
|
||||
#define R_PCSC 0x8 // PCSC
|
||||
#define R_DRECAS 0x9 // Reader DRECAS
|
||||
#define R_EMU 0x17 // Reader EMU
|
||||
/////// proxy readers after R_CS378X
|
||||
#define R_CAMD35 0x20 // Reader cascading camd 3.5x
|
||||
#define R_CAMD33 0x21 // Reader cascading camd 3.3x
|
||||
|
|
@ -893,6 +900,13 @@ typedef struct s_entitlement // contains entitlement Info
|
|||
uint32_t class; // the class needed for some systems
|
||||
time_t start; // startdate
|
||||
time_t end; // enddate
|
||||
#ifdef WITH_EMU
|
||||
bool isKey;
|
||||
bool isData;
|
||||
char name[8];
|
||||
uint8_t *key;
|
||||
uint32_t keyLength;
|
||||
#endif
|
||||
} S_ENTITLEMENT;
|
||||
|
||||
struct s_client;
|
||||
|
|
@ -902,7 +916,7 @@ struct cmd_packet_t;
|
|||
struct s_ecm_answer;
|
||||
struct demux_s;
|
||||
|
||||
#define DEFAULT_MODULE_BUFSIZE 1024
|
||||
#define DEFAULT_MODULE_BUFsize 1024
|
||||
|
||||
struct s_module
|
||||
{
|
||||
|
|
@ -1027,6 +1041,26 @@ typedef struct cw_extendted_t
|
|||
#endif
|
||||
} EXTENDED_CW;
|
||||
|
||||
typedef struct s_cw_vote {
|
||||
uint8_t cw[16];
|
||||
uint8_t votes;
|
||||
uint8_t local_votes;
|
||||
uint8_t has_cacheex_vote; // FIX #2: flaga czy głos pochodzi z cacheex
|
||||
struct s_reader *voters[MAX_VOTE_CANDIDATES];
|
||||
} s_cw_vote;
|
||||
|
||||
// CW Vote settings
|
||||
typedef struct s_cw_vote_caid_data
|
||||
{
|
||||
uint16_t caid;
|
||||
} CW_VOTE_CAID_DATA;
|
||||
|
||||
typedef struct s_cw_vote_caid_tab
|
||||
{
|
||||
int32_t cvcnum;
|
||||
CW_VOTE_CAID_DATA *cvcdata;
|
||||
} CW_VOTE_CAID_TAB;
|
||||
|
||||
typedef struct ecm_request_t
|
||||
{
|
||||
uint8_t ecm[MAX_ECM_SIZE];
|
||||
|
|
@ -1097,7 +1131,6 @@ typedef struct ecm_request_t
|
|||
uint16_t cacheex_mode1_delay; // cacheex mode 1 delay
|
||||
uint8_t cacheex_hitcache; // =1 if wait_time due hitcache
|
||||
void *cw_cache; // pointer to cw stored in cache
|
||||
uint8_t entropy_blocked; // =1 if entropy check blocked CW
|
||||
#endif
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
int32_t ecm_time; // ecm-time in ms
|
||||
|
|
@ -1121,9 +1154,10 @@ typedef struct ecm_request_t
|
|||
#ifdef HAVE_DVBAPI
|
||||
uint8_t adapter_index;
|
||||
#endif
|
||||
uint32_t vote_pool_session; // sesja dla vote_pool - zapobiega wielokrotnemu czyszczeniu
|
||||
struct s_cw_vote vote_pool[MAX_VOTE_CANDIDATES];
|
||||
} ECM_REQUEST;
|
||||
|
||||
|
||||
struct s_ecm_answer
|
||||
{
|
||||
uint8_t status;
|
||||
|
|
@ -1516,6 +1550,7 @@ typedef struct ce_csp_t
|
|||
CAIDVALUETAB maxhop_lg_percaid;
|
||||
#endif
|
||||
CECSPVALUETAB filter_caidtab;
|
||||
CAIDVALUETAB cacheex_nopushafter_tab;
|
||||
uint8_t allow_request;
|
||||
uint8_t allow_reforward;
|
||||
uint8_t drop_csp;
|
||||
|
|
@ -1535,7 +1570,6 @@ typedef struct ce_csp_t
|
|||
uint8_t lg_only_in_aio_only;
|
||||
uint8_t lg_only_remote_settings;
|
||||
int32_t feature_bitfield;
|
||||
CAIDVALUETAB cacheex_nopushafter_tab;
|
||||
char aio_version[CS_AIO_VERSION_LEN];
|
||||
#endif
|
||||
} CECSP;
|
||||
|
|
@ -1691,7 +1725,7 @@ struct s_reader // contains device info, reader info and card info
|
|||
#endif
|
||||
int32_t r_port;
|
||||
char r_usr[64];
|
||||
char r_pwd[64];
|
||||
char r_pwd[64]; // Max length 63 + null terminator
|
||||
int32_t l_port;
|
||||
CAIDTAB ctab;
|
||||
uint32_t boxid;
|
||||
|
|
@ -1800,6 +1834,7 @@ struct s_reader // contains device info, reader info and card info
|
|||
char cc_build[7]; // cccam build number
|
||||
int8_t cc_maxhops; // cccam max distance
|
||||
int8_t cc_mindown; // cccam min downhops
|
||||
int8_t cc_want_emu; // Schlocke: Client want to have EMUs, 0 - NO; 1 - YES
|
||||
uint32_t cc_id;
|
||||
int8_t cc_keepalive;
|
||||
int8_t cc_hop; // For non-cccam reader: hop for virtual cards
|
||||
|
|
@ -1996,6 +2031,11 @@ struct s_reader // contains device info, reader info and card info
|
|||
#ifdef MODULE_GHTTP
|
||||
uint8_t ghttp_use_ssl;
|
||||
#endif
|
||||
#ifdef WITH_EMU
|
||||
FTAB emu_auproviders; // AU providers for Emu reader
|
||||
int8_t emu_datecodedenabled; // date-coded keys for BISS
|
||||
LLIST *ll_biss2_rsa_keys; // BISS2 RSA keys - Read from external PEM files
|
||||
#endif
|
||||
#ifdef READER_CONAX
|
||||
uint8_t cnxlastecm; // == 0 - last ecm has not been paired ecm, > 0 last ecm has been paired ecm
|
||||
#endif
|
||||
|
|
@ -2233,7 +2273,7 @@ struct s_config
|
|||
uint32_t ctimeout;
|
||||
uint32_t ftimeout;
|
||||
CAIDVALUETAB ftimeouttab;
|
||||
CAIDVALUETAB ctimeouttab; // <EFBFBD> clienttimeout_percaid
|
||||
CAIDVALUETAB ctimeouttab; // ← clienttimeout_percaid
|
||||
uint32_t cmaxidle;
|
||||
int32_t ulparent;
|
||||
uint32_t delay;
|
||||
|
|
@ -2445,6 +2485,17 @@ struct s_config
|
|||
int8_t double_check; // schlocke: Double checks each ecm+dcw from two (or more) readers
|
||||
FTAB double_check_caid; // do not store loadbalancer stats with providers for this caid
|
||||
|
||||
// CW Vote settings
|
||||
int8_t cwvote_enabled;
|
||||
int8_t cwvote_log_enabled;
|
||||
int32_t cwvote_timeout; // w milisekundach, 0 = wyłączony
|
||||
int32_t cwvote_min_votes;
|
||||
float cwvote_local_weight; // waga local readera (np. 2.0)
|
||||
int32_t cwvote_max_candidates;
|
||||
int32_t cwvote_compare_len; // 8 lub 16
|
||||
int32_t cwvote_fallback; // 0=wait, 1=best anyway, 2=first CW
|
||||
CW_VOTE_CAID_TAB cwvote_caids;
|
||||
|
||||
#ifdef HAVE_DVBAPI
|
||||
int8_t dvbapi_enabled;
|
||||
int8_t dvbapi_au;
|
||||
|
|
@ -2534,6 +2585,10 @@ struct s_config
|
|||
#else
|
||||
#define DEFAULT_STREAM_SOURCE_PORT 8001 //Enigma2
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WITH_EMU
|
||||
uint32_t emu_stream_ecm_delay;
|
||||
int8_t emu_stream_emm_enabled;
|
||||
#endif
|
||||
|
||||
int32_t max_cache_time; // seconds ecms are stored in ecmcwcache
|
||||
|
|
@ -2568,7 +2623,6 @@ struct s_config
|
|||
CECSPVALUETAB cacheex_wait_timetab;
|
||||
CAIDVALUETAB cacheex_mode1_delay_tab;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
CAIDVALUETAB cacheex_nopushafter_tab;
|
||||
uint8_t waittime_block_start;
|
||||
uint16_t waittime_block_time;
|
||||
#endif
|
||||
|
|
@ -2588,15 +2642,24 @@ struct s_config
|
|||
uint8_t cacheex_lg_only_remote_settings;
|
||||
uint8_t cacheex_localgenerated_only;
|
||||
CAIDTAB cacheex_localgenerated_only_caidtab;
|
||||
FTAB cacheex_lg_only_tab;
|
||||
FTAB lg_only_tab;
|
||||
uint8_t localgenerated_only_in;
|
||||
CAIDTAB localgenerated_only_in_caidtab;
|
||||
FTAB lg_only_in_tab;
|
||||
uint8_t lg_only_in_aio_only;
|
||||
uint8_t lg_only_remote_settings;
|
||||
int32_t feature_bitfield;
|
||||
char aio_version[CS_AIO_VERSION_LEN];
|
||||
#endif
|
||||
CAIDVALUETAB cacheex_nopushafter_tab;
|
||||
uint8_t cacheex_localgenerated_only_in;
|
||||
CAIDTAB cacheex_localgenerated_only_in_caidtab;
|
||||
FTAB cacheex_lg_only_in_tab;
|
||||
uint8_t cacheex_lg_only_in_aio_only;
|
||||
CECSPVALUETAB cacheex_filter_caidtab;
|
||||
CECSPVALUETAB cacheex_filter_caidtab_aio;
|
||||
uint64_t cacheex_push_lg_groups;
|
||||
#endif
|
||||
uint32_t cacheex_push_lg_groups;
|
||||
FTAB cacheex_lg_only_tab;
|
||||
#endif
|
||||
|
||||
#ifdef CW_CYCLE_CHECK
|
||||
|
|
@ -2727,12 +2790,18 @@ bool boxtype_is(const char *boxtype);
|
|||
bool boxname_is(const char *boxname);
|
||||
const char *boxtype_get(void);
|
||||
const char *boxname_get(void);
|
||||
static inline bool caid_is_fake(uint16_t caid) { return caid == 0xffff; }
|
||||
static inline bool caid_is_biss(uint16_t caid) { return caid >> 8 == 0x26; }
|
||||
static inline bool caid_is_biss_fixed(uint16_t caid) { return caid == 0x2600 || caid == 0x2602; } // fixed cw, fake ecm
|
||||
static inline bool caid_is_biss_dynamic(uint16_t caid) { return caid == 0x2610; } // dynamic cw, real ecm and emm
|
||||
static inline bool caid_is_seca(uint16_t caid) { return caid >> 8 == 0x01; }
|
||||
static inline bool caid_is_viaccess(uint16_t caid) { return caid >> 8 == 0x05; }
|
||||
static inline bool caid_is_irdeto(uint16_t caid) { return caid >> 8 == 0x06; }
|
||||
static inline bool caid_is_videoguard(uint16_t caid) { return caid >> 8 == 0x09; }
|
||||
static inline bool caid_is_conax(uint16_t caid) { return caid >> 8 == 0x0B; }
|
||||
static inline bool caid_is_cryptoworks(uint16_t caid) { return caid >> 8 == 0x0D; }
|
||||
static inline bool caid_is_powervu(uint16_t caid) { return caid >> 8 == 0x0E; }
|
||||
static inline bool caid_is_director(uint16_t caid) { return caid >> 8 == 0x10; }
|
||||
static inline bool caid_is_betacrypt(uint16_t caid) { return caid >> 8 == 0x17; }
|
||||
static inline bool caid_is_nagra(uint16_t caid) { return caid >> 8 == 0x18; }
|
||||
static inline bool caid_is_bulcrypt(uint16_t caid) { return caid == 0x5581 || caid == 0x4AEE; }
|
||||
|
|
@ -2750,4 +2819,8 @@ static inline uint8_t get_ecm_mode(const ECM_REQUEST *er) {
|
|||
#endif
|
||||
const char *get_cardsystem_desc_by_caid(uint16_t caid);
|
||||
|
||||
#ifdef WITH_EMU
|
||||
FILTER *get_emu_prids_for_caid(struct s_reader *rdr, uint16_t caid);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -365,14 +365,19 @@
|
|||
|
||||
#define CHECK_BIT(var,pos) (((var) & (1<<(pos)))? 1 : 0)
|
||||
|
||||
// CW VOTING
|
||||
#define MAX_VOTE_CANDIDATES 16 // Zwiększono dla obsługi większej liczby głosów
|
||||
#define MIN_VOTES_REQUIRED 2
|
||||
#define CW_COMPARE_LEN 14
|
||||
|
||||
/* ===========================
|
||||
* constants
|
||||
* =========================== */
|
||||
#define SCM_URL "https://git.streamboard.tv/common/oscam"
|
||||
#define SCM_URL "https://github.com/oscam-mirror/oscam-emu"
|
||||
#define WIKI_URL "https://git.streamboard.tv/common/oscam/-/wikis"
|
||||
#define BOARD_URL "https://board.streamboard.tv"
|
||||
#define BOARD_URL "https://github.com/oscam-mirror/oscam-emu/discussions"
|
||||
#ifndef CS_VERSION
|
||||
#define CS_VERSION "2.26.02-11943"
|
||||
#define CS_VERSION "2.26.01-11942"
|
||||
#endif
|
||||
#ifndef CS_GIT_COMMIT
|
||||
#define CS_GIT_COMMIT "a2b4c6d8"
|
||||
|
|
@ -407,14 +412,18 @@
|
|||
#define CS_ECM_RINGBUFFER_MAX 0x10 // max size for ECM last responsetimes ringbuffer. Keep this set to power of 2 values!
|
||||
|
||||
// Support for multiple CWs per channel and other encryption algos
|
||||
//#define WITH_EXTENDED_CW 1
|
||||
#define WITH_EXTENDED_CW 1
|
||||
|
||||
#define MAX_ECM_SIZE 1024
|
||||
#define MAX_EMM_SIZE 1024
|
||||
|
||||
#define MAX_CMD_SIZE 0xff + 5 // maximum value from length byte + command header
|
||||
|
||||
#ifdef WITH_EMU
|
||||
#define CS_EMMCACHESIZE 1024 // nr of EMMs that EMU reader will cache
|
||||
#else
|
||||
#define CS_EMMCACHESIZE 512 // nr of EMMs that each reader will cache
|
||||
#endif
|
||||
#define MSGLOGSIZE 64 // size of string buffer for a ecm to return messages
|
||||
|
||||
#define D_TRACE 0x0001 // Generate very detailed error/trace messages per routine
|
||||
|
|
@ -449,10 +458,11 @@
|
|||
#define R_MOUSE 0x5 // Reader smartcard mouse
|
||||
/////// internal readers (Dreambox, Coolstream, IPBox) are all R_INTERNAL, they are determined compile-time
|
||||
#define R_INTERNAL 0x6 // Reader smartcard intern
|
||||
#define R_SMART 0x7 // Reader smartcard (generic)
|
||||
/////// readers that do not reed baudrate setting and timings are guarded by reader itself (large buffer built in): AFTER R_SMART
|
||||
#define R_SMART 0x7 // Smartreader+
|
||||
#define R_PCSC 0x8 // PCSC
|
||||
#define R_DRECAS 0x9 // Reader DRECAS
|
||||
#define R_EMU 0x17 // Reader EMU
|
||||
/////// proxy readers after R_CS378X
|
||||
#define R_CAMD35 0x20 // Reader cascading camd 3.5x
|
||||
#define R_CAMD33 0x21 // Reader cascading camd 3.3x
|
||||
|
|
@ -890,6 +900,13 @@ typedef struct s_entitlement // contains entitlement Info
|
|||
uint32_t class; // the class needed for some systems
|
||||
time_t start; // startdate
|
||||
time_t end; // enddate
|
||||
#ifdef WITH_EMU
|
||||
bool isKey;
|
||||
bool isData;
|
||||
char name[8];
|
||||
uint8_t *key;
|
||||
uint32_t keyLength;
|
||||
#endif
|
||||
} S_ENTITLEMENT;
|
||||
|
||||
struct s_client;
|
||||
|
|
@ -899,7 +916,7 @@ struct cmd_packet_t;
|
|||
struct s_ecm_answer;
|
||||
struct demux_s;
|
||||
|
||||
#define DEFAULT_MODULE_BUFSIZE 1024
|
||||
#define DEFAULT_MODULE_BUFsize 1024
|
||||
|
||||
struct s_module
|
||||
{
|
||||
|
|
@ -1024,6 +1041,25 @@ typedef struct cw_extendted_t
|
|||
#endif
|
||||
} EXTENDED_CW;
|
||||
|
||||
typedef struct s_cw_vote {
|
||||
uint8_t cw[16];
|
||||
uint8_t votes;
|
||||
uint8_t local_votes;
|
||||
struct s_reader *voters[MAX_VOTE_CANDIDATES];
|
||||
} s_cw_vote;
|
||||
|
||||
// CW Vote settings
|
||||
typedef struct s_cw_vote_caid_data
|
||||
{
|
||||
uint16_t caid;
|
||||
} CW_VOTE_CAID_DATA;
|
||||
|
||||
typedef struct s_cw_vote_caid_tab
|
||||
{
|
||||
int32_t cvcnum;
|
||||
CW_VOTE_CAID_DATA *cvcdata;
|
||||
} CW_VOTE_CAID_TAB;
|
||||
|
||||
typedef struct ecm_request_t
|
||||
{
|
||||
uint8_t ecm[MAX_ECM_SIZE];
|
||||
|
|
@ -1117,9 +1153,10 @@ typedef struct ecm_request_t
|
|||
#ifdef HAVE_DVBAPI
|
||||
uint8_t adapter_index;
|
||||
#endif
|
||||
uint32_t vote_pool_session; // sesja dla vote_pool - zapobiega wielokrotnemu czyszczeniu
|
||||
struct s_cw_vote vote_pool[MAX_VOTE_CANDIDATES];
|
||||
} ECM_REQUEST;
|
||||
|
||||
|
||||
struct s_ecm_answer
|
||||
{
|
||||
uint8_t status;
|
||||
|
|
@ -1506,12 +1543,9 @@ typedef struct ce_csp_t
|
|||
int8_t maxhop;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
int8_t maxhop_lg;
|
||||
#endif
|
||||
CAIDVALUETAB maxhop_percaid;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
CAIDVALUETAB maxhop_lg_percaid;
|
||||
#endif
|
||||
CECSPVALUETAB filter_caidtab;
|
||||
CAIDVALUETAB cacheex_nopushafter_tab;
|
||||
uint8_t allow_request;
|
||||
uint8_t allow_reforward;
|
||||
uint8_t drop_csp;
|
||||
|
|
@ -1531,7 +1565,6 @@ typedef struct ce_csp_t
|
|||
uint8_t lg_only_in_aio_only;
|
||||
uint8_t lg_only_remote_settings;
|
||||
int32_t feature_bitfield;
|
||||
CAIDVALUETAB cacheex_nopushafter_tab;
|
||||
char aio_version[CS_AIO_VERSION_LEN];
|
||||
#endif
|
||||
} CECSP;
|
||||
|
|
@ -1687,7 +1720,7 @@ struct s_reader // contains device info, reader info and card info
|
|||
#endif
|
||||
int32_t r_port;
|
||||
char r_usr[64];
|
||||
char r_pwd[64];
|
||||
char r_pwd[64]; // Max length 63 + null terminator
|
||||
int32_t l_port;
|
||||
CAIDTAB ctab;
|
||||
uint32_t boxid;
|
||||
|
|
@ -1796,6 +1829,7 @@ struct s_reader // contains device info, reader info and card info
|
|||
char cc_build[7]; // cccam build number
|
||||
int8_t cc_maxhops; // cccam max distance
|
||||
int8_t cc_mindown; // cccam min downhops
|
||||
int8_t cc_want_emu; // Schlocke: Client want to have EMUs, 0 - NO; 1 - YES
|
||||
uint32_t cc_id;
|
||||
int8_t cc_keepalive;
|
||||
int8_t cc_hop; // For non-cccam reader: hop for virtual cards
|
||||
|
|
@ -1992,6 +2026,11 @@ struct s_reader // contains device info, reader info and card info
|
|||
#ifdef MODULE_GHTTP
|
||||
uint8_t ghttp_use_ssl;
|
||||
#endif
|
||||
#ifdef WITH_EMU
|
||||
FTAB emu_auproviders; // AU providers for Emu reader
|
||||
int8_t emu_datecodedenabled; // date-coded keys for BISS
|
||||
LLIST *ll_biss2_rsa_keys; // BISS2 RSA keys - Read from external PEM files
|
||||
#endif
|
||||
#ifdef READER_CONAX
|
||||
uint8_t cnxlastecm; // == 0 - last ecm has not been paired ecm, > 0 last ecm has been paired ecm
|
||||
#endif
|
||||
|
|
@ -2101,8 +2140,6 @@ struct s_auth
|
|||
int32_t cwcacheexerr; // cw=00 or chksum wrong
|
||||
int32_t cwcacheexerrcw; // Same Hex, different CW
|
||||
int32_t cwc_info; // count of in/out comming cacheex ecms with CWCinfo
|
||||
uint8_t cxnodeid_last[9]; // save previous nodeid
|
||||
uint8_t cxnodeid_changer_detected; // save node-changer detection status
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
int32_t cwcacheexgotlg; // count got localgenerated-flagged CWs
|
||||
int32_t cwcacheexpushlg; // count pushed localgenerated-flagged CWs
|
||||
|
|
@ -2229,7 +2266,7 @@ struct s_config
|
|||
uint32_t ctimeout;
|
||||
uint32_t ftimeout;
|
||||
CAIDVALUETAB ftimeouttab;
|
||||
CAIDVALUETAB ctimeouttab; // ‹ clienttimeout_percaid
|
||||
CAIDVALUETAB ctimeouttab; // ← clienttimeout_percaid
|
||||
uint32_t cmaxidle;
|
||||
int32_t ulparent;
|
||||
uint32_t delay;
|
||||
|
|
@ -2441,6 +2478,17 @@ struct s_config
|
|||
int8_t double_check; // schlocke: Double checks each ecm+dcw from two (or more) readers
|
||||
FTAB double_check_caid; // do not store loadbalancer stats with providers for this caid
|
||||
|
||||
// CW Vote settings
|
||||
int8_t cwvote_enabled;
|
||||
int8_t cwvote_log_enabled;
|
||||
int32_t cwvote_timeout; // w milisekundach, 0 = wyłączony
|
||||
int32_t cwvote_min_votes;
|
||||
float cwvote_local_weight; // waga local readera (np. 2.0)
|
||||
int32_t cwvote_max_candidates;
|
||||
int32_t cwvote_compare_len; // 8 lub 16
|
||||
int32_t cwvote_fallback; // 0=wait, 1=best anyway, 2=first CW
|
||||
CW_VOTE_CAID_TAB cwvote_caids;
|
||||
|
||||
#ifdef HAVE_DVBAPI
|
||||
int8_t dvbapi_enabled;
|
||||
int8_t dvbapi_au;
|
||||
|
|
@ -2530,6 +2578,10 @@ struct s_config
|
|||
#else
|
||||
#define DEFAULT_STREAM_SOURCE_PORT 8001 //Enigma2
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WITH_EMU
|
||||
uint32_t emu_stream_ecm_delay;
|
||||
int8_t emu_stream_emm_enabled;
|
||||
#endif
|
||||
|
||||
int32_t max_cache_time; // seconds ecms are stored in ecmcwcache
|
||||
|
|
@ -2564,35 +2616,35 @@ struct s_config
|
|||
CECSPVALUETAB cacheex_wait_timetab;
|
||||
CAIDVALUETAB cacheex_mode1_delay_tab;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
CAIDVALUETAB cacheex_nopushafter_tab;
|
||||
uint8_t waittime_block_start;
|
||||
uint16_t waittime_block_time;
|
||||
#endif
|
||||
CECSP csp; // CSP Settings
|
||||
uint8_t cacheex_enable_stats; // enable stats
|
||||
|
||||
uint8_t cacheex_global_maxhop;
|
||||
uint8_t cacheex_maxhop;
|
||||
uint8_t cacheex_maxhop_lg;
|
||||
|
||||
CAIDVALUETAB cacheex_maxhop_percaid;
|
||||
CAIDVALUETAB cacheex_maxhop_lg_percaid;
|
||||
|
||||
struct s_cacheex_matcher *cacheex_matcher;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
uint8_t cacheex_dropdiffs;
|
||||
uint8_t cacheex_lg_only_remote_settings;
|
||||
uint8_t cacheex_localgenerated_only;
|
||||
CAIDTAB cacheex_localgenerated_only_caidtab;
|
||||
FTAB cacheex_lg_only_tab;
|
||||
FTAB lg_only_tab;
|
||||
uint8_t localgenerated_only_in;
|
||||
CAIDTAB localgenerated_only_in_caidtab;
|
||||
FTAB lg_only_in_tab;
|
||||
uint8_t lg_only_in_aio_only;
|
||||
uint8_t lg_only_remote_settings;
|
||||
int32_t feature_bitfield;
|
||||
char aio_version[CS_AIO_VERSION_LEN];
|
||||
#endif
|
||||
CAIDVALUETAB cacheex_nopushafter_tab;
|
||||
uint8_t cacheex_localgenerated_only_in;
|
||||
CAIDTAB cacheex_localgenerated_only_in_caidtab;
|
||||
FTAB cacheex_lg_only_in_tab;
|
||||
uint8_t cacheex_lg_only_in_aio_only;
|
||||
CECSPVALUETAB cacheex_filter_caidtab;
|
||||
CECSPVALUETAB cacheex_filter_caidtab_aio;
|
||||
uint64_t cacheex_push_lg_groups;
|
||||
#endif
|
||||
uint32_t cacheex_push_lg_groups;
|
||||
FTAB cacheex_lg_only_tab;
|
||||
#endif
|
||||
|
||||
#ifdef CW_CYCLE_CHECK
|
||||
|
|
@ -2723,12 +2775,18 @@ bool boxtype_is(const char *boxtype);
|
|||
bool boxname_is(const char *boxname);
|
||||
const char *boxtype_get(void);
|
||||
const char *boxname_get(void);
|
||||
static inline bool caid_is_fake(uint16_t caid) { return caid == 0xffff; }
|
||||
static inline bool caid_is_biss(uint16_t caid) { return caid >> 8 == 0x26; }
|
||||
static inline bool caid_is_biss_fixed(uint16_t caid) { return caid == 0x2600 || caid == 0x2602; } // fixed cw, fake ecm
|
||||
static inline bool caid_is_biss_dynamic(uint16_t caid) { return caid == 0x2610; } // dynamic cw, real ecm and emm
|
||||
static inline bool caid_is_seca(uint16_t caid) { return caid >> 8 == 0x01; }
|
||||
static inline bool caid_is_viaccess(uint16_t caid) { return caid >> 8 == 0x05; }
|
||||
static inline bool caid_is_irdeto(uint16_t caid) { return caid >> 8 == 0x06; }
|
||||
static inline bool caid_is_videoguard(uint16_t caid) { return caid >> 8 == 0x09; }
|
||||
static inline bool caid_is_conax(uint16_t caid) { return caid >> 8 == 0x0B; }
|
||||
static inline bool caid_is_cryptoworks(uint16_t caid) { return caid >> 8 == 0x0D; }
|
||||
static inline bool caid_is_powervu(uint16_t caid) { return caid >> 8 == 0x0E; }
|
||||
static inline bool caid_is_director(uint16_t caid) { return caid >> 8 == 0x10; }
|
||||
static inline bool caid_is_betacrypt(uint16_t caid) { return caid >> 8 == 0x17; }
|
||||
static inline bool caid_is_nagra(uint16_t caid) { return caid >> 8 == 0x18; }
|
||||
static inline bool caid_is_bulcrypt(uint16_t caid) { return caid == 0x5581 || caid == 0x4AEE; }
|
||||
|
|
@ -2746,4 +2804,8 @@ static inline uint8_t get_ecm_mode(const ECM_REQUEST *er) {
|
|||
#endif
|
||||
const char *get_cardsystem_desc_by_caid(uint16_t caid);
|
||||
|
||||
#ifdef WITH_EMU
|
||||
FILTER *get_emu_prids_for_caid(struct s_reader *rdr, uint16_t caid);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
BIN
images/image1.jpg
Executable file
BIN
images/image1.jpg
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
146
module-cacheex.c
146
module-cacheex.c
|
|
@ -34,44 +34,6 @@ extern struct ecm_request_t *ecm_pushed_deleted;
|
|||
extern CS_MUTEX_LOCK ecmcache_lock;
|
||||
extern struct ecm_request_t *ecmcwcache;
|
||||
|
||||
/* 48/64 learning structure (defined in oscam-ecm.c) */
|
||||
#define CW_DETECT_MAX 8192
|
||||
#define CW_LEARN_THRESHOLD 5
|
||||
#define CW_CACHEEX_NODEID_MAX 5
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t caid;
|
||||
uint32_t prid;
|
||||
uint16_t srvid;
|
||||
|
||||
uint8_t is64;
|
||||
uint8_t is48;
|
||||
|
||||
uint8_t learn64_count;
|
||||
uint8_t learn48_count;
|
||||
|
||||
uint8_t strict_logged;
|
||||
|
||||
/* CacheEX learning: track unique nodeid (64-bit) */
|
||||
uint64_t cacheex_nodeid_48[CW_CACHEEX_NODEID_MAX];
|
||||
uint8_t cacheex_nodeid_48_count;
|
||||
|
||||
uint64_t cacheex_nodeid_64[CW_CACHEEX_NODEID_MAX];
|
||||
uint8_t cacheex_nodeid_64_count;
|
||||
|
||||
/* finalized flags (no more learning) */
|
||||
uint8_t cacheex_48_finalized;
|
||||
uint8_t cacheex_64_finalized;
|
||||
|
||||
} cw_detect_entry;
|
||||
|
||||
/* Forward declares for 48/64 learning (from oscam-ecm.c) */
|
||||
typedef struct ecm_request_t ECM_REQUEST;
|
||||
extern cw_detect_entry *cw_detect_get(ECM_REQUEST *er);
|
||||
extern int cw_checksum_ok(uint8_t *cw);
|
||||
extern int cacheex_learn_add_nodeid(uint64_t *nodeid_list, uint8_t *count, uint64_t new_nodeid);
|
||||
|
||||
// HIT CACHE functions **************************************************************
|
||||
|
||||
typedef struct hit_key_t {
|
||||
|
|
@ -560,7 +522,7 @@ int8_t cacheex_maxhop(struct s_client *cl, ECM_REQUEST *er)
|
|||
|
||||
return default_maxhop;
|
||||
}
|
||||
// GLOBAL override OFF <EFBFBD> reader only
|
||||
// GLOBAL override OFF → reader only
|
||||
else
|
||||
{
|
||||
// Priority 1: reader percaid
|
||||
|
|
@ -584,11 +546,10 @@ int8_t cacheex_maxhop(struct s_client *cl, ECM_REQUEST *er)
|
|||
}
|
||||
}
|
||||
|
||||
// NOT a cacheex reader <EFBFBD> default
|
||||
// NOT a cacheex reader → default
|
||||
return default_maxhop;
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
int8_t cacheex_maxhop_lg(struct s_client *cl, ECM_REQUEST *er)
|
||||
|
|
@ -621,7 +582,7 @@ int8_t cacheex_maxhop_lg(struct s_client *cl, ECM_REQUEST *er)
|
|||
|
||||
return default_maxhop;
|
||||
}
|
||||
// GLOBAL override OFF <EFBFBD> reader only
|
||||
// GLOBAL override OFF → reader only
|
||||
else
|
||||
{
|
||||
// Priority 1: reader percaid LG
|
||||
|
|
@ -1055,7 +1016,9 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in
|
|||
}
|
||||
#endif
|
||||
|
||||
if(chk_is_null_CW(er->cw))
|
||||
// Skip check for BISS1 - cw could be indeed zero
|
||||
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
|
||||
if(chk_is_null_CW(er->cw) && !caid_is_biss(er->caid))
|
||||
{
|
||||
cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received null cw from %s", csp ? "csp" : username(cl));
|
||||
cl->cwcacheexerr++;
|
||||
|
|
@ -1064,7 +1027,9 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in
|
|||
return 0;
|
||||
}
|
||||
|
||||
if(get_odd_even(er) == 0)
|
||||
// Don't check for BISS1 and BISS2 mode 1/E or fake caid (ECM is fake for them)
|
||||
// Don't check for BISS2 mode CA (ECM table is always 0x80)
|
||||
if(!caid_is_biss(er->caid) && !caid_is_fake(er->caid) && get_odd_even(er) == 0)
|
||||
{
|
||||
cs_log_dbg(D_CACHEEX, "push received ecm with null odd/even byte from %s", csp ? "csp" : username(cl));
|
||||
cl->cwcacheexerr++;
|
||||
|
|
@ -1100,99 +1065,6 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in
|
|||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// CACHEEX 48/64 LEARNING (reuse oscam-ecm.c structures)
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
if (!csp && !er->entropy_blocked)
|
||||
{
|
||||
/* Get learning entry from shared cw_detect_table */
|
||||
extern cw_detect_entry cw_detect_table[];
|
||||
(void)cw_detect_table; /* Table is accessed via cw_detect_get */
|
||||
cw_detect_entry *e = cw_detect_get(er);
|
||||
uint8_t checksum_ok = cw_checksum_ok(er->cw);
|
||||
|
||||
/* Get CacheEX source nodeid */
|
||||
uint64_t src_nodeid = 0;
|
||||
if (er->csp_lastnodes && ll_count(er->csp_lastnodes) > 0)
|
||||
{
|
||||
uint8_t remotenodeid[8];
|
||||
cacheex_get_srcnodeid(er, remotenodeid);
|
||||
memcpy(&src_nodeid, remotenodeid, 8);
|
||||
}
|
||||
|
||||
uint8_t whitelist = chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for) ||
|
||||
(cl->account && chk_if_ignore_checksum(er, &cl->account->disablecrccacheex_only_for));
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// CACHEEX LEARNING: ONLY FOR WHITELIST CAIDs IN UNKNOWN STATE
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
if (whitelist && src_nodeid)
|
||||
{
|
||||
/* Learn 64bit (invalid checksum) */
|
||||
if (!checksum_ok && !e->cacheex_64_finalized)
|
||||
{
|
||||
int finalized = cacheex_learn_add_nodeid(
|
||||
e->cacheex_nodeid_64,
|
||||
&e->cacheex_nodeid_64_count,
|
||||
src_nodeid
|
||||
);
|
||||
|
||||
if (finalized)
|
||||
{
|
||||
e->cacheex_64_finalized = 1;
|
||||
e->is64 = 1;
|
||||
cs_log("CACHEEX FINALIZED 64bit after %d unique nodes caid=%04X srvid=%04X",
|
||||
CW_CACHEEX_NODEID_MAX, er->caid, er->srvid);
|
||||
}
|
||||
else
|
||||
{
|
||||
cs_log_dbg(D_CACHEEX, "CACHEEX LEARN 64bit (%d/%d nodes) caid=%04X srvid=%04X src_nodeid=%" PRIu64 "X",
|
||||
e->cacheex_nodeid_64_count, CW_CACHEEX_NODEID_MAX, er->caid, er->srvid, src_nodeid);
|
||||
}
|
||||
}
|
||||
|
||||
/* Learn 48bit (valid checksum) */
|
||||
if (checksum_ok && !e->cacheex_48_finalized)
|
||||
{
|
||||
int finalized = cacheex_learn_add_nodeid(
|
||||
e->cacheex_nodeid_48,
|
||||
&e->cacheex_nodeid_48_count,
|
||||
src_nodeid
|
||||
);
|
||||
|
||||
if (finalized)
|
||||
{
|
||||
e->cacheex_48_finalized = 1;
|
||||
e->is48 = 1;
|
||||
cs_log("CACHEEX FINALIZED 48bit after %d unique nodes caid=%04X srvid=%04X",
|
||||
CW_CACHEEX_NODEID_MAX, er->caid, er->srvid);
|
||||
}
|
||||
else
|
||||
{
|
||||
cs_log_dbg(D_CACHEEX, "CACHEEX LEARN 48bit (%d/%d nodes) caid=%04X srvid=%04X src_nodeid=%" PRIu64 "X",
|
||||
e->cacheex_nodeid_48_count, CW_CACHEEX_NODEID_MAX, er->caid, er->srvid, src_nodeid);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!whitelist && checksum_ok)
|
||||
{
|
||||
/* STRICT MODE: only 48bit allowed, no learning */
|
||||
cs_log_dbg(D_CACHEEX, "CACHEEX STRICT 48bit (CAID outside whitelist) caid=%04X srvid=%04X",
|
||||
er->caid, er->srvid);
|
||||
}
|
||||
else if (!whitelist && !checksum_ok)
|
||||
{
|
||||
/* STRICT MODE: reject 64bit, no learning */
|
||||
cs_log_dbg(D_CACHEEX, "CACHEEX STRICT REJECT 64bit (CAID outside whitelist) caid=%04X srvid=%04X",
|
||||
er->caid, er->srvid);
|
||||
cl->cwcacheexerr++;
|
||||
if (cl->account) cl->account->cwcacheexerr++;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
er->grp |= cl->grp; // ok for mode2 reader too: cl->reader->grp
|
||||
er->rc = E_CACHEEX;
|
||||
er->cacheex_src = cl;
|
||||
|
|
|
|||
1700
module-cacheex.c.bak
1700
module-cacheex.c.bak
File diff suppressed because it is too large
Load Diff
|
|
@ -1,78 +0,0 @@
|
|||
#ifndef MODULE_CACHEEX_H_
|
||||
#define MODULE_CACHEEX_H_
|
||||
|
||||
static inline uint64_t cacheex_node_id(void *var)
|
||||
{
|
||||
uint64_t *x = var;
|
||||
return *x;
|
||||
}
|
||||
|
||||
uint32_t get_cacheex_wait_time(ECM_REQUEST *er, struct s_client *cl);
|
||||
CWCHECK get_cwcheck(ECM_REQUEST *er);
|
||||
uint16_t get_cacheex_mode1_delay(ECM_REQUEST *er);
|
||||
int32_t chk_csp_ctab(ECM_REQUEST *er, CECSPVALUETAB *tab);
|
||||
uint8_t check_cacheex_filter(struct s_client *cl, ECM_REQUEST *er);
|
||||
void cacheex_add_to_cache(struct s_client *cl, ECM_REQUEST *er);
|
||||
void cacheex_add_to_cache_from_csp(struct s_client *cl, ECM_REQUEST *er);
|
||||
void cacheex_cache_push(ECM_REQUEST *er);
|
||||
int32_t cacheex_add_stats(struct s_client *cl, uint16_t caid, uint16_t srvid, uint32_t prid, uint8_t direction
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
, uint8_t localgenerated
|
||||
#endif
|
||||
);
|
||||
int8_t cacheex_maxhop(struct s_client *cl);
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
int8_t cacheex_maxhop_lg(struct s_client *cl);
|
||||
#endif
|
||||
|
||||
#ifdef CS_CACHEEX
|
||||
extern void cacheex_init(void);
|
||||
extern void cacheex_clear_account_stats(struct s_auth *account);
|
||||
extern void cacheex_clear_client_stats(struct s_client *client);
|
||||
extern void cacheex_load_config_file(void);
|
||||
static inline bool cacheex_reader(struct s_reader *rdr) { return rdr ? (rdr->cacheex.mode == 1 ? 1 : 0) : 0; }
|
||||
extern bool cacheex_is_match_alias(struct s_client *cl, ECM_REQUEST *er);
|
||||
void cacheex_set_csp_lastnode(ECM_REQUEST *er);
|
||||
void cacheex_set_cacheex_src(ECM_REQUEST *ecm, struct s_client *cl);
|
||||
void cacheex_init_cacheex_src(ECM_REQUEST *ecm, ECM_REQUEST *er);
|
||||
void cacheex_free_csp_lastnodes(ECM_REQUEST *er);
|
||||
void checkcache_process_thread_start(void);
|
||||
void cacheex_push_out(struct s_client *cl, ECM_REQUEST *er);
|
||||
bool cacheex_check_queue_length(struct s_client *cl);
|
||||
static inline int8_t cacheex_get_rdr_mode(struct s_reader *reader) { return reader ? reader->cacheex.mode : 0; }
|
||||
void cacheex_init_hitcache(void);
|
||||
void cacheex_free_hitcache(void);
|
||||
void cacheex_cleanup_hitcache(bool force);
|
||||
void cacheex_update_hash(ECM_REQUEST *er);
|
||||
void cacheex_mode1_delay(ECM_REQUEST *er);
|
||||
void cacheex_timeout(ECM_REQUEST *er);
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
char* cxaio_ftab_to_buf(FTAB *lg_only_ftab);
|
||||
FTAB caidtab2ftab(CAIDTAB *ctab);
|
||||
void caidtab2ftab_add(CAIDTAB *lgonly_ctab, FTAB *lgonly_tab);
|
||||
#define CACHEEX_FEATURES 127
|
||||
#endif
|
||||
#else
|
||||
static inline void cacheex_init(void) { }
|
||||
static inline void cacheex_clear_account_stats(struct s_auth *UNUSED(account)) { }
|
||||
static inline void cacheex_clear_client_stats(struct s_client *UNUSED(client)) { }
|
||||
static inline void cacheex_load_config_file(void) { }
|
||||
static inline bool cacheex_reader(struct s_reader *UNUSED(rdr)) { return false; }
|
||||
static inline bool cacheex_is_match_alias(struct s_client *UNUSED(cl), ECM_REQUEST *UNUSED(er)) { return false; }
|
||||
static inline void cacheex_set_csp_lastnode(ECM_REQUEST *UNUSED(er)) { }
|
||||
static inline void cacheex_free_csp_lastnodes(ECM_REQUEST *UNUSED(er)) { }
|
||||
static inline void cacheex_set_cacheex_src(ECM_REQUEST *UNUSED(ecm), struct s_client *UNUSED(cl)) { }
|
||||
static inline void cacheex_init_cacheex_src(ECM_REQUEST *UNUSED(ecm), ECM_REQUEST *UNUSED(er)) { }
|
||||
static inline void checkcache_process_thread_start(void) { }
|
||||
static inline void cacheex_push_out(struct s_client *UNUSED(cl), ECM_REQUEST *UNUSED(er)) { }
|
||||
static inline bool cacheex_check_queue_length(struct s_client *UNUSED(cl)) { return 0; }
|
||||
static inline int8_t cacheex_get_rdr_mode(struct s_reader *UNUSED(reader)) { return 0; }
|
||||
static inline void cacheex_init_hitcache(void) { }
|
||||
static inline void cacheex_free_hitcache(void) { }
|
||||
static inline void cacheex_cleanup_hitcache(bool UNUSED(force)) { }
|
||||
static inline void cacheex_update_hash(ECM_REQUEST *UNUSED(er)) { }
|
||||
static inline void cacheex_mode1_delay(ECM_REQUEST *UNUSED(er)) { }
|
||||
static inline void cacheex_timeout(ECM_REQUEST *UNUSED(er)) { }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -783,7 +783,7 @@ void camd35_cacheex_feature_trigger(struct s_client *cl, int32_t feature, uint8_
|
|||
uint8_t *ofs = buf + 20;
|
||||
memcpy(ofs, payload, size - 20);
|
||||
|
||||
camd35_send_without_timeout(cl, buf, size-20); //send adds +20
|
||||
camd35_send_without_timeout(cl, buf, size-20); // send adds +20
|
||||
}
|
||||
|
||||
void camd35_cacheex_feature_request_save(struct s_client *cl, uint8_t *buf)
|
||||
|
|
@ -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);
|
||||
|
|
@ -925,7 +925,7 @@ void camd35_cacheex_feature_request(struct s_client *cl)
|
|||
|
||||
i2b_buf(2, CACHEEX_FEATURES, buf + i); // set feature-list here
|
||||
|
||||
camd35_send_without_timeout(cl, buf, 12); //send adds +20
|
||||
camd35_send_without_timeout(cl, buf, 12); // send adds +20
|
||||
}
|
||||
|
||||
void camd35_cacheex_feature_request_reply(struct s_client *cl, uint8_t *buf)
|
||||
|
|
@ -941,7 +941,7 @@ void camd35_cacheex_feature_request_reply(struct s_client *cl, uint8_t *buf)
|
|||
|
||||
i2b_buf(2, CACHEEX_FEATURES, rbuf + i);
|
||||
|
||||
camd35_send_without_timeout(cl, rbuf, 12); //send adds +20
|
||||
camd35_send_without_timeout(cl, rbuf, 12); // send adds +20
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -954,13 +954,13 @@ void camd35_cacheex_send_push_filter(struct s_client *cl, uint8_t mode)
|
|||
struct s_reader *rdr = cl->reader;
|
||||
int i = 20, j;
|
||||
CECSPVALUETAB *filter;
|
||||
//maximum size: 20+255
|
||||
// maximum size: 20+255
|
||||
uint8_t buf[20+242];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = 0x3c;
|
||||
buf[1] = 0xf2;
|
||||
|
||||
//mode==2 send filters from rdr
|
||||
// mode==2 send filters from rdr
|
||||
if(mode == 2 && rdr)
|
||||
{
|
||||
filter = &rdr->cacheex.filter_caidtab;
|
||||
|
|
@ -970,7 +970,7 @@ void camd35_cacheex_send_push_filter(struct s_client *cl, uint8_t mode)
|
|||
filter = &cfg.cacheex_filter_caidtab;
|
||||
#endif
|
||||
}
|
||||
//mode==3 send filters from acc
|
||||
// mode==3 send filters from acc
|
||||
else if(mode == 3 && cl->typ == 'c' && cl->account)
|
||||
{
|
||||
filter = &cl->account->cacheex.filter_caidtab;
|
||||
|
|
@ -1025,7 +1025,7 @@ void camd35_cacheex_send_push_filter(struct s_client *cl, uint8_t mode)
|
|||
}
|
||||
|
||||
cs_log_dbg(D_CACHEEX, "cacheex: sending push filter request to %s", username(cl));
|
||||
camd35_send_without_timeout(cl, buf, 242); //send adds +20
|
||||
camd35_send_without_timeout(cl, buf, 242); // send adds +20
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1038,13 +1038,13 @@ static void camd35_cacheex_push_filter(struct s_client *cl, uint8_t *buf, uint8_
|
|||
int32_t caid, cmask, provid, srvid;
|
||||
CECSPVALUETAB *filter;
|
||||
|
||||
//mode==2 write filters to acc
|
||||
// mode==2 write filters to acc
|
||||
if(mode == 2 && cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 2
|
||||
&& cl->account->cacheex.allow_filter == 1)
|
||||
{
|
||||
filter = &cl->account->cacheex.filter_caidtab;
|
||||
}
|
||||
//mode==3 write filters to rdr
|
||||
// mode==3 write filters to rdr
|
||||
else if(mode == 3 && rdr && rdr->cacheex.allow_filter == 1)
|
||||
{
|
||||
filter = &rdr->cacheex.filter_caidtab;
|
||||
|
|
@ -1150,7 +1150,7 @@ static int32_t camd35_cacheex_push_chk(struct s_client *cl, ECM_REQUEST *er)
|
|||
}
|
||||
ll_li_destroy(li);
|
||||
|
||||
//node found, so we got it from there, do not push:
|
||||
// node found, so we got it from there, do not push:
|
||||
if(node)
|
||||
{
|
||||
cs_log_dbg(D_CACHEEX,
|
||||
|
|
@ -1158,7 +1158,7 @@ static int32_t camd35_cacheex_push_chk(struct s_client *cl, ECM_REQUEST *er)
|
|||
return 0;
|
||||
}
|
||||
|
||||
//check if cw is already pushed
|
||||
// check if cw is already pushed
|
||||
if(check_is_pushed(er->cw_cache, cl))
|
||||
{ return 0; }
|
||||
|
||||
|
|
@ -1170,10 +1170,10 @@ static int32_t camd35_cacheex_push_chk(struct s_client *cl, ECM_REQUEST *er)
|
|||
static int32_t camd35_cacheex_push_out(struct s_client *cl, struct ecm_request_t *er)
|
||||
{
|
||||
int8_t rc = (er->rc < E_NOTFOUND) ? E_FOUND : er->rc;
|
||||
if(rc != E_FOUND && rc != E_UNHANDLED) { return -1; } //Maybe later we could support other rcs
|
||||
if(rc != E_FOUND && rc != E_UNHANDLED) { return -1; } // Maybe later we could support other rcs
|
||||
|
||||
//E_FOUND : we have the CW,
|
||||
//E_UNHANDLED : incoming ECM request
|
||||
// E_FOUND : we have the CW,
|
||||
// E_UNHANDLED : incoming ECM request
|
||||
|
||||
if(cl->reader)
|
||||
{
|
||||
|
|
@ -1191,10 +1191,10 @@ static int32_t camd35_cacheex_push_out(struct s_client *cl, struct ecm_request_t
|
|||
(ll_count(er->csp_lastnodes) + 1) * 8;
|
||||
#endif
|
||||
uint8_t *buf;
|
||||
if(!cs_malloc(&buf, size + 20)) //camd35_send() adds +20
|
||||
if(!cs_malloc(&buf, size + 20)) // camd35_send() adds +20
|
||||
{ return -1; }
|
||||
|
||||
buf[0] = 0x3f; //New Command: Cache-push
|
||||
buf[0] = 0x3f; // New Command: Cache-push
|
||||
buf[1] = size & 0xff;
|
||||
buf[2] = size >> 8;
|
||||
buf[3] = rc;
|
||||
|
|
@ -1202,7 +1202,7 @@ static int32_t camd35_cacheex_push_out(struct s_client *cl, struct ecm_request_t
|
|||
i2b_buf(2, er->srvid, buf + 8);
|
||||
i2b_buf(2, er->caid, buf + 10);
|
||||
i2b_buf(4, er->prid, buf + 12);
|
||||
//i2b_buf(2, er->idx, buf + 16); // Not relevant...?
|
||||
// i2b_buf(2, er->idx, buf + 16); // Not relevant...?
|
||||
|
||||
if(er->cwc_cycletime && er->cwc_next_cw_cycle < 2)
|
||||
{
|
||||
|
|
@ -1222,27 +1222,27 @@ static int32_t camd35_cacheex_push_out(struct s_client *cl, struct ecm_request_t
|
|||
|
||||
uint8_t *ofs = buf + 20;
|
||||
|
||||
//write oscam ecmd5:
|
||||
memcpy(ofs, er->ecmd5, sizeof(er->ecmd5)); //16
|
||||
// write oscam ecmd5:
|
||||
memcpy(ofs, er->ecmd5, sizeof(er->ecmd5)); // 16
|
||||
ofs += sizeof(er->ecmd5);
|
||||
|
||||
//write csp hashcode:
|
||||
// write csp hashcode:
|
||||
i2b_buf(4, CSP_HASH_SWAP(er->csp_hash), ofs);
|
||||
ofs += 4;
|
||||
|
||||
//write cw:
|
||||
memcpy(ofs, er->cw, sizeof(er->cw)); //16
|
||||
// write cw:
|
||||
memcpy(ofs, er->cw, sizeof(er->cw)); // 16
|
||||
ofs += sizeof(er->cw);
|
||||
|
||||
//write node count:
|
||||
// write node count:
|
||||
*ofs = ll_count(er->csp_lastnodes) + 1;
|
||||
ofs++;
|
||||
|
||||
//write own node:
|
||||
// write own node:
|
||||
memcpy(ofs, camd35_node_id, 8);
|
||||
ofs += 8;
|
||||
|
||||
//write other nodes:
|
||||
// write other nodes:
|
||||
LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0);
|
||||
uint8_t *node;
|
||||
while((node = ll_li_next(li)))
|
||||
|
|
@ -1271,7 +1271,7 @@ static int32_t camd35_cacheex_push_out(struct s_client *cl, struct ecm_request_t
|
|||
static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf)
|
||||
{
|
||||
int8_t rc = buf[3];
|
||||
if(rc != E_FOUND && rc != E_UNHANDLED) //Maybe later we could support other rcs
|
||||
if(rc != E_FOUND && rc != E_UNHANDLED) // Maybe later we could support other rcs
|
||||
{ return; }
|
||||
|
||||
ECM_REQUEST *er;
|
||||
|
|
@ -1289,7 +1289,7 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf)
|
|||
er->caid = b2i(2, buf + 10);
|
||||
er->prid = b2i(4, buf + 12);
|
||||
er->pid = b2i(2, buf + 16);
|
||||
er->ecm[0] = buf[19]!=0x80 && buf[19]!=0x81 ? 0 : buf[19]; //odd/even byte, usefull to send it over CSP and to check cw for swapping
|
||||
er->ecm[0] = buf[19]!=0x80 && buf[19]!=0x81 ? 0 : buf[19]; // odd/even byte, usefull to send it over CSP and to check cw for swapping
|
||||
er->rc = rc;
|
||||
|
||||
er->ecmlen = 0;
|
||||
|
|
@ -1310,8 +1310,8 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf)
|
|||
|
||||
uint8_t *ofs = buf + 20;
|
||||
|
||||
//Read ecmd5
|
||||
memcpy(er->ecmd5, ofs, sizeof(er->ecmd5)); //16
|
||||
// Read ecmd5
|
||||
memcpy(er->ecmd5, ofs, sizeof(er->ecmd5)); // 16
|
||||
ofs += sizeof(er->ecmd5);
|
||||
|
||||
if(!check_cacheex_filter(cl, er))
|
||||
|
|
@ -1352,25 +1352,25 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf)
|
|||
}
|
||||
#endif
|
||||
|
||||
//Read csp_hash:
|
||||
// Read csp_hash:
|
||||
er->csp_hash = CSP_HASH_SWAP(b2i(4, ofs));
|
||||
ofs += 4;
|
||||
|
||||
//Read cw:
|
||||
memcpy(er->cw, ofs, sizeof(er->cw)); //16
|
||||
// Read cw:
|
||||
memcpy(er->cw, ofs, sizeof(er->cw)); // 16
|
||||
ofs += sizeof(er->cw);
|
||||
|
||||
//Check auf neues Format:
|
||||
// Check auf neues Format:
|
||||
uint8_t *data;
|
||||
if(size > (sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw)))
|
||||
{
|
||||
|
||||
//Read lastnodes:
|
||||
// Read lastnodes:
|
||||
uint8_t count = *ofs;
|
||||
ofs++;
|
||||
|
||||
#ifndef CS_CACHEEX_AIO
|
||||
//check max nodes:
|
||||
// check max nodes:
|
||||
if(count > cacheex_maxhop(cl, er))
|
||||
{
|
||||
cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", (int32_t)count, cacheex_maxhop(cl, er), username(cl));
|
||||
|
|
@ -1400,7 +1400,7 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf)
|
|||
er->localgenerated = 1;
|
||||
cs_log_dbg(D_CACHEEX, "cacheex: received ECM with localgenerated flag %04X@%06X:%04X %s", er->caid, er->prid, er->srvid, username(cl));
|
||||
|
||||
//check max nodes for lg flagged cw:
|
||||
// check max nodes for lg flagged cw:
|
||||
if(ll_count(er->csp_lastnodes) > cacheex_maxhop_lg(cl, er))
|
||||
{
|
||||
cs_log_dbg(D_CACHEEX, "cacheex: received (lg) %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop_lg(cl, er), username(cl));
|
||||
|
|
@ -1411,7 +1411,7 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf)
|
|||
// without localgenerated flag
|
||||
else
|
||||
{
|
||||
//check max nodes:
|
||||
// check max nodes:
|
||||
if(ll_count(er->csp_lastnodes) > cacheex_maxhop(cl, er))
|
||||
{
|
||||
cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop(cl, er), username(cl));
|
||||
|
|
@ -1472,16 +1472,19 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf)
|
|||
er->csp_lastnodes = ll_create("csp_lastnodes");
|
||||
}
|
||||
|
||||
//store remote node id if we got one. The remote node is the first node in the node list
|
||||
// store remote node id if we got one. The remote node is the first node in the node list
|
||||
data = ll_has_elements(er->csp_lastnodes);
|
||||
if(data && !cl->ncd_skey[8]) //Ok, this is tricky, we use newcamd key storage for saving the remote node
|
||||
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
|
||||
cl->ncd_skey[8] = 1; // Mark as valid node
|
||||
}
|
||||
cs_log_dbg(D_CACHEEX, "cacheex: received cacheex from remote node id %" PRIu64 "X", cacheex_node_id(cl->ncd_skey));
|
||||
|
||||
//for compatibility: add peer node if no node received (not working now, maybe later):
|
||||
// for compatibility: add peer node if no node received (not working now, maybe later):
|
||||
if(!ll_count(er->csp_lastnodes) && cl->ncd_skey[8])
|
||||
{
|
||||
if(!cs_malloc(&data, 8))
|
||||
|
|
@ -1569,6 +1572,11 @@ static void camd35_server_client_init(struct s_client *cl)
|
|||
if(!cl->init_done)
|
||||
{
|
||||
cl->cacheex_needfilter = 1;
|
||||
|
||||
// inicjalizacja nodeid changer detection
|
||||
cl->cxnodeid_changer_detected = 0;
|
||||
memset(cl->cxnodeid_last, 0, sizeof(cl->cxnodeid_last));
|
||||
memset(cl->ncd_skey, 0, sizeof(cl->ncd_skey));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1577,13 +1585,39 @@ static void camd35_server_client_init(struct s_client *cl)
|
|||
*/
|
||||
static void camd35_cacheex_push_receive_remote_id(struct s_client *cl, uint8_t *buf)
|
||||
{
|
||||
uint8_t new_nodeid[8];
|
||||
|
||||
memcpy(cl->ncd_skey, buf + 20, 8);
|
||||
memcpy(new_nodeid, buf + 20, 8);
|
||||
|
||||
// jeśli pierwszy raz, zapisz baseline
|
||||
if (!cl->cxnodeid_last[8])
|
||||
{
|
||||
memcpy(cl->cxnodeid_last, new_nodeid, 8);
|
||||
cl->cxnodeid_last[8] = 1;
|
||||
}
|
||||
|
||||
// zapisz aktualny nodeid do struktury OSCam
|
||||
memcpy(cl->ncd_skey, new_nodeid, 8);
|
||||
cl->ncd_skey[8] = 1;
|
||||
cs_log_dbg(D_CACHEEX, "cacheex: received id answer from %s: %" PRIu64 "X", username(cl), cacheex_node_id(cl->ncd_skey));
|
||||
|
||||
// sprawdź czy zmieniony
|
||||
if (memcmp(new_nodeid, cl->cxnodeid_last, 8) != 0)
|
||||
{
|
||||
cs_log("CACHEEX WARNING: NodeID changer detected for %s: new=%" PRIu64 "X old=%" PRIu64 "X",
|
||||
username(cl),
|
||||
cacheex_node_id(new_nodeid),
|
||||
cacheex_node_id(cl->cxnodeid_last));
|
||||
|
||||
cl->cxnodeid_changer_detected = 1;
|
||||
|
||||
// zapamiętaj nowy jako baseline
|
||||
memcpy(cl->cxnodeid_last, new_nodeid, 8);
|
||||
cl->cxnodeid_last[8] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void camd35_cacheex_init_dcw(struct s_client *client, ECM_REQUEST *er)
|
||||
{
|
||||
uint8_t *buf = er->src_data; // get orig request
|
||||
|
|
@ -1609,7 +1643,7 @@ void camd35_cacheex_init_dcw(struct s_client *client, ECM_REQUEST *er)
|
|||
*/
|
||||
void camd35_cacheex_push_send_own_id(struct s_client *cl, uint8_t *mbuf)
|
||||
{
|
||||
uint8_t rbuf[32]; //minimal size
|
||||
uint8_t rbuf[32]; // minimal size
|
||||
if(!cl->crypted) { return; }
|
||||
cs_log_dbg(D_CACHEEX, "cacheex: received id request from node %" PRIu64 "X %s", cacheex_node_id(mbuf + 20), username(cl));
|
||||
memset(rbuf, 0, sizeof(rbuf));
|
||||
|
|
@ -1618,7 +1652,7 @@ void camd35_cacheex_push_send_own_id(struct s_client *cl, uint8_t *mbuf)
|
|||
rbuf[2] = 0;
|
||||
memcpy(rbuf + 20, camd35_node_id, 8);
|
||||
cs_log_dbg(D_CACHEEX, "cacheex: sending own id %" PRIu64 "X request %s", cacheex_node_id(camd35_node_id), username(cl));
|
||||
camd35_send(cl, rbuf, 12); //send adds +20
|
||||
camd35_send(cl, rbuf, 12); // send adds +20
|
||||
}
|
||||
|
||||
bool camd35_cacheex_server(struct s_client *client, uint8_t *mbuf)
|
||||
|
|
@ -1631,7 +1665,7 @@ bool camd35_cacheex_server(struct s_client *client, uint8_t *mbuf)
|
|||
}
|
||||
break;
|
||||
case 0x3d: // Cache-push id request
|
||||
camd35_cacheex_push_receive_remote_id(client, mbuf); //reader send request id with its nodeid, so we save it!
|
||||
camd35_cacheex_push_receive_remote_id(client, mbuf); // reader send request id with its nodeid, so we save it!
|
||||
camd35_cacheex_push_send_own_id(client, mbuf);
|
||||
if(client->cacheex_needfilter && client->account && client->account->cacheex.mode==3){
|
||||
camd35_cacheex_send_push_filter(client, 3);
|
||||
|
|
@ -1679,7 +1713,7 @@ bool camd35_cacheex_recv_chk(struct s_client *client, uint8_t *buf)
|
|||
}
|
||||
break;
|
||||
case 0x3d: // Cache-push id request
|
||||
camd35_cacheex_push_receive_remote_id(client, buf); //client send request id with its nodeid, so we save it!
|
||||
camd35_cacheex_push_receive_remote_id(client, buf); // client send request id with its nodeid, so we save it!
|
||||
camd35_cacheex_push_send_own_id(client, buf);
|
||||
break;
|
||||
case 0x3e: // Cache-push id answer
|
||||
|
|
@ -1692,7 +1726,7 @@ bool camd35_cacheex_recv_chk(struct s_client *client, uint8_t *buf)
|
|||
}
|
||||
#endif
|
||||
break;
|
||||
case 0x3f: //cache-push
|
||||
case 0x3f: // cache-push
|
||||
camd35_cacheex_push_in(client, buf);
|
||||
break;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
|
|
@ -1717,14 +1751,14 @@ bool camd35_cacheex_recv_chk(struct s_client *client, uint8_t *buf)
|
|||
*/
|
||||
void camd35_cacheex_push_request_remote_id(struct s_client *cl)
|
||||
{
|
||||
uint8_t rbuf[32];//minimal size
|
||||
uint8_t rbuf[32]; // minimal size
|
||||
memset(rbuf, 0, sizeof(rbuf));
|
||||
rbuf[0] = 0x3d;
|
||||
rbuf[1] = 12;
|
||||
rbuf[2] = 0;
|
||||
memcpy(rbuf + 20, camd35_node_id, 8);
|
||||
cs_log_dbg(D_CACHEEX, "cacheex: sending id request to %s", username(cl));
|
||||
camd35_send(cl, rbuf, 12); //send adds +20
|
||||
camd35_send(cl, rbuf, 12); // send adds +20
|
||||
}
|
||||
|
||||
void camd35_cacheex_module_init(struct s_module *ph)
|
||||
|
|
|
|||
1754
module-camd35-cacheex.c.orig
Executable file
1754
module-camd35-cacheex.c.orig
Executable file
File diff suppressed because it is too large
Load Diff
|
|
@ -1009,7 +1009,7 @@ int32_t cc_send_cli_data(struct s_client *cl)
|
|||
|
||||
memcpy(buf, rdr->r_usr, sizeof(rdr->r_usr));
|
||||
memcpy(buf + 20, cc->node_id, 8);
|
||||
buf[28] = 0; // <-- Client wants to have EMUs, 0 - NO; 1 - YES
|
||||
buf[28] = rdr->cc_want_emu; // <-- Client wants to have EMUs, 0 - NO; 1 - YES
|
||||
memcpy(buf + 29, rdr->cc_version, sizeof(rdr->cc_version)); // cccam version (ascii)
|
||||
memcpy(buf + 61, rdr->cc_build, sizeof(rdr->cc_build)); // build number (ascii)
|
||||
|
||||
|
|
@ -1544,7 +1544,8 @@ struct cc_card *get_matching_card(struct s_client *cl, ECM_REQUEST *cur_er, int8
|
|||
}
|
||||
|
||||
if((ncard->caid == cur_er->caid // caid matches
|
||||
|| lb_match)) // or system matches if caid ends with 00
|
||||
|| (rdr->cc_want_emu && (ncard->caid == (cur_er->caid & 0xFF00))))
|
||||
|| lb_match) // or system matches if caid ends with 00 (needed for wantemu)
|
||||
{
|
||||
int32_t goodSidCount = ll_count(ncard->goodsids);
|
||||
int32_t badSidCount = ll_count(ncard->badsids);
|
||||
|
|
@ -1583,7 +1584,7 @@ struct cc_card *get_matching_card(struct s_client *cl, ECM_REQUEST *cur_er, int8
|
|||
}
|
||||
}
|
||||
|
||||
if(caid_is_nagra(ncard->caid) && (!xcard || ncard->hop < xcard->hop))
|
||||
if(!(rdr->cc_want_emu) && caid_is_nagra(ncard->caid) && (!xcard || ncard->hop < xcard->hop))
|
||||
{
|
||||
xcard = ncard; // remember card (D+ / 1810 fix) if request has no provider, but card has
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,9 @@ static uint8_t checkvalidCW(ECM_REQUEST *er)
|
|||
{
|
||||
uint8_t ret = 1;
|
||||
|
||||
if(chk_is_null_CW(er->cw))
|
||||
// Skip check for BISS1 - cw could be indeed zero
|
||||
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
|
||||
if(chk_is_null_CW(er->cw) && !caid_is_biss(er->caid))
|
||||
{ er->rc = E_NOTFOUND; }
|
||||
|
||||
if(er->rc == E_NOTFOUND)
|
||||
|
|
@ -841,27 +843,37 @@ uint8_t checkcwcycle(struct s_client *client, ECM_REQUEST *er, struct s_reader *
|
|||
break;
|
||||
|
||||
case 9: // CWCYCLE NOK without counting
|
||||
|
||||
snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc NOK");
|
||||
|
||||
if(cfg.onbadcycle > 0)
|
||||
if(cfg.onbadcycle > 0) // ignore ECM Request
|
||||
{
|
||||
cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> DROP CW (LG included)", user, er_ecmf, c_reader);
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
if(!er->localgenerated)
|
||||
{
|
||||
#endif
|
||||
cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> drop cw (ECM Answer)", user, er_ecmf, c_reader);
|
||||
return 0;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
}
|
||||
else
|
||||
{
|
||||
cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> lg-flagged CW -> do nothing", user, er_ecmf, c_reader); //D_CWC| D_TRACE
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else // only logging
|
||||
{
|
||||
cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> do nothing", user, er_ecmf, c_reader);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -312,7 +312,9 @@ void azbox_send_dcw(struct s_client *client, ECM_REQUEST *er)
|
|||
int32_t n;
|
||||
for(n = 0; n < 2; n++)
|
||||
{
|
||||
if(memcmp(er->cw + (n * 8), demux[0].last_cw[0][n], 8) && (memcmp(er->cw + (n * 8), nullcw, 8) != 0))
|
||||
// Skip check for BISS1 - cw could be indeed zero
|
||||
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
|
||||
if(memcmp(er->cw + (n * 8), demux[0].last_cw[0][n], 8) && (memcmp(er->cw + (n * 8), nullcw, 8) != 0 || caid_is_biss(er->caid)))
|
||||
{
|
||||
memcpy(demux[0].last_cw[0][n], er->cw + (n * 8), 8);
|
||||
memcpy(openxcas_cw + (n * 8), er->cw + (n * 8), 8);
|
||||
|
|
|
|||
|
|
@ -601,8 +601,10 @@ void mca_send_dcw(struct s_client *client, ECM_REQUEST *er)
|
|||
int32_t n;
|
||||
for(n = 0; n < 2; n++)
|
||||
{
|
||||
// Skip check for BISS1 - cw could be indeed zero
|
||||
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
|
||||
if((memcmp(er->cw + (n * 8), demux[0].last_cw[0][0], 8) && memcmp(er->cw + (n * 8), demux[0].last_cw[0][1], 8))
|
||||
&& (memcmp(er->cw + (n * 8), nullcw, 8) != 0))
|
||||
&& (memcmp(er->cw + (n * 8), nullcw, 8) != 0 || caid_is_biss(er->caid)))
|
||||
{
|
||||
memcpy(demux[0].last_cw[0][n], er->cw + (n * 8), 8);
|
||||
memcpy(openxcas_cw + (n * 8), er->cw + (n * 8), 8);
|
||||
|
|
|
|||
|
|
@ -730,8 +730,10 @@ int32_t stapi_write_cw(int32_t demux_id, uint8_t *cw, uint16_t *STREAMpids, int3
|
|||
|
||||
for(l = 0; l < 2; l++)
|
||||
{
|
||||
// Skip check for BISS1 - cw could be indeed zero
|
||||
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
|
||||
if(memcmp(cw + (l * 8), demux[demux_id].last_cw[0][l], 8) != 0
|
||||
&& (memcmp(cw + (l * 8), nullcw, 8) != 0))
|
||||
&& (memcmp(cw + (l * 8), nullcw, 8) != 0 || caid_is_biss(demux[demux_id].ECMpids[pidnum].CAID)))
|
||||
{
|
||||
for(n = 0; n < PTINUM; n++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -731,8 +731,10 @@ int32_t stapi_write_cw(int32_t demux_id, uint8_t *cw, uint16_t *STREAMpids, int3
|
|||
|
||||
for(l = 0; l < 2; l++)
|
||||
{
|
||||
// Skip check for BISS1 - cw could be indeed zero
|
||||
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
|
||||
if(memcmp(cw + (l * 8), demux[demux_id].last_cw[0][l], 8) != 0
|
||||
&& (memcmp(cw + (l * 8), nullcw, 8) != 0))
|
||||
&& (memcmp(cw + (l * 8), nullcw, 8) != 0 || caid_is_biss(demux[demux_id].ECMpids[pidnum].CAID)))
|
||||
{
|
||||
ErrorCode = oscam_sttkd_KeyWrite(tkd_desc_info[demux[demux_id].dev_index].key_hndl, l, cw + (l * 8));
|
||||
|
||||
|
|
|
|||
187
module-dvbapi.c
187
module-dvbapi.c
|
|
@ -1886,8 +1886,15 @@ void dvbapi_start_emm_filter(int32_t demux_id)
|
|||
}
|
||||
|
||||
if(match)
|
||||
{
|
||||
if(rdr->typ == R_EMU)
|
||||
{
|
||||
csystem = rdr->csystem;
|
||||
}
|
||||
else
|
||||
{
|
||||
csystem = get_cardsystem_by_caid(caid);
|
||||
}
|
||||
if(csystem)
|
||||
{
|
||||
if(caid != ncaid)
|
||||
|
|
@ -1907,10 +1914,18 @@ void dvbapi_start_emm_filter(int32_t demux_id)
|
|||
}
|
||||
}
|
||||
else if(csystem->get_emm_filter)
|
||||
{
|
||||
if(rdr->typ == R_EMU)
|
||||
{
|
||||
csystem->get_emm_filter_adv(rdr, &dmx_filter, &filter_count, caid, provid, demux[demux_id].program_number,
|
||||
demux[demux_id].tsid, demux[demux_id].onid, demux[demux_id].ens);
|
||||
}
|
||||
else
|
||||
{
|
||||
csystem->get_emm_filter(rdr, &dmx_filter, &filter_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cs_log_dbg(D_DVBAPI, "Demuxer %d cardsystem for emm filter for caid %04X of reader %s not found",
|
||||
|
|
@ -2580,7 +2595,6 @@ void dvbapi_stop_descrambling(int32_t demux_id, uint32_t msgid)
|
|||
}
|
||||
demux[demux_id].pidindex = -1;
|
||||
demux[demux_id].curindex = -1;
|
||||
demux[demux_id].srvtype = -1;
|
||||
|
||||
if(!dvbapi_listenport_active && cfg.dvbapi_boxtype != BOXTYPE_PC_NODMX)
|
||||
{
|
||||
|
|
@ -2652,6 +2666,8 @@ int32_t dvbapi_start_descrambling(int32_t demux_id, int32_t pid, int8_t checked,
|
|||
er->vpid = demux[demux_id].ECMpids[pid].VPID;
|
||||
er->pmtpid = demux[demux_id].pmtpid;
|
||||
er->onid = demux[demux_id].onid;
|
||||
er->tsid = demux[demux_id].tsid;
|
||||
er->ens = demux[demux_id].ens;
|
||||
er->msgid = msgid;
|
||||
|
||||
#ifdef WITH_STAPI5
|
||||
|
|
@ -2683,6 +2699,63 @@ int32_t dvbapi_start_descrambling(int32_t demux_id, int32_t pid, int8_t checked,
|
|||
match = 1; // so make it a match to try it!
|
||||
}
|
||||
|
||||
// BISS1 and BISS2 mode 1/E or FAKE caid
|
||||
// ecm pid is fake, so send out one fake ecm request
|
||||
// special treatment: if we asked the cw first without starting a filter,
|
||||
// the cw request will be killed due to no ecmfilter started
|
||||
if(caid_is_fake(demux[demux_id].ECMpids[pid].CAID) || caid_is_biss_fixed(demux[demux_id].ECMpids[pid].CAID))
|
||||
{
|
||||
int32_t j, n;
|
||||
er->ecmlen = 7;
|
||||
er->ecm[0] = 0x80; // to pass the cache check it must be 0x80 or 0x81
|
||||
er->ecm[1] = 0x00;
|
||||
er->ecm[2] = 0x04;
|
||||
i2b_buf(2, er->srvid, er->ecm + 3);
|
||||
i2b_buf(2, er->pmtpid, er->ecm + 5);
|
||||
|
||||
for(j = 0, n = 7; j < demux[demux_id].STREAMpidcount; j++, n += 2)
|
||||
{
|
||||
i2b_buf(2, demux[demux_id].STREAMpids[j], er->ecm + n);
|
||||
er->ecm[2] += 2;
|
||||
er->ecmlen += 2;
|
||||
}
|
||||
|
||||
er->ens &= 0x0FFFFFFF; // clear top 4 bits (in case of DVB-T/C or garbage), prepare for flagging
|
||||
er->ens |= 0xA0000000; // flag to emu: this is the namespace, not a pid
|
||||
|
||||
i2b_buf(2, er->tsid, er->ecm + er->ecmlen); // place tsid after the last stream pid
|
||||
i2b_buf(2, er->onid, er->ecm + er->ecmlen + 2); // place onid right after tsid
|
||||
i2b_buf(4, er->ens, er->ecm + er->ecmlen + 4); // place namespace at the end of the ecm
|
||||
|
||||
er->ecm[2] += 8;
|
||||
er->ecmlen += 8;
|
||||
|
||||
cs_log("Demuxer %d trying to descramble PID %d CAID %04X PROVID %06X ECMPID %04X ANY CHID PMTPID %04X VPID %04X",
|
||||
demux_id,
|
||||
pid,
|
||||
demux[demux_id].ECMpids[pid].CAID,
|
||||
demux[demux_id].ECMpids[pid].PROVID,
|
||||
demux[demux_id].ECMpids[pid].ECM_PID,
|
||||
demux[demux_id].pmtpid,
|
||||
demux[demux_id].ECMpids[pid].VPID);
|
||||
|
||||
demux[demux_id].curindex = pid; // set current pid to the fresh started one
|
||||
dvbapi_start_filter(demux_id,
|
||||
pid,
|
||||
demux[demux_id].ECMpids[pid].ECM_PID,
|
||||
demux[demux_id].ECMpids[pid].CAID,
|
||||
demux[demux_id].ECMpids[pid].PROVID,
|
||||
0x80,
|
||||
0xF0,
|
||||
3000,
|
||||
TYPE_ECM);
|
||||
|
||||
started = 1;
|
||||
request_cw(dvbapi_client, er, demux_id, 0); // do not register ecm since this try!
|
||||
fake_ecm = 1;
|
||||
break; // we started an ecmfilter so stop looking for next matching reader!
|
||||
}
|
||||
|
||||
if(match) // if matching reader found check for irdeto cas if local irdeto card check if it received emms in last 60 minutes
|
||||
{
|
||||
if(caid_is_irdeto(er->caid)) // irdeto cas init irdeto_curindex to wait for first index (00)
|
||||
|
|
@ -3792,7 +3865,7 @@ static void dvbapi_parse_pmt_ca_descriptor(int32_t demux_id, const uint8_t *buff
|
|||
ca_system_id = b2i(2, buffer);
|
||||
ca_pid = b2i(2, buffer + 2) & 0x1FFF;
|
||||
|
||||
if(ca_system_id == 0x0000 || ca_pid == 0x1FFF)
|
||||
if(ca_system_id == 0x0000 || (!caid_is_biss_fixed(ca_system_id) && !caid_is_fake(ca_system_id) && ca_pid == 0x1FFF))
|
||||
{
|
||||
return; // This is not a valid CAID or ECM pid
|
||||
}
|
||||
|
|
@ -4239,7 +4312,6 @@ typedef struct demux_parameters
|
|||
uint16_t onid;
|
||||
uint16_t tsid;
|
||||
uint32_t ens;
|
||||
int64_t srvtype; // service type mask from descriptor 0x85, or -1 if not present
|
||||
} demux_parameters_t;
|
||||
|
||||
static void get_demux_parameters(const uint8_t *buffer, demux_parameters_t *parameters)
|
||||
|
|
@ -4330,13 +4402,7 @@ static void get_demux_parameters(const uint8_t *buffer, demux_parameters_t *para
|
|||
}
|
||||
|
||||
case SERVICE_TYPE_MASK:
|
||||
{
|
||||
if(descriptor_length == 0x04)
|
||||
{
|
||||
parameters->srvtype = b2i(4, buffer + pos + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DEMUX_DEVICE:
|
||||
{
|
||||
|
|
@ -4387,14 +4453,33 @@ static void dvbapi_capmt_notify(struct demux_s *dmx)
|
|||
|
||||
static void dvbapi_prepare_descrambling(int32_t demux_id, uint32_t msgid)
|
||||
{
|
||||
bool start_emm = true;
|
||||
bool is_powervu = false, start_emm = true;
|
||||
char service_name[CS_SERVICENAME_SIZE];
|
||||
|
||||
// The CA PMT should have given us enough info to determine if descrambling
|
||||
// is possible. Parsing the (real) PMT is not necessary, unless we have a
|
||||
// PowerVu encrypted channel or (for some weird reason) no stream pids at all.
|
||||
// Actually, when no streams are available, we set the PMT pid as the 1st
|
||||
// stream pid, so we have to check against that. Finally, if the PMT pid is
|
||||
// not included in the CA PMT, we start the PAT filter instead.
|
||||
|
||||
#ifdef WITH_EXTENDED_CW
|
||||
uint8_t i;
|
||||
for(i = 0; i < demux[demux_id].ECMpidcount; i++)
|
||||
{
|
||||
if(caid_is_powervu(demux[demux_id].ECMpids[i].CAID))
|
||||
{
|
||||
is_powervu = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(demux[demux_id].pmtpid == 0)
|
||||
{
|
||||
dvbapi_start_pat_filter(demux_id);
|
||||
}
|
||||
else if(demux[demux_id].STREAMpids[0] == demux[demux_id].pmtpid)
|
||||
else if(demux[demux_id].STREAMpids[0] == demux[demux_id].pmtpid || is_powervu)
|
||||
{
|
||||
dvbapi_start_pmt_filter(demux_id);
|
||||
}
|
||||
|
|
@ -4480,7 +4565,6 @@ int32_t dvbapi_parse_capmt(const uint8_t *buffer, uint32_t length, int32_t connf
|
|||
bool is_update = false;
|
||||
demux_parameters_t parameters;
|
||||
memset(¶meters, 0, sizeof(parameters));
|
||||
parameters.srvtype = -1;
|
||||
|
||||
#if defined WITH_COOLAPI || defined WITH_COOLAPI2
|
||||
ca_pmt_list_management = CA_PMT_LIST_ONLY;
|
||||
|
|
@ -4644,7 +4728,6 @@ int32_t dvbapi_parse_capmt(const uint8_t *buffer, uint32_t length, int32_t connf
|
|||
demux[demux_id].ens = parameters.ens;
|
||||
demux[demux_id].tsid = parameters.tsid;
|
||||
demux[demux_id].onid = parameters.onid;
|
||||
demux[demux_id].srvtype = parameters.srvtype;
|
||||
demux[demux_id].stop_descrambling = false;
|
||||
demux[demux_id].running = false;
|
||||
demux[demux_id].sdt_filter = -1;
|
||||
|
|
@ -5597,6 +5680,7 @@ void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer,
|
|||
if(filtertype == TYPE_ECM && curpid)
|
||||
{
|
||||
uint32_t chid = 0x10000;
|
||||
int8_t pvu_skip = 0;
|
||||
ECM_REQUEST *er;
|
||||
|
||||
if(len != 0) // len = 0 receiver encountered an internal bufferoverflow!
|
||||
|
|
@ -5622,8 +5706,25 @@ void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer,
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef WITH_EMU
|
||||
if(caid_is_powervu(curpid->CAID)) // ecm counter for powervu
|
||||
{
|
||||
pvu_skip = 1;
|
||||
|
||||
if(sctlen - 11 > buffer[9])
|
||||
{
|
||||
if(buffer[11 + buffer[9]] > curpid->pvu_counter
|
||||
|| (curpid->pvu_counter == 255 && buffer[11 + buffer[9]] == 0)
|
||||
|| ((curpid->pvu_counter - buffer[11 + buffer[9]]) > 5))
|
||||
{
|
||||
curpid->pvu_counter = buffer[11 + buffer[9]];
|
||||
pvu_skip = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// wait for odd / even ecm change (only not for irdeto!)
|
||||
if(curpid->table == buffer[0] && !caid_is_irdeto(curpid->CAID))
|
||||
if((curpid->table == buffer[0] && !caid_is_irdeto(curpid->CAID)) || pvu_skip)
|
||||
{
|
||||
if(!(er = get_ecmtask()))
|
||||
{
|
||||
|
|
@ -5967,6 +6068,35 @@ void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer,
|
|||
return; // just skip on internal buffer overflow
|
||||
}
|
||||
|
||||
#ifdef WITH_EMU
|
||||
if(caid_is_director(demux[demux_id].demux_fd[filter_num].caid))
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t emmhash;
|
||||
|
||||
if(sctlen < 4)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for(i = 0; i + 2 < sctlen; i++)
|
||||
{
|
||||
if(buffer[i] == 0xF0 && (buffer[i + 2] == 0xE1 || buffer[i + 2] == 0xE4))
|
||||
{
|
||||
emmhash = (buffer[3] << 8) | buffer[sctlen - 2];
|
||||
if(demux[demux_id].demux_fd[filter_num].cadata == emmhash)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
demux[demux_id].demux_fd[filter_num].cadata = emmhash;
|
||||
dvbapi_process_emm(demux_id, filter_num, buffer, sctlen);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// fix to handle more than one irdeto emm packet
|
||||
uint8_t *pbuf = buffer;
|
||||
int32_t done = 0;
|
||||
|
|
@ -7196,8 +7326,10 @@ void dvbapi_write_cw(int32_t demux_id, int32_t pid, int32_t stream_id, uint8_t *
|
|||
for(n = 0; n < 2; n++)
|
||||
{
|
||||
// Check if cw has changed and if new cw is empty (all zeros)
|
||||
// Skip check for BISS1 - cw could be indeed zero
|
||||
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
|
||||
if((memcmp(cw + (n * cw_length), demux[demux_id].last_cw[stream_id][n], cw_length) != 0 || cw_empty)
|
||||
&& (memcmp(cw + (n * cw_length), null_cw, cw_length) != 0))
|
||||
&& (memcmp(cw + (n * cw_length), null_cw, cw_length) != 0 || caid_is_biss(demux[demux_id].ECMpids[pid].CAID)))
|
||||
{
|
||||
// prepare ca device
|
||||
uint32_t idx = dvbapi_ca_set_pid(demux_id, pid, stream_id, (algo == CA_ALGO_DES), msgid);
|
||||
|
|
@ -7489,7 +7621,9 @@ void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er)
|
|||
|
||||
// 0=matching ecm hash, 2=no filter, 3=table reset, 4=cache-ex response
|
||||
// Check only against last_cw[0] (index 0) - No need to check the rest
|
||||
if((status == 0 || status == 3 || status == 4) && er->rc < E_NOTFOUND)
|
||||
// Skip check for BISS1 - cw could be indeed zero
|
||||
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
|
||||
if((status == 0 || status == 3 || status == 4) && er->rc < E_NOTFOUND && !caid_is_biss(er->caid))
|
||||
{
|
||||
// check for matching control word
|
||||
if(memcmp(er->cw, demux[i].last_cw[0][0], 8) == 0 &&
|
||||
|
|
@ -7761,21 +7895,8 @@ void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er)
|
|||
bool set_dvbapi_cw = true;
|
||||
if((cfg.stream_relay_ctab.ctnum == 0 || chk_ctab_ex(er->caid, &cfg.stream_relay_ctab)) && cfg.stream_relay_enabled)
|
||||
{
|
||||
bool sr_handled = stream_write_cw(er);
|
||||
|
||||
if(demux[i].srvtype >= 0 && !(demux[i].srvtype & 0x180))
|
||||
{
|
||||
// E2 sent service type mask without streamrelay bits (type 7/8),
|
||||
// so this is a live-TV/softcsa service. Always send CW via dvbapi,
|
||||
// even if a dying streamrelay client still consumed it.
|
||||
set_dvbapi_cw = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No service type mask (old E2) or streamrelay bits set:
|
||||
// preserve original behavior - let stream_write_cw() decide.
|
||||
set_dvbapi_cw = !sr_handled;
|
||||
}
|
||||
// streamserver set cw
|
||||
set_dvbapi_cw = !stream_write_cw(er);
|
||||
}
|
||||
if (set_dvbapi_cw)
|
||||
#endif
|
||||
|
|
@ -8506,7 +8627,9 @@ int32_t dvbapi_check_ecm_delayed_delivery(int32_t demux_id, ECM_REQUEST *er)
|
|||
}
|
||||
|
||||
// Check for null cw
|
||||
if(memcmp(er->cw, nullcw, 8) == 0 && memcmp(er->cw + 8, nullcw, 8) == 0)
|
||||
// Skip check for BISS1 - cw could be indeed zero
|
||||
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
|
||||
if(memcmp(er->cw, nullcw, 8) == 0 && memcmp(er->cw + 8, nullcw, 8) == 0 && !caid_is_biss(er->caid))
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@
|
|||
#define DEMUX_CA_MASK_ADAPTER 0x82 // deprecated - applications should use descriptors ADAPTER_DEVICE, DEMUX_DEVICE and CA_DEVICE instead
|
||||
#define ADAPTER_DEVICE 0x83
|
||||
#define PMT_PID 0x84
|
||||
#define SERVICE_TYPE_MASK 0x85
|
||||
#define SERVICE_TYPE_MASK 0x85 // not used by OSCam
|
||||
#define DEMUX_DEVICE 0x86
|
||||
#define CA_DEVICE 0x87
|
||||
|
||||
|
|
@ -336,6 +336,9 @@ typedef struct filter_s
|
|||
uint32_t SlotHandle[10];
|
||||
uint32_t BufferHandle[10];
|
||||
#endif
|
||||
#ifdef WITH_EMU
|
||||
uint32_t cadata;
|
||||
#endif
|
||||
} FILTERTYPE;
|
||||
|
||||
#ifdef WITH_EXTENDED_CW
|
||||
|
|
@ -368,6 +371,9 @@ typedef struct s_ecmpid
|
|||
uint32_t index[MAX_STREAM_INDICES]; // ca indices used for this ecm pid (index[0] holds ca index for STREAMmpids[0] and so on)
|
||||
uint32_t streams; // bit mask of STREAMpids enabled for this ECMpid
|
||||
uint32_t cadata;
|
||||
#ifdef WITH_EMU
|
||||
int16_t pvu_counter;
|
||||
#endif
|
||||
} ECMPIDTYPE;
|
||||
|
||||
typedef struct s_emmpid
|
||||
|
|
@ -446,7 +452,6 @@ typedef struct demux_s
|
|||
struct timeb decstart;
|
||||
struct timeb decend;
|
||||
uint32_t msgid;
|
||||
int64_t srvtype; // service type mask from CA PMT descriptor 0x85, or -1 if not present
|
||||
} DEMUXTYPE;
|
||||
|
||||
typedef struct s_streampid
|
||||
|
|
|
|||
883
module-emulator-biss.c
Executable file
883
module-emulator-biss.c
Executable file
|
|
@ -0,0 +1,883 @@
|
|||
#define MODULE_LOG_PREFIX "emu"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
#include "module-emulator-osemu.h"
|
||||
#include "module-emulator-biss.h"
|
||||
#include "oscam-aes.h"
|
||||
#include "oscam-string.h"
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
//#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
// DVB-CISSA v1 IV as defined in ETSI TS 103 127
|
||||
static const uint8_t dvb_cissa_iv[16] =
|
||||
{
|
||||
0x44, 0x56, 0x42, 0x54, 0x4D, 0x43, 0x50, 0x54,
|
||||
0x41, 0x45, 0x53, 0x43, 0x49, 0x53, 0x53, 0x41
|
||||
};
|
||||
|
||||
static void unify_orbitals(uint32_t *namespace)
|
||||
{
|
||||
// Unify orbitals to produce same namespace among users
|
||||
// Set positions according to http://satellites-xml.org
|
||||
|
||||
uint16_t pos = (*namespace & 0x0FFF0000) >> 16;
|
||||
|
||||
switch (pos)
|
||||
{
|
||||
case 29: // Rascom QAF 1R
|
||||
case 31: // Eutelsat 3B
|
||||
{
|
||||
pos = 30;
|
||||
break;
|
||||
}
|
||||
|
||||
case 49:
|
||||
case 50: // SES 5
|
||||
{
|
||||
pos = 48; // Astra 4A
|
||||
break;
|
||||
}
|
||||
|
||||
case 215:
|
||||
{
|
||||
pos = 216; // Eutelsat 21B
|
||||
break;
|
||||
}
|
||||
|
||||
case 285: // Astra 2E
|
||||
{
|
||||
pos = 282; // Astra 2F/2G
|
||||
break;
|
||||
}
|
||||
|
||||
case 328: // Intelsat 28
|
||||
case 329:
|
||||
case 331: // Eutelsat 33C
|
||||
{
|
||||
pos = 330;
|
||||
break;
|
||||
}
|
||||
|
||||
case 359: // Eutelsat 36B
|
||||
case 361: // Express AMU1
|
||||
{
|
||||
pos = 360;
|
||||
break;
|
||||
}
|
||||
|
||||
case 451: // Intelsat 904
|
||||
{
|
||||
pos = 450; // Intelsat 12
|
||||
break;
|
||||
}
|
||||
|
||||
case 550:
|
||||
case 551: // G-Sat 8/16
|
||||
{
|
||||
pos = 549; // Yamal 402
|
||||
break;
|
||||
}
|
||||
|
||||
case 748:
|
||||
case 749: // ABS 2A
|
||||
{
|
||||
pos = 750;
|
||||
break;
|
||||
}
|
||||
|
||||
case 848: // Horizons 2
|
||||
case 852: // Intelsat 15
|
||||
{
|
||||
pos = 850;
|
||||
break;
|
||||
}
|
||||
|
||||
case 914: // Mesasat 3a
|
||||
{
|
||||
pos = 915; // Mesasat 3/3b
|
||||
break;
|
||||
}
|
||||
|
||||
case 934: // G-Sat 17
|
||||
case 936: // Insat 4B
|
||||
{
|
||||
pos = 935; // G-Sat 15
|
||||
break;
|
||||
}
|
||||
|
||||
case 3600 - 911: // Nimiq 6
|
||||
{
|
||||
pos = 3600 - 910; // Galaxy 17
|
||||
break;
|
||||
}
|
||||
|
||||
case 3600 - 870: // SES 2
|
||||
case 3600 - 872: // TKSat 1
|
||||
{
|
||||
pos = 3600 - 871;
|
||||
break;
|
||||
}
|
||||
|
||||
case 3600 - 432: // Sky Brasil 1
|
||||
case 3600 - 430: // Intelsat 11
|
||||
{
|
||||
pos = 3600 - 431;
|
||||
break;
|
||||
}
|
||||
|
||||
case 3600 - 376: // Telstar 11N
|
||||
case 3600 - 374: // NSS 10
|
||||
{
|
||||
pos = 3600 - 375;
|
||||
break;
|
||||
}
|
||||
|
||||
case 3600 - 359: // Hispasat 36W-1
|
||||
{
|
||||
pos = 3600 - 360; // Eutelsat 36 West A
|
||||
break;
|
||||
}
|
||||
|
||||
case 3600 - 81: // Eutelsat 8 West B
|
||||
{
|
||||
pos = 3600 - 80;
|
||||
break;
|
||||
}
|
||||
|
||||
case 3600 - 73: // Eutelsat 7 West A
|
||||
case 3600 - 72:
|
||||
case 3600 - 71:
|
||||
{
|
||||
pos = 3600 - 70; // Nilesat 201
|
||||
break;
|
||||
}
|
||||
|
||||
case 3600 - 10: // Intelsat 10-02
|
||||
case 3600 - 9: // Thor 6
|
||||
case 3600 - 7: // Thor 7
|
||||
case 3600 - 6: // Thor 7
|
||||
{
|
||||
pos = 3600 - 8; // Thor 5
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*namespace = (*namespace & 0xF000FFFF) | (pos << 16);
|
||||
}
|
||||
|
||||
static void annotate(char *buf, uint8_t len, const uint8_t *ecm, uint16_t ecmLen,
|
||||
uint32_t hash, int8_t isNamespaceHash, int8_t datecoded)
|
||||
{
|
||||
// Extract useful information to append to the "Example key ..." message.
|
||||
//
|
||||
// For feeds, the orbital position & frequency are usually embedded in the namespace.
|
||||
// See https://github.com/OpenPLi/enigma2/blob/develop/lib/dvb/frontend.cpp#L476
|
||||
// hash = (sat.orbital_position << 16);
|
||||
// hash |= ((sat.frequency/1000)&0xFFFF)|((sat.polarisation&1) << 15);
|
||||
//
|
||||
// If the onid & tsid appear to be a unique DVB identifier, enigma2 strips the frequency
|
||||
// from our namespace. See https://github.com/OpenPLi/enigma2/blob/develop/lib/dvb/scan.cpp#L55
|
||||
// In that case, our annotation contains the onid:tsid:sid triplet in lieu of frequency.
|
||||
//
|
||||
// For the universal case, we print the number of elementary stream pids & pmtpid.
|
||||
// The sid and current time are included for all. Examples:
|
||||
//
|
||||
// F 1A2B3C4D 00000000 XXXXXXXXXXXXXXXX ; 110.5W 12345H sid:0001 added: 2017-10-17 @ 13:14:15 // namespace
|
||||
// F 1A2B3C4D 20180123 XXXXXXXXXXXXXXXX ; 33.5E ABCD:9876:1234 added: 2017-10-17 @ 13:14:15 // stripped namespace
|
||||
// F 1A2B3C4D 20180123 XXXXXXXXXXXXXXXX ; av:5 pmt:0134 sid:0001 added: 2017-10-17 @ 13:14:15 // universal
|
||||
|
||||
uint8_t pidcount;
|
||||
uint16_t frequency, degrees, pmtpid, srvid, tsid, onid;
|
||||
uint32_t ens;
|
||||
char compass, polarisation, timeStr1[9], timeStr2[19];
|
||||
|
||||
if (datecoded)
|
||||
{
|
||||
date_to_str(timeStr1, sizeof(timeStr1), 4, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(timeStr1, sizeof(timeStr1), "00000000");
|
||||
}
|
||||
|
||||
date_to_str(timeStr2, sizeof(timeStr2), 0, 2);
|
||||
|
||||
if (isNamespaceHash) // Namespace hash
|
||||
{
|
||||
ens = b2i(4, ecm + ecmLen - 4); // Namespace will be the last 4 bytes of the ecm
|
||||
degrees = (ens >> 16) & 0x0FFF; // Remove not-a-pid flag
|
||||
|
||||
if (degrees > 1800)
|
||||
{
|
||||
degrees = 3600 - degrees;
|
||||
compass = 'W';
|
||||
}
|
||||
else
|
||||
{
|
||||
compass = 'E';
|
||||
}
|
||||
|
||||
if (0 == (ens & 0xFFFF)) // Stripped namespace hash
|
||||
{
|
||||
srvid = b2i(2, ecm + 3);
|
||||
tsid = b2i(2, ecm + ecmLen - 8);
|
||||
onid = b2i(2, ecm + ecmLen - 6);
|
||||
// Printing degree sign "\u00B0" requires c99 standard
|
||||
snprintf(buf, len, "F %08X %s XXXXXXXXXXXXXXXX ; %5.1f%c %04X:%04X:%04X added: %s",
|
||||
hash, timeStr1, degrees / 10.0, compass, onid, tsid, srvid, timeStr2);
|
||||
}
|
||||
else // Full namespace hash
|
||||
{
|
||||
srvid = b2i(2, ecm + 3);
|
||||
frequency = ens & 0x7FFF; // Remove polarity bit
|
||||
polarisation = ens & 0x8000 ? 'V' : 'H';
|
||||
// Printing degree sign "\u00B0" requires c99 standard
|
||||
snprintf(buf, len, "F %08X %s XXXXXXXXXXXXXXXX ; %5.1f%c %5d%c sid:%04X added: %s",
|
||||
hash, timeStr1, degrees / 10.0, compass, frequency, polarisation, srvid, timeStr2);
|
||||
}
|
||||
}
|
||||
else // Universal hash
|
||||
{
|
||||
srvid = b2i(2, ecm + 3);
|
||||
pmtpid = b2i(2, ecm + 5);
|
||||
pidcount = (ecmLen - 15) / 2; // video + audio pids count
|
||||
snprintf(buf, len, "F %08X %s XXXXXXXXXXXXXXXX ; av:%d pmt:%04X sid:%04X added: %s",
|
||||
hash, timeStr1, pidcount, pmtpid, srvid, timeStr2);
|
||||
}
|
||||
}
|
||||
|
||||
static int8_t is_common_hash(uint32_t hash)
|
||||
{
|
||||
// Check universal hash against a number of commnon universal
|
||||
// hashes in order to warn users about potential key clashes
|
||||
|
||||
switch (hash)
|
||||
{
|
||||
case 0xBAFCD9FD: // 0001 0020 0200 1010 1020 (most common hash)
|
||||
return 1;
|
||||
case 0xA6A4FBD4: // 0001 0800 0200 1010 1020
|
||||
return 1;
|
||||
case 0xEFAB7A4D: // 0001 0800 1010 1020 0200
|
||||
return 1;
|
||||
case 0x83FA15D1: // 0001 0020 0134 0100 0101
|
||||
return 1;
|
||||
case 0x58934C38: // 0001 0800 1010 1020 1030 0200
|
||||
return 1;
|
||||
case 0x2C3CEC17: // 0001 0020 0134 0100
|
||||
return 1;
|
||||
case 0x73DF7F7E: // 0001 0020 0200 1010 1020 1030
|
||||
return 1;
|
||||
case 0xAFA85BC8: // 0001 0020 0021 0022 0023
|
||||
return 1;
|
||||
case 0x8C51F31D: // 0001 0800 0200 1010 1020 1030 1040
|
||||
return 1;
|
||||
case 0xE2F9BD29: // 0001 0800 0200 1010 1020 1030
|
||||
return 1;
|
||||
case 0xB9EBE0FF: // 0001 0100 0200 1010 1020 (less common hash)
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int8_t is_valid_namespace(uint32_t namespace)
|
||||
{
|
||||
// Note to developers:
|
||||
// If we ever have a satellite at 0.0E, edit to allow stripped namespace
|
||||
// '0xA0000000' with an additional test on tsid and onid being != 0
|
||||
|
||||
uint16_t orbital, frequency;
|
||||
|
||||
orbital = (namespace >> 16) & 0x0FFF;
|
||||
frequency = namespace & 0x7FFF;
|
||||
|
||||
if ((namespace & 0xF0000000) != 0xA0000000) return 0; // Value isn't flagged as namespace
|
||||
if ((namespace & 0x0FFFFFFF) == 0x00000000) return 0; // Empty namespace
|
||||
if (orbital > 3599) return 0; // Allow only DVB-S
|
||||
if (frequency == 0) return 1; // Stripped namespace
|
||||
if (frequency >= 3400 && frequency <= 4200) return 1; // Super extended C band
|
||||
if (frequency >= 10700 && frequency <= 12750) return 1; // Ku band Europe
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int8_t get_sw(uint32_t provider, uint8_t *sw, uint8_t sw_length, int8_t dateCoded, int8_t printMsg)
|
||||
{
|
||||
// If date-coded keys are enabled in the webif, this function evaluates the expiration date
|
||||
// of the found keys. Expired keys are not sent to the calling function. If date-coded keys
|
||||
// are disabled, then every key is sent without any evaluation. It takes the "provider" as
|
||||
// input and outputs the "sw". Returns 0 (key not found, or expired) or 1 (key found).
|
||||
|
||||
// printMsg: 0 => No message
|
||||
// printMsg: 1 => Print message only if key is found
|
||||
// printMsg: 2 => Always print message, regardless if key is found or not
|
||||
|
||||
char keyExpDate[9] = "00000000";
|
||||
|
||||
if (emu_find_key('F', provider, 0, keyExpDate, sw, sw_length, 0, 0, 0, NULL)) // Key found
|
||||
{
|
||||
if (dateCoded) // Date-coded keys are enabled, evaluate expiration date
|
||||
{
|
||||
char currentDate[9];
|
||||
date_to_str(currentDate, sizeof(currentDate), 0, 3);
|
||||
|
||||
if (strncmp("00000000", keyExpDate, 9) == 0 || strncmp(currentDate, keyExpDate, 9) < 0) // Evergreen or not expired
|
||||
{
|
||||
if (printMsg == 1 || printMsg == 2) cs_log("Key found: F %08X %s", provider, keyExpDate);
|
||||
return 1;
|
||||
}
|
||||
else // Key expired
|
||||
{
|
||||
sw = NULL; // Make sure we don't send any expired key
|
||||
if (printMsg == 2) cs_log("Key expired: F %08X %s", provider, keyExpDate);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else // Date-coded keys are disabled, don't evaluate expiration date
|
||||
{
|
||||
if (printMsg == 1 || printMsg == 2) cs_log("Key found: F %08X %s", provider, keyExpDate);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else // Key not found
|
||||
{
|
||||
if (printMsg == 2) cs_log("Key not found: F %08X", provider);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int8_t biss_mode1_ecm(struct s_reader *rdr, const uint8_t *ecm, uint16_t caid, uint16_t ecm_pid, uint8_t *dw, EXTENDED_CW *cw_ex)
|
||||
{
|
||||
// Oscam's fake ecm consists of [sid] [pmtpid] [pid1] [pid2] ... [pidx] [tsid] [onid] [ens]
|
||||
// On enigma boxes tsid, onid and namespace should be non zero, while on non-enigma
|
||||
// boxes they are usually all zero. The top 4 bits of the namespace are flagged with 0xA.
|
||||
|
||||
// The emulator creates a unique channel hash using srvid and enigma namespace or
|
||||
// srvid, tsid, onid and namespace (in case of namespace without frequency) and
|
||||
// another weaker (not unique) hash based on every pid of the channel. This universal
|
||||
// hash should be available on all types of stbs (enigma and non-enigma).
|
||||
|
||||
// Key searches are made from highest priority (tightest test first) to lowest priority
|
||||
// (loosest test last):
|
||||
// 1. Namespace hash (only on enigma boxes)
|
||||
// 2. Universal hash (all box types with emu r752+)
|
||||
// 3. Valid tsid, onid combination
|
||||
// 4. Reverse order pid (audio, video, pmt)
|
||||
// 5. Legacy srvid, ecm pid combination
|
||||
// 6. Default "All Feeds" key
|
||||
|
||||
// If enabled in the webif, a date based key search is performed. If the expiration
|
||||
// date has passed, the key is not sent back from get_sw(). This option is used only
|
||||
// in the namespace hash, universal hash and the "All Feeds" search methods.
|
||||
|
||||
uint32_t i, ens = 0, hash = 0;
|
||||
uint16_t srvid, tsid = 0, onid = 0, pid, ecm_len = SCT_LEN(ecm);
|
||||
uint8_t *sw, sw_length, ecm_copy[ecm_len];
|
||||
char tmp_buffer1[33], tmp_buffer2[90] = "0", tmp_buffer3[90] = "0";
|
||||
|
||||
if (caid == 0x2602 && cw_ex != NULL) // BISS2
|
||||
{
|
||||
cw_ex->mode = CW_MODE_ONE_CW;
|
||||
cw_ex->algo = CW_ALGO_AES128;
|
||||
cw_ex->algo_mode = CW_ALGO_MODE_CBC;
|
||||
memcpy(cw_ex->data, dvb_cissa_iv, 16);
|
||||
|
||||
sw = cw_ex->session_word;
|
||||
sw_length = 16;
|
||||
}
|
||||
else // BISS1
|
||||
{
|
||||
sw = dw;
|
||||
sw_length = 8;
|
||||
}
|
||||
|
||||
srvid = b2i(2, ecm + 3);
|
||||
|
||||
if (ecm_len >= 17) // Likely an r752+ extended ecm
|
||||
{
|
||||
tsid = b2i(2, ecm + ecm_len - 8);
|
||||
onid = b2i(2, ecm + ecm_len - 6);
|
||||
ens = b2i(4, ecm + ecm_len - 4);
|
||||
}
|
||||
|
||||
// 1. Namespace hash (enigma only)
|
||||
if (is_valid_namespace(ens))
|
||||
{
|
||||
unify_orbitals(&ens);
|
||||
memcpy(ecm_copy, ecm, ecm_len);
|
||||
i2b_buf(4, ens, ecm_copy + ecm_len - 4);
|
||||
|
||||
for (i = 0; i < 5; i++) // Find key matching hash made with frequency modified to: f+0, then f-1, f+1, f-2, lastly f+2
|
||||
{
|
||||
ecm_copy[ecm_len - 1] = (i & 1) ? ecm_copy[ecm_len - 1] - i : ecm_copy[ecm_len - 1] + i; // frequency +/- 1, 2 MHz
|
||||
|
||||
if (0 != (ens & 0xFFFF)) // Full namespace - Calculate hash with srvid and namespace only
|
||||
{
|
||||
i2b_buf(2, srvid, ecm_copy + ecm_len - 6); // Put [srvid] right before [ens]
|
||||
hash = crc32(caid, ecm_copy + ecm_len - 6, 6);
|
||||
}
|
||||
else // Namespace without frequency - Calculate hash with srvid, tsid, onid and namespace
|
||||
{
|
||||
i2b_buf(2, srvid, ecm_copy + ecm_len - 10); // Put [srvid] right before [tsid] [onid] [ens] sequence
|
||||
hash = crc32(caid, ecm_copy + ecm_len - 10, 10);
|
||||
}
|
||||
|
||||
if (get_sw(hash, sw, sw_length, rdr->emu_datecodedenabled, i == 0 ? 2 : 1)) // Do not print "key not found" for frequency off by 1, 2
|
||||
{
|
||||
memcpy(sw + sw_length, sw, sw_length);
|
||||
return EMU_OK;
|
||||
}
|
||||
|
||||
if (i == 0) // No key found matching our hash: create example SoftCam.Key BISS line for the live log
|
||||
{
|
||||
annotate(tmp_buffer2, sizeof(tmp_buffer2), ecm_copy, ecm_len, hash, 1, rdr->emu_datecodedenabled);
|
||||
}
|
||||
|
||||
if (0 == (ens & 0xFFFF)) // Namespace without frequency - Do not iterate
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Universal hash (in r752+ style ecms that contain pmt pid)
|
||||
if ((ens & 0xF0000000) == 0xA0000000)
|
||||
{
|
||||
hash = crc32(caid, ecm + 3, ecm_len - 3 - 8); // Do not include [tsid] [onid] [ens] in the hash
|
||||
|
||||
if (get_sw(hash, sw, sw_length, rdr->emu_datecodedenabled, 2))
|
||||
{
|
||||
memcpy(sw + sw_length, sw, sw_length);
|
||||
return EMU_OK;
|
||||
}
|
||||
|
||||
// No key found matching our hash: create example SoftCam.Key BISS line for the live log
|
||||
annotate(tmp_buffer3, sizeof(tmp_buffer3), ecm_copy, ecm_len, hash, 0, rdr->emu_datecodedenabled);
|
||||
}
|
||||
|
||||
// 3. Valid [tsid] [onid] combination (per enigma2)
|
||||
if (onid != 0 && (onid != 1 || tsid >= 2) && onid < 0xFF00)
|
||||
{
|
||||
if (get_sw(tsid << 16 | onid, sw, sw_length, 0, 2))
|
||||
{
|
||||
memcpy(sw + sw_length, sw, sw_length);
|
||||
return EMU_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Reverse order pid search
|
||||
// (better identifies channels with variable counts of audio pids)
|
||||
// Strip [tsid] [onid] [ens] on r752+ ecms to be compatible with older versions)
|
||||
if ((ens & 0xF0000000) == 0xA0000000)
|
||||
{
|
||||
ecm_len -= 8;
|
||||
}
|
||||
|
||||
for (i = ecm_len - 2; i >= 5; i -= 2)
|
||||
{
|
||||
pid = b2i(2, ecm + i);
|
||||
|
||||
if (get_sw((srvid << 16) | pid, sw, sw_length, 0, 2))
|
||||
{
|
||||
memcpy(sw + sw_length, sw, sw_length);
|
||||
return EMU_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Legacy [srvid] [ecm pid] combination
|
||||
if (get_sw((srvid << 16) | ecm_pid, sw, sw_length, 0, 2))
|
||||
{
|
||||
memcpy(sw + sw_length, sw, sw_length);
|
||||
return EMU_OK;
|
||||
}
|
||||
|
||||
// 6. Default BISS key for events with many feeds sharing the same session word
|
||||
// (limited to local ecms, network ecms with ecm pid equal to zero are blocked)
|
||||
if (ecm_pid != 0 && get_sw(0xA11FEED5, sw, sw_length, rdr->emu_datecodedenabled, 2))
|
||||
{
|
||||
memcpy(sw + sw_length, sw, sw_length);
|
||||
cs_hexdump(0, sw, sw_length, tmp_buffer1, sizeof(tmp_buffer1));
|
||||
cs_log("No specific match found. Using 'All Feeds' key: %s", tmp_buffer1);
|
||||
return EMU_OK;
|
||||
}
|
||||
|
||||
// Print example key lines for available hash search methods, if no key is found
|
||||
if (strncmp(tmp_buffer2, "0", 2)) cs_log("Example key based on namespace hash: %s", tmp_buffer2);
|
||||
if (strncmp(tmp_buffer3, "0", 2)) cs_log("Example key based on universal hash: %s", tmp_buffer3);
|
||||
|
||||
// Check if universal hash is common and warn user
|
||||
if (is_common_hash(hash)) cs_log("Feed has commonly used pids, universal hash clashes in SoftCam.Key are likely!");
|
||||
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
static inline int8_t get_ecm_key(uint16_t onid, uint16_t esid, uint8_t parity, uint8_t *key)
|
||||
{
|
||||
return emu_find_key('G', onid << 16 | esid, 0, parity == 0 ? "00" : "01", key, 16, 1, 0, 0, NULL);
|
||||
}
|
||||
|
||||
static int8_t biss2_mode_ca_ecm(const uint8_t *ecm, EXTENDED_CW *cw_ex)
|
||||
{
|
||||
uint8_t ecm_cipher_type, session_key_parity;
|
||||
uint8_t session_key[16], iv[16];
|
||||
uint16_t entitlement_session_id, original_network_id, descriptor_length;
|
||||
uint16_t position, ecm_length = SCT_LEN(ecm);
|
||||
uint32_t payload_checksum, calculated_checksum;
|
||||
char tmp_buffer[64];
|
||||
struct aes_keys aes;
|
||||
|
||||
// Calculate crc32 checksum and compare against the checksum bytes of the ECM
|
||||
payload_checksum = b2i(4, ecm + ecm_length - 4);
|
||||
calculated_checksum = ccitt32_crc((uint8_t *)ecm, ecm_length - 4);
|
||||
|
||||
if (payload_checksum != calculated_checksum)
|
||||
{
|
||||
cs_log_dbg(D_TRACE, "ECM checksum mismatch (payload: %08X vs calculated: %08X",
|
||||
payload_checksum, calculated_checksum);
|
||||
return EMU_CHECKSUM_ERROR;
|
||||
}
|
||||
|
||||
// Unique identifiers of the session key
|
||||
entitlement_session_id = b2i(2, ecm + 3);
|
||||
original_network_id = b2i(2, ecm + 8);
|
||||
|
||||
ecm_cipher_type = ecm[10] >> 5;
|
||||
if (ecm_cipher_type != 0) // Session words shall be encrypted with AES_128_CBC
|
||||
{
|
||||
cs_log("ECM cipher type %d not supported", ecm_cipher_type);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
descriptor_length = b2i(2, ecm + 10) & 0x0FFF;
|
||||
position = 12 + descriptor_length;
|
||||
|
||||
session_key_parity = ecm[position] >> 7; // Parity can be "00" or "01"
|
||||
position++;
|
||||
|
||||
if (!get_ecm_key(original_network_id, entitlement_session_id, session_key_parity, session_key))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
memcpy(iv, ecm + position, 16); // "AES_128_CBC_enc_session_word_iv"
|
||||
memcpy(cw_ex->session_word, ecm + position + 16, 16); // "AES_128_CBC_enc_session_word_0"
|
||||
memcpy(cw_ex->session_word + 16, ecm + position + 32, 16); // "AES_128_CBC_enc_session_word_1"
|
||||
|
||||
// Delete these cs_log calls when everything is confirmed to work correctly
|
||||
cs_hexdump(3, iv, 16, tmp_buffer, sizeof(tmp_buffer));
|
||||
cs_log_dbg(D_TRACE, "session_word_iv: %s", tmp_buffer);
|
||||
|
||||
cs_hexdump(3, cw_ex->session_word, 16, tmp_buffer, sizeof(tmp_buffer));
|
||||
cs_log_dbg(D_TRACE, "encrypted session_word_0: %s", tmp_buffer);
|
||||
|
||||
cs_hexdump(3, cw_ex->session_word + 16, 16, tmp_buffer, sizeof(tmp_buffer));
|
||||
cs_log_dbg(D_TRACE, "encrypted session_word_1: %s", tmp_buffer);
|
||||
|
||||
// Decrypt session words
|
||||
aes_set_key(&aes, (char *)session_key);
|
||||
aes_cbc_decrypt(&aes, cw_ex->session_word, 16, iv);
|
||||
memcpy(iv, ecm + position, 16); // Set iv again to the correct one
|
||||
aes_cbc_decrypt(&aes, cw_ex->session_word + 16, 16, iv);
|
||||
|
||||
// Delete these cs_log calls when everything is confirmed to work correctly
|
||||
cs_hexdump(3, cw_ex->session_word, 16, tmp_buffer, sizeof(tmp_buffer));
|
||||
cs_log_dbg(D_TRACE, "decrypted session_word_0: %s", tmp_buffer);
|
||||
|
||||
cs_hexdump(3, cw_ex->session_word + 16, 16, tmp_buffer, sizeof(tmp_buffer));
|
||||
cs_log_dbg(D_TRACE, "decrypted session_word_1: %s", tmp_buffer);
|
||||
|
||||
cw_ex->mode = CW_MODE_ONE_CW;
|
||||
cw_ex->algo = CW_ALGO_AES128;
|
||||
cw_ex->algo_mode = CW_ALGO_MODE_CBC;
|
||||
memcpy(cw_ex->data, dvb_cissa_iv, 16);
|
||||
|
||||
return EMU_OK;
|
||||
}
|
||||
|
||||
int8_t biss_ecm(struct s_reader *rdr, const uint8_t *ecm, uint16_t caid, uint16_t ecm_pid, uint8_t *dw, EXTENDED_CW *cw_ex)
|
||||
{
|
||||
switch (caid)
|
||||
{
|
||||
case 0x2600:
|
||||
return biss_mode1_ecm(rdr, ecm, caid, ecm_pid, dw, NULL);
|
||||
|
||||
case 0x2602:
|
||||
return biss_mode1_ecm(rdr, ecm, caid, ecm_pid, NULL, cw_ex);
|
||||
|
||||
case 0x2610:
|
||||
return biss2_mode_ca_ecm(ecm, cw_ex);
|
||||
|
||||
default:
|
||||
cs_log("Unknown Biss caid %04X - Please report!", caid);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t parse_session_data_descriptor(const uint8_t *data, uint16_t esid, uint16_t onid, uint32_t *keysAdded)
|
||||
{
|
||||
uint8_t descriptor_tag = data[0];
|
||||
uint8_t descriptor_length = data[1];
|
||||
|
||||
switch (descriptor_tag)
|
||||
{
|
||||
case 0x81: // session_key_descriptor
|
||||
{
|
||||
uint8_t session_key_type = data[2] >> 1;
|
||||
if (session_key_type == 0) // AES-128
|
||||
{
|
||||
uint8_t session_key_parity = data[2] & 0x01;
|
||||
uint8_t session_key_data[16];
|
||||
memcpy(session_key_data, data + 3, 16); // This is the ECM key
|
||||
|
||||
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||||
if (emu_update_key('G', onid << 16 | esid, session_key_parity ? "01" : "00", session_key_data, 16, 1, NULL))
|
||||
{
|
||||
(*keysAdded)++;
|
||||
char tmp[33];
|
||||
cs_hexdump(0, session_key_data, 16, tmp, sizeof(tmp));
|
||||
cs_log("Key found in EMM: G %08X %02d %s", onid << 16 | esid, session_key_parity, tmp);
|
||||
}
|
||||
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x82: // entitlement_flags_descriptor
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 2 + descriptor_length;
|
||||
}
|
||||
|
||||
static int8_t parse_session_data(const uint8_t *data, RSA *key, uint16_t esid, uint16_t onid, uint32_t *keysAdded)
|
||||
{
|
||||
// session_data is encrypted with RSA 2048 bit OAEP
|
||||
// Maximum size of decrypted session_data is less than (256-41) bytes
|
||||
uint8_t session_data[214];
|
||||
|
||||
if (RSA_private_decrypt(256, data, session_data, key, RSA_PKCS1_OAEP_PADDING) > 0)
|
||||
{
|
||||
uint16_t pos = 0;
|
||||
uint16_t descriptor_length = b2i(2, session_data) & 0x0FFF;
|
||||
|
||||
while (pos < descriptor_length)
|
||||
{
|
||||
pos += parse_session_data_descriptor(session_data + 2 + pos, esid, onid, keysAdded);
|
||||
}
|
||||
|
||||
return EMU_OK;
|
||||
}
|
||||
|
||||
return EMU_NOT_SUPPORTED; // Decryption failed for whatever reason
|
||||
}
|
||||
|
||||
static int8_t get_rsa_key(struct s_reader *rdr, const uint8_t *ekid, RSA **key)
|
||||
{
|
||||
LL_ITER itr;
|
||||
biss2_rsa_key_t *data;
|
||||
|
||||
itr = ll_iter_create(rdr->ll_biss2_rsa_keys);
|
||||
while ((data = ll_iter_next(&itr)))
|
||||
{
|
||||
if (data->ekid == ekid)
|
||||
{
|
||||
*key = data->key;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8_t biss_emm(struct s_reader *rdr, const uint8_t *emm, uint32_t *keysAdded)
|
||||
{
|
||||
uint8_t emm_cipher_type, entitlement_priv_data_loop, entitlement_key_id[8];
|
||||
uint16_t entitlement_session_id, original_network_id, descriptor_length;
|
||||
uint16_t pos, emm_length = SCT_LEN(emm);
|
||||
uint32_t payload_checksum, calculated_checksum;
|
||||
int8_t result = EMU_NOT_SUPPORTED;
|
||||
char tmp[17];
|
||||
RSA *key;
|
||||
|
||||
// Calculate crc32 checksum and compare against the checksum bytes of the EMM
|
||||
payload_checksum = b2i(4, emm + emm_length - 4);
|
||||
calculated_checksum = ccitt32_crc((uint8_t *)emm, emm_length - 4);
|
||||
|
||||
if (payload_checksum != calculated_checksum)
|
||||
{
|
||||
cs_log_dbg(D_TRACE, "EMM checksum mismatch (payload: %08X vs calculated: %08X",
|
||||
payload_checksum, calculated_checksum);
|
||||
return EMU_CHECKSUM_ERROR;
|
||||
}
|
||||
|
||||
// Identifiers of the session key carried in the EMM
|
||||
// We just pass them to the "parse_session_data()" function
|
||||
entitlement_session_id = b2i(2, emm + 3);
|
||||
original_network_id = b2i(2, emm + 8);
|
||||
cs_log_dbg(D_TRACE, "onid: %04X, esid: %04X", original_network_id, entitlement_session_id);
|
||||
|
||||
emm_cipher_type = emm[11] >> 5; // top 3 bits;
|
||||
entitlement_priv_data_loop = (emm[11] >> 4) & 0x01; // 4th bit
|
||||
|
||||
if (emm_cipher_type != 0) // EMM payload is not encrypted with RSA_2048_OAEP
|
||||
{
|
||||
cs_log_dbg(D_TRACE, "EMM cipher type %d not supported", emm_cipher_type);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
descriptor_length = b2i(2, emm + 12) & 0x0FFF;
|
||||
pos = 14 + descriptor_length;
|
||||
|
||||
while (pos < emm_length - 4)
|
||||
{
|
||||
// Unique identifier of the public rsa key used for "session_data" encryption
|
||||
memcpy(entitlement_key_id, emm + pos, 8);
|
||||
pos += 8;
|
||||
|
||||
if (get_rsa_key(rdr, entitlement_key_id, &key)) // Key found
|
||||
{
|
||||
cs_hexdump(0, entitlement_key_id, 8, tmp, sizeof(tmp));
|
||||
cs_log_dbg(D_TRACE, "RSA key found (ekid: %s)", tmp);
|
||||
|
||||
// Parse "encrypted_session_data"
|
||||
result = parse_session_data(emm + pos, key, entitlement_session_id, original_network_id, keysAdded);
|
||||
if (result == EMU_OK)
|
||||
{
|
||||
break; // No need to decrypt again with another key
|
||||
}
|
||||
}
|
||||
else // Multiple ekid's can be present in the EMM - Do not exit just yet
|
||||
{
|
||||
cs_hexdump(0, entitlement_key_id, 8, tmp, sizeof(tmp));
|
||||
cs_log_dbg(D_TRACE, "RSA key not found (ekid: %s)", tmp);
|
||||
|
||||
result = EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
pos += 256; // 2048 bits
|
||||
|
||||
if (entitlement_priv_data_loop) // Skip any remaining bytes
|
||||
{
|
||||
pos += 2 + (b2i(2, emm + pos) & 0x0FFF);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int8_t rsa_key_exists(struct s_reader *rdr, const biss2_rsa_key_t *item)
|
||||
{
|
||||
LL_ITER itr;
|
||||
biss2_rsa_key_t *data;
|
||||
|
||||
itr = ll_iter_create(rdr->ll_biss2_rsa_keys);
|
||||
while ((data = ll_iter_next(&itr)))
|
||||
{
|
||||
if (data->ekid == item->ekid)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t biss_read_pem(struct s_reader *rdr, uint8_t max_keys)
|
||||
{
|
||||
FILE *fp_pri = NULL;
|
||||
//FILE *fp_pub = NULL;
|
||||
|
||||
char tmp[256];
|
||||
uint8_t hash[32], *der = NULL;
|
||||
uint16_t i, length, count = 0;;
|
||||
biss2_rsa_key_t *new_item;
|
||||
|
||||
if (!rdr->ll_biss2_rsa_keys)
|
||||
{
|
||||
rdr->ll_biss2_rsa_keys = ll_create("ll_biss2_rsa_keys");
|
||||
}
|
||||
|
||||
for (i = 0; i < max_keys; i++)
|
||||
{
|
||||
if (!cs_malloc(&new_item, sizeof(biss2_rsa_key_t)))
|
||||
{
|
||||
break; // No memory available (?) - Exit
|
||||
}
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "%sbiss2_private_%02d.pem", emu_keyfile_path, i);
|
||||
if ((fp_pri = fopen(tmp, "r")) == NULL)
|
||||
{
|
||||
continue; // File does not exist
|
||||
}
|
||||
|
||||
cs_log("Reading RSA key from: biss2_private_%02d.pem", i);
|
||||
|
||||
// Read RSA private key
|
||||
if ((new_item->key = PEM_read_RSAPrivateKey(fp_pri, NULL, NULL, NULL)) == NULL)
|
||||
{
|
||||
cs_log("Error reading RSA private key");
|
||||
continue;
|
||||
}
|
||||
|
||||
fclose(fp_pri);
|
||||
|
||||
// Write public key in PEM formatted file
|
||||
/*snprintf(tmp, sizeof(tmp), "%sbiss2_public_%02d.pem", emu_keyfile_path, i);
|
||||
if ((fp_pub = fopen(tmp, "w")) != NULL)
|
||||
{
|
||||
PEM_write_RSA_PUBKEY(fp_pub, item->key);
|
||||
fclose(fp_pub);
|
||||
}*/
|
||||
|
||||
// Write public key in DER formatted file
|
||||
/*snprintf(tmp, sizeof(tmp), "%sbiss2_public_%02d.der", emu_keyfile_path, i);
|
||||
if ((fp_pub = fopen(tmp, "wb")) != NULL)
|
||||
{
|
||||
i2d_RSA_PUBKEY_fp(fp_pub, item->key);
|
||||
fclose(fp_pub);
|
||||
}*/
|
||||
|
||||
// Encode RSA public key into DER format
|
||||
if ((length = i2d_RSA_PUBKEY(new_item->key, &der)) <= 0)
|
||||
{
|
||||
cs_log("Error encoding to DER format");
|
||||
NULLFREE(der);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create SHA256 digest
|
||||
EVP_MD_CTX *mdctx;
|
||||
if ((mdctx = EVP_MD_CTX_create()) == NULL)
|
||||
{
|
||||
NULLFREE(der);
|
||||
continue;
|
||||
}
|
||||
|
||||
EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL);
|
||||
EVP_DigestUpdate(mdctx, der, length);
|
||||
EVP_DigestFinal_ex(mdctx, hash, NULL);
|
||||
EVP_MD_CTX_destroy(mdctx);
|
||||
|
||||
NULLFREE(der);
|
||||
memcpy(new_item->ekid, hash, 8);
|
||||
|
||||
// Add new RSA key, if not already present
|
||||
if (!rsa_key_exists(rdr, new_item))
|
||||
{
|
||||
ll_append(rdr->ll_biss2_rsa_keys, new_item);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#endif // WITH_EMU
|
||||
22
module-emulator-biss.h
Executable file
22
module-emulator-biss.h
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef MODULE_EMULATOR_BISS_H
|
||||
#define MODULE_EMULATOR_BISS_H
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
#define BISS2_MAX_RSA_KEYS 16
|
||||
|
||||
typedef struct biss2_rsa_key
|
||||
{
|
||||
uint8_t ekid[8];
|
||||
RSA *key;
|
||||
} biss2_rsa_key_t;
|
||||
|
||||
int8_t biss_ecm(struct s_reader *rdr, const uint8_t *ecm, uint16_t caid, uint16_t ecm_pid, uint8_t *dw, EXTENDED_CW *cw_ex);
|
||||
int8_t biss_emm(struct s_reader *rdr, const uint8_t *emm, uint32_t *keysAdded);
|
||||
uint16_t biss_read_pem(struct s_reader *rdr, uint8_t max_keys);
|
||||
|
||||
#endif // WITH_EMU
|
||||
|
||||
#endif // MODULE_EMULATOR_BISS_H
|
||||
688
module-emulator-cryptoworks.c
Executable file
688
module-emulator-cryptoworks.c
Executable file
|
|
@ -0,0 +1,688 @@
|
|||
#define MODULE_LOG_PREFIX "emu"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
#include "cscrypt/des.h"
|
||||
#include "module-emulator-osemu.h"
|
||||
|
||||
// Cryptoworks EMU
|
||||
|
||||
static int8_t get_key(uint8_t *buf,uint32_t ident, uint8_t keyIndex, uint32_t keyLength, uint8_t isCriticalKey)
|
||||
{
|
||||
char keyName[EMU_MAX_CHAR_KEYNAME];
|
||||
uint32_t tmp;
|
||||
|
||||
if ((ident >> 4) == 0xD02A)
|
||||
{
|
||||
keyIndex &= 0xFE; // map to even number key indexes
|
||||
}
|
||||
|
||||
if ((ident >> 4) == 0xD00C)
|
||||
{
|
||||
ident = 0x0D00C0; // map provider C? to C0
|
||||
}
|
||||
else if (keyIndex == 6 && ((ident >> 8) == 0x0D05))
|
||||
{
|
||||
ident = 0x0D0504; // always use provider 04 system key
|
||||
}
|
||||
|
||||
tmp = keyIndex;
|
||||
snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%.2X", tmp);
|
||||
|
||||
if (emu_find_key('W', ident, 0, keyName, buf, keyLength, isCriticalKey, 0, 0, NULL))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const uint8_t cw_sbox1[64] =
|
||||
{
|
||||
0xD8, 0xD7, 0x83, 0x3D, 0x1C, 0x8A, 0xF0, 0xCF, 0x72, 0x4C, 0x4D, 0xF2, 0xED, 0x33, 0x16, 0xE0,
|
||||
0x8F, 0x28, 0x7C, 0x82, 0x62, 0x37, 0xAF, 0x59, 0xB7, 0xE0, 0x00, 0x3F, 0x09, 0x4D, 0xF3, 0x94,
|
||||
0x16, 0xA5, 0x58, 0x83, 0xF2, 0x4F, 0x67, 0x30, 0x49, 0x72, 0xBF, 0xCD, 0xBE, 0x98, 0x81, 0x7F,
|
||||
0xA5, 0xDA, 0xA7, 0x7F, 0x89, 0xC8, 0x78, 0xA7, 0x8C, 0x05, 0x72, 0x84, 0x52, 0x72, 0x4D, 0x38
|
||||
};
|
||||
|
||||
static const uint8_t cw_sbox2[64] =
|
||||
{
|
||||
0xD8, 0x35, 0x06, 0xAB, 0xEC, 0x40, 0x79, 0x34, 0x17, 0xFE, 0xEA, 0x47, 0xA3, 0x8F, 0xD5, 0x48,
|
||||
0x0A, 0xBC, 0xD5, 0x40, 0x23, 0xD7, 0x9F, 0xBB, 0x7C, 0x81, 0xA1, 0x7A, 0x14, 0x69, 0x6A, 0x96,
|
||||
0x47, 0xDA, 0x7B, 0xE8, 0xA1, 0xBF, 0x98, 0x46, 0xB8, 0x41, 0x45, 0x9E, 0x5E, 0x20, 0xB2, 0x35,
|
||||
0xE4, 0x2F, 0x9A, 0xB5, 0xDE, 0x01, 0x65, 0xF8, 0x0F, 0xB2, 0xD2, 0x45, 0x21, 0x4E, 0x2D, 0xDB
|
||||
};
|
||||
|
||||
static const uint8_t cw_sbox3[64] =
|
||||
{
|
||||
0xDB, 0x59, 0xF4, 0xEA, 0x95, 0x8E, 0x25, 0xD5, 0x26, 0xF2, 0xDA, 0x1A, 0x4B, 0xA8, 0x08, 0x25,
|
||||
0x46, 0x16, 0x6B, 0xBF, 0xAB, 0xE0, 0xD4, 0x1B, 0x89, 0x05, 0x34, 0xE5, 0x74, 0x7B, 0xBB, 0x44,
|
||||
0xA9, 0xC6, 0x18, 0xBD, 0xE6, 0x01, 0x69, 0x5A, 0x99, 0xE0, 0x87, 0x61, 0x56, 0x35, 0x76, 0x8E,
|
||||
0xF7, 0xE8, 0x84, 0x13, 0x04, 0x7B, 0x9B, 0xA6, 0x7A, 0x1F, 0x6B, 0x5C, 0xA9, 0x86, 0x54, 0xF9
|
||||
};
|
||||
|
||||
static const uint8_t cw_sbox4[64] =
|
||||
{
|
||||
0xBC, 0xC1, 0x41, 0xFE, 0x42, 0xFB, 0x3F, 0x10, 0xB5, 0x1C, 0xA6, 0xC9, 0xCF, 0x26, 0xD1, 0x3F,
|
||||
0x02, 0x3D, 0x19, 0x20, 0xC1, 0xA8, 0xBC, 0xCF, 0x7E, 0x92, 0x4B, 0x67, 0xBC, 0x47, 0x62, 0xD0,
|
||||
0x60, 0x9A, 0x9E, 0x45, 0x79, 0x21, 0x89, 0xA9, 0xC3, 0x64, 0x74, 0x9A, 0xBC, 0xDB, 0x43, 0x66,
|
||||
0xDF, 0xE3, 0x21, 0xBE, 0x1E, 0x16, 0x73, 0x5D, 0xA2, 0xCD, 0x8C, 0x30, 0x67, 0x34, 0x9C, 0xCB
|
||||
};
|
||||
|
||||
static const uint8_t AND_bit1[8] = { 0x00, 0x40, 0x04, 0x80, 0x21, 0x10, 0x02, 0x08 };
|
||||
static const uint8_t AND_bit2[8] = { 0x80, 0x08, 0x01, 0x40, 0x04, 0x20, 0x10, 0x02 };
|
||||
static const uint8_t AND_bit3[8] = { 0x82, 0x40, 0x01, 0x10, 0x00, 0x20, 0x04, 0x08 };
|
||||
static const uint8_t AND_bit4[8] = { 0x02, 0x10, 0x04, 0x40, 0x80, 0x08, 0x01, 0x20 };
|
||||
|
||||
static void swap_key(uint8_t *key)
|
||||
{
|
||||
uint8_t k[8];
|
||||
memcpy(k, key, 8);
|
||||
memcpy(key, key + 8, 8);
|
||||
memcpy(key + 8, k, 8);
|
||||
}
|
||||
|
||||
static void swap_data(uint8_t *k)
|
||||
{
|
||||
uint8_t d[4];
|
||||
memcpy(d, k + 4, 4);
|
||||
memcpy(k + 4, k, 4);
|
||||
memcpy(k, d, 4);
|
||||
}
|
||||
|
||||
static void des_round(uint8_t *d, uint8_t *k)
|
||||
{
|
||||
uint8_t aa[44] =
|
||||
{
|
||||
1, 0, 3, 1, 2, 2, 3, 2, 1, 3, 1, 1, 3, 0, 1, 2, 3, 1, 3, 2, 2, 0,
|
||||
7, 6, 5, 4, 7, 6, 5, 7, 6, 5, 6, 7, 5, 7, 5, 7, 6, 6, 7, 5, 4, 4
|
||||
};
|
||||
|
||||
uint8_t bb[44] =
|
||||
{
|
||||
0x80, 0x08, 0x10, 0x02, 0x08, 0x40, 0x01, 0x20, 0x40, 0x80, 0x04,
|
||||
0x10, 0x04, 0x01, 0x01, 0x02, 0x20, 0x20, 0x02, 0x01, 0x80, 0x04,
|
||||
0x02, 0x02, 0x08, 0x02, 0x10, 0x80, 0x01, 0x20, 0x08, 0x80, 0x01,
|
||||
0x08, 0x40, 0x01, 0x02, 0x80, 0x10, 0x40, 0x40, 0x10, 0x08, 0x01
|
||||
};
|
||||
|
||||
uint8_t ff[4] = { 0x02, 0x10, 0x04, 0x04};
|
||||
uint8_t l[24] = { 0, 2, 4, 6, 7, 5, 3, 1, 4, 5, 6, 7, 7, 6, 5, 4, 7, 4, 5, 6, 4, 7, 6, 5 };
|
||||
|
||||
uint8_t des_td[8], i, o, n, c = 1, m = 0, r = 0;
|
||||
uint8_t *a = aa, *b = bb, *f = ff, *p1 = l, *p2 = l + 8, *p3 = l + 16;
|
||||
|
||||
for (m = 0; m < 2; m++)
|
||||
{
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
des_td[*p1++] = (m) ? ((d[*p2++] * 2) & 0x3F) | ((d[*p3++] & 0x80) ? 0x01 : 0x00) :
|
||||
(d[*p2++] / 2) | ((d[*p3++] & 0x01) ? 0x80 : 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
c = (c) ? 0 : 1;
|
||||
r = (c) ? 6 : 7;
|
||||
n = (i) ? i - 1 : 1;
|
||||
o = (c) ? ((k[n] & *f++) ? 1 : 0) : des_td[n];
|
||||
|
||||
for (m = 1; m < r; m++)
|
||||
{
|
||||
o = (c) ? (o * 2) | ((k[*a++] & *b++) ? 0x01 : 0x00) : (o / 2) | ((k[*a++] & *b++) ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
n = (i) ? n + 1 : 0;
|
||||
des_td[n] = (c) ? des_td[n] ^ o : (o ^ des_td[n]) / 4;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
d[0] ^= (AND_bit1[i] & cw_sbox1[des_td[i]]);
|
||||
d[1] ^= (AND_bit2[i] & cw_sbox2[des_td[i]]);
|
||||
d[2] ^= (AND_bit3[i] & cw_sbox3[des_td[i]]);
|
||||
d[3] ^= (AND_bit4[i] & cw_sbox4[des_td[i]]);
|
||||
}
|
||||
|
||||
swap_data(d);
|
||||
}
|
||||
|
||||
static void cw_48_key(uint8_t *inkey, uint8_t *outkey, uint8_t algotype)
|
||||
{
|
||||
uint8_t round_counter, i = 8;
|
||||
uint8_t *key128 = inkey;
|
||||
uint8_t *key48 = inkey + 0x10;
|
||||
|
||||
round_counter = 7 - (algotype & 7);
|
||||
|
||||
memset(outkey, 0, 16);
|
||||
memcpy(outkey, key48, 6);
|
||||
|
||||
for ( ; i > round_counter; i--)
|
||||
{
|
||||
if (i > 1)
|
||||
{
|
||||
outkey[i - 2] = key128[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ls_des_key(uint8_t *key, uint8_t rotate_counter)
|
||||
{
|
||||
uint8_t i, n;
|
||||
uint8_t rnd[] = { 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 1 };
|
||||
uint16_t k[8];
|
||||
|
||||
n = rnd[rotate_counter];
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
k[i] = key[i];
|
||||
}
|
||||
|
||||
for (i = 1; i < n + 1; i++)
|
||||
{
|
||||
k[7] = (k[7] * 2) | ((k[4] & 0x008) ? 1 : 0);
|
||||
k[6] = (k[6] * 2) | ((k[7] & 0xF00) ? 1 : 0);
|
||||
k[7] &= 0xFF;
|
||||
|
||||
k[5] = (k[5] * 2) | ((k[6] & 0xF00) ? 1 : 0);
|
||||
k[6] &= 0xFF;
|
||||
|
||||
k[4] = ((k[4] * 2) | ((k[5] & 0xF00) ? 1 : 0)) & 0xFF;
|
||||
k[5] &= 0xFF;
|
||||
|
||||
k[3] = (k[3] * 2) | ((k[0] & 0x008) ? 1 : 0);
|
||||
k[2] = (k[2] * 2) | ((k[3] & 0xF00) ? 1 : 0);
|
||||
k[3] &= 0xFF;
|
||||
|
||||
k[1] = (k[1] * 2) | ((k[2] & 0xF00) ? 1 : 0);
|
||||
k[2] &= 0xFF;
|
||||
|
||||
k[0] = ((k[0] * 2) | ((k[1] & 0xF00) ? 1 : 0)) & 0xFF;
|
||||
k[1] &= 0xFF;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
key[i] = (uint8_t) k[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void rs_des_key(uint8_t *k, uint8_t rotate_counter)
|
||||
{
|
||||
uint8_t i, c;
|
||||
|
||||
for (i = 1; i < rotate_counter + 1; i++)
|
||||
{
|
||||
c = (k[3] & 0x10) ? 0x80 : 0;
|
||||
k[3] /= 2;
|
||||
|
||||
if (k[2] & 1)
|
||||
{
|
||||
k[3] |= 0x80;
|
||||
}
|
||||
|
||||
k[2] /= 2;
|
||||
|
||||
if (k[1] & 1)
|
||||
{
|
||||
k[2] |= 0x80;
|
||||
}
|
||||
|
||||
k[1] /= 2;
|
||||
|
||||
if (k[0] & 1)
|
||||
{
|
||||
k[1] |= 0x80;
|
||||
}
|
||||
|
||||
k[0] /= 2;
|
||||
k[0] |= c ;
|
||||
c = (k[7] & 0x10) ? 0x80 : 0;
|
||||
k[7] /= 2;
|
||||
|
||||
if (k[6] & 1)
|
||||
{
|
||||
k[7] |= 0x80;
|
||||
}
|
||||
|
||||
k[6] /= 2;
|
||||
|
||||
if (k[5] & 1)
|
||||
{
|
||||
k[6] |= 0x80;
|
||||
}
|
||||
|
||||
k[5] /= 2;
|
||||
|
||||
if (k[4] & 1)
|
||||
{
|
||||
k[5] |= 0x80;
|
||||
}
|
||||
|
||||
k[4] /= 2;
|
||||
k[4] |= c;
|
||||
}
|
||||
}
|
||||
|
||||
static void rs_des_subkey(uint8_t *k, uint8_t rotate_counter)
|
||||
{
|
||||
uint8_t rnd[] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
|
||||
|
||||
rs_des_key(k, rnd[rotate_counter]);
|
||||
}
|
||||
|
||||
static void prep_key(uint8_t *key)
|
||||
{
|
||||
int32_t round_counter = 6, i, a;
|
||||
uint8_t DES_key[8], j;
|
||||
|
||||
key[7] = 6;
|
||||
memset(DES_key, 0, 8);
|
||||
|
||||
do
|
||||
{
|
||||
a = 7;
|
||||
i = key[7];
|
||||
j = key[round_counter];
|
||||
|
||||
do
|
||||
{
|
||||
DES_key[i] = ( (DES_key[i] * 2) | ((j & 1) ? 1 : 0) ) & 0xFF;
|
||||
|
||||
j /= 2;
|
||||
i--;
|
||||
|
||||
if (i < 0)
|
||||
{
|
||||
i = 6;
|
||||
}
|
||||
a--;
|
||||
}
|
||||
while (a >= 0);
|
||||
|
||||
key[7] = i;
|
||||
round_counter--;
|
||||
}
|
||||
while (round_counter >= 0);
|
||||
|
||||
a = DES_key[4];
|
||||
DES_key[4] = DES_key[6];
|
||||
DES_key[6] = a;
|
||||
DES_key[7] = (DES_key[3] * 16) & 0xFF;
|
||||
|
||||
memcpy(key, DES_key, 8);
|
||||
rs_des_key(key, 4);
|
||||
}
|
||||
|
||||
static void l2_des(uint8_t *data, uint8_t *key, uint8_t algo)
|
||||
{
|
||||
uint8_t i, k0[22], k1[22];
|
||||
|
||||
memcpy(k0, key, 22);
|
||||
memcpy(k1, key, 22);
|
||||
|
||||
cw_48_key(k0, k1, algo);
|
||||
prep_key(k1);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
ls_des_key(k1, 15);
|
||||
des_round(data, k1);
|
||||
}
|
||||
}
|
||||
|
||||
static void r2_des(uint8_t *data, uint8_t *key, uint8_t algo)
|
||||
{
|
||||
uint8_t i, k0[22], k1[22];
|
||||
|
||||
memcpy(k0, key, 22);
|
||||
memcpy(k1, key, 22);
|
||||
|
||||
cw_48_key(k0, k1, algo);
|
||||
prep_key(k1);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
ls_des_key(k1, 15);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
des_round(data, k1);
|
||||
rs_des_subkey(k1, 1);
|
||||
}
|
||||
|
||||
swap_data(data);
|
||||
}
|
||||
|
||||
static void cw_des(uint8_t *data, uint8_t *inkey, uint8_t m)
|
||||
{
|
||||
uint8_t key[22], i;
|
||||
|
||||
memcpy(key, inkey + 9, 8);
|
||||
prep_key(key);
|
||||
|
||||
for (i = 16; i > 0; i--)
|
||||
{
|
||||
if (m == 1)
|
||||
{
|
||||
ls_des_key(key, (uint8_t) (i - 1));
|
||||
}
|
||||
|
||||
des_round( data ,key);
|
||||
|
||||
if (m == 0)
|
||||
{
|
||||
rs_des_subkey(key, (uint8_t) (i - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cw_dec_enc(uint8_t *d, uint8_t *k, uint8_t a, uint8_t m)
|
||||
{
|
||||
uint8_t n = m & 1;
|
||||
|
||||
l2_des(d, k, a);
|
||||
cw_des(d, k, n);
|
||||
r2_des(d, k, a);
|
||||
|
||||
if (m & 2)
|
||||
{
|
||||
swap_key(k);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t process_nano80(uint8_t *data, uint32_t caid, int32_t provider, uint8_t *opKey,
|
||||
uint8_t nanoLength, uint8_t nano80Algo)
|
||||
{
|
||||
int32_t i, j;
|
||||
uint8_t key[16], desKey[16], t[8], dat1[8], dat2[8], k0D00C000[16];
|
||||
|
||||
if (nanoLength < 11)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (caid == 0x0D00 && provider != 0xA0 && !get_key(k0D00C000, 0x0D00C0, 0, 16, 1))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nano80Algo > 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(t, 0, 8);
|
||||
memcpy(dat1, data, 8);
|
||||
|
||||
if(caid == 0x0D00 && provider != 0xA0)
|
||||
{
|
||||
memcpy(key, k0D00C000, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(key, opKey, 16);
|
||||
}
|
||||
|
||||
des_ecb3_decrypt(data, key);
|
||||
memcpy(desKey, data, 8);
|
||||
memcpy(data, dat1, 8);
|
||||
|
||||
if (caid == 0x0D00 && provider != 0xA0)
|
||||
{
|
||||
memcpy(key, &k0D00C000[8], 8);
|
||||
memcpy(&key[8], k0D00C000, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(key, &opKey[8], 8);
|
||||
memcpy(&key[8], opKey, 8);
|
||||
}
|
||||
|
||||
des_ecb3_decrypt(data, key);
|
||||
memcpy(&desKey[8], data, 8);
|
||||
|
||||
for (i = 8; i + 7 < nanoLength; i += 8)
|
||||
{
|
||||
memcpy(dat1, &data[i], 8);
|
||||
memcpy(dat2, dat1, 8);
|
||||
memcpy(key, desKey, 16);
|
||||
des_ecb3_decrypt(dat1, key);
|
||||
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
dat1[j] ^= t[j];
|
||||
}
|
||||
|
||||
memcpy(&data[i], dat1, 8);
|
||||
memcpy(t, dat2, 8);
|
||||
}
|
||||
|
||||
return data[10] + 5;
|
||||
}
|
||||
|
||||
static void cryptoworks_signature(const uint8_t *data, uint32_t length, uint8_t *key, uint8_t *signature)
|
||||
{
|
||||
uint32_t i, sigPos;
|
||||
int8_t algo, first;
|
||||
|
||||
algo = data[0] & 7;
|
||||
if (algo == 7)
|
||||
{
|
||||
algo = 6;
|
||||
}
|
||||
|
||||
memset(signature, 0, 8);
|
||||
first = 1;
|
||||
sigPos = 0;
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
signature[sigPos] ^= data[i];
|
||||
sigPos++;
|
||||
|
||||
if (sigPos > 7)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
l2_des(signature, key, algo);
|
||||
}
|
||||
|
||||
cw_des(signature, key, 1);
|
||||
|
||||
sigPos = 0;
|
||||
first = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (sigPos > 0)
|
||||
{
|
||||
cw_des(signature, key, 1);
|
||||
}
|
||||
|
||||
r2_des(signature, key, algo);
|
||||
}
|
||||
|
||||
static void decrypt_des(uint8_t *data, uint8_t algo, uint8_t *key)
|
||||
{
|
||||
int32_t i;
|
||||
uint8_t k[22], t[8];
|
||||
|
||||
algo &= 7;
|
||||
|
||||
if (algo < 7)
|
||||
{
|
||||
cw_dec_enc(data, key, algo, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(k, key, 22);
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
cw_dec_enc(data, k, algo, i & 1);
|
||||
|
||||
memcpy(t, k, 8);
|
||||
memcpy(k, k + 8, 8);
|
||||
memcpy(k + 8, t, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int8_t cryptoworks_ecm(uint32_t caid, uint8_t *ecm, uint8_t *cw)
|
||||
{
|
||||
int32_t provider = -1;
|
||||
uint8_t keyIndex = 0, nanoLength, newEcmLength, key[22], signature[8], nano80Algo = 1;
|
||||
uint16_t i, j, ecmLen = SCT_LEN(ecm);
|
||||
uint32_t ident;
|
||||
|
||||
if (ecmLen < 8)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (ecm[7] != ecmLen - 8)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
memset(key, 0, 22);
|
||||
|
||||
for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2)
|
||||
{
|
||||
if (ecm[i] == 0x83 && i + 2 < ecmLen)
|
||||
{
|
||||
provider = ecm[i + 2] & 0xFC;
|
||||
keyIndex = ecm[i + 2] & 3;
|
||||
keyIndex = keyIndex ? 1 : 0;
|
||||
}
|
||||
else if (ecm[i] == 0x84 && i + 3 < ecmLen)
|
||||
{
|
||||
//nano80Provider = ecm[i + 2] & 0xFC;
|
||||
//nano80KeyIndex = ecm[i + 2] & 3;
|
||||
//nano80KeyIndex = nano80KeyIndex ? 1 : 0;
|
||||
nano80Algo = ecm[i + 3];
|
||||
}
|
||||
}
|
||||
|
||||
if (provider < 0)
|
||||
{
|
||||
switch (caid)
|
||||
{
|
||||
case 0x0D00:
|
||||
provider = 0xC0;
|
||||
break;
|
||||
|
||||
case 0x0D02:
|
||||
provider = 0xA0;
|
||||
break;
|
||||
|
||||
case 0x0D03:
|
||||
provider = 0x04;
|
||||
break;
|
||||
|
||||
case 0x0D05:
|
||||
provider = 0x04;
|
||||
break;
|
||||
|
||||
default:
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
ident = (caid << 8) | provider;
|
||||
|
||||
if (!get_key(key, ident, keyIndex, 16, 1))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!get_key(&key[16], ident, 6, 6, 1))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2)
|
||||
{
|
||||
if (ecm[i] == 0x80 && i + 2 + 7 < ecmLen && i + 2 + ecm[i + 1] <= ecmLen &&
|
||||
(provider == 0xA0 || provider == 0xC0 || provider == 0xC4 || provider == 0xC8))
|
||||
{
|
||||
nanoLength = ecm[i + 1];
|
||||
newEcmLength = process_nano80(ecm + i + 2, caid, provider, key, nanoLength, nano80Algo);
|
||||
|
||||
if (newEcmLength == 0 || newEcmLength > ecmLen - (i + 2 + 3))
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
ecm[i + 2 + 3] = 0x81;
|
||||
ecm[i + 2 + 4] = 0x70;
|
||||
ecm[i + 2 + 5] = newEcmLength;
|
||||
ecm[i + 2 + 6] = 0x81;
|
||||
ecm[i + 2 + 7] = 0xFF;
|
||||
|
||||
return cryptoworks_ecm(caid, ecm + i + 2 + 3, cw);
|
||||
}
|
||||
}
|
||||
|
||||
if (ecmLen - 15 < 1)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
cryptoworks_signature(ecm + 5, ecmLen - 15, key, signature);
|
||||
|
||||
for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2)
|
||||
{
|
||||
switch (ecm[i])
|
||||
{
|
||||
case 0xDA:
|
||||
case 0xDB:
|
||||
case 0xDC:
|
||||
if (i + 2 + ecm[i + 1] > ecmLen)
|
||||
{
|
||||
break;
|
||||
}
|
||||
for (j = 0; j + 7 < ecm[i + 1]; j += 8)
|
||||
{
|
||||
decrypt_des(&ecm[i + 2 + j], ecm[5], key);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xDF:
|
||||
if (i + 2 + 8 > ecmLen)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (memcmp(&ecm[i + 2], signature, 8))
|
||||
{
|
||||
return EMU_CHECKSUM_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2)
|
||||
{
|
||||
switch (ecm[i])
|
||||
{
|
||||
case 0xDB:
|
||||
if (i + 2 + ecm[i + 1] <= ecmLen && ecm[i + 1] == 16)
|
||||
{
|
||||
memcpy(cw, &ecm[i + 2], 16);
|
||||
return EMU_OK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return EMU_CW_NOT_FOUND;
|
||||
}
|
||||
|
||||
#endif // WITH_EMU
|
||||
10
module-emulator-cryptoworks.h
Executable file
10
module-emulator-cryptoworks.h
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef MODULE_EMULATOR_CRYPTOWORKS_H
|
||||
#define MODULE_EMULATOR_CRYPTOWORKS_H
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
int8_t cryptoworks_ecm(uint32_t caid, uint8_t *ecm, uint8_t *cw);
|
||||
|
||||
#endif // WITH_EMU
|
||||
|
||||
#endif // MODULE_EMULATOR_CRYPTOWORKS_H
|
||||
644
module-emulator-director.c
Executable file
644
module-emulator-director.c
Executable file
|
|
@ -0,0 +1,644 @@
|
|||
#define MODULE_LOG_PREFIX "emu"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
#include "cscrypt/des.h"
|
||||
#include "module-emulator-osemu.h"
|
||||
#include "oscam-aes.h"
|
||||
#include "oscam-string.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
|
||||
// Shared functions
|
||||
|
||||
static uint16_t calculate_checksum(uint8_t *data, uint8_t length)
|
||||
{
|
||||
/*
|
||||
* ECM and EMM checksum calculation
|
||||
* 1. Combine data in 2 byte groups
|
||||
* 2. Add them together
|
||||
* 3. Multiply result by itself (power of 7)
|
||||
* 4. XOR with fixed value 0x17E3
|
||||
*/
|
||||
|
||||
uint8_t i;
|
||||
uint16_t checksum = 0;
|
||||
|
||||
for (i = 0; i < length; i += 2)
|
||||
{
|
||||
checksum += (data[i] << 8) | data[i + 1];
|
||||
}
|
||||
|
||||
checksum = checksum * checksum * checksum * checksum * checksum * checksum * checksum;
|
||||
checksum ^= 0x17E3;
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
static inline int8_t get_key(uint32_t keyIndex, char *keyName, uint8_t *key, uint32_t keyLength)
|
||||
{
|
||||
/*
|
||||
* keyIndex meaning for:
|
||||
* ecm keys --> entitlementId
|
||||
* emm keys --> aeskeyIndex
|
||||
* aes keys --> keyIndex
|
||||
*
|
||||
* keyName meaning for:
|
||||
* ecm keys --> "01"
|
||||
* emm keys --> "MK" or "MK01"
|
||||
* aes keys --> "AES"
|
||||
*/
|
||||
|
||||
return emu_find_key('T', keyIndex, 0, keyName, key, keyLength, 1, 0, 0, NULL);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
|
||||
/*
|
||||
* Director ECM emulator
|
||||
* Supported versions: v4, v5, v6 (not working correctly)
|
||||
*/
|
||||
|
||||
int8_t director_ecm(uint8_t *ecm, uint8_t *dw)
|
||||
{
|
||||
uint8_t nanoType, nanoLength;
|
||||
uint8_t *nanoData;
|
||||
uint32_t pos = 3;
|
||||
uint32_t entitlementId;
|
||||
uint32_t ks[32];
|
||||
uint8_t ecmKey[8];
|
||||
uint16_t ecmLen = SCT_LEN(ecm);
|
||||
|
||||
if (ecmLen < 5)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
nanoType = ecm[pos];
|
||||
nanoLength = ecm[pos + 1];
|
||||
|
||||
if (pos + 2 + nanoLength > ecmLen)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
nanoData = ecm + pos + 2;
|
||||
|
||||
// ECM validation
|
||||
uint16_t payloadChecksum = (nanoData[nanoLength - 2] << 8) | nanoData[nanoLength - 1];
|
||||
uint16_t calculatedChecksum = calculate_checksum(nanoData, nanoLength - 2);
|
||||
|
||||
if (calculatedChecksum != payloadChecksum)
|
||||
{
|
||||
cs_log_dbg(D_READER, "ECM checksum error (%.4X instead of %.4X)", calculatedChecksum, payloadChecksum);
|
||||
return EMU_CHECKSUM_ERROR;
|
||||
}
|
||||
// End of ECM validation
|
||||
|
||||
switch (nanoType)
|
||||
{
|
||||
case 0xEC: // Director v6 (September 2017)
|
||||
{
|
||||
if (nanoLength != 0x28)
|
||||
{
|
||||
cs_log_dbg(D_READER, "WARNING: nanoType EC length (%d) != %d", nanoLength, 0x28);
|
||||
break;
|
||||
}
|
||||
|
||||
entitlementId = b2i(4, nanoData);
|
||||
cs_log_dbg(D_READER, "INFO: Using entitlement id %.4X", entitlementId);
|
||||
|
||||
if (!get_key(entitlementId, "01", ecmKey, 8))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Step 1 - Decrypt DES CBC with ecmKey and iv = { 0 } (equal to nanoED)
|
||||
uint8_t encryptedData[32] = { 0 };
|
||||
memcpy(encryptedData, nanoData + 6, 32);
|
||||
|
||||
uint8_t iv[8] = { 0 };
|
||||
des_cbc_decrypt(encryptedData, iv, ecmKey, 32);
|
||||
|
||||
uint8_t nanoMode = nanoData[5];
|
||||
|
||||
if ((nanoMode & 0x20) == 0) // Old algo
|
||||
{
|
||||
// Step 2 - Create CW (equal to nano ED)
|
||||
dw[0] = encryptedData[0x05];
|
||||
dw[1] = encryptedData[0x19];
|
||||
dw[2] = encryptedData[0x1D];
|
||||
dw[3] = (dw[0] + dw[1] + dw[2]) & 0xFF;
|
||||
dw[4] = encryptedData[0x0B];
|
||||
dw[5] = encryptedData[0x12];
|
||||
dw[6] = encryptedData[0x1A];
|
||||
dw[7] = (dw[4] + dw[5] + dw[6]) & 0xFF;
|
||||
dw[8] = encryptedData[0x16];
|
||||
dw[9] = encryptedData[0x03];
|
||||
dw[10] = encryptedData[0x11];
|
||||
dw[11] = (dw[8] + dw[9] + dw[10]) & 0xFF;
|
||||
dw[12] = encryptedData[0x18];
|
||||
dw[13] = encryptedData[0x10];
|
||||
dw[14] = encryptedData[0x0E];
|
||||
dw[15] = (dw[12] + dw[13] + dw[14]) & 0xFF;
|
||||
|
||||
return EMU_OK;
|
||||
}
|
||||
else // New algo (overencryption with AES)
|
||||
{
|
||||
// Step 2 - Prepare data for AES (it is like the creation of CW in nanoED but swapped each 8 bytes)
|
||||
uint8_t dataEC[16] = { 0 };
|
||||
|
||||
dataEC[0] = encryptedData[0x02];
|
||||
dataEC[1] = encryptedData[0x0E];
|
||||
dataEC[2] = encryptedData[0x10];
|
||||
dataEC[3] = encryptedData[0x18];
|
||||
dataEC[4] = encryptedData[0x09];
|
||||
dataEC[5] = encryptedData[0x11];
|
||||
dataEC[6] = encryptedData[0x03];
|
||||
dataEC[7] = encryptedData[0x16];
|
||||
|
||||
dataEC[8] = encryptedData[0x13];
|
||||
dataEC[9] = encryptedData[0x1A];
|
||||
dataEC[10] = encryptedData[0x12];
|
||||
dataEC[11] = encryptedData[0x0B];
|
||||
dataEC[12] = encryptedData[0x04];
|
||||
dataEC[13] = encryptedData[0x1D];
|
||||
dataEC[14] = encryptedData[0x19];
|
||||
dataEC[15] = encryptedData[0x05];
|
||||
|
||||
// Step 3 - Decrypt AES CBC with new aesKey and iv 2EBD816A5E749A708AE45ADDD84333DE
|
||||
uint8_t aesKeyIndex = nanoMode & 0x1F; // 32 possible AES keys
|
||||
uint8_t aesKey[16] = { 0 };
|
||||
|
||||
char tmpBuffer[33];
|
||||
cs_hexdump(0, aesKey, 16, tmpBuffer, sizeof(tmpBuffer));
|
||||
cs_log_dbg(D_READER, "INFO: Using AES key index: %02X, value: %s", aesKeyIndex, tmpBuffer);
|
||||
|
||||
if (!get_key(aesKeyIndex, "AES", aesKey, 16))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
struct aes_keys aes;
|
||||
aes_set_key(&aes, (char *)aesKey);
|
||||
|
||||
uint8_t ivAes[16] = { 0x2E, 0xBD, 0x81, 0x6A, 0x5E, 0x74, 0x9A, 0x70, 0x8A, 0xE4, 0x5A, 0xDD, 0xD8, 0x43, 0x33, 0xDE };
|
||||
aes_cbc_decrypt(&aes, dataEC, 16, ivAes);
|
||||
|
||||
// Step 4 - Create CW (a simple swap)
|
||||
uint8_t offset;
|
||||
for (offset = 0; offset < 16; offset++)
|
||||
{
|
||||
dw[offset] = dataEC[15 - offset];
|
||||
}
|
||||
|
||||
return EMU_OK;
|
||||
}
|
||||
}
|
||||
|
||||
case 0xED: // Director v5 (September 2016)
|
||||
{
|
||||
if (nanoLength != 0x26)
|
||||
{
|
||||
cs_log_dbg(D_READER, "WARNING: nanoType ED length (%d) != %d", nanoLength, 0x26);
|
||||
break;
|
||||
}
|
||||
|
||||
entitlementId = b2i(4, nanoData);
|
||||
cs_log_dbg(D_READER, "INFO: Using entitlement id %.4X", entitlementId);
|
||||
|
||||
if (!get_key(entitlementId, "01", ecmKey, 8))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
uint8_t encryptedData[32] = { 0 };
|
||||
memcpy(encryptedData, nanoData + 4, 32);
|
||||
|
||||
uint8_t iv[8] = { 0 };
|
||||
des_cbc_decrypt(encryptedData, iv, ecmKey, 32);
|
||||
|
||||
dw[0] = encryptedData[0x05];
|
||||
dw[1] = encryptedData[0x19];
|
||||
dw[2] = encryptedData[0x1D];
|
||||
dw[3] = (dw[0] + dw[1] + dw[2]) & 0xFF;
|
||||
dw[4] = encryptedData[0x0B];
|
||||
dw[5] = encryptedData[0x12];
|
||||
dw[6] = encryptedData[0x1A];
|
||||
dw[7] = (dw[4] + dw[5] + dw[6]) & 0xFF;
|
||||
dw[8] = encryptedData[0x16];
|
||||
dw[9] = encryptedData[0x03];
|
||||
dw[10] = encryptedData[0x11];
|
||||
dw[11] = (dw[8] + dw[9] + dw[10]) & 0xFF;
|
||||
dw[12] = encryptedData[0x18];
|
||||
dw[13] = encryptedData[0x10];
|
||||
dw[14] = encryptedData[0x0E];
|
||||
dw[15] = (dw[12] + dw[13] + dw[14]) & 0xFF;
|
||||
|
||||
return EMU_OK;
|
||||
}
|
||||
|
||||
case 0xEE: // Director v4
|
||||
{
|
||||
if (nanoLength != 0x16)
|
||||
{
|
||||
cs_log_dbg(D_READER, "WARNING: nanoType EE length (%d) != %d", nanoLength, 0x16);
|
||||
break;
|
||||
}
|
||||
|
||||
entitlementId = b2i(4, nanoData);
|
||||
cs_log_dbg(D_READER, "INFO: Using entitlement id %.4X", entitlementId);
|
||||
|
||||
if (!get_key(entitlementId, "01", ecmKey, 8))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
memcpy(dw, nanoData + 4 + 8, 8); // even
|
||||
memcpy(dw + 8, nanoData + 4, 8); // odd
|
||||
|
||||
des_set_key(ecmKey, ks);
|
||||
|
||||
des(dw, ks, 0);
|
||||
des(dw + 8, ks, 0);
|
||||
|
||||
dw[3] = (dw[0] + dw[1] + dw[2]) & 0xFF;
|
||||
dw[7] = (dw[4] + dw[5] + dw[6]) & 0xFF;
|
||||
dw[11] = (dw[8] + dw[9] + dw[10]) & 0xFF;
|
||||
dw[15] = (dw[12] + dw[13] + dw[14]) & 0xFF;
|
||||
|
||||
return EMU_OK;
|
||||
}
|
||||
|
||||
default:
|
||||
cs_log_dbg(D_READER, "WARNING: nanoType %.2X not supported", nanoType);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
pos += 2 + nanoLength;
|
||||
|
||||
} while (pos < ecmLen);
|
||||
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
|
||||
/*
|
||||
* Director EMM emulator
|
||||
* Supported versions: v4, v5, v6 (same as v5)
|
||||
*/
|
||||
|
||||
static const uint8_t MixTable[] =
|
||||
{
|
||||
0x12, 0x78, 0x4B, 0x19, 0x13, 0x80, 0x2F, 0x84, 0x86, 0x4C, 0x09, 0x53, 0x15, 0x79, 0x6B, 0x49,
|
||||
0x10, 0x4D, 0x33, 0x43, 0x18, 0x37, 0x83, 0x38, 0x82, 0x1B, 0x6E, 0x24, 0x2A, 0x85, 0x3C, 0x3D,
|
||||
0x5A, 0x58, 0x55, 0x5D, 0x20, 0x41, 0x65, 0x51, 0x0C, 0x45, 0x63, 0x7F, 0x0F, 0x46, 0x21, 0x7C,
|
||||
0x2C, 0x61, 0x7E, 0x0A, 0x42, 0x57, 0x35, 0x16, 0x87, 0x3B, 0x4F, 0x40, 0x34, 0x22, 0x26, 0x74,
|
||||
0x32, 0x69, 0x44, 0x7A, 0x6A, 0x6D, 0x0D, 0x56, 0x23, 0x2B, 0x5C, 0x72, 0x76, 0x36, 0x28, 0x25,
|
||||
0x2E, 0x52, 0x5B, 0x6C, 0x7D, 0x30, 0x0B, 0x5E, 0x47, 0x1F, 0x7B, 0x31, 0x3E, 0x11, 0x77, 0x1E,
|
||||
0x60, 0x75, 0x54, 0x27, 0x50, 0x17, 0x70, 0x59, 0x1A, 0x2D, 0x4A, 0x67, 0x3A, 0x5F, 0x68, 0x08,
|
||||
0x4E, 0x3F, 0x29, 0x6F, 0x81, 0x71, 0x39, 0x64, 0x48, 0x66, 0x73, 0x14, 0x0E, 0x1D, 0x62, 0x1C
|
||||
};
|
||||
|
||||
/*
|
||||
static void rotate_bytes(uint8_t *in, int8_t n)
|
||||
{
|
||||
if (n > 1)
|
||||
{
|
||||
uint8_t *e = in + n - 1;
|
||||
do
|
||||
{
|
||||
uint8_t temp = *in;
|
||||
*in++ = *e;
|
||||
*e-- = temp;
|
||||
}
|
||||
while (in < e);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
static void decrypt_ecm_key(uint8_t *emmKey, uint8_t *tagData, uint8_t *ecmKey)
|
||||
{
|
||||
uint8_t temp, *e, *payLoad, iv[8] = { 0 };
|
||||
|
||||
//rotate_bytes(emmKey, 8);
|
||||
|
||||
e = emmKey + 8 - 1;
|
||||
do
|
||||
{
|
||||
temp = *emmKey;
|
||||
*emmKey++ = *e;
|
||||
*e-- = temp;
|
||||
}
|
||||
while (emmKey < e);
|
||||
|
||||
payLoad = tagData + 4 + 5;
|
||||
des_cbc_decrypt(payLoad, iv, emmKey, 16);
|
||||
|
||||
ecmKey[0] = payLoad[0x0F];
|
||||
ecmKey[1] = payLoad[0x01];
|
||||
ecmKey[2] = payLoad[0x0B];
|
||||
ecmKey[3] = payLoad[0x03];
|
||||
ecmKey[4] = payLoad[0x0E];
|
||||
ecmKey[5] = payLoad[0x04];
|
||||
ecmKey[6] = payLoad[0x0A];
|
||||
ecmKey[7] = payLoad[0x08];
|
||||
}
|
||||
|
||||
static int8_t parse_emm_nano_tags(uint8_t *data, uint32_t length, uint8_t keyIndex, uint32_t *keysAdded)
|
||||
{
|
||||
uint8_t tagType, tagLength, *tagData, blockIndex, emmKey[8], tagDataDecrypted[16][8];
|
||||
uint32_t pos = 0, entitlementId, ks[32];
|
||||
int32_t i, k;
|
||||
char keyValue[17];
|
||||
|
||||
if (length < 2)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
while (pos < length)
|
||||
{
|
||||
tagType = data[pos];
|
||||
tagLength = data[pos+1];
|
||||
|
||||
if (pos + 2 + tagLength > length)
|
||||
{
|
||||
return EMU_CORRUPT_DATA;
|
||||
}
|
||||
|
||||
tagData = data + pos + 2;
|
||||
|
||||
switch (tagType)
|
||||
{
|
||||
case 0xE4: // EMM_TAG_SECURITY_TABLE_DESCRIPTOR (ram emm keys)
|
||||
{
|
||||
uint8_t tagMode = data[pos + 2];
|
||||
|
||||
switch (tagMode)
|
||||
{
|
||||
case 0x01: // keySet 01 (MK01)
|
||||
{
|
||||
if (tagLength != 0x8A)
|
||||
{
|
||||
cs_log_dbg(D_READER, "WARNING: nanoTag E4 length (%d) != %d", tagLength, 0x8A);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (!get_key(keyIndex, "MK01", emmKey, 8))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
uint8_t iv[8] = { 0 };
|
||||
uint8_t *tagPayload = tagData + 2;
|
||||
des_cbc_decrypt(tagPayload, iv, emmKey, 136);
|
||||
|
||||
for (k = 0; k < 16; k++) // loop 16 keys
|
||||
{
|
||||
for (i = 0; i < 8; i++) // loop 8 bytes of key
|
||||
{
|
||||
tagDataDecrypted[k][i] = tagPayload[MixTable[8 * k + i]];
|
||||
}
|
||||
}
|
||||
|
||||
blockIndex = tagData[1] & 0x03;
|
||||
|
||||
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
emu_set_key('T', (blockIndex << 4) + i, "MK01", tagDataDecrypted[i], 8, 0, NULL, NULL);
|
||||
}
|
||||
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFF: // keySet FF (MK)
|
||||
{
|
||||
if (tagLength != 0x82)
|
||||
{
|
||||
cs_log_dbg(D_READER, "WARNING: nanoTag E4 length (%d) != %d", tagLength, 0x82);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (!get_key(keyIndex, "MK", emmKey, 8))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
des_set_key(emmKey, ks);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
des(tagData + 2 + (i * 8), ks, 0);
|
||||
}
|
||||
|
||||
blockIndex = tagData[1] & 0x03;
|
||||
|
||||
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
emu_set_key('T', (blockIndex << 4) + i, "MK", tagData + 2 + (i * 8), 8, 0, NULL, NULL);
|
||||
}
|
||||
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
cs_log_dbg(D_READER, "WARNING: nanoTag E4 mode %.2X not supported", tagMode);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xE1: // EMM_TAG_EVENT_ENTITLEMENT_DESCRIPTOR (ecm keys)
|
||||
{
|
||||
uint8_t tagMode = data[pos + 2 + 4];
|
||||
|
||||
switch (tagMode)
|
||||
{
|
||||
case 0x00: // ecm keys from mode FF
|
||||
{
|
||||
if (tagLength != 0x12)
|
||||
{
|
||||
cs_log_dbg(D_READER, "WARNING: nanoTag E1 length (%d) != %d", tagLength, 0x12);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
entitlementId = b2i(4, tagData);
|
||||
|
||||
if (!get_key(keyIndex, "MK", emmKey, 8))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
des_set_key(emmKey, ks);
|
||||
des(tagData + 4 + 5, ks, 0);
|
||||
uint8_t ecmKeyChk[1] = { 0 };
|
||||
memcpy(ecmKeyChk, tagData + 4 + 5 + 7, 1);
|
||||
|
||||
if (ecmKeyChk[0] != 0x00) // check if key looks valid (last byte 0x00)
|
||||
{
|
||||
cs_log_dbg(D_READER, "Key rejected from EMM (looks invalid)");
|
||||
return EMU_KEY_REJECTED;
|
||||
}
|
||||
|
||||
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||||
if (emu_update_key('T', entitlementId, "01", tagData + 4 + 5, 8, 1, NULL))
|
||||
{
|
||||
(*keysAdded)++;
|
||||
cs_hexdump(0, tagData + 4 + 5, 8, keyValue, sizeof(keyValue));
|
||||
cs_log("Key found in EMM: T %.8X 01 %s", entitlementId, keyValue);
|
||||
}
|
||||
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x01: // ecm keys from mode 01
|
||||
{
|
||||
if (tagLength != 0x1A)
|
||||
{
|
||||
cs_log_dbg(D_READER, "WARNING: nanoTag E1 length (%d) != %d", tagLength, 0x1A);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
entitlementId = b2i(4, tagData);
|
||||
|
||||
if (!get_key(keyIndex, "MK01", emmKey, 8))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
uint8_t ecmKey[8] = { 0 };
|
||||
decrypt_ecm_key(emmKey, tagData, ecmKey);
|
||||
|
||||
if (ecmKey[7] != 0x00) // check if key looks valid (last byte 0x00)
|
||||
{
|
||||
cs_log_dbg(D_READER, "Key rejected from EMM (looks invalid)");
|
||||
return EMU_KEY_REJECTED;
|
||||
}
|
||||
|
||||
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||||
if (emu_update_key('T', entitlementId, "01", ecmKey, 8, 1, NULL))
|
||||
{
|
||||
(*keysAdded)++;
|
||||
cs_hexdump(0, ecmKey, 8, keyValue, sizeof(keyValue));
|
||||
cs_log("Key found in EMM: T %.8X 01 %s", entitlementId, keyValue);
|
||||
}
|
||||
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
cs_log_dbg(D_READER, "WARNING: nanoTag E1 mode %.2X not supported", tagMode);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
cs_log_dbg(D_READER, "WARNING: nanoTag %.2X not supported", tagType);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
pos += 2 + tagLength;
|
||||
}
|
||||
|
||||
return EMU_OK;
|
||||
}
|
||||
|
||||
static int8_t parse_emm_nano_data(uint8_t *data, uint32_t *nanoLength, uint32_t maxLength,
|
||||
uint8_t keyIndex, uint32_t *keysAdded)
|
||||
{
|
||||
uint32_t pos = 0;
|
||||
uint16_t sectionLength;
|
||||
int8_t ret = EMU_OK;
|
||||
|
||||
if (maxLength < 2)
|
||||
{
|
||||
(*nanoLength) = 0;
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
sectionLength = ((data[pos] << 8) | data[pos + 1]) & 0x0FFF;
|
||||
|
||||
if (pos + 2 + sectionLength > maxLength)
|
||||
{
|
||||
(*nanoLength) = pos;
|
||||
return EMU_CORRUPT_DATA;
|
||||
}
|
||||
|
||||
ret = parse_emm_nano_tags(data + pos + 2, sectionLength, keyIndex, keysAdded);
|
||||
|
||||
pos += 2 + sectionLength;
|
||||
|
||||
(*nanoLength) = pos;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int8_t director_emm(uint8_t *emm, uint32_t *keysAdded)
|
||||
{
|
||||
uint8_t keyIndex, ret = EMU_OK;
|
||||
uint16_t emmLen = SCT_LEN(emm);
|
||||
uint32_t pos = 3;
|
||||
uint32_t permissionDataType;
|
||||
uint32_t nanoLength = 0;
|
||||
|
||||
while (pos < emmLen && !ret)
|
||||
{
|
||||
permissionDataType = emm[pos];
|
||||
|
||||
switch (permissionDataType)
|
||||
{
|
||||
case 0x00:
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
pos += 0x0A;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
pos += 0x26;
|
||||
break;
|
||||
|
||||
default:
|
||||
cs_log_dbg(D_READER, "ERROR: unknown permissionDataType %.2X (pos: %d)", permissionDataType, pos);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (pos + 6 >= emmLen)
|
||||
{
|
||||
return EMU_CORRUPT_DATA;
|
||||
}
|
||||
|
||||
keyIndex = emm[pos + 1];
|
||||
|
||||
// EMM validation
|
||||
// Copy payload checksum bytes and then set them to zero,
|
||||
// so they do not affect the calculated checksum.
|
||||
uint16_t payloadChecksum = (emm[pos + 2] << 8) | emm[pos + 3];
|
||||
memset(emm + pos + 2, 0, 2);
|
||||
uint16_t calculatedChecksum = calculate_checksum(emm + 3, emmLen - 3);
|
||||
|
||||
if (calculatedChecksum != payloadChecksum)
|
||||
{
|
||||
cs_log_dbg(D_READER, "EMM checksum error (%.4X instead of %.4X)", calculatedChecksum, payloadChecksum);
|
||||
return EMU_CHECKSUM_ERROR;
|
||||
}
|
||||
// End of EMM validation
|
||||
|
||||
pos += 0x04;
|
||||
ret = parse_emm_nano_data(emm + pos, &nanoLength, emmLen - pos, keyIndex, keysAdded);
|
||||
pos += nanoLength;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // WITH_EMU
|
||||
11
module-emulator-director.h
Executable file
11
module-emulator-director.h
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef MODULE_EMULATOR_DIRECTOR_H
|
||||
#define MODULE_EMULATOR_DIRECTOR_H
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
int8_t director_ecm(uint8_t *ecm, uint8_t *dw);
|
||||
int8_t director_emm(uint8_t *emm, uint32_t *keysAdded);
|
||||
|
||||
#endif // WITH_EMU
|
||||
|
||||
#endif // MODULE_EMULATOR_DIRECTOR_H
|
||||
602
module-emulator-irdeto.c
Executable file
602
module-emulator-irdeto.c
Executable file
|
|
@ -0,0 +1,602 @@
|
|||
#define MODULE_LOG_PREFIX "emu"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
#include "cscrypt/des.h"
|
||||
#include "module-emulator-osemu.h"
|
||||
#include "oscam-string.h"
|
||||
|
||||
static inline void xxor(uint8_t *data, int32_t len, const uint8_t *v1, const uint8_t *v2)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
switch (len)
|
||||
{
|
||||
case 16:
|
||||
for (i = 0; i < 16; ++i)
|
||||
{
|
||||
data[i] = v1[i] ^ v2[i];
|
||||
}
|
||||
break;
|
||||
|
||||
case 8:
|
||||
for (i = 0; i < 8; ++i)
|
||||
{
|
||||
data[i] = v1[i] ^ v2[i];
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
data[i] = v1[i] ^ v2[i];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
while (len--)
|
||||
{
|
||||
*data++ = *v1++ ^ *v2++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Irdeto EMU
|
||||
static int8_t get_key(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex,
|
||||
uint8_t isCriticalKey, uint32_t *keyRef)
|
||||
{
|
||||
char keyStr[EMU_MAX_CHAR_KEYNAME];
|
||||
|
||||
if (*keyRef > 0xFF)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex);
|
||||
|
||||
if (emu_find_key('I', ident, 0, keyStr, buf, 16, *keyRef > 0 ? 0 : isCriticalKey, *keyRef, 0, NULL))
|
||||
{
|
||||
(*keyRef)++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void irdeto2_encrypt(uint8_t *data, const uint8_t *seed, const uint8_t *key, int32_t len)
|
||||
{
|
||||
int32_t i;
|
||||
const uint8_t *tmp = seed;
|
||||
uint32_t ks1[32], ks2[32];
|
||||
|
||||
des_set_key(key, ks1);
|
||||
des_set_key(key + 8, ks2);
|
||||
|
||||
len &= ~7;
|
||||
|
||||
for (i = 0; i + 7 < len; i += 8)
|
||||
{
|
||||
xxor(&data[i], 8, &data[i], tmp);
|
||||
tmp = &data[i];
|
||||
des(&data[i], ks1, 1);
|
||||
des(&data[i], ks2, 0);
|
||||
des(&data[i], ks1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void irdeto2_decrypt(uint8_t *data, const uint8_t *seed, const uint8_t *key, int32_t len)
|
||||
{
|
||||
int32_t i, n = 0;
|
||||
uint8_t buf[2][8];
|
||||
uint32_t ks1[32], ks2[32];
|
||||
|
||||
des_set_key(key, ks1);
|
||||
des_set_key(key + 8, ks2);
|
||||
|
||||
len &= ~7;
|
||||
|
||||
memcpy(buf[n], seed, 8);
|
||||
|
||||
for (i = 0; i + 7 < len; i += 8, data += 8, n ^= 1)
|
||||
{
|
||||
memcpy(buf[1 - n], data, 8);
|
||||
des(data, ks1, 0);
|
||||
des(data, ks2, 1);
|
||||
des(data, ks1, 0);
|
||||
xxor(data, 8, data, buf[n]);
|
||||
}
|
||||
}
|
||||
|
||||
static int8_t calculate_hash(const uint8_t *key, const uint8_t *iv, const uint8_t *data, int32_t len)
|
||||
{
|
||||
int32_t l, y;
|
||||
uint8_t cbuff[32];
|
||||
uint32_t ks1[32], ks2[32];
|
||||
|
||||
des_set_key(key, ks1);
|
||||
des_set_key(key + 8, ks2);
|
||||
|
||||
memset(cbuff, 0, sizeof(cbuff));
|
||||
|
||||
len -= 8;
|
||||
|
||||
for (y = 0; y < len; y += 8)
|
||||
{
|
||||
if (y < len - 8)
|
||||
{
|
||||
xxor(cbuff, 8, cbuff, &data[y]);
|
||||
}
|
||||
else
|
||||
{
|
||||
l = len - y;
|
||||
xxor(cbuff, l, cbuff, &data[y]);
|
||||
xxor(cbuff + l, 8 - l, cbuff + l, iv + 8);
|
||||
}
|
||||
|
||||
des(cbuff, ks1, 1);
|
||||
des(cbuff, ks2, 0);
|
||||
des(cbuff, ks1, 1);
|
||||
}
|
||||
|
||||
return memcmp(cbuff, &data[len], 8) == 0;
|
||||
}
|
||||
|
||||
int8_t irdeto2_ecm(uint16_t caid, uint8_t *oecm, uint8_t *dw)
|
||||
{
|
||||
uint8_t keyNr = 0, length, end, key[16], okeySeed[16], keySeed[16], keyIV[16], tmp[16];
|
||||
uint8_t ecmCopy[EMU_MAX_ECM_LEN], *ecm = oecm;
|
||||
uint16_t ecmLen = SCT_LEN(ecm);
|
||||
uint32_t key0Ref, keySeedRef, keyIVRef, ident, i, j, l;
|
||||
|
||||
if (ecmLen < 12)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
length = ecm[11];
|
||||
keyNr = ecm[9];
|
||||
ident = ecm[8] | caid << 8;
|
||||
|
||||
if (ecmLen < length + 12)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
key0Ref = 0;
|
||||
|
||||
while (get_key(key, ident, '0', keyNr, 1, &key0Ref))
|
||||
{
|
||||
keySeedRef = 0;
|
||||
|
||||
while (get_key(okeySeed, ident, 'M', 1, 1, &keySeedRef))
|
||||
{
|
||||
keyIVRef = 0;
|
||||
|
||||
while (get_key(keyIV, ident, 'M', 2, 1, &keyIVRef))
|
||||
{
|
||||
memcpy(keySeed, okeySeed, 16);
|
||||
memcpy(ecmCopy, oecm, ecmLen);
|
||||
|
||||
ecm = ecmCopy;
|
||||
memset(tmp, 0, 16);
|
||||
irdeto2_encrypt(keySeed, tmp, key, 16);
|
||||
|
||||
ecm += 12;
|
||||
irdeto2_decrypt(ecm, keyIV, keySeed, length);
|
||||
|
||||
i = (ecm[0] & 7) + 1;
|
||||
end = length - 8 < 0 ? 0 : length - 8;
|
||||
|
||||
while (i < end)
|
||||
{
|
||||
l = ecm[i + 1] ? (ecm[i + 1] & 0x3F) + 2 : 1;
|
||||
|
||||
switch (ecm[i])
|
||||
{
|
||||
case 0x10:
|
||||
case 0x50:
|
||||
if (l == 0x13 && i <= length - 8 - l)
|
||||
{
|
||||
irdeto2_decrypt(&ecm[i + 3], keyIV, key, 16);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x78:
|
||||
if (l == 0x14 && i <= length - 8 - l)
|
||||
{
|
||||
irdeto2_decrypt(&ecm[i + 4], keyIV, key, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
i += l;
|
||||
}
|
||||
|
||||
i = (ecm[0] & 7) + 1;
|
||||
|
||||
if (calculate_hash(keySeed, keyIV, ecm - 6, length + 6))
|
||||
{
|
||||
while (i < end)
|
||||
{
|
||||
l = ecm[i + 1] ? (ecm[i + 1] & 0x3F) + 2 : 1;
|
||||
|
||||
switch (ecm[i])
|
||||
{
|
||||
case 0x78:
|
||||
{
|
||||
if (l == 0x14 && i <= length - 8 - l)
|
||||
{
|
||||
memcpy(dw, &ecm[i + 4], 16);
|
||||
|
||||
for (j = 0; j < 16; j += 4) // fix dw checksum bytes
|
||||
{
|
||||
dw[j + 3] = (dw[j] + dw[j + 1] + dw[j + 2]) & 0xFF;
|
||||
}
|
||||
return EMU_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
i += l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (keyIVRef == 0)
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
if (keySeedRef == 0)
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
if (key0Ref == 0)
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// Irdeto2 EMM EMU
|
||||
static int8_t do_emm_type_op(uint32_t ident, uint8_t *emm, uint8_t *keySeed, uint8_t *keyIV, uint8_t *keyPMK,
|
||||
uint16_t emmLen, uint8_t startOffset, uint8_t length, uint32_t *keysAdded)
|
||||
{
|
||||
uint8_t tmp[16];
|
||||
uint32_t end, i, l;
|
||||
char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36];
|
||||
|
||||
memset(tmp, 0, 16);
|
||||
irdeto2_encrypt(keySeed, tmp, keyPMK, 16);
|
||||
irdeto2_decrypt(&emm[startOffset], keyIV, keySeed, length);
|
||||
|
||||
i = 16;
|
||||
end = startOffset + (length - 8 < 0 ? 0 : length - 8);
|
||||
|
||||
while (i < end)
|
||||
{
|
||||
l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1;
|
||||
|
||||
switch (emm[i])
|
||||
{
|
||||
case 0x10:
|
||||
case 0x50:
|
||||
if (l == 0x13 && i <= startOffset + length - 8 - l)
|
||||
{
|
||||
irdeto2_decrypt(&emm[i + 3], keyIV, keyPMK, 16);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x78:
|
||||
if (l == 0x14 && i <= startOffset + length - 8 - l)
|
||||
{
|
||||
irdeto2_decrypt(&emm[i + 4], keyIV, keyPMK, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
i += l;
|
||||
}
|
||||
|
||||
memmove(emm + 6, emm + 7, emmLen - 7);
|
||||
|
||||
i = 15;
|
||||
end = startOffset + (length - 9 < 0 ? 0 : length - 9);
|
||||
|
||||
if (calculate_hash(keySeed, keyIV, emm + 3, emmLen - 4))
|
||||
{
|
||||
while (i < end)
|
||||
{
|
||||
l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1;
|
||||
|
||||
switch (emm[i])
|
||||
{
|
||||
case 0x10:
|
||||
case 0x50:
|
||||
{
|
||||
if (l == 0x13 && i <= startOffset + length - 9 - l)
|
||||
{
|
||||
snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%02X", emm[i + 2] >> 2);
|
||||
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||||
emu_set_key('I', ident, keyName, &emm[i + 3], 16, 1, NULL, NULL);
|
||||
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||||
|
||||
(*keysAdded)++;
|
||||
cs_hexdump(0, &emm[i + 3], 16, keyValue, sizeof(keyValue));
|
||||
cs_log("Key found in EMM: I %06X %s %s", ident, keyName, keyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
i += l;
|
||||
}
|
||||
|
||||
if (*keysAdded > 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int8_t do_emm_type_pmk(uint32_t ident, uint8_t *emm, uint8_t *keySeed, uint8_t *keyIV, uint8_t *keyPMK,
|
||||
uint16_t emmLen, uint8_t startOffset, uint8_t length, uint32_t *keysAdded)
|
||||
{
|
||||
uint32_t end, i, j, l;
|
||||
char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36];
|
||||
|
||||
irdeto2_decrypt(&emm[startOffset], keyIV, keySeed, length);
|
||||
|
||||
i = 13;
|
||||
end = startOffset + (length - 8 < 0 ? 0 : length - 8);
|
||||
|
||||
while (i < end)
|
||||
{
|
||||
l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1;
|
||||
|
||||
switch (emm[i])
|
||||
{
|
||||
case 0x10:
|
||||
case 0x50:
|
||||
if (l == 0x13 && i <= startOffset + length - 8 - l)
|
||||
{
|
||||
irdeto2_decrypt(&emm[i + 3], keyIV, keyPMK, 16);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x78:
|
||||
if (l == 0x14 && i <= startOffset + length - 8 - l)
|
||||
{
|
||||
irdeto2_decrypt(&emm[i + 4], keyIV, keyPMK, 16);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x68:
|
||||
if (l == 0x26 && i <= startOffset + length - 8 - l)
|
||||
{
|
||||
irdeto2_decrypt(&emm[i + 3], keyIV, keyPMK, 16 * 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
i += l;
|
||||
}
|
||||
|
||||
memmove(emm + 7, emm + 9, emmLen - 9);
|
||||
|
||||
i = 11;
|
||||
end = startOffset + (length - 10 < 0 ? 0 : length - 10);
|
||||
|
||||
if (calculate_hash(keySeed, keyIV, emm + 3, emmLen - 5))
|
||||
{
|
||||
while (i < end)
|
||||
{
|
||||
l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1;
|
||||
|
||||
switch (emm[i])
|
||||
{
|
||||
case 0x68:
|
||||
{
|
||||
if (l == 0x26 && i <= startOffset + length - 10 - l)
|
||||
{
|
||||
for (j = 0; j < 2; j++)
|
||||
{
|
||||
snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "M%01X", 3 + j);
|
||||
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||||
emu_set_key('I', ident, keyName, &emm[i + 3 + j * 16], 16, 1, NULL, NULL);
|
||||
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||||
|
||||
(*keysAdded)++;
|
||||
cs_hexdump(0, &emm[i + 3 + j * 16], 16, keyValue, sizeof(keyValue));
|
||||
cs_log("Key found in EMM: I %06X %s %s", ident, keyName, keyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
i += l;
|
||||
}
|
||||
|
||||
if (*keysAdded > 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const uint8_t fausto_xor[16] =
|
||||
{
|
||||
0x22, 0x58, 0xBD, 0x85, 0x2E, 0x8E, 0x52, 0x80,
|
||||
0xA3, 0x79, 0x98, 0x69, 0x68, 0xE2, 0xD8, 0x4D
|
||||
};
|
||||
|
||||
int8_t irdeto2_emm(uint16_t caid, uint8_t *oemm, uint32_t *keysAdded)
|
||||
{
|
||||
uint8_t length, okeySeed[16], keySeed[16], keyIV[16], keyPMK[16], startOffset, emmType;
|
||||
uint8_t emmCopy[EMU_MAX_EMM_LEN], *emm = oemm;
|
||||
uint16_t emmLen = SCT_LEN(emm);
|
||||
uint32_t ident, keySeedRef, keyIVRef, keyPMK0Ref, keyPMK1Ref, keyPMK0ERef, keyPMK1ERef;
|
||||
|
||||
if (emmLen < 11)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (emm[3] == 0xC3 || emm[3] == 0xCB)
|
||||
{
|
||||
emmType = 2;
|
||||
startOffset = 11;
|
||||
}
|
||||
else
|
||||
{
|
||||
emmType = 1;
|
||||
startOffset = 10;
|
||||
}
|
||||
|
||||
ident = emm[startOffset - 2] | caid << 8;
|
||||
length = emm[startOffset - 1];
|
||||
|
||||
if (emmLen < length + startOffset)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
keySeedRef = 0;
|
||||
|
||||
while (get_key(okeySeed, ident, 'M', emmType == 1 ? 0 : 0xA, 1, &keySeedRef))
|
||||
{
|
||||
keyIVRef = 0;
|
||||
|
||||
while (get_key(keyIV, ident, 'M', 2, 1, &keyIVRef))
|
||||
{
|
||||
keyPMK0Ref = 0;
|
||||
keyPMK1Ref = 0;
|
||||
keyPMK0ERef = 0;
|
||||
keyPMK1ERef = 0;
|
||||
|
||||
while (get_key(keyPMK, ident, 'M', emmType == 1 ? 3 : 0xB, 1, &keyPMK0Ref))
|
||||
{
|
||||
memcpy(keySeed, okeySeed, 16);
|
||||
memcpy(emmCopy, oemm, emmLen);
|
||||
emm = emmCopy;
|
||||
|
||||
if (emmType == 1)
|
||||
{
|
||||
if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0)
|
||||
{
|
||||
return EMU_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (do_emm_type_pmk(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0)
|
||||
{
|
||||
return EMU_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (emmType == 1)
|
||||
{
|
||||
while (get_key(keyPMK, ident, 'M', 4, 1, &keyPMK1Ref))
|
||||
{
|
||||
memcpy(keySeed, okeySeed, 16);
|
||||
memcpy(emmCopy, oemm, emmLen);
|
||||
emm = emmCopy;
|
||||
|
||||
if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0)
|
||||
{
|
||||
return EMU_OK;
|
||||
}
|
||||
}
|
||||
|
||||
while (get_key(keyPMK, ident, 'M', 5, 1, &keyPMK0ERef))
|
||||
{
|
||||
xxor(keyPMK, 16, keyPMK, fausto_xor);
|
||||
memcpy(keySeed, okeySeed, 16);
|
||||
memcpy(emmCopy, oemm, emmLen);
|
||||
emm = emmCopy;
|
||||
|
||||
if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0)
|
||||
{
|
||||
return EMU_OK;
|
||||
}
|
||||
}
|
||||
|
||||
while (get_key(keyPMK, ident, 'M', 6, 1, &keyPMK1ERef))
|
||||
{
|
||||
xxor(keyPMK, 16, keyPMK, fausto_xor);
|
||||
memcpy(keySeed, okeySeed, 16);
|
||||
memcpy(emmCopy, oemm, emmLen);
|
||||
emm = emmCopy;
|
||||
|
||||
if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0)
|
||||
{
|
||||
return EMU_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (keyPMK0Ref == 0 && keyPMK1Ref == 0 && keyPMK0ERef == 0 && keyPMK1ERef == 0)
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyIVRef == 0)
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
if (keySeedRef == 0)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
int8_t irdeto2_get_hexserial(uint16_t caid, uint8_t *hexserial)
|
||||
{
|
||||
uint32_t i, len;
|
||||
KeyDataContainer *KeyDB;
|
||||
KeyData *tmpKeyData;
|
||||
|
||||
KeyDB = emu_get_key_container('I');
|
||||
|
||||
if (KeyDB == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < KeyDB->keyCount; i++)
|
||||
{
|
||||
if (KeyDB->EmuKeys[i].provider >> 8 != caid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(KeyDB->EmuKeys[i].keyName, "MC"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tmpKeyData = &KeyDB->EmuKeys[i];
|
||||
len = tmpKeyData->keyLength;
|
||||
|
||||
if (len > 3)
|
||||
{ len = 3; }
|
||||
|
||||
memcpy(hexserial + (3 - len), tmpKeyData->key, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // WITH_EMU
|
||||
15
module-emulator-irdeto.h
Executable file
15
module-emulator-irdeto.h
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef MODULE_EMULATOR_IRDETO_H
|
||||
#define MODULE_EMULATOR_IRDETO_H
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
int8_t irdeto2_ecm(uint16_t caid, uint8_t *oecm, uint8_t *dw);
|
||||
int8_t irdeto2_emm(uint16_t caid, uint8_t *oemm, uint32_t *keysAdded);
|
||||
|
||||
// hexserial must be of type "uint8_t hexserial[3]"
|
||||
// returns 0 on error, 1 on success
|
||||
int8_t irdeto2_get_hexserial(uint16_t caid, uint8_t *hexserial);
|
||||
|
||||
#endif // WITH_EMU
|
||||
|
||||
#endif // MODULE_EMULATOR_IRDETO_H
|
||||
376
module-emulator-nagravision.c
Executable file
376
module-emulator-nagravision.c
Executable file
|
|
@ -0,0 +1,376 @@
|
|||
#define MODULE_LOG_PREFIX "emu"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
#include "cscrypt/bn.h"
|
||||
#include "cscrypt/des.h"
|
||||
#include "cscrypt/idea.h"
|
||||
#include "module-emulator-osemu.h"
|
||||
|
||||
static void reverse_mem(uint8_t *in, int32_t len)
|
||||
{
|
||||
uint8_t temp;
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; i < (len / 2); i++)
|
||||
{
|
||||
temp = in[i];
|
||||
in[i] = in[len - i - 1];
|
||||
in[len - i - 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static void reverse_mem_in_out(uint8_t *out, const uint8_t *in, int32_t n)
|
||||
{
|
||||
if (n > 0)
|
||||
{
|
||||
out += n;
|
||||
do
|
||||
{
|
||||
*(--out) = *(in++);
|
||||
}
|
||||
while (--n);
|
||||
}
|
||||
}
|
||||
|
||||
static int8_t rsa_input(BIGNUM *d, const uint8_t *in, int32_t n, int8_t le)
|
||||
{
|
||||
int8_t result = 0;
|
||||
|
||||
if (le)
|
||||
{
|
||||
uint8_t *tmp = (uint8_t *)malloc(sizeof(uint8_t) * n);
|
||||
|
||||
if (tmp == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
reverse_mem_in_out(tmp, in, n);
|
||||
result = BN_bin2bn(tmp, n, d) != 0;
|
||||
free(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = BN_bin2bn(in, n, d) != 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int32_t rsa_output(uint8_t *out, int32_t n, BIGNUM *r, int8_t le)
|
||||
{
|
||||
int32_t s = BN_num_bytes(r);
|
||||
|
||||
if (s > n)
|
||||
{
|
||||
uint8_t *buff = (uint8_t *)malloc(sizeof(uint8_t) * s);
|
||||
|
||||
if (buff == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
BN_bn2bin(r, buff);
|
||||
memcpy(out, buff + s - n, n);
|
||||
free(buff);
|
||||
}
|
||||
else if (s < n)
|
||||
{
|
||||
int32_t l = n - s;
|
||||
|
||||
memset(out, 0, l);
|
||||
BN_bn2bin(r, out + l);
|
||||
}
|
||||
else
|
||||
{
|
||||
BN_bn2bin(r, out);
|
||||
}
|
||||
|
||||
if (le)
|
||||
{
|
||||
reverse_mem(out, n);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int32_t emu_rsa(uint8_t *out, const uint8_t *in, int32_t n, BIGNUM *exp, BIGNUM *mod, int8_t le)
|
||||
{
|
||||
BN_CTX *ctx;
|
||||
BIGNUM *r, *d;
|
||||
int32_t result = 0;
|
||||
|
||||
ctx = BN_CTX_new();
|
||||
r = BN_new();
|
||||
d = BN_new();
|
||||
|
||||
if (rsa_input(d, in, n, le) && BN_mod_exp(r, d, exp, mod, ctx))
|
||||
{
|
||||
result = rsa_output(out, n, r, le);
|
||||
}
|
||||
|
||||
BN_free(d);
|
||||
BN_free(r);
|
||||
BN_CTX_free(ctx);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Nagra EMU
|
||||
|
||||
static int8_t get_key(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex, uint8_t isCriticalKey)
|
||||
{
|
||||
char keyStr[EMU_MAX_CHAR_KEYNAME];
|
||||
snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex);
|
||||
|
||||
if (emu_find_key('N', ident, 0, keyStr, buf, keyName == 'M' ? 64 : 16, isCriticalKey, 0, 0, NULL))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int8_t nagra2_signature(const uint8_t *vkey, const uint8_t *sig, const uint8_t *msg, int32_t len)
|
||||
{
|
||||
uint8_t buff[16], iv[8];
|
||||
int32_t i, j;
|
||||
|
||||
memcpy(buff, vkey, sizeof(buff));
|
||||
|
||||
for (i = 0; i + 7 < len; i += 8)
|
||||
{
|
||||
IDEA_KEY_SCHEDULE ek;
|
||||
|
||||
idea_set_encrypt_key(buff, &ek);
|
||||
memcpy(buff, buff + 8, 8);
|
||||
memset(iv, 0, sizeof(iv));
|
||||
idea_cbc_encrypt(msg + i, buff + 8, 8, &ek, iv, IDEA_ENCRYPT);
|
||||
|
||||
for (j = 7; j >= 0; j--)
|
||||
{
|
||||
buff[j + 8] ^= msg[i + j];
|
||||
}
|
||||
}
|
||||
|
||||
buff[8] &= 0x7F;
|
||||
|
||||
return (memcmp(sig, buff + 8, 8) == 0);
|
||||
}
|
||||
|
||||
static int8_t decrypt_ecm(uint8_t *in, uint8_t *out, const uint8_t *key, int32_t len,
|
||||
const uint8_t *vkey, uint8_t *keyM)
|
||||
{
|
||||
BIGNUM *exp, *mod;
|
||||
uint8_t iv[8];
|
||||
int32_t i = 0, sign = in[0] & 0x80;
|
||||
uint8_t binExp = 3;
|
||||
int8_t result = 1;
|
||||
|
||||
exp = BN_new();
|
||||
mod = BN_new();
|
||||
BN_bin2bn(&binExp, 1, exp);
|
||||
BN_bin2bn(keyM, 64, mod);
|
||||
|
||||
if (emu_rsa(out, in + 1, 64, exp, mod, 1) <= 0)
|
||||
{
|
||||
BN_free(exp);
|
||||
BN_free(mod);
|
||||
return 0;
|
||||
}
|
||||
|
||||
out[63] |= sign;
|
||||
|
||||
if (len > 64)
|
||||
{
|
||||
memcpy(out + 64, in + 65, len - 64);
|
||||
}
|
||||
|
||||
memset(iv, 0, sizeof(iv));
|
||||
|
||||
if (in[0] & 0x04)
|
||||
{
|
||||
uint8_t key1[8], key2[8];
|
||||
|
||||
reverse_mem_in_out(key1, &key[0], 8);
|
||||
reverse_mem_in_out(key2, &key[8], 8);
|
||||
|
||||
for (i = 7; i >= 0; i--)
|
||||
{
|
||||
reverse_mem(out + 8 * i, 8);
|
||||
}
|
||||
|
||||
des_ede2_cbc_decrypt(out, iv, key1, key2, len);
|
||||
|
||||
for (i = 7; i >= 0; i--)
|
||||
{
|
||||
reverse_mem(out + 8 * i, 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IDEA_KEY_SCHEDULE ek;
|
||||
|
||||
idea_set_encrypt_key(key, &ek);
|
||||
idea_cbc_encrypt(out, out, len & ~7, &ek, iv, IDEA_DECRYPT);
|
||||
}
|
||||
|
||||
reverse_mem(out, 64);
|
||||
|
||||
if (result && emu_rsa(out, out, 64, exp, mod, 0) <= 0)
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
|
||||
if (result && vkey && !nagra2_signature(vkey, out, out + 8, len - 8))
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
|
||||
BN_free(exp);
|
||||
BN_free(mod);
|
||||
return result;
|
||||
}
|
||||
|
||||
int8_t nagra2_ecm(uint8_t *ecm, uint8_t *dw)
|
||||
{
|
||||
int8_t useVerifyKey = 0;
|
||||
int32_t l = 0, s;
|
||||
|
||||
uint8_t cmdLen, ideaKeyNr, *dec, ideaKey[16], vKey[16], m1Key[64], mecmAlgo = 0;
|
||||
uint16_t i = 0, ecmLen = SCT_LEN(ecm);
|
||||
uint32_t ident, identMask, tmp1, tmp2, tmp3;
|
||||
|
||||
if (ecmLen < 8)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
cmdLen = ecm[4] - 5;
|
||||
ident = (ecm[5] << 8) + ecm[6];
|
||||
ideaKeyNr = (ecm[7] & 0x10) >> 4;
|
||||
|
||||
if (ideaKeyNr)
|
||||
{
|
||||
ideaKeyNr = 1;
|
||||
}
|
||||
|
||||
if (ident == 1283 || ident == 1285 || ident == 1297)
|
||||
{
|
||||
ident = 1281;
|
||||
}
|
||||
|
||||
if (cmdLen <= 63 || ecmLen < cmdLen + 10)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (!get_key(ideaKey, ident, '0', ideaKeyNr, 1))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (get_key(vKey, ident, 'V', 0, 0))
|
||||
{
|
||||
useVerifyKey = 1;
|
||||
}
|
||||
|
||||
if (!get_key(m1Key, ident, 'M', 1, 1))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
reverse_mem(m1Key, 64);
|
||||
|
||||
dec = (uint8_t *)malloc(sizeof(uint8_t) * cmdLen);
|
||||
if (dec == NULL)
|
||||
{
|
||||
return EMU_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!decrypt_ecm(ecm + 9, dec, ideaKey, cmdLen, useVerifyKey ? vKey : 0, m1Key))
|
||||
{
|
||||
free(dec);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
for (i = (dec[14] & 0x10) ? 16 : 20; i < cmdLen && l != 3; )
|
||||
{
|
||||
switch (dec[i])
|
||||
{
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
if (i + 10 < cmdLen && dec[i + 1] == 0x09)
|
||||
{
|
||||
s = (~dec[i]) & 1;
|
||||
mecmAlgo = dec[i + 2] & 0x60;
|
||||
memcpy(dw + (s << 3), &dec[i + 3], 8);
|
||||
i += 11;
|
||||
l |= (s + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x00:
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case 0x30:
|
||||
case 0x31:
|
||||
case 0x32:
|
||||
case 0x33:
|
||||
case 0x34:
|
||||
case 0x35:
|
||||
case 0x36:
|
||||
case 0xB0:
|
||||
if (i + 1 < cmdLen)
|
||||
{
|
||||
i += dec[i + 1] + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
free(dec);
|
||||
|
||||
if (l != 3)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (mecmAlgo > 0)
|
||||
{
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
identMask = ident & 0xFF00;
|
||||
|
||||
if (identMask == 0x1100 || identMask == 0x500 || identMask == 0x3100)
|
||||
{
|
||||
memcpy(&tmp1, dw, 4);
|
||||
memcpy(&tmp2, dw + 4, 4);
|
||||
memcpy(&tmp3, dw + 12, 4);
|
||||
memcpy(dw, dw + 8, 4);
|
||||
memcpy(dw + 4, &tmp3, 4);
|
||||
memcpy(dw + 8, &tmp1, 4);
|
||||
memcpy(dw + 12, &tmp2, 4);
|
||||
}
|
||||
|
||||
return EMU_OK;
|
||||
}
|
||||
|
||||
#endif // WITH_EMU
|
||||
10
module-emulator-nagravision.h
Executable file
10
module-emulator-nagravision.h
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef MODULE_EMULATOR_NAGRAVISION_H
|
||||
#define MODULE_EMULATOR_NAGRAVISION_H
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
int8_t nagra2_ecm(uint8_t *ecm, uint8_t *dw);
|
||||
|
||||
#endif // WITH_EMU
|
||||
|
||||
#endif // MODULE_EMULATOR_NAGRAVISION_H
|
||||
72
module-emulator-omnicrypt.c
Executable file
72
module-emulator-omnicrypt.c
Executable file
|
|
@ -0,0 +1,72 @@
|
|||
#define MODULE_LOG_PREFIX "emu"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
#include "module-emulator-osemu.h"
|
||||
#include "module-emulator-omnicrypt.h"
|
||||
#include "oscam-aes.h"
|
||||
#include "oscam-string.h"
|
||||
|
||||
|
||||
static inline int8_t get_ecm_key(uint16_t provider, uint8_t parity, uint8_t *key)
|
||||
{
|
||||
return emu_find_key('O', provider, 0, parity == 0 ? "00" : "01", key, 16, 1, 0, 0, NULL);
|
||||
}
|
||||
|
||||
int8_t omnicrypt_ecm(uint8_t *ecm, uint8_t *dw)
|
||||
{
|
||||
uint8_t section_syntax_indicator, session_key[16], session_key_parity, position;
|
||||
uint16_t private_section_length, session_key_id, payload_length;
|
||||
struct aes_keys aes;
|
||||
|
||||
section_syntax_indicator = ecm[1] >> 7;
|
||||
if (section_syntax_indicator != 0) // The private_data_bytes immediately follow the private_section_length field
|
||||
{
|
||||
cs_log("ECM section syntax indicator %d not supported", section_syntax_indicator);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
private_section_length = b2i(2, ecm + 1) & 0x0FFF;
|
||||
if (private_section_length != 0x2D)
|
||||
{
|
||||
cs_log("ECM has an unsupported private section length of %d", private_section_length);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
session_key_parity = ecm[3] & 0x01;
|
||||
session_key_id = b2i(2, ecm + 4);
|
||||
|
||||
if (!get_ecm_key(session_key_id, session_key_parity, session_key))
|
||||
{
|
||||
return EMU_KEY_NOT_FOUND;
|
||||
}
|
||||
aes_set_key(&aes, (char *)session_key);
|
||||
|
||||
payload_length = b2i(2, ecm + 6) & 0x0FFF;
|
||||
if (payload_length != 0x28)
|
||||
{
|
||||
cs_log("ECM has an unsupported payload length of %d", payload_length);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
for (position = 8; position + 1 < payload_length; position += 4 + 16) // Run twice for odd, even CW
|
||||
{
|
||||
uint8_t parity = ecm[position + 1] & 0x01;
|
||||
uint8_t length = ecm[position + 3];
|
||||
|
||||
if (length != 16)
|
||||
{
|
||||
cs_log("CW %d has an unsupported length of %d", parity, length);
|
||||
return EMU_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
aes_decrypt(&aes, ecm + position + 4, 16);
|
||||
memcpy(dw + parity * 8, ecm + position + 4, 8); // Copy the first 8 bytes (rest are zeros)
|
||||
}
|
||||
|
||||
return EMU_OK;
|
||||
}
|
||||
|
||||
#endif // WITH_EMU
|
||||
10
module-emulator-omnicrypt.h
Executable file
10
module-emulator-omnicrypt.h
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef MODULE_EMULATOR_OMNICRYPT_H
|
||||
#define MODULE_EMULATOR_OMNICRYPT_H
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
int8_t omnicrypt_ecm(uint8_t *ecm, uint8_t *dw);
|
||||
|
||||
#endif // WITH_EMU
|
||||
|
||||
#endif // MODULE_EMULATOR_OMNICRYPT_H
|
||||
986
module-emulator-osemu.c
Executable file
986
module-emulator-osemu.c
Executable file
|
|
@ -0,0 +1,986 @@
|
|||
#define MODULE_LOG_PREFIX "emu"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
#include "oscam-string.h"
|
||||
#include "module-streamrelay.h"
|
||||
#include "module-emulator-osemu.h"
|
||||
#include "module-emulator-biss.h"
|
||||
#include "module-emulator-cryptoworks.h"
|
||||
#include "module-emulator-director.h"
|
||||
#include "module-emulator-irdeto.h"
|
||||
#include "module-emulator-nagravision.h"
|
||||
#include "module-emulator-omnicrypt.h"
|
||||
#include "module-emulator-powervu.h"
|
||||
#include "module-emulator-viaccess.h"
|
||||
|
||||
// Shared functions
|
||||
|
||||
int8_t is_valid_dcw(uint8_t *dw)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < 8; i+= 4)
|
||||
{
|
||||
if (((dw[i] + dw[i + 1] + dw[i + 2]) & 0xFF) != dw[i + 3])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int8_t char_to_bin(uint8_t *out, const char *in, uint32_t inLen)
|
||||
{
|
||||
uint32_t i, tmp;
|
||||
|
||||
for (i = 0; i < inLen / 2; i++)
|
||||
{
|
||||
if (sscanf(in + i * 2, "%02X", &tmp) != 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
out[i] = (uint8_t)tmp;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void date_to_str(char *dateStr, uint8_t len, int8_t offset, uint8_t format)
|
||||
{
|
||||
// Creates a formatted date string for use in various functions.
|
||||
// A positive or negative time offset (in hours) can be set as well
|
||||
// as the format of the output string.
|
||||
|
||||
time_t rawtime;
|
||||
struct tm timeinfo;
|
||||
|
||||
time(&rawtime);
|
||||
rawtime += (time_t) offset * 60 * 60; // Add a positive or negative offset
|
||||
localtime_r(&rawtime, &timeinfo);
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case 1:
|
||||
strftime(dateStr, len, "%c", &timeinfo);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
strftime(dateStr, len, "%F @ %R", &timeinfo);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
strftime(dateStr, len, "%y%m%d%H", &timeinfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Key DB
|
||||
*
|
||||
* The Emu reader gets keys from the OSCcam-Emu binary and the "SoftCam.Key" file.
|
||||
*
|
||||
* The keys are stored in structures of type "KeyDataContainer", one per CAS. Each
|
||||
* container points to a dynamically allocated array of type "KeyData", which holds
|
||||
* the actual keys. The array initially holds up to 64 keys (64 * KeyData), and it
|
||||
* is expanded by 16 every time it's filled with keys. The "KeyDataContainer" also
|
||||
* includes info about the number of keys it contains ("KeyCount") and the maximum
|
||||
* number of keys it can store ("KeyMax").
|
||||
*
|
||||
* The "KeyData" structure, on the other hand, stores the actual key information,
|
||||
* including the "identifier", "provider", "keyName", "key" and "keyLength". There
|
||||
* is also a "nextKey" pointer to a similar "KeyData" structure which is only used
|
||||
* for Irdeto multiple keys, in a linked list style structure. For all other CAS,
|
||||
* the "nextKey" is a "NULL" pointer.
|
||||
*
|
||||
* For storing keys, the "SetKey" function is used. Duplicate keys are not allowed.
|
||||
* When storing a key that is already present in the database, its "key" value is
|
||||
* updated with the new one. For reading keys from the database, the "FindKey"
|
||||
* function is used. To delete all keys in a container, the "DeleteKeysInContainer"
|
||||
* function can be called.
|
||||
*/
|
||||
|
||||
char *emu_keyfile_path = NULL;
|
||||
|
||||
void emu_set_keyfile_path(const char *path)
|
||||
{
|
||||
uint32_t pathLength;
|
||||
|
||||
if (emu_keyfile_path != NULL)
|
||||
{
|
||||
free(emu_keyfile_path);
|
||||
}
|
||||
|
||||
pathLength = cs_strlen(path);
|
||||
emu_keyfile_path = (char *)malloc(pathLength + 1);
|
||||
if (emu_keyfile_path == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
cs_strncpy(emu_keyfile_path, path, pathLength + 1);
|
||||
}
|
||||
|
||||
KeyDataContainer CwKeys = { NULL, 0, 0 };
|
||||
KeyDataContainer ViKeys = { NULL, 0, 0 };
|
||||
KeyDataContainer NagraKeys = { NULL, 0, 0 };
|
||||
KeyDataContainer IrdetoKeys = { NULL, 0, 0 };
|
||||
KeyDataContainer BissSWs = { NULL, 0, 0 };
|
||||
KeyDataContainer Biss2Keys = { NULL, 0, 0 };
|
||||
KeyDataContainer OmnicryptKeys = { NULL, 0, 0 };
|
||||
KeyDataContainer PowervuKeys = { NULL, 0, 0 };
|
||||
KeyDataContainer TandbergKeys = { NULL, 0, 0 };
|
||||
KeyDataContainer StreamKeys = { NULL, 0, 0 };
|
||||
|
||||
KeyDataContainer *emu_get_key_container(char identifier)
|
||||
{
|
||||
switch (identifier)
|
||||
{
|
||||
case 'W':
|
||||
return &CwKeys;
|
||||
case 'V':
|
||||
return &ViKeys;
|
||||
case 'N':
|
||||
return &NagraKeys;
|
||||
case 'I':
|
||||
return &IrdetoKeys;
|
||||
case 'F':
|
||||
return &BissSWs;
|
||||
case 'G':
|
||||
return &Biss2Keys;
|
||||
case 'O':
|
||||
return &OmnicryptKeys;
|
||||
case 'P':
|
||||
return &PowervuKeys;
|
||||
case 'T':
|
||||
return &TandbergKeys;
|
||||
case 'A':
|
||||
return &StreamKeys;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_key_to_file(char identifier, uint32_t provider, const char *keyName, uint8_t *key,
|
||||
uint32_t keyLength, char *comment)
|
||||
{
|
||||
char line[1200], dateText[100], filename[EMU_KEY_FILENAME_MAX_LEN + 1];
|
||||
char *path, *filepath, *keyValue;
|
||||
uint32_t pathLength;
|
||||
uint8_t fileNameLen = cs_strlen(EMU_KEY_FILENAME);
|
||||
struct dirent *pDirent;
|
||||
DIR *pDir;
|
||||
FILE *file = NULL;
|
||||
|
||||
pathLength = cs_strlen(emu_keyfile_path);
|
||||
path = (char *)malloc(pathLength + 1);
|
||||
if (path == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
cs_strncpy(path, emu_keyfile_path, pathLength + 1);
|
||||
|
||||
pathLength = cs_strlen(path);
|
||||
if (pathLength >= fileNameLen && strcasecmp(path + pathLength - fileNameLen, EMU_KEY_FILENAME) == 0)
|
||||
{
|
||||
// cut file name
|
||||
path[pathLength - fileNameLen] = '\0';
|
||||
}
|
||||
|
||||
pathLength = cs_strlen(path);
|
||||
if (path[pathLength - 1] == '/' || path[pathLength - 1] == '\\')
|
||||
{
|
||||
// cut trailing /
|
||||
path[pathLength - 1] = '\0';
|
||||
}
|
||||
|
||||
pDir = opendir(path);
|
||||
if (pDir == NULL)
|
||||
{
|
||||
cs_log("Cannot open key file path: %s", path);
|
||||
free(path);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((pDirent = readdir(pDir)) != NULL)
|
||||
{
|
||||
if (strcasecmp(pDirent->d_name, EMU_KEY_FILENAME) == 0)
|
||||
{
|
||||
cs_strncpy(filename, pDirent->d_name, sizeof(filename));
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(pDir);
|
||||
|
||||
if (pDirent == NULL)
|
||||
{
|
||||
cs_strncpy(filename, EMU_KEY_FILENAME, sizeof(filename));
|
||||
}
|
||||
|
||||
pathLength = cs_strlen(path) + 1 + cs_strlen(filename) + 1;
|
||||
filepath = (char *)malloc(pathLength);
|
||||
if (filepath == NULL)
|
||||
{
|
||||
free(path);
|
||||
return;
|
||||
}
|
||||
snprintf(filepath, pathLength, "%s/%s", path, filename);
|
||||
free(path);
|
||||
|
||||
cs_log("Writing key file: %s", filepath);
|
||||
|
||||
file = fopen(filepath, "a");
|
||||
free(filepath);
|
||||
if (file == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
date_to_str(dateText, sizeof(dateText), 0, 1);
|
||||
|
||||
keyValue = (char *)malloc((keyLength * 2) + 1);
|
||||
if (keyValue == NULL)
|
||||
{
|
||||
fclose(file);
|
||||
return;
|
||||
}
|
||||
cs_hexdump(0, key, keyLength, keyValue, (keyLength * 2) + 1);
|
||||
|
||||
if (comment)
|
||||
{
|
||||
snprintf(line, sizeof(line), "\n%c %08X %s %s ; added by Emu %s %s",
|
||||
identifier, provider, keyName, keyValue, dateText, comment);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(line, sizeof(line), "\n%c %08X %s %s ; added by Emu %s",
|
||||
identifier, provider, keyName, keyValue, dateText);
|
||||
}
|
||||
|
||||
cs_log("Key written: %c %08X %s %s", identifier, provider, keyName, keyValue);
|
||||
|
||||
free(keyValue);
|
||||
|
||||
fwrite(line, cs_strlen(line), 1, file);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
int8_t emu_set_key(char identifier, uint32_t provider, char *keyName, uint8_t *orgKey, uint32_t keyLength,
|
||||
uint8_t writeKey, char *comment, struct s_reader *rdr)
|
||||
{
|
||||
uint32_t i, j;
|
||||
uint8_t *tmpKey = NULL;
|
||||
KeyDataContainer *KeyDB;
|
||||
KeyData *tmpKeyData, *newKeyData;
|
||||
|
||||
identifier = (char)toupper((int)identifier);
|
||||
|
||||
KeyDB = emu_get_key_container(identifier);
|
||||
if (KeyDB == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
keyName = strtoupper(keyName);
|
||||
|
||||
if (identifier == 'F') // Prepare BISS keys before saving to the db
|
||||
{
|
||||
// Convert legacy BISS "00" & "01" keynames
|
||||
if (0 == strcmp(keyName, "00") || 0 == strcmp(keyName, "01"))
|
||||
{
|
||||
keyName = "00000000";
|
||||
}
|
||||
|
||||
// All keyNames should have a length of 8 after converting
|
||||
if (cs_strlen(keyName) != 8)
|
||||
{
|
||||
cs_log("WARNING: Wrong key format in %s: F %08X %s", EMU_KEY_FILENAME, provider, keyName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Verify date-coded keyName (if enabled), ignoring old (expired) keys
|
||||
if (rdr->emu_datecodedenabled)
|
||||
{
|
||||
char timeStr[9];
|
||||
date_to_str(timeStr, sizeof(timeStr), 0, 3);
|
||||
|
||||
// Reject old date-coded keys, but allow our "00000000" evergreen label
|
||||
if (strcmp("00000000", keyName) != 0 && strcmp(timeStr, keyName) >= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix checksum for BISS keys with a length of 6
|
||||
if (identifier == 'F' && keyLength == 6)
|
||||
{
|
||||
tmpKey = (uint8_t *)malloc(8 * sizeof(uint8_t));
|
||||
if(tmpKey == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmpKey[0] = orgKey[0];
|
||||
tmpKey[1] = orgKey[1];
|
||||
tmpKey[2] = orgKey[2];
|
||||
tmpKey[3] = ((orgKey[0] + orgKey[1] + orgKey[2]) & 0xFF);
|
||||
tmpKey[4] = orgKey[3];
|
||||
tmpKey[5] = orgKey[4];
|
||||
tmpKey[6] = orgKey[5];
|
||||
tmpKey[7] = ((orgKey[3] + orgKey[4] + orgKey[5]) & 0xFF);
|
||||
|
||||
keyLength = 8;
|
||||
}
|
||||
else // All keys with a length of 8, including BISS
|
||||
{
|
||||
tmpKey = (uint8_t *)malloc(keyLength * sizeof(uint8_t));
|
||||
if (tmpKey == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(tmpKey, orgKey, keyLength);
|
||||
}
|
||||
|
||||
// Fix patched mgcamd format for Irdeto
|
||||
if (identifier == 'I' && provider < 0xFFFF)
|
||||
{
|
||||
provider = provider << 8;
|
||||
}
|
||||
|
||||
// Key already exists on db, update its value
|
||||
for (i = 0; i < KeyDB->keyCount; i++)
|
||||
{
|
||||
if (KeyDB->EmuKeys[i].provider != provider)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't match keyName (i.e. expiration date) for BISS1 and BISS2 mode 1/E sesssion words
|
||||
if (identifier != 'F' && strcmp(KeyDB->EmuKeys[i].keyName, keyName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allow multiple keys for Irdeto
|
||||
if (identifier == 'I')
|
||||
{
|
||||
// Reject duplicates
|
||||
tmpKeyData = &KeyDB->EmuKeys[i];
|
||||
do
|
||||
{
|
||||
if (memcmp(tmpKeyData->key, tmpKey, tmpKeyData->keyLength < keyLength ? tmpKeyData->keyLength : keyLength) == 0)
|
||||
{
|
||||
free(tmpKey);
|
||||
return 0;
|
||||
}
|
||||
tmpKeyData = tmpKeyData->nextKey;
|
||||
}
|
||||
while(tmpKeyData != NULL);
|
||||
|
||||
// Add new key
|
||||
newKeyData = (KeyData *)malloc(sizeof(KeyData));
|
||||
if (newKeyData == NULL)
|
||||
{
|
||||
free(tmpKey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
newKeyData->identifier = identifier;
|
||||
newKeyData->provider = provider;
|
||||
|
||||
if (cs_strlen(keyName) < EMU_MAX_CHAR_KEYNAME)
|
||||
{
|
||||
cs_strncpy(newKeyData->keyName, keyName, EMU_MAX_CHAR_KEYNAME);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(newKeyData->keyName, keyName, EMU_MAX_CHAR_KEYNAME);
|
||||
}
|
||||
|
||||
newKeyData->keyName[EMU_MAX_CHAR_KEYNAME - 1] = 0;
|
||||
newKeyData->key = tmpKey;
|
||||
newKeyData->keyLength = keyLength;
|
||||
newKeyData->nextKey = NULL;
|
||||
|
||||
tmpKeyData = &KeyDB->EmuKeys[i];
|
||||
j = 0;
|
||||
|
||||
while (tmpKeyData->nextKey != NULL)
|
||||
{
|
||||
if (j == 0xFE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
tmpKeyData = tmpKeyData->nextKey;
|
||||
j++;
|
||||
}
|
||||
|
||||
if (tmpKeyData->nextKey)
|
||||
{
|
||||
NULLFREE(tmpKeyData->nextKey->key);
|
||||
NULLFREE(tmpKeyData->nextKey);
|
||||
}
|
||||
tmpKeyData->nextKey = newKeyData;
|
||||
|
||||
if (writeKey)
|
||||
{
|
||||
write_key_to_file(identifier, provider, keyName, tmpKey, keyLength, comment);
|
||||
}
|
||||
}
|
||||
else // identifier != 'I'
|
||||
{
|
||||
free(KeyDB->EmuKeys[i].key);
|
||||
KeyDB->EmuKeys[i].key = tmpKey;
|
||||
KeyDB->EmuKeys[i].keyLength = keyLength;
|
||||
|
||||
if (identifier == 'F') // Update keyName (i.e. expiration date) for BISS
|
||||
{
|
||||
cs_strncpy(KeyDB->EmuKeys[i].keyName, keyName, EMU_MAX_CHAR_KEYNAME);
|
||||
}
|
||||
|
||||
if (writeKey)
|
||||
{
|
||||
write_key_to_file(identifier, provider, keyName, tmpKey, keyLength, comment);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Key does not exist on db
|
||||
if (KeyDB->keyCount + 1 > KeyDB->keyMax)
|
||||
{
|
||||
if (KeyDB->EmuKeys == NULL) // db is empty
|
||||
{
|
||||
KeyDB->EmuKeys = (KeyData *)malloc(sizeof(KeyData) * (KeyDB->keyMax + 64));
|
||||
if (KeyDB->EmuKeys == NULL)
|
||||
{
|
||||
free(tmpKey);
|
||||
return 0;
|
||||
}
|
||||
KeyDB->keyMax += 64;
|
||||
}
|
||||
else // db is full, expand it
|
||||
{
|
||||
tmpKeyData = (KeyData *)realloc(KeyDB->EmuKeys, sizeof(KeyData) * (KeyDB->keyMax + 16));
|
||||
if (tmpKeyData == NULL)
|
||||
{
|
||||
free(tmpKey);
|
||||
return 0;
|
||||
}
|
||||
KeyDB->EmuKeys = tmpKeyData;
|
||||
KeyDB->keyMax += 16;
|
||||
}
|
||||
}
|
||||
|
||||
KeyDB->EmuKeys[KeyDB->keyCount].identifier = identifier;
|
||||
KeyDB->EmuKeys[KeyDB->keyCount].provider = provider;
|
||||
|
||||
if (cs_strlen(keyName) < EMU_MAX_CHAR_KEYNAME)
|
||||
{
|
||||
cs_strncpy(KeyDB->EmuKeys[KeyDB->keyCount].keyName, keyName, EMU_MAX_CHAR_KEYNAME);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(KeyDB->EmuKeys[KeyDB->keyCount].keyName, keyName, EMU_MAX_CHAR_KEYNAME);
|
||||
}
|
||||
|
||||
KeyDB->EmuKeys[KeyDB->keyCount].keyName[EMU_MAX_CHAR_KEYNAME - 1] = 0;
|
||||
KeyDB->EmuKeys[KeyDB->keyCount].key = tmpKey;
|
||||
KeyDB->EmuKeys[KeyDB->keyCount].keyLength = keyLength;
|
||||
KeyDB->EmuKeys[KeyDB->keyCount].nextKey = NULL;
|
||||
KeyDB->keyCount++;
|
||||
|
||||
if (writeKey)
|
||||
{
|
||||
write_key_to_file(identifier, provider, keyName, tmpKey, keyLength, comment);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int8_t emu_find_key(char identifier, uint32_t provider, uint32_t providerIgnoreMask, char *keyName,
|
||||
uint8_t *key, uint32_t maxKeyLength, uint8_t isCriticalKey, uint32_t keyRef,
|
||||
uint8_t matchLength, uint32_t *getProvider)
|
||||
{
|
||||
uint32_t i;
|
||||
uint16_t j;
|
||||
uint8_t provider_matching_key_count = 0;
|
||||
KeyDataContainer *KeyDB;
|
||||
KeyData *tmpKeyData;
|
||||
|
||||
KeyDB = emu_get_key_container(identifier);
|
||||
if (KeyDB == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < KeyDB->keyCount; i++)
|
||||
{
|
||||
if ((KeyDB->EmuKeys[i].provider & ~providerIgnoreMask) != provider)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't match keyName (i.e. expiration date) for BISS
|
||||
if (identifier != 'F' && strcmp(KeyDB->EmuKeys[i].keyName, keyName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// "matchLength" cannot be used when multiple keys are allowed
|
||||
// for a single provider/keyName combination.
|
||||
// Currently this is the case only for Irdeto keys.
|
||||
if (matchLength && KeyDB->EmuKeys[i].keyLength != maxKeyLength)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (providerIgnoreMask)
|
||||
{
|
||||
if (provider_matching_key_count < keyRef)
|
||||
{
|
||||
provider_matching_key_count++;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyRef = 0;
|
||||
}
|
||||
}
|
||||
|
||||
tmpKeyData = &KeyDB->EmuKeys[i];
|
||||
|
||||
j = 0;
|
||||
while (j < keyRef && tmpKeyData->nextKey != NULL)
|
||||
{
|
||||
j++;
|
||||
tmpKeyData = tmpKeyData->nextKey;
|
||||
}
|
||||
|
||||
if (j == keyRef)
|
||||
{
|
||||
memcpy(key, tmpKeyData->key, tmpKeyData->keyLength > maxKeyLength ? maxKeyLength : tmpKeyData->keyLength);
|
||||
if (tmpKeyData->keyLength < maxKeyLength)
|
||||
{
|
||||
memset(key + tmpKeyData->keyLength, 0, maxKeyLength - tmpKeyData->keyLength);
|
||||
}
|
||||
|
||||
// Report the keyName (i.e. expiration date) of the session word found
|
||||
if (identifier == 'F')
|
||||
{
|
||||
cs_strncpy(keyName, tmpKeyData->keyName, EMU_MAX_CHAR_KEYNAME);
|
||||
}
|
||||
|
||||
if (getProvider != NULL)
|
||||
{
|
||||
(*getProvider) = tmpKeyData->provider;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCriticalKey)
|
||||
{
|
||||
cs_log("Key not found: %c %X %s", identifier, provider, keyName);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8_t emu_update_key(char identifier, uint32_t provider, char *keyName, uint8_t *key,
|
||||
uint32_t keyLength, uint8_t writeKey, char *comment)
|
||||
{
|
||||
uint32_t keyRef = 0;
|
||||
uint8_t *tmpKey = (uint8_t *)malloc(sizeof(uint8_t) * keyLength);
|
||||
|
||||
if (tmpKey == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (emu_find_key(identifier, provider, 0, keyName, tmpKey, keyLength, 0, keyRef, 0, NULL))
|
||||
{
|
||||
if (memcmp(tmpKey, key, keyLength) == 0)
|
||||
{
|
||||
free(tmpKey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
keyRef++;
|
||||
}
|
||||
|
||||
free(tmpKey);
|
||||
return emu_set_key(identifier, provider, keyName, key, keyLength, writeKey, comment, NULL);
|
||||
}
|
||||
|
||||
static int32_t delete_keys_in_container(char identifier)
|
||||
{
|
||||
// Deletes all keys stored in memory for the specified identifier,
|
||||
// but keeps the container itself, re-initialized at { NULL, 0, 0 }.
|
||||
// Returns the count of deleted keys.
|
||||
|
||||
uint32_t oldKeyCount, i;
|
||||
KeyData *tmpKeyData;
|
||||
KeyDataContainer *KeyDB = emu_get_key_container(identifier);
|
||||
|
||||
if (KeyDB == NULL || KeyDB->EmuKeys == NULL || KeyDB->keyCount == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < KeyDB->keyCount; i++)
|
||||
{
|
||||
// For Irdeto multiple keys only (linked list structure)
|
||||
while (KeyDB->EmuKeys[i].nextKey != NULL)
|
||||
{
|
||||
tmpKeyData = KeyDB->EmuKeys[i].nextKey;
|
||||
KeyDB->EmuKeys[i].nextKey = KeyDB->EmuKeys[i].nextKey->nextKey;
|
||||
free(tmpKeyData->key); // Free key
|
||||
free(tmpKeyData); // Free KeyData
|
||||
}
|
||||
|
||||
// For single keys (all identifiers, including Irdeto)
|
||||
free(KeyDB->EmuKeys[i].key); // Free key
|
||||
}
|
||||
|
||||
// Free the KeyData array
|
||||
NULLFREE(KeyDB->EmuKeys);
|
||||
oldKeyCount = KeyDB->keyCount;
|
||||
KeyDB->keyCount = 0;
|
||||
KeyDB->keyMax = 0;
|
||||
|
||||
return oldKeyCount;
|
||||
}
|
||||
|
||||
void emu_clear_keydata(void)
|
||||
{
|
||||
uint32_t total = 0;
|
||||
|
||||
total = CwKeys.keyCount;
|
||||
total += ViKeys.keyCount;
|
||||
total += NagraKeys.keyCount;
|
||||
total += IrdetoKeys.keyCount;
|
||||
total += BissSWs.keyCount;
|
||||
total += Biss2Keys.keyCount;
|
||||
total += OmnicryptKeys.keyCount;
|
||||
total += PowervuKeys.keyCount;
|
||||
total += TandbergKeys.keyCount;
|
||||
total += StreamKeys.keyCount;
|
||||
|
||||
if (total != 0)
|
||||
{
|
||||
cs_log("Freeing keys in memory: W:%d V:%d N:%d I:%d F:%d G:%d O:%d P:%d T:%d A:%d",
|
||||
CwKeys.keyCount, ViKeys.keyCount, NagraKeys.keyCount, IrdetoKeys.keyCount, BissSWs.keyCount,
|
||||
Biss2Keys.keyCount, OmnicryptKeys.keyCount, PowervuKeys.keyCount, TandbergKeys.keyCount,
|
||||
StreamKeys.keyCount);
|
||||
|
||||
delete_keys_in_container('W');
|
||||
delete_keys_in_container('V');
|
||||
delete_keys_in_container('N');
|
||||
delete_keys_in_container('I');
|
||||
delete_keys_in_container('F');
|
||||
delete_keys_in_container('G');
|
||||
delete_keys_in_container('O');
|
||||
delete_keys_in_container('P');
|
||||
delete_keys_in_container('T');
|
||||
delete_keys_in_container('A');
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t emu_read_keyfile(struct s_reader *rdr, const char *opath)
|
||||
{
|
||||
char line[1200], keyName[EMU_MAX_CHAR_KEYNAME], keyString[1026], identifier;
|
||||
char *path, *filepath, filename[EMU_KEY_FILENAME_MAX_LEN + 1];
|
||||
uint32_t pathLength, provider, keyLength;
|
||||
uint8_t fileNameLen = cs_strlen(EMU_KEY_FILENAME);
|
||||
uint8_t *key;
|
||||
struct dirent *pDirent;
|
||||
DIR *pDir;
|
||||
FILE *file = NULL;
|
||||
|
||||
pathLength = cs_strlen(opath);
|
||||
path = (char *)malloc(pathLength + 1);
|
||||
if (path == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
cs_strncpy(path, opath, pathLength + 1);
|
||||
|
||||
pathLength = cs_strlen(path);
|
||||
if (pathLength >= fileNameLen && strcasecmp(path + pathLength - fileNameLen, EMU_KEY_FILENAME) == 0)
|
||||
{
|
||||
// cut file name
|
||||
path[pathLength - fileNameLen] = '\0';
|
||||
}
|
||||
|
||||
pathLength = cs_strlen(path);
|
||||
if (path[pathLength - 1] == '/' || path[pathLength - 1] == '\\')
|
||||
{
|
||||
// cut trailing /
|
||||
path[pathLength - 1] = '\0';
|
||||
}
|
||||
|
||||
pDir = opendir(path);
|
||||
if (pDir == NULL)
|
||||
{
|
||||
cs_log("Cannot open key file path: %s", path);
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((pDirent = readdir(pDir)) != NULL)
|
||||
{
|
||||
if (strcasecmp(pDirent->d_name, EMU_KEY_FILENAME) == 0)
|
||||
{
|
||||
cs_strncpy(filename, pDirent->d_name, sizeof(filename));
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(pDir);
|
||||
|
||||
if (pDirent == NULL)
|
||||
{
|
||||
cs_log("Key file not found in: %s", path);
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pathLength = cs_strlen(path) + 1 + cs_strlen(filename) + 1;
|
||||
filepath = (char *)malloc(pathLength);
|
||||
if (filepath == NULL)
|
||||
{
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
snprintf(filepath, pathLength, "%s/%s", path, filename);
|
||||
free(path);
|
||||
|
||||
cs_log("Reading key file: %s", filepath);
|
||||
|
||||
file = fopen(filepath, "r");
|
||||
free(filepath);
|
||||
if (file == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
emu_set_keyfile_path(opath);
|
||||
|
||||
while (fgets(line, 1200, file))
|
||||
{
|
||||
if (sscanf(line, "%c %8x %11s %1024s", &identifier, &provider, keyName, keyString) != 4)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
keyLength = cs_strlen(keyString) / 2;
|
||||
key = (uint8_t *)malloc(keyLength);
|
||||
if (key == NULL)
|
||||
{
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (char_to_bin(key, keyString, cs_strlen(keyString))) // Conversion OK
|
||||
{
|
||||
emu_set_key(identifier, provider, keyName, key, keyLength, 0, NULL, rdr);
|
||||
}
|
||||
else // Non-hex characters in keyString
|
||||
{
|
||||
if ((identifier != ';' && identifier != '#' && // Skip warning for comments, etc.
|
||||
identifier != '=' && identifier != '-' &&
|
||||
identifier != ' ') &&
|
||||
!(identifier == 'F' && 0 == strncmp(keyString, "XXXXXXXXXXXX", 12))) // Skip warning for BISS 'Example key' lines
|
||||
{
|
||||
// Alert user regarding faulty line
|
||||
cs_log("WARNING: non-hex value in %s at %c %08X %s %s",
|
||||
EMU_KEY_FILENAME, identifier, provider, keyName, keyString);
|
||||
}
|
||||
}
|
||||
free(key);
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(WITH_SOFTCAM) && !defined(__APPLE__) && !defined(__ANDROID__)
|
||||
extern uint8_t SoftCamKey_Data[] __asm__("_binary_SoftCam_Key_start");
|
||||
extern uint8_t SoftCamKey_DataEnd[] __asm__("_binary_SoftCam_Key_end");
|
||||
|
||||
void emu_read_keymemory(struct s_reader *rdr)
|
||||
{
|
||||
char *keyData, *line, *saveptr, keyName[EMU_MAX_CHAR_KEYNAME], keyString[1026], identifier;
|
||||
uint32_t provider, keyLength;
|
||||
uint8_t *key;
|
||||
|
||||
keyData = (char *)malloc(SoftCamKey_DataEnd - SoftCamKey_Data + 1);
|
||||
if (keyData == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
memcpy(keyData, SoftCamKey_Data, SoftCamKey_DataEnd - SoftCamKey_Data);
|
||||
keyData[SoftCamKey_DataEnd-SoftCamKey_Data] = 0x00;
|
||||
|
||||
line = strtok_r(keyData, "\n", &saveptr);
|
||||
while (line != NULL)
|
||||
{
|
||||
if (sscanf(line, "%c %8x %11s %1024s", &identifier, &provider, keyName, keyString) != 4)
|
||||
{
|
||||
line = strtok_r(NULL, "\n", &saveptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
keyLength = cs_strlen(keyString) / 2;
|
||||
|
||||
key = (uint8_t *)malloc(keyLength);
|
||||
if (key == NULL)
|
||||
{
|
||||
free(keyData);
|
||||
return;
|
||||
}
|
||||
|
||||
if (char_to_bin(key, keyString, cs_strlen(keyString))) // Conversion OK
|
||||
{
|
||||
emu_set_key(identifier, provider, keyName, key, keyLength, 0, NULL, rdr);
|
||||
}
|
||||
else // Non-hex characters in keyString
|
||||
{
|
||||
if ((identifier != ';' && identifier != '#' && // Skip warning for comments, etc.
|
||||
identifier != '=' && identifier != '-' &&
|
||||
identifier != ' ') &&
|
||||
!(identifier == 'F' && 0 == strncmp(keyString, "XXXXXXXXXXXX", 12))) // Skip warning for BISS 'Example key' lines
|
||||
{
|
||||
// Alert user regarding faulty line
|
||||
cs_log("WARNING: non-hex value in internal keyfile at %c %08X %s %s",
|
||||
identifier, provider, keyName, keyString);
|
||||
}
|
||||
}
|
||||
free(key);
|
||||
line = strtok_r(NULL, "\n", &saveptr);
|
||||
}
|
||||
free(keyData);
|
||||
}
|
||||
#else
|
||||
void emu_read_keymemory(struct s_reader *UNUSED(rdr)) { }
|
||||
#endif
|
||||
|
||||
static const char *get_error_reason(int8_t result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case EMU_OK:
|
||||
return "No error";
|
||||
|
||||
case EMU_NOT_SUPPORTED:
|
||||
return "Not supported";
|
||||
|
||||
case EMU_KEY_NOT_FOUND:
|
||||
return "Key not found";
|
||||
|
||||
case EMU_KEY_REJECTED:
|
||||
return "ECM key rejected";
|
||||
|
||||
case EMU_CORRUPT_DATA:
|
||||
return "Corrupt data";
|
||||
|
||||
case EMU_CW_NOT_FOUND:
|
||||
return "CW not found";
|
||||
|
||||
case EMU_CHECKSUM_ERROR:
|
||||
return "Checksum error";
|
||||
|
||||
case EMU_OUT_OF_MEMORY:
|
||||
return "Out of memory";
|
||||
|
||||
default:
|
||||
return "Unknown reason";
|
||||
}
|
||||
}
|
||||
|
||||
int8_t emu_process_ecm(struct s_reader *rdr, const ECM_REQUEST *er, uint8_t *cw, EXTENDED_CW *cw_ex)
|
||||
{
|
||||
if (er->ecmlen < 3)
|
||||
{
|
||||
cs_log_dbg(D_TRACE, "Received ecm data of zero length!");
|
||||
return 4;
|
||||
}
|
||||
|
||||
uint16_t ecmLen = SCT_LEN(er->ecm);
|
||||
uint8_t ecmCopy[ecmLen];
|
||||
int8_t result = 1;
|
||||
|
||||
if (ecmLen != er->ecmlen)
|
||||
{
|
||||
cs_log_dbg(D_TRACE, "Actual ecm data length 0x%03X but ecm section length is 0x%03X",
|
||||
er->ecmlen, ecmLen);
|
||||
return 4;
|
||||
}
|
||||
|
||||
if (ecmLen > EMU_MAX_ECM_LEN)
|
||||
{
|
||||
cs_log_dbg(D_TRACE, "Actual ecm data length 0x%03X but maximum supported ecm length is 0x%03X",
|
||||
er->ecmlen, EMU_MAX_ECM_LEN);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(ecmCopy, er->ecm, ecmLen);
|
||||
|
||||
if (caid_is_viaccess(er->caid)) result = viaccess_ecm(ecmCopy, cw);
|
||||
else if (caid_is_irdeto(er->caid)) result = irdeto2_ecm(er->caid, ecmCopy, cw);
|
||||
else if (caid_is_cryptoworks(er->caid)) result = cryptoworks_ecm(er->caid, ecmCopy, cw);
|
||||
else if (caid_is_powervu(er->caid))
|
||||
{
|
||||
#ifdef MODULE_STREAMRELAY
|
||||
result = powervu_ecm(ecmCopy, cw, cw_ex, er->srvid, er->caid, er->tsid, er->onid, er->ens, NULL);
|
||||
#else
|
||||
result = powervu_ecm(ecmCopy, cw, cw_ex, er->srvid, er->caid, er->tsid, er->onid, er->ens);
|
||||
#endif
|
||||
}
|
||||
else if (caid_is_director(er->caid)) result = director_ecm(ecmCopy, cw);
|
||||
else if (caid_is_nagra(er->caid)) result = nagra2_ecm(ecmCopy, cw);
|
||||
else if (caid_is_biss(er->caid)) result = biss_ecm(rdr, er->ecm, er->caid, er->pid, cw, cw_ex);
|
||||
else if (er->caid == 0x00FF) result = omnicrypt_ecm(ecmCopy, cw); // temp caid
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
cs_log("ECM failed: %s", get_error_reason(result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int8_t emu_process_emm(struct s_reader *rdr, uint16_t caid, const uint8_t *emm, uint32_t *keysAdded)
|
||||
{
|
||||
uint16_t emmLen = SCT_LEN(emm);
|
||||
uint8_t emmCopy[emmLen];
|
||||
int8_t result = 1;
|
||||
|
||||
if (emmLen > EMU_MAX_EMM_LEN)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
memcpy(emmCopy, emm, emmLen);
|
||||
*keysAdded = 0;
|
||||
|
||||
if (caid_is_viaccess(caid)) result = viaccess_emm(emmCopy, keysAdded);
|
||||
else if (caid_is_irdeto(caid)) result = irdeto2_emm(caid, emmCopy, keysAdded);
|
||||
else if (caid_is_powervu(caid)) result = powervu_emm(emmCopy, keysAdded);
|
||||
else if (caid_is_director(caid)) result = director_emm(emmCopy, keysAdded);
|
||||
else if (caid_is_biss_dynamic(caid)) result = biss_emm(rdr, emmCopy, keysAdded);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
cs_log_dbg(D_EMM,"EMM failed: %s", get_error_reason(result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // WITH_EMU
|
||||
96
module-emulator-osemu.h
Executable file
96
module-emulator-osemu.h
Executable file
|
|
@ -0,0 +1,96 @@
|
|||
#ifndef MODULE_EMULATOR_OSEMU_H_
|
||||
#define MODULE_EMULATOR_OSEMU_H_
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
// Version info
|
||||
#define EMU_VERSION 802
|
||||
|
||||
#define EMU_MAX_CHAR_KEYNAME 12
|
||||
#define EMU_KEY_FILENAME "SoftCam.Key"
|
||||
#define EMU_KEY_FILENAME_MAX_LEN 31
|
||||
#define EMU_MAX_ECM_LEN MAX_ECM_SIZE
|
||||
#define EMU_MAX_EMM_LEN MAX_EMM_SIZE
|
||||
|
||||
/*
|
||||
* Error codes for ProccessECM and ProccessEMM functions
|
||||
* 0 - OK
|
||||
* 1 - ECM / EMM not supported
|
||||
* 2 - ECM / EMM key not found
|
||||
* 3 - ECM key rejected
|
||||
* 4 - Corrupt data
|
||||
* 5 - CW not found
|
||||
* 6 - CW / ECM / EMM checksum error
|
||||
* 7 - Out of memory
|
||||
*/
|
||||
|
||||
#define EMU_OK 0
|
||||
#define EMU_NOT_SUPPORTED 1
|
||||
#define EMU_KEY_NOT_FOUND 2
|
||||
#define EMU_KEY_REJECTED 3
|
||||
#define EMU_CORRUPT_DATA 4
|
||||
#define EMU_CW_NOT_FOUND 5
|
||||
#define EMU_CHECKSUM_ERROR 6
|
||||
#define EMU_OUT_OF_MEMORY 7
|
||||
|
||||
typedef struct KeyData KeyData;
|
||||
|
||||
struct KeyData
|
||||
{
|
||||
char identifier;
|
||||
uint32_t provider;
|
||||
char keyName[EMU_MAX_CHAR_KEYNAME];
|
||||
uint8_t *key;
|
||||
uint32_t keyLength;
|
||||
KeyData *nextKey;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
KeyData *EmuKeys;
|
||||
uint32_t keyCount;
|
||||
uint32_t keyMax;
|
||||
} KeyDataContainer;
|
||||
|
||||
extern KeyDataContainer CwKeys;
|
||||
extern KeyDataContainer ViKeys;
|
||||
extern KeyDataContainer NagraKeys;
|
||||
extern KeyDataContainer IrdetoKeys;
|
||||
extern KeyDataContainer BissSWs; // 'F' identifier - BISS1 and BISS2 mode 1/E session words
|
||||
extern KeyDataContainer Biss2Keys; // 'G' identifier - BISS2 mode CA session keys (ECM keys)
|
||||
extern KeyDataContainer OmnicryptKeys;
|
||||
extern KeyDataContainer PowervuKeys;
|
||||
extern KeyDataContainer TandbergKeys;
|
||||
extern KeyDataContainer StreamKeys;
|
||||
extern uint8_t viasat_const[];
|
||||
extern char *emu_keyfile_path;
|
||||
extern pthread_mutex_t emu_key_data_mutex;
|
||||
|
||||
void emu_set_keyfile_path(const char *path);
|
||||
void emu_clear_keydata(void);
|
||||
uint8_t emu_read_keyfile(struct s_reader *rdr, const char *path);
|
||||
void emu_read_keymemory(struct s_reader *rdr);
|
||||
|
||||
int8_t is_valid_dcw(uint8_t *dw);
|
||||
int8_t char_to_bin(uint8_t *out, const char *in, uint32_t inLen);
|
||||
void date_to_str(char *dateStr, uint8_t len, int8_t offset, uint8_t format);
|
||||
|
||||
KeyDataContainer *emu_get_key_container(char identifier);
|
||||
|
||||
int8_t emu_process_ecm(struct s_reader *rdr, const ECM_REQUEST *er, uint8_t *cw, EXTENDED_CW* cw_ex);
|
||||
|
||||
int8_t emu_process_emm(struct s_reader *rdr, uint16_t caid, const uint8_t *emm, uint32_t *keysAdded);
|
||||
|
||||
int8_t emu_find_key(char identifier, uint32_t provider, uint32_t providerIgnoreMask, char *keyName,
|
||||
uint8_t *key, uint32_t maxKeyLength, uint8_t isCriticalKey, uint32_t keyRef,
|
||||
uint8_t matchLength, uint32_t *getProvider);
|
||||
|
||||
int8_t emu_set_key(char identifier, uint32_t provider, char *keyName, uint8_t *orgKey, uint32_t keyLength,
|
||||
uint8_t writeKey, char *comment, struct s_reader *rdr);
|
||||
|
||||
int8_t emu_update_key(char identifier, uint32_t provider, char *keyName, uint8_t *key, uint32_t keyLength,
|
||||
uint8_t writeKey, char *comment);
|
||||
|
||||
#endif // WITH_EMU
|
||||
|
||||
#endif // MODULE_EMULATOR_H_
|
||||
2795
module-emulator-powervu.c
Executable file
2795
module-emulator-powervu.c
Executable file
File diff suppressed because it is too large
Load Diff
63
module-emulator-powervu.h
Executable file
63
module-emulator-powervu.h
Executable file
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef MODULE_EMULATOR_POWERVU_H
|
||||
#define MODULE_EMULATOR_POWERVU_H
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
#define PVU_CW_VID 0 // VIDeo
|
||||
#define PVU_CW_HSD 1 // High Speed Data
|
||||
#define PVU_CW_A1 2 // Audio 1
|
||||
#define PVU_CW_A2 3 // Audio 2
|
||||
#define PVU_CW_A3 4 // Audio 3
|
||||
#define PVU_CW_A4 5 // Audio 4
|
||||
#define PVU_CW_UTL 6 // UTiLity
|
||||
#define PVU_CW_VBI 7 // Vertical Blanking Interval
|
||||
|
||||
#define PVU_CONVCW_VID_ECM 0x80 // VIDeo
|
||||
#define PVU_CONVCW_HSD_ECM 0x40 // High Speed Data
|
||||
#define PVU_CONVCW_A1_ECM 0x20 // Audio 1
|
||||
#define PVU_CONVCW_A2_ECM 0x10 // Audio 2
|
||||
#define PVU_CONVCW_A3_ECM 0x08 // Audio 3
|
||||
#define PVU_CONVCW_A4_ECM 0x04 // Audio 4
|
||||
#define PVU_CONVCW_UTL_ECM 0x02 // UTiLity
|
||||
#define PVU_CONVCW_VBI_ECM 0x01 // Vertical Blanking Interval
|
||||
|
||||
#ifdef MODULE_STREAMRELAY
|
||||
int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens, emu_stream_client_key_data *cdata);
|
||||
#else
|
||||
int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens);
|
||||
#endif
|
||||
int8_t powervu_emm(uint8_t *emm, uint32_t *keysAdded);
|
||||
|
||||
/*
|
||||
* This function searches for EMM keys and adds their Unique Addresses (UA) as EMM filters.
|
||||
* The EMM keys are picked from all group id's that have ECM keys for the srvid specified
|
||||
* as input. If there is a large ammount of EMM keys matching these criteria, only the first
|
||||
* "maxCount" UA's are added as EMM filters. The rest are not used at all.
|
||||
*
|
||||
* In the rare case where two or more EMM keys with the same UA belong to different groups,
|
||||
* and these groups also have ECM keys for the srvid in request, there is a chance the ECM
|
||||
* keys in the "wrong" group to be updated. This is because the EMM algorithm has no way of
|
||||
* knowing in which group the service id belongs to. A workaround for this designing flaw
|
||||
* is to make sure there are no EMM keys with the same UA between different groups.
|
||||
*
|
||||
* Hexserials must be of type "uint8_t hexserials[maxCount][4]". If srvid is equal to 0xFFFF
|
||||
* all serials are added (no service id filtering is done). Returns the count of hexserials
|
||||
* added as filters.
|
||||
*/
|
||||
int8_t powervu_get_hexserials(uint8_t hexserials[][4], uint32_t maxCount, uint16_t srvid);
|
||||
|
||||
/*
|
||||
* Like the previous function, it adds UAs as EMM filters. It is used in conjunction with the
|
||||
* new method of entering ECM keys, where one key can serve every channel in the group. Since
|
||||
* there is no srvid to search for, we need to know the group id prior to searching for EMM
|
||||
* keys. To do so, this function calulates a hash using the tsid, onid and enigma namespace of
|
||||
* the transponder, which is only available in enigma2.
|
||||
*
|
||||
* Hexserials must be of type "uint8_t hexserials[maxCount][4]" like before. It returns the
|
||||
* count of hexserials added as filters.
|
||||
*/
|
||||
int8_t powervu_get_hexserials_new(uint8_t hexserials[][4], uint32_t maxCount, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens);
|
||||
|
||||
#endif // WITH_EMU
|
||||
|
||||
#endif // MODULE_EMULATOR_POWERVU_H
|
||||
1183
module-emulator-viaccess.c
Executable file
1183
module-emulator-viaccess.c
Executable file
File diff suppressed because it is too large
Load Diff
11
module-emulator-viaccess.h
Executable file
11
module-emulator-viaccess.h
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef MODULE_EMULATOR_VIACCESS_H
|
||||
#define MODULE_EMULATOR_VIACCESS_H
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
int8_t viaccess_ecm(uint8_t *ecm, uint8_t *dw);
|
||||
int8_t viaccess_emm(uint8_t *emm, uint32_t *keysAdded);
|
||||
|
||||
#endif // WITH_EMU
|
||||
|
||||
#endif // MODULE_EMULATOR_VIACCESS_H
|
||||
894
module-emulator.c
Executable file
894
module-emulator.c
Executable file
|
|
@ -0,0 +1,894 @@
|
|||
#define MODULE_LOG_PREFIX "emu"
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#ifdef WITH_EMU
|
||||
|
||||
#include "module-streamrelay.h"
|
||||
#include "module-emulator-osemu.h"
|
||||
#include "module-emulator-biss.h"
|
||||
#include "module-emulator-irdeto.h"
|
||||
#include "module-emulator-powervu.h"
|
||||
#include "oscam-conf-chk.h"
|
||||
#include "oscam-config.h"
|
||||
#include "oscam-reader.h"
|
||||
#include "oscam-string.h"
|
||||
|
||||
/*
|
||||
* Readers in OSCam consist of 2 basic parts.
|
||||
* The hardware or the device part. This is where physical smart cards are inserted
|
||||
* and made available to OSCam.
|
||||
* The software or the emulation part. This is where the actual card reading is done,
|
||||
* including ecm and emm processing (i.e emulation of the various cryptosystems).
|
||||
* In the Emu reader, the device part has no meaning, but we have to create it in
|
||||
* order to be compatible with OSCam's reader structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Create the Emu "emulation" part. This is of type s_cardsystem.
|
||||
* Similar structures are found in the main sources folder (files reader-xxxxxx.c)
|
||||
* for every cryptosystem supported by OSCam.
|
||||
* Here we read keys from our virtual card (aka the SoftCam.Key file) and we inform
|
||||
* OSCam about them. This is done with the emu_card_info() function. Keep in mind
|
||||
* that Emu holds all its keys to separate structures for faster access.
|
||||
* In addition, ECM and EMM requests are processed here, with the emu_do_ecm() and
|
||||
* emu_do_emm() functions.
|
||||
*/
|
||||
|
||||
#define CS_OK 1
|
||||
#define CS_ERROR 0
|
||||
|
||||
extern char cs_confdir[128];
|
||||
#ifdef MODULE_STREAMRELAY
|
||||
static int8_t emu_key_data_mutex_init = 0;
|
||||
#endif
|
||||
pthread_mutex_t emu_key_data_mutex;
|
||||
|
||||
static void set_hexserial_to_version(struct s_reader *rdr)
|
||||
{
|
||||
char cVersion[32];
|
||||
uint32_t version = EMU_VERSION;
|
||||
uint8_t hversion[2];
|
||||
memset(hversion, 0, 2);
|
||||
snprintf(cVersion, sizeof(cVersion), "%04d", version);
|
||||
char_to_bin(hversion, cVersion, 4);
|
||||
rdr->hexserial[3] = hversion[0];
|
||||
rdr->hexserial[4] = hversion[1];
|
||||
}
|
||||
|
||||
static void set_prids(struct s_reader *rdr)
|
||||
{
|
||||
int32_t i, j;
|
||||
|
||||
rdr->nprov = 0;
|
||||
|
||||
for (i = 0; (i < rdr->emu_auproviders.nfilts) && (rdr->nprov < CS_MAXPROV); i++)
|
||||
{
|
||||
for (j = 0; (j < rdr->emu_auproviders.filts[i].nprids) && (rdr->nprov < CS_MAXPROV); j++)
|
||||
{
|
||||
i2b_buf(4, rdr->emu_auproviders.filts[i].prids[j], rdr->prid[i]);
|
||||
rdr->nprov++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void emu_add_entitlement(struct s_reader *rdr, uint16_t caid, uint32_t provid, uint8_t *key, char *keyName, uint32_t keyLength, uint8_t isData)
|
||||
{
|
||||
if (!rdr->ll_entitlements)
|
||||
{
|
||||
rdr->ll_entitlements = ll_create("ll_entitlements");
|
||||
}
|
||||
|
||||
S_ENTITLEMENT *item;
|
||||
if (cs_malloc(&item, sizeof(S_ENTITLEMENT)))
|
||||
{
|
||||
// fill item
|
||||
item->caid = caid;
|
||||
item->provid = provid;
|
||||
item->id = 0;
|
||||
item->class = 0;
|
||||
item->start = 0;
|
||||
item->end = 2147472000;
|
||||
item->type = 0;
|
||||
item->isKey = 1;
|
||||
memcpy(item->name, keyName, 8);
|
||||
item->key = key;
|
||||
item->keyLength = keyLength;
|
||||
item->isData = isData;
|
||||
|
||||
// add item
|
||||
ll_append(rdr->ll_entitlements, item);
|
||||
}
|
||||
}
|
||||
|
||||
static void refresh_entitlements(struct s_reader *rdr)
|
||||
{
|
||||
uint32_t i;
|
||||
uint16_t caid;
|
||||
KeyData *tmpKeyData;
|
||||
LL_ITER itr;
|
||||
biss2_rsa_key_t *item;
|
||||
|
||||
cs_clear_entitlement(rdr);
|
||||
|
||||
for (i = 0; i < StreamKeys.keyCount; i++)
|
||||
{
|
||||
emu_add_entitlement(rdr, b2i(2, StreamKeys.EmuKeys[i].key), StreamKeys.EmuKeys[i].provider, StreamKeys.EmuKeys[i].key,
|
||||
StreamKeys.EmuKeys[i].keyName, StreamKeys.EmuKeys[i].keyLength, 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < ViKeys.keyCount; i++)
|
||||
{
|
||||
emu_add_entitlement(rdr, 0x0500, ViKeys.EmuKeys[i].provider, ViKeys.EmuKeys[i].key,
|
||||
ViKeys.EmuKeys[i].keyName, ViKeys.EmuKeys[i].keyLength, 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < IrdetoKeys.keyCount; i++)
|
||||
{
|
||||
tmpKeyData = &IrdetoKeys.EmuKeys[i];
|
||||
do
|
||||
{
|
||||
emu_add_entitlement(rdr, tmpKeyData->provider >> 8, tmpKeyData->provider & 0xFF,
|
||||
tmpKeyData->key, tmpKeyData->keyName, tmpKeyData->keyLength, 0);
|
||||
|
||||
tmpKeyData = tmpKeyData->nextKey;
|
||||
}
|
||||
while (tmpKeyData != NULL);
|
||||
}
|
||||
|
||||
for (i = 0; i < CwKeys.keyCount; i++)
|
||||
{
|
||||
emu_add_entitlement(rdr, CwKeys.EmuKeys[i].provider >> 8, CwKeys.EmuKeys[i].provider & 0xFF,
|
||||
CwKeys.EmuKeys[i].key, CwKeys.EmuKeys[i].keyName, CwKeys.EmuKeys[i].keyLength, 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < PowervuKeys.keyCount; i++)
|
||||
{
|
||||
emu_add_entitlement(rdr, 0x0E00, PowervuKeys.EmuKeys[i].provider, PowervuKeys.EmuKeys[i].key,
|
||||
PowervuKeys.EmuKeys[i].keyName, PowervuKeys.EmuKeys[i].keyLength, 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < TandbergKeys.keyCount; i++)
|
||||
{
|
||||
emu_add_entitlement(rdr, 0x1010, TandbergKeys.EmuKeys[i].provider, TandbergKeys.EmuKeys[i].key,
|
||||
TandbergKeys.EmuKeys[i].keyName, TandbergKeys.EmuKeys[i].keyLength, 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < NagraKeys.keyCount; i++)
|
||||
{
|
||||
emu_add_entitlement(rdr, 0x1801, NagraKeys.EmuKeys[i].provider, NagraKeys.EmuKeys[i].key,
|
||||
NagraKeys.EmuKeys[i].keyName, NagraKeys.EmuKeys[i].keyLength, 0);
|
||||
}
|
||||
|
||||
// Session words for BISS1 mode 1/E (caid 2600) and BISS2 mode 1/E (caid 2602)
|
||||
for (i = 0; i < BissSWs.keyCount; i++)
|
||||
{
|
||||
caid = (BissSWs.EmuKeys[i].keyLength == 8) ? 0x2600 : 0x2602;
|
||||
emu_add_entitlement(rdr, caid, BissSWs.EmuKeys[i].provider, BissSWs.EmuKeys[i].key,
|
||||
BissSWs.EmuKeys[i].keyName, BissSWs.EmuKeys[i].keyLength, 0);
|
||||
}
|
||||
|
||||
// Session keys (ECM keys) for BISS2 mode CA
|
||||
for (i = 0; i < Biss2Keys.keyCount; i++)
|
||||
{
|
||||
emu_add_entitlement(rdr, 0x2610, Biss2Keys.EmuKeys[i].provider, Biss2Keys.EmuKeys[i].key,
|
||||
Biss2Keys.EmuKeys[i].keyName, Biss2Keys.EmuKeys[i].keyLength, 0);
|
||||
}
|
||||
|
||||
// RSA keys (EMM keys) for BISS2 mode CA
|
||||
itr = ll_iter_create(rdr->ll_biss2_rsa_keys);
|
||||
while ((item = ll_iter_next(&itr)))
|
||||
{
|
||||
emu_add_entitlement(rdr, 0x2610, 0, item->ekid, "RSAPRI", 8, 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < OmnicryptKeys.keyCount; i++)
|
||||
{
|
||||
emu_add_entitlement(rdr, 0x00FF, OmnicryptKeys.EmuKeys[i].provider, OmnicryptKeys.EmuKeys[i].key,
|
||||
OmnicryptKeys.EmuKeys[i].keyName, OmnicryptKeys.EmuKeys[i].keyLength, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t emu_do_ecm(struct s_reader *rdr, const ECM_REQUEST *er, struct s_ecm_answer *ea)
|
||||
{
|
||||
if (!emu_process_ecm(rdr, er, ea->cw, &ea->cw_ex))
|
||||
{
|
||||
return CS_OK;
|
||||
}
|
||||
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
static int32_t emu_do_emm(struct s_reader *rdr, EMM_PACKET *emm)
|
||||
{
|
||||
uint32_t keysAdded = 0;
|
||||
|
||||
if (emm->emmlen < 3)
|
||||
{
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
if (SCT_LEN(emm->emm) > emm->emmlen)
|
||||
{
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
if (!emu_process_emm(rdr, b2i(2, emm->caid), emm->emm, &keysAdded))
|
||||
{
|
||||
if (keysAdded > 0)
|
||||
{
|
||||
refresh_entitlements(rdr);
|
||||
}
|
||||
|
||||
return CS_OK;
|
||||
}
|
||||
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
static int32_t emu_card_info(struct s_reader *rdr)
|
||||
{
|
||||
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||||
|
||||
// Delete keys from Emu's memory
|
||||
emu_clear_keydata();
|
||||
|
||||
// Delete BISS2 mode CA RSA keys
|
||||
ll_destroy_data(&rdr->ll_biss2_rsa_keys);
|
||||
|
||||
// Read keys built in the OSCam-Emu binary
|
||||
emu_read_keymemory(rdr);
|
||||
|
||||
// Read keys from SoftCam.Key file
|
||||
emu_set_keyfile_path(cs_confdir);
|
||||
|
||||
if (!emu_read_keyfile(rdr, cs_confdir))
|
||||
{
|
||||
if (emu_read_keyfile(rdr, "/var/keys/"))
|
||||
{
|
||||
emu_set_keyfile_path("/var/keys/");
|
||||
}
|
||||
}
|
||||
|
||||
// Read BISS2 mode CA RSA keys from PEM files
|
||||
biss_read_pem(rdr, BISS2_MAX_RSA_KEYS);
|
||||
|
||||
cs_log("Total keys in memory: W:%d V:%d N:%d I:%d F:%d G:%d O:%d P:%d T:%d A:%d",
|
||||
CwKeys.keyCount, ViKeys.keyCount, NagraKeys.keyCount, IrdetoKeys.keyCount, BissSWs.keyCount,
|
||||
Biss2Keys.keyCount, OmnicryptKeys.keyCount, PowervuKeys.keyCount, TandbergKeys.keyCount,
|
||||
StreamKeys.keyCount);
|
||||
|
||||
// Inform OSCam about all available keys.
|
||||
// This is used for listing the "entitlements" in the webif's reader page.
|
||||
refresh_entitlements(rdr);
|
||||
|
||||
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||||
|
||||
set_prids(rdr);
|
||||
|
||||
set_hexserial_to_version(rdr);
|
||||
|
||||
return CS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
static int32_t emu_card_init(struct s_reader *UNUSED(rdr), struct s_ATR *UNUSED(atr))
|
||||
{
|
||||
return CS_ERROR;
|
||||
}
|
||||
*/
|
||||
|
||||
int32_t emu_get_via3_emm_type(EMM_PACKET *ep, struct s_reader *rdr)
|
||||
{
|
||||
uint32_t provid = 0;
|
||||
|
||||
if(ep->emm[3] == 0x90 && ep->emm[4] == 0x03)
|
||||
{
|
||||
provid = b2i(3, ep->emm + 5);
|
||||
provid &= 0xFFFFF0;
|
||||
i2b_buf(4, provid, ep->provid);
|
||||
}
|
||||
|
||||
switch (ep->emm[0])
|
||||
{
|
||||
case 0x88:
|
||||
ep->type = UNIQUE;
|
||||
memset(ep->hexserial, 0, 8);
|
||||
memcpy(ep->hexserial, ep->emm + 4, 4);
|
||||
rdr_log_dbg(rdr, D_EMM, "UNIQUE");
|
||||
return 1;
|
||||
|
||||
case 0x8A:
|
||||
case 0x8B:
|
||||
ep->type = GLOBAL;
|
||||
rdr_log_dbg(rdr, D_EMM, "GLOBAL");
|
||||
return 1;
|
||||
|
||||
case 0x8C:
|
||||
case 0x8D:
|
||||
ep->type = SHARED;
|
||||
rdr_log_dbg(rdr, D_EMM, "SHARED (part)");
|
||||
// We need those packets to pass otherwise we would never
|
||||
// be able to complete EMM reassembly
|
||||
return 1;
|
||||
|
||||
case 0x8E:
|
||||
ep->type = SHARED;
|
||||
rdr_log_dbg(rdr, D_EMM, "SHARED");
|
||||
memset(ep->hexserial, 0, 8);
|
||||
memcpy(ep->hexserial, ep->emm + 3, 3);
|
||||
return 1;
|
||||
|
||||
default:
|
||||
ep->type = UNKNOWN;
|
||||
rdr_log_dbg(rdr, D_EMM, "UNKNOWN");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t emu_get_ird2_emm_type(EMM_PACKET *ep, struct s_reader *rdr)
|
||||
{
|
||||
int32_t l = (ep->emm[3] & 0x07);
|
||||
int32_t base = (ep->emm[3] >> 3);
|
||||
char dumprdrserial[l * 3], dumpemmserial[l * 3];
|
||||
|
||||
switch (l)
|
||||
{
|
||||
case 0:
|
||||
// global emm, 0 bytes addressed
|
||||
ep->type = GLOBAL;
|
||||
rdr_log_dbg(rdr, D_EMM, "GLOBAL base = %02x", base);
|
||||
return 1;
|
||||
|
||||
case 2:
|
||||
// shared emm, 2 bytes addressed
|
||||
ep->type = SHARED;
|
||||
memset(ep->hexserial, 0, 8);
|
||||
memcpy(ep->hexserial, ep->emm + 4, l);
|
||||
cs_hexdump(1, rdr->hexserial, l, dumprdrserial, sizeof(dumprdrserial));
|
||||
cs_hexdump(1, ep->hexserial, l, dumpemmserial, sizeof(dumpemmserial));
|
||||
rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED l = %d ep = {%s} rdr = {%s} base = %02x",
|
||||
l, dumpemmserial, dumprdrserial, base);
|
||||
return 1;
|
||||
|
||||
case 3:
|
||||
// unique emm, 3 bytes addressed
|
||||
ep->type = UNIQUE;
|
||||
memset(ep->hexserial, 0, 8);
|
||||
memcpy(ep->hexserial, ep->emm + 4, l);
|
||||
cs_hexdump(1, rdr->hexserial, l, dumprdrserial, sizeof(dumprdrserial));
|
||||
cs_hexdump(1, ep->hexserial, l, dumpemmserial, sizeof(dumpemmserial));
|
||||
rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE l = %d ep = {%s} rdr = {%s} base = %02x",
|
||||
l, dumpemmserial, dumprdrserial, base);
|
||||
return 1;
|
||||
|
||||
default:
|
||||
ep->type = UNKNOWN;
|
||||
rdr_log_dbg(rdr, D_EMM, "UNKNOWN");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t emu_get_pvu_emm_type(EMM_PACKET *ep, struct s_reader *rdr)
|
||||
{
|
||||
if (ep->emm[0] == 0x82)
|
||||
{
|
||||
ep->type = UNIQUE;
|
||||
memset(ep->hexserial, 0, 8);
|
||||
memcpy(ep->hexserial, ep->emm + 12, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
ep->type = UNKNOWN;
|
||||
rdr_log_dbg(rdr, D_EMM, "UNKNOWN");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t emu_get_tan_emm_type(EMM_PACKET *ep, struct s_reader *rdr)
|
||||
{
|
||||
if (ep->emm[0] == 0x82 || ep->emm[0] == 0x83)
|
||||
{
|
||||
ep->type = GLOBAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
ep->type = UNKNOWN;
|
||||
rdr_log_dbg(rdr, D_EMM, "UNKNOWN");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t emu_get_biss_emm_type(EMM_PACKET *ep, struct s_reader *rdr)
|
||||
{
|
||||
switch (ep->emm[0])
|
||||
{
|
||||
case 0x81: // Spec say this is for EMM, but oscam (and all other crypto systems) use it for ECM
|
||||
case 0x82:
|
||||
case 0x83:
|
||||
case 0x84:
|
||||
case 0x85:
|
||||
case 0x86:
|
||||
case 0x87:
|
||||
case 0x88:
|
||||
case 0x89:
|
||||
case 0x8A:
|
||||
case 0x8B:
|
||||
case 0x8C:
|
||||
case 0x8D:
|
||||
case 0x8E:
|
||||
case 0x8F:
|
||||
ep->type = GLOBAL;
|
||||
return 1;
|
||||
|
||||
default:
|
||||
ep->type = UNKNOWN;
|
||||
rdr_log_dbg(rdr, D_EMM, "UNKNOWN");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t emu_get_emm_type(struct emm_packet_t *ep, struct s_reader *rdr)
|
||||
{
|
||||
uint16_t caid = b2i(2, ep->caid);
|
||||
|
||||
if (caid_is_viaccess(caid)) return emu_get_via3_emm_type(ep, rdr);
|
||||
if (caid_is_irdeto(caid)) return emu_get_ird2_emm_type(ep, rdr);
|
||||
if (caid_is_powervu(caid)) return emu_get_pvu_emm_type(ep, rdr);
|
||||
if (caid_is_director(caid)) return emu_get_tan_emm_type(ep, rdr);
|
||||
if (caid_is_biss_dynamic(caid)) return emu_get_biss_emm_type(ep, rdr);
|
||||
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
FILTER *get_emu_prids_for_caid(struct s_reader *rdr, uint16_t caid)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; i < rdr->emu_auproviders.nfilts; i++)
|
||||
{
|
||||
if (caid == rdr->emu_auproviders.filts[i].caid)
|
||||
{
|
||||
return &rdr->emu_auproviders.filts[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int32_t emu_get_via3_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid))
|
||||
{
|
||||
if (*emm_filters == NULL)
|
||||
{
|
||||
const unsigned int max_filter_count = 1;
|
||||
if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter)))
|
||||
{
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
struct s_csystem_emm_filter *filters = *emm_filters;
|
||||
*filter_count = 0;
|
||||
|
||||
int32_t idx = 0;
|
||||
|
||||
filters[idx].type = EMM_GLOBAL;
|
||||
filters[idx].enabled = 1;
|
||||
filters[idx].filter[0] = 0x8A;
|
||||
filters[idx].mask[0] = 0xFE;
|
||||
filters[idx].filter[3] = 0x80;
|
||||
filters[idx].mask[3] = 0x80;
|
||||
idx++;
|
||||
|
||||
*filter_count = idx;
|
||||
}
|
||||
|
||||
return CS_OK;
|
||||
}
|
||||
|
||||
static int32_t emu_get_ird2_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t caid, uint32_t UNUSED(provid))
|
||||
{
|
||||
uint8_t hexserial[3], prid[4];
|
||||
FILTER *emu_provids;
|
||||
int8_t have_provid = 0, have_serial = 0;
|
||||
int32_t i;
|
||||
|
||||
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||||
if(irdeto2_get_hexserial(caid, hexserial))
|
||||
{
|
||||
have_serial = 1;
|
||||
}
|
||||
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||||
|
||||
emu_provids = get_emu_prids_for_caid(rdr, caid);
|
||||
if (emu_provids != NULL && emu_provids->nprids > 0)
|
||||
{
|
||||
have_provid = 1;
|
||||
}
|
||||
|
||||
if (*emm_filters == NULL)
|
||||
{
|
||||
const unsigned int max_filter_count = have_serial + (2 * (have_provid ? emu_provids->nprids : 0));
|
||||
if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter)))
|
||||
{
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
struct s_csystem_emm_filter *filters = *emm_filters;
|
||||
*filter_count = 0;
|
||||
|
||||
unsigned int idx = 0;
|
||||
|
||||
if (have_serial)
|
||||
{
|
||||
filters[idx].type = EMM_UNIQUE;
|
||||
filters[idx].enabled = 1;
|
||||
filters[idx].filter[0] = 0x82;
|
||||
filters[idx].mask[0] = 0xFF;
|
||||
filters[idx].filter[1] = 0xFB;
|
||||
filters[idx].mask[1] = 0x07;
|
||||
memcpy(&filters[idx].filter[2], hexserial, 3);
|
||||
memset(&filters[idx].mask[2], 0xFF, 3);
|
||||
idx++;
|
||||
}
|
||||
|
||||
for (i = 0; have_provid && i < emu_provids->nprids; i++)
|
||||
{
|
||||
i2b_buf(4, emu_provids->prids[i], prid);
|
||||
|
||||
filters[idx].type = EMM_UNIQUE;
|
||||
filters[idx].enabled = 1;
|
||||
filters[idx].filter[0] = 0x82;
|
||||
filters[idx].mask[0] = 0xFF;
|
||||
filters[idx].filter[1] = 0xFB;
|
||||
filters[idx].mask[1] = 0x07;
|
||||
memcpy(&filters[idx].filter[2], &prid[1], 3);
|
||||
memset(&filters[idx].mask[2], 0xFF, 3);
|
||||
idx++;
|
||||
|
||||
filters[idx].type = EMM_SHARED;
|
||||
filters[idx].enabled = 1;
|
||||
filters[idx].filter[0] = 0x82;
|
||||
filters[idx].mask[0] = 0xFF;
|
||||
filters[idx].filter[1] = 0xFA;
|
||||
filters[idx].mask[1] = 0x07;
|
||||
memcpy(&filters[idx].filter[2], &prid[1], 2);
|
||||
memset(&filters[idx].mask[2], 0xFF, 2);
|
||||
idx++;
|
||||
}
|
||||
|
||||
*filter_count = idx;
|
||||
}
|
||||
|
||||
return CS_OK;
|
||||
}
|
||||
|
||||
static int32_t emu_get_pvu_emm_filter(struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count,
|
||||
uint16_t caid, uint16_t srvid, uint16_t tsid, uint16_t onid, uint32_t ens)
|
||||
{
|
||||
uint8_t hexserials[32][4];
|
||||
uint32_t i, count = 0;
|
||||
|
||||
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||||
count = powervu_get_hexserials_new(hexserials, 32, caid, tsid, onid, ens);
|
||||
if (count == 0)
|
||||
{
|
||||
count = powervu_get_hexserials(hexserials, 32, srvid);
|
||||
if (count == 0)
|
||||
{
|
||||
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||||
return CS_ERROR;
|
||||
}
|
||||
}
|
||||
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||||
|
||||
if (*emm_filters == NULL)
|
||||
{
|
||||
const unsigned int max_filter_count = count;
|
||||
if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter)))
|
||||
{
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
struct s_csystem_emm_filter *filters = *emm_filters;
|
||||
*filter_count = 0;
|
||||
|
||||
int32_t idx = 0;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
filters[idx].type = EMM_UNIQUE;
|
||||
filters[idx].enabled = 1;
|
||||
filters[idx].filter[0] = 0x82;
|
||||
filters[idx].filter[10] = hexserials[i][0];
|
||||
filters[idx].filter[11] = hexserials[i][1];
|
||||
filters[idx].filter[12] = hexserials[i][2];
|
||||
filters[idx].filter[13] = hexserials[i][3];
|
||||
filters[idx].mask[0] = 0xFF;
|
||||
filters[idx].mask[10] = 0xFF;
|
||||
filters[idx].mask[11] = 0xFF;
|
||||
filters[idx].mask[12] = 0xFF;
|
||||
filters[idx].mask[13] = 0xFF;
|
||||
idx++;
|
||||
}
|
||||
|
||||
*filter_count = idx;
|
||||
}
|
||||
|
||||
return CS_OK;
|
||||
}
|
||||
|
||||
static int32_t emu_get_tan_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid))
|
||||
{
|
||||
if (*emm_filters == NULL)
|
||||
{
|
||||
const unsigned int max_filter_count = 2;
|
||||
uint8_t buf[8];
|
||||
|
||||
if (!emu_find_key('T', 0x40, 0, "MK", buf, 8, 0, 0, 0, NULL) &&
|
||||
!emu_find_key('T', 0x40, 0, "MK01", buf, 8, 0, 0, 0, NULL))
|
||||
{
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter)))
|
||||
{
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
struct s_csystem_emm_filter *filters = *emm_filters;
|
||||
*filter_count = 0;
|
||||
|
||||
int32_t idx = 0;
|
||||
|
||||
filters[idx].type = EMM_GLOBAL;
|
||||
filters[idx].enabled = 1;
|
||||
filters[idx].filter[0] = 0x82;
|
||||
filters[idx].mask[0] = 0xFF;
|
||||
idx++;
|
||||
|
||||
filters[idx].type = EMM_GLOBAL;
|
||||
filters[idx].enabled = 1;
|
||||
filters[idx].filter[0] = 0x83;
|
||||
filters[idx].mask[0] = 0xFF;
|
||||
idx++;
|
||||
|
||||
*filter_count = idx;
|
||||
}
|
||||
|
||||
return CS_OK;
|
||||
}
|
||||
|
||||
static int32_t emu_get_biss_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid))
|
||||
{
|
||||
if (*emm_filters == NULL)
|
||||
{
|
||||
const unsigned int max_filter_count = 15;
|
||||
if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter)))
|
||||
{
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
struct s_csystem_emm_filter *filters = *emm_filters;
|
||||
*filter_count = 0;
|
||||
|
||||
int32_t idx = 0;
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < max_filter_count; i++)
|
||||
{
|
||||
filters[idx].type = EMM_GLOBAL;
|
||||
filters[idx].enabled = 1;
|
||||
filters[idx].filter[0] = 0x81 + i; // What about table 0x81?
|
||||
filters[idx].mask[0] = 0xFF;
|
||||
idx++;
|
||||
|
||||
*filter_count = idx;
|
||||
}
|
||||
}
|
||||
return CS_OK;
|
||||
}
|
||||
|
||||
static int32_t emu_get_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **UNUSED(emm_filters), unsigned int *UNUSED(filter_count))
|
||||
{
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
static int32_t emu_get_emm_filter_adv(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count,
|
||||
uint16_t caid, uint32_t provid, uint16_t srvid, uint16_t tsid, uint16_t onid, uint32_t ens)
|
||||
{
|
||||
if (caid_is_viaccess(caid)) return emu_get_via3_emm_filter(rdr, emm_filters, filter_count, caid, provid);
|
||||
if (caid_is_irdeto(caid)) return emu_get_ird2_emm_filter(rdr, emm_filters, filter_count, caid, provid);
|
||||
if (caid_is_powervu(caid)) return emu_get_pvu_emm_filter(emm_filters, filter_count, caid, srvid, tsid, onid, ens);
|
||||
if (caid_is_director(caid)) return emu_get_tan_emm_filter(rdr, emm_filters, filter_count, caid, provid);
|
||||
if (caid_is_biss_dynamic(caid)) return emu_get_biss_emm_filter(rdr, emm_filters, filter_count, caid, provid);
|
||||
|
||||
return CS_ERROR;
|
||||
}
|
||||
|
||||
const struct s_cardsystem reader_emu =
|
||||
{
|
||||
.desc = "emu",
|
||||
.caids = (uint16_t[]){ 0x05, 0x06, 0x0D, 0x0E, 0x10, 0x18, 0x26, 0 },
|
||||
.do_ecm = emu_do_ecm,
|
||||
.do_emm = emu_do_emm,
|
||||
.card_info = emu_card_info,
|
||||
//.card_init = emu_card_init, // apparently this is not needed at all
|
||||
.get_emm_type = emu_get_emm_type,
|
||||
.get_emm_filter = emu_get_emm_filter, // needed to pass checks
|
||||
.get_emm_filter_adv = emu_get_emm_filter_adv,
|
||||
};
|
||||
|
||||
/*
|
||||
* Create the Emu virtual "device" part. This is of type s_cardreader.
|
||||
* Similar structures are found in the csctapi (Card System Card Terminal API)
|
||||
* folder for every IFD (InterFace Device), aka smart card reader.
|
||||
* Since we have no hardware to initialize, we start our Stream Relay server
|
||||
* with the emu_reader_init() function.
|
||||
* At Emu shutdown, we remove keys from memory with the emu_close() function.
|
||||
*/
|
||||
|
||||
#define CR_OK 0
|
||||
#define CR_ERROR 1
|
||||
|
||||
static int32_t emu_reader_init(struct s_reader *UNUSED(reader))
|
||||
{
|
||||
#ifdef MODULE_STREAMRELAY
|
||||
if (cfg.stream_relay_enabled && (stream_server_thread_init == 0))
|
||||
{
|
||||
int32_t i;
|
||||
stream_server_thread_init = 1;
|
||||
SAFE_MUTEX_INIT(&emu_fixed_key_srvid_mutex, NULL);
|
||||
|
||||
for (i = 0; i < EMU_STREAM_SERVER_MAX_CONNECTIONS; i++)
|
||||
{
|
||||
SAFE_MUTEX_INIT(&emu_fixed_key_data_mutex[i], NULL);
|
||||
ll_emu_stream_delayed_keys[i] = ll_create("ll_emu_stream_delayed_keys");
|
||||
memset(&emu_fixed_key_data[i], 0, sizeof(emu_stream_client_key_data));
|
||||
}
|
||||
|
||||
start_thread("stream_key_delayer", stream_key_delayer, NULL, NULL, 1, 1);
|
||||
cs_log("Stream key delayer initialized");
|
||||
}
|
||||
|
||||
// Initialize mutex for exclusive access to key database and key file
|
||||
if (!emu_key_data_mutex_init)
|
||||
{
|
||||
SAFE_MUTEX_INIT(&emu_key_data_mutex, NULL);
|
||||
emu_key_data_mutex_init = 1;
|
||||
}
|
||||
#endif
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
static int32_t emu_close(struct s_reader *UNUSED(reader))
|
||||
{
|
||||
cs_log("Reader is shutting down");
|
||||
|
||||
// Delete keys from Emu's memory
|
||||
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||||
emu_clear_keydata();
|
||||
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
static int32_t emu_get_status(struct s_reader *UNUSED(reader), int32_t *in) { *in = 1; return CR_OK; }
|
||||
static int32_t emu_activate(struct s_reader *UNUSED(reader), struct s_ATR *UNUSED(atr)) { return CR_OK; }
|
||||
static int32_t emu_transmit(struct s_reader *UNUSED(reader), uint8_t *UNUSED(buffer), uint32_t UNUSED(size), uint32_t UNUSED(expectedlen), uint32_t UNUSED(delay), uint32_t UNUSED(timeout)) { return CR_OK; }
|
||||
static int32_t emu_receive(struct s_reader *UNUSED(reader), uint8_t *UNUSED(buffer), uint32_t UNUSED(size), uint32_t UNUSED(delay), uint32_t UNUSED(timeout)) { return CR_OK; }
|
||||
static int32_t emu_write_settings(struct s_reader *UNUSED(reader), struct s_cardreader_settings *UNUSED(s)) { return CR_OK; }
|
||||
static int32_t emu_card_write(struct s_reader *UNUSED(pcsc_reader), const uint8_t *UNUSED(buf), uint8_t *UNUSED(cta_res), uint16_t *UNUSED(cta_lr), int32_t UNUSED(l)) { return CR_OK; }
|
||||
static int32_t emu_set_protocol(struct s_reader *UNUSED(rdr), uint8_t *UNUSED(params), uint32_t *UNUSED(length), uint32_t UNUSED(len_request)) { return CR_OK; }
|
||||
|
||||
const struct s_cardreader cardreader_emu =
|
||||
{
|
||||
.desc = "emu",
|
||||
.typ = R_EMU,
|
||||
.skip_extra_atr_parsing = 1,
|
||||
.reader_init = emu_reader_init,
|
||||
.get_status = emu_get_status,
|
||||
.activate = emu_activate,
|
||||
.transmit = emu_transmit,
|
||||
.receive = emu_receive,
|
||||
.close = emu_close,
|
||||
.write_settings = emu_write_settings,
|
||||
.card_write = emu_card_write,
|
||||
.set_protocol = emu_set_protocol,
|
||||
};
|
||||
|
||||
void add_emu_reader(void)
|
||||
{
|
||||
// This function is called inside oscam.c and creates an emu [reader] with default
|
||||
// settings in oscam.server file. If an emu [reader] already exists, it uses that.
|
||||
|
||||
LL_ITER itr;
|
||||
struct s_reader *rdr;
|
||||
int8_t haveEmuReader = 0;
|
||||
char emuName[] = "emulator";
|
||||
char *ctab, *ftab, *emu_auproviders, *disablecrccws_only_for;
|
||||
|
||||
// Check if emu [reader] entry already exists in oscam.server file and get it
|
||||
itr = ll_iter_create(configured_readers);
|
||||
while ((rdr = ll_iter_next(&itr)))
|
||||
{
|
||||
if (rdr->typ == R_EMU)
|
||||
{
|
||||
haveEmuReader = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rdr = NULL;
|
||||
|
||||
// If there's no emu [reader] in oscam.server, create one with default settings
|
||||
if (!haveEmuReader)
|
||||
{
|
||||
if (!cs_malloc(&rdr, sizeof(struct s_reader)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
reader_set_defaults(rdr);
|
||||
|
||||
rdr->enable = 1;
|
||||
rdr->typ = R_EMU;
|
||||
cs_strncpy(rdr->label, emuName, sizeof(emuName));
|
||||
cs_strncpy(rdr->device, emuName, sizeof(emuName));
|
||||
|
||||
// CAIDs
|
||||
ctab = strdup("0500,0604,0D00,0E00,1010,1801,2600,2602,2610");
|
||||
chk_caidtab(ctab, &rdr->ctab);
|
||||
NULLFREE(ctab);
|
||||
|
||||
// Idents
|
||||
ftab = strdup("0500:020A00,021110;"
|
||||
"0604:000000;"
|
||||
"0D00:0000C0;"
|
||||
"0E00:000000;"
|
||||
"1010:000000;"
|
||||
"1801:000000,001101,002111,007301;"
|
||||
"2600:000000;"
|
||||
"2602:000000;"
|
||||
"2610:000000;"
|
||||
);
|
||||
chk_ftab(ftab, &rdr->ftab);
|
||||
NULLFREE(ftab);
|
||||
|
||||
// AU providers
|
||||
emu_auproviders = strdup("0604:010200;0E00:000000;1010:000000;2610:000000;");
|
||||
chk_ftab(emu_auproviders, &rdr->emu_auproviders);
|
||||
NULLFREE(emu_auproviders);
|
||||
|
||||
// EMM cache
|
||||
rdr->cachemm = 2;
|
||||
rdr->rewritemm = 1;
|
||||
rdr->logemm = 2;
|
||||
rdr->deviceemm = 1;
|
||||
|
||||
// User group
|
||||
rdr->grp = 0x1ULL;
|
||||
|
||||
// Add the "device" part to our emu reader
|
||||
rdr->crdr = &cardreader_emu;
|
||||
|
||||
// Disable CW checksum test for PowerVu
|
||||
disablecrccws_only_for = strdup("0E00:000000");
|
||||
chk_ftab(disablecrccws_only_for, &rdr->disablecrccws_only_for);
|
||||
NULLFREE(disablecrccws_only_for);
|
||||
|
||||
reader_fixups_fn(rdr);
|
||||
ll_append(configured_readers, rdr);
|
||||
}
|
||||
|
||||
// Set DVB Api delayer option
|
||||
#ifdef HAVE_DVBAPI
|
||||
if (cfg.dvbapi_enabled && cfg.dvbapi_delayer < 60)
|
||||
{
|
||||
cfg.dvbapi_delayer = 60;
|
||||
}
|
||||
#endif
|
||||
|
||||
cs_log("OSCam-Emu version %d", EMU_VERSION);
|
||||
}
|
||||
|
||||
#endif // WITH_EMU
|
||||
|
|
@ -6,6 +6,9 @@
|
|||
#define DES_RIGHT 4
|
||||
#define DES_HASH 8
|
||||
|
||||
#define DES_ECM_CRYPT 0
|
||||
#define DES_ECM_HASH DES_HASH
|
||||
|
||||
void nc_des(uint8_t key[], uint8_t mode, uint8_t data[]);
|
||||
int nc_des_encrypt(uint8_t *buffer, int len, uint8_t *deskey);
|
||||
int nc_des_decrypt(uint8_t *buffer, int len, uint8_t *deskey);
|
||||
|
|
|
|||
|
|
@ -1118,6 +1118,13 @@ static int8_t newcamd_auth_client(IN_ADDR_T ip, uint8_t *deskey)
|
|||
// set userfilter for au enabled clients
|
||||
if(aureader)
|
||||
{
|
||||
#ifdef WITH_EMU
|
||||
if(aureader->typ == R_EMU)
|
||||
{
|
||||
usr_filter = *get_emu_prids_for_caid(aureader, cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts[0].caid);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
mk_user_au_ftab(aureader, &usr_filter);
|
||||
}
|
||||
|
||||
|
|
@ -1184,7 +1191,7 @@ static int8_t newcamd_auth_client(IN_ADDR_T ip, uint8_t *deskey)
|
|||
uint32_t rprid;
|
||||
found = 0;
|
||||
|
||||
if(pufilt->caid == aureader->caid)
|
||||
if(pufilt->caid == aureader->caid && aureader->typ != R_EMU)
|
||||
{
|
||||
for(k = 0; k < aureader->nprov; k++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -901,7 +901,7 @@ void check_lb_auto_betatunnel_mode(ECM_REQUEST *er)
|
|||
|
||||
uint16_t get_rdr_caid(struct s_reader *rdr)
|
||||
{
|
||||
if(is_network_reader(rdr))
|
||||
if(is_network_reader(rdr) || rdr->typ == R_EMU)
|
||||
{
|
||||
return 0; // reader caid is not real caid
|
||||
}
|
||||
|
|
@ -1292,7 +1292,7 @@ void stat_get_best_reader(ECM_REQUEST *er)
|
|||
for(ea = er->matching_rdr; ea; ea = ea->next)
|
||||
{
|
||||
rdr = ea->reader;
|
||||
if(is_network_reader(rdr)) // reader caid is not real caid
|
||||
if(is_network_reader(rdr) || rdr->typ == R_EMU) // reader caid is not real caid
|
||||
{
|
||||
prv = ea;
|
||||
continue; // proxy can convert or reject
|
||||
|
|
@ -2094,14 +2094,13 @@ void lb_update_last(struct s_ecm_answer *ea_er, struct s_reader *reader)
|
|||
cs_ftime(&reader->lb_last);
|
||||
}
|
||||
|
||||
void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc)
|
||||
void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc, int32_t ecm_time)
|
||||
{
|
||||
if(rc >= E_99 || cacheex_reader(rdr))
|
||||
{ return; }
|
||||
|
||||
int32_t ecm_time = cfg.ctimeout;
|
||||
if(ea->ecm_time && ea->rc <= E_NOTFOUND)
|
||||
{ ecm_time = ea->ecm_time; }
|
||||
if(!ecm_time)
|
||||
ecm_time = cfg.ctimeout;
|
||||
|
||||
add_stat(rdr, er, ecm_time, rc, ea->rcEx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ void init_stat(void);
|
|||
void stat_finish(void);
|
||||
void load_stat_from_file(void);
|
||||
void lb_destroy_stats(struct s_reader *rdr);
|
||||
void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc);
|
||||
void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc, int32_t ecm_time);
|
||||
void stat_get_best_reader(ECM_REQUEST *er);
|
||||
void lb_mark_last_reader(ECM_REQUEST *er);
|
||||
void check_lb_auto_betatunnel_mode(ECM_REQUEST *er);
|
||||
|
|
@ -32,7 +32,7 @@ static inline void init_stat(void) { }
|
|||
static inline void stat_finish(void) { }
|
||||
static inline void load_stat_from_file(void) { }
|
||||
static inline void lb_destroy_stats(struct s_reader *UNUSED(rdr)) { }
|
||||
static inline void send_reader_stat(struct s_reader *UNUSED(rdr), ECM_REQUEST *UNUSED(er), struct s_ecm_answer *UNUSED(ea), int8_t UNUSED(rc)) { }
|
||||
static inline void send_reader_stat(struct s_reader *UNUSED(rdr), ECM_REQUEST *UNUSED(er), struct s_ecm_answer *UNUSED(ea), int8_t UNUSED(rc), int32_t UNUSED(ecm_time)) { }
|
||||
static inline void stat_get_best_reader(ECM_REQUEST *UNUSED(er)) { }
|
||||
static inline void lb_mark_last_reader(ECM_REQUEST *UNUSED(er)) { }
|
||||
static inline void check_lb_auto_betatunnel_mode(ECM_REQUEST *UNUSED(er)) { }
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -10,26 +10,47 @@
|
|||
#define DVB_BUFFER_WAIT_CSA 188*(DVB_MAX_TS_PACKETS-128)
|
||||
#define DVB_BUFFER_SIZE DVB_BUFFER_SIZE_CSA
|
||||
|
||||
#ifdef WITH_EMU
|
||||
#define EMU_STREAM_MAX_AUDIO_SUB_TRACKS 4
|
||||
#define EMU_DVB_BUFFER_SIZE_CSA DVB_BUFFER_SIZE_CSA
|
||||
#define EMU_DVB_BUFFER_WAIT_CSA DVB_BUFFER_WAIT_CSA
|
||||
#define EMU_DVB_BUFFER_SIZE_DES 188*32
|
||||
#define EMU_DVB_BUFFER_WAIT_DES 188*29
|
||||
#define EMU_STREAM_SERVER_MAX_CONNECTIONS STREAM_SERVER_MAX_CONNECTIONS
|
||||
#define emu_fixed_key_srvid_mutex fixed_key_srvid_mutex
|
||||
#define emu_stream_cur_srvid stream_cur_srvid
|
||||
#define emu_stream_client_data stream_client_data
|
||||
#endif
|
||||
|
||||
//#define __BISS__
|
||||
#ifdef __BISS__
|
||||
#define MAX_STREAM_PIDS 32
|
||||
#endif
|
||||
|
||||
#include "cscrypt/md5.h"
|
||||
#include <dvbcsa/dvbcsa.h>
|
||||
#if DVBCSA_KEY_ECM
|
||||
#define DVBCSA_HEADER_ECM 1
|
||||
#define dvbcsa_bs_key_set(a,b) dvbcsa_bs_key_set_ecm(ecm,a,b)
|
||||
#else
|
||||
#define DVBCSA_HEADER_ECM 0
|
||||
#endif
|
||||
#ifndef STATIC_LIBDVBCSA
|
||||
#define STATIC_LIBDVBCSA 0
|
||||
#endif
|
||||
|
||||
#define EVEN 0
|
||||
#define ODD 1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef WITH_EMU
|
||||
struct dvbcsa_bs_key_s *key[EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2][2];
|
||||
#else
|
||||
struct dvbcsa_bs_key_s *key[2];
|
||||
#endif
|
||||
} stream_client_key_data;
|
||||
|
||||
#ifdef WITH_EMU
|
||||
typedef struct
|
||||
{
|
||||
uint32_t pvu_des_ks[EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2][2][32];
|
||||
int8_t csa_used;
|
||||
int32_t connid;
|
||||
} emu_stream_client_key_data;
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int32_t connid;
|
||||
|
|
@ -58,7 +79,20 @@ typedef struct
|
|||
uint16_t ecm_pid;
|
||||
uint16_t emm_pid;
|
||||
uint16_t pcr_pid;
|
||||
#ifdef __BISS__
|
||||
uint8_t STREAMpidcount;
|
||||
uint16_t STREAMpids[MAX_STREAM_PIDS];
|
||||
#endif
|
||||
uint8_t ecm_md5[MD5_DIGEST_LENGTH];
|
||||
#ifdef WITH_EMU
|
||||
int16_t ecm_nb;
|
||||
int8_t reset_key_data;
|
||||
uint16_t video_pid;
|
||||
uint16_t teletext_pid;
|
||||
uint16_t audio_pids[EMU_STREAM_MAX_AUDIO_SUB_TRACKS];
|
||||
uint8_t audio_pid_count;
|
||||
emu_stream_client_key_data key;
|
||||
#endif
|
||||
} stream_client_data;
|
||||
|
||||
void init_stream_server(void);
|
||||
|
|
@ -66,6 +100,30 @@ void stop_stream_server(void);
|
|||
|
||||
bool stream_write_cw(ECM_REQUEST *er);
|
||||
|
||||
#ifdef WITH_EMU
|
||||
extern int8_t stream_server_thread_init;
|
||||
extern pthread_mutex_t fixed_key_srvid_mutex;
|
||||
extern uint16_t stream_cur_srvid[STREAM_SERVER_MAX_CONNECTIONS];
|
||||
extern int8_t stream_server_has_ecm[STREAM_SERVER_MAX_CONNECTIONS];
|
||||
extern uint8_t emu_stream_server_mutex_init;
|
||||
extern bool has_dvbcsa_ecm;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct timeb write_time;
|
||||
int8_t csa_used;
|
||||
int8_t is_even;
|
||||
uint8_t cw[8][8];
|
||||
} emu_stream_cw_item;
|
||||
|
||||
extern pthread_mutex_t emu_fixed_key_data_mutex[EMU_STREAM_SERVER_MAX_CONNECTIONS];
|
||||
extern stream_client_key_data key_data[STREAM_SERVER_MAX_CONNECTIONS];
|
||||
extern emu_stream_client_key_data emu_fixed_key_data[EMU_STREAM_SERVER_MAX_CONNECTIONS];
|
||||
|
||||
extern LLIST *ll_emu_stream_delayed_keys[EMU_STREAM_SERVER_MAX_CONNECTIONS];
|
||||
void *stream_key_delayer(void *arg);
|
||||
#endif // WITH_EMU
|
||||
|
||||
#endif // MODULE_STREAMRELAY
|
||||
|
||||
#endif // MODULE_STREAMRELAY_H_
|
||||
|
|
|
|||
|
|
@ -456,6 +456,7 @@ char *tpl_getUnparsedTpl(const char *name, int8_t removeHeader, const char *subd
|
|||
check_conf(WITH_SSL, ptr2);
|
||||
check_conf(WITH_STAPI, ptr2);
|
||||
check_conf(WITH_STAPI5, ptr2);
|
||||
check_conf(WITH_EMU, ptr2);
|
||||
} // for
|
||||
if(ok == 0)
|
||||
{
|
||||
|
|
|
|||
159
module-webif.c
159
module-webif.c
|
|
@ -30,18 +30,25 @@
|
|||
#include "oscam-string.h"
|
||||
#include "oscam-time.h"
|
||||
#include "oscam-work.h"
|
||||
#include "oscam-ecm.h"
|
||||
|
||||
#define WEBIF_MAX_NODES 512
|
||||
static uint64_t webif_last_nodeid[WEBIF_MAX_NODES];
|
||||
|
||||
|
||||
#ifdef MODULE_GBOX
|
||||
#include "module-gbox-sms.h"
|
||||
#include "module-gbox.h"
|
||||
#include "module-gbox-cards.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef WEBIF_WIKI
|
||||
#include "webif/pages_wiki.h"
|
||||
#endif
|
||||
|
||||
uint32_t webif_last_nodeid[WEBIF_MAX_NODES];
|
||||
|
||||
extern const struct s_cardreader *cardreaders[];
|
||||
extern char cs_confdir[];
|
||||
extern uint32_t ecmcwcache_size;
|
||||
|
|
@ -143,8 +150,9 @@ static bool use_srvid2 = false;
|
|||
#define MNU_GBX_FSTAINF 27
|
||||
#define MNU_GBX_FEXPINF 28
|
||||
#define MNU_GBX_INFOLOG 29
|
||||
#define MNU_CFG_FSOFTCAMKEY 30
|
||||
|
||||
#define MNU_CFG_TOTAL_ITEMS 30 // sum of items above. Use it for "All inactive" in function calls too.
|
||||
#define MNU_CFG_TOTAL_ITEMS 31 // sum of items above. Use it for "All inactive" in function calls too.
|
||||
|
||||
static void set_status_info_var(struct templatevars *vars, char *varname, int no_data, char *fmt, double value)
|
||||
{
|
||||
|
|
@ -457,8 +465,7 @@ static void refresh_oscam(enum refreshtypes refreshtype)
|
|||
|
||||
case REFR_SERVER:
|
||||
cs_log("Refresh Server requested by WebIF from %s", cs_inet_ntoa(GET_IP()));
|
||||
//kill(first_client->pid, SIGHUP);
|
||||
//todo how I can refresh the server after global settings
|
||||
reload_global_config(); // Wczytaj ponownie globalną konfigurację
|
||||
break;
|
||||
|
||||
case REFR_SERVICES:
|
||||
|
|
@ -610,13 +617,55 @@ static void webif_save_config(char *section, struct templatevars *vars, struct u
|
|||
}
|
||||
int i;
|
||||
int cnt = (*params).paramcount;
|
||||
// First pass: check for checkbox values (value=1) to identify which checkboxes are checked
|
||||
// This is needed because hidden fields (value=0) should not override checkbox values
|
||||
bool *checkbox_checked = NULL;
|
||||
if(cnt > 0 && !cs_malloc(&checkbox_checked, cnt * sizeof(bool)))
|
||||
{
|
||||
return; // Allocation failed, proceed with original behavior
|
||||
}
|
||||
for(i = 0; i < cnt; i++)
|
||||
{
|
||||
char *token = (*params).params[i];
|
||||
char *value = (*params).values[i];
|
||||
if(!streq(token, "part") && !streq(token, "action"))
|
||||
{ config_set(section, token, value); }
|
||||
{
|
||||
// Check if this is a checkbox with value=1
|
||||
if(strcmp(value, "1") == 0)
|
||||
{
|
||||
checkbox_checked[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Second pass: apply settings, skip value=0 if checkbox is checked (hidden field)
|
||||
for(i = 0; i < cnt; i++)
|
||||
{
|
||||
char *token = (*params).params[i];
|
||||
char *value = (*params).values[i];
|
||||
if(!streq(token, "part") && !streq(token, "action"))
|
||||
{
|
||||
// Skip hidden field value=0 if checkbox is checked (value=1 exists)
|
||||
if(strcmp(value, "0") == 0)
|
||||
{
|
||||
bool checkbox_will_be_checked = false;
|
||||
int j;
|
||||
for(j = i + 1; j < cnt; j++)
|
||||
{
|
||||
if(strcmp((*params).params[j], token) == 0 && strcmp((*params).values[j], "1") == 0)
|
||||
{
|
||||
checkbox_will_be_checked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(checkbox_will_be_checked)
|
||||
{
|
||||
continue; // Skip this hidden field, checkbox will set it to 1
|
||||
}
|
||||
}
|
||||
config_set(section, token, value);
|
||||
}
|
||||
}
|
||||
free(checkbox_checked);
|
||||
if(write_config() == 0)
|
||||
{
|
||||
tpl_addMsg(vars, "Configuration was saved.");
|
||||
|
|
@ -742,6 +791,20 @@ static char *send_oscam_config_global(struct templatevars *vars, struct uriparam
|
|||
tpl_addVar(vars, TPLADD, "IGNCHKSUMONLYFORGLOBAL", value);
|
||||
free_mk_t(value);
|
||||
|
||||
tpl_addVar(vars, TPLADD, "CWVOTEENABLEDCHECKED", (cfg.cwvote_enabled == 1) ? "checked" : "");
|
||||
tpl_addVar(vars, TPLADD, "CWVOTELOGENABLEDCHECKED", (cfg.cwvote_log_enabled == 1) ? "checked" : "");
|
||||
tpl_printf(vars, TPLADD, "CWVOTETIMEOUT", "%d", cfg.cwvote_timeout);
|
||||
tpl_printf(vars, TPLADD, "CWVOTEMINVOTES", "%d", cfg.cwvote_min_votes);
|
||||
tpl_printf(vars, TPLADD, "CWVOTELOCALWEIGHT", "%.1f", cfg.cwvote_local_weight);
|
||||
tpl_printf(vars, TPLADD, "CWVOTEMAXCANDIDATES", "%d", cfg.cwvote_max_candidates);
|
||||
tpl_addVar(vars, TPLADD, "CWVOTECOMPARE8", (cfg.cwvote_compare_len == 8) ? "selected" : "");
|
||||
tpl_addVar(vars, TPLADD, "CWVOTECOMPARE16", (cfg.cwvote_compare_len == 16) ? "selected" : "");
|
||||
tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK0", (cfg.cwvote_fallback == 0) ? "selected" : "");
|
||||
tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK1", (cfg.cwvote_fallback == 1) ? "selected" : "");
|
||||
tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK2", (cfg.cwvote_fallback == 2) ? "selected" : "");
|
||||
value = mk_t_cwvote_caidtab(&cfg.cwvote_caids);
|
||||
tpl_addVar(vars, TPLADD, "CWVOTECAIDS", value);
|
||||
free_mk_t(value);
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
tpl_addVar(vars, TPLADD, "CACHEEXSRCNAME", (cfg.cacheex_srcname_webif == 1) ? "checked" : "");
|
||||
#endif
|
||||
|
|
@ -1345,6 +1408,11 @@ static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uri
|
|||
|
||||
tpl_printf(vars, TPLADD, "STREAM_RELAY_BUFFER_TIME", "%d", cfg.stream_relay_buffer_time);
|
||||
tpl_printf(vars, TPLADD, "STREAM_RELAY_RECONNECT_COUNT", "%d", cfg.stream_relay_reconnect_count);
|
||||
#ifdef WITH_EMU
|
||||
tpl_printf(vars, TPLADD, "TMP", "STREAMEMMENABLEDSELECTED%d", cfg.emu_stream_emm_enabled);
|
||||
tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected");
|
||||
tpl_printf(vars, TPLADD, "STREAM_ECM_DELAY", "%d", cfg.emu_stream_ecm_delay);
|
||||
#endif
|
||||
|
||||
tpl_printf(vars, TPLADD, "TMP", "STREAMCONFIGCLIENTSELECTED%d", cfg.stream_display_client);
|
||||
tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected");
|
||||
|
|
@ -2464,7 +2532,7 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
|
|||
chk_reader("services", servicelabels, rdr);
|
||||
chk_reader("lb_whitelist_services", servicelabelslb, rdr);
|
||||
|
||||
if(is_network_reader(rdr)) //physical readers make trouble if re-started
|
||||
if(is_network_reader(rdr) || rdr->typ == R_EMU) //physical readers make trouble if re-started
|
||||
{
|
||||
if(rdr)
|
||||
{
|
||||
|
|
@ -3261,6 +3329,8 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
|
|||
tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare);
|
||||
tpl_printf(vars, TPLADD, "CCCRECONNECT", "%d", rdr->cc_reconnect);
|
||||
|
||||
if(rdr->cc_want_emu)
|
||||
{ tpl_addVar(vars, TPLADD, "CCCWANTEMUCHECKED", "checked"); }
|
||||
if(rdr->cc_keepalive)
|
||||
{ tpl_addVar(vars, TPLADD, "KEEPALIVECHECKED", "checked"); }
|
||||
#endif
|
||||
|
|
@ -3298,6 +3368,23 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
|
|||
tpl_addVar(vars, TPLADD, "USERSCRIPT", rdr->userscript);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_EMU
|
||||
//emu_auproviders
|
||||
value = mk_t_ftab(&rdr->emu_auproviders);
|
||||
tpl_addVar(vars, TPLADD, "EMUAUPROVIDERS", value);
|
||||
free_mk_t(value);
|
||||
|
||||
// Date-coded BISS keys
|
||||
if(!apicall)
|
||||
{
|
||||
tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "checked" : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "1" : "0");
|
||||
}
|
||||
#endif
|
||||
|
||||
tpl_addVar(vars, TPLADD, "PROTOCOL", reader_get_type_desc(rdr, 0));
|
||||
|
||||
// Show only parameters which needed for the reader
|
||||
|
|
@ -3319,6 +3406,9 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
|
|||
case R_CAMD35 :
|
||||
tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCAMD35BIT"));
|
||||
break;
|
||||
case R_EMU :
|
||||
tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGEMUBIT"));
|
||||
break;
|
||||
case R_CS378X :
|
||||
tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCS378XBIT"));
|
||||
break;
|
||||
|
|
@ -5356,9 +5446,34 @@ static char *send_oscam_entitlement(struct templatevars *vars, struct uriparams
|
|||
|
||||
tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", "<BR><BR>New Structure:<BR>");
|
||||
char tbuffer[83];
|
||||
#ifdef WITH_EMU
|
||||
char keyBuffer[1024];
|
||||
#endif
|
||||
int jsondelimiter = 0;
|
||||
while((item = ll_iter_next(&itr)))
|
||||
{
|
||||
#ifdef WITH_EMU
|
||||
if(item->isKey)
|
||||
{
|
||||
tpl_addVar(vars, TPLADD, "ENTSTARTDATE", "");
|
||||
tpl_addVar(vars, TPLADD, "ENTENDDATE", "");
|
||||
cs_hexdump(0, item->key, item->keyLength, keyBuffer, sizeof(keyBuffer));
|
||||
tpl_addVar(vars, TPLADD, "ENTEXPIERED", "e_valid");
|
||||
tpl_printf(vars, TPLADD, "ENTCAID", "%04X", item->caid);
|
||||
tpl_printf(vars, TPLADD, "ENTPROVID", "%08X", item->provid);
|
||||
tpl_addVar(vars, TPLADD, "ENTID", item->name);
|
||||
tpl_addVar(vars, TPLADD, "ENTCLASS", keyBuffer);
|
||||
if(item->isData) { tpl_addVar(vars, TPLADD, "ENTTYPE", "data"); }
|
||||
else { tpl_addVar(vars, TPLADD, "ENTTYPE", "key"); }
|
||||
tpl_addVar(vars, TPLADD, "ENTRESNAME", "");
|
||||
|
||||
if((strcmp(getParam(params, "hideexpired"), "1") != 0) || (item->end > now))
|
||||
{ tpl_addVar(vars, TPLAPPEND, "READERENTENTRY", tpl_getTpl(vars, "ENTITLEMENTITEMBIT")); }
|
||||
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
localtime_r(&item->start, &start_t);
|
||||
localtime_r(&item->end, &end_t);
|
||||
|
||||
|
|
@ -5855,6 +5970,9 @@ static char *send_oscam_status(struct templatevars * vars, struct uriparams * pa
|
|||
filtered = (type == cl->typ) || (type == 'x' && (cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode));
|
||||
#else
|
||||
filtered = (type == cl->typ);
|
||||
#endif
|
||||
#ifdef WITH_EMU
|
||||
if(type == 'e' && cl->typ == 'r' && cl->reader->typ == R_EMU) filtered = 1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -7446,6 +7564,9 @@ static char *send_oscam_files(struct templatevars * vars, struct uriparams * par
|
|||
{ "stats.info", MNU_GBX_FSTAINF, FTYPE_GBOX }, // id 27
|
||||
{ "expired.info", MNU_GBX_FEXPINF, FTYPE_GBOX }, // id 28
|
||||
{ "info.log", MNU_GBX_INFOLOG, FTYPE_GBOX }, // id 29
|
||||
#endif
|
||||
#ifdef WITH_EMU
|
||||
{ "SoftCam.Key", MNU_CFG_FSOFTCAMKEY,FTYPE_CONFIG }, // id 30
|
||||
#endif
|
||||
{ NULL, 0, 0 },
|
||||
};
|
||||
|
|
@ -7918,8 +8039,12 @@ static char *send_oscam_EMM_running(struct templatevars * vars, struct uriparams
|
|||
else if(!proxy && rdr->csystem_active) // local active reader
|
||||
{
|
||||
csystem = rdr->csystem;
|
||||
|
||||
if(rdr->typ != R_EMU)
|
||||
{
|
||||
caid = rdr->caid;
|
||||
}
|
||||
}
|
||||
|
||||
if(csystem)
|
||||
{
|
||||
|
|
@ -8201,7 +8326,6 @@ static char *send_oscam_cacheex(struct templatevars * vars, struct uriparams * p
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
{ tpl_addVar(vars, TPLADD, "IP", ""); }
|
||||
|
||||
|
||||
|
|
@ -8224,20 +8348,7 @@ else
|
|||
webif_last_nodeid[idx] = current_node;
|
||||
|
||||
// --- end detection ---
|
||||
if(IP_ISSET(cl->ip))
|
||||
{ tpl_addVar(vars, TPLADD, "IP", cs_inet_ntoa(cl->ip)); }
|
||||
else if(cl->login > cl->logout)
|
||||
{ tpl_addVar(vars, TPLADD, "IP", "camd.socket"); }
|
||||
else
|
||||
{ tpl_addVar(vars, TPLADD, "IP", ""); }
|
||||
tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", get_cacheex_node(cl));
|
||||
tpl_addVar(vars, TPLADD, "LEVEL", level[cl->account->cacheex.mode]);
|
||||
tpl_printf(vars, TPLADD, "PUSH", "%d", cl->account->cwcacheexpush);
|
||||
tpl_printf(vars, TPLADD, "GOT", "%d", cl->account->cwcacheexgot);
|
||||
tpl_printf(vars, TPLADD, "CWCINFO", "%d", cl->account->cwc_info);
|
||||
tpl_printf(vars, TPLADD, "HIT", "%d", cl->account->cwcacheexhit);
|
||||
tpl_printf(vars, TPLADD, "ERR", "%d", cl->account->cwcacheexerr);
|
||||
tpl_printf(vars, TPLADD, "ERRCW", "%d", cl->account->cwcacheexerrcw);
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
tpl_printf(vars, TPLADD, "GOTLG", "%d", cl->account->cwcacheexgotlg);
|
||||
tpl_printf(vars, TPLADD, "PUSHLG", "%d", cl->account->cwcacheexpushlg);
|
||||
|
|
@ -9245,7 +9356,11 @@ static int32_t readRequest(FILE * f, IN_ADDR_T in, char **result, int8_t forcePl
|
|||
memcpy(*result + bufsize, buf2, n);
|
||||
bufsize += n;
|
||||
|
||||
#ifdef WITH_EMU
|
||||
if(bufsize > 204800) // max request size 200kb
|
||||
#else
|
||||
if(bufsize > 102400) // max request size 100kb
|
||||
#endif
|
||||
{
|
||||
cs_log("error: too much data received from %s", cs_inet_ntoa(in));
|
||||
NULLFREE(*result);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "oscam-string.h"
|
||||
#include "oscam-time.h"
|
||||
#include "oscam-work.h"
|
||||
#include "oscam-ecm.h" // Dodano dla refresh_cw_vote_config
|
||||
#ifdef MODULE_GBOX
|
||||
#include "module-gbox-sms.h"
|
||||
#include "module-gbox.h"
|
||||
|
|
@ -141,8 +142,9 @@ static bool use_srvid2 = false;
|
|||
#define MNU_GBX_FSTAINF 27
|
||||
#define MNU_GBX_FEXPINF 28
|
||||
#define MNU_GBX_INFOLOG 29
|
||||
#define MNU_CFG_FSOFTCAMKEY 30
|
||||
|
||||
#define MNU_CFG_TOTAL_ITEMS 30 // sum of items above. Use it for "All inactive" in function calls too.
|
||||
#define MNU_CFG_TOTAL_ITEMS 31 // sum of items above. Use it for "All inactive" in function calls too.
|
||||
|
||||
static void set_status_info_var(struct templatevars *vars, char *varname, int no_data, char *fmt, double value)
|
||||
{
|
||||
|
|
@ -455,8 +457,7 @@ static void refresh_oscam(enum refreshtypes refreshtype)
|
|||
|
||||
case REFR_SERVER:
|
||||
cs_log("Refresh Server requested by WebIF from %s", cs_inet_ntoa(GET_IP()));
|
||||
//kill(first_client->pid, SIGHUP);
|
||||
//todo how I can refresh the server after global settings
|
||||
reload_global_config(); // Wczytaj ponownie globalną konfigurację
|
||||
break;
|
||||
|
||||
case REFR_SERVICES:
|
||||
|
|
@ -608,13 +609,55 @@ static void webif_save_config(char *section, struct templatevars *vars, struct u
|
|||
}
|
||||
int i;
|
||||
int cnt = (*params).paramcount;
|
||||
// First pass: check for checkbox values (value=1) to identify which checkboxes are checked
|
||||
// This is needed because hidden fields (value=0) should not override checkbox values
|
||||
bool *checkbox_checked = NULL;
|
||||
if(cnt > 0 && !cs_malloc(&checkbox_checked, cnt * sizeof(bool)))
|
||||
{
|
||||
return; // Allocation failed, proceed with original behavior
|
||||
}
|
||||
for(i = 0; i < cnt; i++)
|
||||
{
|
||||
char *token = (*params).params[i];
|
||||
char *value = (*params).values[i];
|
||||
if(!streq(token, "part") && !streq(token, "action"))
|
||||
{ config_set(section, token, value); }
|
||||
{
|
||||
// Check if this is a checkbox with value=1
|
||||
if(strcmp(value, "1") == 0)
|
||||
{
|
||||
checkbox_checked[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Second pass: apply settings, skip value=0 if checkbox is checked (hidden field)
|
||||
for(i = 0; i < cnt; i++)
|
||||
{
|
||||
char *token = (*params).params[i];
|
||||
char *value = (*params).values[i];
|
||||
if(!streq(token, "part") && !streq(token, "action"))
|
||||
{
|
||||
// Skip hidden field value=0 if checkbox is checked (value=1 exists)
|
||||
if(strcmp(value, "0") == 0)
|
||||
{
|
||||
bool checkbox_will_be_checked = false;
|
||||
int j;
|
||||
for(j = i + 1; j < cnt; j++)
|
||||
{
|
||||
if(strcmp((*params).params[j], token) == 0 && strcmp((*params).values[j], "1") == 0)
|
||||
{
|
||||
checkbox_will_be_checked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(checkbox_will_be_checked)
|
||||
{
|
||||
continue; // Skip this hidden field, checkbox will set it to 1
|
||||
}
|
||||
}
|
||||
config_set(section, token, value);
|
||||
}
|
||||
}
|
||||
free(checkbox_checked);
|
||||
if(write_config() == 0)
|
||||
{
|
||||
tpl_addMsg(vars, "Configuration was saved.");
|
||||
|
|
@ -740,6 +783,20 @@ static char *send_oscam_config_global(struct templatevars *vars, struct uriparam
|
|||
tpl_addVar(vars, TPLADD, "IGNCHKSUMONLYFORGLOBAL", value);
|
||||
free_mk_t(value);
|
||||
|
||||
tpl_addVar(vars, TPLADD, "CWVOTEENABLEDCHECKED", (cfg.cwvote_enabled == 1) ? "checked" : "");
|
||||
tpl_addVar(vars, TPLADD, "CWVOTELOGENABLEDCHECKED", (cfg.cwvote_log_enabled == 1) ? "checked" : "");
|
||||
tpl_printf(vars, TPLADD, "CWVOTETIMEOUT", "%d", cfg.cwvote_timeout);
|
||||
tpl_printf(vars, TPLADD, "CWVOTEMINVOTES", "%d", cfg.cwvote_min_votes);
|
||||
tpl_printf(vars, TPLADD, "CWVOTELOCALWEIGHT", "%.1f", cfg.cwvote_local_weight);
|
||||
tpl_printf(vars, TPLADD, "CWVOTEMAXCANDIDATES", "%d", cfg.cwvote_max_candidates);
|
||||
tpl_addVar(vars, TPLADD, "CWVOTECOMPARE8", (cfg.cwvote_compare_len == 8) ? "selected" : "");
|
||||
tpl_addVar(vars, TPLADD, "CWVOTECOMPARE16", (cfg.cwvote_compare_len == 16) ? "selected" : "");
|
||||
tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK0", (cfg.cwvote_fallback == 0) ? "selected" : "");
|
||||
tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK1", (cfg.cwvote_fallback == 1) ? "selected" : "");
|
||||
tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK2", (cfg.cwvote_fallback == 2) ? "selected" : "");
|
||||
value = mk_t_cwvote_caidtab(&cfg.cwvote_caids);
|
||||
tpl_addVar(vars, TPLADD, "CWVOTECAIDS", value);
|
||||
free_mk_t(value);
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
tpl_addVar(vars, TPLADD, "CACHEEXSRCNAME", (cfg.cacheex_srcname_webif == 1) ? "checked" : "");
|
||||
#endif
|
||||
|
|
@ -982,14 +1039,6 @@ static char *send_oscam_config_cache(struct templatevars *vars, struct uriparams
|
|||
|
||||
tpl_addVar(vars, TPLADD, "CACHEEXSTATSSELECTED", (cfg.cacheex_enable_stats == 1) ? "checked" : "");
|
||||
|
||||
/* GLOBAL CACHEEX MAXHOP */
|
||||
tpl_printf(vars, TPLADD, "CACHEEX_GLOBAL_MAXHOP",cfg.cacheex_global_maxhop ? "checked" : "");
|
||||
tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_GLOBAL", "%d", cfg.cacheex_maxhop);
|
||||
tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_LG_GLOBAL", "%d", cfg.cacheex_maxhop_lg);
|
||||
|
||||
tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_PERCAID", "%s", mk_t_caidvaluetab(&cfg.cacheex_maxhop_percaid));
|
||||
tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_LG_PERCAID", "%s", mk_t_caidvaluetab(&cfg.cacheex_maxhop_lg_percaid));
|
||||
|
||||
tpl_addVar(vars, TPLADD, "WTTCHECKED", (cfg.wait_until_ctimeout == 1) ? "checked" : "");
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
|
|
@ -1343,6 +1392,11 @@ static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uri
|
|||
|
||||
tpl_printf(vars, TPLADD, "STREAM_RELAY_BUFFER_TIME", "%d", cfg.stream_relay_buffer_time);
|
||||
tpl_printf(vars, TPLADD, "STREAM_RELAY_RECONNECT_COUNT", "%d", cfg.stream_relay_reconnect_count);
|
||||
#ifdef WITH_EMU
|
||||
tpl_printf(vars, TPLADD, "TMP", "STREAMEMMENABLEDSELECTED%d", cfg.emu_stream_emm_enabled);
|
||||
tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected");
|
||||
tpl_printf(vars, TPLADD, "STREAM_ECM_DELAY", "%d", cfg.emu_stream_ecm_delay);
|
||||
#endif
|
||||
|
||||
tpl_printf(vars, TPLADD, "TMP", "STREAMCONFIGCLIENTSELECTED%d", cfg.stream_display_client);
|
||||
tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected");
|
||||
|
|
@ -2462,7 +2516,7 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
|
|||
chk_reader("services", servicelabels, rdr);
|
||||
chk_reader("lb_whitelist_services", servicelabelslb, rdr);
|
||||
|
||||
if(is_network_reader(rdr)) //physical readers make trouble if re-started
|
||||
if(is_network_reader(rdr) || rdr->typ == R_EMU) //physical readers make trouble if re-started
|
||||
{
|
||||
if(rdr)
|
||||
{
|
||||
|
|
@ -3259,6 +3313,8 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
|
|||
tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare);
|
||||
tpl_printf(vars, TPLADD, "CCCRECONNECT", "%d", rdr->cc_reconnect);
|
||||
|
||||
if(rdr->cc_want_emu)
|
||||
{ tpl_addVar(vars, TPLADD, "CCCWANTEMUCHECKED", "checked"); }
|
||||
if(rdr->cc_keepalive)
|
||||
{ tpl_addVar(vars, TPLADD, "KEEPALIVECHECKED", "checked"); }
|
||||
#endif
|
||||
|
|
@ -3296,6 +3352,23 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
|
|||
tpl_addVar(vars, TPLADD, "USERSCRIPT", rdr->userscript);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_EMU
|
||||
//emu_auproviders
|
||||
value = mk_t_ftab(&rdr->emu_auproviders);
|
||||
tpl_addVar(vars, TPLADD, "EMUAUPROVIDERS", value);
|
||||
free_mk_t(value);
|
||||
|
||||
// Date-coded BISS keys
|
||||
if(!apicall)
|
||||
{
|
||||
tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "checked" : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "1" : "0");
|
||||
}
|
||||
#endif
|
||||
|
||||
tpl_addVar(vars, TPLADD, "PROTOCOL", reader_get_type_desc(rdr, 0));
|
||||
|
||||
// Show only parameters which needed for the reader
|
||||
|
|
@ -3317,6 +3390,9 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam
|
|||
case R_CAMD35 :
|
||||
tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCAMD35BIT"));
|
||||
break;
|
||||
case R_EMU :
|
||||
tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGEMUBIT"));
|
||||
break;
|
||||
case R_CS378X :
|
||||
tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCS378XBIT"));
|
||||
break;
|
||||
|
|
@ -5354,9 +5430,34 @@ static char *send_oscam_entitlement(struct templatevars *vars, struct uriparams
|
|||
|
||||
tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", "<BR><BR>New Structure:<BR>");
|
||||
char tbuffer[83];
|
||||
#ifdef WITH_EMU
|
||||
char keyBuffer[1024];
|
||||
#endif
|
||||
int jsondelimiter = 0;
|
||||
while((item = ll_iter_next(&itr)))
|
||||
{
|
||||
#ifdef WITH_EMU
|
||||
if(item->isKey)
|
||||
{
|
||||
tpl_addVar(vars, TPLADD, "ENTSTARTDATE", "");
|
||||
tpl_addVar(vars, TPLADD, "ENTENDDATE", "");
|
||||
cs_hexdump(0, item->key, item->keyLength, keyBuffer, sizeof(keyBuffer));
|
||||
tpl_addVar(vars, TPLADD, "ENTEXPIERED", "e_valid");
|
||||
tpl_printf(vars, TPLADD, "ENTCAID", "%04X", item->caid);
|
||||
tpl_printf(vars, TPLADD, "ENTPROVID", "%08X", item->provid);
|
||||
tpl_addVar(vars, TPLADD, "ENTID", item->name);
|
||||
tpl_addVar(vars, TPLADD, "ENTCLASS", keyBuffer);
|
||||
if(item->isData) { tpl_addVar(vars, TPLADD, "ENTTYPE", "data"); }
|
||||
else { tpl_addVar(vars, TPLADD, "ENTTYPE", "key"); }
|
||||
tpl_addVar(vars, TPLADD, "ENTRESNAME", "");
|
||||
|
||||
if((strcmp(getParam(params, "hideexpired"), "1") != 0) || (item->end > now))
|
||||
{ tpl_addVar(vars, TPLAPPEND, "READERENTENTRY", tpl_getTpl(vars, "ENTITLEMENTITEMBIT")); }
|
||||
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
localtime_r(&item->start, &start_t);
|
||||
localtime_r(&item->end, &end_t);
|
||||
|
||||
|
|
@ -5853,6 +5954,9 @@ static char *send_oscam_status(struct templatevars * vars, struct uriparams * pa
|
|||
filtered = (type == cl->typ) || (type == 'x' && (cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode));
|
||||
#else
|
||||
filtered = (type == cl->typ);
|
||||
#endif
|
||||
#ifdef WITH_EMU
|
||||
if(type == 'e' && cl->typ == 'r' && cl->reader->typ == R_EMU) filtered = 1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -7444,6 +7548,9 @@ static char *send_oscam_files(struct templatevars * vars, struct uriparams * par
|
|||
{ "stats.info", MNU_GBX_FSTAINF, FTYPE_GBOX }, // id 27
|
||||
{ "expired.info", MNU_GBX_FEXPINF, FTYPE_GBOX }, // id 28
|
||||
{ "info.log", MNU_GBX_INFOLOG, FTYPE_GBOX }, // id 29
|
||||
#endif
|
||||
#ifdef WITH_EMU
|
||||
{ "SoftCam.Key", MNU_CFG_FSOFTCAMKEY,FTYPE_CONFIG }, // id 30
|
||||
#endif
|
||||
{ NULL, 0, 0 },
|
||||
};
|
||||
|
|
@ -7916,8 +8023,12 @@ static char *send_oscam_EMM_running(struct templatevars * vars, struct uriparams
|
|||
else if(!proxy && rdr->csystem_active) // local active reader
|
||||
{
|
||||
csystem = rdr->csystem;
|
||||
|
||||
if(rdr->typ != R_EMU)
|
||||
{
|
||||
caid = rdr->caid;
|
||||
}
|
||||
}
|
||||
|
||||
if(csystem)
|
||||
{
|
||||
|
|
@ -8199,29 +8310,6 @@ static char *send_oscam_cacheex(struct templatevars * vars, struct uriparams * p
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
{ tpl_addVar(vars, TPLADD, "IP", ""); }
|
||||
|
||||
|
||||
// --- NODEID changer detection (persistent, based on cxnodeid_changer_detected) ---
|
||||
int idx = cl ? cl->idx : 0;
|
||||
if(idx < 0 || idx >= WEBIF_MAX_NODES) idx = 0;
|
||||
|
||||
uint64_t current_node = get_cacheex_node(cl);
|
||||
|
||||
if(cl && cl->cxnodeid_changer_detected)
|
||||
{
|
||||
tpl_printf(vars, TPLADD, "NODE", "<font color=\"red\"><b>%" PRIu64 "X</b></font>", current_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", current_node);
|
||||
}
|
||||
|
||||
// zapamiętaj nodeid w RAM (optional)
|
||||
webif_last_nodeid[idx] = current_node;
|
||||
|
||||
// --- end detection ---
|
||||
if(IP_ISSET(cl->ip))
|
||||
{ tpl_addVar(vars, TPLADD, "IP", cs_inet_ntoa(cl->ip)); }
|
||||
else if(cl->login > cl->logout)
|
||||
|
|
@ -8284,17 +8372,7 @@ webif_last_nodeid[idx] = current_node;
|
|||
{ tpl_addVar(vars, TPLADD, "IP", "camd.socket"); }
|
||||
else
|
||||
{ tpl_addVar(vars, TPLADD, "IP", ""); }
|
||||
|
||||
uint64_t current_node = get_cacheex_node(cl);
|
||||
|
||||
if(cl && cl->cxnodeid_changer_detected)
|
||||
{
|
||||
tpl_printf(vars, TPLADD, "NODE", "<font color=\"red\"><b>%" PRIu64 "X</b></font>", current_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", current_node);
|
||||
}
|
||||
tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", get_cacheex_node(cl));
|
||||
tpl_addVar(vars, TPLADD, "LEVEL", level[cl->reader->cacheex.mode]);
|
||||
tpl_printf(vars, TPLADD, "PUSH", "%d", cl->cwcacheexpush);
|
||||
tpl_printf(vars, TPLADD, "CWCINFO", "%d", cl->cwc_info);
|
||||
|
|
@ -9243,7 +9321,11 @@ static int32_t readRequest(FILE * f, IN_ADDR_T in, char **result, int8_t forcePl
|
|||
memcpy(*result + bufsize, buf2, n);
|
||||
bufsize += n;
|
||||
|
||||
#ifdef WITH_EMU
|
||||
if(bufsize > 204800) // max request size 200kb
|
||||
#else
|
||||
if(bufsize > 102400) // max request size 100kb
|
||||
#endif
|
||||
{
|
||||
cs_log("error: too much data received from %s", cs_inet_ntoa(in));
|
||||
NULLFREE(*result);
|
||||
|
|
@ -1,985 +0,0 @@
|
|||
#define MODULE_LOG_PREFIX "cache"
|
||||
|
||||
#include "globals.h"
|
||||
#include "module-cacheex.h"
|
||||
#include "module-cw-cycle-check.h"
|
||||
#include "oscam-cache.h"
|
||||
#include "oscam-chk.h"
|
||||
#include "oscam-client.h"
|
||||
#include "oscam-ecm.h"
|
||||
#include "oscam-garbage.h"
|
||||
#include "oscam-lock.h"
|
||||
#include "oscam-net.h"
|
||||
#include "oscam-string.h"
|
||||
#include "oscam-time.h"
|
||||
#include "oscam-hashtable.h"
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
#include "oscam-log.h"
|
||||
#endif
|
||||
|
||||
|
||||
// CACHE functions **************************************************************+
|
||||
struct s_pushclient
|
||||
{
|
||||
struct s_client *cl;
|
||||
struct s_pushclient *next_push;
|
||||
};
|
||||
|
||||
typedef struct cw_t
|
||||
{
|
||||
uint8_t cw[16];
|
||||
uint8_t odd_even; // odd/even byte (0x80 0x81)
|
||||
uint8_t cwc_cycletime;
|
||||
uint8_t cwc_next_cw_cycle;
|
||||
uint8_t got_bad_cwc; // used by cycle check
|
||||
uint16_t caid; // first caid received
|
||||
uint32_t prid; // first prid received
|
||||
uint16_t srvid; // first srvid received
|
||||
struct s_reader *selected_reader; // first answering: reader
|
||||
struct s_client *cacheex_src; // first answering: cacheex client
|
||||
uint64_t grp; // updated grp
|
||||
uint8_t csp; // updated if answer from csp
|
||||
uint8_t cacheex; // updated if answer from cacheex
|
||||
uint8_t localcards; // updated if answer from local cards (or proxy using localcards option)
|
||||
uint8_t proxy; // updated if answer from local reader
|
||||
uint32_t count; // count of same cws receved
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
uint8_t localgenerated; // flag for local generated CWs
|
||||
#endif
|
||||
// for push out
|
||||
pthread_rwlock_t pushout_client_lock;
|
||||
struct s_pushclient *pushout_client; // list of clients that pushing cw
|
||||
// end push out
|
||||
node ht_node; // node for hash table
|
||||
node ll_node; // node for linked list
|
||||
} CW;
|
||||
|
||||
typedef struct cache_t
|
||||
{
|
||||
hash_table ht_cw;
|
||||
list ll_cw;
|
||||
struct timeb upd_time; // updated time. Update time at each cw got
|
||||
struct timeb first_recv_time; // time of first cw received
|
||||
uint32_t csp_hash;
|
||||
node ht_node; // node for hash table
|
||||
node ll_node; // node for linked list
|
||||
} ECMHASH;
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
typedef struct cw_cache_t
|
||||
{
|
||||
uint8_t cw[16];
|
||||
uint16_t caid;
|
||||
uint32_t prid;
|
||||
uint16_t srvid;
|
||||
struct timeb first_recv_time; // time of first cw received
|
||||
struct timeb upd_time; // updated time. Update time at each cw got
|
||||
node ht_node;
|
||||
node ll_node;
|
||||
} CW_CACHE;
|
||||
|
||||
typedef struct cw_cache_setting_t
|
||||
{
|
||||
int8_t mode;
|
||||
uint16_t timediff_old_cw;
|
||||
} CW_CACHE_SETTING;
|
||||
#endif
|
||||
|
||||
static pthread_rwlock_t cache_lock;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
static pthread_rwlock_t cw_cache_lock;
|
||||
#endif
|
||||
static hash_table ht_cache;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
static hash_table ht_cw_cache;
|
||||
#endif
|
||||
static list ll_cache;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
static list ll_cw_cache;
|
||||
#endif
|
||||
static int8_t cache_init_done = 0;
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
static int8_t cw_cache_init_done = 0;
|
||||
static uint32_t lg_cache_size = 0;
|
||||
|
||||
void init_cw_cache(void)
|
||||
{
|
||||
#ifdef CS_CACHEEX
|
||||
if(cfg.cw_cache_size > 0 || cfg.cw_cache_memory > 0)
|
||||
{
|
||||
init_hash_table(&ht_cw_cache, &ll_cw_cache);
|
||||
if (pthread_rwlock_init(&cw_cache_lock,NULL) != 0)
|
||||
{ cs_log("Error creating lock cw_cache_lock!"); }
|
||||
else
|
||||
{ cw_cache_init_done = 1; }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void init_cache(void)
|
||||
{
|
||||
init_hash_table(&ht_cache, &ll_cache);
|
||||
if (pthread_rwlock_init(&cache_lock,NULL) != 0)
|
||||
{ cs_log("Error creating lock cache_lock!"); }
|
||||
else
|
||||
{ cache_init_done = 1; }
|
||||
}
|
||||
|
||||
void free_cache(void)
|
||||
{
|
||||
cleanup_cache(true);
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
cw_cache_cleanup(true);
|
||||
ecm_cache_cleanup(true);
|
||||
cw_cache_init_done = 0;
|
||||
deinitialize_hash_table(&ht_cw_cache);
|
||||
pthread_rwlock_destroy(&cw_cache_lock);
|
||||
#endif
|
||||
cache_init_done = 0;
|
||||
deinitialize_hash_table(&ht_cache);
|
||||
pthread_rwlock_destroy(&cache_lock);
|
||||
}
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
uint32_t cache_size_lg(void)
|
||||
{
|
||||
if(!cache_init_done)
|
||||
{ return 0; }
|
||||
|
||||
return lg_cache_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t cache_size(void)
|
||||
{
|
||||
if(!cache_init_done)
|
||||
{ return 0; }
|
||||
|
||||
return count_hash_table(&ht_cache);
|
||||
}
|
||||
|
||||
static uint8_t count_sort(CW *a, CW *b)
|
||||
{
|
||||
if (a->count == b->count) return 0;
|
||||
return (a->count > b->count) ? -1 : 1; // DESC order by count
|
||||
}
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
static uint8_t time_sort(CW_CACHE *a, CW_CACHE *b)
|
||||
{
|
||||
if (((int64_t)(a->upd_time.time) * 1000ull + (int64_t) a->upd_time.millitm) == ((int64_t)(b->upd_time.time) * 1000ull + (int64_t) b->upd_time.millitm)) return 0;
|
||||
return (((int64_t)(a->upd_time.time) * 1000ull + (int64_t) a->upd_time.millitm) > ((int64_t)(b->upd_time.time) * 1000ull + (int64_t) b->upd_time.millitm)) ? -1 : 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t check_is_pushed(void *cwp, struct s_client *cl)
|
||||
{
|
||||
struct s_pushclient *cl_tmp;
|
||||
CW* cw = (CW*)cwp;
|
||||
bool pushed=false;
|
||||
|
||||
SAFE_RWLOCK_RDLOCK(&cw->pushout_client_lock);
|
||||
for (cl_tmp = cw->pushout_client; cl_tmp; cl_tmp = cl_tmp->next_push)
|
||||
{
|
||||
if(cl_tmp->cl==cl)
|
||||
{
|
||||
pushed=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!pushed)
|
||||
{
|
||||
SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock);
|
||||
SAFE_RWLOCK_WRLOCK(&cw->pushout_client_lock);
|
||||
|
||||
struct s_pushclient *new_push_client;
|
||||
if(cs_malloc(&new_push_client, sizeof(struct s_pushclient)))
|
||||
{
|
||||
new_push_client->cl=cl;
|
||||
|
||||
new_push_client->next_push=cw->pushout_client;
|
||||
cw->pushout_client=new_push_client;
|
||||
}
|
||||
|
||||
SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t get_odd_even(ECM_REQUEST *er)
|
||||
{
|
||||
return (er->ecm[0] != 0x80 && er->ecm[0] != 0x81 ? 0 : er->ecm[0]);
|
||||
}
|
||||
|
||||
|
||||
CW *get_first_cw(ECMHASH *ecmhash, ECM_REQUEST *er)
|
||||
{
|
||||
if(!ecmhash) return NULL;
|
||||
|
||||
node *j;
|
||||
CW *cw;
|
||||
|
||||
j = get_first_node_list(&ecmhash->ll_cw);
|
||||
while (j) {
|
||||
cw = get_data_from_node(j);
|
||||
|
||||
if(cw && cw->odd_even == get_odd_even(er) && !cw->got_bad_cwc)
|
||||
return cw;
|
||||
|
||||
j = j->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int compare_csp_hash(const void *arg, const void *obj)
|
||||
{
|
||||
uint32_t h = ((const ECMHASH*)obj)->csp_hash;
|
||||
return memcmp(arg, &h, 4);
|
||||
}
|
||||
|
||||
static int compare_cw(const void *arg, const void *obj)
|
||||
{
|
||||
return memcmp(arg, ((const CW*)obj)->cw, 16);
|
||||
}
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
static int compare_cw_cache(const void *arg, const void *obj)
|
||||
{
|
||||
return memcmp(arg, ((const CW_CACHE*)obj)->cw, 16);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool cwcycle_check_cache(struct s_client *cl, ECM_REQUEST *er, CW *cw)
|
||||
{
|
||||
(void)cl; (void)er; (void)cw;
|
||||
|
||||
#ifdef CW_CYCLE_CHECK
|
||||
if(cw->got_bad_cwc)
|
||||
return 0;
|
||||
|
||||
uint8_t cwc_ct = cw->cwc_cycletime > 0 ? cw->cwc_cycletime : 0;
|
||||
uint8_t cwc_ncwc = cw->cwc_next_cw_cycle < 2 ? cw->cwc_next_cw_cycle : 2;
|
||||
if(checkcwcycle(cl, er, NULL, cw->cw, 0, cwc_ct, cwc_ncwc) != 0)
|
||||
{
|
||||
cs_log_dbg(D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [check_cache] cyclecheck passed ecm in INT. cache.", (cl ? cl->account->usr : "-"), er->caid, er->srvid);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
if(!er->localgenerated)
|
||||
{
|
||||
#endif
|
||||
cs_log_dbg(D_CWC, "cyclecheck [BAD CW Cycle] from Int. Cache detected.. {client %s, caid %04X, srvid %04X} [check_cache] -> skip cache answer", (cl ? cl->account->usr : "-"), er->caid, er->srvid);
|
||||
cw->got_bad_cwc = 1; // no need to check it again
|
||||
return 0;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
}
|
||||
else
|
||||
{
|
||||
cs_log_dbg(D_CWC, "cyclecheck [BAD CW Cycle] from Int. Cache detected.. {client %s, caid %04X, srvid %04X} [check_cache] -> lg-flagged CW -> do nothing", (cl ? cl->account->usr : "-"), er->caid, er->srvid);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns cw (mostly received) in cache for er, or NULL if not found.
|
||||
* IMPORTANT:
|
||||
* - If found, DON'T forget to free returned ecm, because it is a copy useful to get data
|
||||
* - If found, and cacheex_src client of returned ecm is not NULL, and we want to access it,
|
||||
* remember to check for its validity (client structure is still existent)
|
||||
* E.g.: if(ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill)
|
||||
* We don't want make this stuff here to avoid useless cpu time if outside function we would not access to it.
|
||||
*/
|
||||
struct ecm_request_t *check_cache(ECM_REQUEST *er, struct s_client *cl)
|
||||
{
|
||||
if(!cache_init_done || !er->csp_hash) return NULL;
|
||||
|
||||
ECM_REQUEST *ecm = NULL;
|
||||
ECMHASH *result;
|
||||
CW *cw;
|
||||
uint64_t grp = cl?cl->grp:0;
|
||||
|
||||
SAFE_RWLOCK_RDLOCK(&cache_lock);
|
||||
|
||||
result = find_hash_table(&ht_cache, &er->csp_hash, sizeof(uint32_t),&compare_csp_hash);
|
||||
cw = get_first_cw(result, er);
|
||||
if (!cw)
|
||||
goto out_err;
|
||||
|
||||
if(cw->csp // csp have no grp!
|
||||
|| !grp // csp client(no grp) searching for cache
|
||||
|| (grp && cw->grp // ecm group --> only when readers/ex-clients answer (e_found) it
|
||||
&& (grp & cw->grp)))
|
||||
{
|
||||
#ifdef CS_CACHEEX
|
||||
//if preferlocalcards=2 for this ecm request, we can server ONLY cw from localcards readers until stage<3
|
||||
if(er->preferlocalcards==2 && !cw->localcards && er->stage<3){
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
CWCHECK check_cw = get_cwcheck(er);
|
||||
|
||||
if((!cw->proxy && !cw->localcards) // cw received from ONLY cacheex/csp peers
|
||||
&& check_cw.counter>1
|
||||
&& cw->count < check_cw.counter
|
||||
&& (check_cw.mode == 1 || !er->cacheex_wait_time_expired))
|
||||
{
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
// client
|
||||
if( cl && !cw->localgenerated
|
||||
&& !(chk_srvid_localgenerated_only_exception(er)) // service-based exception
|
||||
&& (cl->account->cacheex.localgenerated_only
|
||||
|| (chk_lg_only(er, &cl->account->cacheex.lg_only_tab))
|
||||
) // only lg-flagged CWs
|
||||
)
|
||||
{
|
||||
goto out_err;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!cwcycle_check_cache(cl, er, cw))
|
||||
goto out_err;
|
||||
|
||||
if (cs_malloc(&ecm, sizeof(ECM_REQUEST)))
|
||||
{
|
||||
ecm->rc = E_FOUND;
|
||||
ecm->rcEx = 0;
|
||||
memcpy(ecm->cw, cw->cw, 16);
|
||||
ecm->grp = cw->grp;
|
||||
ecm->selected_reader = cw->selected_reader;
|
||||
ecm->cwc_cycletime = cw->cwc_cycletime;
|
||||
ecm->cwc_next_cw_cycle = cw->cwc_next_cw_cycle;
|
||||
ecm->cacheex_src = cw->cacheex_src;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
ecm->localgenerated = (cw->localgenerated) ? 1:0;
|
||||
#endif
|
||||
ecm->cw_count = cw->count;
|
||||
}
|
||||
}
|
||||
|
||||
out_err:
|
||||
SAFE_RWLOCK_UNLOCK(&cache_lock);
|
||||
return ecm;
|
||||
}
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
uint16_t get_cacheex_nopushafter(ECM_REQUEST *er)
|
||||
{
|
||||
return caidvaluetab_get_value(&cfg.cacheex_nopushafter_tab, er->caid, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cacheex_cache_add(ECM_REQUEST *er, ECMHASH *result, CW *cw, bool add_new_cw)
|
||||
{
|
||||
(void)er; (void)result; (void)cw; (void)add_new_cw;
|
||||
#ifdef CS_CACHEEX
|
||||
er->cw_cache = cw;
|
||||
cacheex_cache_push(er);
|
||||
|
||||
// cacheex debug log lines and cw diff stuff
|
||||
if(!check_client(er->cacheex_src))
|
||||
return;
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
if (D_CACHEEX & cs_dblevel)
|
||||
{
|
||||
uint8_t remotenodeid[8];
|
||||
cacheex_get_srcnodeid(er, remotenodeid);
|
||||
|
||||
if(!add_new_cw)
|
||||
{
|
||||
debug_ecm(D_CACHEEX| D_CSP, "got duplicate pushed ECM %s from %s - hop %i %s, src-nodeid %" PRIu64 "X", buf, er->from_csp ? "csp" : username(er->cacheex_src), ll_count(er->csp_lastnodes), er->localgenerated ? "(lg)" : "", cacheex_node_id(remotenodeid));
|
||||
return;
|
||||
}
|
||||
|
||||
debug_ecm(D_CACHEEX|D_CSP, "got pushed ECM %s from %s - hop %i %s, src-nodeid %" PRIu64 "X", buf, er->from_csp ? "csp" : username(er->cacheex_src), ll_count(er->csp_lastnodes), er->localgenerated ? "(lg)" : "", cacheex_node_id(remotenodeid));
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
if(!add_new_cw)
|
||||
{
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
debug_ecm(D_CACHEEX| D_CSP, "got duplicate pushed ECM %s from %s - hop %i %s", buf, er->from_csp ? "csp" : username(er->cacheex_src), ll_count(er->csp_lastnodes), er->localgenerated ? "(lg)" : "");
|
||||
#else
|
||||
debug_ecm(D_CACHEEX| D_CSP, "got duplicate pushed ECM %s from %s", buf, er->from_csp ? "csp" : username(er->cacheex_src));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
debug_ecm(D_CACHEEX|D_CSP, "got pushed ECM %s from %s - hop %i %s", buf, er->from_csp ? "csp" : username(er->cacheex_src), ll_count(er->csp_lastnodes), er->localgenerated ? "(lg)" : "");
|
||||
#else
|
||||
debug_ecm(D_CACHEEX|D_CSP, "got pushed ECM %s from %s", buf, er->from_csp ? "csp" : username(er->cacheex_src));
|
||||
#endif
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
}
|
||||
#endif
|
||||
|
||||
CW *cw_first = get_first_cw(result, er);
|
||||
if(!cw_first)
|
||||
return;
|
||||
|
||||
// compare er cw with mostly counted cached cw
|
||||
if(memcmp(er->cw, cw_first->cw, sizeof(er->cw)) != 0)
|
||||
{
|
||||
er->cacheex_src->cwcacheexerrcw++;
|
||||
if (er->cacheex_src->account)
|
||||
er->cacheex_src->account->cwcacheexerrcw++;
|
||||
|
||||
if (((0x0200| 0x0800) & cs_dblevel)) // avoid useless operations if debug is not enabled
|
||||
{
|
||||
char cw1[16*3+2], cw2[16*3+2];
|
||||
cs_hexdump(0, er->cw, 16, cw1, sizeof(cw1));
|
||||
cs_hexdump(0, cw_first->cw, 16, cw2, sizeof(cw2));
|
||||
|
||||
char ip1[20]="", ip2[20]="";
|
||||
if (check_client(er->cacheex_src))
|
||||
cs_strncpy(ip1, cs_inet_ntoa(er->cacheex_src->ip), sizeof(ip1));
|
||||
if (check_client(cw_first->cacheex_src))
|
||||
cs_strncpy(ip2, cs_inet_ntoa(cw_first->cacheex_src->ip), sizeof(ip2));
|
||||
else if (cw_first->selected_reader && check_client(cw_first->selected_reader->client))
|
||||
cs_strncpy(ip2, cs_inet_ntoa(cw_first->selected_reader->client->ip), sizeof(ip2));
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
uint8_t remotenodeid[8];
|
||||
cacheex_get_srcnodeid(er, remotenodeid);
|
||||
|
||||
uint8_t fakeF0 = 0, offset = 0;
|
||||
|
||||
if(get_odd_even(er) == 0x81)
|
||||
offset = 8;
|
||||
|
||||
if(
|
||||
(cw_first->cw[7+offset] != 0x00 && er->cw[7+offset] != 0x00)
|
||||
&& (cw_first->cw[7+offset] ^ 0xF0) == er->cw[7+offset]
|
||||
)
|
||||
{
|
||||
fakeF0 = 1;
|
||||
}
|
||||
|
||||
debug_ecm(D_CACHEEX| D_CSP, "WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s lg: %i<>%i, hop:%02i, src-nodeid: %" PRIu64 "X%s", buf,
|
||||
#else
|
||||
debug_ecm(D_CACHEEX| D_CSP, "WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s ", buf,
|
||||
#endif
|
||||
er->from_csp ? "csp" : username(er->cacheex_src), ip1,
|
||||
check_client(cw_first->cacheex_src)?username(cw_first->cacheex_src):(cw_first->selected_reader?cw_first->selected_reader->label:"unknown/csp"), ip2,
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
cw1, cw2, er->localgenerated, cw_first->localgenerated, er->csp_lastnodes ? ll_count(er->csp_lastnodes) : 0, er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0, fakeF0 ? " [last byte xor 0xF0]" : "");
|
||||
#else
|
||||
cw1, cw2);
|
||||
#endif
|
||||
#ifdef WITH_DEBUG
|
||||
if(cs_dblevel & D_CACHEEX)
|
||||
{
|
||||
LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0);
|
||||
uint8_t *nodeid;
|
||||
uint8_t hops = 0;
|
||||
while((nodeid = ll_li_next(li)))
|
||||
{
|
||||
cs_log_dbg(D_CACHEEX, "Different CW-nodelist hop%02u: %" PRIu64 "X", ++hops, cacheex_node_id(nodeid));
|
||||
}
|
||||
ll_li_destroy(li);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
CW_CACHE_SETTING get_cw_cache(ECM_REQUEST *er)
|
||||
{
|
||||
int32_t i, timediff_old_cw = 0;
|
||||
int8_t mode = 0;
|
||||
|
||||
for(i = 0; i < cfg.cw_cache_settings.cwchecknum; i++)
|
||||
{
|
||||
CWCHECKTAB_DATA *d = &cfg.cw_cache_settings.cwcheckdata[i];
|
||||
|
||||
if(i == 0 && d->caid <= 0)
|
||||
{
|
||||
mode = d->mode;
|
||||
timediff_old_cw = d->counter;
|
||||
continue; //check other, only valid for unset
|
||||
}
|
||||
|
||||
if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1))
|
||||
{
|
||||
if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1)
|
||||
{
|
||||
if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1)
|
||||
{
|
||||
mode = d->mode;
|
||||
timediff_old_cw = d->counter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check for correct values
|
||||
if(mode>3 || mode<0) mode=0;
|
||||
if(timediff_old_cw<1) timediff_old_cw=0;
|
||||
|
||||
CW_CACHE_SETTING cw_cache_setting;
|
||||
memset(&cw_cache_setting, 0, sizeof(CW_CACHE_SETTING));
|
||||
cw_cache_setting.mode = mode;
|
||||
cw_cache_setting.timediff_old_cw = timediff_old_cw;
|
||||
|
||||
return cw_cache_setting;
|
||||
}
|
||||
|
||||
static bool cw_cache_check(ECM_REQUEST *er)
|
||||
{
|
||||
if(cw_cache_init_done)
|
||||
{
|
||||
CW_CACHE_SETTING cw_cache_setting = get_cw_cache(er);
|
||||
if(cw_cache_setting.mode > 0)
|
||||
{
|
||||
CW_CACHE *cw_cache = NULL;
|
||||
SAFE_RWLOCK_WRLOCK(&cw_cache_lock);
|
||||
cw_cache = find_hash_table(&ht_cw_cache, &er->cw, sizeof(er->cw), &compare_cw_cache);
|
||||
// add cw to ht_cw_cache if < cw_cache_size
|
||||
if(!cw_cache)
|
||||
{
|
||||
// cw_cache-size(count/memory) pre-check
|
||||
if(
|
||||
(cfg.cw_cache_size && (cfg.cw_cache_size > tommy_hashlin_count(&ht_cw_cache)))
|
||||
|| (cfg.cw_cache_memory && (cfg.cw_cache_memory*1024*1024 > (2 * tommy_hashlin_memory_usage(&ht_cw_cache))))
|
||||
)
|
||||
{
|
||||
if(cs_malloc(&cw_cache, sizeof(CW_CACHE)))
|
||||
{
|
||||
memcpy(cw_cache->cw, er->cw, sizeof(er->cw));
|
||||
cw_cache->caid = er->caid;
|
||||
cw_cache->prid = er->prid;
|
||||
cw_cache->srvid = er->srvid;
|
||||
cs_ftime(&cw_cache->first_recv_time);
|
||||
cs_ftime(&cw_cache->upd_time);
|
||||
|
||||
tommy_hashlin_insert(&ht_cw_cache, &cw_cache->ht_node, cw_cache, tommy_hash_u32(0, &er->cw, sizeof(er->cw)));
|
||||
tommy_list_insert_tail(&ll_cw_cache, &cw_cache->ll_node, cw_cache);
|
||||
|
||||
SAFE_RWLOCK_UNLOCK(&cw_cache_lock);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SAFE_RWLOCK_UNLOCK(&cw_cache_lock);
|
||||
cs_log("[cw_cache] ERROR: NO added HASH to cw_cache!!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// clean cache call;
|
||||
SAFE_RWLOCK_UNLOCK(&cw_cache_lock);
|
||||
cw_cache_cleanup(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// cw found
|
||||
else
|
||||
{
|
||||
char cw1[16*3+2];
|
||||
char cw2[16*3+2];
|
||||
int8_t drop_cw = 0;
|
||||
int64_t gone_diff = 0;
|
||||
|
||||
gone_diff = comp_timeb(&er->tps, &cw_cache->first_recv_time);
|
||||
|
||||
if(D_CW_CACHE & cs_dblevel)
|
||||
{
|
||||
cs_hexdump(0, cw_cache->cw, 16, cw1, sizeof(cw1));
|
||||
cs_hexdump(0, er->cw, 16, cw2, sizeof(cw2));
|
||||
}
|
||||
|
||||
if(cw_cache_setting.timediff_old_cw > 0 && gone_diff > cw_cache_setting.timediff_old_cw) // late (>cw_cache_setting.timediff_old_cw) cw incoming
|
||||
{
|
||||
// log every dupe cw
|
||||
if(cs_dblevel & D_CW_CACHE)
|
||||
{
|
||||
uint8_t remotenodeid[8];
|
||||
cacheex_get_srcnodeid(er, remotenodeid);
|
||||
cs_log_dbg(D_CW_CACHE,"[dupe CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s | diff(now): %"PRIi64" ms > %"PRIu16" - %s - hop %i%s, src-nodeid %" PRIu64 "X", cw_cache->caid, cw_cache->prid, cw_cache->srvid, cw1, er->caid, er->prid, er->srvid, cw2, gone_diff, cw_cache_setting.timediff_old_cw, (er->selected_reader && cs_strlen(er->selected_reader->label)) ? er->selected_reader->label : username(er->cacheex_src), ll_count(er->csp_lastnodes), (er->localgenerated) ? " (lg)" : "", er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0);
|
||||
}
|
||||
|
||||
if(cw_cache->srvid == er->srvid && cw_cache->caid == er->caid) // same cw for same caid&srvid
|
||||
{
|
||||
cs_ftime(&cw_cache->upd_time);
|
||||
cs_log_dbg(D_CW_CACHE,"[late CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s | diff(now): %"PRIi64" ms > %"PRIu16" - %s - hop %i%s", cw_cache->caid, cw_cache->prid, cw_cache->srvid, cw1, er->caid, er->prid, er->srvid, cw2, gone_diff, cw_cache_setting.timediff_old_cw, (er->selected_reader && cs_strlen(er->selected_reader->label)) ? er->selected_reader->label : username(er->cacheex_src), ll_count(er->csp_lastnodes), (er->localgenerated) ? " (lg)" : "");
|
||||
drop_cw=1;
|
||||
|
||||
}
|
||||
else if(cw_cache->srvid != er->srvid) // same cw for different srvid & late
|
||||
{
|
||||
cs_ftime(&cw_cache->upd_time);
|
||||
cs_log_dbg(D_CW_CACHE,"[dupe&late CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s| diff(now): %"PRIi64" ms - %s - hop %i%s", cw_cache->caid, cw_cache->prid, cw_cache->srvid, cw1, er->caid, er->prid, er->srvid, cw2, gone_diff, (er->selected_reader && cs_strlen(er->selected_reader->label)) ? er->selected_reader->label : username(er->cacheex_src), ll_count(er->csp_lastnodes), (er->localgenerated) ? " (lg)" : "");
|
||||
drop_cw = 1;
|
||||
}
|
||||
else if(gone_diff > 15000) // same cw later as 15 secs
|
||||
{
|
||||
#ifdef WITH_DEBUG
|
||||
if(cs_dblevel & D_CW_CACHE)
|
||||
{
|
||||
uint8_t remotenodeid[8];
|
||||
cacheex_get_srcnodeid(er, remotenodeid);
|
||||
cs_log_dbg(D_CW_CACHE,"[late-15sec+ CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s | diff(now): %"PRIi64" ms > %"PRIu16" - %s - hop %i%s, src-nodeid %" PRIu64 "X", cw_cache->caid, cw_cache->prid, cw_cache->srvid, cw1, er->caid, er->prid, er->srvid, cw2, gone_diff, cw_cache_setting.timediff_old_cw, (er->selected_reader && cs_strlen(er->selected_reader->label)) ? er->selected_reader->label : username(er->cacheex_src), ll_count(er->csp_lastnodes), (er->localgenerated) ? " (lg)" : "", er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0);
|
||||
}
|
||||
#endif
|
||||
drop_cw = 1;
|
||||
}
|
||||
|
||||
if(cw_cache_setting.mode > 1 && drop_cw)
|
||||
{
|
||||
// cw_cache->drop_count++;
|
||||
cs_log_dbg(D_CW_CACHE,"incoming CW dropped - current cw_cache_size: %i - cw_cache-mem-size: %iMiB", count_hash_table(&ht_cw_cache), 2*(int)tommy_hashlin_memory_usage(&ht_cw_cache)/1024/1024);
|
||||
SAFE_RWLOCK_UNLOCK(&cw_cache_lock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SAFE_RWLOCK_UNLOCK(&cw_cache_lock);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cs_log_dbg(D_CW_CACHE,"[cw_cache] cw_cache_init_done %i cfg.cw_cache_size: %u cfg.cw_cache_memory %u", cw_cache_init_done, cfg.cw_cache_size, cfg.cw_cache_memory);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void add_cache(ECM_REQUEST *er)
|
||||
{
|
||||
if(!cache_init_done || !er->csp_hash) return;
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
// cw_cache_check
|
||||
if(!cw_cache_check(er))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
ECMHASH *result = NULL;
|
||||
CW *cw = NULL;
|
||||
bool add_new_cw=false;
|
||||
|
||||
SAFE_RWLOCK_WRLOCK(&cache_lock);
|
||||
|
||||
// add csp_hash to cache
|
||||
result = find_hash_table(&ht_cache, &er->csp_hash, sizeof(uint32_t), &compare_csp_hash);
|
||||
if(!result)
|
||||
{
|
||||
if(cs_malloc(&result, sizeof(ECMHASH)))
|
||||
{
|
||||
result->csp_hash = er->csp_hash;
|
||||
init_hash_table(&result->ht_cw, &result->ll_cw);
|
||||
cs_ftime(&result->first_recv_time);
|
||||
add_hash_table(&ht_cache, &result->ht_node, &ll_cache, &result->ll_node, result, &result->csp_hash, sizeof(uint32_t));
|
||||
}
|
||||
else
|
||||
{
|
||||
SAFE_RWLOCK_UNLOCK(&cache_lock);
|
||||
cs_log("ERROR: NO added HASH to cache!!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cs_ftime(&result->upd_time); // need to be updated at each cw! We use it for deleting this hash when no more cws arrive inside max_cache_time!
|
||||
|
||||
//add cw to this csp hash
|
||||
cw = find_hash_table(&result->ht_cw, er->cw, sizeof(er->cw), &compare_cw);
|
||||
|
||||
if(!cw)
|
||||
{
|
||||
if(count_hash_table(&result->ht_cw) >= 10) // max 10 different cws stored
|
||||
{
|
||||
SAFE_RWLOCK_UNLOCK(&cache_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
while(1)
|
||||
{
|
||||
if(cs_malloc(&cw, sizeof(CW)))
|
||||
{
|
||||
memcpy(cw->cw, er->cw, sizeof(er->cw));
|
||||
cw->odd_even = get_odd_even(er);
|
||||
cw->cwc_cycletime = er->cwc_cycletime;
|
||||
cw->cwc_next_cw_cycle = er->cwc_next_cw_cycle;
|
||||
cw->count= 0;
|
||||
cw->csp = 0;
|
||||
cw->cacheex = 0;
|
||||
cw->localcards=0;
|
||||
cw->proxy=0;
|
||||
cw->grp = 0;
|
||||
cw->caid = er->caid;
|
||||
cw->prid = er->prid;
|
||||
cw->srvid = er->srvid;
|
||||
cw->selected_reader=er->selected_reader;
|
||||
cw->cacheex_src=er->cacheex_src;
|
||||
cw->pushout_client = NULL;
|
||||
|
||||
while(1)
|
||||
{
|
||||
if (pthread_rwlock_init(&cw->pushout_client_lock, NULL) == 0)
|
||||
break;
|
||||
|
||||
cs_log("Error creating lock pushout_client_lock!");
|
||||
cs_sleepms(1);
|
||||
}
|
||||
|
||||
add_hash_table(&result->ht_cw, &cw->ht_node, &result->ll_cw, &cw->ll_node, cw, cw->cw, sizeof(er->cw));
|
||||
add_new_cw=true;
|
||||
break;
|
||||
}
|
||||
|
||||
cs_log("ERROR: NO added CW to cache!! Re-trying...");
|
||||
cs_sleepms(1);
|
||||
}
|
||||
}
|
||||
|
||||
// update if answered from csp/cacheex/local_proxy
|
||||
if(er->from_cacheex) cw->cacheex = 1;
|
||||
if(er->from_csp) cw->csp = 1;
|
||||
if(!er->cacheex_src)
|
||||
{
|
||||
if(is_localreader(er->selected_reader, er)) cw->localcards=1;
|
||||
else cw->proxy = 1;
|
||||
}
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
// copy flag for local generated CW
|
||||
if(er->localgenerated || (er->selected_reader && !is_network_reader(er->selected_reader)))
|
||||
{
|
||||
cw->localgenerated = 1;
|
||||
er->localgenerated = 1;
|
||||
// to favorite CWs with this flag while sorting
|
||||
if(cw->count < 0x0F000000)
|
||||
{
|
||||
cw->count |= 0x0F000000;
|
||||
lg_cache_size++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cw->localgenerated = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// always update group and counter
|
||||
cw->grp |= er->grp;
|
||||
cw->count++;
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
// add count to er for checking @ cacheex_push
|
||||
er->cw_count += cw->count;
|
||||
#endif
|
||||
// sort cw_list by counter (DESC order)
|
||||
if(cw->count>1)
|
||||
sort_list(&result->ll_cw, count_sort);
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
// dont push not flagged CWs - global
|
||||
if(!er->localgenerated &&
|
||||
(
|
||||
!chk_srvid_localgenerated_only_exception(er)
|
||||
&& (cfg.cacheex_localgenerated_only || chk_lg_only(er, &cfg.cacheex_lg_only_tab))
|
||||
) )
|
||||
{
|
||||
cs_log_dbg(D_CACHEEX, "cacheex: push denied, cacheex_localgenerated_only->global");
|
||||
SAFE_RWLOCK_UNLOCK(&cache_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
// dont push CW if time for caid > x && from local reader | proxy
|
||||
if(er->rc < 3 && er->ecm_time && get_cacheex_nopushafter(er) != 0 &&(get_cacheex_nopushafter(er) < er->ecm_time ))
|
||||
{
|
||||
cs_log_dbg(D_CACHEEX, "cacheex: push denied, cacheex_nopushafter %04X:%u < %i, reader: %s", er->caid, get_cacheex_nopushafter(er), er->ecm_time, er->selected_reader->label);
|
||||
SAFE_RWLOCK_UNLOCK(&cache_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
// no cacheex-push on diff-cw's if no localgenerated flag exist
|
||||
if(cfg.cacheex_dropdiffs && (count_hash_table(&result->ht_cw) > 1) && !er->localgenerated)
|
||||
{
|
||||
cs_log_dbg(D_CACHEEX,"cacheex: diff CW - cacheex push denied src: %s", er->selected_reader->label);
|
||||
SAFE_RWLOCK_UNLOCK(&cache_lock);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
SAFE_RWLOCK_UNLOCK(&cache_lock);
|
||||
|
||||
cacheex_cache_add(er, result, cw, add_new_cw);
|
||||
}
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
void cw_cache_cleanup(bool force)
|
||||
{
|
||||
if(!cw_cache_init_done)
|
||||
{ return; }
|
||||
|
||||
SAFE_RWLOCK_WRLOCK(&cw_cache_lock);
|
||||
|
||||
CW_CACHE *cw_cache;
|
||||
node *i, *i_next;
|
||||
|
||||
uint32_t ll_c = 0;
|
||||
uint32_t ll_ten_percent = (uint)tommy_list_count(&ll_cw_cache)*0.1; // 10 percent of cache
|
||||
|
||||
if(!force)
|
||||
sort_list(&ll_cw_cache, time_sort);
|
||||
|
||||
i = get_first_node_list(&ll_cw_cache);
|
||||
while(i)
|
||||
{
|
||||
i_next = i->next;
|
||||
|
||||
cw_cache = get_data_from_node(i);
|
||||
|
||||
if(!cw_cache)
|
||||
{
|
||||
i = i_next;
|
||||
continue;
|
||||
}
|
||||
if(!force)
|
||||
{
|
||||
++ll_c;
|
||||
|
||||
if(ll_c < ll_ten_percent)
|
||||
{
|
||||
remove_elem_list(&ll_cw_cache, &cw_cache->ll_node);
|
||||
remove_elem_hash_table(&ht_cw_cache, &cw_cache->ht_node);
|
||||
NULLFREE(cw_cache);
|
||||
}
|
||||
else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
remove_elem_list(&ll_cw_cache, &cw_cache->ll_node);
|
||||
remove_elem_hash_table(&ht_cw_cache, &cw_cache->ht_node);
|
||||
NULLFREE(cw_cache);
|
||||
}
|
||||
|
||||
i = i_next;
|
||||
}
|
||||
|
||||
SAFE_RWLOCK_UNLOCK(&cw_cache_lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
void cleanup_cache(bool force)
|
||||
{
|
||||
ECMHASH *ecmhash;
|
||||
CW *cw;
|
||||
struct s_pushclient *pc, *nxt;
|
||||
node *i,*i_next,*j,*j_next;
|
||||
|
||||
struct timeb now;
|
||||
int64_t gone_first, gone_upd;
|
||||
|
||||
if(!cache_init_done)
|
||||
{ return; }
|
||||
|
||||
SAFE_RWLOCK_WRLOCK(&cache_lock);
|
||||
|
||||
i = get_first_node_list(&ll_cache);
|
||||
while(i)
|
||||
{
|
||||
i_next = i->next;
|
||||
ecmhash = get_data_from_node(i);
|
||||
|
||||
if(!ecmhash)
|
||||
{
|
||||
i = i_next;
|
||||
continue;
|
||||
}
|
||||
|
||||
cs_ftime(&now);
|
||||
gone_first = comp_timeb(&now, &ecmhash->first_recv_time);
|
||||
gone_upd = comp_timeb(&now, &ecmhash->upd_time);
|
||||
|
||||
if(!force && gone_first<=(cfg.max_cache_time*1000)) // not continue, useless check for nexts one!
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if(force || gone_upd>(cfg.max_cache_time*1000))
|
||||
{
|
||||
j = get_first_node_list(&ecmhash->ll_cw);
|
||||
while(j)
|
||||
{
|
||||
j_next = j->next;
|
||||
cw = get_data_from_node(j);
|
||||
if(cw)
|
||||
{
|
||||
pthread_rwlock_destroy(&cw->pushout_client_lock);
|
||||
pc = cw->pushout_client;
|
||||
cw->pushout_client=NULL;
|
||||
while(pc)
|
||||
{
|
||||
nxt = pc->next_push;
|
||||
NULLFREE(pc);
|
||||
pc = nxt;
|
||||
}
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
if(cw->count >= 0x0F000000)
|
||||
{
|
||||
lg_cache_size--;
|
||||
}
|
||||
#endif
|
||||
remove_elem_list(&ecmhash->ll_cw, &cw->ll_node);
|
||||
remove_elem_hash_table(&ecmhash->ht_cw, &cw->ht_node);
|
||||
NULLFREE(cw);
|
||||
}
|
||||
j = j_next;
|
||||
}
|
||||
|
||||
deinitialize_hash_table(&ecmhash->ht_cw);
|
||||
remove_elem_list(&ll_cache, &ecmhash->ll_node);
|
||||
remove_elem_hash_table(&ht_cache, &ecmhash->ht_node);
|
||||
NULLFREE(ecmhash);
|
||||
}
|
||||
i = i_next;
|
||||
}
|
||||
SAFE_RWLOCK_UNLOCK(&cache_lock);
|
||||
}
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
void cacheex_get_srcnodeid(ECM_REQUEST *er, uint8_t *remotenodeid)
|
||||
{
|
||||
uint8_t *data;
|
||||
data = ll_last_element(er->csp_lastnodes);
|
||||
if(data)
|
||||
{
|
||||
memcpy(remotenodeid, data, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(remotenodeid, 0 , 8);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
14
oscam-chk.c
14
oscam-chk.c
|
|
@ -894,7 +894,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if( !is_network_reader(rdr) && (rdr->caid >> 8 != ((er->caid >> 8) & 0xFF)) && (rdr->caid >> 8 != ((er->ocaid >> 8) & 0xFF)) )
|
||||
if(!(rdr->typ == R_EMU) && !is_network_reader(rdr) && ((rdr->caid >> 8) != ((er->caid >> 8) & 0xFF) && (rdr->caid >> 8) != ((er->ocaid >> 8) & 0xFF)))
|
||||
{
|
||||
if (!rdr->csystem)
|
||||
{ return 0; }
|
||||
|
|
@ -932,7 +932,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
|
|||
}
|
||||
|
||||
// Checking ident:
|
||||
if(!chk_rfilter(er, rdr))
|
||||
if(!(rdr->typ == R_EMU && caid_is_biss(er->caid)) && !chk_rfilter(er, rdr))
|
||||
{
|
||||
cs_log_dbg(D_TRACE, "r-filter reader %s", rdr->label);
|
||||
return (0);
|
||||
|
|
@ -993,7 +993,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
|
|||
}
|
||||
|
||||
// Checking entitlements:
|
||||
if(ll_count(rdr->ll_entitlements) > 0)
|
||||
if(ll_count(rdr->ll_entitlements) > 0 && !(rdr->typ == R_EMU))
|
||||
{
|
||||
LL_ITER itr = ll_iter_create(rdr->ll_entitlements);
|
||||
S_ENTITLEMENT *item;
|
||||
|
|
@ -1174,7 +1174,7 @@ int32_t chk_caid(uint16_t caid, CAIDTAB *ctab)
|
|||
|
||||
int32_t chk_caid_rdr(struct s_reader *rdr, uint16_t caid)
|
||||
{
|
||||
if(is_network_reader(rdr))
|
||||
if(is_network_reader(rdr) || rdr->typ == R_EMU)
|
||||
{
|
||||
return 1; // reader caid is not real caid
|
||||
}
|
||||
|
|
@ -1268,7 +1268,7 @@ int8_t chk_fullCW(ECM_REQUEST *er, uint8_t *cw)
|
|||
return -1;
|
||||
}
|
||||
|
||||
// jeœli to halfCW system — pomijamy
|
||||
// jeśli to halfCW system — pomijamy
|
||||
if(is_halfCW_er(er))
|
||||
{
|
||||
return -1;
|
||||
|
|
@ -1277,13 +1277,13 @@ int8_t chk_fullCW(ECM_REQUEST *er, uint8_t *cw)
|
|||
int8_t part1 = checkCWpart(cw, 0);
|
||||
int8_t part2 = checkCWpart(cw, 1);
|
||||
|
||||
// full CW musi mieæ obie czêœci poprawne
|
||||
// full CW musi mieć obie części poprawne
|
||||
if(part1 && part2)
|
||||
{
|
||||
return 1; // OK
|
||||
}
|
||||
|
||||
return 0; // Z£Y FULL CW
|
||||
return 0; // ZŁY FULL CW
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -894,7 +894,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if( !is_network_reader(rdr) && (rdr->caid >> 8 != ((er->caid >> 8) & 0xFF)) && (rdr->caid >> 8 != ((er->ocaid >> 8) & 0xFF)) )
|
||||
if(!(rdr->typ == R_EMU) && !is_network_reader(rdr) && ((rdr->caid >> 8) != ((er->caid >> 8) & 0xFF) && (rdr->caid >> 8) != ((er->ocaid >> 8) & 0xFF)))
|
||||
{
|
||||
if (!rdr->csystem)
|
||||
{ return 0; }
|
||||
|
|
@ -932,7 +932,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
|
|||
}
|
||||
|
||||
// Checking ident:
|
||||
if(!chk_rfilter(er, rdr))
|
||||
if(!(rdr->typ == R_EMU && caid_is_biss(er->caid)) && !chk_rfilter(er, rdr))
|
||||
{
|
||||
cs_log_dbg(D_TRACE, "r-filter reader %s", rdr->label);
|
||||
return (0);
|
||||
|
|
@ -993,7 +993,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr)
|
|||
}
|
||||
|
||||
// Checking entitlements:
|
||||
if(ll_count(rdr->ll_entitlements) > 0)
|
||||
if(ll_count(rdr->ll_entitlements) > 0 && !(rdr->typ == R_EMU))
|
||||
{
|
||||
LL_ITER itr = ll_iter_create(rdr->ll_entitlements);
|
||||
S_ENTITLEMENT *item;
|
||||
|
|
@ -1174,7 +1174,7 @@ int32_t chk_caid(uint16_t caid, CAIDTAB *ctab)
|
|||
|
||||
int32_t chk_caid_rdr(struct s_reader *rdr, uint16_t caid)
|
||||
{
|
||||
if(is_network_reader(rdr))
|
||||
if(is_network_reader(rdr) || rdr->typ == R_EMU)
|
||||
{
|
||||
return 1; // reader caid is not real caid
|
||||
}
|
||||
11
oscam-chk.c.rej
Executable file
11
oscam-chk.c.rej
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
--- oscam-chk.c (revision 11871)
|
||||
+++ oscam-chk.c (working copy)
|
||||
@@ -1213,7 +1213,7 @@
|
||||
**/
|
||||
int8_t is_halfCW_er(ECM_REQUEST *er)
|
||||
{
|
||||
- if( caid_is_videoguard(er->caid) && (er->caid == 0x09C4 || er->caid == 0x098C || er->caid == 0x098D || er->caid == 0x0963 || er->caid == 0x09CD || er->caid == 0x0919 || er->caid == 0x093B || er->caid == 0x098E))
|
||||
+ if( caid_is_videoguard(er->caid) && (er->caid == 0x098C || er->caid == 0x098D || er->caid == 0x098E || er->caid == 0x09BD || er->caid == 0x0963 || er->caid == 0x09CD || er->caid == 0x0919 || er->caid == 0x093B))
|
||||
{ return 1; }
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -622,3 +622,47 @@ void clear_cacheextab(CECSPVALUETAB *ctab)
|
|||
ctab->cevnum = 0;
|
||||
NULLFREE(ctab->cevdata);
|
||||
}
|
||||
|
||||
void cwvote_caidtab_clear(CW_VOTE_CAID_TAB *cwvote_caid_table)
|
||||
{
|
||||
if (cwvote_caid_table->cvcdata)
|
||||
{
|
||||
NULLFREE(cwvote_caid_table->cvcdata);
|
||||
}
|
||||
cwvote_caid_table->cvcnum = 0;
|
||||
}
|
||||
|
||||
void chk_cwvote_caidtab(char *value, CW_VOTE_CAID_TAB *cwvote_caid_table)
|
||||
{
|
||||
char *ptr1, *saveptr1 = NULL;
|
||||
int32_t i;
|
||||
uint16_t caid;
|
||||
|
||||
cwvote_caidtab_clear(cwvote_caid_table);
|
||||
|
||||
for (i = 0, ptr1 = strtok_r(value, ",", &saveptr1); ptr1; ptr1 = strtok_r(NULL, ",", &saveptr1))
|
||||
{
|
||||
if (i >= CS_MAXPROV)
|
||||
{
|
||||
cs_log("WARNING: Too many CW Vote CAIDs defined, max %d allowed!", CS_MAXPROV);
|
||||
break;
|
||||
}
|
||||
errno = 0; // Reset errno before calling strtoul
|
||||
caid = (uint16_t)strtoul(ptr1, NULL, 16);
|
||||
if (errno != 0) {
|
||||
cs_log("WARNING: Invalid CAID format '%s' in cwvote_caids, ignoring.", ptr1);
|
||||
continue;
|
||||
}
|
||||
if (caid > 0)
|
||||
{
|
||||
if (!cs_realloc(&cwvote_caid_table->cvcdata, sizeof(CW_VOTE_CAID_DATA) * (i + 1)))
|
||||
{
|
||||
cwvote_caid_table->cvcnum = 0;
|
||||
return;
|
||||
}
|
||||
cwvote_caid_table->cvcdata[i].caid = caid;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
cwvote_caid_table->cvcnum = i;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@ void chk_port_tab(char *portasc, PTAB *ptab);
|
|||
void chk_port_camd35_tab(char *portasc, PTAB *ptab);
|
||||
void chk_ecm_whitelist(char *value, ECM_WHITELIST *ecm_whitelist);
|
||||
void chk_ecm_hdr_whitelist(char *value, ECM_HDR_WHITELIST *ecm_hdr_whitelist);
|
||||
void chk_cwvote_caidtab(char *value, CW_VOTE_CAID_TAB *cwvote_caid_table);
|
||||
|
||||
void clear_sip(struct s_ip **sip);
|
||||
void clear_sip(struct s_ip **base);
|
||||
void clear_ptab(struct s_ptab *ptab);
|
||||
void clear_cacheextab(CECSPVALUETAB *ctab);
|
||||
void cwvote_caidtab_clear(CW_VOTE_CAID_TAB *cwvote_caid_table);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1109,6 +1109,26 @@ char *mk_t_allowedtimeframe(struct s_auth *account)
|
|||
* mk_t-functions give back a constant empty string when allocation fails or when the result is an empty string.
|
||||
* This function thus checks the stringlength and only frees if necessary.
|
||||
*/
|
||||
char *mk_t_cwvote_caidtab(CW_VOTE_CAID_TAB *cwvote_caid_table)
|
||||
{
|
||||
if (!cwvote_caid_table || !cwvote_caid_table->cvcnum) { return ""; }
|
||||
|
||||
// Max entry length is cs_strlen("0000,") == 5
|
||||
int32_t i, maxlen = 6 * cwvote_caid_table->cvcnum, pos = 0;
|
||||
char *ret;
|
||||
if (!cs_malloc(&ret, maxlen))
|
||||
{ return ""; }
|
||||
|
||||
const char *comma = "";
|
||||
|
||||
for (i = 0; i < cwvote_caid_table->cvcnum; i++)
|
||||
{
|
||||
pos += snprintf(ret + pos, maxlen - pos, "%s%04X", comma, cwvote_caid_table->cvcdata[i].caid);
|
||||
comma = ",";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void free_mk_t(char *value)
|
||||
{
|
||||
if(cs_strlen(value) > 0) { NULLFREE(value); }
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ extern char *mk_t_cltab(CLASSTAB *clstab);
|
|||
extern char *mk_t_emmbylen(struct s_reader *rdr);
|
||||
extern char *mk_t_allowedprotocols(struct s_auth *account);
|
||||
extern char *mk_t_allowedtimeframe(struct s_auth *account);
|
||||
extern char *mk_t_cwvote_caidtab(CW_VOTE_CAID_TAB *cwvote_caid_table);
|
||||
extern void free_mk_t(char *value);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
67
oscam-conf.c
67
oscam-conf.c
|
|
@ -70,7 +70,8 @@ int config_list_parse(const struct config_list *clist, const char *token, char *
|
|||
{
|
||||
case OPT_INT8:
|
||||
{
|
||||
*(int8_t *)var = (int8_t)strToIntVal(value, c->def.d_int8);
|
||||
int8_t tmp = (int8_t)strToIntVal(value, c->def.d_int8);
|
||||
*(int8_t *)var = tmp;
|
||||
return 1;
|
||||
}
|
||||
case OPT_UINT8:
|
||||
|
|
@ -91,6 +92,12 @@ int config_list_parse(const struct config_list *clist, const char *token, char *
|
|||
memcpy(var, &tmp, sizeof(uint32_t));
|
||||
return 1;
|
||||
}
|
||||
case OPT_FLOAT:
|
||||
{
|
||||
float tmp = (float)atof(value); // Użyj atof do konwersji na float
|
||||
memcpy(var, &tmp, sizeof(float));
|
||||
return 1;
|
||||
}
|
||||
case OPT_STRING:
|
||||
{
|
||||
char **scfg = var;
|
||||
|
|
@ -173,24 +180,35 @@ void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_
|
|||
{
|
||||
int8_t val = *(int8_t *)var;
|
||||
|
||||
// always save pmt_mode, because external tools parse it
|
||||
if(save_all || val != c->def.d_int8 || !strcmp(c->config_name, "pmt_mode"))
|
||||
{ fprintf_conf(f, c->config_name, "%d\n", val); }
|
||||
// always save pmt_mode, and all cwvote settings, because external tools parse it
|
||||
if(save_all || val != c->def.d_int8 || !strcmp(c->config_name, "pmt_mode") ||
|
||||
!strcmp(c->config_name, "cwvote_enabled") || !strcmp(c->config_name, "cwvote_log_enabled"))
|
||||
{
|
||||
fprintf_conf(f, c->config_name, "%d\n", val);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case OPT_UINT8:
|
||||
{
|
||||
uint8_t val = *(uint8_t *)var;
|
||||
if(save_all || val != c->def.d_uint8)
|
||||
{ fprintf_conf(f, c->config_name, "%u\n", val); }
|
||||
{
|
||||
fprintf_conf(f, c->config_name, "%u\n", val);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case OPT_INT32:
|
||||
{
|
||||
int32_t val;
|
||||
memcpy(&val, var, sizeof(int32_t));
|
||||
if(save_all || val != c->def.d_int32)
|
||||
{ fprintf_conf(f, c->config_name, "%d\n", val); }
|
||||
// always save all cwvote settings
|
||||
if(save_all || val != c->def.d_int32 ||
|
||||
!strcmp(c->config_name, "cwvote_timeout") || !strcmp(c->config_name, "cwvote_min_votes") ||
|
||||
!strcmp(c->config_name, "cwvote_max_candidates") || !strcmp(c->config_name, "cwvote_compare_len") ||
|
||||
!strcmp(c->config_name, "cwvote_fallback"))
|
||||
{
|
||||
fprintf_conf(f, c->config_name, "%d\n", val);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case OPT_UINT32:
|
||||
|
|
@ -198,7 +216,20 @@ void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_
|
|||
uint32_t val;
|
||||
memcpy(&val, var, sizeof(uint32_t));
|
||||
if(save_all || val != c->def.d_uint32)
|
||||
{ fprintf_conf(f, c->config_name, "%u\n", val); }
|
||||
{
|
||||
fprintf_conf(f, c->config_name, "%u\n", val);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case OPT_FLOAT:
|
||||
{
|
||||
float val;
|
||||
memcpy(&val, var, sizeof(float));
|
||||
// always save cwvote_local_weight
|
||||
if(save_all || val != c->def.d_float || !strcmp(c->config_name, "cwvote_local_weight"))
|
||||
{
|
||||
fprintf_conf(f, c->config_name, "%g\n", val);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case OPT_STRING:
|
||||
|
|
@ -238,8 +269,18 @@ void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_
|
|||
continue;
|
||||
}
|
||||
case OPT_FUNC:
|
||||
{
|
||||
// always save cwvote_caids
|
||||
if(save_all || !strcmp(c->config_name, "cwvote_caids"))
|
||||
{
|
||||
c->ops.process_fn((const char *)c->config_name, NULL, var, f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Call the function only if it's not a cwvote_caids and not save_all
|
||||
// or if it's a cwvote_caids but save_all is false (handled above)
|
||||
c->ops.process_fn((const char *)c->config_name, NULL, var, f);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case OPT_FUNC_EXTRA:
|
||||
|
|
@ -310,6 +351,11 @@ void config_list_set_defaults(const struct config_list *clist, void *config_data
|
|||
memcpy(var, &c->def.d_uint32, sizeof(uint32_t));
|
||||
break;
|
||||
}
|
||||
case OPT_FLOAT:
|
||||
{
|
||||
memcpy(var, &c->def.d_float, sizeof(float));
|
||||
break;
|
||||
}
|
||||
case OPT_STRING:
|
||||
{
|
||||
char **scfg = var;
|
||||
|
|
@ -520,7 +566,7 @@ FILE *create_config_file(const char *conf_filename)
|
|||
|
||||
bool flush_config_file(FILE *f, const char *conf_filename)
|
||||
{
|
||||
char dst_file[256], tmp_file[256], bak_file[256];
|
||||
char dst_file[220], tmp_file[220], bak_file[220]; // Zmniejszono rozmiar bufora, aby uniknąć potencjalnych problemów z przepełnieniem
|
||||
get_config_filename(dst_file, sizeof(dst_file), conf_filename);
|
||||
memcpy(tmp_file, dst_file, sizeof(tmp_file));
|
||||
memcpy(bak_file, dst_file, sizeof(bak_file));
|
||||
|
|
@ -530,5 +576,6 @@ bool flush_config_file(FILE *f, const char *conf_filename)
|
|||
{
|
||||
fclose(f);
|
||||
}
|
||||
return safe_overwrite_with_bak(dst_file, tmp_file, bak_file, cfg.http_overwrite_bak_file);
|
||||
int32_t result = safe_overwrite_with_bak(dst_file, tmp_file, bak_file, cfg.http_overwrite_bak_file);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
74
oscam-conf.h
74
oscam-conf.h
|
|
@ -10,6 +10,7 @@ enum opt_types
|
|||
OPT_UINT8,
|
||||
OPT_INT32,
|
||||
OPT_UINT32,
|
||||
OPT_FLOAT,
|
||||
OPT_STRING,
|
||||
OPT_SSTRING,
|
||||
OPT_HEX_ARRAY,
|
||||
|
|
@ -31,6 +32,7 @@ struct config_list
|
|||
uint8_t d_uint8;
|
||||
int32_t d_int32;
|
||||
uint32_t d_uint32;
|
||||
float d_float;
|
||||
char *d_char;
|
||||
long d_extra;
|
||||
uint32_t array_size;
|
||||
|
|
@ -51,7 +53,10 @@ struct config_list
|
|||
.opt_type = OPT_INT8, \
|
||||
.config_name = __name, \
|
||||
.var_offset = __var_ofs, \
|
||||
.def.d_int8 = __default \
|
||||
.str_size = 0, \
|
||||
.def.d_int8 = __default, \
|
||||
.ops.process_fn = NULL, \
|
||||
.free_value = NULL \
|
||||
}
|
||||
|
||||
#define DEF_OPT_UINT8(__name, __var_ofs, __default) \
|
||||
|
|
@ -59,7 +64,10 @@ struct config_list
|
|||
.opt_type = OPT_UINT8, \
|
||||
.config_name = __name, \
|
||||
.var_offset = __var_ofs, \
|
||||
.def.d_uint8 = __default \
|
||||
.str_size = 0, \
|
||||
.def.d_uint8 = __default, \
|
||||
.ops.process_fn = NULL, \
|
||||
.free_value = NULL \
|
||||
}
|
||||
|
||||
#define DEF_OPT_INT32(__name, __var_ofs, __default) \
|
||||
|
|
@ -67,7 +75,10 @@ struct config_list
|
|||
.opt_type = OPT_INT32, \
|
||||
.config_name = __name, \
|
||||
.var_offset = __var_ofs, \
|
||||
.def.d_int32 = __default \
|
||||
.str_size = 0, \
|
||||
.def.d_int32 = __default, \
|
||||
.ops.process_fn = NULL, \
|
||||
.free_value = NULL \
|
||||
}
|
||||
|
||||
#define DEF_OPT_UINT32(__name, __var_ofs, __default) \
|
||||
|
|
@ -75,7 +86,21 @@ struct config_list
|
|||
.opt_type = OPT_UINT32, \
|
||||
.config_name = __name, \
|
||||
.var_offset = __var_ofs, \
|
||||
.def.d_uint32 = __default \
|
||||
.str_size = 0, \
|
||||
.def.d_uint32 = __default, \
|
||||
.ops.process_fn = NULL, \
|
||||
.free_value = NULL \
|
||||
}
|
||||
|
||||
#define DEF_OPT_FLOAT(__name, __var_ofs, __default) \
|
||||
{ \
|
||||
.opt_type = OPT_FLOAT, \
|
||||
.config_name = __name, \
|
||||
.var_offset = __var_ofs, \
|
||||
.str_size = 0, \
|
||||
.def.d_float = __default, \
|
||||
.ops.process_fn = NULL, \
|
||||
.free_value = NULL \
|
||||
}
|
||||
|
||||
#define DEF_OPT_STR(__name, __var_ofs, __default) \
|
||||
|
|
@ -83,7 +108,10 @@ struct config_list
|
|||
.opt_type = OPT_STRING, \
|
||||
.config_name = __name, \
|
||||
.var_offset = __var_ofs, \
|
||||
.def.d_char = __default \
|
||||
.str_size = 0, \
|
||||
.def.d_char = __default, \
|
||||
.ops.process_fn = NULL, \
|
||||
.free_value = NULL \
|
||||
}
|
||||
|
||||
#define DEF_OPT_SSTR(__name, __var_ofs, __default, __str_size) \
|
||||
|
|
@ -92,7 +120,9 @@ struct config_list
|
|||
.config_name = __name, \
|
||||
.var_offset = __var_ofs, \
|
||||
.str_size = __str_size, \
|
||||
.def.d_char = __default \
|
||||
.def.d_char = __default, \
|
||||
.ops.process_fn = NULL, \
|
||||
.free_value = NULL \
|
||||
}
|
||||
|
||||
#define DEF_OPT_HEX(__name, __var_ofs, __array_size) \
|
||||
|
|
@ -100,7 +130,10 @@ struct config_list
|
|||
.opt_type = OPT_HEX_ARRAY, \
|
||||
.config_name = __name, \
|
||||
.var_offset = __var_ofs, \
|
||||
.def.array_size = __array_size \
|
||||
.str_size = 0, \
|
||||
.def.array_size = __array_size, \
|
||||
.ops.process_fn = NULL, \
|
||||
.free_value = NULL \
|
||||
}
|
||||
|
||||
#define DEF_OPT_FUNC(__name, __var_ofs, __process_fn, ...) \
|
||||
|
|
@ -108,6 +141,8 @@ struct config_list
|
|||
.opt_type = OPT_FUNC, \
|
||||
.config_name = __name, \
|
||||
.var_offset = __var_ofs, \
|
||||
.str_size = 0, \
|
||||
.def.d_int32 = 0, /* Inicjalizacja pola unii, aby uniknąć niezainicjalizowanych wartości */ \
|
||||
.ops.process_fn = __process_fn, \
|
||||
##__VA_ARGS__ \
|
||||
}
|
||||
|
|
@ -117,26 +152,43 @@ struct config_list
|
|||
.opt_type = OPT_FUNC_EXTRA, \
|
||||
.config_name = __name, \
|
||||
.var_offset = __var_ofs, \
|
||||
.ops.process_fn_extra = __process_fn_extra, \
|
||||
.str_size = 0, \
|
||||
.def.d_extra = __extra, \
|
||||
.ops.process_fn_extra = __process_fn_extra, \
|
||||
##__VA_ARGS__ \
|
||||
}
|
||||
|
||||
#define DEF_OPT_SAVE_FUNC(__fn) \
|
||||
{ \
|
||||
.opt_type = OPT_SAVE_FUNC, \
|
||||
.ops.should_save_fn = __fn \
|
||||
.config_name = NULL, \
|
||||
.var_offset = 0, \
|
||||
.str_size = 0, \
|
||||
.def.d_int32 = 0, \
|
||||
.ops.should_save_fn = __fn, \
|
||||
.free_value = NULL \
|
||||
}
|
||||
|
||||
#define DEF_OPT_FIXUP_FUNC(__fn) \
|
||||
{ \
|
||||
.opt_type = OPT_FIXUP_FUNC, \
|
||||
.ops.fixup_fn = __fn \
|
||||
.config_name = NULL, \
|
||||
.var_offset = 0, \
|
||||
.str_size = 0, \
|
||||
.def.d_int32 = 0, \
|
||||
.ops.fixup_fn = __fn, \
|
||||
.free_value = NULL \
|
||||
}
|
||||
|
||||
#define DEF_LAST_OPT \
|
||||
{ \
|
||||
.opt_type = OPT_UNKNOWN \
|
||||
.opt_type = OPT_UNKNOWN, \
|
||||
.config_name = NULL, \
|
||||
.var_offset = 0, \
|
||||
.str_size = 0, \
|
||||
.def.d_int32 = 0, \
|
||||
.ops.process_fn = NULL, \
|
||||
.free_value = NULL \
|
||||
}
|
||||
|
||||
struct config_sections
|
||||
|
|
|
|||
|
|
@ -478,6 +478,10 @@ static const struct config_list account_opts[] =
|
|||
DEF_OPT_INT8("cacheex_maxhop" , OFS(cacheex.maxhop), 0),
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
DEF_OPT_INT8("cacheex_maxhop_lg" , OFS(cacheex.maxhop_lg), 0),
|
||||
#endif
|
||||
DEF_OPT_FUNC("cacheex_maxhop_percaid" , OFS(cacheex.maxhop_percaid), caidvaluetab_fn),
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
DEF_OPT_FUNC("cacheex_maxhop_lg_percaid" , OFS(cacheex.maxhop_lg_percaid),caidvaluetab_fn),
|
||||
#endif
|
||||
DEF_OPT_FUNC("cacheex_ecm_filter" , OFS(cacheex.filter_caidtab), cacheex_hitvaluetab_fn),
|
||||
DEF_OPT_UINT8("cacheex_drop_csp" , OFS(cacheex.drop_csp), 0),
|
||||
|
|
|
|||
|
|
@ -199,6 +199,40 @@ void caidvaluetab_fn(const char *token, char *value, void *setting, FILE *f)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/resource.h> // for setpriority
|
||||
#endif
|
||||
|
||||
void cwvote_caidtab_fn(const char *token, char *value, void *setting, FILE *f)
|
||||
{
|
||||
CW_VOTE_CAID_TAB *cwvote_caid_table = setting;
|
||||
if(value)
|
||||
{
|
||||
if(cs_strlen(value)) {
|
||||
chk_cwvote_caidtab(value, cwvote_caid_table);
|
||||
} else {
|
||||
cwvote_caidtab_clear(cwvote_caid_table);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(cwvote_caid_table->cvcnum || cfg.http_full_cfg)
|
||||
{
|
||||
value = mk_t_cwvote_caidtab(cwvote_caid_table);
|
||||
fprintf_conf(f, token, "%s\n", value);
|
||||
free_mk_t(value);
|
||||
}
|
||||
}
|
||||
|
||||
static void cwvote_local_weight_fixups_fn(void *UNUSED(var))
|
||||
{
|
||||
if(cfg.cwvote_local_weight < 0.0f || cfg.cwvote_local_weight > 10.0f) {
|
||||
cfg.cwvote_local_weight = 1.0f;
|
||||
cs_log("CW Vote: local_weight out of range (0.0-10.0), using default value 1.0");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CS_CACHEEX
|
||||
void cacheex_valuetab_fn(const char *token, char *value, void *setting, FILE *f)
|
||||
{
|
||||
|
|
@ -264,12 +298,6 @@ void cacheex_hitvaluetab_fn(const char *token, char *value, void *setting, FILE
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/resource.h> // for setpriority
|
||||
#endif
|
||||
|
||||
void global_fixups_fn(void *UNUSED(var))
|
||||
{
|
||||
if(!cfg.usrfile) { cfg.disableuserfile = 1; }
|
||||
|
|
@ -317,6 +345,7 @@ void global_fixups_fn(void *UNUSED(var))
|
|||
static const struct config_list global_opts[] =
|
||||
{
|
||||
DEF_OPT_FIXUP_FUNC(global_fixups_fn),
|
||||
DEF_OPT_FIXUP_FUNC(cwvote_local_weight_fixups_fn),
|
||||
#ifdef LEDSUPPORT
|
||||
DEF_OPT_INT8("enableled" , OFS(enableled) , 0),
|
||||
#endif
|
||||
|
|
@ -398,6 +427,15 @@ static const struct config_list global_opts[] =
|
|||
DEF_OPT_INT8("double_check" , OFS(double_check) , 0),
|
||||
DEF_OPT_INT8("disablecrccws" , OFS(disablecrccws) , 0),
|
||||
DEF_OPT_FUNC("disablecrccws_only_for" , OFS(disablecrccws_only_for) , chk_ftab_fn),
|
||||
DEF_OPT_INT8("cwvote_enabled" , OFS(cwvote_enabled) , 0),
|
||||
DEF_OPT_INT8("cwvote_log_enabled" , OFS(cwvote_log_enabled) , 0),
|
||||
DEF_OPT_INT32("cwvote_timeout" , OFS(cwvote_timeout) , 0),
|
||||
DEF_OPT_INT32("cwvote_min_votes" , OFS(cwvote_min_votes) , 2),
|
||||
DEF_OPT_FLOAT("cwvote_local_weight" , OFS(cwvote_local_weight) , 2.0f),
|
||||
DEF_OPT_INT32("cwvote_max_candidates" , OFS(cwvote_max_candidates) , 8),
|
||||
DEF_OPT_INT32("cwvote_compare_len" , OFS(cwvote_compare_len) , 8),
|
||||
DEF_OPT_INT32("cwvote_fallback" , OFS(cwvote_fallback) , 0), // 0 = czekaj, 1 = weź najlepszy, 2 = weź pierwszy
|
||||
DEF_OPT_FUNC("cwvote_caids" , OFS(cwvote_caids) , cwvote_caidtab_fn),
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
DEF_OPT_INT8("cacheex_srcname_webif" , OFS(cacheex_srcname_webif) , 0),
|
||||
#endif
|
||||
|
|
@ -949,6 +987,10 @@ static const struct config_list streamrelay_opts[] =
|
|||
DEF_OPT_STR("stream_source_auth_password" , OFS(stream_source_auth_password), NULL),
|
||||
DEF_OPT_UINT32("stream_relay_buffer_time" , OFS(stream_relay_buffer_time), 0),
|
||||
DEF_OPT_UINT8("stream_relay_reconnect_count" , OFS(stream_relay_reconnect_count), 0),
|
||||
#ifdef WITH_EMU
|
||||
DEF_OPT_INT8("stream_emm_enabled" , OFS(emu_stream_emm_enabled), 0),
|
||||
DEF_OPT_UINT32("stream_ecm_delay" , OFS(emu_stream_ecm_delay), 600),
|
||||
#endif
|
||||
DEF_OPT_INT8("stream_display_client" , OFS(stream_display_client), 0),
|
||||
DEF_OPT_INT8("stream_reuse_client" , OFS(stream_reuse_client), 0),
|
||||
#ifdef WEBIF
|
||||
|
|
@ -1399,6 +1441,7 @@ void config_free(void)
|
|||
caidvaluetab_clear(&cfg.ftimeouttab);
|
||||
ftab_clear(&cfg.double_check_caid);
|
||||
ftab_clear(&cfg.disablecrccws_only_for);
|
||||
cwvote_caidtab_clear(&cfg.cwvote_caids);
|
||||
#ifdef WITH_LB
|
||||
caidvaluetab_clear(&cfg.lb_retrylimittab);
|
||||
caidvaluetab_clear(&cfg.lb_nbest_readers_tab);
|
||||
|
|
@ -1460,6 +1503,7 @@ int32_t init_config(void)
|
|||
#ifdef HAVE_DVBAPI
|
||||
cfg.dvbapi_enabled = 1;
|
||||
#endif
|
||||
cs_log("DEBUG: init_config - No oscam.conf found, using default settings.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1529,3 +1573,73 @@ int32_t write_config(void)
|
|||
config_sections_save(oscam_conf, f, &cfg);
|
||||
return flush_config_file(f, cs_conf);
|
||||
}
|
||||
|
||||
int32_t reload_global_config(void)
|
||||
{
|
||||
FILE *fp = open_config_file(cs_conf);
|
||||
if(!fp)
|
||||
{
|
||||
cs_log("WARNING: Cannot open oscam.conf for reloading global settings.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *token;
|
||||
if(!cs_malloc(&token, MAXLINESIZE))
|
||||
{
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const struct config_sections *global_section = config_find_section(oscam_conf, "global");
|
||||
if(!global_section || !config_section_is_active(global_section))
|
||||
{
|
||||
cs_log("ERROR: Global section not found or not active during reload.");
|
||||
NULLFREE(token);
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int in_global_section = 0;
|
||||
while(fgets(token, MAXLINESIZE, fp))
|
||||
{
|
||||
int len = cs_strlen(trim(token));
|
||||
if(len < 3) { continue; }
|
||||
if(token[0] == '#') { continue; }
|
||||
|
||||
if(token[0] == '[' && token[len - 1] == ']')
|
||||
{
|
||||
token[len - 1] = '\0';
|
||||
if(streq(token + 1, "global"))
|
||||
{
|
||||
in_global_section = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
in_global_section = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if(in_global_section)
|
||||
{
|
||||
char *value = strchr(token, '=');
|
||||
if(!value) { continue; }
|
||||
*value++ = '\0';
|
||||
char *tvalue = trim(value);
|
||||
char *ttoken = trim(strtolower(token));
|
||||
|
||||
if(!config_list_parse(global_section->config, ttoken, tvalue, &cfg))
|
||||
{
|
||||
cs_log("WARNING: During global config reload, unknown setting '%s=%s' in section [global].", ttoken, tvalue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NULLFREE(token);
|
||||
fclose(fp);
|
||||
|
||||
// Apply fixups after reloading global settings
|
||||
config_list_apply_fixups(global_section->config, &cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -114,6 +114,7 @@ static void protocol_fn(const char *token, char *value, void *setting, FILE *f)
|
|||
{ "newcamd525", R_NEWCAMD },
|
||||
{ "newcamd524", R_NEWCAMD },
|
||||
{ "drecas", R_DRECAS },
|
||||
{ "emu", R_EMU },
|
||||
{ NULL, 0 }
|
||||
}, *p;
|
||||
int i;
|
||||
|
|
@ -690,6 +691,9 @@ void ftab_fn(const char *token, char *value, void *setting, long ftab_type, FILE
|
|||
if(ftab_type & FTAB_FBPCAID) { rdr = container_of(setting, struct s_reader, fallback_percaid); }
|
||||
if(ftab_type & FTAB_LOCALCARDS) { rdr = container_of(setting, struct s_reader, localcards); }
|
||||
if(ftab_type & FTAB_IGNCHKSMCAID){ rdr = container_of(setting, struct s_reader, disablecrccws_only_for); }
|
||||
#ifdef WITH_EMU
|
||||
if(ftab_type & FTAB_EMUAU) { rdr = container_of(setting, struct s_reader, emu_auproviders); }
|
||||
#endif
|
||||
#ifdef MODULE_GBOX
|
||||
if(ftab_type & FTAB_CCCGBXRESHARE){ rdr = container_of(setting, struct s_reader, ccc_gbx_reshare_ident); }
|
||||
#endif
|
||||
|
|
@ -1196,6 +1200,10 @@ static const struct config_list reader_opts[] =
|
|||
DEF_OPT_INT8("cacheex_maxhop" , OFS(cacheex.maxhop), 0),
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
DEF_OPT_INT8("cacheex_maxhop_lg" , OFS(cacheex.maxhop_lg), 0),
|
||||
#endif
|
||||
DEF_OPT_FUNC("cacheex_maxhop_percaid" , OFS(cacheex.maxhop_percaid), caidvaluetab_fn),
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
DEF_OPT_FUNC("cacheex_maxhop_lg_percaid" , OFS(cacheex.maxhop_lg_percaid), caidvaluetab_fn),
|
||||
#endif
|
||||
DEF_OPT_FUNC("cacheex_ecm_filter" , OFS(cacheex.filter_caidtab), cacheex_hitvaluetab_fn),
|
||||
DEF_OPT_UINT8("cacheex_allow_request" , OFS(cacheex.allow_request), 0),
|
||||
|
|
@ -1350,6 +1358,7 @@ static const struct config_list reader_opts[] =
|
|||
DEF_OPT_SSTR("cccversion" , OFS(cc_version), "", SIZEOF(cc_version)),
|
||||
DEF_OPT_INT8("cccmaxhops" , OFS(cc_maxhops), DEFAULT_CC_MAXHOPS),
|
||||
DEF_OPT_INT8("cccmindown" , OFS(cc_mindown), 0),
|
||||
DEF_OPT_INT8("cccwantemu" , OFS(cc_want_emu), 0),
|
||||
DEF_OPT_INT8("ccckeepalive" , OFS(cc_keepalive), DEFAULT_CC_KEEPALIVE),
|
||||
DEF_OPT_INT8("cccreshare" , OFS(cc_reshare), DEFAULT_CC_RESHARE),
|
||||
DEF_OPT_INT32("cccreconnect" , OFS(cc_reconnect), DEFAULT_CC_RECONNECT),
|
||||
|
|
@ -1364,6 +1373,10 @@ static const struct config_list reader_opts[] =
|
|||
#endif
|
||||
#ifdef READER_DRECAS
|
||||
DEF_OPT_STR("stmkeys" , OFS(stmkeys), NULL),
|
||||
#endif
|
||||
#ifdef WITH_EMU
|
||||
DEF_OPT_FUNC_X("emu_auproviders" , OFS(emu_auproviders), ftab_fn, FTAB_READER | FTAB_EMUAU),
|
||||
DEF_OPT_INT8("emu_datecodedenabled" , OFS(emu_datecodedenabled), 0),
|
||||
#endif
|
||||
DEF_OPT_INT8("resetalways" , OFS(resetalways), 0),
|
||||
#ifdef WITH_CARDREADER
|
||||
|
|
@ -1466,7 +1479,7 @@ static bool reader_check_setting(const struct config_list *UNUSED(clist), void *
|
|||
// These are written only when the reader is CCCAM
|
||||
static const char *cccam_settings[] =
|
||||
{
|
||||
"cccversion", "cccmaxhops", "cccmindown", "ccckeepalive",
|
||||
"cccversion", "cccmaxhops", "cccmindown", "cccwantemu", "ccckeepalive",
|
||||
"cccreconnect",
|
||||
0
|
||||
};
|
||||
|
|
|
|||
|
|
@ -796,7 +796,7 @@ int32_t init_fakecws(void)
|
|||
int32_t nr = 0, i, j, idx;
|
||||
uint32_t alloccount[0x100], count[0x100], tmp, max_compares = 0, average_compares = 0;
|
||||
char *token, cw_string[64];
|
||||
uint8_t cw[16], wrong_checksum, c, have_fakecw = 0;
|
||||
uint8_t cw[16], wrong_checksum, __attribute__((unused)) c, have_fakecw = 0;
|
||||
FILE *fp;
|
||||
|
||||
memset(alloccount, 0, sizeof(count));
|
||||
|
|
@ -827,15 +827,6 @@ int32_t init_fakecws(void)
|
|||
{
|
||||
wrong_checksum = 0;
|
||||
|
||||
for(i = 0; i < 16; i += 4)
|
||||
{
|
||||
c = ((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff);
|
||||
if(cw[i + 3] != c)
|
||||
{
|
||||
wrong_checksum = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(wrong_checksum)
|
||||
{
|
||||
cs_log("skipping fake cw %s because of wrong checksum!", cw_string);
|
||||
|
|
@ -886,15 +877,6 @@ int32_t init_fakecws(void)
|
|||
{
|
||||
wrong_checksum = 0;
|
||||
|
||||
for(i = 0; i < 16; i += 4)
|
||||
{
|
||||
c = ((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff);
|
||||
if(cw[i + 3] != c)
|
||||
{
|
||||
wrong_checksum = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(!wrong_checksum)
|
||||
{
|
||||
idx = ((cw[0] & 0xF) << 4) | (cw[8] & 0xF);
|
||||
|
|
|
|||
1494
oscam-config.c.orig
Executable file
1494
oscam-config.c.orig
Executable file
File diff suppressed because it is too large
Load Diff
|
|
@ -7,6 +7,7 @@ int32_t init_config(void);
|
|||
void config_set(char *section, const char *token, char *value);
|
||||
void config_free(void);
|
||||
int32_t write_config(void);
|
||||
int32_t reload_global_config(void); // Dodano deklarację funkcji do ponownego wczytywania globalnej konfiguracji
|
||||
|
||||
void chk_account(const char *token, char *value, struct s_auth *account);
|
||||
void account_set_defaults(struct s_auth *auth);
|
||||
|
|
@ -64,6 +65,7 @@ enum ftab_fn
|
|||
FTAB_LOCALCARDS = 0x20,
|
||||
FTAB_IGNCHKSMCAID = 0x40,
|
||||
FTAB_IGNCRCCEX4USERONLYFOR = 0x80,
|
||||
FTAB_EMUAU = 0x100,
|
||||
FTAB_CCCGBXRESHARE = 0x200
|
||||
};
|
||||
|
||||
|
|
|
|||
1236
oscam-ecm.c
1236
oscam-ecm.c
File diff suppressed because it is too large
Load Diff
|
|
@ -2,54 +2,6 @@
|
|||
|
||||
#include "globals.h"
|
||||
#include <math.h>
|
||||
// ---------- ENTROPY FUNCTIONS START ----------
|
||||
|
||||
static double entropy_calc(const uint8_t *data, int len)
|
||||
{
|
||||
if(!data || len <= 0)
|
||||
return 0.0;
|
||||
|
||||
int freq[256] = {0};
|
||||
|
||||
for(int i = 0; i < len; i++)
|
||||
freq[data[i]]++;
|
||||
|
||||
double entropy = 0.0;
|
||||
|
||||
for(int i = 0; i < 256; i++)
|
||||
{
|
||||
if(freq[i] == 0)
|
||||
continue;
|
||||
|
||||
double p = (double)freq[i] / len;
|
||||
entropy -= p * (log(p) / log(2));
|
||||
}
|
||||
|
||||
return entropy;
|
||||
}
|
||||
|
||||
static void entropy_log_ecm(ECM_REQUEST *er, struct s_reader *reader)
|
||||
{
|
||||
if(!er)
|
||||
return;
|
||||
|
||||
double full = entropy_calc(er->cw, 16);
|
||||
double half1 = entropy_calc(er->cw, 8);
|
||||
double half2 = entropy_calc(er->cw + 8, 8);
|
||||
|
||||
cs_log_dbg(D_TRACE,
|
||||
"ENTROPY reader=%s caid=%04X FULL=%.4f HALF1=%.4f HALF2=%.4f",
|
||||
reader ? reader->label : "?",
|
||||
er->caid,
|
||||
full,
|
||||
half1,
|
||||
half2);
|
||||
}
|
||||
|
||||
// ---------- ENTROPY FUNCTIONS END ----------
|
||||
#define ENTROPY_THRESHOLD_DEFAULT 3.0
|
||||
#define ENTROPY_THRESHOLD_NDS 2.3
|
||||
|
||||
#include "cscrypt/md5.h"
|
||||
#include "module-anticasc.h"
|
||||
#include "module-cacheex.h"
|
||||
|
|
@ -76,91 +28,6 @@ static void entropy_log_ecm(ECM_REQUEST *er, struct s_reader *reader)
|
|||
#include "oscam-hashtable.h"
|
||||
#endif
|
||||
|
||||
// ADD THIS LINE:
|
||||
static int cw_checksum_ok(uint8_t *cw);
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// AUTODETECT STRUCT AND TABLE (ADD THIS HERE)
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
#define CW_DETECT_MAX 8192
|
||||
#define CW_LEARN_THRESHOLD 5
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t caid;
|
||||
uint32_t prid;
|
||||
uint16_t srvid;
|
||||
|
||||
uint8_t is64;
|
||||
uint8_t is48;
|
||||
|
||||
uint8_t learn64_count;
|
||||
uint8_t learn48_count;
|
||||
|
||||
uint8_t strict_logged;
|
||||
|
||||
} cw_detect_entry;
|
||||
|
||||
|
||||
static cw_detect_entry cw_detect_table[CW_DETECT_MAX];
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// GET OR CREATE ENTRY
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static cw_detect_entry *cw_detect_get(ECM_REQUEST *er)
|
||||
{
|
||||
int i;
|
||||
|
||||
// search existing entry
|
||||
for(i = 0; i < CW_DETECT_MAX; i++)
|
||||
{
|
||||
if(cw_detect_table[i].caid == er->caid &&
|
||||
cw_detect_table[i].prid == er->prid &&
|
||||
cw_detect_table[i].srvid == er->srvid)
|
||||
{
|
||||
return &cw_detect_table[i];
|
||||
}
|
||||
}
|
||||
|
||||
// create new entry
|
||||
for(i = 0; i < CW_DETECT_MAX; i++)
|
||||
{
|
||||
if(cw_detect_table[i].caid == 0)
|
||||
{
|
||||
cw_detect_table[i].caid = er->caid;
|
||||
cw_detect_table[i].prid = er->prid;
|
||||
cw_detect_table[i].srvid = er->srvid;
|
||||
|
||||
cw_detect_table[i].is64 = 0;
|
||||
cw_detect_table[i].is48 = 0;
|
||||
|
||||
cw_detect_table[i].learn64_count = 0;
|
||||
cw_detect_table[i].learn48_count = 0;
|
||||
|
||||
cw_detect_table[i].strict_logged = 0;
|
||||
|
||||
return &cw_detect_table[i];
|
||||
}
|
||||
}
|
||||
|
||||
return &cw_detect_table[0];
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CW CHECKSUM FUNCTION (ADD THIS BELOW cw_detect_get)
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static int cw_checksum_ok(uint8_t *cw)
|
||||
{
|
||||
if(((cw[0]+cw[1]+cw[2])&0xFF)!=cw[3]) return 0;
|
||||
if(((cw[4]+cw[5]+cw[6])&0xFF)!=cw[7]) return 0;
|
||||
if(((cw[8]+cw[9]+cw[10])&0xFF)!=cw[11]) return 0;
|
||||
if(((cw[12]+cw[13]+cw[14])&0xFF)!=cw[15]) return 0;
|
||||
return 1;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
extern CS_MUTEX_LOCK ecmcache_lock;
|
||||
extern struct ecm_request_t *ecmcwcache;
|
||||
extern uint32_t ecmcwcache_size;
|
||||
|
|
@ -921,127 +788,7 @@ uint8_t checkCWpart(uint8_t *cw, int8_t part)
|
|||
if(cw[i + eo]) { return 1; }
|
||||
return 0;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ===== CW AUTODETECT PATCH WITH LEARN THRESHOLD (48 + 64 LEARNING) =====
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define CW_LEARN_THRESHOLD 5
|
||||
|
||||
static int cw_autodetect_and_block(struct s_reader *reader, ECM_REQUEST *er)
|
||||
{
|
||||
if(!er || !er->cw)
|
||||
return 1;
|
||||
|
||||
cw_detect_entry *e = cw_detect_get(er);
|
||||
|
||||
const char *reader_name =
|
||||
(reader && reader->label) ? reader->label : "?";
|
||||
|
||||
int checksum_ok = cw_checksum_ok(er->cw);
|
||||
|
||||
int whitelist =
|
||||
chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for) ||
|
||||
(reader && chk_if_ignore_checksum(er, &reader->disablecrccws_only_for));
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// STRICT MODE (NOT WHITELIST) → ONLY 48bit ALLOWED
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
if(!whitelist)
|
||||
{
|
||||
if(!e->strict_logged)
|
||||
{
|
||||
cs_log("AUTODETECT STRICT 48bit MODE reader=%s (%04X@%06X/%04X)",
|
||||
reader_name, er->caid, er->prid, er->srvid);
|
||||
|
||||
e->strict_logged = 1;
|
||||
}
|
||||
|
||||
// block 64bit
|
||||
if(!checksum_ok)
|
||||
{
|
||||
cs_log("AUTODETECT BLOCK 64bit reader=%s (%04X@%06X/%04X)",
|
||||
reader_name, er->caid, er->prid, er->srvid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// WHITELIST MODE → LEARN 48bit AND 64bit SEPARATELY
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
// ---------- LEARN 64bit ----------
|
||||
if(!checksum_ok)
|
||||
{
|
||||
if(!e->is64)
|
||||
{
|
||||
if(e->learn64_count < CW_LEARN_THRESHOLD)
|
||||
{
|
||||
e->learn64_count++;
|
||||
|
||||
cs_log("AUTODETECT LEARN 64bit STEP %d/%d reader=%s (%04X@%06X/%04X)",
|
||||
e->learn64_count,
|
||||
CW_LEARN_THRESHOLD,
|
||||
reader_name,
|
||||
er->caid,
|
||||
er->prid,
|
||||
er->srvid);
|
||||
}
|
||||
|
||||
if(e->learn64_count >= CW_LEARN_THRESHOLD)
|
||||
{
|
||||
e->is64 = 1;
|
||||
|
||||
cs_log("AUTODETECT LOCKED 64bit reader=%s (%04X@%06X/%04X)",
|
||||
reader_name,
|
||||
er->caid,
|
||||
er->prid,
|
||||
er->srvid);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ---------- LEARN 48bit ----------
|
||||
if(checksum_ok)
|
||||
{
|
||||
if(!e->is48)
|
||||
{
|
||||
if(e->learn48_count < CW_LEARN_THRESHOLD)
|
||||
{
|
||||
e->learn48_count++;
|
||||
|
||||
cs_log("AUTODETECT LEARN 48bit STEP %d/%d reader=%s (%04X@%06X/%04X)",
|
||||
e->learn48_count,
|
||||
CW_LEARN_THRESHOLD,
|
||||
reader_name,
|
||||
er->caid,
|
||||
er->prid,
|
||||
er->srvid);
|
||||
}
|
||||
|
||||
if(e->learn48_count >= CW_LEARN_THRESHOLD)
|
||||
{
|
||||
e->is48 = 1;
|
||||
|
||||
cs_log("AUTODETECT LOCKED 48bit reader=%s (%04X@%06X/%04X)",
|
||||
reader_name,
|
||||
er->caid,
|
||||
er->prid,
|
||||
er->srvid);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void update_n_request(void)
|
||||
{
|
||||
struct s_client *cl;
|
||||
|
|
@ -2671,6 +2418,10 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Special checks for rc
|
||||
// Skip check for BISS1 - cw could be zero but still catch cw=0 by anticascading
|
||||
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
|
||||
|
||||
// bad/wrong chksum/ecm
|
||||
if(rc == E_NOTFOUND && rcEx == E2_WRONG_CHKSUM)
|
||||
{
|
||||
|
|
@ -2690,7 +2441,7 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui
|
|||
}
|
||||
}
|
||||
|
||||
if(rc < E_NOTFOUND && cw && chk_is_null_CW(cw))
|
||||
if(rc < E_NOTFOUND && cw && chk_is_null_CW(cw) && !caid_is_biss(er->caid))
|
||||
{
|
||||
rc = E_NOTFOUND;
|
||||
cs_log_dbg(D_TRACE | D_LB, "WARNING: reader %s send fake cw, set rc=E_NOTFOUND!", reader ? reader->label : "-");
|
||||
|
|
@ -2832,116 +2583,6 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui
|
|||
#endif
|
||||
//END -- SPECIAL CHECKs for rc
|
||||
|
||||
|
||||
//////////////// TRUST / ENTROPY / AUTODETECT BLOCK ////////////////////////////////////////////////////////////////
|
||||
if(rc == E_FOUND && cw && !chk_is_null_CW(cw))
|
||||
{
|
||||
/* SKIP CACHEEX readers */
|
||||
if(reader && reader->cacheex.mode)
|
||||
goto TRUST_DONE;
|
||||
|
||||
/* Używamy lokalnej kopii CW do testów */
|
||||
uint8_t testcw[16];
|
||||
memcpy(testcw, cw, 16);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// ENTROPY CALC
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
double full = entropy_calc(testcw, 16);
|
||||
double half1 = entropy_calc(testcw, 8);
|
||||
double half2 = entropy_calc(testcw + 8, 8);
|
||||
|
||||
double threshold;
|
||||
double entropy_value;
|
||||
|
||||
// NDS HALF entropy (always 64bit CAID)
|
||||
if(er->caid == 0x098D || er->caid == 0x098C)
|
||||
{
|
||||
entropy_value = (half1 > half2) ? half1 : half2;
|
||||
threshold = 1.35;
|
||||
}
|
||||
else
|
||||
{
|
||||
entropy_value = full;
|
||||
threshold = 2.5778;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// ENTROPY BLOCK (LEVEL 1)
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
if(entropy_value < threshold)
|
||||
{
|
||||
cs_log("TRUST BLOCK ENTROPY reader=%s (%04X@%06X/%04X) entropy=%.4f threshold=%.4f",
|
||||
reader ? reader->label : "?",
|
||||
er->caid,
|
||||
er->prid,
|
||||
er->srvid,
|
||||
entropy_value,
|
||||
threshold);
|
||||
|
||||
rc = E_NOTFOUND;
|
||||
rcEx = E2_WRONG_CHKSUM;
|
||||
|
||||
cw = NULL;
|
||||
|
||||
goto TRUST_DONE;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// AUTODETECT BLOCK (LEVEL 2)
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
/* ustaw testcw tymczasowo do er->cw dla autodetect */
|
||||
uint8_t original_cw[16];
|
||||
memcpy(original_cw, er->cw, 16);
|
||||
|
||||
memcpy(er->cw, testcw, 16);
|
||||
|
||||
if(!cw_autodetect_and_block(reader, er))
|
||||
{
|
||||
cs_log("TRUST BLOCK AUTODETECT reader=%s (%04X@%06X/%04X) entropy=%.4f threshold=%.4f",
|
||||
reader ? reader->label : "?",
|
||||
er->caid,
|
||||
er->prid,
|
||||
er->srvid,
|
||||
entropy_value,
|
||||
threshold);
|
||||
|
||||
rc = E_NOTFOUND;
|
||||
rcEx = E2_WRONG_CHKSUM;
|
||||
|
||||
memset(er->cw, 0, 16);
|
||||
cw = NULL;
|
||||
|
||||
goto TRUST_DONE;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// TRUST OK → FINAL COPY
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
memcpy(er->cw, testcw, 16);
|
||||
|
||||
cs_log("TRUST OK reader=%s (%04X@%06X/%04X) entropy=%.4f threshold=%.4f",
|
||||
reader ? reader->label : "?",
|
||||
er->caid,
|
||||
er->prid,
|
||||
er->srvid,
|
||||
entropy_value,
|
||||
threshold);
|
||||
|
||||
TRUST_DONE:
|
||||
;
|
||||
}
|
||||
|
||||
// global protection
|
||||
if(rc != E_FOUND)
|
||||
{
|
||||
cw = NULL;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
if(!dontsetAnswered)
|
||||
{
|
||||
|
|
@ -2987,7 +2628,9 @@ if(rc != E_FOUND)
|
|||
if(!ea->is_pending) // not for pending ea - only once for ea
|
||||
{
|
||||
// cache update
|
||||
if(ea && (ea->rc < E_NOTFOUND) && !chk_is_null_CW(ea->cw))
|
||||
// Skip check for BISS1 - cw could be indeed zero
|
||||
// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
|
||||
if(ea && (ea->rc < E_NOTFOUND) && (!chk_is_null_CW(ea->cw) && !caid_is_biss(er->caid)))
|
||||
{
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
int32_t ecmtime = ea->ecm_time;
|
||||
|
|
@ -3207,19 +2850,8 @@ 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;
|
||||
er->selected_reader = ecm->selected_reader;
|
||||
er->cw_count = ecm->cw_count;
|
||||
|
||||
#ifdef CS_CACHEEX
|
||||
// here we should be sure cex client has not been freed!
|
||||
|
|
@ -3347,20 +2979,7 @@ static bool ecm_cache_check(ECM_REQUEST *er)
|
|||
|
||||
void get_cw(struct s_client *client, ECM_REQUEST *er)
|
||||
{
|
||||
// clienttimeout per CAID override
|
||||
uint32_t client_timeout = cfg.ctimeout;
|
||||
int32_t percaid_timeout = caidvaluetab_get_value(&cfg.ctimeouttab, er->caid, 0);
|
||||
|
||||
if(percaid_timeout > 0)
|
||||
{
|
||||
client_timeout = percaid_timeout;
|
||||
}
|
||||
|
||||
er->client_timeout = client_timeout;
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
|
||||
|
||||
cacheex_update_hash(er);
|
||||
if(!ecm_cache_check(er))
|
||||
{
|
||||
|
|
@ -3654,7 +3273,9 @@ void get_cw(struct s_client *client, ECM_REQUEST *er)
|
|||
}
|
||||
|
||||
// Check for odd/even byte
|
||||
if(get_odd_even(er) == 0)
|
||||
// Don't check for BISS1 and BISS2 mode 1/E or fake caid (ECM is fake for them)
|
||||
// Don't check for BISS2 mode CA (ECM table is always 0x80)
|
||||
if(!caid_is_biss(er->caid) && !caid_is_fake(er->caid) && get_odd_even(er) == 0)
|
||||
{
|
||||
cs_log_dbg(D_TRACE, "warning: ecm with null odd/even byte from %s", (check_client(er->client) ? er->client->account->usr : "-"));
|
||||
er->rc = E_INVALID;
|
||||
|
|
@ -33,6 +33,12 @@ void request_cw_from_readers(ECM_REQUEST *er, uint8_t stop_stage);
|
|||
void checkCW(ECM_REQUEST *er);
|
||||
uint8_t checkCWpart(uint8_t *cw, int8_t part);
|
||||
|
||||
// CW VOTING
|
||||
int cw_vote_add(struct ecm_request_t *er, uint8_t *cw, struct s_reader *rdr);
|
||||
int cw_vote_decide(struct ecm_request_t *er);
|
||||
int32_t is_cwvote_caid(ECM_REQUEST *er);
|
||||
void refresh_cw_vote_config(void);
|
||||
|
||||
#ifdef CS_CACHEEX_AIO
|
||||
void init_ecm_cache(void);
|
||||
void free_ecm_cache(void);
|
||||
|
|
|
|||
19
oscam-emm.c
19
oscam-emm.c
|
|
@ -40,6 +40,7 @@ static int8_t cs_emmlen_is_blocked(struct s_reader *rdr, int16_t len)
|
|||
static int8_t do_simple_emm_filter(struct s_reader *rdr, const struct s_cardsystem *csystem, EMM_PACKET *ep, int8_t cl_dvbapi)
|
||||
{
|
||||
if(is_network_reader(rdr)) { return 1; } // don't evaluate on network readers, server with local reader will check it
|
||||
if(rdr->typ == R_EMU) { return 1; } // don't evalutate on emu reader
|
||||
|
||||
//copied and enhanced from module-dvbapi.c
|
||||
//dvbapi_start_emm_filter()
|
||||
|
|
@ -242,6 +243,24 @@ int32_t emm_reader_match(struct s_reader *reader, uint16_t caid, uint32_t provid
|
|||
rdr_log_dbg(reader, D_EMM, "reader auprovid = %06X fixup to %06X (ignoring last digit)", reader->auprovid, prid);
|
||||
}
|
||||
|
||||
#ifdef WITH_EMU
|
||||
if(reader->typ == R_EMU)
|
||||
{
|
||||
FILTER *emu_provids = get_emu_prids_for_caid(reader, caid);
|
||||
if(emu_provids != NULL)
|
||||
{
|
||||
for(i = 0; i < emu_provids->nprids; i++)
|
||||
{
|
||||
if(provid == emu_provids->prids[i])
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(prid == provid)
|
||||
{
|
||||
rdr_log_dbg(reader, D_EMM, "reader auprovid = %06X matching with emm provid = %06X -> SEND!", prid, provid);
|
||||
|
|
|
|||
|
|
@ -143,6 +143,10 @@ int32_t file_copy(char *srcfile, char *destfile)
|
|||
}
|
||||
}
|
||||
fclose(src);
|
||||
if(fsync(fileno(dest)) == -1) // Dodano fsync, aby wymusić zapis na dysk
|
||||
{
|
||||
cs_log("ERROR: file_copy - fsync failed for %s (errno=%d %s)!", destfile, errno, strerror(errno));
|
||||
}
|
||||
fclose(dest);
|
||||
return (0);
|
||||
}
|
||||
|
|
@ -153,16 +157,17 @@ int32_t file_copy(char *srcfile, char *destfile)
|
|||
int32_t safe_overwrite_with_bak(char *destfile, char *temp_file, char *bakfile, int32_t forceBakOverWrite)
|
||||
{
|
||||
int32_t rc;
|
||||
|
||||
if(file_exists(destfile))
|
||||
{
|
||||
if(forceBakOverWrite != 0 || !file_exists(bakfile))
|
||||
{
|
||||
if(file_copy(destfile, bakfile) < 0)
|
||||
{
|
||||
cs_log("Error copying original config file %s to %s. The original config will be left untouched!", destfile, bakfile);
|
||||
cs_log("ERROR: safe_overwrite_with_bak - Error copying original config file %s to %s. The original config will be left untouched!", destfile, bakfile);
|
||||
if(unlink(temp_file) < 0)
|
||||
{
|
||||
cs_log("Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
|
||||
cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -172,21 +177,21 @@ int32_t safe_overwrite_with_bak(char *destfile, char *temp_file, char *bakfile,
|
|||
rc = file_copy(temp_file, destfile);
|
||||
if(rc < 0)
|
||||
{
|
||||
cs_log("An error occured while writing the new config file %s.", destfile);
|
||||
cs_log("ERROR: safe_overwrite_with_bak - An error occured while writing the new config file %s. Return code: %d, errno: %d (%s).", destfile, rc, errno, strerror(errno));
|
||||
if(rc == -2)
|
||||
{
|
||||
cs_log("The config will be missing or only partly filled upon next startup as this is a non-recoverable error! Please restore from backup or try again.");
|
||||
cs_log("ERROR: safe_overwrite_with_bak - The config will be missing or only partly filled upon next startup as this is a non-recoverable error! Please restore from backup or try again.");
|
||||
}
|
||||
if(unlink(temp_file) < 0)
|
||||
{
|
||||
cs_log("Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
|
||||
cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(unlink(temp_file) < 0)
|
||||
{
|
||||
cs_log("Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
|
||||
cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -468,6 +468,8 @@ const char *get_cardsystem_desc_by_caid(uint16_t caid)
|
|||
if(caid_is_viaccess(caid)) { return "viaccess"; }
|
||||
if(caid_is_irdeto(caid)) { return "irdeto"; }
|
||||
if(caid_is_videoguard(caid)) { return "videoguard"; }
|
||||
if(caid_is_powervu(caid)) { return "powervu"; }
|
||||
if(caid_is_director(caid)) { return "director"; }
|
||||
if(caid_is_conax(caid)) { return "conax"; }
|
||||
if(caid_is_cryptoworks(caid)) { return "cryptoworks"; }
|
||||
if(caid_is_betacrypt(caid)) { return "betacrypt"; }
|
||||
|
|
@ -476,6 +478,7 @@ const char *get_cardsystem_desc_by_caid(uint16_t caid)
|
|||
if(caid >= 0x5501 && caid <= 0x551A) { return "griffin"; }
|
||||
if(caid_is_dre(caid)) { return "drecrypt"; }
|
||||
if(caid_is_bulcrypt(caid)) { return "bulcrypt"; }
|
||||
if(caid_is_biss(caid)) { return "biss"; }
|
||||
if(caid == 0x4ABF) { return "dgcrypt"; }
|
||||
return "unknown";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
20
oscam.c
|
|
@ -44,6 +44,10 @@
|
|||
#include "reader-common.h"
|
||||
#include "module-gbox.h"
|
||||
|
||||
#ifdef WITH_EMU
|
||||
void add_emu_reader(void);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_SSL
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
|
@ -372,9 +376,9 @@ static void write_versionfile(bool use_stdout)
|
|||
st.tm_hour, st.tm_min, st.tm_sec);
|
||||
}
|
||||
|
||||
fprintf(fp, "Build Date: %s\n", CS_BUILD_DATE);
|
||||
fprintf(fp, "Version: %s@%s\n", CS_VERSION, CS_GIT_COMMIT);
|
||||
fprintf(fp, "Compiler: %s\n", CS_TARGET);
|
||||
fprintf(fp, "Build Date: %s\n", "UNKNOWN"); // Placeholder if not defined by build system
|
||||
fprintf(fp, "Version: %s@%s\n", "UNKNOWN", "UNKNOWN"); // Placeholder if not defined by build system
|
||||
fprintf(fp, "Compiler: %s\n", "UNKNOWN"); // Placeholder if not defined by build system
|
||||
#ifdef USE_COMPRESS
|
||||
fprintf(fp, "Compression: %s, level %s\n", COMP_VERSION, COMP_LEVEL);
|
||||
#endif
|
||||
|
|
@ -457,6 +461,8 @@ static void write_versionfile(bool use_stdout)
|
|||
#ifdef WITH_SIGNING
|
||||
write_conf(WITH_SIGNING, "Binary signing support");
|
||||
#endif
|
||||
write_conf(WITH_EMU, "Emulator support");
|
||||
write_conf(WITH_SOFTCAM, "Built-in SoftCam.Key");
|
||||
|
||||
fprintf(fp, "\n");
|
||||
write_conf(MODULE_CAMD33, "camd 3.3x");
|
||||
|
|
@ -1682,6 +1688,9 @@ const struct s_cardreader *cardreaders[] =
|
|||
#ifdef CARDREADER_STINGER
|
||||
&cardreader_stinger,
|
||||
#endif
|
||||
#ifdef WITH_EMU
|
||||
&cardreader_emu,
|
||||
#endif
|
||||
|
||||
NULL
|
||||
};
|
||||
|
|
@ -1800,6 +1809,8 @@ int32_t main(int32_t argc, char *argv[])
|
|||
|
||||
parse_cmdline_params(argc, argv);
|
||||
|
||||
cs_log("DEBUG: main - cs_confdir after parsing command line and finding config dir: '%s'", cs_confdir);
|
||||
|
||||
if(bg && do_daemon(1, 0))
|
||||
{
|
||||
printf("Error starting in background (errno=%d: %s)", errno, strerror(errno));
|
||||
|
|
@ -1861,6 +1872,9 @@ int32_t main(int32_t argc, char *argv[])
|
|||
|
||||
init_sidtab();
|
||||
init_readerdb();
|
||||
#ifdef WITH_EMU
|
||||
add_emu_reader();
|
||||
#endif
|
||||
cfg.account = init_userdb();
|
||||
init_signal();
|
||||
init_provid();
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "reader-common.h"
|
||||
//#include "csctapi/atr.h"
|
||||
#include "csctapi/icc_async.h"
|
||||
#include "readers.h" // required by the EMU reader
|
||||
|
||||
extern const struct s_cardsystem *cardsystems[];
|
||||
extern char *RDR_CD_TXT[];
|
||||
|
|
@ -152,6 +153,18 @@ static int32_t reader_get_cardsystem(struct s_reader *reader, ATR *atr)
|
|||
{
|
||||
int32_t i;
|
||||
|
||||
#ifdef WITH_EMU
|
||||
if(reader->typ == R_EMU)
|
||||
{
|
||||
NULLFREE(reader->csystem_data);
|
||||
rdr_log(reader, "found card system %s", reader_emu.desc);
|
||||
reader->csystem = &reader_emu;
|
||||
reader->csystem_active = true;
|
||||
led_status_found_cardsystem();
|
||||
return (reader->csystem_active);
|
||||
}
|
||||
#endif
|
||||
|
||||
for(i = 0; cardsystems[i]; i++)
|
||||
{
|
||||
NULLFREE(reader->csystem_data);
|
||||
|
|
|
|||
|
|
@ -17,5 +17,6 @@ extern const struct s_cardsystem reader_tongfang;
|
|||
extern const struct s_cardsystem reader_bulcrypt;
|
||||
extern const struct s_cardsystem reader_griffin;
|
||||
extern const struct s_cardsystem reader_dgcrypt;
|
||||
extern const struct s_cardsystem reader_emu;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
<TR><TH COLSPAN="2">CacheEx / CSP</TH></TR>
|
||||
<TR><TD><A>CW Cache Settings:</A></TD><TD><input name="cw_cache_settings" type="text" maxlength="1400" value="##CWCACHESETTINGS##"><br />Format: caid[&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[&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>
|
||||
|
|
@ -142,6 +142,29 @@
|
|||
<TR><TH COLSPAN="2">Doublecheck</TH></TR>
|
||||
<TR><TD><A>ECM Doublecheck:</A></TD><TD><input name="double_check" value="0" type="hidden"><input name="double_check" value="1" type="checkbox" ##DCHECKCSELECTED##><label></label></TD></TR>
|
||||
<TR><TD><A>Doublecheck caids:</A></TD><TD><input name="double_check_caid" type="text" maxlength="160" value="##DOUBLECHECKCAID##"></TD></TR>
|
||||
<TR><TH COLSPAN="2">Ai Fake DCW Detector</TH></TR>
|
||||
<TR><TD><A>Ai Vote Enabled:</A></TD><TD><input name="cwvote_enabled" value="0" type="hidden"><input name="cwvote_enabled" value="1" type="checkbox" ##CWVOTEENABLEDCHECKED##></TD></TR>
|
||||
<TR><TD><A>Ai Vote Log Enabled:</A></TD><TD><input name="cwvote_log_enabled" value="0" type="hidden"><input name="cwvote_log_enabled" value="1" type="checkbox" ##CWVOTELOGENABLEDCHECKED##></TD></TR>
|
||||
<TR><TD><A>Ai Vote Timeout (ms):</A></TD><TD><input name="cwvote_timeout" type="text" size="8" maxlength="8" value="##CWVOTETIMEOUT##"> <small>(default 1000)</small></TD></TR>
|
||||
<TR><TD><A>Ai Vote Min Votes:</A></TD><TD><input name="cwvote_min_votes" type="text" size="4" maxlength="3" value="##CWVOTEMINVOTES##"> <small>(default min. 2)</small></TD></TR>
|
||||
<TR><TD><A>Ai Vote Local Weight:</A></TD><TD><input name="cwvote_local_weight" type="text" size="5" maxlength="5" value="##CWVOTELOCALWEIGHT##"> <small>(0.0-10.0, default 2.0)</small></TD></TR>
|
||||
<TR><TD><A>Ai Vote Max Candidates:</A></TD><TD><input name="cwvote_max_candidates" type="text" size="4" maxlength="3" value="##CWVOTEMAXCANDIDATES##"> <small>(default 8 caution! use max 16)</small></TD></TR>
|
||||
<TR><TD><A>Ai Vote Compare Length:</A></TD><TD>
|
||||
<select name="cwvote_compare_len">
|
||||
<option value="8" ##CWVOTECOMPARE8##>8 bytes (recomended)</option>
|
||||
<option value="16" ##CWVOTECOMPARE16##>16 bytes (pełne)</option>
|
||||
</select>
|
||||
</TD></TR>
|
||||
|
||||
<TR><TD><A>Ai Vote Fallback Mode:</A></TD><TD>
|
||||
<select name="cwvote_fallback">
|
||||
<option value="0" ##CWVOTEFALLBACK0##>0 - Keep Waiting</option>
|
||||
<option value="1" ##CWVOTEFALLBACK1##>1 - Take best CW (recomended)</option>
|
||||
<option value="2" ##CWVOTEFALLBACK2##>2 - Take first CW</option>
|
||||
</select>
|
||||
</TD></TR>
|
||||
|
||||
<TR><TD><A>Ai Vote CAIDs:</A></TD><TD><input name="cwvote_caids" type="text" maxlength="1024" value="##CWVOTECAIDS##"></TD></TR>
|
||||
##TPLSUPPRESSCMD08##
|
||||
##TPLGETBLOCKEMMAUPROVID##
|
||||
##TPLENABLELEDBIT##
|
||||
|
|
|
|||
|
|
@ -1,145 +0,0 @@
|
|||
<input name="part" type="hidden" value="global">
|
||||
<input name="suppresscmd08" type="hidden" value="0">
|
||||
<TABLE CLASS="config">
|
||||
<TR><TH COLSPAN="2">Edit Global Config</TH></TR>
|
||||
<TR><TD><A>Serverip:</A></TD><TD><input name="serverip" class="medium" type="text" maxlength="15" value="##SERVERIP##"></TD></TR>
|
||||
<TR><TD><A>Nice:</A></TD><TD><input name="nice" class="short" type="text" maxlength="3" value="##NICE##"></TD></TR>
|
||||
<TR><TD><A>Net prio:</A></TD>
|
||||
<TD>
|
||||
<select name="netprio">
|
||||
<option value="0" ##NETPRIO0##>0 - IPP=0; DSCP=CS0 (default)</option>
|
||||
<option value="1" ##NETPRIO1##>1 - IPP=1; DSCP=CS1</option>
|
||||
<option value="2" ##NETPRIO2##>2 - IPP=1; DSCP=AF11</option>
|
||||
<option value="3" ##NETPRIO3##>3 - IPP=1; DSCP=AF12</option>
|
||||
<option value="4" ##NETPRIO4##>4 - IPP=1; DSCP=AF13</option>
|
||||
<option value="5" ##NETPRIO5##>5 - IPP=2; DSCP=CS2</option>
|
||||
<option value="6" ##NETPRIO6##>6 - IPP=2; DSCP=AF21</option>
|
||||
<option value="7" ##NETPRIO7##>7 - IPP=2; DSCP=AF22</option>
|
||||
<option value="8" ##NETPRIO8##>8 - IPP=2; DSCP=AF23</option>
|
||||
<option value="9" ##NETPRIO9##>9 - IPP=3; DSCP=CS3</option>
|
||||
<option value="10" ##NETPRIO10##>10 - IPP=3; DSCP=AF31</option>
|
||||
<option value="11" ##NETPRIO11##>11 - IPP=3; DSCP=AF32</option>
|
||||
<option value="12" ##NETPRIO12##>12 - IPP=3; DSCP=AF33</option>
|
||||
<option value="13" ##NETPRIO13##>13 - IPP=4; DSCP=CS4</option>
|
||||
<option value="14" ##NETPRIO14##>14 - IPP=4; DSCP=AF41</option>
|
||||
<option value="15" ##NETPRIO15##>15 - IPP=4; DSCP=AF42</option>
|
||||
<option value="16" ##NETPRIO16##>16 - IPP=4; DSCP=AF43</option>
|
||||
<option value="17" ##NETPRIO17##>17 - IPP=5; DSCP=CS5</option>
|
||||
<option value="18" ##NETPRIO18##>18 - IPP=5; DSCP=EF</option>
|
||||
<option value="19" ##NETPRIO19##>19 - IPP=6; DSCP=CS6</option>
|
||||
<option value="20" ##NETPRIO20##>20 - IPP=7; DSCP=CS7</option>
|
||||
</select>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR><TD><A>Bind wait:</A></TD><TD><input name="bindwait" class="withunit short" type="text" maxlength="5" value="##BINDWAIT##"> s</TD></TR>
|
||||
<TR><TD><A>Resolver:</A></TD>
|
||||
<TD>
|
||||
<select name="resolvegethostbyname">
|
||||
<option value="0" ##RESOLVER0##>0 - getadressinfo()</option>
|
||||
<option value="1" ##RESOLVER1##>1 - gethostbyname()</option>
|
||||
</select>
|
||||
</TD>
|
||||
</TR>
|
||||
##TPLLOCALCARDS##
|
||||
<TR><TD><A>Prefer local cards:</A></TD><TD><select name="preferlocalcards"><option value="0">0 - local cards like proxied</option><option value="1" ##PREFERCACHEEX##>1 - prefer cache-ex then local cards</option><option value="2" ##PREFERLOCALCARDS##>2 - prefer local cards above cache-ex</option></select></TD></TR>
|
||||
##TPLUNLOCKPARENTAL##
|
||||
<TR><TD>SIGHUP reload:</TD>
|
||||
<TD CLASS="invisible">
|
||||
<TABLE>
|
||||
<TR>
|
||||
<TD><A>accounts:</A></TD><TD><input name="reload_useraccounts" type="hidden" value="0"><input name="reload_useraccounts" type="checkbox" value="1" ##RELOADUSERACCOUNTSCHECKED##></TD>
|
||||
<TD><A>readers:</A></TD><TD><input name="reload_readers" type="hidden" value="0"><input name="reload_readers" type="checkbox" value="1" ##RELOADREADERSCHECKED##></TD>
|
||||
<TD><A>provid:</A></TD><TD><input name="reload_provid" type="hidden" value="0"><input name="reload_provid" type="checkbox" value="1" ##RELOADPROVIDCHECKED##></TD>
|
||||
<TD><A>services ids:</A></TD><TD><input name="reload_services_ids" type="hidden" value="0"><input name="reload_services_ids" type="checkbox" value="1" ##RELOADSERVICESIDSCHECKED##></TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD><A>tier ids:</A></TD><TD><input name="reload_tier_ids" type="hidden" value="0"><input name="reload_tier_ids" type="checkbox" value="1" ##RELOADTIERUDSCHECKED##></TD>
|
||||
<TD><A>fake CWs:</A></TD><TD><input name="reload_fakecws" type="hidden" value="0"><input name="reload_fakecws" type="checkbox" value="1" ##RELOADFAKECWSCHECKED##></TD>
|
||||
<TD><A>AC stats:</A></TD><TD><input name="reload_ac_stat" type="hidden" value="0"><input name="reload_ac_stat" type="checkbox" value="1" ##RELOADACSTATCHECKED##></TD>
|
||||
<TD><A>log file:</A></TD><TD><input name="reload_log" type="hidden" value="0"><input name="reload_log" type="checkbox" value="1" ##RELOADLOGCHECKED##></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR><TD>Simple block:</TD>
|
||||
<TD CLASS="invisible">
|
||||
<TABLE>
|
||||
<TR>
|
||||
<TD><A>same IP:</A></TD><TD><input name="block_same_ip" type="hidden" value="0"><input name="block_same_ip" type="checkbox" value="1" ##BLOCKSAMEIPCHECKED##></TD>
|
||||
<TD><A>same Name:</A></TD><TD><input name="block_same_name" type="hidden" value="0"><input name="block_same_name" type="checkbox" value="1" ##BLOCKSAMENAMECHECKED##></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR><TD><A>Drop duplicate users:</A></TD>
|
||||
<TD>
|
||||
<input name="dropdups" value="0" type="hidden"><input name="dropdups" value="1" type="checkbox" ##DROPDUPSCHECKED##>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR><TD><A>Skip CWs checksum test:</A></TD>
|
||||
<TD>
|
||||
<input name="disablecrccws" type="hidden" value="0"><input name="disablecrccws" type="checkbox" value="1" ##DISABLECRCCWSCHECKEDGLOBAL##>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR><TD><A>Skip CWs checksum test only for:</A></TD><TD><textarea name="disablecrccws_only_for" rows="3" class="bt">##IGNCHKSUMONLYFORGLOBAL##</textarea>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR><TH COLSPAN="2">Logging</TH></TR>
|
||||
<TR><TD><A>Usr file:</A></TD>
|
||||
<TD CLASS="invisible">
|
||||
<TABLE>
|
||||
<TR>
|
||||
<TD><input name="usrfile" type="text" maxlength="128" value="##USERFILE##"></TD>
|
||||
<TD><A>enabled:</A></TD><TD><input name="disableuserfile" type="hidden" value="1"><input name="disableuserfile" type="checkbox" value="0" ##DISABLEUSERFILECHECKED##></TD>
|
||||
<TD><A>flag:</A></TD><TD><select name="usrfileflag"><option VALUE="0">0 - just join/leave</option><option VALUE="1" ##USERFILEFLAGCHECKED##>1 - each zap</option></select></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR><TD><A>Mail file:</A></TD>
|
||||
<TD CLASS="invisible">
|
||||
<TABLE>
|
||||
<TR>
|
||||
<TD><input name="mailfile" type="text" maxlength="128" value="##MAILFILE##"></TD>
|
||||
<TD><A>enabled:</A></TD><TD><input name="disablemail" type="hidden" value="1"><input name="disablemail" type="checkbox" value="0" ##DISABLEMAILCHECKED##></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR><TD><A>Log file / max size:</A></TD>
|
||||
<TD CLASS="invisible">
|
||||
<TABLE>
|
||||
<TR>
|
||||
<TD><input name="logfile" type="text" maxlength="128" value="##LOGFILE##"></TD>
|
||||
<TD><A>enabled:</A></TD><TD><input name="disablelog" type="hidden" value="1"><input name="disablelog" type="checkbox" value="0" ##DISABLELOGCHECKED##></TD>
|
||||
<TD><A>max size:</A></TD><TD><input name="maxlogsize" class="withunit short" type="text" maxlength="5" value="##MAXLOGSIZE##"> kB</TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR><TD><A>Log duplicated lines:</A></TD><TD><input name="logduplicatelines" value="0" type="hidden"><input name="logduplicatelines" value="1" type="checkbox" ##LOGDUPSCHECKED##><label></label></TD></TR>
|
||||
<TR><TD><A>Initial debug level:</A></TD><TD><input name="initial_debuglevel" type="text" maxlength="10" value="##INITIALDEBUGLEVEL##"></TD></TR>
|
||||
<TR><TD><A>Pid file:</A></TD><TD><input name="pidfile" type="text" maxlength="128" value="##PIDFILE##"></TD></TR>
|
||||
<TR><TD><A>CW log dir:</A></TD><TD><input name="cwlogdir" type="text" maxlength="128" value="##CWLOGDIR##"></TD></TR>
|
||||
<TR><TD><A>EMM log dir:</A></TD><TD><input name="emmlogdir" type="text" maxlength="128" value="##EMMLOGDIR##"></TD></TR>
|
||||
<TR><TD><A>ECM log format:</A></TD><TD><input name="ecmfmt" type="text" maxlength="128" value="##ECMFMT##"></TD></TR>
|
||||
<TR><TD><A>Loghistory Lines:</A></TD><TD><input name="loghistorylines" class="short" type="text" maxlength="4" value="##LOGHISTORYLINES##"></TD></TR>
|
||||
<TR><TD><A>Syslog server:</A></TD><TD><input name="sysloghost" type="text" maxlength="128" value="##SYSLOGHOST##"></TD></TR>
|
||||
<TR><TD><A>Syslog port:</A></TD><TD><input name="syslogport" class="short" type="text" maxlength="5" value="##SYSLOGPORT##"></TD></TR>
|
||||
##TPLCACHEEXAIOLOGGING##
|
||||
<TR><TH COLSPAN="2">Failban</TH></TR>
|
||||
<TR><TD><A>Failban time:</A></TD><TD><input name="failbantime" class="withunit short" type="text" maxlength="6" value="##FAILBANTIME##"> min blocking IP based</TD></TR>
|
||||
<TR><TD><A>Failban count:</A></TD><TD><input name="failbancount" class="withunit short" type="text" maxlength="2" value="##FAILBANCOUNT##"> chances with wrong credentials</TD></TR>
|
||||
<TR><TH COLSPAN="2">Timeouts / Times</TH></TR>
|
||||
<TR><TD><A>Client timeout:</A></TD><TD><input name="clienttimeout" class="withunit short" type="text" maxlength="5" value="##CLIENTTIMEOUT##"> ms to give up and return timeout</TD></TR>
|
||||
<TR><TD><A>Fallback timeout:</A></TD><TD><input name="fallbacktimeout" class="withunit short" type="text" maxlength="5" value="##FALLBACKTIMEOUT##"> ms to switch to fallback reader</TD></TR>
|
||||
<TR><TD><A>Fallback timeout per caid:</A></TD><TD><input name="fallbacktimeout_percaid" type="text" maxlength="320" value="##FALLBACKTIMEOUT_PERCAID##"></TD></TR>
|
||||
<TR><TD><A>Client max idle:</A></TD><TD><input name="clientmaxidle" class="withunit short" type="text" maxlength="5" value="##CLIENTMAXIDLE##"> s to disconnect idle clients</TD></TR>
|
||||
<TR><TD><A>Global sleep:</A></TD><TD><input name="sleep" class="withunit short" type="text" maxlength="5" value="##SLEEP##"> min to switch a client in sleepmode</TD></TR>
|
||||
<TR><TD><A>Reader restart seconds:</A></TD><TD><input name="readerrestartseconds" class="withunit short" type="text" maxlength="5" value="##READERRESTARTSECONDS##"> s waittime to restart a reader</TD></TR>
|
||||
<TR><TH COLSPAN="2">Doublecheck</TH></TR>
|
||||
<TR><TD><A>ECM Doublecheck:</A></TD><TD><input name="double_check" value="0" type="hidden"><input name="double_check" value="1" type="checkbox" ##DCHECKCSELECTED##><label></label></TD></TR>
|
||||
<TR><TD><A>Doublecheck caids:</A></TD><TD><input name="double_check_caid" type="text" maxlength="160" value="##DOUBLECHECKCAID##"></TD></TR>
|
||||
##TPLSUPPRESSCMD08##
|
||||
##TPLGETBLOCKEMMAUPROVID##
|
||||
##TPLENABLELEDBIT##
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
<TR><TD><A>Source Stream Password:</A></TD><TD><input name="stream_source_auth_password" type="text" value="##STREAM_SOURCE_AUTH_PASSWORD##"></TD></TR>
|
||||
<TR><TD><A>Relay Buffer Time:</A></TD><TD><input name="stream_relay_buffer_time" class="short" type="text" maxlength="5" value="##STREAM_RELAY_BUFFER_TIME##"><label> ms (delay for stream processing)</label></TD></TR>
|
||||
<TR><TD><A>Relay Reconnect Count:</A></TD><TD><input name="stream_relay_reconnect_count" class="short" type="text" maxlength="2" value="##STREAM_RELAY_RECONNECT_COUNT##"><label> attempts until an interrupted stream is disconnected (0 = disabled)</label></TD></TR>
|
||||
##TPLSTREAMEMUSETTINGS##
|
||||
<TR><TD><A>Relay Client Display Options:</A></TD>
|
||||
<TD CLASS="invisible">
|
||||
<TABLE>
|
||||
|
|
|
|||
9
webif/config/streamrelay_emusettings.html
Executable file
9
webif/config/streamrelay_emusettings.html
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
<TR><TD><A>Process EMM from stream (emu):</A></TD>
|
||||
<TD>
|
||||
<select name="stream_emm_enabled">
|
||||
<option value="0" ##STREAMEMMENABLEDSELECTED0##>0 - disabled</option>
|
||||
<option value="1" ##STREAMEMMENABLEDSELECTED1##>1 - enabled</option>
|
||||
</select>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR><TD><A>ECM fix delay (emu):</A></TD><TD><input name="stream_ecm_delay" class="short" type="text" maxlength="5" value="##STREAM_ECM_DELAY##"><label> ms (delay for PowerVU processing)</label></TD></TR>
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
<LI CLASS="##CMENUACTIVE7##"><A HREF="files.html?file=logfile">logfile</A></LI>
|
||||
<LI CLASS="##CMENUACTIVE8##"><A HREF="files.html?file=userfile">userfile</A></LI>
|
||||
##TPLFILEMENUGBOX## <!-- CMENUACTIVE19-29 -->
|
||||
<LI CLASS="##CMENUACTIVE9## ##CMENUACTIVE10## ##CMENUACTIVE11## ##CMENUACTIVE12## ##CMENUACTIVE13## ##CMENUACTIVE14## ##CMENUACTIVE15## ##CMENUACTIVE16## ##CMENUACTIVE17## ##CMENUACTIVE18##"><A HREF="#" class="drop">other files<b class="subcaret"></b></A>
|
||||
<LI CLASS="##CMENUACTIVE9## ##CMENUACTIVE10## ##CMENUACTIVE11## ##CMENUACTIVE12## ##CMENUACTIVE13## ##CMENUACTIVE14## ##CMENUACTIVE15## ##CMENUACTIVE16## ##CMENUACTIVE17## ##CMENUACTIVE18## ##CMENUACTIVE30##"><A HREF="#" class="drop">other files<b class="subcaret"></b></A>
|
||||
<UL CLASS="dropdown_nav">
|
||||
<LI CLASS="##CMENUACTIVE9##"><A HREF="files.html?file=oscam.services">oscam.services</A></LI>
|
||||
<LI CLASS="##CMENUACTIVE10##"><A HREF="files.html?file=oscam.provid">oscam.provid</A></LI>
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
##VIEW_FILEMENUCSS## <!-- CMENUACTIVE16 -->
|
||||
##TPLFILEMENUTWIN## <!-- CMENUACTIVE17 -->
|
||||
##TPLFILEMENUCONSTCW## <!-- CMENUACTIVE18 -->
|
||||
##TPLFILEMENUSOFTCAMKEY## <!-- CMENUACTIVE30 -->
|
||||
</UL>
|
||||
</LI>
|
||||
</UL>
|
||||
|
|
|
|||
1
webif/files/menu_softcamkey.html
Executable file
1
webif/files/menu_softcamkey.html
Executable file
|
|
@ -0,0 +1 @@
|
|||
<LI CLASS="##CMENUACTIVE30##"><A HREF="files.html?file=SoftCam.Key">SoftCam.Key</A></LI>
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
# This file contains index of the templates.
|
||||
# The format is simple "TEMPLATE_NAME FILENAME DEPENDENCY1,DEPENDENCYx"
|
||||
# The format is simple "TEMPLATE_NAME FILENAME DEPENDENCY1,DEPENDANCYx"
|
||||
# Lines starting with # are ignorred
|
||||
# TEMPLATE_NAME - name of the template and also name of the file that
|
||||
# contains the template
|
||||
# FILENAME - The file that contains the template
|
||||
# DEPENDENCYx - The config variable which is responsible for this template
|
||||
# DEPENDANCYx - The config variable which is responsible for this template
|
||||
|
||||
JSONCACHEEX api.json/cacheex.json CS_CACHEEX
|
||||
JSONCACHEEXBIT api.json/cacheexbit.json CS_CACHEEX
|
||||
|
|
@ -62,7 +62,7 @@ CONFIGCCCAM config/cccam.html
|
|||
CONFIGCCCAMCTRL config/cccam_control.html MODULE_CCCSHARE
|
||||
CONFIGCONTENT config/config.html
|
||||
CONFIGDVBAPI config/dvbapi.html HAVE_DVBAPI
|
||||
EXTENDEDCWAPI config/dvbapi_extended_cw_api.html WITH_EXTENDED_CW
|
||||
EXTENDEDCWAPI config/dvbapi_extended_cw_api.html WITH_EXTENDED_CW,WITH_EMU
|
||||
DEMUXERFIX config/dvbapi_demuxerfix.html MODULE_STREAMRELAY
|
||||
CCCAMRESHAREBIT config/cccreshare.html MODULE_GBOX
|
||||
CONFIGGBOX config/gbox.html MODULE_GBOX
|
||||
|
|
@ -92,6 +92,7 @@ CONFIGMENUNEWCAMD config/menu_newcamd.html
|
|||
CONFIGMENURADEGAST config/menu_radegast.html MODULE_RADEGAST
|
||||
CONFIGMENUSCAM config/menu_scam.html MODULE_SCAM
|
||||
CONFIGMENUSTREAMRELAY config/menu_streamrelay.html MODULE_STREAMRELAY
|
||||
STREAMEMUSETTINGS config/streamrelay_emusettings.html WITH_EMU
|
||||
CONFIGMENUSERIAL config/menu_serial.html MODULE_SERIAL
|
||||
CONFIGMONITOR config/monitor.html MODULE_MONITOR
|
||||
CONFIGNEWCAMD config/newcamd.html MODULE_NEWCAMD
|
||||
|
|
@ -131,6 +132,7 @@ FILEMENUDVBAPI files/menu_dvbapi.html
|
|||
FILEMENUFAKECWS files/menu_fakecws.html CS_CACHEEX
|
||||
FILEMENUGBOX files/menu_gbox.html MODULE_GBOX
|
||||
FILEMENUTWIN files/menu_twin.html MODULE_SERIAL
|
||||
FILEMENUSOFTCAMKEY files/menu_softcamkey.html WITH_EMU
|
||||
|
||||
AUTOCONF ghttp/autoconf.html MODULE_GHTTP
|
||||
PREAUTOCONF ghttp/pre_autoconf.html MODULE_GHTTP
|
||||
|
|
@ -199,6 +201,7 @@ READEREDITCACHEEXBIT readerconfig/readerconfig_cacheexbit.html
|
|||
READEREDITCACHEEXAIOBIT readerconfig/readerconfig_cacheexaiobit.html CS_CACHEEX_AIO
|
||||
READERCONFIGCAMD35BIT readerconfig/readerconfig_camd35bit.html MODULE_CAMD35
|
||||
READERCONFIGCCCAMBIT readerconfig/readerconfig_cccambit.html MODULE_CCCAM
|
||||
READERCONFIGEMUBIT readerconfig/readerconfig_emubit.html WITH_EMU
|
||||
READERCONFIGCS378XBIT readerconfig/readerconfig_cs378xbit.html MODULE_CAMD35_TCP
|
||||
READERCONFIGGBOXBIT readerconfig/readerconfig_gboxbit.html MODULE_GBOX
|
||||
READERINFOGBOXREMM readerconfig/readerinfo_gbox_remm.html MODULE_GBOX
|
||||
|
|
|
|||
1461
webif/pages_wiki.c
1461
webif/pages_wiki.c
File diff suppressed because one or more lines are too long
|
|
@ -7,13 +7,11 @@
|
|||
|
||||
#ifdef WEBIF_WIKI
|
||||
|
||||
#define COMPRESSED_WIKI 1
|
||||
|
||||
struct wiki_entry {
|
||||
uint32_t param_ofs;
|
||||
uint32_t config_ofs;
|
||||
uint32_t section_ofs;
|
||||
uint32_t text_ofs;
|
||||
const char *param;
|
||||
const char *config;
|
||||
const char *section;
|
||||
const char *text;
|
||||
int8_t status;
|
||||
};
|
||||
|
||||
|
|
@ -21,8 +19,6 @@ int32_t wiki_count(void);
|
|||
const struct wiki_entry *wiki_get_entries(void);
|
||||
const char *wiki_get_help(const char *config, const char *section, const char *param);
|
||||
int8_t wiki_get_status(const char *config, const char *section, const char *param);
|
||||
void wiki_get_data(const char **data, size_t *data_len, size_t *data_olen);
|
||||
char *wiki_get_decompressed_data(void);
|
||||
void webif_wiki_prepare(void);
|
||||
void webif_wiki_free(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,4 +22,5 @@
|
|||
<TR><TD><A>Maxhop:</A></TD><TD><input name="cccmaxhops" class="short" type="text" maxlength="2" value="##CCCMAXHOPS##"></TD></TR>
|
||||
<TR><TD><A>Mindown:</A></TD><TD><input name="cccmindown" class="short" type="text" maxlength="2" value="##CCCMINDOWN##"></TD></TR>
|
||||
<TR><TD><A>Reshare:</A></TD><TD><input name="cccreshare" class="short" type="text" maxlength="2" value="##CCCRESHARE##"> 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>
|
||||
|
|
|
|||
2
webif/readerconfig/readerconfig_emubit.html
Executable file
2
webif/readerconfig/readerconfig_emubit.html
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
<TR><TD><A>AU providers:</A></TD><TD><textarea name="emu_auproviders" rows="3" class="bt">##EMUAUPROVIDERS##</textarea></TD></TR>
|
||||
<TR><TD><A>[BISS] Enable date-coded keys:</A></TD><TD><input name="emu_datecodedenabled" type="hidden" value="0"><input name="emu_datecodedenabled" type="checkbox" value="1" ##EMUDATECODEDENABLED##><label></label></TD></TR>
|
||||
BIN
webif/wiki_gen
BIN
webif/wiki_gen
Binary file not shown.
Loading…
Reference in New Issue
Block a user