./ MultiCS.r69 / ecmdata.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#ifdef WIN32
#include <windows.h>
#else
#include <fcntl.h>
#include <sys/time.h>
#include <time.h>
#include <pthread.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <errno.h>
#endif

#include "common.h"
#include "tools.h"
#include "debug.h"
#include "ecmdata.h"

ECM_DATA ecmdata[MAX_ECM_DATA];

int srvmsgid = 0;


void init_ecmdata()
{
	memset(ecmdata, 0, sizeof(ecmdata));
}

uint32_t ecm_crc( uint8_t *ecm, int ecmlen)
{
  uchar checksum[4];
  int counter;
  
  checksum[3]= ecm[0];
  checksum[2]= ecm[1];
  checksum[1]= ecm[2];
  checksum[0]= ecm[3];
  for ( counter=1; counter< (ecmlen/4) - 4; counter++)
  {
    checksum[3] ^=ecm[counter*4];
    checksum[2] ^=ecm[counter*4+1];
    checksum[1] ^=ecm[counter*4+2];
    checksum[0] ^=ecm[counter*4+3];
  }
  return ( (checksum[3]<<24) | (checksum[2]<<16) | (checksum[1]<<8) | checksum[0] );
}

uint32_t hashCode( uint8_t *buf, int count)
{
	int h = 0;
	int i;
    for (i = 0; i < count; i++) h = 31*h + buf[i];
    return h;
}

ECM_DATA *getecmbyid(int id)
{
	return &ecmdata[id];
}


int prevecmid( int ecmid )
{
	if (ecmid<1) ecmid = MAX_ECM_DATA-1; else ecmid--; 
	return ecmid;
}

int nextecmid( int ecmid )
{
	ecmid++; if (ecmid>=MAX_ECM_DATA) ecmid=0;
	return ecmid;
}

#ifdef CHECK_NEXTDCW
void checkfreeze_storeECM(int ecmid);
#endif


uint32_t ecm_getprovid( uint8_t *ecm, uint16_t caid )
{
	int32_t i, len, descriptor_length = 0;
	uint32_t provid = 0;

	switch(caid >> 8) {
		case 0x01: // seca
			provid = (ecm[3]<<8)|ecm[4];
			break;

		case 0x05:
			// viaccess
			i = (ecm[4] == 0xD2) ? ecm[5]+2 : 0;  // skip d2 nano
			if((ecm[5+i] == 3) && ((ecm[4+i] == 0x90) || (ecm[4+i] == 0x40)))
				provid = (ecm[i+6]<<16)|(ecm[i+7]<<8)|(ecm[i+8]&0xF0);

			i = (ecm[6] == 0xD2) ? ecm[7]+2 : 0;  // skip d2 nano long ecm
			if((ecm[7+i] == 7) && ((ecm[6+i] == 0x90) || (ecm[6+i] == 0x40)))
				provid = (ecm[i+8]<<16)|(ecm[i+9]<<8)|(ecm[i+10]&0xF0);
			break;

		case 0x0D:
			// cryptoworks
			len = (((ecm[1] & 0xf) << 8) | ecm[2])+3;
			for(i=8; i<len; i+=descriptor_length+2) {
				descriptor_length = ecm[i+1];
				if (ecm[i] == 0x83) {
					provid = (uint32_t)ecm[i+2] & 0xFE;
					break;
				}
			}
			break;
	}
	return(provid);
}

uint16_t ecm_getchid( uint8_t *ecm, uint16_t caid )
{
	if ( (caid>>8)==0x06 )	return (ecm[6]<<8)|ecm[7];
	return 0;
}

int store_ecmdata(int csid, uchar *ecm,int ecmlen, unsigned short sid, unsigned short caid, unsigned int provid)
{
	uint ticks = GetTickCount();
	srvmsgid = nextecmid(srvmsgid);
	// Check Ecm
	if ( ecmdata[srvmsgid].recvtime && (ecmdata[srvmsgid].recvtime+30000>ticks) ) {
		debugf(0," Cannot store ecm request\n");
		return -1;		
	}
	memset( &ecmdata[srvmsgid], 0, sizeof(ECM_DATA) );
	memcpy( ecmdata[srvmsgid].ecm, ecm, ecmlen);
	ecmdata[srvmsgid].csid = csid;
	ecmdata[srvmsgid].chid = ecm_getchid(ecm, caid);
	ecmdata[srvmsgid].ecmlen = ecmlen;
	ecmdata[srvmsgid].recvtime = ticks;
	ecmdata[srvmsgid].lastrecvtime = ticks;
	ecmdata[srvmsgid].crc = ecm_crc(ecm, ecmlen);
	ecmdata[srvmsgid].hash = hashCode(ecm+3, ecmlen-3);
	ecmdata[srvmsgid].dcwstatus = STAT_DCW_WAIT;
	ecmdata[srvmsgid].sid = sid;
	ecmdata[srvmsgid].caid = caid;
	ecmdata[srvmsgid].provid = provid;
	ecmdata[srvmsgid].srvmsgid = srvmsgid;
	memset( &ecmdata[srvmsgid].server, 0, sizeof(ecmdata[srvmsgid].server) );
	ecmdata[srvmsgid].waitcache = 0;
	ecmdata[srvmsgid].cachestatus = ECM_CACHE_NONE;

	//ecmdata[srvmsgid].dcwsrvtype = DCW_SOURCE_NONE;

#ifdef CHECK_NEXTDCW
	//checkfreeze_storeECM(srvmsgid);
#endif

	return srvmsgid;
}


void ecm_addip( int ecmid, unsigned int ip)
{
	ECM_DATA *ecm = getecmbyid(ecmid);
	register int i;
	for(i=0; i<20; i++) {
		if (!ecm->iplist[i]) {
			ecm->iplist[i] = ip;
			return;
		}
		if (ecm->iplist[i]==ip) return;
	}
}

int ecm_checkip( int ecmid, unsigned int ip)
{
	ECM_DATA *ecm = getecmbyid(ecmid);
	register int i;
	for(i=0; i<20; i++) {
		if (!ecm->iplist[i]) return FALSE;
		if (ecm->iplist[i]==ip) return TRUE; // found
	}
	return FALSE;
}

#define TIME_ECMALIVE 60000

int search_ecmdata( uchar *ecm, int ecmlen, unsigned short sid)
{
	int i;
	uint32 ticks = GetTickCount();
	uint32 crc = ecm_crc(ecm, ecmlen);
	for (i=0; i<MAX_ECM_DATA; i++) {
		if ( (ticks-ecmdata[i].recvtime) < TIME_ECMALIVE )
		if (sid==ecmdata[i].sid)
		if (crc==ecmdata[i].crc) return i;
	}
	return -1;
}  

int search_ecmdata_bymsgid( int msgid )
{
	int i;
	uint32 ticks = GetTickCount();
	for (i=0; i<MAX_ECM_DATA; i++) {
		if ( (ticks-ecmdata[i].recvtime) < TIME_ECMALIVE )
		if (msgid==ecmdata[i].srvmsgid) return i;
	}
	return -1;
}

int search_ecmdata_dcw( uchar *ecm, int ecmlen, unsigned short sid)
{
	int i;
	uint32 ticks = GetTickCount();
	uint32 crc = ecm_crc(ecm, ecmlen);
	for (i=0; i<MAX_ECM_DATA; i++) {
		if ( (ticks-ecmdata[i].recvtime) < 60000 )
		if (ecmdata[i].dcwstatus!=STAT_DCW_FAILED)
		if (ecmlen==ecmdata[i].ecmlen)
		if (crc==ecmdata[i].crc)
		if (sid==ecmdata[i].sid)
		if ( !memcmp(ecm, ecmdata[i].ecm, ecmlen) ) return i;
	}
	return -1;
}  

int search_ecmdata_byhash( uint32 hash )
{
	int i;
	uint32 ticks = GetTickCount();
	for (i=0; i<MAX_ECM_DATA; i++) {
		if ( (ticks-ecmdata[i].recvtime) < TIME_ECMALIVE )
		if (hash==ecmdata[i].hash) return i;
	}
	return -1;
}



int ecm_addsrv(ECM_DATA *ecm, unsigned int srvid)
{
	int i;
	uint32 ticks = GetTickCount();
	for(i=0; i<20; i++) {
		if (!ecm->server[i].srvid) {
			ecm->server[i].srvid = srvid;
			ecm->server[i].flag = ECM_SRV_REQUEST;
			ecm->server[i].sendtime = ticks;
			ecm->server[i].statustime = ticks; // last changed status time
			return 1;
		}
	}
	return 0;
}

int ecm_nbservers(ECM_DATA *ecm)
{
	int i;
	int count=0;
	for(i=0; i<20; i++) {
		if (ecm->server[i].srvid) {
			count++;
		} else break;
	}
	return count;
}


int ecm_nbsentsrv(ECM_DATA *ecm)
{
	int i;
	int count=0;
	for(i=0; i<20; i++) {
		if (ecm->server[i].srvid) {
			if (ecm->server[i].flag!=ECM_SRV_EXCLUDE) count++;
		} else break;
	}
	return count;
}

int ecm_nbwaitsrv(ECM_DATA *ecm)
{
	int i;
	int count=0;
	for(i=0; i<20; i++) {
		if (ecm->server[i].srvid) {
			if (ecm->server[i].flag==ECM_SRV_REQUEST) count++;
		} else break;
	}
	return count;
}

int ecm_setsrvflag(int ecmid, unsigned int srvid, int flag)
{
	int i;
	ECM_DATA *ecm = getecmbyid(ecmid);
	for(i=0; i<20; i++) {
		if (ecm->server[i].srvid) {
			if (ecm->server[i].srvid==srvid) {
				ecm->server[i].flag=flag;
				ecm->server[i].statustime = GetTickCount();
				return 1;
			}
		} else break;
	}
	return 0;
}

int ecm_setsrvflagdcw(int ecmid, unsigned int srvid, int flag, uchar dcw[16])
{
	int i;
	ECM_DATA *ecm = getecmbyid(ecmid);
	for(i=0; i<20; i++) {
		if (ecm->server[i].srvid) {
			if (ecm->server[i].srvid==srvid) {
				ecm->server[i].flag=flag;
				ecm->server[i].statustime = GetTickCount();
				memcpy(ecm->server[i].dcw, dcw, 16);
				return 1;
			}
		} else break;
	}
	return 0;
}

int ecm_getsrvflag(int ecmid, unsigned int srvid)
{
	int i;
	ECM_DATA *ecm = getecmbyid(ecmid);
	for(i=0; i<20; i++) {
		if (ecm->server[i].srvid) {
			if (ecm->server[i].srvid==srvid) {
				return ecm->server[i].flag;
			}
		} else break;
	}
	return 0;
}

int ecm_getreplysrvid(int ecmid)
{
	int i;
	ECM_DATA *ecm = getecmbyid(ecmid);
	for(i=0; i<20; i++)
		if ( ecm->server[i].srvid && (ecm->server[i].flag==ECM_SRV_REPLY_GOOD) ) return ecm->server[i].srvid;

	for(i=0; i<20; i++)
		if ( ecm->server[i].srvid && (ecm->server[i].flag==ECM_SRV_REPLY_FAIL) ) return ecm->server[i].srvid;

	return 0;
}

int search_ecmdata_bycw( unsigned char *cw, uint32 hash, unsigned short sid, unsigned short caid, unsigned int provid)
{
	int i;
	for (i=0; i<MAX_ECM_DATA; i++) {
		if (ecmdata[i].dcwstatus==STAT_DCW_SUCCESS)
		if (provid==ecmdata[i].provid)
		if (caid==ecmdata[i].caid)
		if (sid==ecmdata[i].sid)
		if (hash!=ecmdata[i].hash)
		if (!memcmp(cw, ecmdata[i].cw, 16)) return i;
	}
	return -1;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

#ifdef CHECK_NEXTDCW

/*

- search for ecm request of same channel, must be newest one and has decode success
- 

*/

// Get Last DCW for the same Channel
void checkfreeze_storeECM(int ecmid)
{
	// find after storing ecm
	ECM_DATA *ecm = getecmbyid(ecmid);
	if (!ecm) return;
	if (ecm->lastdecode.status<0) return; // error
	if (ecm->lastdecode.status>0) return; // already done!!!

	if ( (ecm->lastdecode.status==0)&&(ecm->dcwstatus!=STAT_DCW_SUCCESS) ) {
		//debugf(0," \n[SROTE ECM] New (%04x:%06x:%04x/%08x)\n",ecm->caid, ecm->provid, ecm->sid, ecm->hash);
		int oldecmid = ecmid;
		while ( (oldecmid=prevecmid(oldecmid)) != ecmid ) {
			ECM_DATA *oldecm = getecmbyid(oldecmid);
			if (!oldecm) break;
			if ((ecm->recvtime-oldecm->recvtime)>60000) break;
			if ( (oldecm->dcwstatus==STAT_DCW_SUCCESS)
				&&(oldecm->ecmlen==ecm->ecmlen)&&(oldecm->ecm[0]!=ecm->ecm[0])
				&&(oldecm->caid==ecm->caid)&&(oldecm->provid==ecm->provid)&&(oldecm->sid==ecm->sid)
			) {
/*
				char str1[512];
				array2hex( oldecm->cw, str1, 16);
				debugf(0,"Found (%04x:%06x:%04x/%08x) [%s] Stat:%d Cntr:%d Time:%d\n", oldecm->caid, oldecm->provid, oldecm->sid, oldecm->hash, str1, oldecm->lastdecode.status, oldecm->lastdecode.counter, oldecm->lastdecode.dcwchangetime);
*/
				ecm->lastdecode.ecmid = oldecmid;
				if ( (oldecm->lastdecode.status==1)&&(oldecm->lastdecode.counter>1) ) {
					if ((ecm->recvtime-oldecm->recvtime)<(oldecm->lastdecode.dcwchangetime*12/10)) {
						// found one (not sure)
						ecm->lastdecode.status = 1;
						ecm->lastdecode.counter = oldecm->lastdecode.counter;
						// get average of ecm change time
						ecm->lastdecode.dcwchangetime = (oldecm->lastdecode.dcwchangetime*oldecm->lastdecode.counter+(ecm->recvtime-oldecm->recvtime)) / (oldecm->lastdecode.counter+1);
						ecm->lastdecode.dcwchangetime = ((ecm->lastdecode.dcwchangetime+300)/1000)*1000;
						memcpy( ecm->lastdecode.dcw, oldecm->cw, 16); // Store latest DCW
					}
				}
				else {
					// maybe??
					ecm->lastdecode.status = 1;
					ecm->lastdecode.counter = oldecm->lastdecode.counter;
					ecm->lastdecode.dcwchangetime = ecm->recvtime-oldecm->recvtime;
					memcpy( ecm->lastdecode.dcw, oldecm->cw, 16); // Store latest DCW
				}

				break;
			}
		}
	}
}


void checkfreeze_checkECM(int ecmid, int oldecmid)
{
	ECM_DATA *ecm = getecmbyid(ecmid);
	ECM_DATA *oldecm = getecmbyid(oldecmid);
	if (!ecm) return;
	if (!oldecm) return;

			if ((ecm->recvtime-oldecm->recvtime)>60000) return;
			if ( (oldecm->dcwstatus==STAT_DCW_SUCCESS)
				&&(oldecm->ecmlen==ecm->ecmlen)&&(oldecm->ecm[0]!=ecm->ecm[0])
				&&(oldecm->caid==ecm->caid)&&(oldecm->provid==ecm->provid)&&(oldecm->sid==ecm->sid)
			) {
				if ( (oldecm->lastdecode.status==1)&&(oldecm->lastdecode.counter>1) ) {
					if ((ecm->recvtime-oldecm->recvtime)<(oldecm->lastdecode.dcwchangetime*12/10)) {
						// found one (not sure)
						ecm->lastdecode.status = 1;
						ecm->lastdecode.counter = oldecm->lastdecode.counter;
						// get average of ecm change time
						ecm->lastdecode.dcwchangetime = (oldecm->lastdecode.dcwchangetime*oldecm->lastdecode.counter+(ecm->recvtime-oldecm->recvtime)) / (oldecm->lastdecode.counter+1);
						ecm->lastdecode.dcwchangetime = ((ecm->lastdecode.dcwchangetime+300)/1000)*1000;
						memcpy( ecm->lastdecode.dcw, oldecm->cw, 16); // Store latest DCW
					}
				}
				else if (ecm->lastdecode.status!=1) {
					// maybe??
					ecm->lastdecode.status = 1;
					ecm->lastdecode.counter = oldecm->lastdecode.counter;
					ecm->lastdecode.dcwchangetime = ecm->recvtime-oldecm->recvtime;
					memcpy( ecm->lastdecode.dcw, oldecm->cw, 16); // Store latest DCW
				}

			}
}

// return 0:wrong dcw, 1: good dcw
int checkfreeze_setdcw( int ecmid, uchar dcw[16] )
{
	char nullcw[8] = "\0\0\0\0\0\0\0\0";
	ECM_DATA *ecm = getecmbyid(ecmid);
	if (!ecm) return 1;
	if (ecm->lastdecode.status!=1) return 1; // no old successful decode

/*
	char str1[512];
	char str2[512];
	array2hex( ecm->lastdecode.dcw, str1, 16);
	array2hex( dcw, str2, 16);
	debugf(0," \n[SET DCW] (%04x:%06x:%04x/%08x) Cntr:%d\nOLD[%s] -- NEW[%s]\n",ecm->caid, ecm->provid, ecm->sid, ecm->hash, ecm->lastdecode.counter, str1, str2);
*/
	if (ecm->lastdecode.counter<2) {
		// Check consecutif cw
		if ( memcmp(dcw,ecm->lastdecode.dcw,16) && ( !memcmp(dcw,ecm->lastdecode.dcw,8)||!memcmp(dcw+8,ecm->lastdecode.dcw+8,8) ) )
			ecm->lastdecode.counter++;
		else
			ecm->lastdecode.counter = 0;
		//debugf(0,"OK1 New Cntr:%d\n",ecm->lastdecode.counter);
		return 1;
	}
	else {
		// Check similar cws // or null cw1/cw2 ==> no more consec.
		if ( memcmp(dcw,ecm->lastdecode.dcw,16) ) { // must be not the same
			if ( (!memcmp(dcw,ecm->lastdecode.dcw,8)||!memcmp(dcw+8,ecm->lastdecode.dcw+8,8)) ) {
				ecm->lastdecode.counter++;
				//debugf(0,"OK2 New Cntr:%d\n",ecm->lastdecode.counter);
				//if (ecm->lastdecode.error) 	fdebugf(" ***(GOOD%d)SET DCW (%04x:%06x:%04x/%08x) Cntr:%d\nOLD[%s] -- NEW[%s]\n",ecm->lastdecode.error, ecm->caid, ecm->provid, ecm->sid, ecm->hash, ecm->lastdecode.counter, str1, str2);
				return 1;
			}
			else if ( (!memcmp(dcw,nullcw,8)||!memcmp(dcw+8,nullcw,8)) ) {
				// Remove consec
				ecm->lastdecode.counter = 0;
				//debugf("OK3 New Cntr:%d\n",ecm->lastdecode.counter);
				return 1;
			}
		}
	}
	ecm->lastdecode.error++;
	//fdebugf(" (FAILED%d) SET DCW (%04x:%06x:%04x/%08x) Cntr:%d\nOLD[%s] -- NEW[%s]\n",ecm->lastdecode.error, ecm->caid, ecm->provid, ecm->sid, ecm->hash, ecm->lastdecode.counter, str1, str2);
	//debugf("FAILED\n");
	return 0;
}


#endif