./ MultiCS.r69 / clustredcache.c
// Multics ID encryption

static unsigned char T1[]={
  0x2a,0xe1,0x0b,0x13,0x3e,0x6e,0x32,0x48,
  0xd3,0x31,0x08,0x8c,0x8f,0x95,0xbd,0xd0,
  0xe4,0x6d,0x50,0x81,0x20,0x30,0xbb,0x75,
  0xf5,0xd4,0x7c,0x87,0x2c,0x4e,0xe8,0xf4,
  0xbe,0x24,0x9e,0x4d,0x80,0x37,0xd2,0x5f,
  0xdb,0x04,0x7a,0x3f,0x14,0x72,0x67,0x2d,
  0xcd,0x15,0xa6,0x4c,0x2e,0x3b,0x0c,0x41,
  0x62,0xfa,0xee,0x83,0x1e,0xa2,0x01,0x0e,
  0x7f,0x59,0xc9,0xb9,0xc4,0x9d,0x9b,0x1b,
  0x9c,0xca,0xaf,0x3c,0x73,0x1a,0x65,0xb1,
  0x76,0x84,0x39,0x98,0xe9,0x53,0x94,0xba,
  0x1d,0x29,0xcf,0xb4,0x0d,0x05,0x7d,0xd1,
  0xd7,0x0a,0xa0,0x5c,0x91,0x71,0x92,0x88,
  0xab,0x93,0x11,0x8a,0xd6,0x5a,0x77,0xb5,
  0xc3,0x19,0xc1,0xc7,0x8e,0xf9,0xec,0x35,
  0x4b,0xcc,0xd9,0x4a,0x18,0x23,0x9f,0x52,
  0xdd,0xe3,0xad,0x7b,0x47,0x97,0x60,0x10,
  0x43,0xef,0x07,0xa5,0x49,0xc6,0xb3,0x55,
  0x28,0x51,0x5d,0x64,0x66,0xfc,0x44,0x42,
  0xbc,0x26,0x09,0x74,0x6f,0xf7,0x6b,0x4f,
  0x2f,0xf0,0xea,0xb8,0xae,0xf3,0x63,0x6a,
  0x56,0xb2,0x02,0xd8,0x34,0xa4,0x00,0xe6,
  0x58,0xeb,0xa3,0x82,0x85,0x45,0xe0,0x89,
  0x7e,0xfd,0xf2,0x3a,0x36,0x57,0xff,0x06,
  0x69,0x54,0x79,0x9a,0xb6,0x6c,0xdc,0x8b,
  0xa7,0x1f,0x90,0x03,0x17,0x1c,0xed,0xd5,
  0xaa,0x5e,0xfe,0xda,0x78,0xb0,0xbf,0x12,
  0xa8,0x22,0x21,0x3d,0xc2,0xc0,0xb7,0xa9,
  0xe7,0x33,0xfb,0xf1,0x70,0xe5,0x17,0x96,
  0xf8,0x8d,0x46,0xa1,0x86,0xe2,0x40,0x38,
  0xf6,0x68,0x25,0x16,0xac,0x61,0x27,0xcb,
  0x5b,0xc8,0x2b,0x0f,0x99,0xde,0xce,0xc5
};

static unsigned char T2[]={
  0xbf,0x11,0x6d,0xfa,0x26,0x7f,0xf3,0xc8,
  0x9e,0xdd,0x3f,0x16,0x97,0xbd,0x08,0x80,
  0x51,0x42,0x93,0x49,0x5b,0x64,0x9b,0x25,
  0xf5,0x0f,0x24,0x34,0x44,0xb8,0xee,0x2e,
  0xda,0x8f,0x31,0xcc,0xc0,0x5e,0x8a,0x61,
  0xa1,0x63,0xc7,0xb2,0x58,0x09,0x4d,0x46,
  0x81,0x82,0x68,0x4b,0xf6,0xbc,0x9d,0x03,
  0xac,0x91,0xe8,0x3d,0x94,0x37,0xa0,0xbb,
  0xce,0xeb,0x98,0xd8,0x38,0x56,0xe9,0x6b,
  0x28,0xfd,0x84,0xc6,0xcd,0x5f,0x6e,0xb6,
  0x32,0xf7,0x0e,0xf1,0xf8,0x54,0xc1,0x53,
  0xf0,0xa7,0x95,0x7b,0x19,0x21,0x23,0x7d,
  0xe1,0xa9,0x75,0x3e,0xd6,0xed,0x8e,0x6f,
  0xdb,0xb7,0x07,0x41,0x05,0x77,0xb4,0x2d,
  0x45,0xdf,0x29,0x22,0x43,0x89,0x83,0xfc,
  0xd5,0xa4,0x88,0xd1,0xf4,0x55,0x4f,0x78,
  0x62,0x1e,0x1d,0xb9,0xe0,0x2f,0x01,0x13,
  0x15,0xe6,0x17,0x6a,0x8d,0x0c,0x96,0x7e,
  0x86,0x27,0xa6,0x0d,0xb5,0x73,0x71,0xaa,
  0x36,0xd0,0x06,0x66,0xdc,0xb1,0x2a,0x5a,
  0x72,0xbe,0x3a,0xc5,0x40,0x65,0x1b,0x02,
  0x10,0x9f,0x3b,0xf9,0x2b,0x18,0x5c,0xd7,
  0x12,0x47,0xef,0x1a,0x87,0xd2,0xc2,0x8b,
  0x99,0x9c,0xd3,0x57,0xe4,0x76,0x67,0xca,
  0x3c,0xfb,0x90,0x20,0x14,0x48,0xc9,0x60,
  0xb0,0x70,0x4e,0xa2,0xad,0x35,0xea,0xc4,
  0x74,0xcb,0x39,0xde,0xe7,0xd4,0xa3,0xa5,
  0x04,0x92,0x8c,0xd9,0x7c,0x1c,0x7a,0xa8,
  0x52,0x79,0xf2,0x33,0xba,0x1f,0x30,0x9a,
  0x00,0x50,0x4c,0xff,0xe5,0xcf,0x59,0xc3,
  0xe3,0x0a,0x85,0xb3,0xae,0xec,0x0b,0xfe,
  0xe2,0xab,0x4a,0xaf,0x69,0x6c,0x2c,0x5d
};

#define SN(b) (((b&0xf0)>>4)+((b&0xf)<<4))

static void fase(unsigned char *k,unsigned char *D)
{
	unsigned char l,dt; // paso 1 

	for(l=0;l<4;++l) D[l]^=k[l];  // paso 2 

	for(l=0;l<4;++l) D[l]=T1[D[l]];

	for(l=6;l>3;--l) { 
		D[(l+2)&3]^=D[(l+1)&3]; 
		dt=(SN(D[(l+1)&3])+D[l&3])&0xff; 
		D[l&3]=T2[dt];
	} 
	for(l=3;l>0;--l) {
		D[(l+2)&3]^=D[(l+1)&3]; 
		D[l&3]=T1[(SN(D[(l+1)&3])+D[l&3])&0xff]; 
	} 
	D[2]^=D[1]; 
	D[1]^=D[0]; 
}


// Packet Encryption

void encryptcache(uint8_t *buf, int len)
{
	int i;
	for (i=1; i<len; i++) buf[i] = (buf[i]+i) & 0xff;
}

void decryptcache(uint8_t *buf, int len)
{
	int i;
	for (i=1; i<len; i++) buf[i] = (0xff00+buf[i]-i) & 0xff;
}

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

#define CACHE_STAT_WAIT     0
#define CACHE_STAT_DCW      1

#define CACHE_NONE_SENT     0
#define CACHE_REQUEST_SENT  1
#define CACHE_REPLY_SENT    2

struct cache_data {
	unsigned char status; // 0:Wait; 1: dcw received
	unsigned int recvtime;
	unsigned char tag;
	unsigned short sid;
	unsigned short onid;
	unsigned short caid;
	unsigned int hash;
	unsigned int prov;
	unsigned char cw[16];
	int peerid;
	//
	int sendpipe; // flag send dcw to ecmpipe
	int sendcache; // local cache send status
};


#define MAX_CACHETAB 9999


struct cache_data cachetab[MAX_CACHETAB];
int icachetab=0;

int prevcachetab( int index )
{
	if (index<1) index = MAX_CACHETAB-1; else index--; 
	return index;
}

struct cache_data *cache_new( struct cache_data *newdata )
{
	int i = icachetab;
	memset( &cachetab[i], 0, sizeof(struct cache_data) );
	cachetab[i].status = CACHE_STAT_WAIT; // 0:Wait; 1: dcw received
	cachetab[i].recvtime = GetTickCount();
	cachetab[i].tag = newdata->tag;
	cachetab[i].sid = newdata->sid;
	cachetab[i].onid = newdata->onid;
	cachetab[i].caid = newdata->caid;
	cachetab[i].hash = newdata->hash;
	cachetab[i].prov = newdata->prov;
	icachetab++;
	if (icachetab>=MAX_CACHETAB) icachetab=0;
	return &cachetab[i];
}

struct cache_data *cache_fetch( struct cache_data *thereq )
{
	int i = icachetab;
	uint32 ticks = GetTickCount();
	do {
		if (i<1) i = MAX_CACHETAB-1; else i--; 
		if ( (cachetab[i].recvtime+25000)<ticks ) return NULL;
		if ( (cachetab[i].hash==thereq->hash)&&(cachetab[i].caid==thereq->caid)&&(cachetab[i].tag==thereq->tag)&&(cachetab[i].sid==thereq->sid) )
			//if ( (cachetab[i].onid==0)||(cachetab[i].onid==thereq->onid) )
			return &cachetab[i];
	} while (i!=icachetab);
	return NULL;
}

int cache_check( struct cache_data *req )
{
	if ( ((req->tag&0xFE)!=0x80)||!req->caid||!req->hash ) return 0;
	//if (!cfg.cache.faccept0onid && !req->onid ) return 0;
	return 1;
}


int cache_check_request( unsigned char tag, unsigned short sid, unsigned short onid, unsigned short caid, unsigned int hash )
{
	if ( ((tag&0xFE)!=0x80)||!caid||!hash ) return 0;
	//if (!cfg.cache.faccept0onid && !onid ) return 0;
	return 1;
}

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


///////////////////////////////////////////////////////////////////////////////
// pipe --> cache
///////////////////////////////////////////////////////////////////////////////

int pipe_send_cache_find( ECM_DATA *ecm, struct cardserver_data *cs)
{
	if ( !cache_check_request(ecm->ecm[0], ecm->sid, cs->option.onid, ecm->caid, ecm->hash) ) return 0;
	//send pipe to cache
	uchar buf[32];
	buf[0] = PIPE_CACHE_FIND;
	buf[2] = ecm->ecm[0];
	buf[3] = (ecm->sid)>>8; buf[4] = (ecm->sid)&0xff;
	buf[5] = (cs->option.onid)>>8; buf[6] = (cs->option.onid)&0xff;
	buf[7] = (ecm->caid)>>8; buf[8] = (ecm->caid)&0xff;
	buf[9] = ecm->hash>>24; buf[10] = ecm->hash>>16; buf[11] = ecm->hash>>8; buf[12] = ecm->hash & 0xff;
	buf[13] = ecm->provid>>16; buf[14] = ecm->provid>>8; buf[15] = ecm->provid & 0xff;
	pipe_send( srvsocks[0], buf, 16);
	return 1;
}

int pipe_send_cache_request( ECM_DATA *ecm, struct cardserver_data *cs)
{
	if ( !cache_check_request(ecm->ecm[0], ecm->sid, cs->option.onid, ecm->caid, ecm->hash) ) return 0;
	//send pipe to cache
	uchar buf[32];
	buf[0] = PIPE_CACHE_REQUEST;

	buf[2] = ecm->ecm[0];
	buf[3] = (ecm->sid)>>8;
	buf[4] = (ecm->sid)&0xff;
	buf[5] = (cs->option.onid)>>8;
	buf[6] = (cs->option.onid)&0xff;
	buf[7] = (ecm->caid)>>8;
	buf[8] = (ecm->caid)&0xff;
	buf[9] = ecm->hash>>24;
	buf[10] = ecm->hash>>16;
	buf[11] = ecm->hash>>8;
	buf[12] = ecm->hash & 0xff;
	buf[13] = ecm->provid>>16; buf[14] = ecm->provid>>8; buf[15] = ecm->provid & 0xff;
	pipe_send( srvsocks[0], buf, 16);
	return 1;
}

int pipe_send_cache_reply( ECM_DATA *ecm, struct cardserver_data *cs)
{
	if ( !cache_check_request(ecm->ecm[0], ecm->sid, cs->option.onid, ecm->caid, ecm->hash) ) return 0;
	uchar buf[32];
	buf[0] = PIPE_CACHE_REPLY;

	buf[2] = ecm->ecm[0];
	buf[3] = (ecm->sid)>>8;
	buf[4] = (ecm->sid)&0xff;
	buf[5] = (cs->option.onid)>>8;
	buf[6] = (cs->option.onid)&0xff;
	buf[7] = (ecm->caid)>>8;
	buf[8] = (ecm->caid)&0xff;
	buf[9] = ecm->hash>>24;
	buf[10] = ecm->hash>>16;
	buf[11] = ecm->hash>>8;
	buf[12] = ecm->hash & 0xff;
	buf[13] = ecm->provid>>16; buf[14] = ecm->provid>>8; buf[15] = ecm->provid & 0xff;

	if (ecm->dcwstatus==STAT_DCW_SUCCESS) {
		memcpy(buf+16, ecm->cw, 16);
		pipe_send( srvsocks[0], buf, 32);
	}
	else {
		pipe_send( srvsocks[0], buf, 16);
	}
	return 1;
}



int pipe_send_cache( uint8_t type, ECM_DATA *ecm, struct cardserver_data *cs)
{
	if ( !cache_check_request(ecm->ecm[0], ecm->sid, cs->option.onid, ecm->caid, ecm->hash) ) return 0;
	uchar buf[32];
	buf[0] = type;
	buf[2] = ecm->ecm[0];
	buf[3] = (ecm->sid)>>8;
	buf[4] = (ecm->sid)&0xff;
	buf[5] = (cs->option.onid)>>8;
	buf[6] = (cs->option.onid)&0xff;
	buf[7] = (ecm->caid)>>8;
	buf[8] = (ecm->caid)&0xff;
	buf[9] = ecm->hash>>24;
	buf[10] = ecm->hash>>16;
	buf[11] = ecm->hash>>8;
	buf[12] = ecm->hash & 0xff;
	buf[13] = ecm->provid>>16; buf[14] = ecm->provid>>8; buf[15] = ecm->provid & 0xff;
	if (ecm->dcwstatus==STAT_DCW_SUCCESS) {
		memcpy(buf+16, ecm->cw, 16);
		pipe_send( srvsocks[0], buf, 32);
	}
	else {
		pipe_send( srvsocks[0], buf, 16);
	}
	return 1;
}


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


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

SOCKET outsock;

void sendtoip( uint32_t ip, int port, unsigned char *buf, int len)
{
	if (ip && port) {
		struct sockaddr_in si_other;
		int slen=sizeof(si_other);
		memset((char *) &si_other, 0, sizeof(si_other));
		si_other.sin_family = AF_INET;
		si_other.sin_port = htons( port );
		si_other.sin_addr.s_addr = ip;
		sendto(outsock, buf, len, 0, (struct sockaddr *)&si_other, slen);
	}
}

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

void sendtopeer( struct cachepeer_data *peer, unsigned char *buf, int len)
{

	if (peer->host->ip && peer->port) {
		struct sockaddr_in si_other;
		int slen=sizeof(si_other);
		memset((char *) &si_other, 0, sizeof(si_other));
		si_other.sin_family = AF_INET;
		si_other.sin_port = htons( peer->port );
		si_other.sin_addr.s_addr = peer->host->ip;
#ifdef DEBUG_NETWORK
		if (flag_debugnet) {
			debugf(getdbgflag(DBG_CACHE,0,0)," cache: send data (%d) to peer (%s:%d)\n", len, peer->host->name,peer->port);
			debughex(buf,len);
		}
#endif
		while (1) {
			struct pollfd pfd;
			pfd.fd = peer->outsock;
			pfd.events = POLLOUT;
			int retval = poll(&pfd, 1, 10);
			if (retval>0) {
				if ( pfd.revents & (POLLOUT) ) {
					sendto(peer->outsock, buf, len, 0, (struct sockaddr *)&si_other, slen);
				} else debugf(getdbgflag(DBG_CACHE,0,0)," cache: error sending data\n");
				break;
			}
			else if (retval<0) {
				if ( (errno!=EINTR)&&(errno!=EAGAIN) ) {
					debugf(getdbgflag(DBG_CACHE,0,0)," cache: error sending data\n");
					break;
				}
			}
			else debugf(getdbgflag(DBG_CACHE,0,0)," cache: error sending data\n");
		}
	}
}

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

#define TYPE_REQUEST   1
#define TYPE_REPLY     2
#define TYPE_PINGREQ   3
#define TYPE_PINGRPL   4
#define TYPE_RESENDREQ 5

#ifdef NEWCACHE

#define TYPE_HELLO          0x03
#define TYPE_HELLO_ACK      0x10
#define TYPE_KEEPALIVE      0x11
#define TYPE_KEEPALIVE_ACK  0x12
#define TYPE_CARD_LIST      0x13
#define TYPE_SMS            0x14
#define TYPE_SMS_ACK        0x15

#define TYPE_VERSION        0x90
#define TYPE_VERSION_ACK    0x91

#define TYPE_CHECKREQ       0x92
#define TYPE_CHECKREQ_ACK   0x93

#define TYPE_UNKNOWN        0xFF

#endif



int peer_acceptcard( struct cachepeer_data *peer, uint16_t caid, uint32_t prov)
{
	if (!peer->cards[0]) return 1;
	int i;
	int caprov = (caid<<16) | prov; 
	for (i=0; i<1024; i++) {
		if (!peer->cards[0]) return 0;
		if (peer->cards[i] == caprov) return 1;
	}
	return 0;
}		


void cache_send_request(struct cache_data *pcache,struct cachepeer_data *peer)
{
	uchar buf[16];
	//01 80 00CD 0001 0500 8D1DB359
	buf[0] = TYPE_REQUEST;
	buf[1] = pcache->tag;
	buf[2] = pcache->sid>>8;
	buf[3] = pcache->sid;
	buf[4] = pcache->onid>>8;
	buf[5] = pcache->onid&0xff;
	buf[6] = pcache->caid>>8;
	buf[7] = pcache->caid;
	buf[8] = pcache->hash>>24;
	buf[9] = pcache->hash>>16;
	buf[10] = pcache->hash>>8;
	buf[11] = pcache->hash;
	if (peer) {
		sendtopeer(peer, buf, 12);
		peer->sentreq++;
	}
	else {
		peer = cfg.cache.peer;
		while (peer) {
			if (!IS_DISABLED(peer->flags))
			if (peer->host->ip && peer->port)
#ifdef DONT_CHECK_PEER_ADDR
			if ( peer->ping>0 )
#endif
			if ( !peer->fblock0onid || pcache->onid )
			if ( peer_acceptcard(peer, pcache->caid, pcache->prov) ) {
				sendtopeer(peer, buf, 12);
				peer->sentreq++;
			}
			peer = peer->next;
		}
	}
}


int cache_send_reply(struct cache_data *pcache,struct cachepeer_data *peer)
{
	if (pcache->status==CACHE_STAT_DCW) {
		uchar buf[64];
		//Common Data
		buf[0] = TYPE_REPLY;
		buf[1] = pcache->tag;
		buf[2] = pcache->sid>>8;
		buf[3] = pcache->sid;
		buf[4] = pcache->onid>>8;
		buf[5] = pcache->onid&0xff;
		buf[6] = pcache->caid>>8;
		buf[7] = pcache->caid;
		buf[8] = pcache->hash>>24;
		buf[9] = pcache->hash>>16;
		buf[10] = pcache->hash>>8;
		buf[11] = pcache->hash;
		buf[12] = pcache->tag;
		memcpy( buf+13, pcache->cw, 16);
		if (peer) {
			sendtopeer(peer, buf, 29);
			peer->sentrep++;
			return 1;
		}
		else {
			int count = 0;
			peer = cfg.cache.peer;
			while ( peer ) {
				if ( !IS_DISABLED(peer->flags) )
				if ( peer->host->ip && peer->port )
#ifdef DONT_CHECK_PEER_ADDR
				if ( peer->ping>0 )
#endif
				if ( !peer->fblock0onid || pcache->onid )
				if ( peer_acceptcard(peer, pcache->caid, pcache->prov) ) {
					sendtopeer(peer, buf, 29);
					peer->sentrep++;
					count++;
				}
				peer = peer->next;
			}
			return count;
		}
	}
	return 0;
}

void cache_send_resendreq(struct cache_data *pcache,struct cachepeer_data *peer)
{
	// <1:TYPE_RESENDREQ> <2:port> <1:ecmtag> <2:sid> <2:onid> <2:caid> <4:hash>
	uchar buf[64];
	buf[0] = TYPE_RESENDREQ;
	//Port
	buf[1] = 0;
	buf[2] = 0;
	buf[3] = cfg.cache.port>>8;
	buf[4] = cfg.cache.port&0xff;

	buf[5] = pcache->tag;
	buf[6] = pcache->sid>>8;
	buf[7] = pcache->sid;
	buf[8] = pcache->onid>>8;
	buf[9] = pcache->onid&0xff;
	buf[10] = pcache->caid>>8;
	buf[11] = pcache->caid;
	buf[12] = pcache->hash>>24;
	buf[13] = pcache->hash>>16;
	buf[14] = pcache->hash>>8;
	buf[15] = pcache->hash;

	if (peer) sendtopeer(peer, buf, 16);
	else {
		peer = cfg.cache.peer;
		while ( peer ) {
			if ( !IS_DISABLED(peer->flags) )
			if ( peer->host->ip && peer->port )
			if ( peer->ping>0 )
			if ( peer_acceptcard(peer, pcache->caid, pcache->prov) )
				sendtopeer(peer, buf, 16);
			peer = peer->next;
		}
	}
}


void cache_send_ping(struct cachepeer_data *peer)
{
	unsigned char buf[32];
	buf[0] = TYPE_PINGREQ;
	// New Cache IDENT
	buf[1] = 'M';
	buf[2] = 'C';
	buf[3] = 1;
	// PEER ID
	buf[4] = peer->id>>8; 
	buf[5] = peer->id&0xff;
	// MULTICS CRC
	buf[6] = peer->crc[0] = 0xff & rand();
	buf[7] = peer->crc[1] = 0xff & rand();
	buf[8] = peer->crc[2] = 0xff & rand();
	//Port
	buf[9] = peer->crc[3] = 0;
	buf[10] = 0;
	buf[11] = cfg.cache.port>>8;
	buf[12] = cfg.cache.port&0xff;
	//Program
	buf[13] = 0x01; //ID
	buf[14] = 7; //LEN
	buf[15] = 'M'; buf[16] = 'u'; buf[17] = 'l'; buf[18] = 't'; buf[19] = 'i'; buf[20] = 'C'; buf[21] = 'S';
	//Version
	buf[22] = 0x02; //ID
	buf[23] = 3; //LEN
	buf[24] = 'r'; buf[25] = '0'+(REVISION/10); buf[26] = '0'+(REVISION%10);
	//
	sendtopeer( peer, buf, 27);
}


#ifdef NEWCACHE

void cache_send_keepalive(struct cachepeer_data *peer)
{
	if (peer->protocol&1) {
		unsigned char buf[32];
		buf[0] = TYPE_KEEPALIVE;
		buf[1] = peer->id>>8; 
		buf[2] = peer->id&0xff;
		sendtopeer( peer, buf, 3);
	}
	else cache_send_ping(peer);
}


struct sms_data *cache_new_sms(char *msg)
{
	struct sms_data *sms = malloc( sizeof(struct sms_data) );
	int len = strlen(msg);
	uint32_t hash = hashCode((uint8_t*)msg, len);
	strcpy( sms->msg, msg );
	sms->hash = hash;
	gettimeofday( &sms->tv, NULL );
	sms->next = NULL;
	return (sms);
}

void cache_send_sms(struct cachepeer_data *peer, struct sms_data *sms)
{
	int len = strlen(sms->msg);
	sms->status = 1; // bit 0 (0:in,1:out) bit 1 (0:unread/unack, 1:read/ack)
	sms->next = peer->sms;
	peer->sms = sms;
	// Send Buffer
	uint8_t buf[1024];
	buf[0] = TYPE_SMS;
	buf[1] = sms->hash>>24;
	buf[2] = sms->hash>>16;
	buf[3] = sms->hash>>8;
	buf[4] = sms->hash;
	memcpy( buf+5, sms->msg, len );
	sendtopeer(peer, buf, len+5);
	// Debug
	debugf(getdbgflag(DBG_CACHE,0,0)," SMS to peer (%s:%d)\n", peer->host->name, peer->port);
}


#endif

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

#ifdef DONT_CHECK_PEER_ADDR
struct cachepeer_data *getpeerbyip(uint32_t ip)
{
	struct cachepeer_data *peer = cfg.cache.peer;
	while(peer) {
		if (peer->host->ip==ip) return peer;
		peer = peer->next;
	}
	return NULL;
}
#endif

struct cachepeer_data *getpeerbyaddr(uint32_t ip, uint16_t port)
{
	struct cachepeer_data *peer = cfg.cache.peer;
	while(peer) {
		if ( (peer->host->ip==ip)&&(peer->recvport==port) ) return peer;
		peer = peer->next;
	}
	return NULL;
}

struct cachepeer_data *getpeerbyid(int id)
{
	struct cachepeer_data *peer = cfg.cache.peer;
	while(peer) {
		if (peer->id==id) return peer;
		peer = peer->next;
	}
	return NULL;
}


void peer_check_messages( struct cachepeer_data *peer )
{
	// Remove Old Messages if there is too much
	struct sms_data *sms = peer->sms;
	int nb = 0;
	while (sms) {
		nb++;
		if (nb>=30) break;
		sms = sms->next;
	}
	// Remove Messages
	if (sms) {
		struct sms_data *next = sms->next;
		sms->next = NULL;
		while (next) {
			sms = next;
			next = sms->next;
			free(sms);
		}
	}
}


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

void cache_recvmsg()
{
	unsigned int recv_ip;
	unsigned short recv_port;
	unsigned char buf[1024];
	char str[1024];
	struct sockaddr_in si_other;
	socklen_t slen=sizeof(si_other);
	struct cachepeer_data *peer;

	int received = recvfrom( cfg.cache.handle, buf, sizeof(buf), 0, (struct sockaddr*)&si_other, &slen);
	if (received>1020) return;
	memcpy( &recv_ip, &si_other.sin_addr, 4);
	recv_port = ntohs(si_other.sin_port);

	if (received>0) {
#ifdef DEBUG_NETWORK
		if (flag_debugnet) {
			debugf(getdbgflag(DBG_CACHE,0,0)," cache: recv data (%d) from address (%s:%d)\n", received, ip2string(recv_ip), recv_port );
			debughex(buf,received);
		}
#endif
		// Store Data
		struct cache_data req;

		switch(buf[0]) {

				// 03 00 00 01 2F EE B1 CB 54 00 00 D8 03 
				case TYPE_PINGREQ:
					// Check Peer
					peer = cfg.cache.peer;
					int port = (buf[11]<<8)|buf[12];
					struct cachepeer_data *peerip = NULL;
					while (peer) {
						if (peer->host->ip==recv_ip) {
							peerip = peer;
							if (peer->port==port) break; // Found
						}
						peer = peer->next;
					}
					if (!peer) {
						if (peerip) {
							// Check for peer reuse
							peer = cfg.cache.peer;
							while (peer) {
								if ( (peer->port==port) && !strcmp(peer->host->name,peerip->host->name) ) break;
								peer = peer->next;
							}
							if (!peer) {
								if (peerip->port==0) peerip->port = port;
								else {
									// add new peer
									peer = malloc( sizeof(struct cachepeer_data) );
									memset( peer, 0, sizeof(struct cachepeer_data) );
									peer->host = peerip->host;
									peer->port = port;
									peer->id = cfg.cachepeerid;
									peer->outsock = CreateClientSockUdp();
									peer->runtime = 1;
									peer->fblock0onid = peerip->fblock0onid;
									//peer->fsendrep = peerip->fsendrep;
									cfg.cachepeerid++;
									cfg_addcachepeer(&cfg, peer);
									debugf(getdbgflag(DBG_CACHE,0,0), " Cache: new peer (%s:%d)\n", peer->host->name, peer->port );
								}
							}
							else {
								peer->host->checkiptime = 0;
							}
						}
						else debugf(getdbgflag(DBG_CACHE,0,0), " Cache Alert: unknown peer (%s:%d)\n", ip2string(recv_ip), port );
					}
					//
					if (peer) {
						// Check Status
						if (IS_DISABLED(peer->flags)) break;
						// Set Defaults
						memset( peer->cards, 0, sizeof(peer->cards) );
						peer->protocol = 0; // Normal CSP Protocol
						strcpy(peer->program,"CSP");
						peer->version[0] = 0;
						// Check for extended reply ( Program Name/Version )
						int index = 13;
						while (received>index) {
							if ( (index+buf[index+1]+2)>received ) break;
							switch(buf[index]) {
								case 0x01:
									if (buf[index+1]<32) { memcpy(peer->program, buf+index+2, buf[index+1]); peer->program[buf[index+1]] = 0; }
									break;
								case 0x02:
									if (buf[index+1]<32) { memcpy(peer->version, buf+index+2, buf[index+1]); peer->version[buf[index+1]] = 0; }
									break;
							}
							index += 2+buf[index+1];
						}
						// Set Default Reply
						buf[0] = TYPE_PINGRPL;
						// Check for New Protocol
						if ( buf[1]=='M' && buf[2]=='C' ) {
							peer->protocol = buf[3];
							if (peer->protocol&1) {
								buf[0] = TYPE_HELLO_ACK;
								// Decode CRC
								buf[13] = cfg.cache.port>>8;
								buf[14] = cfg.cache.port;
								fase( buf+11, buf+6);
							}
						}
						sendtopeer( peer, buf, 9);
						// Check for activity
						if (peer->recvport!=recv_port) {
							peer->ping = 0;
							peer->recvport = recv_port;
							peer->ismultics = 0;
						}
					}
					break;


				case TYPE_PINGRPL:
					// Get Peer
					peer = cfg.cache.peer;
					int peerid = (buf[4]<<8) | buf[5];
					while (peer) {
						if ( (peer->host->ip==recv_ip)&&(peer->id==peerid) ) {
							peer->protocol = 0;
							peer->recvport = recv_port;
							peer->lastpingrecv = GetTickCount();
							if (peer->ping>0)
								peer->ping = (peer->ping+peer->lastpingrecv-peer->lastpingsent)/2;
							else {
								debugf(getdbgflag(DBG_CACHE,0,peer->id), " Cache: Peer (%s:%d) come Online\n", peer->host->name, peer->port );
								peer->ping = peer->lastpingrecv-peer->lastpingsent;
							}
							peer->ping++;
							break;
						}
						peer = peer->next;
					}
					break;

				case TYPE_REQUEST:
					// Check Peer
					peer = getpeerbyaddr(recv_ip,recv_port);
#ifdef DONT_CHECK_PEER_ADDR
					if (!peer) peer = getpeerbyip(recv_ip);
#endif
					if (!peer) break;
					// Check Status
					if (IS_DISABLED(peer->flags)) break;
					// Check Multics r50/r51
					if ( !strcmp("r50",peer->version)||!strcmp("r51",peer->version) ) break;
					// Get DATA
					req.tag = buf[1];
					req.sid = (buf[2]<<8) | buf[3];
					req.onid = (buf[4]<<8) | buf[5];
					req.caid = (buf[6]<<8) | buf[7];
					req.hash = (buf[8]<<24) | (buf[9]<<16) | (buf[10]<<8) |buf[11];
					// Check Cache Request
					if (!cache_check(&req)) break;
					//
					peer->reqnb++;
					// ADD CACHE
					struct cache_data *pcache = cache_fetch( &req );
					if (pcache==NULL) {
						pcache = cache_new( &req );
						// Send Tracker
						if (cfg.cache.tracker) {
							pcache->sendcache = CACHE_REQUEST_SENT;
							cfg.cache.req++;
							struct cachepeer_data *p = cfg.cache.peer;
							while (p) {
								if (p!=peer)
								if (!IS_DISABLED(p->flags))
								if (p->host->ip && p->port)
								if (p->ping>0)
								if ( !p->fblock0onid || pcache->onid )
									cache_send_request(pcache,p);
								p = p->next;
							}
						}
					}
					else
					 if ( !cfg.cache.tracker && (pcache->status==CACHE_STAT_DCW) && (pcache->sendcache==CACHE_NONE_SENT) ) {
						peer->ihitfwd++;
						peer->hitfwd++;
						cache_send_reply(pcache,peer);
					}
					break;

				case TYPE_REPLY:
					// Check Peer
					peer = getpeerbyaddr(recv_ip,recv_port);
#ifdef DONT_CHECK_PEER_ADDR
					if (!peer) peer = getpeerbyip(recv_ip);
#endif
					if (!peer) break;
					// Check Status
					if (IS_DISABLED(peer->flags)) break;
					// Check Multics r50/r51
					if ( !strcmp("r50",peer->version)||!strcmp("r51",peer->version) ) break;
					// Check Integrity
					if (buf[12]!=buf[1]) break;
					// SetUp Request
					req.tag = buf[1];
					req.sid = (buf[2]<<8) | buf[3];
					req.onid = (buf[4]<<8) | buf[5];
					req.caid = (buf[6]<<8) | buf[7];
					req.hash = (buf[8]<<24) | (buf[9]<<16) | (buf[10]<<8) |buf[11];
					// Check Cache Request
					if (!cache_check(&req)) break;
					//
					if (received==13) break;
					else if (received>=29) {
						if ( !acceptDCW(buf+13) ) break;
						peer->repok++; // Request+Reply
						// Search for Cache data
						struct cache_data *pcache = cache_fetch( &req );
						if (pcache==NULL) pcache = cache_new( &req );
						//
						if ( (pcache->status!=CACHE_STAT_DCW)
							|| ( (pcache->status==CACHE_STAT_DCW) && pcache->sendpipe && memcmp(pcache->cw, buf+13, 16) )
						) {
							//*debugf(" [CACHE] Update Cache DCW %04x:%04x:%08x\n", pcache->caid, pcache->sid, pcache->hash);
							pcache->peerid = peer->id;
							memcpy(pcache->cw, buf+13, 16);
							pcache->status = CACHE_STAT_DCW;
							// Send PIPE
							if (pcache->sendpipe) {
								uchar buf[32];
								buf[0] = PIPE_CACHE_FIND_SUCCESS;
								buf[1] = 11+2+16; // Data length
								buf[2] = pcache->tag;
								buf[3] = pcache->sid>>8; buf[4] = pcache->sid&0xff;
								buf[5] = pcache->onid>>8; buf[6] = pcache->onid&0xff;
								buf[7] = pcache->caid>>8; buf[8] = pcache->caid&0xff;
								buf[9] = pcache->hash>>24; buf[10] = pcache->hash>>16; buf[11] = pcache->hash>>8; buf[12] = pcache->hash & 0xff;
								buf[13] = peer->id>>8; buf[14] = peer->id&0xff;
								memcpy( buf+15, pcache->cw, 16);
								//*debugf(" pipe Cache->Ecm: PIPE_CACHE_FIND_SUCCESS %04x:%04x:%08x\n",pcache->caid, pcache->sid, pcache->hash); // debughex(buf, 13+16);
								pipe_send( srvsocks[1], buf, 13+2+16);
								//pcache->sendpipe = 0;
							}
							// Send Tracker
							if (cfg.cache.tracker) {
								pcache->sendcache = CACHE_REPLY_SENT;
								cfg.cache.rep++;
								struct cachepeer_data *p = cfg.cache.peer;
								while (p) {
									if (p!=peer)
									if (!IS_DISABLED(p->flags))
									if (p->host->ip && p->port)
#ifdef DONT_CHECK_PEER_ADDR
									if (p->ping>0)
#endif
									if ( !p->fblock0onid || pcache->onid )
										cache_send_reply(pcache,p);
									p = p->next;
								}
							}
							else {
								struct cachepeer_data *p = cfg.cache.peer;
								while (p) {
									if (p->fsendrep)
									if (p!=peer)
									if (!IS_DISABLED(p->flags))
									if (p->host->ip && p->port)
#ifdef DONT_CHECK_PEER_ADDR
									if (p->ping>0)
#endif
									if ( !p->fblock0onid || pcache->onid )
										cache_send_reply(pcache,p);
									p = p->next;
								}
							}
						}
					}
					break;

				case TYPE_RESENDREQ:
					// Check Peer
					peer = getpeerbyaddr(recv_ip,recv_port);
					if (!peer) break;
					// Check Status
					if (IS_DISABLED(peer->flags)) break;
					// Check Multics r50/r51
					if ( !strcmp("r50",peer->version)||!strcmp("r51",peer->version) ) break;
					//
					if (received>=16) { // Good Packet
						struct cache_data req;
						req.tag = buf[5];
						req.sid = (buf[6]<<8) | buf[7];
						req.onid = (buf[8]<<8) | buf[9];
						req.caid = (buf[10]<<8) | buf[11];
						req.hash = (buf[12]<<24) | (buf[13]<<16) | (buf[14]<<8) | buf[15];
						// Check Cache Request
						if (!cache_check(&req)) break;
						struct cache_data *pcache = cache_fetch( &req );
						if ( (pcache!=NULL)&&(pcache->status==CACHE_STAT_DCW) ) {
							buf[4] = TYPE_REPLY;
							buf[16] = buf[3];
							memcpy( buf+17, pcache->cw, 16);
							sendtopeer( peer, buf+4, 29);
						}
					}
					break;


#ifdef NEWCACHE
				case TYPE_HELLO_ACK:
					// Get Peer
					peerid = (buf[4]<<8) | buf[5];
					peer = cfg.cache.peer;
					while (peer) {
						if ( (peer->host->ip==recv_ip)&&(peer->id==peerid) ) {
							// Check for new cache
							uint8_t k[4]; k[0] = cfg.cache.port>>8; k[1] = cfg.cache.port; k[2]=peer->port>>8; k[3]=peer->port; 
							fase( k, peer->crc);
							if ( (peer->crc[0]==buf[6])&&(peer->crc[1]==buf[7])&&(peer->crc[2]==buf[8]) ) peer->ismultics =1; else peer->ismultics = 0;
							peer->recvport = recv_port;
							peer->lastpingrecv = GetTickCount();
							if (peer->ping>0)
								peer->ping = (peer->ping+peer->lastpingrecv-peer->lastpingsent)/2;
							else {
								peer->ping = peer->lastpingrecv-peer->lastpingsent;
								debugf(getdbgflag(DBG_CACHE,0,peer->id), " Cache: Peer (%s:%d) come Online*\n", peer->host->name, peer->port );
							}
							peer->ping++;
							//debugf(getdbgflag(DBG_CACHE,0,peer->id), " Cache: sending card data to peer (%s:%d)\n", peer->host->name, peer->port );
							// Send CARDS DATA
							buf[0] = TYPE_CARD_LIST;
							buf[1] = 1; // Reset Cards
							int pos = 2;
							struct cardserver_data *cs = cfg.cardserver;
							while (cs) {
								int i;
								if (cs->option.fallowcache)
								for (i=0; i<cs->card.nbprov; i++) {
									uint32_t caprov = (cs->card.caid<<16)|(cs->card.prov[i]);
 									buf[pos] = caprov>>24;
 									buf[pos+1] = caprov>>16;
 									buf[pos+2] = caprov>>8;
 									buf[pos+3] = caprov;
									pos +=4;
									if (pos>400) {
										sendtopeer( peer, buf, pos);
										buf[0] = TYPE_CARD_LIST;
										buf[1] = 0; // no Reset
										pos = 2;
									}
								}
								cs = cs->next;
							}
							if (pos>2) {
								sendtopeer( peer, buf, pos);
							}
							break;
						}
						peer = peer->next;
					}
					break;

				case TYPE_CARD_LIST:
					// Check Peer
					peer = getpeerbyaddr(recv_ip,recv_port);
					if (!peer) break;
					// reset cards
					int i = 0;
					if (buf[1]&1) memset( peer->cards, 0, sizeof(peer->cards) ); 
					else for (i=0; i<1024; i++) if (!peer->cards[i]) break;
					//
					int totalcards = (received-2)/4;
					int j=0;
					while ( (j<totalcards)&&(i<1024) ){
						peer->cards[i] = (buf[2+j*4]<<24)|(buf[3+j*4]<<16)|(buf[4+j*4]<<8)|(buf[5+j*4]);
						j++; i++; 
					}
					break;

				case TYPE_KEEPALIVE:
					// Check Peer
					peer = getpeerbyaddr(recv_ip,recv_port);
					if (!peer) break;
					// Check Status
					if (IS_DISABLED(peer->flags)) break;
					// Send Reply
					buf[0] = TYPE_KEEPALIVE_ACK;
					sendtopeer( peer, buf, received);
					break;


				case TYPE_KEEPALIVE_ACK:
					// Check Peer
					peer = getpeerbyaddr(recv_ip,recv_port);
					if (!peer) break;
					// Check Status
					if (IS_DISABLED(peer->flags)) break;
					//
					peer->lastpingrecv = GetTickCount();
					if (peer->ping>0)
						peer->ping = (peer->ping+peer->lastpingrecv-peer->lastpingsent)/2;
					else
						peer->ping = peer->lastpingrecv-peer->lastpingsent;
					peer->ping++;
					break;

				case TYPE_CHECKREQ:
					//
					if (received!=20) break;
					decryptcache( buf, received );
					if (buf[19]!=TYPE_CHECKREQ) break;
					struct cache_data req;
					req.tag = buf[1];
					req.sid = (buf[2]<<8) | buf[3];
					req.onid = (buf[4]<<8) | buf[5];
					req.caid = (buf[6]<<8) | buf[7];
					req.hash = (buf[8]<<24) | (buf[9]<<16) | (buf[10]<<8) | buf[11];
					uint32_t ip = (buf[12]<<24) | (buf[13]<<16) | (buf[14]<<8) | buf[15];
					port =  (buf[16]<<8) | buf[17];
					int uphops = buf[18];
					// Get Cache Request
					if (!cache_check(&req)) break;
					pcache = cache_fetch( &req );
					if ( (pcache!=NULL)&&(pcache->status==CACHE_STAT_DCW) ) {
						buf[0] = TYPE_CHECKREQ_ACK;
						buf[12] = pcache->tag;
						memcpy( buf+13, pcache->cw, 16);
						buf[29] = TYPE_CHECKREQ_ACK;
						encryptcache( buf, 30 );
						sendtoip( ip, port, buf, 30 );
					}
					else if ( uphops && port && ip ) {
						buf[18]--;
						// Send to MultiCS PEERS
						encryptcache( buf, 20 );
						struct cachepeer_data *p = cfg.cache.peer;
						while (p) {
							if (!IS_DISABLED(p->flags))
							if (p->host->ip && p->port)
							if ( p->ping>0 )
							//if ( peer_acceptcard(p, req.caid, req.prov) )
							if ( p->protocol&1 )
							if ( p->ismultics )
								sendtopeer(p, buf, 20);
							p = p->next;
						}
					}
					break;


				case TYPE_SMS:
					peer = getpeerbyaddr(recv_ip,recv_port);
					if (!peer) break;
					if (received<6) break;
					uint32_t hash = (buf[1]<<24) | (buf[2]<<16) | (buf[3]<<8) | (buf[4]);
					buf[received] = 0;
					//
					peer_check_messages( peer );
					// Create data
					struct sms_data *sms = malloc( sizeof(struct sms_data) );
					strcpy( sms->msg, (char*)buf+5);
					sms->hash = hash;
					sms->status = 0; // bit 0 (0:in,1:out) bit 1 (0:unread/unack, 1:read/ack)
					gettimeofday( &sms->tv, NULL );
					sms->next = peer->sms;
					peer->sms = sms;

					// SEND ACK
					buf[0] = TYPE_SMS_ACK;
					sendtopeer( peer, buf, 5 );
					// debug
					debugf(getdbgflag(DBG_CACHE,0,0)," Cache: SMS from peer (%s:%d)\n", peer->host->name, peer->port);
					break;

				case TYPE_SMS_ACK:
					peer = getpeerbyaddr(recv_ip,recv_port);
					if (!peer) break;
					if (received!=5) break;
					if (!peer->sms) break;
					hash = (buf[1]<<24) | (buf[2]<<16) | (buf[3]<<8) | (buf[4]);
					// Search for data
					sms = peer->sms;
					while (sms) {
						if ( (sms->hash==hash)&&(sms->status==1) ) {
							debugf(getdbgflag(DBG_CACHE,0,0)," Cache: SMS ACK from peer (%s:%d)\n", peer->host->name, peer->port);
							sms->status = 3;
						}
						sms = sms->next;
					}
					break;
#endif

				case TYPE_VERSION:
					peer = getpeerbyaddr(recv_ip,recv_port);
					if ( peer && (peer->protocol&1) ) {
						buf[0] = TYPE_UNKNOWN;
						buf[1] = TYPE_VERSION;
						sendtopeer( peer, buf, 2 );
					}
					break;

				case TYPE_UNKNOWN:
					break;

				default:
					if (received>100) array2hex( buf, str, 100); else array2hex( buf, str, received);
					debugf(getdbgflag(DBG_CACHE,0,0)," Cache: Unknown message from (%s) : %s\n", ip2string(recv_ip), received, str );
#ifdef NEWCACHE
					peer = getpeerbyaddr(recv_ip,recv_port);
					if ( peer && (peer->protocol&1) ) {
						buf[1] = buf[0];
						buf[0] = TYPE_UNKNOWN;
						sendtopeer( peer, buf, 2 );
					}
#endif
					break;
			}
	}
}

  
void cache_pipe_recvmsg()
{
	uchar buf[300];
	struct cache_data req;
	struct cache_data *pcache;

	int len =  pipe_recv( srvsocks[1], buf );
	if (len>0) {
		//debugf(" Recv from Cache Pipe\n"); debughex(buf,len);
		switch(buf[0]) {
			case PIPE_LOCK:
				pthread_mutex_lock(&prg.lockmain);
				pthread_mutex_unlock(&prg.lockmain);
				break;
			case PIPE_CACHE_FIND:
				//Check for cachepeer
				req.tag = buf[2];
				req.sid = (buf[3]<<8) | buf[4];
				req.onid = (buf[5]<<8) | buf[6];
				req.caid = (buf[7]<<8) | buf[8];
				req.hash = (buf[9]<<24) | (buf[10]<<16) | (buf[11]<<8) | (buf[12]);
				req.prov = (buf[13]<<16) | (buf[14]<<8) | (buf[15]);
				// Check Cache Request
				if (!cache_check(&req)) {
					// Send find failed
					buf[0] = PIPE_CACHE_FIND_FAILED;
					buf[1] = 11;
					pipe_send( srvsocks[1], buf, 13);
					break;
				}
				pcache = cache_fetch( &req );
				if (pcache==NULL) {
					pcache = cache_new( &req );
					pcache->sendpipe = 1;
					// Send find failed
					buf[0] = PIPE_CACHE_FIND_FAILED;
					buf[1] = 11;
					//*debugf(" Pipe Cache->Ecm: PIPE_CACHE_FIND_FAILED\n"); debughex(buf,len);
					pipe_send( srvsocks[1], buf, 13);
				}
				else {
					pcache->prov = req.prov;
					//if (!pcache->sid) pcache->sid = req.sid;
					//if (!pcache->caid) pcache->caid = req.caid;
					if (pcache->status==CACHE_STAT_DCW) {
						struct cachepeer_data *peer = getpeerbyid(pcache->peerid);
						if (peer) {
							peer->lastcaid = pcache->caid;
							peer->lastprov = pcache->prov;
							peer->lastsid = pcache->sid;
							peer->lastdecodetime = 0; //ticks - ecm->recvtime;
							buf[13] = peer->id>>8; buf[14] = peer->id & 0xff;
							memcpy( buf+15, pcache->cw, 16);
							buf[0] = PIPE_CACHE_FIND_SUCCESS;
							buf[1] = 11+2+16;
							//*debugf(" Pipe Cache->Ecm: PIPE_CACHE_FIND_SUCCESS\n"); debughex(buf,len);
							pipe_send( srvsocks[1], buf, 13+2+16);
							pcache->sendpipe = 0;
						}// else buf[13]=0; buf[14]=0;
					}
					else if (pcache->status==CACHE_STAT_WAIT) {
						pcache->sendpipe = 1;
						buf[0] = PIPE_CACHE_FIND_WAIT;
						buf[1] = 11;
						//debugf(" Pipe Cache->Ecm: PIPE_CACHE_FIND_WAIT\n");
						pipe_send( srvsocks[1], buf, 13);
					}
					else  {
						pcache->sendpipe = 1;
						// Send find failed
						buf[0] = PIPE_CACHE_FIND_FAILED;
						buf[1] = 11;
						//*debugf(" Pipe Cache->Ecm: PIPE_CACHE_FIND_FAILED\n"); debughex(buf,len);
						pipe_send( srvsocks[1], buf, 13);
					}
				}
				//debugf(" Cache Req Sendpipe = %d\n", pcache->sendpipe);
				break;

			case PIPE_CACHE_REQUEST:
				// Setup Cache Request
				req.tag = buf[2];
				req.sid = (buf[3]<<8) | buf[4];
				req.onid = (buf[5]<<8) | buf[6];
				req.caid = (buf[7]<<8) | buf[8];
				req.hash = (buf[9]<<24) | (buf[10]<<16) | (buf[11]<<8) |buf[12];
				req.prov = (buf[13]<<16) | (buf[14]<<8) | (buf[15]);
				// Check Cache Request
				if (!cache_check(&req)) break;
				//
				pcache = cache_fetch( &req );
				if (pcache==NULL) pcache = cache_new( &req );
				// Send Request if not sent
				if (pcache->sendcache==CACHE_NONE_SENT) {
					pcache->sendcache = CACHE_REQUEST_SENT;
					cfg.cache.req++;
					cache_send_request(pcache,NULL);
				}
				break;

			case PIPE_CACHE_REPLY:
				// Setup Cache Request
				req.tag = buf[2];
				req.sid = (buf[3]<<8) | buf[4];
				req.onid = (buf[5]<<8) | buf[6];
				req.caid = (buf[7]<<8) | buf[8];
				req.hash = (buf[9]<<24) | (buf[10]<<16) | (buf[11]<<8) |buf[12];
				req.prov = (buf[13]<<16) | (buf[14]<<8) | (buf[15]);
				// Check Cache Request
				if (!cache_check(&req)) break;
				//
				pcache = cache_fetch( &req );
				if (pcache==NULL) pcache = cache_new( &req );
				// Send Reply
				if (pcache->sendcache!=CACHE_REPLY_SENT) {
					//Set DCW
					if (len==32) {
						if (pcache->status!=CACHE_STAT_DCW) { // already sent
							memcpy( pcache->cw, buf+16, 16); //dcw
							pcache->status = CACHE_STAT_DCW;
							pcache->peerid = 0; // from local
						}
					}
					pcache->sendcache = CACHE_REPLY_SENT;
					if (pcache->status==CACHE_STAT_DCW) cfg.cache.rep++;
					cache_send_reply(pcache,NULL);
				}
				break;

		}//switch
	}//if
}

void cache_check_peers()
{
	struct cachepeer_data *peer = cfg.cache.peer;
	while (peer) {
		if ( (peer->host->ip)&&(peer->port) ) {
			uint ticks = GetTickCount();
			if (peer->ping==0) { // inactive
				if ( (!peer->lastpingsent)||((peer->lastpingsent+5000)<ticks) ) { // send every 15s
					cache_send_ping(peer);
					peer->lastpingsent = ticks;
					peer->lastpingrecv = 0;
					peer->ping = -1;
				}
			}
			else if (peer->ping==-1) { // inactive
				if ( (!peer->lastpingsent)||((peer->lastpingsent+10000)<ticks) ) { // send every 15s
					cache_send_ping(peer);
					peer->lastpingsent = ticks;
					peer->lastpingrecv = 0;
					peer->ping = -2;
				}
			}
			else if (peer->ping==-2) { // inactive
				if ( (!peer->lastpingsent)||((peer->lastpingsent+20000)<ticks) ) { // send every 15s
					cache_send_ping(peer);
					peer->lastpingsent = ticks;
					peer->lastpingrecv = 0;
					peer->ping = -3;
				}
			}
			else if (peer->ping<=-3) { // inactive
				if ( (!peer->lastpingsent)||((peer->lastpingsent+60000)<ticks) ) { // send every 15s
					cache_send_ping(peer);
					peer->lastpingsent = ticks;
					peer->lastpingrecv = 0;
				}
			}
			else if (peer->ping>0) {
				if ( (!peer->lastpingrecv)&&((peer->lastpingsent+5000)<ticks) ) {
					cache_send_ping(peer);
					peer->lastpingsent = ticks;
					peer->lastpingrecv = 0;
					peer->ping = 0;
					peer->host->checkiptime = 0; // maybe ip changed
				}
				else if ( (peer->lastpingsent+60000)<ticks ) { // send every 75s
#ifdef NEWCACHE
					cache_send_keepalive(peer);
#else
					cache_send_ping(peer);
#endif
					peer->lastpingsent = ticks;
					peer->lastpingrecv = 0;
				}
			}
		}
		peer = peer->next;
	}
}


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

void *cache_thread(void *param)
{
	prg.pid_cache = syscall(SYS_gettid);
	prg.tid_cache = pthread_self();

	uint32 chkticks = 0;

	while(1) {
		if (cfg.cache.handle>0) {
			// Check Peers Ping
			if ( GetTickCount()>(chkticks+3000) ) {
				cache_check_peers();
				chkticks = GetTickCount();
			}
			struct pollfd pfd;
			pfd.fd = cfg.cache.handle;
			pfd.events = POLLIN | POLLPRI;
			int retval = poll(&pfd, 1, 3000);

			if ( retval>0 ) {
				if ( pfd.revents & (POLLIN|POLLPRI) ) {
					pthread_mutex_lock( &prg.lockcache );
					cache_recvmsg();
/*
					do {
						cache_recvmsg();
						pfd.fd = cfg.cache.handle;
						pfd.events = POLLIN | POLLPRI;
						if ( poll(&pfd, 1, 0) <1 ) break;
					} while (pfd.revents & (POLLIN|POLLPRI));
*/
					pthread_mutex_unlock( &prg.lockcache );
				}
			}

		} else usleep( 30000 );
	}
	close(cfg.cache.handle);
}


void *cachepipe_thread(void *param)
{
	while(1) {
		struct pollfd pfd;
		pfd.fd = srvsocks[1];
		pfd.events = POLLIN | POLLPRI;
		int retval = poll(&pfd, 1, 3000);
		if ( retval>0 ) {
			if ( pfd.revents & (POLLIN|POLLPRI) ) {
				pthread_mutex_lock( &prg.lockcache );
				cache_pipe_recvmsg();
				pthread_mutex_unlock( &prg.lockcache );
			}
		}
	}
}

int start_thread_cache()
{
	// Default outsock
	outsock = CreateClientSockUdp();
	create_prio_thread(&prg.tid_cache, (threadfn)cachepipe_thread,NULL, 50);
	create_prio_thread(&prg.tid_cache, (threadfn)cache_thread,NULL, 50);
	return 0;
}