/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Command & Conquer *
* *
* $Archive:: /Commando/Code/SControl/servercontrolsocket.cpp $*
* *
* $Author:: Steve_t $*
* *
* $Modtime:: 2/22/02 7:10p $*
* *
* $Revision:: 3 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* *
* SocketHandlerClass::SocketHandlerClass -- Class constructor *
* SocketHandlerClass::~SocketHandlerClass -- Class destrctor *
* SocketHandlerClass::Open -- Opens a socket and binds it to the given port. *
* SocketHandlerClass::Close -- Closes the socket if it's open. Shuts down the class. *
* *
* *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include
#include "servercontrolsocket.h"
#include "systimer.h"
/*
** All instances are tracked here.
*/
#define PACKET_ENCRYPTION
/***********************************************************************************************
* ServerControlSocketClass::ServerControlSocketClass -- Class constructor *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 2/24/00 12:23PM ST : Created *
*=============================================================================================*/
ServerControlSocketClass::ServerControlSocketClass(void)
{
Socket = INVALID_SOCKET;
Port = -1;
for (int i=0 ; iIsAllocated) {
delete packet;
} else {
packet->InUse = false;
InBuffersUsed--;
}
InBuffers.Delete(0);
}
InBuffersUsed = 0;
InBufferArrayPos = 0;
}
/***********************************************************************************************
* ServerControlSocketClass::Discard_Out_Buffers -- Discard any packets in our outgoing buffers*
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/1/00 11:51AM ST : Created *
*=============================================================================================*/
void ServerControlSocketClass::Discard_Out_Buffers(void)
{
WinsockBufferType *packet;
while (OutBuffers.Count()) {
packet = OutBuffers[0];
if (packet->IsAllocated) {
delete packet;
} else {
packet->InUse = false;
OutBuffersUsed--;
}
OutBuffers.Delete(0);
}
OutBuffersUsed = 0;
OutBufferArrayPos = 0;
}
/***********************************************************************************************
* ServerControlSocketClass::Clear_Socket_Error -- Clear any outstanding erros on the socket *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/1/00 11:51AM ST : Created *
*=============================================================================================*/
void ServerControlSocketClass::Clear_Socket_Error(void)
{
unsigned long error_code;
int length = 4;
if (Socket != INVALID_SOCKET) {
getsockopt (Socket, SOL_SOCKET, SO_ERROR, (char*)&error_code, &length);
error_code = 0;
setsockopt (Socket, SOL_SOCKET, SO_ERROR, (char*)&error_code, length);
}
}
/***********************************************************************************************
* ServerControlSocketClass::Write -- Add packet into the outgoing queue *
* *
* *
* *
* INPUT: ptr to buffer containing data to send *
* length of data to send *
* address to send data to. *
* *
* OUTPUT: Nothing *
* *
* *
* HISTORY: *
* 3/1/00 11:51AM ST : Created *
*=============================================================================================*/
void ServerControlSocketClass::Write(void *buffer, int buffer_len, void *address, unsigned short port)
{
/*
** Create a temporary holding area for the packet.
*/
WinsockBufferType *packet = (WinsockBufferType*) Get_New_Out_Buffer();
fw_assert (packet != NULL);
/*
** Copy the packet into the holding buffer.
*/
memcpy (packet->Buffer, buffer, buffer_len);
packet->BufferLen = buffer_len;
packet->IsBroadcast = false;
memcpy (packet->Address, address, 4);
packet->Port = port;
#ifdef PACKET_ENCRYPTION
Encrypt(packet->Buffer, buffer_len);
#endif //PACKET_ENCRYPTION
Build_Packet_CRC(packet);
/*
** Add it to our out list.
*/
OutBuffers.Add(packet);
/*
** Send it now if possible.
*/
Service();
}
/***********************************************************************************************
* ServerControlSocketClass::Read -- Get the next pending incoming packet *
* *
* *
* *
* INPUT: ptr to buffer to receive input *
* length of buffer *
* ptr to address to fill with address that packet was sent from *
* ptr to address of port number to return *
* packet number to read *
* *
* OUTPUT: number of bytes transfered to buffer *
* *
* WARNINGS: *
* None. *
* *
* HISTORY: *
* 3/8/00 1:12PM ST : Created *
*=============================================================================================*/
int ServerControlSocketClass::Read(void *buffer, int buffer_len, void *address, unsigned short *port, int packetnum)
{
/*
** Call the Service function in case there are any outstanding unqueued packets.
*/
Service();
/*
** If there are no available packets then return 0
*/
if (InBuffers.Count() == 0) return (0);
/*
** If the packet number is out of range then return 0.
*/
if (packetnum >= InBuffers.Count()) {
return(0);
}
WinsockBufferType *packet = InBuffers[packetnum];
fw_assert(packet != NULL);
if (packet == NULL) {
return(0);
}
fw_assert(packet->InUse);
fw_assert(buffer_len >= packet->BufferLen);
/*
** Copy the data and the address it came from into the supplied buffers.
*/
int bytes = min(packet->BufferLen, (int)sizeof(packet->Buffer));
bytes = min(bytes, buffer_len);
memcpy(buffer, packet->Buffer, bytes);
memcpy(address, packet->Address, sizeof (packet->Address));
if (port) {
*port = packet->Port;
}
/*
** Delete the temporary storage for the packet now that it is being passed to the game.
*/
InBuffers.Delete(packetnum);
if (packet->IsAllocated) {
delete packet;
}else{
packet->InUse = false;
InBuffersUsed--;
}
DebugString(("ServerControlSocketClass - ServerControlSocketClass::Read - Returning %d bytes. %d InBuffers left\n", bytes, InBuffers.Count()));
return (bytes);
}
/***********************************************************************************************
* ServerControlSocketClass::Peek -- Get a copy of the specified packet *
* *
* *
* *
* INPUT: ptr to buffer to receive input *
* length of buffer *
* ptr to address to fill with address that packet was sent from *
* ptr to address of port number to return *
* packet number to read *
* *
* OUTPUT: number of bytes transfered to buffer *
* *
* WARNINGS: *
* None. *
* *
* HISTORY: *
* 3/8/00 1:12PM ST : Created *
*=============================================================================================*/
int ServerControlSocketClass::Peek(void *buffer, int buffer_len, void *address, unsigned short *port, int packetnum)
{
/*
** Call the Service function in case there are any outstanding unqueued packets.
*/
Service();
/*
** If there are no available packets then return 0
*/
if (InBuffers.Count() == 0) return (0);
/*
** If the packet number is out of range then return 0.
*/
if (packetnum >= InBuffers.Count()) {
return(0);
}
WinsockBufferType *packet = InBuffers[packetnum];
fw_assert(packet != NULL);
if (packet == NULL) {
return(0);
}
fw_assert(packet->InUse);
fw_assert(buffer_len >= packet->BufferLen);
/*
** Copy the data and the address it came from into the supplied buffers.
*/
int bytes = min(packet->BufferLen, (int)sizeof(packet->Buffer));
bytes = min(bytes, buffer_len);
memcpy(buffer, packet->Buffer, bytes);
memcpy(address, packet->Address, sizeof (packet->Address));
if (port) {
*port = packet->Port;
}
DebugString(("ServerControlSocketClass - ServerControlSocketClass::Peek - Returning packet %d of %d bytes. %d InBuffers left\n", packetnum, bytes, InBuffers.Count()));
return (bytes);
}
/***********************************************************************************************
* ServerControlSocketClass::Build_Packet_CRC -- Create a CRC value for a packet. *
* *
* *
* *
* INPUT: ptr to packet *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/5/99 1:26PM ST : Created *
*=============================================================================================*/
void ServerControlSocketClass::Build_Packet_CRC(WinsockBufferType *packet)
{
fw_assert (packet->InUse);
fw_assert (packet->BufferLen);
packet->CRC = 0;
unsigned long *crc_ptr = &(packet->CRC);
unsigned long *packetptr = (unsigned long*) &(packet->Buffer[0]);
for (int i=0 ; iBufferLen/4 ; i++) {
Add_CRC (crc_ptr, *packetptr++);
}
int leftover = packet->BufferLen & 3;
if (leftover) {
unsigned long val = *packetptr;
val = val & (0xffffffff >> ((4-leftover) << 3));
Add_CRC (crc_ptr, val);
}
}
/***********************************************************************************************
* WIC::Passes_CRC_Check -- Checks the CRC for a packet *
* *
* *
* *
* INPUT: ptr to packet *
* *
* OUTPUT: true if packet passes CRC check *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/5/99 1:26PM ST : Created *
*=============================================================================================*/
bool ServerControlSocketClass::Passes_CRC_Check(WinsockBufferType *packet)
{
fw_assert (packet->InUse);
fw_assert (packet->BufferLen < 768);
if (packet->BufferLen >= 768) {
return (false);
}
unsigned long crc = 0;
unsigned long *crc_ptr = &crc;
unsigned long *packetptr = (unsigned long*) &(packet->Buffer[0]);
for (int i=0 ; iBufferLen/4 ; i++) {
Add_CRC (crc_ptr, *packetptr++);
}
int leftover = packet->BufferLen & 3;
if (leftover) {
unsigned long val = *packetptr;
val = val & (0xffffffff >> ((4-leftover) << 3));
Add_CRC (crc_ptr, val);
}
if (crc == packet->CRC) {
return (true);
}
fw_assert (crc == packet->CRC);
DebugString(("ServerControlSocketClass - Error in Winsock packet CRC\n"));
return (false);
}
/***********************************************************************************************
* ServerControlSocketClass::Get_New_Out_Buffer -- Get a holding buffer for an outgoing packet *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: ptr to out buffer *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/2/00 12:39PM ST : Created *
*=============================================================================================*/
void *ServerControlSocketClass::Get_New_Out_Buffer(void)
{
WinsockBufferType *buffer = NULL;
int pos;
fw_assert (OutBuffersUsed <= SERVER_CONTROL_MAX_STATIC_BUFFERS);
/*
** If there are no more free buffers in the heap then allocate one.
*/
if (OutBuffersUsed == SERVER_CONTROL_MAX_STATIC_BUFFERS) {
buffer = new WinsockBufferType;
buffer->IsAllocated = true;
buffer->InUse = true;
}else{
/*
** Find the next free buffer in the heap.
*/
for (int i=0 ; i SERVER_CONTROL_MAX_STATIC_BUFFERS-1) {
OutBufferArrayPos = 0;
}
if (StaticOutBuffers[pos].InUse == false) {
buffer = &StaticOutBuffers[pos];
buffer->InUse = true;
OutBuffersUsed++;
break;
}
}
}
fw_assert (buffer != NULL);
return (buffer);
}
/***********************************************************************************************
* ServerControlSocketClass::Get_New_In_Buffer -- Get a holding buffer for an incoming packet *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: ptr to in buffer *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/2/00 12:39PM ST : Created *
*=============================================================================================*/
void *ServerControlSocketClass::Get_New_In_Buffer(void)
{
WinsockBufferType *buffer = NULL;
int pos;
fw_assert (InBuffersUsed <= SERVER_CONTROL_MAX_STATIC_BUFFERS);
/*
** If there are no more free buffers in the heap then allocate one.
*/
if (InBuffersUsed == SERVER_CONTROL_MAX_STATIC_BUFFERS) {
buffer = new WinsockBufferType;
buffer->IsAllocated = true;
buffer->InUse = true;
}else{
/*
** Find the next free buffer in the heap.
*/
for (int i=0 ; i SERVER_CONTROL_MAX_STATIC_BUFFERS-1) {
InBufferArrayPos = 0;
}
if (StaticInBuffers[pos].InUse == false) {
buffer = &StaticInBuffers[pos];
buffer->InUse = true;
InBuffersUsed++;
break;
}
}
}
fw_assert (buffer != NULL);
return (buffer);
}
/***********************************************************************************************
* ServerControlSocketClass::Service -- Service the connection - do all reads and writes *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 3/2/00 1:04PM ST : Created *
*=============================================================================================*/
void ServerControlSocketClass::Service(void)
{
unsigned long bytes;
struct sockaddr_in addr;
int addr_len;
WinsockBufferType *packet;
int result;
unsigned long timeout_check = TIMEGETTIME();
int times = 0;
for (;;) {
/*
** Some bail out code, just in case things get stuck.
*/
if (times > 5 && (TIMEGETTIME() - timeout_check) > (TIMER_SECOND*5)) {
break;
}
times++;
Sleep(0);
/*
**
** First, check to see if there is any data waiting to be read.
**
**
**
*/
result = ioctlsocket(Socket, FIONREAD, &bytes);
/*
** Result of 0 is success.
*/
if (result != 0) {
DebugString(("ioctlsocket returned error code %d\n", LAST_ERROR));
break;
} else {
/*
** If there is outstanding data, 'bytes' will contain the size of the next queued datagram.
*/
if (bytes == 0) {
break;
} else {
/*
** Call recvfrom function to get the outstanding packet.
*/
addr_len = sizeof(addr);
result = recvfrom(Socket, (char*)ReceiveBuffer, sizeof(ReceiveBuffer), 0, (LPSOCKADDR)&addr, &addr_len);
/*
** See if we got an error.
*/
if (result == SOCKET_ERROR) {
DebugString(("ServerControlSocketClass - recvfrom returned error code %d\n", LAST_ERROR));
Clear_Socket_Error();
break;
} else {
/*
** Create a new holding buffer to store this packet in.
*/
packet = (WinsockBufferType*) Get_New_In_Buffer();
fw_assert (packet != NULL);
/*
** Store the packet into the holding buffer.
** result is the number of bytes read.
*/
packet->BufferLen = result - sizeof(packet->CRC);
packet->CRC = *((unsigned long*) (&ReceiveBuffer[0]));
memcpy (packet->Buffer, ReceiveBuffer + sizeof(packet->CRC), packet->BufferLen);
/*
** Make sure the CRC looks right.
*/
if (!Passes_CRC_Check(packet)) {
/*
** Bad CRC, throw away the packet.
*/
DebugString(("ServerControlSocketClass - Throwing away malformed packet\n"));
if (packet->IsAllocated) {
delete packet;
}else{
packet->InUse = false;
OutBuffersUsed--;
}
} else {
/*
** Copy the address data into the holding buffer address area.
*/
memset (packet->Address, 0, sizeof (packet->Address));
memcpy (packet->Address, &addr.sin_addr.s_addr, 4);
packet->Port = ntohs(addr.sin_port);
//DebugString(("ServerControlSocketClass - recvfrom %s ; %d\n", IPAddressClass(packet->Address).As_String(), (unsigned int)((unsigned short)ntohs(addr.sin_port))));
#ifdef PACKET_ENCRYPTION
Decrypt(packet->Buffer, packet->BufferLen);
#endif //PACKET_ENCRYPTION
/*
** Add the holding buffer to the packet list.
*/
InBuffers.Add (packet);
DebugString(("ServerControlSocketClass - InBuffers.Count() == %d\n", InBuffers.Count()));
}
}
}
}
}
//DebugString(("ServerControlSocketClass - SocketHandler service\n"));
timeout_check = TIMEGETTIME();
times = 0;
/*
**
** Send any packets in the outgoing queue.
**
**
**
*/
while (OutBuffers.Count()) {
/*
** Some bail out code, just in case things get stuck.
*/
if (times > 5 && (TIMEGETTIME() - timeout_check) > (TIMER_SECOND*5)) {
break;
}
times++;
Sleep(0);
/*
** Get a pointer to the first packet.
*/
packet = OutBuffers[0];
fw_assert (packet->InUse);
/*
** Set up the address structure of the outgoing packet
*/
addr.sin_family = AF_INET;
if (packet->Port == 0) {
addr.sin_port = (unsigned short) htons((unsigned short)Port);
} else {
addr.sin_port = htons(packet->Port);
}
memcpy (&addr.sin_addr.s_addr, packet->Address, 4);
//DebugString(("ServerControlSocketClass - sendto %s ; %d\n", IPAddressClass(packet->Address).As_String(), (unsigned int)((unsigned short)ntohs(addr.sin_port))));
/*
** Send it.
*/
result = sendto(Socket, ((char const *)packet->Buffer) - sizeof(packet->CRC), packet->BufferLen + sizeof(packet->CRC), 0, (LPSOCKADDR)&addr, sizeof (addr));
if (result == SOCKET_ERROR){
if (LAST_ERROR != WSAEWOULDBLOCK) {
DebugString(("ServerControlSocketClass - sendto returned error code %d\n", LAST_ERROR));
Clear_Socket_Error();
} else {
/*
** No more room for outgoing packets.
*/
DebugString(("ServerControlSocketClass - sendto returned WSAEWOULDBLOCK\n"));
Sleep(0);
}
break;
}
/*
** Delete the sent packet.
*/
OutBuffers.Delete(0);
if (packet->IsAllocated) {
delete packet;
}else{
packet->InUse = false;
OutBuffersUsed--;
}
}
}
/***********************************************************************************************
* Add_CRC -- Adds a value to a CRC *
* *
* *
* INPUT: crc ptr to crc *
* val value to add *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 05/09/1995 BRR : Created *
*=============================================================================================*/
void ServerControlSocketClass::Add_CRC(unsigned long *crc, unsigned long val)
{
int hibit;
if ((*crc) & 0x80000000) {
hibit = 1;
} else {
hibit = 0;
}
(*crc) <<= 1;
(*crc) += val;
(*crc) += hibit;
}
/***********************************************************************************************
* ServerControlSocketClass::Set_Encryption_Key -- Sets the key for encryption *
* *
* *
* *
* INPUT: Key string (only 1st 8 bytes used) *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 2/22/2002 12:36PM ST : Created *
*=============================================================================================*/
void ServerControlSocketClass::Set_Encryption_Key(char *key)
{
strncpy(Key, key, 8);
}
/***********************************************************************************************
* ServerControlSocketClass::Encrypt -- Encrypt packet *
* *
* *
* *
* INPUT: Ptr to packet *
* Size of packet *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 2/22/2002 12:37PM ST : Created *
*=============================================================================================*/
void ServerControlSocketClass::Encrypt(unsigned char *packet, int size)
{
char key[8];
memcpy(key, Key, 8);
char a;
for (int i=0 ; i