2026-02-16 09:02:48 +00:00
# define MODULE_LOG_PREFIX "camd35"
# include "globals.h"
# include "oscam-array.h"
# if defined(CS_CACHEEX) && (defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP))
# include "module-cacheex.h"
# include "module-camd35.h"
# include "module-camd35-cacheex.h"
# include "oscam-cache.h"
# include "oscam-client.h"
# include "oscam-ecm.h"
# include "oscam-string.h"
# include "oscam-reader.h"
# ifdef CS_CACHEEX_AIO
# include "oscam-chk.h"
# include "oscam-config.h"
# endif
uint8_t camd35_node_id [ 8 ] ;
2026-02-23 16:40:08 +00:00
# define CSP_HASH_SWAP(n) (((((uint32_t)(n) & 0xFF)) << 24) | \
( ( ( ( uint32_t ) ( n ) & 0xFF00 ) ) < < 8 ) | \
( ( ( ( uint32_t ) ( n ) & 0xFF0000 ) ) > > 8 ) | \
( ( ( ( uint32_t ) ( n ) & 0xFF000000 ) ) > > 24 ) )
2026-02-16 09:02:48 +00:00
# ifdef CS_CACHEEX_AIO
void camd35_cacheex_feature_trigger_in ( struct s_client * cl , uint8_t * buf )
{
int32_t feature = 0 ;
uint16_t i = 20 ;
uint8_t filter_count ;
uint8_t j , k , l , rc ;
feature = buf [ 21 ] | ( buf [ 20 ] < < 8 ) ;
FTAB * lgonly_tab ;
// check client & cacheex-mode
if (
! check_client ( cl ) | |
! (
( cl - > typ = = ' c ' & & cl - > account - > cacheex . mode > 0 ) | |
( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode > 0 )
)
)
{
return ;
}
switch ( feature )
{
// set localgenerated only
case 1 :
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) )
{
if ( cfg . cacheex_lg_only_remote_settings | | cl - > account - > cacheex . lg_only_remote_settings )
cl - > account - > cacheex . localgenerated_only = buf [ 24 ] ;
else if ( buf [ 24 ] )
cl - > account - > cacheex . localgenerated_only = buf [ 24 ] ;
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 )
{
if ( cfg . cacheex_lg_only_remote_settings | | cl - > reader - > cacheex . lg_only_remote_settings )
cl - > reader - > cacheex . localgenerated_only = buf [ 24 ] ;
else if ( buf [ 24 ] )
cl - > reader - > cacheex . localgenerated_only = buf [ 24 ] ;
}
break ;
// set localgenerated only caidtab
case 2 :
filter_count = buf [ i + 4 ] ;
i + = 5 ;
memset ( & lgonly_tab , 0 , sizeof ( lgonly_tab ) ) ;
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) )
{
lgonly_tab = & cl - > account - > cacheex . lg_only_tab ;
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 )
{
lgonly_tab = & cl - > reader - > cacheex . lg_only_tab ;
}
else
{
return ;
}
// remotesettings enabled - replace local settings
if ( cfg . cacheex_lg_only_remote_settings | |
(
( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) & & cl - > account - > cacheex . lg_only_remote_settings )
| | ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 & & cl - > reader - > cacheex . lg_only_remote_settings )
)
)
{
ftab_clear ( lgonly_tab ) ;
for ( j = 0 ; j < filter_count ; j + + )
{
FILTER d ;
memset ( & d , 0 , sizeof ( d ) ) ;
d . caid = b2i ( 2 , buf + i ) ;
i + = 2 ;
d . nprids = 1 ;
d . prids [ 0 ] = NO_PROVID_VALUE ;
ftab_add ( lgonly_tab , & d ) ;
}
}
// remotesettings disabled - write additional remote-caids received
else
{
for ( j = 0 ; j < filter_count ; j + + )
{
FILTER d ;
memset ( & d , 0 , sizeof ( d ) ) ;
d . caid = b2i ( 2 , buf + i ) ;
i + = 2 ;
d . nprids = 1 ;
d . prids [ 0 ] = NO_PROVID_VALUE ;
if ( ! chk_lg_only_cp ( d . caid , d . prids [ 0 ] , lgonly_tab ) )
{
cs_log_dbg ( D_CACHEEX , " %04X:%06X not found in local settings - adding them " , d . caid , d . prids [ 0 ] ) ;
for ( l = rc = 0 ; ( ! rc ) & & ( l < lgonly_tab - > nfilts ) ; l + + )
{
if ( lgonly_tab - > filts [ l ] . caid = = d . caid )
{
rc = 1 ;
if ( lgonly_tab - > filts [ l ] . nprids + 1 < = CS_MAXPROV )
{
lgonly_tab - > filts [ l ] . prids [ lgonly_tab - > filts [ l ] . nprids ] = d . prids [ 0 ] ;
lgonly_tab - > filts [ l ] . nprids + + ;
}
else
{
cs_log_dbg ( D_CACHEEX , " error: cacheex_lg_only_tab -> max. number(%i) of providers reached " , CS_MAXPROV ) ;
}
}
}
if ( ! rc )
{
ftab_add ( lgonly_tab , & d ) ;
}
}
}
}
break ;
// set cacheex_ecm_filter - extended
case 4 :
filter_count = buf [ i + 4 ] ;
i + = 5 ;
CECSPVALUETAB * filter ;
memset ( & filter , 0 , sizeof ( filter ) ) ;
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) & & cl - > account - > cacheex . allow_filter )
{
filter = & cl - > account - > cacheex . filter_caidtab ;
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 & & cl - > reader - > cacheex . allow_filter )
{
filter = & cl - > reader - > cacheex . filter_caidtab ;
}
else
{
return ;
}
cecspvaluetab_clear ( filter ) ;
for ( j = 0 ; j < filter_count ; j + + )
{
int32_t caid = - 1 , cmask = - 1 , provid = - 1 , srvid = - 1 ;
CECSPVALUETAB_DATA d ;
memset ( & d , 0 , sizeof ( d ) ) ;
caid = b2i ( 2 , buf + i ) ;
if ( caid = = 0xFFFF ) caid = - 1 ;
i + = 2 ;
cmask = b2i ( 2 , buf + i ) ;
if ( cmask = = 0xFFFF ) cmask = - 1 ;
i + = 2 ;
provid = b2i ( 3 , buf + i ) ;
if ( provid = = 0xFFFFFF ) provid = - 1 ;
i + = 3 ;
srvid = b2i ( 2 , buf + i ) ;
if ( srvid = = 0xFFFF ) srvid = - 1 ;
i + = 2 ;
if ( caid > 0 )
{
d . caid = caid ;
d . cmask = cmask ;
d . prid = provid ;
d . srvid = srvid ;
cecspvaluetab_add ( filter , & d ) ;
}
}
break ;
// no push after
case 8 : ;
CAIDVALUETAB * ctab ;
memset ( & ctab , 0 , sizeof ( ctab ) ) ;
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) )
{
ctab = & cl - > account - > cacheex . cacheex_nopushafter_tab ;
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 )
{
ctab = & cl - > reader - > cacheex . cacheex_nopushafter_tab ;
}
else
{
return ;
}
filter_count = buf [ i + 4 ] ;
i + = 5 ;
caidvaluetab_clear ( ctab ) ;
for ( j = 0 ; j < filter_count ; j + + )
{
uint16_t caid = 0 , value = 0 ;
CAIDVALUETAB_DATA d ;
memset ( & d , 0 , sizeof ( d ) ) ;
caid = b2i ( 2 , buf + i ) ;
if ( caid = = 0xFFFF ) caid = - 1 ;
i + = 2 ;
value = b2i ( 2 , buf + i ) ;
if ( value = = 0xFFFF ) value = - 1 ;
i + = 2 ;
if ( caid > 0 )
{
d . caid = caid ;
d . value = value ;
caidvaluetab_add ( ctab , & d ) ;
}
}
break ;
// max hop
case 16 :
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) & & cl - > account - > cacheex . allow_maxhop )
{
cl - > account - > cacheex . maxhop = buf [ 24 ] ;
cl - > account - > cacheex . maxhop_lg = buf [ 25 ] ;
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 & & cl - > reader - > cacheex . allow_maxhop )
{
cl - > reader - > cacheex . maxhop = buf [ 24 ] ;
cl - > reader - > cacheex . maxhop_lg = buf [ 25 ] ;
}
break ;
// aio-version
case 32 : ;
uint16_t payload_size = b2i ( 2 , buf + i + 2 ) ;
if ( cl - > typ = = ' c ' & & cl - > account - > cacheex . mode > 0 )
{
char * ofs = ( char * ) buf + i + 4 ;
memset ( cl - > account - > cacheex . aio_version , 0 , sizeof ( cl - > account - > cacheex . aio_version ) ) ;
if ( payload_size > 0 )
{
size_t str_len = strnlen ( ofs , payload_size ) ;
if ( str_len > = sizeof ( cl - > account - > cacheex . aio_version ) )
str_len = sizeof ( cl - > account - > cacheex . aio_version ) - 1 ;
memcpy ( cl - > account - > cacheex . aio_version , ofs , str_len ) ;
// sanitize: remove non-printable characters
size_t x ;
for ( x = 0 ; x < str_len ; x + + )
{
if ( cl - > account - > cacheex . aio_version [ x ] < 0x20 | | cl - > account - > cacheex . aio_version [ x ] > 0x7E )
{
cl - > account - > cacheex . aio_version [ x ] = ' \0 ' ;
break ;
}
}
}
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode > 0 )
{
char * ofs = ( char * ) buf + i + 4 ;
memset ( cl - > reader - > cacheex . aio_version , 0 , sizeof ( cl - > reader - > cacheex . aio_version ) ) ;
if ( payload_size > 0 )
{
size_t str_len = strnlen ( ofs , payload_size ) ;
if ( str_len > = sizeof ( cl - > reader - > cacheex . aio_version ) )
str_len = sizeof ( cl - > reader - > cacheex . aio_version ) - 1 ;
memcpy ( cl - > reader - > cacheex . aio_version , ofs , str_len ) ;
// sanitize: remove non-printable characters
size_t x ;
for ( x = 0 ; x < str_len ; x + + )
{
if ( cl - > reader - > cacheex . aio_version [ x ] < 0x20 | | cl - > reader - > cacheex . aio_version [ x ] > 0x7E )
{
cl - > reader - > cacheex . aio_version [ x ] = ' \0 ' ;
break ;
}
}
}
}
break ;
// lg_only_tab caid:prov1[,provN][;caid:prov]
case 64 : ;
memset ( & lgonly_tab , 0 , sizeof ( lgonly_tab ) ) ;
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) )
{
lgonly_tab = & cl - > account - > cacheex . lg_only_tab ;
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 )
{
lgonly_tab = & cl - > reader - > cacheex . lg_only_tab ;
}
else
{
return ;
}
filter_count = buf [ i + 4 ] ;
i + = 5 ;
// remotesettings enabled - replace local settings
if ( cfg . cacheex_lg_only_remote_settings | |
(
( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) & & cl - > account - > cacheex . lg_only_remote_settings )
| | ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 & & cl - > reader - > cacheex . lg_only_remote_settings )
| | ! lgonly_tab - > nfilts
)
)
{
ftab_clear ( lgonly_tab ) ;
for ( j = 0 ; j < filter_count ; j + + )
{
FILTER d ;
memset ( & d , 0 , sizeof ( d ) ) ;
d . caid = b2i ( 2 , buf + i ) ;
i + = 2 ;
d . nprids = b2i ( 1 , buf + i ) ;
i + = 1 ;
for ( k = 0 ; k < d . nprids ; k + + )
{
d . prids [ k ] = b2i ( 3 , buf + i ) ;
i + = 3 ;
}
ftab_add ( lgonly_tab , & d ) ;
}
}
// remotesettings disabled - write additional remote-caid/provids received
else
{
for ( j = 0 ; j < filter_count ; j + + )
{
FILTER d ;
memset ( & d , 0 , sizeof ( d ) ) ;
d . caid = b2i ( 2 , buf + i ) ;
i + = 2 ;
d . nprids = b2i ( 1 , buf + i ) ;
i + = 1 ;
for ( k = 0 ; k < d . nprids ; k + + )
{
d . prids [ k ] = b2i ( 3 , buf + i ) ;
i + = 3 ;
if ( ! chk_ident_filter ( d . caid , d . prids [ k ] , lgonly_tab ) )
{
cs_log_dbg ( D_CACHEEX , " %04X:%06X not found in local settings - adding them " , d . caid , d . prids [ k ] ) ;
for ( l = rc = 0 ; ( ! rc ) & & ( l < lgonly_tab - > nfilts ) ; l + + )
{
if ( lgonly_tab - > filts [ l ] . caid = = d . caid )
{
rc = 1 ;
if ( lgonly_tab - > filts [ l ] . nprids + 1 < = CS_MAXPROV )
{
lgonly_tab - > filts [ l ] . prids [ lgonly_tab - > filts [ l ] . nprids ] = d . prids [ k ] ;
lgonly_tab - > filts [ l ] . nprids + + ;
}
else
{
cs_log_dbg ( D_CACHEEX , " error: cacheex_lg_only_tab -> max. number of providers reached " ) ;
}
}
}
if ( ! rc )
{
ftab_add ( lgonly_tab , & d ) ;
}
}
}
}
}
break ;
default :
return ;
}
}
void camd35_cacheex_feature_trigger ( struct s_client * cl , int32_t feature , uint8_t mode )
{
// size: 20 + (feature-bitfield & mask: 2) + payload-size: 2 + feature-payload :x
uint16_t size = 20 + 2 + 2 ;
int i = 0 ;
uint8_t j ;
uint8_t payload [ MAX_ECM_SIZE - size ] ;
memset ( payload , 0 , sizeof ( payload ) ) ;
// check client & cacheex-mode
if ( ! check_client ( cl ) )
{
return ;
}
switch ( feature )
{
FTAB * lgonly_tab ;
// set localgenerated only
case 1 :
size + = 1 ;
if ( size < 32 )
size = 32 ;
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
// payload-size
i2b_buf ( 2 , 1 , payload + i ) ;
i + = 2 ;
// set payload
if ( mode = = 2 )
{
if ( cl - > reader - > cacheex . localgenerated_only_in )
payload [ i ] = cl - > reader - > cacheex . localgenerated_only_in ;
else
payload [ i ] = cfg . cacheex_localgenerated_only_in ;
}
else if ( mode = = 3 )
{
if ( cl - > account - > cacheex . localgenerated_only_in )
payload [ i ] = cl - > account - > cacheex . localgenerated_only_in ;
else
payload [ i ] = cfg . cacheex_localgenerated_only_in ;
}
break ;
// set localgenerated only caidtab; cx-aio < 9.2.6-04
case 2 : ;
if ( mode = = 2 )
{
lgonly_tab = & cl - > reader - > cacheex . lg_only_in_tab ;
if ( ! lgonly_tab - > nfilts )
lgonly_tab = & cfg . cacheex_lg_only_in_tab ;
}
else if ( mode = = 3 )
{
lgonly_tab = & cl - > account - > cacheex . lg_only_in_tab ;
if ( ! lgonly_tab - > nfilts )
lgonly_tab = & cfg . cacheex_lg_only_in_tab ;
}
else
{
return ;
}
size + = ( lgonly_tab - > nfilts * 2 + 1 ) ;
if ( size < 32 )
size = 32 ;
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
// payload-size
if ( ( lgonly_tab - > nfilts * 2 + 1 ) > ( int ) sizeof ( payload ) )
{
cs_log_dbg ( D_CACHEEX , " ERROR: too much localgenerated only caidtab-entries (max. 255) " ) ;
return ;
}
i2b_buf ( 2 , ( lgonly_tab - > nfilts * 2 + 1 ) , payload + i ) ; // n * caid + ctnum
i + = 2 ;
// set payload
if ( lgonly_tab - > nfilts > 255 )
{
cs_log_dbg ( D_CACHEEX , " ERROR: too much localgenerated only caidtab-entries (max. 255) " ) ;
return ;
}
payload [ i ] = lgonly_tab - > nfilts ;
i + = 1 ;
for ( j = 0 ; j < lgonly_tab - > nfilts ; j + + )
{
FILTER * d = & lgonly_tab - > filts [ j ] ;
if ( d - > caid )
{
i2b_buf ( 2 , d - > caid , payload + i ) ;
i + = 2 ;
}
else
{
continue ;
}
}
break ;
// cacchex_ecm_filter extendend
case 4 : ;
CECSPVALUETAB * filter ;
if ( mode = = 2 )
{
filter = & cl - > reader - > cacheex . filter_caidtab ;
// if not set, use global settings
if ( cl - > reader - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab . cevnum > 0 )
filter = & cfg . cacheex_filter_caidtab ;
// if aio, use global aio settings
if ( cl - > reader - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab_aio . cevnum > 0 & & cl - > cacheex_aio_checked & & ( cl - > reader - > cacheex . feature_bitfield & 4 ) )
filter = & cfg . cacheex_filter_caidtab_aio ;
}
else if ( mode = = 3 )
{
filter = & cl - > account - > cacheex . filter_caidtab ;
// if not set, use global settings
if ( cl - > account - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab . cevnum > 0 )
filter = & cfg . cacheex_filter_caidtab ;
if ( cl - > account - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab_aio . cevnum > 0 & & cl - > cacheex_aio_checked & & ( cl - > account - > cacheex . feature_bitfield & 4 ) )
filter = & cfg . cacheex_filter_caidtab_aio ;
}
else
{
return ;
}
size + = ( filter - > cevnum * 9 + 1 ) ;
if ( size < 32 )
size = 32 ;
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
// payload-size
if ( ( filter - > cevnum * 9 + 1 ) > ( int ) sizeof ( payload ) )
{
cs_log_dbg ( D_CACHEEX , " ERROR: to much cacheex_ecm_filter-entries (max. 63), only 15 default camd3-filters sent " ) ;
return ;
}
i2b_buf ( 2 , ( filter - > cevnum * 9 + 1 ) , payload + i ) ; // n * (caid2,mask2,provid3,srvid2) + ctnum1
i + = 2 ;
// set payload
payload [ i ] = filter - > cevnum ;
i + = 1 ;
for ( j = 0 ; j < filter - > cevnum ; j + + )
{
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
if ( d - > caid )
{
i2b_buf ( 2 , d - > caid , payload + i ) ;
i + = 2 ;
}
if ( d - > cmask )
{
i2b_buf ( 2 , d - > cmask , payload + i ) ;
}
i + = 2 ;
if ( d - > prid )
{
i2b_buf ( 3 , d - > prid , payload + i ) ;
}
i + = 3 ;
if ( d - > srvid )
{
i2b_buf ( 2 , d - > srvid , payload + i ) ;
}
i + = 2 ;
}
camd35_cacheex_send_push_filter ( cl , 2 ) ;
break ;
// no push after
case 8 : ;
CAIDVALUETAB * ctab ;
if ( mode = = 2 )
{
ctab = & cl - > reader - > cacheex . cacheex_nopushafter_tab ;
if ( ! ctab - > cvnum )
ctab = & cfg . cacheex_nopushafter_tab ;
}
else if ( mode = = 3 )
{
ctab = & cl - > account - > cacheex . cacheex_nopushafter_tab ;
if ( ! ctab - > cvnum )
ctab = & cfg . cacheex_nopushafter_tab ;
}
else
{
return ;
}
size + = ( ctab - > cvnum * 4 + 1 ) ;
if ( size < 32 )
size = 32 ;
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
// payload-size
if ( ( ctab - > cvnum * 4 + 1 ) > ( int ) sizeof ( payload ) )
{
cs_log_dbg ( D_CACHEEX , " ERROR: to much no push after caidtvalueab-entries (max. 255) " ) ;
return ;
}
i2b_buf ( 2 , ( ctab - > cvnum * 4 + 1 ) , payload + i ) ; // n * (caid2+value2) + cvnum
i + = 2 ;
// set payload
if ( ctab - > cvnum > 255 )
{
cs_log_dbg ( D_CACHEEX , " ERROR: to much no push after caidtvalueab-entries (max. 255) " ) ;
return ;
}
payload [ i ] = ctab - > cvnum ;
i + = 1 ;
for ( j = 0 ; j < ctab - > cvnum ; j + + )
{
CAIDVALUETAB_DATA * d = & ctab - > cvdata [ j ] ;
if ( d - > caid )
{
i2b_buf ( 2 , d - > caid , payload + i ) ;
i + = 2 ;
i2b_buf ( 2 , d - > value , payload + i ) ;
i + = 2 ;
}
else
{
continue ;
}
}
break ;
// maxhop
case 16 :
size + = 2 ;
if ( size < 32 )
size = 32 ;
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
// payload-size
i2b_buf ( 2 , 2 , payload + i ) ;
i + = 2 ;
// set payload
if ( mode = = 2 )
{
if ( cl - > reader - > cacheex . maxhop )
payload [ i ] = cl - > reader - > cacheex . maxhop ;
else
payload [ i ] = 0 ;
i + = 1 ;
if ( cl - > reader - > cacheex . maxhop_lg )
payload [ i ] = cl - > reader - > cacheex . maxhop_lg ;
else
payload [ i ] = 0 ;
}
else if ( mode = = 3 )
{
if ( cl - > account - > cacheex . maxhop )
payload [ i ] = cl - > account - > cacheex . maxhop ;
else
payload [ i ] = 0 ;
i + = 1 ;
if ( cl - > account - > cacheex . maxhop_lg )
payload [ i ] = cl - > account - > cacheex . maxhop_lg ;
else
payload [ i ] = 0 ;
}
break ;
// aio-version
case 32 : ;
size + = CS_AIO_VERSION_LEN ;
if ( size < 32 )
size = 32 ;
uint8_t token [ CS_AIO_VERSION_LEN ] ;
memset ( token , 0 , sizeof ( token ) ) ;
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
// payload-size
i2b_buf ( 2 , sizeof ( token ) , payload + i ) ;
i + = 2 ;
// set payload
snprintf ( ( char * ) token , sizeof ( token ) , " %s " , CS_AIO_VERSION ) ;
uint8_t * ofs = payload + i ;
memcpy ( ofs , token , sizeof ( token ) ) ;
break ;
// lg_only_tab
case 64 : ;
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
if ( mode = = 2 )
{
lgonly_tab = & cl - > reader - > cacheex . lg_only_in_tab ;
if ( ! lgonly_tab - > nfilts )
lgonly_tab = & cfg . cacheex_lg_only_in_tab ;
}
else if ( mode = = 3 )
{
lgonly_tab = & cl - > account - > cacheex . lg_only_in_tab ;
if ( ! lgonly_tab - > nfilts )
lgonly_tab = & cfg . cacheex_lg_only_in_tab ;
}
else
{
return ;
}
char * cx_aio_ftab ;
cx_aio_ftab = cxaio_ftab_to_buf ( lgonly_tab ) ;
if ( cs_strlen ( cx_aio_ftab ) > 0 & & cx_aio_ftab [ 0 ] ! = ' \0 ' )
{
size + = cs_strlen ( cx_aio_ftab ) * sizeof ( char ) ;
// payload-size
i2b_buf ( 2 , cs_strlen ( cx_aio_ftab ) , payload + i ) ;
i + = 2 ;
// filter counter
payload [ i ] = lgonly_tab - > nfilts ;
i + = 1 ;
for ( j = 0 ; j < cs_strlen ( cx_aio_ftab ) ; j + = 2 )
{
payload [ i ] = ( gethexval ( cx_aio_ftab [ j ] ) < < 4 ) | gethexval ( cx_aio_ftab [ j + 1 ] ) ;
i + + ;
}
}
if ( size < 32 )
size = 32 ;
NULLFREE ( cx_aio_ftab ) ;
break ;
default :
return ;
}
uint8_t buf [ size ] ;
memset ( buf , 0 , sizeof ( buf ) ) ;
buf [ 0 ] = 0x42 ; // camd35_cacheex_feature_trigger
buf [ 1 ] = ( size - 20 ) & 0xFF ;
buf [ 2 ] = ( size - 20 ) > > 8 ;
uint8_t * ofs = buf + 20 ;
memcpy ( ofs , payload , size - 20 ) ;
2026-02-23 16:40:08 +00:00
camd35_send_without_timeout ( cl , buf , size - 20 ) ; //send adds +20
2026-02-16 09:02:48 +00:00
}
void camd35_cacheex_feature_request_save ( struct s_client * cl , uint8_t * buf )
{
int32_t field = b2i ( 2 , ( buf + 20 ) ) ;
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) )
{
cl - > account - > cacheex . feature_bitfield = field ;
// flag 32 => aio-version
if ( cl - > account - > cacheex . feature_bitfield & 32 )
{
camd35_cacheex_feature_trigger ( cl , 32 , 2 ) ;
}
}
if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 )
{
cl - > reader - > cacheex . feature_bitfield = field ;
// flag 32 => aio-version
if ( cl - > reader - > cacheex . feature_bitfield & 32 )
{
camd35_cacheex_feature_trigger ( cl , 32 , 3 ) ;
}
}
if ( cl - > typ = = ' c ' & & cl - > account - > cacheex . mode = = 3 )
{
struct s_auth * acc = cl - > account ;
if ( acc )
{
acc - > cacheex . feature_bitfield = field ;
// process feature-specific actions based on feature_bitfield received
// flag 1 => set localgenerated only flag
if ( acc - > cacheex . feature_bitfield & 1 )
{
camd35_cacheex_feature_trigger ( cl , 1 , 3 ) ;
}
// flag 2 => set localgenerated only caids flag
if ( acc - > cacheex . feature_bitfield & 2 & & ! ( acc - > cacheex . feature_bitfield & 64 ) )
{
camd35_cacheex_feature_trigger ( cl , 2 , 3 ) ;
}
// flag 4 => set cacheex_ecm_filter (extended)
if ( acc - > cacheex . feature_bitfield & 4 )
{
camd35_cacheex_feature_trigger ( cl , 4 , 3 ) ;
}
// flag 8 => np push after caids
if ( acc - > cacheex . feature_bitfield & 8 )
{
camd35_cacheex_feature_trigger ( cl , 8 , 3 ) ;
}
// flag 16 => maxhop
if ( acc - > cacheex . feature_bitfield & 16 )
{
camd35_cacheex_feature_trigger ( cl , 16 , 3 ) ;
}
// flag 32 => aio-version
if ( acc - > cacheex . feature_bitfield & 32 )
{
camd35_cacheex_feature_trigger ( cl , 32 , 3 ) ;
}
// flag 64 => lg_only_tab
if ( acc - > cacheex . feature_bitfield & 64 )
{
camd35_cacheex_feature_trigger ( cl , 64 , 3 ) ;
}
}
else
{
cs_log_dbg ( D_CACHEEX , " feature_bitfield save failed - cl, %s " , username ( cl ) ) ;
}
}
else if ( cl - > typ = = ' p ' & & ( cl - > reader - > cacheex . mode = = 2 | | cl - > reader - > cacheex . mode = = 1 ) )
{
struct s_reader * rdr = cl - > reader ;
if ( rdr )
{
rdr - > cacheex . feature_bitfield = field ;
// process feature-specific actions
// flag 1 => set localgenerated_only; cause of rdr->cacheex.localgenerated_only_in is set
if ( rdr - > cacheex . feature_bitfield & 1 )
{
camd35_cacheex_feature_trigger ( cl , 1 , 2 ) ;
}
// flag 2 => set lg_only_tab; cause of rdr->cacheex.lg_only_in_tab is set
if ( rdr - > cacheex . feature_bitfield & 2 & & ! ( rdr - > cacheex . feature_bitfield & 64 ) )
{
camd35_cacheex_feature_trigger ( cl , 2 , 2 ) ;
}
2026-02-23 16:40:08 +00:00
// // flag 4 => set cacheex_ecm_filter (extended)
2026-02-16 09:02:48 +00:00
if ( rdr - > cacheex . feature_bitfield & 4 )
{
camd35_cacheex_feature_trigger ( cl , 4 , 2 ) ;
}
// flag 8 => no push after caids
if ( rdr - > cacheex . feature_bitfield & 8 )
{
camd35_cacheex_feature_trigger ( cl , 8 , 2 ) ;
}
// flag 16 => maxhop
if ( rdr - > cacheex . feature_bitfield & 16 )
{
camd35_cacheex_feature_trigger ( cl , 16 , 2 ) ;
}
// flag 32 => aio-version
if ( rdr - > cacheex . feature_bitfield & 32 )
{
camd35_cacheex_feature_trigger ( cl , 32 , 2 ) ;
}
// flag 64 => lg_only_tab
if ( rdr - > cacheex . feature_bitfield & 64 )
{
camd35_cacheex_feature_trigger ( cl , 64 , 2 ) ;
}
}
else
{
cs_log_dbg ( D_CACHEEX , " feature_bitfield save failed - rdr, %s " , username ( cl ) ) ;
}
}
}
void camd35_cacheex_feature_request ( struct s_client * cl )
{
int i = 20 ;
uint8_t buf [ 32 ] ;
memset ( buf , 0 , sizeof ( buf ) ) ;
buf [ 0 ] = 0x40 ;
buf [ 1 ] = 12 ;
buf [ 2 ] = 0 ;
i2b_buf ( 2 , CACHEEX_FEATURES , buf + i ) ; // set feature-list here
2026-02-23 16:40:08 +00:00
camd35_send_without_timeout ( cl , buf , 12 ) ; //send adds +20
2026-02-16 09:02:48 +00:00
}
void camd35_cacheex_feature_request_reply ( struct s_client * cl , uint8_t * buf )
{
camd35_cacheex_feature_request_save ( cl , buf ) ;
int i = 20 ;
uint8_t rbuf [ 32 ] ;
memset ( rbuf , 0 , sizeof ( rbuf ) ) ;
rbuf [ 0 ] = 0x41 ;
rbuf [ 1 ] = 12 ;
rbuf [ 2 ] = 0 ;
i2b_buf ( 2 , CACHEEX_FEATURES , rbuf + i ) ;
2026-02-23 16:40:08 +00:00
camd35_send_without_timeout ( cl , rbuf , 12 ) ; //send adds +20
2026-02-16 09:02:48 +00:00
}
# endif
/**
* send push filter
*/
void camd35_cacheex_send_push_filter ( struct s_client * cl , uint8_t mode )
{
struct s_reader * rdr = cl - > reader ;
int i = 20 , j ;
CECSPVALUETAB * filter ;
2026-02-23 16:40:08 +00:00
//maximum size: 20+255
2026-02-16 09:02:48 +00:00
uint8_t buf [ 20 + 242 ] ;
memset ( buf , 0 , sizeof ( buf ) ) ;
buf [ 0 ] = 0x3c ;
buf [ 1 ] = 0xf2 ;
2026-02-23 16:40:08 +00:00
//mode==2 send filters from rdr
2026-02-16 09:02:48 +00:00
if ( mode = = 2 & & rdr )
{
filter = & rdr - > cacheex . filter_caidtab ;
# ifdef CS_CACHEEX_AIO
// if not set, use global settings
if ( rdr - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab . cevnum > 0 )
filter = & cfg . cacheex_filter_caidtab ;
# endif
}
2026-02-23 16:40:08 +00:00
//mode==3 send filters from acc
2026-02-16 09:02:48 +00:00
else if ( mode = = 3 & & cl - > typ = = ' c ' & & cl - > account )
{
filter = & cl - > account - > cacheex . filter_caidtab ;
# ifdef CS_CACHEEX_AIO
// if not set, use global settings
if ( cl - > account - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab . cevnum > 0 )
filter = & cfg . cacheex_filter_caidtab ;
# endif
}
else {
return ;
}
i2b_buf ( 2 , filter - > cevnum , buf + i ) ;
i + = 2 ;
int32_t max_filters = 15 ;
for ( j = 0 ; j < max_filters ; j + + )
{
if ( filter - > cevnum > j ) {
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
i2b_buf ( 4 , d - > caid , buf + i ) ;
}
i + = 4 ;
}
for ( j = 0 ; j < max_filters ; j + + )
{
if ( filter - > cevnum > j ) {
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
i2b_buf ( 4 , d - > cmask , buf + i ) ;
}
i + = 4 ;
}
for ( j = 0 ; j < max_filters ; j + + )
{
if ( filter - > cevnum > j ) {
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
i2b_buf ( 4 , d - > prid , buf + i ) ;
}
i + = 4 ;
}
for ( j = 0 ; j < max_filters ; j + + )
{
if ( filter - > cevnum > j ) {
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
i2b_buf ( 4 , d - > srvid , buf + i ) ;
}
i + = 4 ;
}
cs_log_dbg ( D_CACHEEX , " cacheex: sending push filter request to %s " , username ( cl ) ) ;
2026-02-23 16:40:08 +00:00
camd35_send_without_timeout ( cl , buf , 242 ) ; //send adds +20
2026-02-16 09:02:48 +00:00
}
/**
* store received push filter
*/
static void camd35_cacheex_push_filter ( struct s_client * cl , uint8_t * buf , uint8_t mode )
{
struct s_reader * rdr = cl - > reader ;
int i = 20 , j ;
int32_t caid , cmask , provid , srvid ;
CECSPVALUETAB * filter ;
2026-02-23 16:40:08 +00:00
//mode==2 write filters to acc
2026-02-16 09:02:48 +00:00
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 ;
}
2026-02-23 16:40:08 +00:00
//mode==3 write filters to rdr
2026-02-16 09:02:48 +00:00
else if ( mode = = 3 & & rdr & & rdr - > cacheex . allow_filter = = 1 )
{
filter = & rdr - > cacheex . filter_caidtab ;
}
else {
return ;
}
cecspvaluetab_clear ( filter ) ;
i + = 2 ;
int32_t max_filters = 15 ;
for ( j = 0 ; j < max_filters ; j + + )
{
caid = b2i ( 4 , buf + i ) ;
if ( caid > 0 ) {
CECSPVALUETAB_DATA d ;
memset ( & d , 0 , sizeof ( d ) ) ;
d . caid = b2i ( 4 , buf + i ) ;
cecspvaluetab_add ( filter , & d ) ;
}
i + = 4 ;
}
for ( j = 0 ; j < max_filters ; j + + )
{
cmask = b2i ( 4 , buf + i ) ;
if ( j < filter - > cevnum ) {
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
d - > cmask = cmask ;
}
i + = 4 ;
}
for ( j = 0 ; j < max_filters ; j + + )
{
provid = b2i ( 4 , buf + i ) ;
if ( j < filter - > cevnum ) {
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
d - > prid = provid ;
}
i + = 4 ;
}
for ( j = 0 ; j < max_filters ; j + + )
{
srvid = b2i ( 4 , buf + i ) ;
if ( j < filter - > cevnum ) {
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
d - > srvid = srvid ;
}
i + = 4 ;
}
cs_log_dbg ( D_CACHEEX , " cacheex: received push filter request from %s " , username ( cl ) ) ;
}
static int32_t camd35_cacheex_push_chk ( struct s_client * cl , ECM_REQUEST * er )
{
if (
2026-02-24 22:12:28 +00:00
ll_count ( er - > csp_lastnodes ) > = cacheex_maxhop ( cl , er ) // check max 10 nodes to push
2026-02-16 09:02:48 +00:00
# ifdef CS_CACHEEX_AIO
2026-02-24 22:12:28 +00:00
& & ( ! er - > localgenerated | | ( er - > localgenerated & & ( ll_count ( er - > csp_lastnodes ) > = cacheex_maxhop_lg ( cl , er ) ) ) ) // check maxhop_lg if cw is lg-flagged
2026-02-16 09:02:48 +00:00
# endif
)
{
# ifdef CS_CACHEEX_AIO
2026-02-24 22:12:28 +00:00
cs_log_dbg ( D_CACHEEX , " cacheex: nodelist reached %d nodes(non-lg) or reached %d nodes(lg), no push " , cacheex_maxhop ( cl , er ) , cacheex_maxhop_lg ( cl , er ) ) ;
2026-02-16 09:02:48 +00:00
# else
2026-02-24 22:12:28 +00:00
cs_log_dbg ( D_CACHEEX , " cacheex: nodelist reached %d nodes, no push " , cacheex_maxhop ( cl , er ) ) ;
2026-02-16 09:02:48 +00:00
# endif
return 0 ;
}
if ( cl - > reader )
{
if ( ! cl - > reader - > tcp_connected )
{
cs_log_dbg ( D_CACHEEX , " cacheex: not connected %s -> no push " , username ( cl ) ) ;
return 0 ;
}
}
//if(chk_is_null_nodeid(remote_node)){
if ( ! cl - > ncd_skey [ 8 ] )
{
cs_log_dbg ( D_CACHEEX , " cacheex: NO peer_node_id got yet, skip! " ) ;
return 0 ;
}
uint8_t * remote_node = cl - > ncd_skey ; //it is sended by reader(mode 2) or client (mode 3) each 30s using keepalive msgs
//search existing peer nodes:
LL_LOCKITER * li = ll_li_create ( er - > csp_lastnodes , 0 ) ;
uint8_t * node ;
while ( ( node = ll_li_next ( li ) ) )
{
cs_log_dbg ( D_CACHEEX , " cacheex: check node % " PRIu64 " X == % " PRIu64 " X ? " , cacheex_node_id ( node ) , cacheex_node_id ( remote_node ) ) ;
if ( memcmp ( node , remote_node , 8 ) = = 0 )
{
break ;
}
}
ll_li_destroy ( li ) ;
2026-02-23 16:40:08 +00:00
//node found, so we got it from there, do not push:
2026-02-16 09:02:48 +00:00
if ( node )
{
cs_log_dbg ( D_CACHEEX ,
" cacheex: node % " PRIu64 " X found in list => skip push! " , cacheex_node_id ( node ) ) ;
return 0 ;
}
2026-02-23 16:40:08 +00:00
//check if cw is already pushed
2026-02-16 09:02:48 +00:00
if ( check_is_pushed ( er - > cw_cache , cl ) )
{ return 0 ; }
cs_log_dbg ( D_CACHEEX , " cacheex: push ok % " PRIu64 " X to % " PRIu64 " X %s " , cacheex_node_id ( camd35_node_id ) , cacheex_node_id ( remote_node ) , username ( cl ) ) ;
return 1 ;
}
static int32_t camd35_cacheex_push_out ( struct s_client * cl , struct ecm_request_t * er )
{
int8_t rc = ( er - > rc < E_NOTFOUND ) ? E_FOUND : er - > rc ;
2026-02-23 16:40:08 +00:00
if ( rc ! = E_FOUND & & rc ! = E_UNHANDLED ) { return - 1 ; } //Maybe later we could support other rcs
2026-02-16 09:02:48 +00:00
2026-02-23 16:40:08 +00:00
//E_FOUND : we have the CW,
//E_UNHANDLED : incoming ECM request
2026-02-16 09:02:48 +00:00
if ( cl - > reader )
{
if ( ! camd35_tcp_connect ( cl ) )
{
cs_log_dbg ( D_CACHEEX , " cacheex: not connected %s -> no push " , username ( cl ) ) ;
return ( - 1 ) ;
}
}
uint32_t size = sizeof ( er - > ecmd5 ) + sizeof ( er - > csp_hash ) + sizeof ( er - > cw ) + sizeof ( uint8_t ) +
# ifdef CS_CACHEEX_AIO
( ll_count ( er - > csp_lastnodes ) + 1 ) * 8 + sizeof ( uint8_t ) ;
# else
( ll_count ( er - > csp_lastnodes ) + 1 ) * 8 ;
# endif
uint8_t * buf ;
2026-02-23 16:40:08 +00:00
if ( ! cs_malloc ( & buf , size + 20 ) ) //camd35_send() adds +20
2026-02-16 09:02:48 +00:00
{ return - 1 ; }
2026-02-23 16:40:08 +00:00
buf [ 0 ] = 0x3f ; //New Command: Cache-push
2026-02-16 09:02:48 +00:00
buf [ 1 ] = size & 0xff ;
buf [ 2 ] = size > > 8 ;
buf [ 3 ] = rc ;
i2b_buf ( 2 , er - > srvid , buf + 8 ) ;
i2b_buf ( 2 , er - > caid , buf + 10 ) ;
i2b_buf ( 4 , er - > prid , buf + 12 ) ;
2026-02-23 16:40:08 +00:00
//i2b_buf(2, er->idx, buf + 16); // Not relevant...?
2026-02-16 09:02:48 +00:00
if ( er - > cwc_cycletime & & er - > cwc_next_cw_cycle < 2 )
{
buf [ 18 ] = er - > cwc_cycletime ; // contains cwc stage3 cycletime
if ( er - > cwc_next_cw_cycle = = 1 )
{ buf [ 18 ] = ( buf [ 18 ] | 0x80 ) ; } // set bit 8 to high
if ( cl - > typ = = ' c ' & & cl - > account & & cl - > account - > cacheex . mode )
{ cl - > account - > cwc_info + + ; }
else if ( ( cl - > typ = = ' p ' | | cl - > typ = = ' r ' ) & & ( cl - > reader & & cl - > reader - > cacheex . mode ) )
{ cl - > cwc_info + + ; }
cs_log_dbg ( D_CWC , " CWC (CE) push to %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X " , username ( cl ) , er - > cwc_cycletime , er - > cwc_next_cw_cycle , er - > caid , er - > prid , er - > srvid ) ;
}
buf [ 19 ] = er - > ecm [ 0 ] ! = 0x80 & & er - > ecm [ 0 ] ! = 0x81 ? 0 : er - > ecm [ 0 ] ;
uint8_t * ofs = buf + 20 ;
2026-02-23 16:40:08 +00:00
//write oscam ecmd5:
memcpy ( ofs , er - > ecmd5 , sizeof ( er - > ecmd5 ) ) ; //16
2026-02-16 09:02:48 +00:00
ofs + = sizeof ( er - > ecmd5 ) ;
2026-02-23 16:40:08 +00:00
//write csp hashcode:
2026-02-16 09:02:48 +00:00
i2b_buf ( 4 , CSP_HASH_SWAP ( er - > csp_hash ) , ofs ) ;
ofs + = 4 ;
2026-02-23 16:40:08 +00:00
//write cw:
memcpy ( ofs , er - > cw , sizeof ( er - > cw ) ) ; //16
2026-02-16 09:02:48 +00:00
ofs + = sizeof ( er - > cw ) ;
2026-02-23 16:40:08 +00:00
//write node count:
2026-02-16 09:02:48 +00:00
* ofs = ll_count ( er - > csp_lastnodes ) + 1 ;
ofs + + ;
2026-02-23 16:40:08 +00:00
//write own node:
2026-02-16 09:02:48 +00:00
memcpy ( ofs , camd35_node_id , 8 ) ;
ofs + = 8 ;
2026-02-23 16:40:08 +00:00
//write other nodes:
2026-02-16 09:02:48 +00:00
LL_LOCKITER * li = ll_li_create ( er - > csp_lastnodes , 0 ) ;
uint8_t * node ;
while ( ( node = ll_li_next ( li ) ) )
{
memcpy ( ofs , node , 8 ) ;
ofs + = 8 ;
}
ll_li_destroy ( li ) ;
# ifdef CS_CACHEEX_AIO
// add localgenerated cw-flag
if ( er - > localgenerated )
{
* ofs = 1 ;
}
else
{
* ofs = 0xFF ;
}
# endif
int32_t res = camd35_send ( cl , buf , size ) ;
NULLFREE ( buf ) ;
return res ;
}
static void camd35_cacheex_push_in ( struct s_client * cl , uint8_t * buf )
{
int8_t rc = buf [ 3 ] ;
2026-02-23 16:40:08 +00:00
if ( rc ! = E_FOUND & & rc ! = E_UNHANDLED ) //Maybe later we could support other rcs
2026-02-16 09:02:48 +00:00
{ return ; }
ECM_REQUEST * er ;
uint16_t size = buf [ 1 ] | ( buf [ 2 ] < < 8 ) ;
if ( size < sizeof ( er - > ecmd5 ) + sizeof ( er - > csp_hash ) + sizeof ( er - > cw ) )
{
cs_log_dbg ( D_CACHEEX , " cacheex: %s received old cache-push format! data ignored! " , username ( cl ) ) ;
return ;
}
if ( ! ( er = get_ecmtask ( ) ) )
{ return ; }
er - > srvid = b2i ( 2 , buf + 8 ) ;
er - > caid = b2i ( 2 , buf + 10 ) ;
er - > prid = b2i ( 4 , buf + 12 ) ;
er - > pid = b2i ( 2 , buf + 16 ) ;
2026-02-23 16:40:08 +00:00
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
2026-02-16 09:02:48 +00:00
er - > rc = rc ;
er - > ecmlen = 0 ;
if ( buf [ 18 ] )
{
if ( buf [ 18 ] & ( 0x01 < < 7 ) )
{
er - > cwc_cycletime = ( buf [ 18 ] & 0x7F ) ; // remove bit 8 to get cycletime
er - > cwc_next_cw_cycle = 1 ;
}
else
{
er - > cwc_cycletime = buf [ 18 ] ;
er - > cwc_next_cw_cycle = 0 ;
}
}
uint8_t * ofs = buf + 20 ;
2026-02-23 16:40:08 +00:00
//Read ecmd5
memcpy ( er - > ecmd5 , ofs , sizeof ( er - > ecmd5 ) ) ; //16
2026-02-16 09:02:48 +00:00
ofs + = sizeof ( er - > ecmd5 ) ;
if ( ! check_cacheex_filter ( cl , er ) )
{
return ;
}
# ifdef CS_CACHEEX_AIO
// check incoming cache
if ( check_client ( cl ) & & cl - > typ = = ' p ' & & cl - > reader & & cl - > reader - > cacheex . mode = = 2
& & ( ( cl - > reader - > cacheex . filter_caidtab . cevnum > 0 & & ! chk_csp_ctab ( er , & cl - > reader - > cacheex . filter_caidtab ) ) // reader cacheex_ecm_filter not matching if set
| | ( cl - > reader - > cacheex . filter_caidtab . cevnum = = 0 & & ( cl - > reader - > cacheex . feature_bitfield & 4 ) & & cfg . cacheex_filter_caidtab_aio . cevnum > 0 & & ! chk_csp_ctab ( er , & cfg . cacheex_filter_caidtab_aio ) ) // global cacheex_ecm_filter_aio not matching if set
| | ( cl - > reader - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab_aio . cevnum = = 0 & & cfg . cacheex_filter_caidtab . cevnum > 0 & & ! chk_csp_ctab ( er , & cfg . cacheex_filter_caidtab ) ) // global cacheex_ecm_filter not matching if set
)
)
{
cs_log_dbg ( D_CACHEEX , " cacheex: received cache not matching cacheex_ecm_filter => pushing filter again " ) ;
2026-02-23 16:40:08 +00:00
camd35_cacheex_send_push_filter ( cl , 2 ) ; // get cache != cacheex_ecm_filter, send filter again - remote restarted
2026-02-16 09:02:48 +00:00
if ( cl - > reader - > cacheex . feature_bitfield & 4 )
camd35_cacheex_feature_trigger ( cl , 4 , 2 ) ;
free_push_in_ecm ( er ) ;
return ;
}
if ( check_client ( cl ) & & cl - > typ = = ' c ' & & cl - > account & & cl - > account - > cacheex . mode = = 3
& & ( ( cl - > account - > cacheex . filter_caidtab . cevnum > 0 & & ! chk_csp_ctab ( er , & cl - > account - > cacheex . filter_caidtab ) ) // account cacheex_ecm_filter not matching if set
| | ( cl - > account - > cacheex . filter_caidtab . cevnum = = 0 & & ( cl - > account - > cacheex . feature_bitfield & 4 ) & & cfg . cacheex_filter_caidtab_aio . cevnum > 0 & & ! chk_csp_ctab ( er , & cfg . cacheex_filter_caidtab_aio ) ) // global cacheex_ecm_filter_aio not matching if set
| | ( cl - > account - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab_aio . cevnum = = 0 & & cfg . cacheex_filter_caidtab . cevnum > 0 & & ! chk_csp_ctab ( er , & cfg . cacheex_filter_caidtab ) ) // global cacheex_ecm_filter not matching if set
)
)
{
cs_log_dbg ( D_CACHEEX , " cacheex: received cache not matching cacheex_ecm_filter => pushing filter again " ) ;
camd35_cacheex_send_push_filter ( cl , 3 ) ; // get cache != cacheex_ecm_filter, send filter again - remote restarted
if ( cl - > account - > cacheex . feature_bitfield & 4 )
camd35_cacheex_feature_trigger ( cl , 4 , 3 ) ;
free_push_in_ecm ( er ) ;
return ;
}
# endif
2026-02-23 16:40:08 +00:00
//Read csp_hash:
2026-02-16 09:02:48 +00:00
er - > csp_hash = CSP_HASH_SWAP ( b2i ( 4 , ofs ) ) ;
ofs + = 4 ;
2026-02-23 16:40:08 +00:00
//Read cw:
memcpy ( er - > cw , ofs , sizeof ( er - > cw ) ) ; //16
2026-02-16 09:02:48 +00:00
ofs + = sizeof ( er - > cw ) ;
2026-02-23 16:40:08 +00:00
//Check auf neues Format:
2026-02-16 09:02:48 +00:00
uint8_t * data ;
if ( size > ( sizeof ( er - > ecmd5 ) + sizeof ( er - > csp_hash ) + sizeof ( er - > cw ) ) )
{
2026-02-23 16:40:08 +00:00
//Read lastnodes:
2026-02-16 09:02:48 +00:00
uint8_t count = * ofs ;
ofs + + ;
# ifndef CS_CACHEEX_AIO
2026-02-23 16:40:08 +00:00
//check max nodes:
2026-02-24 22:12:28 +00:00
if ( count > cacheex_maxhop ( cl , er ) )
2026-02-16 09:02:48 +00:00
{
2026-02-24 22:12:28 +00:00
cs_log_dbg ( D_CACHEEX , " cacheex: received %d nodes (max=%d), ignored! %s " , ( int32_t ) count , cacheex_maxhop ( cl , er ) , username ( cl ) ) ;
2026-02-16 09:02:48 +00:00
NULLFREE ( er ) ;
return ;
}
# endif
cs_log_dbg ( D_CACHEEX , " cacheex: received %d nodes %s " , ( int32_t ) count , username ( cl ) ) ;
if ( er ) {
er - > csp_lastnodes = ll_create ( " csp_lastnodes " ) ;
}
while ( count )
{
if ( ! cs_malloc ( & data , 8 ) )
{ break ; }
memcpy ( data , ofs , 8 ) ;
ofs + = 8 ;
ll_append ( er - > csp_lastnodes , data ) ;
count - - ;
cs_log_dbg ( D_CACHEEX , " cacheex: received node % " PRIu64 " X %s " , cacheex_node_id ( data ) , username ( cl ) ) ;
}
# ifdef CS_CACHEEX_AIO
// check byte after nodelist for "localgenerated CW"-flag
if ( b2i ( 1 , ofs ) = = 1 )
{
er - > localgenerated = 1 ;
cs_log_dbg ( D_CACHEEX , " cacheex: received ECM with localgenerated flag %04X@%06X:%04X %s " , er - > caid , er - > prid , er - > srvid , username ( cl ) ) ;
2026-02-23 16:40:08 +00:00
//check max nodes for lg flagged cw:
2026-02-24 22:12:28 +00:00
if ( ll_count ( er - > csp_lastnodes ) > cacheex_maxhop_lg ( cl , er ) )
2026-02-16 09:02:48 +00:00
{
2026-02-24 22:12:28 +00:00
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 ) ) ;
2026-02-16 09:02:48 +00:00
free_push_in_ecm ( er ) ;
return ;
}
}
// without localgenerated flag
else
{
2026-02-23 16:40:08 +00:00
//check max nodes:
2026-02-24 22:12:28 +00:00
if ( ll_count ( er - > csp_lastnodes ) > cacheex_maxhop ( cl , er ) )
2026-02-16 09:02:48 +00:00
{
2026-02-24 22:12:28 +00:00
cs_log_dbg ( D_CACHEEX , " cacheex: received %d nodes (max=%d), ignored! %s " , ll_count ( er - > csp_lastnodes ) , cacheex_maxhop ( cl , er ) , username ( cl ) ) ;
2026-02-16 09:02:48 +00:00
free_push_in_ecm ( er ) ;
return ;
}
if (
( cl - > typ = = ' p ' & & cl - > reader & & cl - > reader - > cacheex . mode = = 2 & & ! chk_srvid_localgenerated_only_exception ( er ) // cx1&2
& & (
// !aio
( cl - > cacheex_aio_checked & & ! cl - > reader - > cacheex . feature_bitfield
& & (
! cfg . cacheex_lg_only_in_aio_only & & ! cl - > reader - > cacheex . lg_only_in_aio_only
& & ( cfg . cacheex_localgenerated_only_in | | cl - > reader - > cacheex . localgenerated_only_in | | chk_lg_only ( er , & cl - > reader - > cacheex . lg_only_in_tab ) | | chk_lg_only ( er , & cfg . cacheex_lg_only_in_tab ) )
)
)
| |
// aio
( cl - > cacheex_aio_checked & & cl - > reader - > cacheex . feature_bitfield
& & (
cfg . cacheex_localgenerated_only_in | | cl - > reader - > cacheex . localgenerated_only_in | | chk_lg_only ( er , & cl - > reader - > cacheex . lg_only_in_tab ) | | chk_lg_only ( er , & cfg . cacheex_lg_only_in_tab )
)
)
)
)
| |
( cl - > typ = = ' c ' & & cl - > account & & cl - > account - > cacheex . mode = = 3 & & ! chk_srvid_localgenerated_only_exception ( er ) // cx3
& & (
// !aio
( cl - > cacheex_aio_checked & & ! cl - > account - > cacheex . feature_bitfield
& & (
! cfg . cacheex_lg_only_in_aio_only & & ! cl - > account - > cacheex . lg_only_in_aio_only
& & ( cfg . cacheex_localgenerated_only_in | | cl - > account - > cacheex . localgenerated_only_in | | chk_lg_only ( er , & cl - > account - > cacheex . lg_only_in_tab ) | | chk_lg_only ( er , & cfg . cacheex_lg_only_in_tab ) )
)
)
| |
// aio
( cl - > cacheex_aio_checked & & cl - > account - > cacheex . feature_bitfield
& & (
cfg . cacheex_localgenerated_only_in | | cl - > account - > cacheex . localgenerated_only_in | | chk_lg_only ( er , & cl - > account - > cacheex . lg_only_in_tab ) | | chk_lg_only ( er , & cfg . cacheex_lg_only_in_tab )
)
)
)
)
)
{
cs_log_dbg ( D_CACHEEX , " cacheex: drop ECM without localgenerated flag %04X@%06X:%04X %s " , er - > caid , er - > prid , er - > srvid , username ( cl ) ) ;
free_push_in_ecm ( er ) ;
return ;
}
}
# endif
}
else
{
cs_log_dbg ( D_CACHEEX , " cacheex: received old cachex from %s " , username ( cl ) ) ;
er - > csp_lastnodes = ll_create ( " csp_lastnodes " ) ;
}
2026-02-23 16:40:08 +00:00
//store remote node id if we got one. The remote node is the first node in the node list
2026-02-16 09:02:48 +00:00
data = ll_has_elements ( er - > csp_lastnodes ) ;
2026-02-23 16:40:08 +00:00
if ( data & & ! cl - > ncd_skey [ 8 ] ) //Ok, this is tricky, we use newcamd key storage for saving the remote node
2026-02-16 09:02:48 +00:00
{
memcpy ( cl - > ncd_skey , data , 8 ) ;
2026-02-23 16:40:08 +00:00
cl - > ncd_skey [ 8 ] = 1 ; //Mark as valid node
2026-02-16 09:02:48 +00:00
}
cs_log_dbg ( D_CACHEEX , " cacheex: received cacheex from remote node id % " PRIu64 " X " , cacheex_node_id ( cl - > ncd_skey ) ) ;
2026-02-23 16:40:08 +00:00
//for compatibility: add peer node if no node received (not working now, maybe later):
2026-02-16 09:02:48 +00:00
if ( ! ll_count ( er - > csp_lastnodes ) & & cl - > ncd_skey [ 8 ] )
{
if ( ! cs_malloc ( & data , 8 ) )
{
# ifdef CS_CACHEEX_AIO
free_push_in_ecm ( er ) ;
# endif
return ;
}
memcpy ( data , cl - > ncd_skey , 8 ) ;
ll_append ( er - > csp_lastnodes , data ) ;
cs_log_dbg ( D_CACHEEX , " cacheex: added missing remote node id % " PRIu64 " X " , cacheex_node_id ( data ) ) ;
}
# ifdef CS_CACHEEX_AIO
if ( er - > cwc_cycletime & & er - > cwc_next_cw_cycle < 2 )
{
if ( cl - > typ = = ' c ' & & cl - > account & & cl - > account - > cacheex . mode )
{ cl - > account - > cwc_info + + ; }
else if ( ( cl - > typ = = ' p ' | | cl - > typ = = ' r ' ) & & ( cl - > reader & & cl - > reader - > cacheex . mode ) )
{ cl - > cwc_info + + ; }
cs_log_dbg ( D_CWC , " CWC (CE) received from %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X " , username ( cl ) , er - > cwc_cycletime , er - > cwc_next_cw_cycle , er - > caid , er - > prid , er - > srvid ) ;
}
# endif
cacheex_add_to_cache ( cl , er ) ;
}
void camd35_cacheex_recv_ce1_cwc_info ( struct s_client * cl , uint8_t * buf , int32_t idx )
{
if ( ! ( buf [ 0 ] = = 0x01 & & buf [ 18 ] < 0xFF & & buf [ 18 ] > 0x00 ) ) // cwc info ; normal camd3 ecms send 0xFF but we need no cycletime of 255 ;)
return ;
ECM_REQUEST * er = NULL ;
int32_t i ;
for ( i = 0 ; i < cfg . max_pending ; i + + )
{
if ( cl - > ecmtask [ i ] . idx = = idx )
{
er = & cl - > ecmtask [ i ] ;
break ;
}
}
if ( ! er )
{ return ; }
int8_t rc = buf [ 3 ] ;
if ( rc ! = E_FOUND )
{ return ; }
if ( buf [ 18 ] )
{
if ( buf [ 18 ] & ( 0x01 < < 7 ) )
{
er - > cwc_cycletime = ( buf [ 18 ] & 0x7F ) ; // remove bit 8 to get cycletime
er - > parent - > cwc_cycletime = er - > cwc_cycletime ;
er - > cwc_next_cw_cycle = 1 ;
er - > parent - > cwc_next_cw_cycle = er - > cwc_next_cw_cycle ;
}
else
{
er - > cwc_cycletime = buf [ 18 ] ;
er - > parent - > cwc_cycletime = er - > cwc_cycletime ;
er - > cwc_next_cw_cycle = 0 ;
er - > parent - > cwc_next_cw_cycle = er - > cwc_next_cw_cycle ;
}
}
if ( cl - > typ = = ' c ' & & cl - > account & & cl - > account - > cacheex . mode )
{ cl - > account - > cwc_info + + ; }
else if ( ( cl - > typ = = ' p ' | | cl - > typ = = ' r ' ) & & ( cl - > reader & & cl - > reader - > cacheex . mode ) )
{ cl - > cwc_info + + ; }
cs_log_dbg ( D_CWC , " CWC (CE1) received from %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X " , username ( cl ) , er - > cwc_cycletime , er - > cwc_next_cw_cycle , er - > caid , er - > prid , er - > srvid ) ;
}
/**
* when a server client connects
*/
static void camd35_server_client_init ( struct s_client * cl )
{
if ( ! cl - > init_done )
{
cl - > cacheex_needfilter = 1 ;
}
}
/**
* store received remote id
*/
static void camd35_cacheex_push_receive_remote_id ( struct s_client * cl , uint8_t * buf )
{
2026-02-23 16:40:08 +00:00
memcpy ( cl - > ncd_skey , buf + 20 , 8 ) ;
2026-02-16 09:02:48 +00:00
cl - > ncd_skey [ 8 ] = 1 ;
2026-02-23 16:40:08 +00:00
cs_log_dbg ( D_CACHEEX , " cacheex: received id answer from %s: % " PRIu64 " X " , username ( cl ) , cacheex_node_id ( cl - > ncd_skey ) ) ;
2026-02-16 09:02:48 +00:00
}
void camd35_cacheex_init_dcw ( struct s_client * client , ECM_REQUEST * er )
{
uint8_t * buf = er - > src_data ; // get orig request
if ( ( ( client - > typ = = ' c ' & & client - > account & & client - > account - > cacheex . mode )
| | ( ( client - > typ = = ' p ' | | client - > typ = = ' r ' ) & & ( client - > reader & & client - > reader - > cacheex . mode ) ) )
& & er - > cwc_cycletime & & er - > cwc_next_cw_cycle < 2 ) // ce1
{
buf [ 18 ] = er - > cwc_cycletime ; // contains cwc stage3 cycletime
if ( er - > cwc_next_cw_cycle = = 1 )
{ buf [ 18 ] = ( buf [ 18 ] | 0x80 ) ; } // set bit 8 to high
if ( client - > typ = = ' c ' & & client - > account & & client - > account - > cacheex . mode )
{ client - > account - > cwc_info + + ; }
else if ( ( client - > typ = = ' p ' | | client - > typ = = ' r ' ) & & ( client - > reader & & client - > reader - > cacheex . mode ) )
{ client - > cwc_info + + ; }
cs_log_dbg ( D_CWC , " CWC (CE1) push to %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X " , username ( client ) , er - > cwc_cycletime , er - > cwc_next_cw_cycle , er - > caid , er - > prid , er - > srvid ) ;
buf [ 19 ] = er - > ecm [ 0 ] ;
}
}
/**
* send own id
*/
void camd35_cacheex_push_send_own_id ( struct s_client * cl , uint8_t * mbuf )
{
2026-02-23 16:40:08 +00:00
uint8_t rbuf [ 32 ] ; //minimal size
2026-02-16 09:02:48 +00:00
if ( ! cl - > crypted ) { return ; }
cs_log_dbg ( D_CACHEEX , " cacheex: received id request from node % " PRIu64 " X %s " , cacheex_node_id ( mbuf + 20 ) , username ( cl ) ) ;
memset ( rbuf , 0 , sizeof ( rbuf ) ) ;
rbuf [ 0 ] = 0x3e ;
rbuf [ 1 ] = 12 ;
rbuf [ 2 ] = 0 ;
memcpy ( rbuf + 20 , camd35_node_id , 8 ) ;
cs_log_dbg ( D_CACHEEX , " cacheex: sending own id % " PRIu64 " X request %s " , cacheex_node_id ( camd35_node_id ) , username ( cl ) ) ;
2026-02-23 16:40:08 +00:00
camd35_send ( cl , rbuf , 12 ) ; //send adds +20
2026-02-16 09:02:48 +00:00
}
bool camd35_cacheex_server ( struct s_client * client , uint8_t * mbuf )
{
switch ( mbuf [ 0 ] )
{
2026-02-23 16:40:08 +00:00
case 0x3c : // Cache-push filter request
2026-02-16 09:02:48 +00:00
if ( client - > account & & client - > account - > cacheex . mode = = 2 ) {
camd35_cacheex_push_filter ( client , mbuf , 2 ) ;
}
break ;
2026-02-23 16:40:08 +00:00
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!
2026-02-16 09:02:48 +00:00
camd35_cacheex_push_send_own_id ( client , mbuf ) ;
if ( client - > cacheex_needfilter & & client - > account & & client - > account - > cacheex . mode = = 3 ) {
camd35_cacheex_send_push_filter ( client , 3 ) ;
client - > cacheex_needfilter = 0 ;
}
# ifdef CS_CACHEEX_AIO
if ( ! client - > cacheex_aio_checked & & ( ( client - > account & & client - > account - > cacheex . mode > 0 ) | | ( client - > reader & & client - > reader - > cacheex . mode > 0 ) ) )
{
camd35_cacheex_feature_request ( client ) ;
client - > cacheex_aio_checked = 1 ;
}
# endif
break ;
2026-02-23 16:40:08 +00:00
case 0x3e : // Cache-push id answer
2026-02-16 09:02:48 +00:00
camd35_cacheex_push_receive_remote_id ( client , mbuf ) ;
break ;
2026-02-23 16:40:08 +00:00
case 0x3f : // Cache-push
2026-02-16 09:02:48 +00:00
camd35_cacheex_push_in ( client , mbuf ) ;
break ;
# ifdef CS_CACHEEX_AIO
2026-02-23 16:40:08 +00:00
case 0x40 : // cacheex-features request
2026-02-16 09:02:48 +00:00
camd35_cacheex_feature_request_reply ( client , mbuf ) ;
break ;
2026-02-23 16:40:08 +00:00
case 0x41 : // cacheex-features answer
2026-02-16 09:02:48 +00:00
// camd35_cacheex_feature_request_save(client, mbuf);
break ;
2026-02-23 16:40:08 +00:00
case 0x42 : // cacheex-feature trigger in
2026-02-16 09:02:48 +00:00
camd35_cacheex_feature_trigger_in ( client , mbuf ) ;
break ;
# endif
default :
return 0 ; // Not processed by cacheex
}
return 1 ; // Processed by cacheex
}
bool camd35_cacheex_recv_chk ( struct s_client * client , uint8_t * buf )
{
struct s_reader * rdr = client - > reader ;
switch ( buf [ 0 ] )
{
2026-02-23 16:40:08 +00:00
case 0x3c : // Cache-push filter request
2026-02-16 09:02:48 +00:00
if ( rdr - > cacheex . mode = = 3 ) {
camd35_cacheex_push_filter ( client , buf , 3 ) ;
}
break ;
2026-02-23 16:40:08 +00:00
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!
2026-02-16 09:02:48 +00:00
camd35_cacheex_push_send_own_id ( client , buf ) ;
break ;
2026-02-23 16:40:08 +00:00
case 0x3e : // Cache-push id answer
2026-02-16 09:02:48 +00:00
camd35_cacheex_push_receive_remote_id ( client , buf ) ;
# ifdef CS_CACHEEX_AIO
if ( ! client - > cacheex_aio_checked & & ( ( client - > account & & client - > account - > cacheex . mode > 0 ) | | ( client - > reader & & client - > reader - > cacheex . mode > 0 ) ) )
{
camd35_cacheex_feature_request ( client ) ;
client - > cacheex_aio_checked = 1 ;
}
# endif
break ;
2026-02-23 16:40:08 +00:00
case 0x3f : //cache-push
2026-02-16 09:02:48 +00:00
camd35_cacheex_push_in ( client , buf ) ;
break ;
# ifdef CS_CACHEEX_AIO
2026-02-23 16:40:08 +00:00
case 0x40 : // cacheex-features request
2026-02-16 09:02:48 +00:00
camd35_cacheex_feature_request_reply ( client , buf ) ;
break ;
2026-02-23 16:40:08 +00:00
case 0x41 : // cacheex-features answer
2026-02-16 09:02:48 +00:00
// camd35_cacheex_feature_request_save(client, buf);
break ;
2026-02-23 16:40:08 +00:00
case 0x42 : // cacheex-feature trigger in
2026-02-16 09:02:48 +00:00
camd35_cacheex_feature_trigger_in ( client , buf ) ;
break ;
# endif
default :
return 0 ; // Not processed by cacheex
}
return 1 ; // Processed by cacheex
}
/**
* request remote id
*/
void camd35_cacheex_push_request_remote_id ( struct s_client * cl )
{
2026-02-23 16:40:08 +00:00
uint8_t rbuf [ 32 ] ; //minimal size
2026-02-16 09:02:48 +00:00
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 ) ) ;
2026-02-23 16:40:08 +00:00
camd35_send ( cl , rbuf , 12 ) ; //send adds +20
2026-02-16 09:02:48 +00:00
}
void camd35_cacheex_module_init ( struct s_module * ph )
{
ph - > c_cache_push = camd35_cacheex_push_out ;
ph - > c_cache_push_chk = camd35_cacheex_push_chk ;
ph - > s_init = camd35_server_client_init ;
}
# endif