./ MultiCS.r82 / cli-camd35.c
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

void camd35_send_keepalive(struct server_data *srv)
{
	uint8_t buf[64];
#ifdef CACHEEX
	if (srv->cacheex_mode) {
		// Request Nodeid
		memset(buf, 0, 32);
		buf[0] = 0x3D;
		buf[1] = 12;
		memcpy( buf+20, cccam_nodeid, 8);
		camd35_sendto( srv->handle, srv->host->ip, srv->port, &srv->encryptkey, srv->ucrc, buf, 20+12);
	}
	else
#endif
	{
		// keepalive
		uint8_t buf[64];
		memset(buf,0, 21);
		buf[0] = 0x37;
		buf[1] = 1;
		camd35_sendto( srv->handle, srv->host->ip, srv->port, &srv->encryptkey, srv->ucrc, buf, 20+1);
	}
}

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

int camd35_sendecm_srv(struct server_data *srv, ECM_DATA *ecm)
{
	srv->ecm.msgid++;
	if (srv->ecm.msgid>0xfff) srv->ecm.msgid = 1;

	unsigned char buf[1024];
	memset(buf, 0, 20);
	buf[0] = 0; // Command
	buf[1] = 0; // Length
	buf[8] = ecm->sid>>8;
	buf[9] = ecm->sid&0xff;
	buf[10] = ecm->caid>>8;
	buf[11] = ecm->caid&0xff;
	buf[12] = ecm->provid>>24;
	buf[13] = ecm->provid>>16;
	buf[14] = ecm->provid>>8;
	buf[15] = ecm->provid&0xff;
	buf[16] = srv->ecm.msgid>>8;
	buf[17] = srv->ecm.msgid;
	memcpy( buf+20, ecm->ecm, ecm->ecmlen);
	camd35_sendto( srv->handle, srv->host->ip, srv->port, &srv->encryptkey, srv->ucrc, buf, 20+ecm->ecmlen);
	return 1;
}


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

void camd35_srv_recvmsg(struct server_data *srv)
{
	uint8_t cw[16];
	struct cardserver_data *cs;

	struct sockaddr_in si_other;
	socklen_t slen = sizeof(si_other);
	unsigned char buf[1024];

	int received = recvfrom( srv->handle, buf, sizeof(buf), 0, (struct sockaddr*)&si_other, &slen);
	if ( (received<20)||(received>1020) ) return;

	uint32_t ucrc = (buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|buf[3];
	//Check for client
	if (srv->ucrc!=ucrc) return;
	//
	aes_decrypt( &srv->decryptkey, buf+4, received-4);
#ifdef DEBUG_NETWORK
	if (flag_debugnet) {
		unsigned int recv_ip;
		unsigned short recv_port;
		memcpy( &recv_ip, &si_other.sin_addr, 4);
		recv_port = ntohs(si_other.sin_port);
		debugf(0," camd35: Recv data (length=%d) from address (%s:%d)\n", received, ip2string(recv_ip), recv_port );
		debughex(buf,received);
	}
#endif

	switch (buf[4]) {

		case CAMD_ECM_REPLY:
			srv->lastdcwtime = GetTickCount();
			if (!srv->busy) {
				debugf(getdbgflag(DBG_SERVER,0,srv->id)," [!] dcw error from camd35 server (%s:%d), unknown ecm request\n",srv->host->name,srv->port);
				break;
			}
			//
			if (buf[5]!=0x10) {
				debugf(getdbgflag(DBG_SERVER,0,srv->id)," [!] dcw error from camd35 server (%s:%d), wrong length!!!\n",srv->host->name,srv->port);
				break;
			}
			// Check for DCW
			if (!acceptDCW( buf+24 ) ) {
				srv->ecmerrdcw++;
				debugf(getdbgflag(DBG_SERVER,0,srv->id)," [!] dcw error from camd35 server (%s:%d), bad dcw!!!\n",srv->host->name,srv->port);
				break;
			}
			// Checl Stored ECM
			ECM_DATA *ecm = srv->ecm.request;
			if (!ecm) {
				debugf(getdbgflag(DBG_SERVER,0,srv->id)," [!] dcw error from camd35 server (%s:%d), ecm not found!!!\n",srv->host->name,srv->port);
				break;
			}
			//
			srv->busy = 0;
			pipe_cmd( prg.pipe.ecm[1], PIPE_SRV_AVAILABLE );
			pthread_mutex_lock(&prg.lockecm); //###
			// check for ECM validity
			if (ecm->hash!=srv->ecm.hash) {
				debugf(getdbgflag(DBG_SERVER,0,srv->id)," [!] dcw error from camd35 server (%s:%d), ecm deleted!!!\n",srv->host->name,srv->port);
				pthread_mutex_unlock(&prg.lockecm);
				break;
			}

			srv->ecmok++;
			srv->lastecmoktime = GetTickCount()-srv->lastecmtime;
			srv->ecmoktime += srv->lastecmoktime;
			ecm_setsrvflagdcw( ecm, srv->id, ECM_SRV_REPLY_GOOD, buf+24 );
			debugf(getdbgflagpro(DBG_SERVER,0,srv->id,ecm->cs->id)," <= cw from camd35 server (%s:%d) ch %04x:%06x:%04x (%dms)\n", srv->host->name,srv->port, ecm->caid,ecm->provid,ecm->sid, GetTickCount()-srv->lastecmtime);

			if (ecm->dcwstatus!=STAT_DCW_SUCCESS) {
				static char msg[] = "Good dcw from camd35 server";
				ecm->statusmsg = msg;
				// Store ECM Answer
				ecm_setdcw( ecm, buf+24, DCW_SOURCE_SERVER, srv->id );
			}
			else {	//TODO: check same dcw between cards
				srv->ecmerrdcw ++;
				if ( memcmp( ecm->cw, buf+24, 16) ) debugf(getdbgflagpro(DBG_SERVER,0,srv->id,ecm->cs->id)," !!! different dcw from camd35 server (%s:%d)\n",srv->host->name,srv->port);
			}
#ifdef SID_FILTER
			// ADD IN SID LIST
			cs= ecm->cs;
			if (cs) {
				cardsids_update( srv->busycard, ecm->provid, ecm->sid, 1);
				srv_cstatadd( srv, cs->id, 1 , srv->lastecmoktime);
			}
#endif
			pthread_mutex_unlock(&prg.lockecm); //###
			break;

		case 0x44:
			srv->lastdcwtime = GetTickCount();
			if (!srv->busy) {
				debugf(getdbgflag(DBG_SERVER,0,srv->id)," [!] dcw error from camd35 server (%s:%d), unknown ecm request\n",srv->host->name,srv->port);
				break;
			}
			// Checl Stored ECM
			ecm = srv->ecm.request;
			if (!ecm) {
				debugf(getdbgflag(DBG_SERVER,0,srv->id)," [!] dcw error from camd35 server (%s:%d), ecm not found!!!\n",srv->host->name,srv->port);
				break;
			}
			//
			srv->busy = 0;
			pipe_cmd( prg.pipe.ecm[1], PIPE_SRV_AVAILABLE );
			pthread_mutex_lock(&prg.lockecm); //###
			// check for ECM validity
			if (ecm->hash!=srv->ecm.hash) {
				debugf(getdbgflag(DBG_SERVER,0,srv->id)," [!] dcw error from camd35 server (%s:%d), ecm deleted!!!\n",srv->host->name,srv->port);
				pthread_mutex_unlock(&prg.lockecm);
				break;
			}
			cs= ecm->cs;
			ecm_setsrvflag(ecm, srv->id, ECM_SRV_REPLY_FAIL);
			debugf(getdbgflagpro(DBG_SERVER,0,srv->id,ecm->cs->id)," <| decode failed from camd35 server (%s:%d) ch %04x:%06x:%04x (%dms)\n", srv->host->name,srv->port, ecm->caid,ecm->provid,ecm->sid, GetTickCount()-srv->lastecmtime);
#ifdef SID_FILTER
			// ADD IN SID LIST
			if (cs) {
				cardsids_update( srv->busycard, ecm->provid, ecm->sid, -1);
				srv_cstatadd( srv, cs->id, 0 , 0);
			}
#endif
			pthread_mutex_unlock(&prg.lockecm); //###
			wakeup_sendecm(); // Wakeup ecm waiting for availabe servers
			break;

		// Keepalive
		case CAMD_KEEPALIVE:
			srv->keepalive.status = 0;
			//debugf(0," server(camd35): Keepalive from (%s:%d)\n", srv->host->name, srv->port);
			if (srv->connection.status<=0) {
				debugf(0," connect to camd35 server (%s:%d)\n", srv->host->name, srv->port);
				srv->connection.status = 1;
				srv->connection.time = GetTickCount();
			}
			break;

		// Request Nodeid
		case CAMD_CEX_IDREPLY:
			srv->keepalive.status = 0;
			//debugf(0," server(camd35): Got Nodeid from (%s:%d)\n", srv->host->name, srv->port);
			memcpy( srv->nodeid, buf+24, 8);
			if (srv->connection.status<=0) {
				char str[8*3+1];
				array2hex( srv->nodeid, str, 8);
				debugf(0," connected to camd35 server (%s:%d), Nodeid = %s\n", srv->host->name, srv->port, str);
				srv->connection.status = 1;
				srv->connection.time = GetTickCount();
			}
			break;

#ifdef CACHEEX
		// push out
		case CAMD_CEX_PUSH:
			memcpy( cw, buf+44, 16);
			if (!acceptDCW(cw)) break;
			//srv->cacheex.totalrep++;
			struct cache_data cacheex;
			cacheex.sid = (buf[12]<<8)|buf[13];
			cacheex.caid = (buf[14]<<8)|buf[15];
			cacheex.provid = (buf[16]<<24)|(buf[17]<<16)|(buf[18]<<8)|buf[19];
			// Look for cardserver
			cs = getcsbycaprovid(cacheex.caid, cacheex.provid);
			if ( !cs || !cs->option.fallowcacheex ) {
				srv->cacheex.badcw++;
				break;
			}
			if ((buf[23]&0xFE)==0x80) cacheex.tag = buf[23]; else cacheex.tag = 0;
			memcpy( cacheex.ecmd5, buf+24, 16);
			//if ( !checkECMD5(cacheex.ecmd5) ) cli->cacheex.totalcsp++;
			cacheex.hash = (buf[43]<<24) | (buf[42]<<16) | (buf[41]<<8) | buf[40];
			if (!cacheex_check(&cacheex)) break;
			//debugf( getdbgflag(DBG_CACHEEX, 0, 0)," CACHEEX PUSH from client(%d) %04x:%06x:%04x (%08x)\n",cli->id,cacheex.caid,cacheex.provid,cacheex.sid,cacheex.hash);
			srv->cacheex.got[0]++;
			int uphop = buf[60];
			if (uphop<10) srv->cacheex.got[uphop]++;
			//
			pthread_mutex_lock( &prg.lockcache );
			int res = cache_setdcw( &cacheex, cw, NO_CYCLE, PEER_CACHEEX_SERVER | srv->id );
			pthread_mutex_unlock( &prg.lockcache );
			if (res&DCW_ERROR) {
				if ( !(res&DCW_SKIP)) srv->cacheex.badcw++;
			}
			else if (res&DCW_CYCLE) {
				if ( cs->option.cacheex.maxhop>uphop ) {
					uint8_t nodeid[8];
					memcpy( nodeid, buf+61, 8);
					pipe_send_cacheex_push_cache(&cacheex, cw, nodeid); //cacheex_push(&cacheex, cw, nodeid);
				}
			}
			//debugf(0," camd35: push out from server %04x:%06x:%04x|%02x:%08x\n", cacheex.caid,cacheex.provid,cacheex.sid, cacheex.tag,cacheex.hash);
			break;
#endif

	}

}

// CACHEEX MODE 2
void *camd35_srv_recvmsg_thread(struct server_data *srv)
{
	srv->pid = syscall(SYS_gettid);

	while (!prg.restart) {
		if (srv->handle<=0) {
			srv->pid = 0;
			return NULL;
		}
		//
		struct pollfd pfd;
		pfd.fd = srv->handle;
		pfd.events = POLLIN | POLLPRI;
		int retval = poll(&pfd, 1, 3009);
		if (retval==0) continue; // timeout
		if (retval<0) {
			disconnect_srv(srv);
			srv->pid = 0;
			return NULL;
		}
		camd35_srv_recvmsg(srv);
	}
	return NULL;
}


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

int camd35_connect_srv(struct server_data *srv, int fd)
{
	uint8_t buf[64];
#ifdef CACHEEX
	if (srv->cacheex_mode) {
		// Request Nodeid
		memset(buf, 0, 32);
		buf[0] = CAMD_CEX_IDREQUEST;
		buf[1] = 12;
		memcpy( buf+20, cccam_nodeid, 8);
		camd35_sendto( fd, srv->host->ip, srv->port, &srv->encryptkey, srv->ucrc, buf, 20+12);
		struct pollfd pfd;
		pfd.fd = fd;
		pfd.events = POLLIN | POLLPRI;
		int ret = poll(&pfd, 1, 3000);
		while (ret>0) {
			struct sockaddr_in si_other;
			socklen_t slen = sizeof(si_other);
			unsigned char buf[1024];
			int received = recvfrom( fd, buf, sizeof(buf), 0, (struct sockaddr*)&si_other, &slen);
			if ( (received<20)||(received>1020) ) break;
			uint32_t ucrc = (buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|buf[3];
			if (srv->ucrc!=ucrc) break;
			aes_decrypt( &srv->decryptkey, buf+4, received-4);
			if (buf[4]==CAMD_CEX_IDREPLY) { // good
				memcpy( srv->nodeid, buf+24, 8);
				array2hex( srv->nodeid, buf, 8);
				debugf(0," Connected to camd35 server (%s:%d), Nodeid = %s\n", srv->host->name, srv->port, buf);
				srv->connection.status = 1;
				srv->connection.time = GetTickCount();
				srv->keepalive.status = 0;
				srv->keepalive.time = GetTickCount();
				srv->handle = fd;
				if (srv->cacheex_mode==2) {
					if (!create_thread(&srv->tid, (threadfn)camd35_srv_recvmsg_thread, srv)) {
						disconnect_srv(srv);
					}
				}
				else pipe_wakeup( prg.pipe.cacheex[1] );
				return 0;
			}
			break;
		}
		close(fd);			
	}
	else
#endif

	{
		// keepalive
		uint8_t buf[64];
		memset(buf,0, 21);
		buf[0] = CAMD_KEEPALIVE;
		buf[1] = 1;
		camd35_sendto( fd, srv->host->ip, srv->port, &srv->encryptkey, srv->ucrc, buf, 20+1);
		struct pollfd pfd;
		pfd.fd = fd;
		pfd.events = POLLIN | POLLPRI;
		int ret = poll(&pfd, 1, 3000);
		while (ret>0) {
			struct sockaddr_in si_other;
			socklen_t slen = sizeof(si_other);
			unsigned char buf[1024];
			int received = recvfrom( fd, buf, sizeof(buf), 0, (struct sockaddr*)&si_other, &slen);
			if ( (received<20)||(received>1020) ) break;
			uint32_t ucrc = (buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|buf[3];
			if (srv->ucrc!=ucrc) break;
			aes_decrypt( &srv->decryptkey, buf+4, received-4);
			if (buf[4]==CAMD_KEEPALIVE) { // good
				//SetUP Cards
				int count = 0;
				while (srv->sharelimits[count].caid!=0xFFFF) {
					struct cs_card_data *pcard = malloc( sizeof(struct cs_card_data) );
					memset(pcard, 0, sizeof(struct cs_card_data) );
					pcard->caid = srv->sharelimits[count].caid;
					pcard->nbprov = 1;
					pcard->prov[0] = srv->sharelimits[count].provid;
					pcard->uphops = srv->sharelimits[count].uphops;
					pcard->next = srv->card;
					srv->card = pcard;
					count++;
				}
				//
				debugf(0," Connected to camd35 server (%s:%d)\n", srv->host->name, srv->port);
				srv->connection.status = 1;
				srv->connection.time = GetTickCount();
				srv->keepalive.status = 0;
				srv->keepalive.time = GetTickCount();
				srv->handle = fd;
#ifdef EPOLL_ECM
				pipe_pointer( prg.pipe.ecm[1], PIPE_SRV_CONNECTED, srv );
#else
				pipe_cmd( prg.pipe.ecm[1], PIPE_SRV_CONNECTED );
#endif
				return 0;
			}
			break;
		}
		close(fd);			
	}
	return 1;
}