./ MultiCS.r82 / srv-freecccam.c
///////////////////////////////////////////////////////////////////////////////
// File: srv-freecccam.c
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// PROTO
///////////////////////////////////////////////////////////////////////////////

void freecc_cli_recvmsg(struct cc_client_data *cli);



///////////////////////////////////////////////////////////////////////////////
// FREECCCAM SERVER: CONNECT CLIENTS
///////////////////////////////////////////////////////////////////////////////

void frcc_sendcards_cli(struct cc_client_data *cli)
{
	int nbcard=0;
	struct cardserver_data *cs = cfg.cardserver;

	int i;
	if (cfg.freecccam.csport[0]) {
		for(i=0;i<MAX_CSPORTS;i++) {
			if(cfg.freecccam.csport[i]) {
				cs = getcsbyport(cfg.freecccam.csport[i]);
				if (cs)
					if (cc_sendcard_cli(cs, cli,0)) nbcard++;
			} else break;
		}
	}
	else {
		while (cs) {
			if (cc_sendcard_cli(cs, cli,0)) nbcard++;
			cs = cs->next;
		}
	}

	debugf(0," FreeCCcam: %d cards --> client(%s)\n",  nbcard, ip2string(cli->ip) );
}


///////////////////////////////////////////////////////////////////////////////
// CCCAM SERVER: DISCONNECT CLIENTS
///////////////////////////////////////////////////////////////////////////////

void frcc_disconnect_cli(struct cc_client_data *cli)
{
	if ( (cli->connection.status>0) && (cli->handle>0) ) {
		cli->connection.status = 0;
		uint32_t ticks = GetTickCount();
		cli->connection.uptime += ticks - cli->connection.time;
		cli->connection.lastseen = ticks; // Last Seen
		close(cli->handle);
		cli->handle = -1;
		debugf(0," FreeCCcam: client '%s' disconnected \n", cli->user);
		//////cli->parent->clipfd.update = 1;
	}
}

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

void *freecc_connect_cli(struct connect_cli_data *param)
{
	uint8_t buf[CC_MAXMSGSIZE];
	uint8_t data[64];
	int i;
	struct cc_crypt_block sendblock;	// crypto state block
	struct cc_crypt_block recvblock;	// crypto state block
	char usr[64];
	char pwd[255];

	int sock = param->sock;
	uint32_t ip = param->ip;
	free(param);

	memset(usr, 0, sizeof(usr));
	memset(pwd, 0, sizeof(pwd));
	// create & send random seed
	for(i=0; i<12; i++ ) data[i]=fast_rnd();
	// Create Multics ID
	data[3] = (data[0]^'M') + data[1] + data[2];
	data[7] = data[4] + (data[5]^'C') + data[6];
	data[11] = data[8] + data[9] + (data[10]^'S');
	//Create checksum for "O" cccam:
	for (i = 0; i < 4; i++) {
		data[12 + i] = (data[i] + data[4 + i] + data[8 + i]) & 0xff;
	}
	if ( !send_nonb(sock, data, 16, 100) ) {
		close(sock);
		return NULL;
	}
	//XOR init bytes with 'CCcam'
	cc_crypt_xor(data);
	//SHA1
	SHA_CTX ctx;
	SHA1_Init(&ctx);
	SHA1_Update(&ctx, data, 16);
	SHA1_Final(buf, &ctx);
	//initialisate crypto states
	cc_crypt_init(&sendblock, buf, 20);
	cc_decrypt(&sendblock, data, 16);
	cc_crypt_init(&recvblock, data, 16);
	cc_decrypt(&recvblock, buf, 20);
	//debugdump(buf, 20, "SHA1 hash:");
	memcpy(usr,buf,20);
	if ((i=recv_nonb(sock, buf, 20,3000)) == 20) {
		cc_decrypt(&recvblock, buf, 20);
		//debugdump(buf, 20, "Recv SHA1 hash:");
		if ( memcmp(buf,usr,20)!=0 ) {
			//debugf(0," cc_connect_cli(): wrong sha1 hash from client! (%s)\n",ip2string(ip));
			close(sock);
			return NULL;
		}
	} else {
		//debugf(0," cc_connect_cli(): recv sha1 timeout\n");
		close(sock);
		return NULL;
	}

  // receive username
	if ((i=recv_nonb(sock, buf, 20,3000)) == 20) {
		cc_decrypt(&recvblock, buf, i);
		memcpy(usr,buf,20);
		//debugf(0," cc_connect_cli(): username '%s'\n", usr);
	}
	else {
		//debugf(0," cc_connect_cli(): recv user timeout\n");
		close(sock);
		return NULL;
	}


  // Check for username
	if ( strcmp(cfg.freecccam.user,usr) ) {
		debugf(0," FreeCCcam: Failed to connect client (%s), wrong username\n",ip2string(ip));
		close(sock);
		return NULL;
	}

	pthread_mutex_lock(&prg.lockfreecccli);

	int found = 0;
	struct cc_client_data *cli = cfg.freecccam.server.client;
	while (cli) {
		if (cli->handle<=0) {
			found = 1;
			break;
		}
		else {
			if (cli->ip == ip) { // dont connect
				frcc_disconnect_cli(cli);
				found = 1;
				break;
			}
		}
		cli = cli->next;
	}
	// check for inactive clients
	if (!found) {
		uint32_t ticks = GetTickCount();
		while (cli) {
			if (cli->handle>0) {
				// Check if we can disconnect idle state clients
				if  ( (ticks-cli->lastecmtime) > 90000 ) frcc_disconnect_cli(cli);
			}
			if (cli->handle<=0) {
				found = 1;
				break;
			}
			cli = cli->next;
		}
	}

	pthread_mutex_unlock(&prg.lockfreecccli);

	if (!found) {
		debugf(0," FreeCCcam: Failed to connect client (%s), no available connection\n",ip2string(ip));
		close(sock);
		return NULL;
	}

  // receive passwd / 'CCcam'
	if ((i=recv_nonb(sock, buf, 6,3000)) == 6) {
		memset(pwd, 0, sizeof(pwd));
		strcpy( pwd, cfg.freecccam.pass);
		cc_encrypt(&recvblock, (uint8_t *)pwd, strlen(pwd));
		cc_decrypt(&recvblock, buf, 6);
		if (memcmp( buf, "CCcam\0",6)) {
			debugf(0," FreeCCcam: login failed from client(%s)\n",ip2string(ip));
			close(sock);
			return NULL;
		}
	} 
	else {
		close(sock);
		return NULL;
	}

  // send passwd ack
	memset(buf, 0, 20);
	memcpy(buf, "CCcam\0", 6);
	//debugf(0,"Server: send ack '%s'\n",buf);
	cc_encrypt(&sendblock, buf, 20);
	if ( !send_nonb(sock, buf, 20, 100) ) {
		close(sock);
		return NULL;
	}
	sprintf(cli->user,"%s", ip2string(ip));
	//cli->ecmnb=0;
	//cli->ecmok=0;
	memcpy(&cli->sendblock,&sendblock,sizeof(sendblock));
	memcpy(&cli->recvblock,&recvblock,sizeof(recvblock));
	debugf(0," FreeCCcam: client(%s) connected\n",ip2string(ip));

  // recv cli data
	memset(buf, 0, sizeof(buf));
	i = cc_msg_recv( sock, &cli->recvblock, buf, 3000);
	if (i!=97) {
		debugf(0," error recv cli data\n");
		close(sock);
		return NULL;
	}

  // Setup Client Data
//	pthread_mutex_lock(&prg.lockfreecccli);
	memcpy( cli->nodeid, buf+24, 8);
	memcpy( cli->version, buf+33, 32);
	memcpy( cli->build, buf+65, 32 );
	debugf(0," FreeCCcam: client(%s) running version %s build %s\n",ip2string(ip), cli->version, cli->build);  // cli->nodeid,8,
	cli->cardsent = 0;

	cli->connection.status = 1;
	cli->connection.time = GetTickCount();
	cli->lastactivity = GetTickCount();
	cli->lastecmtime = GetTickCount();
	cli->chkrecvtime = 0;
	cli->ip = ip;
	cli->msg.len = 0;
	cli->handle = sock;
	cli->ecm.busy = 0;

//	pthread_mutex_unlock(&prg.lockfreecccli);
  // send cli data ack
	cc_msg_send( sock, &cli->sendblock, CC_MSG_CLI_INFO, 0, NULL);
	//cc_msg_send( sock, &cli->sendblock, CC_MSG_BAD_ECM, 0, NULL);
	int sendversion = ( (cli->version[28]=='W')&&(cli->version[29]='H')&&(cli->version[30]='O') );
	cc_sendinfo_cli(cli, sendversion);
	//cc_msg_send( sock, &cli->sendblock, CC_MSG_BAD_ECM, 0, NULL);
	cli->cardsent = 1;
	usleep(99000);
	//if (!sendversion)
	frcc_sendcards_cli(cli);

	pipe_wakeup( frcc_pipe[1] );
	return cli;
}



void freecccam_srv_accept(struct cccam_server_data *srv)
{
	struct sockaddr_in newaddr;
	socklen_t socklen = sizeof(struct sockaddr);
	int newfd = accept( srv->handle, (struct sockaddr*)&newaddr, /*(socklen_t*)*/&socklen);
	if ( newfd<=0 ) {
		if ( (errno!=EAGAIN) && (errno!=EINTR) ) debugf( getdbgflag(DBG_CCCAM,0,0)," FreeCCcam%d: Accept failed (errno=%d)\n", errno);
	}
	else {
		uint32_t newip = newaddr.sin_addr.s_addr;
		if ( isblockedip(newip) ) {
			debugf(getdbgflag(DBG_CCCAM,0,0)," FreeCCcam%d: New Connection (%s) closed, ip blocked\n", ip2string(newip) );
			close(newfd);
		}
		else {
			pthread_t srv_tid;
			SetSocketKeepalive(newfd);
			SetSoketNonBlocking(newfd);
			//debugf(getdbgflag(DBG_CCCAM,0,0)," FreeCCcam%d: new connection (%s)\n", ip2string(newip) );
			struct connect_cli_data *newdata = malloc( sizeof(struct connect_cli_data) );
			newdata->server = srv; 
			newdata->sock = newfd; 
			newdata->ip = newaddr.sin_addr.s_addr;
			if ( !create_thread(&srv_tid, (threadfn)freecc_connect_cli,newdata) ) {
				free( newdata );
				close( newfd );
			}
		}
	}
}


void *freecccam_accept_thread(void *param)
{
	prctl(PR_SET_NAME,"FreeCCcam Accept",0,0,0);

	while(!prg.restart) {

		struct pollfd pfd[3];
		int pfdcount = 0;

		if ( !IS_DISABLED(cfg.freecccam.server.flags)&&(cfg.freecccam.server.handle>0) ) {
				cfg.freecccam.server.ipoll = pfdcount;
				pfd[pfdcount].fd = cfg.freecccam.server.handle;
				pfd[pfdcount++].events = POLLIN | POLLPRI;
		} else cfg.freecccam.server.ipoll = -1;

		if (pfdcount) {
			int retval = poll(pfd, pfdcount, 3006);
			if ( retval>0 ) {
				if ( !IS_DISABLED(cfg.freecccam.server.flags) && (cfg.freecccam.server.handle>0) && (cfg.freecccam.server.ipoll>=0) && (cfg.freecccam.server.handle==pfd[cfg.freecccam.server.ipoll].fd) ) {
					if ( pfd[cfg.freecccam.server.ipoll].revents & (POLLIN|POLLPRI) ) freecccam_srv_accept( &cfg.freecccam.server );
				}
			}
			else if (retval<0) usleep(96000);
		} else sleep(1);
	}
	return NULL;
}

////////////////////////////////////////////////////////////////////////////////
// CCCAM SERVER: SEND DCW TO CLIENTS
////////////////////////////////////////////////////////////////////////////////

void frcc_senddcw_cli(struct cc_client_data *cli)
{
	uint8_t buf[CC_MAXMSGSIZE];
	uint32_t ticks = GetTickCount();

	if (cli->ecm.status==STAT_DCW_SENT) {
		debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," +> cw send failed to CCcam client '%s', cw already sent\n", cli->user); 
		return;
	}
	if (cli->handle<=0) {
		debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," +> cw send failed to CCcam client '%s', client disconnected\n", cli->user); 
		return;
	}
	if (!cli->ecm.busy) {
		debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," +> cw send failed to CCcam client '%s', no ecm request\n", cli->user); 
		return;
	}

	ECM_DATA *ecm = cli->ecm.request;
	//FREEZE
	int samechannel = (cli->lastecm.caid==ecm->caid)&&(cli->lastecm.prov==ecm->provid)&&(cli->lastecm.sid==ecm->sid);
	int enablefreeze=0;
	if (samechannel) {
		if ( (cli->lastecm.hash!=ecm->hash)&&(cli->lastecm.tag!=ecm->ecm[0]) )
		if ( (cli->lastecm.status=1)&&(cli->lastdcwtime+200<ticks) ) enablefreeze = 1;
	} else cli->zap++;
	//
	cli->lastecm.caid = ecm->caid;
	cli->lastecm.prov = ecm->provid;
	cli->lastecm.sid = ecm->sid;
	cli->lastecm.hash = ecm->hash;
	cli->lastecm.tag = ecm->ecm[0];
	cli->lastecm.decodetime = ticks-cli->ecm.recvtime;
	cli->lastecm.request = cli->ecm.request;

	if ( (ecm->dcwstatus==STAT_DCW_SUCCESS)&&(ecm->hash==cli->ecm.hash) ) {
		cli->lastecm.dcwsrctype = ecm->dcwsrctype;
		cli->lastecm.dcwsrcid = ecm->dcwsrcid;
		cli->lastecm.status=1;
		cli->ecmok++;
		cli->lastdcwtime = ticks;
		cli->ecmoktime += ticks-cli->ecm.recvtime;
		//cli->lastecmoktime = ticks-cli->ecm.recvtime;
		memcpy( buf, ecm->cw, 16 );
		cc_crypt_cw( cli->nodeid, cli->ecm.cardid , buf);
		cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_ECM_REQUEST, 16, buf);
		cc_encrypt(&cli->sendblock, buf, 16); // additional crypto step
		debugf(0," => cw to CCcam client '%s' ch %04x:%06x:%04x (%dms)\n", cli->user, ecm->caid,ecm->provid,ecm->sid, GetTickCount()-cli->ecm.recvtime);
	}
	else { //if (ecm->data->dcwstatus==STAT_DCW_FAILED)
		if (enablefreeze) {
			cli->freeze++;
		}
		cli->lastecm.dcwsrctype = DCW_SOURCE_NONE;
		cli->lastecm.dcwsrcid = 0;
		cli->lastecm.status=0;
		cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_ECM_NOK1, 0, NULL);
		debugf(0," |> decode failed to CCcam client '%s' ch %04x:%06x:%04x (%dms)\n", cli->user, ecm->caid,ecm->provid,ecm->sid, GetTickCount()-cli->ecm.recvtime);
	}
	cli->ecm.busy=0;
	cli->ecm.status = STAT_DCW_SENT;
}


// Receive messages from clients
void freecc_cli_recvmsg(struct cc_client_data *cli)
{
	unsigned char buf[CC_MAXMSGSIZE];
	unsigned char data[CC_MAXMSGSIZE]; // for other use
	unsigned int cardid;
	int len;

	if (cli->handle>0) {
		len = cc_peekmsg( cli->handle, &cli->recvblock, &cli->msg, buf );
		uint32_t ticks = GetTickCount();
		if (len==0) {
			debugf(0," FreeCCcam: client(%s) read failed %d\n", cli->user,len);
			frcc_disconnect_cli(cli);
		}
		else if (len==-1) {
			if (!cli->chkrecvtime) cli->chkrecvtime = ticks;
			else if ( (cli->chkrecvtime+500)<ticks ) {
				debugf(0," FreeCCcam: client(%s) read failed %d\n", cli->user,len);
				frcc_disconnect_cli(cli);
			}
		}
		else if (len>0) {
			cli->chkrecvtime = 0;
				cli->lastactivity = ticks;
				switch (buf[1]) {
					 case CC_MSG_ECM_REQUEST:
						cli->ecmnb++;
						cli->lastecmtime = ticks;
						if (len<20) return; // Avoid malicious peers

						if (cli->ecm.busy) {
							// send decode failed
							debugf(0," <|> decode failed to FreeCCcam client(%s), too many ecm requests\n", cli->user);
							break;
						}
						cli->ecm.busy = 0;

						//Check for card availability
						memcpy( data, buf+17, len-17);
						cardid = buf[10]<<24 | buf[11]<<16 | buf[12]<<8 | buf[13];
						uint16_t caid = buf[4]<<8 | buf[5];
						uint16_t sid = buf[14]<<8 | buf[15];
						uint32_t provid = ecm_getprovid( data, caid );
						if (provid==0) provid = buf[6]<<24 | buf[7]<<16 | buf[8]<<8 | buf[9];

						// Check for Profile
						struct cardserver_data *cs=getcsbyid( cardid );
						if (!cs) {
							cli->ecmdenied++;
							cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_ECM_NOK2, 0, NULL);
							debugf(0," <|> decode failed to FreeCCcam client(%s), card-id %x not found\n", cli->user, cardid);
							break;
						}
						// Check ECM
						uint8_t cw1cycle;
						char *error = cs_accept_ecm(cs,caid,provid,sid,ecm_getchid(data,caid), len-17, data, &cw1cycle);
						if (error) {
							cli->ecmdenied++;
							cs->ecmdenied++;
							cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_ECM_NOK2, 0, NULL);
							debugf(0," <|> decode failed to FreeCCcam client(%s) ch %04x:%06x:%04x, %s\n", cli->user, caid,provid,sid, error);
							break;
						}
						// ACCEPTED
						pthread_mutex_lock(&prg.lockecm);
	
						// Search for ECM
						ECM_DATA *ecm = search_ecmdata_any(cs, data,  len-17, sid, caid); // dont get failed ecm request from cache
						int isnew =  ( ecm==NULL );
						if (ecm) {
							ecm->lastrecvtime = ticks;
							if (ecm->dcwstatus==STAT_DCW_FAILED) {
								cc_store_ecmclient(ecm, cli);
								debugf( getdbgflagpro(DBG_CCCAM,0,cli->id,cs->id)," <- ecm from FreeCCcam client '%s' ch %04x:%06x:%04x**\n", cli->user, caid, provid, sid);
								cli->ecm.busy=1;
								cli->ecm.hash = ecm->hash;
								cli->ecm.cardid = cardid;
								ecm->recvtime = ticks;
								ecm->dcwstatus = STAT_DCW_WAIT;
								ecm->cachestatus = 0; //ECM_CACHE_NONE; // Resend Request
								ecm->checktime = 1; // Check NOW
							}
							else { // SUCCESS/WAIT
								cc_store_ecmclient(ecm, cli);
								debugf(getdbgflagpro(DBG_CCCAM,0,cli->id,cs->id)," <- ecm from FreeCCcam client '%s' ch %04x:%06x:%04x*\n", cli->user, caid, provid, sid);
								cli->ecm.busy=1;
								cli->ecm.hash = ecm->hash;
								cli->ecm.cardid = cardid;
								// Check for Success/Timeout
								if (!ecm->checktime) frcc_senddcw_cli(cli);
							}
						}
						else {
							cs->ecmaccepted++;
							// Setup ECM Request for Server(s)
							ecm = store_ecmdata(cs, data, len-17, sid, caid, provid);
							cc_store_ecmclient(ecm, cli);
							debugf( getdbgflagpro(DBG_CCCAM,0,cli->id,cs->id), " <- ecm from FreeCCcam client(%s) ch %04x:%06x:%04x\n", cli->user,caid,provid,sid);
							cli->ecm.busy=1;
							cli->ecm.hash = ecm->hash;
							cli->ecm.cardid = cardid;
							ecm->dcwstatus = STAT_DCW_WAIT;
							ecm->checktime = 1; // Check NOW
							if (cs->option.fallowcache) {
								ecm->waitcache = 1;
								ecm->dcwstatus = STAT_DCW_WAITCACHE;
								ecm->checktime = ecm->recvtime + cs->option.cachetimeout;
								pipe_cache_find(ecm, cs);
							}
						}

						pthread_mutex_unlock(&prg.lockecm);
						if (isnew) wakeup_sendecm();
						break;

					 case CC_MSG_KEEPALIVE:
						//printf(" Keepalive from client '%s'\n",cli->user);
						cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_KEEPALIVE, 0, NULL);
						break;
				}
			}
	}
}

// Check sending cw to clients
void freecc_check_sendcw(ECM_DATA *ecm)
{
	struct cccam_server_data *cccam = &cfg.freecccam.server;
	if (cccam) {
		if ( !IS_DISABLED(cccam->flags) && (cccam->handle>0) ) {
			struct cc_client_data *cli = cccam->client;
			while (cli) {
				if ( !IS_DISABLED(cli->flags)&&(cli->handle>0)&&(cli->ecm.busy)&&(cli->ecm.request==ecm) ) {
					frcc_senddcw_cli( cli );
				}
				cli = cli->next;
			}
		}
	}
}


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

void frcc_recv_pipe()
{
	uint8_t buf[1024];
	struct pollfd pfd[2];
	do {
		int len = pipe_recv( frcc_pipe[0], buf);
		if (len>0) {
			switch(buf[0]) {
				case PIPE_WAKEUP:  // ADD NEW CLIENT
					//debugf(" wakeup csmsg\n");
					break;
			}
		}
		pfd[0].fd = frcc_pipe[0];
		pfd[0].events = POLLIN | POLLPRI;
	} while (poll(pfd, 1, 1)>0);
}

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

void *frcc_recvmsg_thread(void *param)
{
	struct pollfd pfd[MAX_PFD];
	int pfdcount;
	prctl(PR_SET_NAME,"FreeCCcam RecvMSG Thread",0,0,0);
	while(1) {
		pfdcount = 0;
		// PIPE
		pfd[pfdcount].fd = frcc_pipe[0];
		pfd[pfdcount++].events = POLLIN | POLLPRI;
		struct cccam_server_data *freecccam = &cfg.freecccam.server;
		if ( !IS_DISABLED(freecccam->flags)&&(freecccam->handle>0) ) {
			struct cc_client_data *cli = freecccam->client;
			while (cli && (pfdcount<MAX_PFD)) {
				if ( !IS_DISABLED(cli->flags)&&(cli->handle>0) ) {
					cli->ipoll = pfdcount;
					pfd[pfdcount].fd = cli->handle;
					pfd[pfdcount++].events = POLLIN | POLLPRI;
				} else cli->ipoll = -1;
				cli = cli->next;
			}
		}

		int retval = poll(pfd, pfdcount, 3008); // for 3seconds

		if ( retval>0 ) {

			// CCcam Clients
			struct cccam_server_data *freecccam = &cfg.freecccam.server;
			if ( !IS_DISABLED(freecccam->flags)&&(freecccam->handle>0) ) {
				//pthread_mutex_lock(&prg.lockcccli);
				struct cc_client_data *cli = freecccam->client;
				while (cli) {
					if ( !IS_DISABLED(cli->flags)&&(cli->handle>0)&&(cli->ipoll>=0)&&(cli->handle==pfd[cli->ipoll].fd) ) {
						if ( pfd[cli->ipoll].revents & (POLLHUP|POLLNVAL) ) frcc_disconnect_cli(cli);
						else if ( pfd[cli->ipoll].revents & (POLLIN|POLLPRI) ) {
							freecc_cli_recvmsg(cli);
						}
						///else if ( (GetTickCount()-cccli->lastactivity) > 600000 ) frcc_disconnect_cli(cccli);
					}
					cli = cli->next;
				}
				//pthread_mutex_unlock(&prg.lockcccli);
			}

			//
			if ( pfd[0].revents & (POLLIN|POLLPRI) ) frcc_recv_pipe();
		}
		else if ( retval<0 ) {
			debugf(0, " thread receive messages: poll error %d(errno=%d)\n", retval, errno);
		}

	}
	return NULL;
}



///////////////////////////////////////////////////////////////////////////////
// FREECCCAM SERVER: START/STOP
///////////////////////////////////////////////////////////////////////////////

int start_thread_freecccam()
{
	pthread_t tid;
#ifndef MONOTHREAD_ACCEPT
	create_thread(&tid, freecccam_accept_thread,NULL);
#endif

	create_thread(&tid, frcc_recvmsg_thread,NULL);
	return 0;
}