2026-02-16 09:02:48 +00:00
# define MODULE_LOG_PREFIX "cccam"
# include "globals.h"
# include "oscam-array.h"
# if defined(CS_CACHEEX) && defined(MODULE_CCCAM)
# include "module-cacheex.h"
# include "module-cccam-data.h"
# include "module-cccam-cacheex.h"
# include "oscam-cache.h"
# include "oscam-client.h"
# include "oscam-ecm.h"
# include "oscam-string.h"
# include "oscam-chk.h"
# include "oscam-reader.h"
# ifdef CS_CACHEEX_AIO
# include "oscam-chk.h"
# include "oscam-config.h"
# endif
# define CSP_HASH_SWAP(n) (((((uint32_t)(n) & 0xFF)) << 24) | \
( ( ( ( uint32_t ) ( n ) & 0xFF00 ) ) < < 8 ) | \
( ( ( ( uint32_t ) ( n ) & 0xFF0000 ) ) > > 8 ) | \
( ( ( ( uint32_t ) ( n ) & 0xFF000000 ) ) > > 24 ) )
extern int32_t cc_cli_connect ( struct s_client * cl ) ;
extern int32_t cc_cmd_send ( struct s_client * cl , uint8_t * buf , int32_t len , cc_msg_type_t cmd ) ;
# ifdef CS_CACHEEX_AIO
void cc_cacheex_feature_trigger_in ( struct s_client * cl , uint8_t * buf )
{
int32_t feature = 0 ;
int i = 0 ;
uint8_t filter_count ;
uint8_t j , k , l , rc ;
feature = buf [ 1 ] | ( buf [ 0 ] < < 8 ) ;
FTAB * lgonly_tab ;
// check client & cacheex-mode
if (
! check_client ( cl ) | |
! (
( cl - > typ = = ' c ' & & cl - > account - > cacheex . mode > 0 ) | |
( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode > 0 )
)
)
{
return ;
}
switch ( feature )
{
// set localgenerated only
case 1 :
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) )
{
if ( cfg . cacheex_lg_only_remote_settings | | cl - > account - > cacheex . lg_only_remote_settings )
cl - > account - > cacheex . localgenerated_only = buf [ 4 ] ;
else if ( buf [ 4 ] )
cl - > account - > cacheex . localgenerated_only = buf [ 4 ] ;
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 )
{
if ( cfg . cacheex_lg_only_remote_settings | | cl - > reader - > cacheex . lg_only_remote_settings )
cl - > reader - > cacheex . localgenerated_only = buf [ 4 ] ;
else if ( buf [ 4 ] )
cl - > reader - > cacheex . localgenerated_only = buf [ 4 ] ;
}
break ;
// set localgenerated only caidtab
case 2 :
filter_count = buf [ i + 4 ] ;
i + = 5 ;
memset ( & lgonly_tab , 0 , sizeof ( lgonly_tab ) ) ;
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) )
{
lgonly_tab = & cl - > account - > cacheex . lg_only_tab ;
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 )
{
lgonly_tab = & cl - > reader - > cacheex . lg_only_tab ;
}
else
{
return ;
}
// remotesettings enabled - replace local settings
if ( cfg . cacheex_lg_only_remote_settings | |
(
( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) & & cl - > account - > cacheex . lg_only_remote_settings )
| | ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 & & cl - > reader - > cacheex . lg_only_remote_settings )
)
)
{
ftab_clear ( lgonly_tab ) ;
for ( j = 0 ; j < filter_count ; j + + )
{
FILTER d ;
memset ( & d , 0 , sizeof ( d ) ) ;
d . caid = b2i ( 2 , buf + i ) ;
i + = 2 ;
d . nprids = 1 ;
d . prids [ 0 ] = NO_PROVID_VALUE ;
ftab_add ( lgonly_tab , & d ) ;
}
}
// remotesettings disabled - write additional remote-caids received
else
{
for ( j = 0 ; j < filter_count ; j + + )
{
FILTER d ;
memset ( & d , 0 , sizeof ( d ) ) ;
d . caid = b2i ( 2 , buf + i ) ;
i + = 2 ;
d . nprids = 1 ;
d . prids [ 0 ] = NO_PROVID_VALUE ;
if ( ! chk_lg_only_cp ( d . caid , d . prids [ 0 ] , lgonly_tab ) )
{
cs_log_dbg ( D_CACHEEX , " %04X:%06X not found in local settings - adding them " , d . caid , d . prids [ 0 ] ) ;
for ( l = rc = 0 ; ( ! rc ) & & ( l < lgonly_tab - > nfilts ) ; l + + )
{
if ( lgonly_tab - > filts [ l ] . caid = = d . caid )
{
rc = 1 ;
if ( lgonly_tab - > filts [ l ] . nprids + 1 < = CS_MAXPROV )
{
lgonly_tab - > filts [ l ] . prids [ lgonly_tab - > filts [ l ] . nprids ] = d . prids [ 0 ] ;
lgonly_tab - > filts [ l ] . nprids + + ;
}
else
{
cs_log_dbg ( D_CACHEEX , " error: cacheex_lg_only_tab -> max. number(%i) of providers reached " , CS_MAXPROV ) ;
}
}
}
if ( ! rc )
{
ftab_add ( lgonly_tab , & d ) ;
}
}
}
}
break ;
// set cacheex_ecm_filter - extended
case 4 :
filter_count = buf [ i + 4 ] ;
i + = 5 ;
CECSPVALUETAB * filter ;
memset ( & filter , 0 , sizeof ( filter ) ) ;
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) & & cl - > account - > cacheex . allow_filter )
{
filter = & cl - > account - > cacheex . filter_caidtab ;
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 & & cl - > reader - > cacheex . allow_filter )
{
filter = & cl - > reader - > cacheex . filter_caidtab ;
}
else
{
return ;
}
cecspvaluetab_clear ( filter ) ;
for ( j = 0 ; j < filter_count ; j + + )
{
int32_t caid = - 1 , cmask = - 1 , provid = - 1 , srvid = - 1 ;
CECSPVALUETAB_DATA d ;
memset ( & d , 0 , sizeof ( d ) ) ;
caid = b2i ( 2 , buf + i ) ;
if ( caid = = 0xFFFF ) caid = - 1 ;
i + = 2 ;
cmask = b2i ( 2 , buf + i ) ;
if ( cmask = = 0xFFFF ) cmask = - 1 ;
i + = 2 ;
provid = b2i ( 3 , buf + i ) ;
if ( provid = = 0xFFFFFF ) provid = - 1 ;
i + = 3 ;
srvid = b2i ( 2 , buf + i ) ;
if ( srvid = = 0xFFFF ) srvid = - 1 ;
i + = 2 ;
if ( caid > 0 )
{
d . caid = caid ;
d . cmask = cmask ;
d . prid = provid ;
d . srvid = srvid ;
cecspvaluetab_add ( filter , & d ) ;
}
}
break ;
// no push after
case 8 : ;
CAIDVALUETAB * ctab ;
memset ( & ctab , 0 , sizeof ( ctab ) ) ;
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) )
{
ctab = & cl - > account - > cacheex . cacheex_nopushafter_tab ;
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 )
{
ctab = & cl - > reader - > cacheex . cacheex_nopushafter_tab ;
}
else
{
return ;
}
filter_count = buf [ i + 4 ] ;
i + = 5 ;
caidvaluetab_clear ( ctab ) ;
for ( j = 0 ; j < filter_count ; j + + )
{
uint16_t caid = 0 , value = 0 ;
CAIDVALUETAB_DATA d ;
memset ( & d , 0 , sizeof ( d ) ) ;
caid = b2i ( 2 , buf + i ) ;
if ( caid = = 0xFFFF ) caid = - 1 ;
i + = 2 ;
value = b2i ( 2 , buf + i ) ;
if ( value = = 0xFFFF ) value = - 1 ;
i + = 2 ;
if ( caid > 0 )
{
d . caid = caid ;
d . value = value ;
caidvaluetab_add ( ctab , & d ) ;
}
}
break ;
// max_hop
case 16 :
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) & & cl - > account - > cacheex . allow_maxhop )
{
cl - > account - > cacheex . maxhop = buf [ 4 ] ;
cl - > account - > cacheex . maxhop_lg = buf [ 5 ] ;
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 & & cl - > reader - > cacheex . allow_maxhop )
{
cl - > reader - > cacheex . maxhop = buf [ 4 ] ;
cl - > reader - > cacheex . maxhop_lg = buf [ 5 ] ;
}
break ;
// aio-version
case 32 : ;
uint16_t payload_size = b2i ( 2 , buf + i + 2 ) ;
if ( cl - > typ = = ' c ' & & cl - > account - > cacheex . mode > 0 )
{
char * ofs = ( char * ) buf + i + 4 ;
memset ( cl - > account - > cacheex . aio_version , 0 , sizeof ( cl - > account - > cacheex . aio_version ) ) ;
if ( payload_size > 0 )
{
size_t str_len = strnlen ( ofs , payload_size ) ;
if ( str_len > = sizeof ( cl - > account - > cacheex . aio_version ) )
str_len = sizeof ( cl - > account - > cacheex . aio_version ) - 1 ;
memcpy ( cl - > account - > cacheex . aio_version , ofs , str_len ) ;
// sanitize: remove non-printable characters
size_t x ;
for ( x = 0 ; x < str_len ; x + + )
{
if ( cl - > account - > cacheex . aio_version [ x ] < 0x20 | | cl - > account - > cacheex . aio_version [ x ] > 0x7E )
{
cl - > account - > cacheex . aio_version [ x ] = ' \0 ' ;
break ;
}
}
}
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode > 0 )
{
char * ofs = ( char * ) buf + i + 4 ;
memset ( cl - > reader - > cacheex . aio_version , 0 , sizeof ( cl - > reader - > cacheex . aio_version ) ) ;
if ( payload_size > 0 )
{
size_t str_len = strnlen ( ofs , payload_size ) ;
if ( str_len > = sizeof ( cl - > reader - > cacheex . aio_version ) )
str_len = sizeof ( cl - > reader - > cacheex . aio_version ) - 1 ;
memcpy ( cl - > reader - > cacheex . aio_version , ofs , str_len ) ;
// sanitize: remove non-printable characters
size_t x ;
for ( x = 0 ; x < str_len ; x + + )
{
if ( cl - > reader - > cacheex . aio_version [ x ] < 0x20 | | cl - > reader - > cacheex . aio_version [ x ] > 0x7E )
{
cl - > reader - > cacheex . aio_version [ x ] = ' \0 ' ;
break ;
}
}
}
}
break ;
// lg_only_tab caid:prov1[,provN][;caid:prov]
case 64 : ;
memset ( & lgonly_tab , 0 , sizeof ( lgonly_tab ) ) ;
if ( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) )
{
lgonly_tab = & cl - > account - > cacheex . lg_only_tab ;
}
else if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 )
{
lgonly_tab = & cl - > reader - > cacheex . lg_only_tab ;
}
else
{
return ;
}
filter_count = buf [ i + 4 ] ;
i + = 5 ;
// remotesettings enabled - replace local settings
if ( cfg . cacheex_lg_only_remote_settings | |
(
( cl - > typ = = ' c ' & & ( cl - > account - > cacheex . mode = = 2 | | cl - > account - > cacheex . mode = = 1 ) & & cl - > account - > cacheex . lg_only_remote_settings )
| | ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 & & cl - > reader - > cacheex . lg_only_remote_settings )
| | ! lgonly_tab - > nfilts
)
)
{
ftab_clear ( lgonly_tab ) ;
for ( j = 0 ; j < filter_count ; j + + )
{
FILTER d ;
memset ( & d , 0 , sizeof ( d ) ) ;
d . caid = b2i ( 2 , buf + i ) ;
i + = 2 ;
d . nprids = b2i ( 1 , buf + i ) ;
i + = 1 ;
for ( k = 0 ; k < d . nprids ; k + + )
{
d . prids [ k ] = b2i ( 3 , buf + i ) ;
i + = 3 ;
}
ftab_add ( lgonly_tab , & d ) ;
}
}
// remotesettings disabled - write additional remote-caid/provids received
else
{
for ( j = 0 ; j < filter_count ; j + + )
{
FILTER d ;
memset ( & d , 0 , sizeof ( d ) ) ;
d . caid = b2i ( 2 , buf + i ) ;
i + = 2 ;
d . nprids = b2i ( 1 , buf + i ) ;
i + = 1 ;
for ( k = 0 ; k < d . nprids ; k + + )
{
d . prids [ k ] = b2i ( 3 , buf + i ) ;
i + = 3 ;
if ( ! chk_ident_filter ( d . caid , d . prids [ k ] , lgonly_tab ) )
{
cs_log_dbg ( D_CACHEEX , " %04X:%06X not found in local settings - adding them " , d . caid , d . prids [ k ] ) ;
for ( l = rc = 0 ; ( ! rc ) & & ( l < lgonly_tab - > nfilts ) ; l + + )
{
if ( lgonly_tab - > filts [ l ] . caid = = d . caid )
{
rc = 1 ;
if ( lgonly_tab - > filts [ l ] . nprids + 1 < = CS_MAXPROV )
{
lgonly_tab - > filts [ l ] . prids [ lgonly_tab - > filts [ l ] . nprids ] = d . prids [ k ] ;
lgonly_tab - > filts [ l ] . nprids + + ;
}
else
{
cs_log_dbg ( D_CACHEEX , " error: cacheex_lg_only_tab -> max. number of providers reached " ) ;
}
}
}
if ( ! rc )
{
ftab_add ( lgonly_tab , & d ) ;
}
}
}
}
}
break ;
default :
return ;
}
}
void cc_cacheex_feature_trigger ( struct s_client * cl , int32_t feature , uint8_t mode )
{
// size: (feature-bitfield & mask: 2) + payload-size: 2 + feature-payload :x
uint16_t size = 2 + 2 ;
int i = 0 ;
uint8_t j ;
uint8_t payload [ MAX_ECM_SIZE - size ] ;
memset ( payload , 0 , sizeof ( payload ) ) ;
// check client & cacheex-mode
if ( ! check_client ( cl ) )
{
return ;
}
switch ( feature )
{
FTAB * lgonly_tab ;
// set localgenerated only
case 1 :
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
// payload-size
i2b_buf ( 2 , 1 , payload + i ) ;
i + = 2 ;
size + = 1 ;
// set payload
if ( mode = = 2 )
{
if ( cl - > reader - > cacheex . localgenerated_only_in )
payload [ i ] = cl - > reader - > cacheex . localgenerated_only_in ;
else
payload [ i ] = cfg . cacheex_localgenerated_only_in ;
}
else if ( mode = = 3 )
{
if ( cl - > account - > cacheex . localgenerated_only_in )
payload [ i ] = cl - > account - > cacheex . localgenerated_only_in ;
else
payload [ i ] = cfg . cacheex_localgenerated_only_in ;
}
break ;
// set localgenerated only caidtab; cx-aio < 9.2.6-04
case 2 : ;
if ( mode = = 2 )
{
lgonly_tab = & cl - > reader - > cacheex . lg_only_in_tab ;
if ( ! lgonly_tab - > nfilts )
lgonly_tab = & cfg . cacheex_lg_only_in_tab ;
}
else if ( mode = = 3 )
{
lgonly_tab = & cl - > account - > cacheex . lg_only_in_tab ;
if ( ! lgonly_tab - > nfilts )
lgonly_tab = & cfg . cacheex_lg_only_in_tab ;
}
else
{
return ;
}
size + = ( lgonly_tab - > nfilts * 2 + 1 ) ;
if ( size < 32 )
size = 32 ;
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
// payload-size
if ( ( lgonly_tab - > nfilts * 2 + 1 ) > ( int ) sizeof ( payload ) )
{
cs_log_dbg ( D_CACHEEX , " ERROR: too much localgenerated only caidtab-entries (max. 255) " ) ;
return ;
}
i2b_buf ( 2 , ( lgonly_tab - > nfilts * 2 + 1 ) , payload + i ) ; // n * caid + ctnum
i + = 2 ;
// set payload
if ( lgonly_tab - > nfilts > 255 )
{
cs_log_dbg ( D_CACHEEX , " ERROR: too much localgenerated only caidtab-entries (max. 255) " ) ;
return ;
}
payload [ i ] = lgonly_tab - > nfilts ;
i + = 1 ;
for ( j = 0 ; j < lgonly_tab - > nfilts ; j + + )
{
FILTER * d = & lgonly_tab - > filts [ j ] ;
if ( d - > caid )
{
i2b_buf ( 2 , d - > caid , payload + i ) ;
i + = 2 ;
}
else
{
continue ;
}
}
break ;
// cacchex_ecm_filter extendend
case 4 : ;
CECSPVALUETAB * filter ;
if ( mode = = 2 )
{
filter = & cl - > reader - > cacheex . filter_caidtab ;
// if not set, use global settings
if ( cl - > reader - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab . cevnum > 0 )
filter = & cfg . cacheex_filter_caidtab ;
// if aio, use global aio settings
if ( cl - > reader - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab_aio . cevnum > 0 & & cl - > cacheex_aio_checked & & ( cl - > reader - > cacheex . feature_bitfield & 4 ) )
filter = & cfg . cacheex_filter_caidtab_aio ;
}
else if ( mode = = 3 )
{
filter = & cl - > account - > cacheex . filter_caidtab ;
// if not set, use global settings
if ( cl - > account - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab . cevnum > 0 )
filter = & cfg . cacheex_filter_caidtab ;
// if aio, use global aio settings
if ( cl - > account - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab_aio . cevnum > 0 & & cl - > cacheex_aio_checked & & ( cl - > account - > cacheex . feature_bitfield & 4 ) )
filter = & cfg . cacheex_filter_caidtab_aio ;
}
else
{
return ;
}
size + = ( filter - > cevnum * 9 + 1 ) ;
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
// payload-size
if ( ( filter - > cevnum * 9 + 1 ) > ( int ) sizeof ( payload ) )
{
cs_log_dbg ( D_CACHEEX , " ERROR: to much cacheex_ecm_filter-entries (max. 63), only 30 default cccam-filters sent " ) ;
return ;
}
i2b_buf ( 2 , ( filter - > cevnum * 9 + 1 ) , payload + i ) ; // n * (caid2,mask2,provid3,srvid2) + ctnum1
i + = 2 ;
// set payload
payload [ i ] = filter - > cevnum ;
i + = 1 ;
for ( j = 0 ; j < filter - > cevnum ; j + + )
{
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
if ( d - > caid )
{
i2b_buf ( 2 , d - > caid , payload + i ) ;
i + = 2 ;
}
if ( d - > cmask )
{
i2b_buf ( 2 , d - > cmask , payload + i ) ;
}
i + = 2 ;
if ( d - > prid )
{
i2b_buf ( 3 , d - > prid , payload + i ) ;
}
i + = 3 ;
if ( d - > srvid )
{
i2b_buf ( 2 , d - > srvid , payload + i ) ;
}
i + = 2 ;
}
break ;
// no push after
case 8 : ;
CAIDVALUETAB * ctab ;
if ( mode = = 2 )
{
ctab = & cl - > reader - > cacheex . cacheex_nopushafter_tab ;
if ( ! ctab - > cvnum )
ctab = & cfg . cacheex_nopushafter_tab ;
}
else if ( mode = = 3 )
{
ctab = & cl - > account - > cacheex . cacheex_nopushafter_tab ;
if ( ! ctab - > cvnum )
ctab = & cfg . cacheex_nopushafter_tab ;
}
else
{
return ;
}
size + = ( ctab - > cvnum * 4 + 1 ) ;
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
// payload-size
if ( ( ctab - > cvnum * 4 + 1 ) > ( int ) sizeof ( payload ) )
{
cs_log_dbg ( D_CACHEEX , " ERROR: to much no push after caidtvalueab-entries (max. 255) " ) ;
return ;
}
i2b_buf ( 2 , ( ctab - > cvnum * 4 + 1 ) , payload + i ) ; // n * (caid2+value2) + cvnum
i + = 2 ;
// set payload
if ( ctab - > cvnum > 255 )
{
cs_log_dbg ( D_CACHEEX , " ERROR: to much no push after caidtvalueab-entries (max. 255) " ) ;
return ;
}
payload [ i ] = ctab - > cvnum ;
i + = 1 ;
for ( j = 0 ; j < ctab - > cvnum ; j + + )
{
CAIDVALUETAB_DATA * d = & ctab - > cvdata [ j ] ;
if ( d - > caid )
{
i2b_buf ( 2 , d - > caid , payload + i ) ;
i + = 2 ;
i2b_buf ( 2 , d - > value , payload + i ) ;
i + = 2 ;
}
else
{
continue ;
}
}
break ;
// max hop
case 16 :
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
// payload-size
i2b_buf ( 2 , 2 , payload + i ) ;
i + = 2 ;
size + = 2 ;
// set payload
if ( mode = = 2 )
{
if ( cl - > reader - > cacheex . maxhop )
payload [ i ] = cl - > reader - > cacheex . maxhop ;
else
payload [ i ] = 0 ;
i + = 1 ;
if ( cl - > reader - > cacheex . maxhop_lg )
payload [ i ] = cl - > reader - > cacheex . maxhop_lg ;
else
payload [ i ] = 0 ;
}
else if ( mode = = 3 )
{
if ( cl - > account - > cacheex . maxhop )
payload [ i ] = cl - > account - > cacheex . maxhop ;
else
payload [ i ] = 0 ;
i + = 1 ;
if ( cl - > account - > cacheex . maxhop_lg )
payload [ i ] = cl - > account - > cacheex . maxhop_lg ;
else
payload [ i ] = 0 ;
}
break ;
// aio-version
case 32 : ;
uint8_t token [ CS_AIO_VERSION_LEN ] ;
memset ( token , 0 , sizeof ( token ) ) ;
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
// payload-size
i2b_buf ( 2 , sizeof ( token ) , payload + i ) ;
i + = 2 ;
size + = sizeof ( token ) ;
// set payload
snprintf ( ( char * ) token , sizeof ( token ) , " %s " , CS_AIO_VERSION ) ;
uint8_t * ofs = payload + i ;
memcpy ( ofs , token , sizeof ( token ) ) ;
break ;
// lg_only_tab
case 64 : ;
// bitfield
i2b_buf ( 2 , feature , payload + i ) ;
i + = 2 ;
if ( mode = = 2 )
{
lgonly_tab = & cl - > reader - > cacheex . lg_only_in_tab ;
if ( ! lgonly_tab - > nfilts )
lgonly_tab = & cfg . cacheex_lg_only_in_tab ;
}
else if ( mode = = 3 )
{
lgonly_tab = & cl - > account - > cacheex . lg_only_in_tab ;
if ( ! lgonly_tab - > nfilts )
lgonly_tab = & cfg . cacheex_lg_only_in_tab ;
}
else
{
return ;
}
char * cx_aio_ftab ;
cx_aio_ftab = cxaio_ftab_to_buf ( lgonly_tab ) ;
if ( cs_strlen ( cx_aio_ftab ) > 0 & & cx_aio_ftab [ 0 ] ! = ' \0 ' )
{
size + = cs_strlen ( cx_aio_ftab ) * sizeof ( char ) ;
// payload-size
i2b_buf ( 2 , cs_strlen ( cx_aio_ftab ) , payload + i ) ;
i + = 2 ;
// filter counter
payload [ i ] = lgonly_tab - > nfilts ;
i + = 1 ;
for ( j = 0 ; 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 ) ) ;
memcpy ( buf , payload , size ) ;
cc_cmd_send ( cl , payload , size , MSG_CACHE_FEATURE_TRIGGER ) ;
}
void cc_cacheex_feature_request_save ( struct s_client * cl , uint8_t * buf )
{
int32_t field = b2i ( 2 , buf ) ;
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 )
{
cc_cacheex_feature_trigger ( cl , 32 , 2 ) ;
}
}
if ( cl - > typ = = ' p ' & & cl - > reader - > cacheex . mode = = 3 )
{
cl - > reader - > cacheex . feature_bitfield = field ;
// flag 32 => aio-version
if ( cl - > reader - > cacheex . feature_bitfield & 32 )
{
cc_cacheex_feature_trigger ( cl , 32 , 3 ) ;
}
}
if ( cl - > typ = = ' c ' & & cl - > account - > cacheex . mode = = 3 )
{
struct s_auth * acc = cl - > account ;
if ( acc )
{
acc - > cacheex . feature_bitfield = field ;
// process feature-specific actions based on feature_bitfield received
// flag 1 => set localgenerated only flag
if ( acc - > cacheex . feature_bitfield & 1 )
{
cc_cacheex_feature_trigger ( cl , 1 , 3 ) ;
}
// flag 2 => set localgenerated only caids flag
if ( acc - > cacheex . feature_bitfield & 2 & & ! ( acc - > cacheex . feature_bitfield & 64 ) )
{
cc_cacheex_feature_trigger ( cl , 2 , 3 ) ;
}
// flag 4 => set cacheex_ecm_filter (extended)
if ( acc - > cacheex . feature_bitfield & 4 )
{
cc_cacheex_feature_trigger ( cl , 4 , 3 ) ;
}
// flag 8 => np push after caids
if ( acc - > cacheex . feature_bitfield & 8 )
{
cc_cacheex_feature_trigger ( cl , 8 , 3 ) ;
}
// flag 16 => maxhop
if ( acc - > cacheex . feature_bitfield & 16 )
{
cc_cacheex_feature_trigger ( cl , 16 , 3 ) ;
}
// flag 32 => aio-version
if ( acc - > cacheex . feature_bitfield & 32 )
{
cc_cacheex_feature_trigger ( cl , 32 , 3 ) ;
}
// flag 64 => lg_only_tab
if ( acc - > cacheex . feature_bitfield & 64 )
{
cc_cacheex_feature_trigger ( cl , 64 , 3 ) ;
}
}
else
{
cs_log_dbg ( D_CACHEEX , " feature_bitfield save failed - cl, %s " , username ( cl ) ) ;
}
}
else if ( cl - > typ = = ' p ' & & ( cl - > reader - > cacheex . mode = = 2 | | cl - > reader - > cacheex . mode = = 1 ) )
{
struct s_reader * rdr = cl - > reader ;
if ( rdr )
{
rdr - > cacheex . feature_bitfield = field ;
// process feature-specific actions
// flag 1 => set localgenerated_only; cause of rdr->cacheex.localgenerated_only_in is set
if ( rdr - > cacheex . feature_bitfield & 1 )
{
cc_cacheex_feature_trigger ( cl , 1 , 2 ) ;
}
// flag 2 => set localgenerated_only_caidtab; cause of rdr->cacheex.localgenerated_only_in_caidtab is set
if ( rdr - > cacheex . feature_bitfield & 2 & & ! ( rdr - > cacheex . feature_bitfield & 64 ) )
{
cc_cacheex_feature_trigger ( cl , 2 , 2 ) ;
}
// flag 4 => set cacchex_ecm_filter extendend
if ( rdr - > cacheex . feature_bitfield & 4 )
{
cc_cacheex_feature_trigger ( cl , 4 , 2 ) ;
}
// flag 8 => np push after caids
if ( rdr - > cacheex . feature_bitfield & 8 )
{
cc_cacheex_feature_trigger ( cl , 8 , 2 ) ;
}
// flag 16 => maxhop
if ( rdr - > cacheex . feature_bitfield & 16 )
{
cc_cacheex_feature_trigger ( cl , 16 , 2 ) ;
}
// flag 32 => aio-version
if ( rdr - > cacheex . feature_bitfield & 32 )
{
cc_cacheex_feature_trigger ( cl , 32 , 2 ) ;
}
// flag 64 => lg_only_tab
if ( rdr - > cacheex . feature_bitfield & 64 )
{
cc_cacheex_feature_trigger ( cl , 64 , 2 ) ;
}
}
else
{
cs_log_dbg ( D_CACHEEX , " feature_bitfield save failed - rdr, %s " , username ( cl ) ) ;
}
}
}
void cc_cacheex_feature_request_reply ( struct s_client * cl )
{
int32_t size = 2 ;
uint8_t rbuf [ size ] ;
i2b_buf ( 2 , CACHEEX_FEATURES , rbuf ) ;
cc_cmd_send ( cl , rbuf , size , MSG_CACHE_FEATURE_EXCHANGE_REPLY ) ;
}
void cc_cacheex_feature_request ( struct s_client * cl )
{
int32_t size = 2 ;
uint8_t rbuf [ 2 ] ;
i2b_buf ( 2 , CACHEEX_FEATURES , rbuf ) ;
cc_cmd_send ( cl , rbuf , size , MSG_CACHE_FEATURE_EXCHANGE ) ;
}
# endif
void cc_cacheex_filter_out ( struct s_client * cl )
{
struct s_reader * rdr = ( cl - > typ = = ' c ' ) ? NULL : cl - > reader ;
int i = 0 , j ;
CECSPVALUETAB * filter ;
int32_t size = 482 ; // minimal size, keep it <= 512 for max UDP packet size without fragmentation
uint8_t buf [ 482 ] ;
memset ( buf , 0 , sizeof ( buf ) ) ;
if ( rdr & & ( rdr - > cacheex . mode = = 2
# ifdef CS_CACHEEX_AIO
| | rdr - > cacheex . mode = = 1
# endif
) ) // mode == 2 send filters from rdr
{
filter = & rdr - > cacheex . filter_caidtab ;
# ifdef CS_CACHEEX_AIO
// if not set, use global settings
if ( rdr - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab . cevnum > 0 )
filter = & cfg . cacheex_filter_caidtab ;
# endif
}
else if ( cl - > typ = = ' c ' & & cl - > account & & cl - > account - > cacheex . mode = = 3 ) // mode == 3 send filters from acc
{
filter = & cl - > account - > cacheex . filter_caidtab ;
# ifdef CS_CACHEEX_AIO
// if not set, use global settings
if ( cl - > account - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab . cevnum > 0 )
filter = & cfg . cacheex_filter_caidtab ;
# endif
}
else
{
return ;
}
i2b_buf ( 2 , filter - > cevnum , buf + i ) ;
i + = 2 ;
int32_t max_filters = 30 ;
for ( j = 0 ; j < max_filters ; j + + )
{
if ( filter - > cevnum > j )
{
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
i2b_buf ( 4 , d - > caid , buf + i ) ;
}
i + = 4 ;
}
for ( j = 0 ; j < max_filters ; j + + )
{
if ( filter - > cevnum > j )
{
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
i2b_buf ( 4 , d - > cmask , buf + i ) ;
}
i + = 4 ;
}
for ( j = 0 ; j < max_filters ; j + + )
{
if ( filter - > cevnum > j )
{
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
i2b_buf ( 4 , d - > prid , buf + i ) ;
}
i + = 4 ;
}
for ( j = 0 ; j < max_filters ; j + + )
{
if ( filter - > cevnum > j )
{
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
i2b_buf ( 4 , d - > srvid , buf + i ) ;
}
i + = 4 ;
}
cs_log_dbg ( D_CACHEEX , " cacheex: sending push filter request to %s " , username ( cl ) ) ;
cc_cmd_send ( cl , buf , size , MSG_CACHE_FILTER ) ;
}
void cc_cacheex_filter_in ( struct s_client * cl , uint8_t * buf )
{
struct s_reader * rdr = ( cl - > typ = = ' c ' ) ? NULL : cl - > reader ;
int i = 0 , j ;
int32_t caid , cmask , provid , srvid ;
CECSPVALUETAB * filter ;
// mode == 2 write filters to acc
if ( cl - > typ = = ' c ' & & cl - > account & & ( cl - > account - > cacheex . mode = = 2
# ifdef CS_CACHEEX_AIO
| | cl - > account - > cacheex . mode = = 1
# endif
) & & cl - > account - > cacheex . allow_filter = = 1 )
{
filter = & cl - > account - > cacheex . filter_caidtab ;
}
else if ( rdr & & rdr - > cacheex . mode = = 3 & & rdr - > cacheex . allow_filter = = 1 ) // mode == 3 write filters to rdr
{
filter = & rdr - > cacheex . filter_caidtab ;
}
else
{
return ;
}
cecspvaluetab_clear ( filter ) ;
i + = 2 ;
int32_t max_filters = 30 ;
for ( j = 0 ; j < max_filters ; j + + )
{
caid = b2i ( 4 , buf + i ) ;
if ( caid > 0 )
{
CECSPVALUETAB_DATA d ;
memset ( & d , 0 , sizeof ( d ) ) ;
d . caid = b2i ( 4 , buf + i ) ;
cecspvaluetab_add ( filter , & d ) ;
}
i + = 4 ;
}
for ( j = 0 ; j < max_filters ; j + + )
{
cmask = b2i ( 4 , buf + i ) ;
if ( j < filter - > cevnum )
{
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
d - > cmask = cmask ;
}
i + = 4 ;
}
for ( j = 0 ; j < max_filters ; j + + )
{
provid = b2i ( 4 , buf + i ) ;
if ( j < filter - > cevnum )
{
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
d - > prid = provid ;
}
i + = 4 ;
}
for ( j = 0 ; j < max_filters ; j + + )
{
srvid = b2i ( 4 , buf + i ) ;
if ( j < filter - > cevnum )
{
CECSPVALUETAB_DATA * d = & filter - > cevdata [ j ] ;
d - > srvid = srvid ;
}
i + = 4 ;
}
cs_log_dbg ( D_CACHEEX , " cacheex: received push filter request from %s " , username ( cl ) ) ;
}
static int32_t cc_cacheex_push_chk ( struct s_client * cl , struct ecm_request_t * er )
{
struct cc_data * cc = cl - > cc ;
if ( chk_is_null_nodeid ( cc - > peer_node_id ) )
{
cs_log_dbg ( D_CACHEEX , " cacheex: NO peer_node_id got yet, skip! " ) ;
return 0 ;
}
if (
2026-02-17 09:41:05 +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-17 09:41:05 +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-17 09:41:05 +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-17 09:41:05 +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 ;
}
uint8_t * remote_node = cc - > peer_node_id ;
// search existing peer nodes
LL_LOCKITER * li = ll_li_create ( er - > csp_lastnodes , 0 ) ;
uint8_t * node ;
while ( ( node = ll_li_next ( li ) ) )
{
cs_log_dbg ( D_CACHEEX , " cacheex: check node % " PRIu64 " X == % " PRIu64 " X ? " ,
cacheex_node_id ( node ) , cacheex_node_id ( remote_node ) ) ;
if ( memcmp ( node , remote_node , 8 ) = = 0 )
{
break ;
}
}
ll_li_destroy ( li ) ;
// node found, so we got it from there, do not push
if ( node )
{
cs_log_dbg ( D_CACHEEX , " cacheex: node % " PRIu64 " X found in list => skip push! " , cacheex_node_id ( node ) ) ;
return 0 ;
}
if ( ! cl - > cc )
{
if ( cl - > reader & & ! cl - > reader - > tcp_connected )
{
cc_cli_connect ( cl ) ;
}
}
if ( ! cc | | ! cl - > udp_fd )
{
cs_log_dbg ( D_CACHEEX , " cacheex: not connected %s -> no push " , username ( cl ) ) ;
return 0 ;
}
// check if cw is already pushed
if ( check_is_pushed ( er - > cw_cache , cl ) )
{
return 0 ;
}
return 1 ;
}
static int32_t cc_cacheex_push_out ( struct s_client * cl , struct ecm_request_t * er )
{
int8_t rc = ( er - > rc < E_NOTFOUND ) ? E_FOUND : er - > rc ;
if ( rc ! = E_FOUND & & rc ! = E_UNHANDLED )
{
return - 1 ; // Maybe later we could support other rcs
}
if ( cl - > reader )
{
if ( ! cl - > reader - > tcp_connected )
{
cc_cli_connect ( cl ) ;
}
}
struct cc_data * cc = cl - > cc ;
if ( ! cc | | ! cl - > udp_fd )
{
cs_log_dbg ( D_CACHEEX , " cacheex: not connected %s -> no push " , username ( cl ) ) ;
return ( - 1 ) ;
}
uint32_t size = sizeof ( er - > ecmd5 ) + sizeof ( er - > csp_hash ) + sizeof ( er - > cw ) + sizeof ( uint8_t ) +
# ifdef CS_CACHEEX_AIO
( ll_count ( er - > csp_lastnodes ) + 1 ) * 8 + sizeof ( uint8_t ) ;
# else
( ll_count ( er - > csp_lastnodes ) + 1 ) * 8 ;
# endif
uint8_t * buf ;
if ( ! cs_malloc ( & buf , size + 20 ) ) // camd35_send() adds +20
{
return - 1 ;
}
// build ecm message
//buf[0] = er->caid >> 8;
//buf[1] = er->caid & 0xff;
//buf[2] = er->prid >> 24;
//buf[3] = er->prid >> 16;
//buf[4] = er->prid >> 8;
//buf[5] = er->prid & 0xff;
//buf[10] = er->srvid >> 8;
//buf[11] = er->srvid & 0xff;
buf [ 12 ] = ( sizeof ( er - > ecmd5 ) + sizeof ( er - > csp_hash ) + sizeof ( er - > cw ) ) & 0xff ;
buf [ 13 ] = ( sizeof ( er - > ecmd5 ) + sizeof ( er - > csp_hash ) + sizeof ( er - > cw ) ) > > 8 ;
//buf[12] = 0;
//buf[13] = 0;
buf [ 14 ] = rc ;
i2b_buf ( 2 , er - > caid , buf + 0 ) ;
i2b_buf ( 4 , er - > prid , buf + 2 ) ;
i2b_buf ( 2 , er - > srvid , buf + 10 ) ;
if ( er - > cwc_cycletime & & er - > cwc_next_cw_cycle < 2 )
{
buf [ 18 ] = er - > cwc_cycletime ; // contains cwc stage3 cycletime
if ( er - > cwc_next_cw_cycle = = 1 )
{
buf [ 18 ] = ( buf [ 18 ] | 0x80 ) ; // set bit 8 to high
}
if ( cl - > typ = = ' c ' & & cl - > account & & cl - > account - > cacheex . mode )
{
cl - > account - > cwc_info + + ;
}
else if ( ( cl - > typ = = ' p ' | | cl - > typ = = ' r ' ) & & ( cl - > reader & & cl - > reader - > cacheex . mode ) )
{
cl - > cwc_info + + ;
}
cs_log_dbg ( D_CWC , " CWC (CE) push to %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X " ,
username ( cl ) , er - > cwc_cycletime , er - > cwc_next_cw_cycle , er - > caid , er - > prid , er - > srvid ) ;
}
buf [ 19 ] = er - > ecm [ 0 ] ! = 0x80 & & er - > ecm [ 0 ] ! = 0x81 ? 0 : er - > ecm [ 0 ] ;
uint8_t * ofs = buf + 20 ;
// write oscam ecmd5
memcpy ( ofs , er - > ecmd5 , sizeof ( er - > ecmd5 ) ) ; // 16
ofs + = sizeof ( er - > ecmd5 ) ;
// write csp hashcode
i2b_buf ( 4 , CSP_HASH_SWAP ( er - > csp_hash ) , ofs ) ;
ofs + = 4 ;
// write cw
memcpy ( ofs , er - > cw , sizeof ( er - > cw ) ) ; // 16
ofs + = sizeof ( er - > cw ) ;
// write node count
* ofs = ll_count ( er - > csp_lastnodes ) + 1 ;
ofs + + ;
// write own node
memcpy ( ofs , cc - > node_id , 8 ) ;
ofs + = 8 ;
// write other nodes
LL_LOCKITER * li = ll_li_create ( er - > csp_lastnodes , 0 ) ;
uint8_t * node ;
while ( ( node = ll_li_next ( li ) ) )
{
memcpy ( ofs , node , 8 ) ;
ofs + = 8 ;
}
ll_li_destroy ( li ) ;
# ifdef CS_CACHEEX_AIO
// add localgenerated cw-flag
if ( er - > localgenerated )
{
* ofs = 1 ;
}
else
{
* ofs = 0xFF ;
}
# endif
int32_t res = cc_cmd_send ( cl , buf , size + 20 , MSG_CACHE_PUSH ) ;
if ( res > 0 ) // cache-ex is pushing out, so no receive but last_g should be updated otherwise disconnect!
{
if ( cl - > reader )
{
cl - > reader - > last_s = cl - > reader - > last_g = time ( ( time_t * ) 0 ) ; // correct
}
if ( cl )
{
cl - > last = time ( NULL ) ;
}
}
NULLFREE ( buf ) ;
return res ;
}
void cc_cacheex_push_in ( struct s_client * cl , uint8_t * buf )
{
struct cc_data * cc = cl - > cc ;
ECM_REQUEST * er ;
if ( ! cc )
{
return ;
}
if ( cl - > reader )
{
cl - > reader - > last_s = cl - > reader - > last_g = time ( ( time_t * ) 0 ) ;
}
if ( cl )
{
cl - > last = time ( NULL ) ;
}
int8_t rc = buf [ 14 ] ;
if ( rc ! = E_FOUND & & rc ! = E_UNHANDLED ) // Maybe later we could support other rcs
{
return ;
}
uint16_t size = buf [ 12 ] | ( buf [ 13 ] < < 8 ) ;
if ( size ! = sizeof ( er - > ecmd5 ) + sizeof ( er - > csp_hash ) + sizeof ( er - > cw ) )
{
cs_log_dbg ( D_CACHEEX , " cacheex: %s received old cash-push format! data ignored! " , username ( cl ) ) ;
return ;
}
if ( ! ( er = get_ecmtask ( ) ) )
{
return ;
}
er - > caid = b2i ( 2 , buf + 0 ) ;
er - > prid = b2i ( 4 , buf + 2 ) ;
er - > srvid = b2i ( 2 , buf + 10 ) ;
er - > ecm [ 0 ] = buf [ 19 ] ! = 0x80 & & buf [ 19 ] ! = 0x81 ? 0 : buf [ 19 ] ; // odd/even byte, usefull to send it over CSP and to check cw for swapping
er - > rc = rc ;
er - > ecmlen = 0 ;
if ( buf [ 18 ] )
{
if ( buf [ 18 ] & ( 0x01 < < 7 ) )
{
er - > cwc_cycletime = ( buf [ 18 ] & 0x7F ) ; // remove bit 8 to get cycletime
er - > cwc_next_cw_cycle = 1 ;
}
else
{
er - > cwc_cycletime = buf [ 18 ] ;
er - > cwc_next_cw_cycle = 0 ;
}
}
# ifndef CS_CACHEEX_AIO
if ( er - > cwc_cycletime & & er - > cwc_next_cw_cycle < 2 )
{
if ( cl - > typ = = ' c ' & & cl - > account & & cl - > account - > cacheex . mode )
{
cl - > account - > cwc_info + + ;
}
else if ( ( cl - > typ = = ' p ' | | cl - > typ = = ' r ' ) & & ( cl - > reader & & cl - > reader - > cacheex . mode ) )
{
cl - > cwc_info + + ;
}
cs_log_dbg ( D_CWC , " CWC (CE) received from %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X " ,
username ( cl ) , er - > cwc_cycletime , er - > cwc_next_cw_cycle , er - > caid , er - > prid , er - > srvid ) ;
}
# endif
uint8_t * ofs = buf + 20 ;
// Read ecmd5
memcpy ( er - > ecmd5 , ofs , sizeof ( er - > ecmd5 ) ) ; // 16
ofs + = sizeof ( er - > ecmd5 ) ;
if ( ! check_cacheex_filter ( cl , er ) )
{
return ;
}
# ifdef CS_CACHEEX_AIO
// check cacheex_ecm_filter
if ( check_client ( cl ) & & cl - > typ = = ' p ' & & cl - > reader & & cl - > reader - > cacheex . mode = = 2
& & ( ( cl - > reader - > cacheex . filter_caidtab . cevnum > 0 & & ! chk_csp_ctab ( er , & cl - > reader - > cacheex . filter_caidtab ) ) // reader cacheex_ecm_filter not matching if set
| | ( cl - > reader - > cacheex . filter_caidtab . cevnum = = 0 & & ( cl - > reader - > cacheex . feature_bitfield & 4 ) & & cfg . cacheex_filter_caidtab_aio . cevnum > 0 & & ! chk_csp_ctab ( er , & cfg . cacheex_filter_caidtab_aio ) ) // global cacheex_ecm_filter_aio not matching if set
| | ( cl - > reader - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab_aio . cevnum = = 0 & & cfg . cacheex_filter_caidtab . cevnum > 0 & & ! chk_csp_ctab ( er , & cfg . cacheex_filter_caidtab ) ) // global cacheex_ecm_filter not matching if set
)
)
{
cs_log_dbg ( D_CACHEEX , " cacheex: received cache not matching cacheex_ecm_filter => pushing filter again " ) ;
cc_cacheex_filter_out ( cl ) ; // get cache != cacheex_ecm_filter, send filter again - remote restarted
if ( cl - > reader - > cacheex . feature_bitfield & 4 )
cc_cacheex_feature_trigger ( cl , 4 , 2 ) ;
free_push_in_ecm ( er ) ;
return ;
}
if ( check_client ( cl ) & & cl - > typ = = ' c ' & & cl - > account & & cl - > account - > cacheex . mode = = 3
& & ( ( cl - > account - > cacheex . filter_caidtab . cevnum > 0 & & ! chk_csp_ctab ( er , & cl - > account - > cacheex . filter_caidtab ) ) // account cacheex_ecm_filter not matching if set
| | ( cl - > account - > cacheex . filter_caidtab . cevnum = = 0 & & ( cl - > account - > cacheex . feature_bitfield & 4 ) & & cfg . cacheex_filter_caidtab_aio . cevnum > 0 & & ! chk_csp_ctab ( er , & cfg . cacheex_filter_caidtab_aio ) ) // global cacheex_ecm_filter_aio not matching if set
| | ( cl - > account - > cacheex . filter_caidtab . cevnum = = 0 & & cfg . cacheex_filter_caidtab_aio . cevnum = = 0 & & cfg . cacheex_filter_caidtab . cevnum > 0 & & ! chk_csp_ctab ( er , & cfg . cacheex_filter_caidtab ) ) // global cacheex_ecm_filter not matching if set
)
)
{
cs_log_dbg ( D_CACHEEX , " cacheex: received cache not matching cacheex_ecm_filter => pushing filter again " ) ;
cc_cacheex_filter_out ( cl ) ; // get cache != cacheex_ecm_filter, send filter again - remote restarted
if ( cl - > account - > cacheex . feature_bitfield & 4 )
cc_cacheex_feature_trigger ( cl , 4 , 3 ) ;
free_push_in_ecm ( er ) ;
return ;
}
# endif
// Read csp_hash
er - > csp_hash = CSP_HASH_SWAP ( b2i ( 4 , ofs ) ) ;
ofs + = 4 ;
// Read cw
memcpy ( er - > cw , ofs , sizeof ( er - > cw ) ) ; // 16
ofs + = sizeof ( er - > cw ) ;
// Read lastnode count
uint8_t count = * ofs ;
ofs + + ;
# ifndef CS_CACHEEX_AIO
// check max nodes
2026-02-17 09:41:05 +00:00
if ( count > cacheex_maxhop ( cl , er ) )
2026-02-16 09:02:48 +00:00
{
cs_log_dbg ( D_CACHEEX , " cacheex: received %d nodes (max=%d), ignored! %s " ,
2026-02-17 09:41:05 +00:00
( 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 ) ) ;
// Read lastnodes
uint8_t * data ;
if ( er )
{
er - > csp_lastnodes = ll_create ( " csp_lastnodes " ) ;
}
while ( count )
{
if ( ! cs_malloc ( & data , 8 ) )
{
break ;
}
memcpy ( data , ofs , 8 ) ;
ofs + = 8 ;
ll_append ( er - > csp_lastnodes , data ) ;
count - - ;
cs_log_dbg ( D_CACHEEX , " cacheex: received node % " PRIu64 " X %s " , cacheex_node_id ( data ) , username ( cl ) ) ;
}
# ifdef CS_CACHEEX_AIO
if ( b2i ( 1 , ofs ) = = 1 )
{
er - > localgenerated = 1 ;
cs_log_dbg ( D_CACHEEX , " cacheex: received ECM with localgenerated flag %04X@%06X:%04X %s " , er - > caid , er - > prid , er - > srvid , username ( cl ) ) ;
//check max nodes for lg flagged cw:
2026-02-17 09:41:05 +00:00
if ( ll_count ( er - > csp_lastnodes ) > cacheex_maxhop_lg ( cl , er ) )
2026-02-16 09:02:48 +00:00
{
2026-02-17 09:41:05 +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
{
//check max nodes:
2026-02-17 09:41:05 +00:00
if ( ll_count ( er - > csp_lastnodes ) > cacheex_maxhop ( cl , er ) )
2026-02-16 09:02:48 +00:00
{
2026-02-17 09:41:05 +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 ) // cx2
& & (
// !aio
( cl - > cacheex_aio_checked & & ! cl - > reader - > cacheex . feature_bitfield
& & (
! cfg . cacheex_lg_only_in_aio_only & & ! cl - > reader - > cacheex . lg_only_in_aio_only
& & ( cfg . cacheex_localgenerated_only_in | | cl - > reader - > cacheex . localgenerated_only_in | | ( ( cl - > reader - > cacheex . feature_bitfield & 64 ) & & ( chk_lg_only ( er , & cl - > reader - > cacheex . lg_only_in_tab ) | | chk_lg_only ( er , & cfg . cacheex_lg_only_in_tab ) ) ) | | ( ! ( cl - > reader - > cacheex . feature_bitfield & 64 ) & & ( chk_ctab_ex ( er - > caid , & cl - > reader - > cacheex . localgenerated_only_in_caidtab ) | | chk_ctab_ex ( er - > caid , & cfg . cacheex_localgenerated_only_in_caidtab ) ) ) )
)
)
| |
// aio
( cl - > cacheex_aio_checked & & cl - > reader - > cacheex . feature_bitfield
& & (
cfg . cacheex_localgenerated_only_in | | cl - > reader - > cacheex . localgenerated_only_in | | ( ( cl - > reader - > cacheex . feature_bitfield & 64 ) & & ( chk_lg_only ( er , & cl - > reader - > cacheex . lg_only_in_tab ) | | chk_lg_only ( er , & cfg . cacheex_lg_only_in_tab ) ) ) | | ( ! ( cl - > reader - > cacheex . feature_bitfield & 64 ) & & ( chk_ctab_ex ( er - > caid , & cl - > reader - > cacheex . localgenerated_only_in_caidtab ) | | chk_ctab_ex ( er - > caid , & cfg . cacheex_localgenerated_only_in_caidtab ) ) )
)
)
)
)
| |
( cl - > typ = = ' c ' & & cl - > account & & cl - > account - > cacheex . mode = = 3 & & ! chk_srvid_localgenerated_only_exception ( er ) // cx3
& & (
// !aio
( cl - > cacheex_aio_checked & & ! cl - > account - > cacheex . feature_bitfield
& & (
! cfg . cacheex_lg_only_in_aio_only & & ! cl - > account - > cacheex . lg_only_in_aio_only
& & ( cfg . cacheex_localgenerated_only_in | | cl - > account - > cacheex . localgenerated_only_in | | ( ( cl - > account - > cacheex . feature_bitfield & 64 ) & & ( chk_lg_only ( er , & cl - > account - > cacheex . lg_only_in_tab ) | | chk_lg_only ( er , & cfg . cacheex_lg_only_in_tab ) ) ) | | ( ! ( cl - > account - > cacheex . feature_bitfield & 64 ) & & ( chk_ctab_ex ( er - > caid , & cl - > account - > cacheex . localgenerated_only_in_caidtab ) | | chk_ctab_ex ( er - > caid , & cfg . cacheex_localgenerated_only_in_caidtab ) ) ) )
)
)
| |
// aio
( cl - > cacheex_aio_checked & & cl - > account - > cacheex . feature_bitfield
& & (
cfg . cacheex_localgenerated_only_in | | cl - > account - > cacheex . localgenerated_only_in | | ( ( cl - > account - > cacheex . feature_bitfield & 64 ) & & ( chk_lg_only ( er , & cl - > account - > cacheex . lg_only_in_tab ) | | chk_lg_only ( er , & cfg . cacheex_lg_only_in_tab ) ) ) | | ( ! ( cl - > account - > cacheex . feature_bitfield & 64 ) & & ( chk_ctab_ex ( er - > caid , & cl - > account - > cacheex . localgenerated_only_in_caidtab ) | | chk_ctab_ex ( er - > caid , & cfg . cacheex_localgenerated_only_in_caidtab ) ) )
)
)
)
)
)
{
cs_log_dbg ( D_CACHEEX , " cacheex: drop ECM without localgenerated flag %04X@%06X:%04X %s " , er - > caid , er - > prid , er - > srvid , username ( cl ) ) ;
free_push_in_ecm ( er ) ;
return ;
}
}
# endif
// for compatibility: add peer node if no node received
if ( ! ll_count ( er - > csp_lastnodes ) )
{
if ( ! cs_malloc ( & data , 8 ) )
{
return ;
}
memcpy ( data , cc - > peer_node_id , 8 ) ;
ll_append ( er - > csp_lastnodes , data ) ;
cs_log_dbg ( D_CACHEEX , " cacheex: added missing remote node id % " PRIu64 " X " , cacheex_node_id ( data ) ) ;
}
# ifdef CS_CACHEEX_AIO
if ( er - > cwc_cycletime & & er - > cwc_next_cw_cycle < 2 )
{
if ( cl - > typ = = ' c ' & & cl - > account & & cl - > account - > cacheex . mode )
{
cl - > account - > cwc_info + + ;
}
else if ( ( cl - > typ = = ' p ' | | cl - > typ = = ' r ' ) & & ( cl - > reader & & cl - > reader - > cacheex . mode ) )
{
cl - > cwc_info + + ;
}
cs_log_dbg ( D_CWC , " CWC (CE) received from %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X " ,
username ( cl ) , er - > cwc_cycletime , er - > cwc_next_cw_cycle , er - > caid , er - > prid , er - > srvid ) ;
}
# endif
cacheex_add_to_cache ( cl , er ) ;
}
void cc_cacheex_module_init ( struct s_module * ph )
{
ph - > c_cache_push = cc_cacheex_push_out ;
ph - > c_cache_push_chk = cc_cacheex_push_chk ;
}
# endif